package main import ( "context" "encoding/json" "fmt" "os" "time" "chat-agent-system/internal/config" "chat-agent-system/internal/events" "github.com/jackc/pgx/v5/pgxpool" ) func main() { if len(os.Args) < 2 { usage() os.Exit(2) } envPath := os.Getenv("EVENTMODE_ENV") if envPath == "" { envPath = "configs/.env.dev" } cfg, err := config.Load(envPath) if err != nil { fmt.Fprintln(os.Stderr, "eventmode: load env") os.Exit(1) } if cfg.Env != events.OpsCutoverEnvironment { fmt.Fprintln(os.Stderr, "eventmode: env must be dev") os.Exit(1) } if cfg.ContextDbName != "agent_system_dev" { fmt.Fprintln(os.Stderr, "eventmode: database must be agent_system_dev") os.Exit(1) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() pool, err := pgxpool.New(ctx, cfg.ContextDSN()) if err != nil { fmt.Fprintln(os.Stderr, "eventmode: pool") os.Exit(1) } defer pool.Close() switch os.Args[1] { case "inventory": if err := runInventory(ctx, pool); err != nil { fmt.Fprintln(os.Stderr, "eventmode: inventory failed") os.Exit(1) } case "set": if len(os.Args) != 4 { usage() os.Exit(2) } consumer, mode := os.Args[2], os.Args[3] before, _, _ := events.LoadMode(ctx, pool, cfg.Env, consumer) if err := events.ApplyOpsCutover(ctx, pool, cfg.Env, consumer, mode); err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } after, rev, _ := events.LoadMode(ctx, pool, cfg.Env, consumer) enc := json.NewEncoder(os.Stdout) _ = enc.Encode(map[string]any{ "environment": cfg.Env, "consumer": consumer, "before": before, "after": after, "revision": rev, }) case "quarantine-illegal": n, err := events.QuarantineIllegalPending(ctx, pool, "cursor-ops", "phase2-shared-live") if err != nil { fmt.Fprintln(os.Stderr, "eventmode: quarantine failed") os.Exit(1) } _ = json.NewEncoder(os.Stdout).Encode(map[string]any{"quarantined": n}) default: usage() os.Exit(2) } } func runInventory(ctx context.Context, pool *pgxpool.Pool) error { modes, err := events.ListModes(ctx, pool) if err != nil { return err } backlog, err := events.ListBacklog(ctx, pool) if err != nil { return err } pending, err := events.ClassifyPending(ctx, pool) if err != nil { return err } type wire struct { EventID string `json:"event_id"` Consumer string `json:"consumer"` Classification string `json:"classification"` Reason string `json:"reason"` } class := make([]wire, 0, len(pending)) for _, r := range pending { class = append(class, wire{r.EventID, r.Consumer, r.Classification, r.Reason}) } return json.NewEncoder(os.Stdout).Encode(map[string]any{ "modes": modes, "backlog": backlog, "pending_classifications": class, }) } func usage() { fmt.Fprintln(os.Stderr, "usage: eventmode inventory | set | quarantine-illegal") }