post inicial
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
// Comando sysagent: agente autónomo de administración de sistemas.
|
||||
//
|
||||
// Uso:
|
||||
//
|
||||
// sysagent procesa las tareas *.md de PROJECT_ROOT
|
||||
// sysagent --cli sesión interactiva (ignora las tareas)
|
||||
// sysagent --version
|
||||
//
|
||||
// PROJECT_ROOT es siempre el directorio actual.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"sysagent/internal/agent"
|
||||
"sysagent/internal/project"
|
||||
)
|
||||
|
||||
// version se inyecta al compilar:
|
||||
//
|
||||
// go build -ldflags "-X main.version=0.1.0" ./cmd/sysagent
|
||||
var version = "0.1.0-dev"
|
||||
|
||||
func main() {
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
// run contiene la lógica de main y devuelve el código de salida, para que
|
||||
// los defer se ejecuten antes de os.Exit.
|
||||
func run() int {
|
||||
var (
|
||||
cli = flag.Bool("cli", false, "start an interactive session (task *.md files are ignored)")
|
||||
dryRun = flag.Bool("dry-run", false, "describe actions without modifying the system")
|
||||
verbose = flag.Bool("verbose", false, "show DEBUG/INFO logs on the console")
|
||||
rerun = flag.Bool("rerun", false, "execute again tasks that contexto.mem records as DONE")
|
||||
showVersion = flag.Bool("version", false, "print version and exit")
|
||||
)
|
||||
flag.Parse()
|
||||
if *showVersion {
|
||||
fmt.Printf("sysagent %s\n", version)
|
||||
return 0
|
||||
}
|
||||
if flag.NArg() > 0 {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: unexpected arguments: %v\n", flag.Args())
|
||||
return 2
|
||||
}
|
||||
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: cannot determine current directory: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Las señales se capturan desde el principio. En modo tareas cancelan el
|
||||
// contexto raíz; en --cli las gestiona el propio CLI (ver RunCLI).
|
||||
interrupts := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupts, os.Interrupt, syscall.SIGTERM)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
a, err := agent.Bootstrap(ctx, agent.Options{
|
||||
Root: root,
|
||||
CLI: *cli,
|
||||
DryRun: *dryRun,
|
||||
Verbose: *verbose,
|
||||
Rerun: *rerun,
|
||||
Version: version,
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, project.ErrAgentNotFound) {
|
||||
fmt.Fprintln(os.Stderr, "ERROR: agent.md not found in PROJECT_ROOT")
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
defer a.Close()
|
||||
|
||||
if *cli {
|
||||
err = a.RunCLI(ctx, interrupts)
|
||||
} else {
|
||||
go func() {
|
||||
// Primera señal: cancelar de forma ordenada (se matan los procesos
|
||||
// hijos y se guarda contexto.mem al volver de RunTasks).
|
||||
// Segunda señal: salida inmediata, por si algo no responde.
|
||||
sig := <-interrupts
|
||||
fmt.Fprintf(os.Stderr, "\nreceived %s, stopping (press Ctrl+C again to force)...\n", sig)
|
||||
cancel()
|
||||
<-interrupts
|
||||
fmt.Fprintln(os.Stderr, "forced exit")
|
||||
os.Exit(130)
|
||||
}()
|
||||
err = a.RunTasks(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user