mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-11-28 21:20:25 +08:00
* build manifest * working on secrets injection (secretstore + secret-bindings.json) * tool progress indicators * build output and errors injected as the result of the edit calls so AI gets instant feedback on edits * change edits to not be atomic (allows AI to make better progress) * updated binary location for waveapps * publish button * new partial json parser (for sending incremental tool progress indication) * updated tsunami view to use new embedded scaffold + config vars * lots of work on cleaning up the output so it is more useful to users + AI agents * fix builder init flow
175 lines
3 KiB
Go
175 lines
3 KiB
Go
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package utilfn
|
|
|
|
import (
|
|
"encoding/json"
|
|
)
|
|
|
|
type stackItem int
|
|
|
|
const (
|
|
stackInvalid stackItem = iota
|
|
stackLBrace
|
|
stackLBrack
|
|
stackBeforeKey
|
|
stackKey
|
|
stackKeyColon
|
|
stackQuote
|
|
)
|
|
|
|
type jsonStack []stackItem
|
|
|
|
func (s *jsonStack) push(item stackItem) {
|
|
*s = append(*s, item)
|
|
}
|
|
|
|
func (s *jsonStack) pop() stackItem {
|
|
if len(*s) == 0 {
|
|
return stackInvalid
|
|
}
|
|
item := (*s)[len(*s)-1]
|
|
*s = (*s)[:len(*s)-1]
|
|
return item
|
|
}
|
|
|
|
func (s jsonStack) peek() stackItem {
|
|
if len(s) == 0 {
|
|
return stackInvalid
|
|
}
|
|
return s[len(s)-1]
|
|
}
|
|
func (s jsonStack) isTop(items ...stackItem) bool {
|
|
top := s.peek()
|
|
for _, item := range items {
|
|
if top == item {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *jsonStack) replaceTop(item stackItem) {
|
|
if len(*s) > 0 {
|
|
(*s)[len(*s)-1] = item
|
|
}
|
|
}
|
|
|
|
func repairJson(data []byte) []byte {
|
|
if len(data) == 0 {
|
|
return data
|
|
}
|
|
|
|
var stack jsonStack
|
|
inString := false
|
|
escaped := false
|
|
lastComma := false
|
|
|
|
for i := 0; i < len(data); i++ {
|
|
b := data[i]
|
|
|
|
if escaped {
|
|
escaped = false
|
|
continue
|
|
}
|
|
|
|
if inString {
|
|
if b == '\\' {
|
|
escaped = true
|
|
continue
|
|
}
|
|
if b == '"' {
|
|
inString = false
|
|
}
|
|
continue
|
|
}
|
|
|
|
if b == ' ' || b == '\t' || b == '\n' || b == '\r' {
|
|
continue
|
|
}
|
|
valueStart := b == '{' || b == '[' || b == 'n' || b == 't' || b == 'f' || b == '"' || (b >= '0' && b <= '9') || b == '-'
|
|
if valueStart && lastComma {
|
|
lastComma = false
|
|
}
|
|
if valueStart && stack.isTop(stackKeyColon) {
|
|
stack.pop()
|
|
}
|
|
if valueStart && stack.isTop(stackBeforeKey) {
|
|
stack.replaceTop(stackKey)
|
|
}
|
|
switch b {
|
|
case '{':
|
|
stack.push(stackLBrace)
|
|
stack.push(stackBeforeKey)
|
|
case '[':
|
|
stack.push(stackLBrack)
|
|
case '}':
|
|
if stack.isTop(stackBeforeKey) {
|
|
stack.pop()
|
|
}
|
|
if stack.isTop(stackLBrace) {
|
|
stack.pop()
|
|
}
|
|
case ']':
|
|
if stack.isTop(stackLBrack) {
|
|
stack.pop()
|
|
}
|
|
case '"':
|
|
inString = true
|
|
case ':':
|
|
if stack.isTop(stackKey) {
|
|
stack.replaceTop(stackKeyColon)
|
|
}
|
|
case ',':
|
|
lastComma = true
|
|
if stack.isTop(stackLBrace) {
|
|
stack.push(stackBeforeKey)
|
|
}
|
|
default:
|
|
}
|
|
}
|
|
|
|
if len(stack) == 0 && !inString {
|
|
return data
|
|
}
|
|
|
|
result := append([]byte{}, data...)
|
|
if escaped && len(result) > 0 {
|
|
result = result[:len(result)-1]
|
|
}
|
|
if inString {
|
|
result = append(result, '"')
|
|
}
|
|
if lastComma {
|
|
for i := len(result) - 1; i >= 0; i-- {
|
|
if result[i] == ',' {
|
|
result = result[:i]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
for i := len(stack) - 1; i >= 0; i-- {
|
|
switch stack[i] {
|
|
case stackKeyColon:
|
|
result = append(result, []byte("null")...)
|
|
case stackKey:
|
|
result = append(result, []byte(": null")...)
|
|
case stackLBrace:
|
|
result = append(result, '}')
|
|
case stackLBrack:
|
|
result = append(result, ']')
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ParsePartialJson(data []byte) (any, error) {
|
|
fixedData := repairJson(data)
|
|
var output any
|
|
err := json.Unmarshal(fixedData, &output)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return output, nil
|
|
}
|