feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out: los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms + E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client). - go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths relativos reajustados a la nueva ubicacion dentro de fn_registry). - app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales. - modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports). agents_and_robots queda archivado como museo de la era Matrix.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package skills
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Executor ejecuta scripts de skills de forma segura con allowlist de interpreters.
|
||||
type Executor struct {
|
||||
allowedInterpreters []string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewExecutor crea un nuevo Executor con la configuracion dada.
|
||||
// Si allowedInterpreters esta vacio, se usa un default de ["bash", "sh"].
|
||||
func NewExecutor(allowedInterpreters []string, timeout time.Duration) *Executor {
|
||||
if len(allowedInterpreters) == 0 {
|
||||
allowedInterpreters = []string{"bash", "sh"}
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = 60 * time.Second
|
||||
}
|
||||
return &Executor{
|
||||
allowedInterpreters: allowedInterpreters,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Run ejecuta un script de skill con los argumentos dados.
|
||||
// scriptPath es la ruta absoluta al script.
|
||||
// args son los argumentos pasados al script.
|
||||
//
|
||||
// El script debe tener una extension reconocida (.sh, .bash, .py, etc.) o
|
||||
// un shebang que indique el interprete.
|
||||
//
|
||||
// Retorna stdout+stderr combinados y error si falla.
|
||||
func (e *Executor) Run(ctx context.Context, scriptPath string, args []string) (string, error) {
|
||||
// Inferir interprete desde extension
|
||||
interpreter, err := e.inferInterpreter(scriptPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Validar que el interprete esta en la allowlist
|
||||
if !e.isAllowed(interpreter) {
|
||||
return "", fmt.Errorf("interpreter not allowed: %s (allowed: %v)", interpreter, e.allowedInterpreters)
|
||||
}
|
||||
|
||||
// Construir comando
|
||||
cmdArgs := append([]string{scriptPath}, args...)
|
||||
cmd := exec.CommandContext(ctx, interpreter, cmdArgs...)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
// Aplicar timeout
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, e.timeout)
|
||||
defer cancel()
|
||||
|
||||
cmd = exec.CommandContext(timeoutCtx, interpreter, cmdArgs...)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err = cmd.Run()
|
||||
output := stdout.String() + stderr.String()
|
||||
|
||||
if timeoutCtx.Err() == context.DeadlineExceeded {
|
||||
return output, fmt.Errorf("script timeout exceeded (%s)", e.timeout)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return output, fmt.Errorf("script failed: %w", err)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// inferInterpreter detecta el interprete a usar desde la extension del archivo.
|
||||
func (e *Executor) inferInterpreter(path string) (string, error) {
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
|
||||
switch ext {
|
||||
case ".sh", ".bash":
|
||||
return "bash", nil
|
||||
case ".py":
|
||||
return "python3", nil
|
||||
case ".rb":
|
||||
return "ruby", nil
|
||||
case ".js":
|
||||
return "node", nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported script extension: %s", ext)
|
||||
}
|
||||
}
|
||||
|
||||
// isAllowed verifica si un interprete esta en la allowlist.
|
||||
func (e *Executor) isAllowed(interpreter string) bool {
|
||||
for _, allowed := range e.allowedInterpreters {
|
||||
if allowed == interpreter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user