package main import ( "context" "encoding/json" "fmt" "os" "strings" "time" "github.com/kami/hexis/pkg/client" ) var cli *client.Client func main() { if len(os.Args) < 2 { printUsage() os.Exit(1) } baseURL := os.Getenv("HEXIS_URL") if baseURL == "" { baseURL = "http://localhost:9741" } if os.Args[1] == "--url" && len(os.Args) > 2 { baseURL = os.Args[2] os.Args = append(os.Args[:1], os.Args[3:]...) } cli = client.New(baseURL) cmd := os.Args[1] args := os.Args[2:] switch cmd { case "capability": handleCapability(args) case "exec": handleExec(args) case "execution": handleExecution(args) case "health": handleHealth() default: fmt.Fprintf(os.Stderr, "unknown command: %s\n", cmd) printUsage() os.Exit(1) } } func handleCapability(args []string) { if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: hexis capability [...]") os.Exit(1) } sub := args[0] rest := args[1:] switch sub { case "create": capCreate(rest) case "list": capList(rest) case "show": capShow(rest) case "delete": capDelete(rest) default: fmt.Fprintf(os.Stderr, "unknown capability subcommand: %s\n", sub) os.Exit(1) } } func capCreate(args []string) { flags := parseFlags(args) req := client.CreateCapabilityRequest{ Name: flags["name"], Description: flags["description"], TargetEntityID: flags["target"], Provider: flags["provider"], Operation: flags["operation"], Risk: flags["risk"], ExpectedSideEffects: flags["side-effects"], } if flags["read-only"] == "true" { req.ReadOnly = true } if typesStr := flags["types"]; typesStr != "" { req.TargetTypes = strings.Split(typesStr, ",") } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cap, err := cli.CreateCapability(ctx, req) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } printJSON(cap) } func capList(args []string) { flags := parseFlags(args) entityID := flags["entity"] ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() caps, err := cli.Capabilities(ctx, entityID) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } printJSON(caps) } func capShow(args []string) { if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: hexis capability show ") os.Exit(1) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() cap, err := cli.GetCapability(ctx, args[0]) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } printJSON(cap) } func capDelete(args []string) { fmt.Fprintln(os.Stderr, "capability delete not yet implemented") os.Exit(1) } func handleExec(args []string) { if len(args) < 2 { fmt.Fprintln(os.Stderr, "usage: hexis exec [--args JSON] [--idempotency KEY]") os.Exit(1) } flags := parseFlags(args[2:]) req := client.ExecuteRequest{ CapabilityID: args[0], TargetEntityID: args[1], IdempotencyKey: flags["idempotency"], } if argsStr := flags["args"]; argsStr != "" { json.Unmarshal([]byte(argsStr), &req.Arguments) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() result, err := cli.Execute(ctx, req) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } printJSON(result) } func handleExecution(args []string) { if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: hexis execution ") os.Exit(1) } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() exec, err := cli.GetExecution(ctx, args[0]) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } printJSON(exec) } func handleHealth() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := cli.Health(ctx); err != nil { fmt.Fprintf(os.Stderr, "unhealthy: %v\n", err) os.Exit(1) } fmt.Println("ok") } func printUsage() { fmt.Fprintf(os.Stderr, `Usage: hexis [--url URL] [args] Commands: capability create --name NAME --provider PROVIDER --operation OP [--target ENTITY] [--types TYPES] [--risk RISK] [--read-only true] [--side-effects TEXT] capability list [--entity ENTITY_ID] capability show exec [--args JSON] [--idempotency KEY] execution health `) } func parseFlags(args []string) map[string]string { flags := map[string]string{} for i := 0; i < len(args); i++ { if strings.HasPrefix(args[i], "--") { key := strings.TrimPrefix(args[i], "--") if i+1 < len(args) && !strings.HasPrefix(args[i+1], "--") { flags[key] = args[i+1] i++ } else { flags[key] = "true" } } } return flags } func printJSON(v any) { enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") enc.Encode(v) }