33b11a63c8
Implementa pkg/command/ como core puro: tipos Spec/ParsedArgs, parser de args key=value con soporte de comillas, specs de 8 comandos built-in (help, tools, tool, ping, status, info, clear, version) y BuiltinNames() para aliases. En agents/runtime.go: nuevo flujo handleEvent que prioriza comandos sobre LLM — custom rules del agente → built-in handlers → comando desconocido → LLM fallback. Handlers en agents/commands.go. El comando !tool ejecuta tools directamente via Registry con args key=value parseados. LLM ahora es opcional: si no hay provider configurado, el agente corre como simple_bot respondiendo solo a comandos. Se extrae executeActions() como helper reutilizable para ambos flujos (comando y no-comando).
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
package command
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
// ParseArgs converts a slice of raw arguments into structured ParsedArgs.
|
|
// Supports: positional args, key=value pairs, and quoted values like key="hello world".
|
|
// Pure function — no side effects.
|
|
func ParseArgs(args []string) ParsedArgs {
|
|
p := ParsedArgs{
|
|
Named: make(map[string]string),
|
|
Raw: args,
|
|
}
|
|
|
|
// First, rejoin args to handle quoted values that were split by Fields().
|
|
joined := strings.Join(args, " ")
|
|
tokens := tokenize(joined)
|
|
|
|
for _, tok := range tokens {
|
|
if idx := strings.IndexByte(tok, '='); idx > 0 {
|
|
key := tok[:idx]
|
|
val := tok[idx+1:]
|
|
// Strip surrounding quotes from value
|
|
val = stripQuotes(val)
|
|
p.Named[key] = val
|
|
} else {
|
|
p.Positional = append(p.Positional, tok)
|
|
}
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// ArgsToJSON converts a named args map to a JSON string for tools.Registry.Execute.
|
|
// Pure function.
|
|
func ArgsToJSON(named map[string]string) string {
|
|
if len(named) == 0 {
|
|
return ""
|
|
}
|
|
m := make(map[string]any, len(named))
|
|
for k, v := range named {
|
|
m[k] = v
|
|
}
|
|
b, _ := json.Marshal(m)
|
|
return string(b)
|
|
}
|
|
|
|
// tokenize splits a string respecting quoted values.
|
|
// e.g. `host=server1 command="uptime -a"` → ["host=server1", `command="uptime -a"`]
|
|
func tokenize(s string) []string {
|
|
var tokens []string
|
|
var current strings.Builder
|
|
inQuote := false
|
|
quoteChar := byte(0)
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
ch := s[i]
|
|
switch {
|
|
case !inQuote && (ch == '"' || ch == '\''):
|
|
inQuote = true
|
|
quoteChar = ch
|
|
current.WriteByte(ch)
|
|
case inQuote && ch == quoteChar:
|
|
inQuote = false
|
|
current.WriteByte(ch)
|
|
case !inQuote && ch == ' ':
|
|
if current.Len() > 0 {
|
|
tokens = append(tokens, current.String())
|
|
current.Reset()
|
|
}
|
|
default:
|
|
current.WriteByte(ch)
|
|
}
|
|
}
|
|
if current.Len() > 0 {
|
|
tokens = append(tokens, current.String())
|
|
}
|
|
return tokens
|
|
}
|
|
|
|
// stripQuotes removes surrounding double or single quotes from a string.
|
|
func stripQuotes(s string) string {
|
|
if len(s) >= 2 {
|
|
if (s[0] == '"' && s[len(s)-1] == '"') || (s[0] == '\'' && s[len(s)-1] == '\'') {
|
|
return s[1 : len(s)-1]
|
|
}
|
|
}
|
|
return s
|
|
}
|