package server import ( "context" "errors" "flag" "fmt" "io" "log" "net/http" "os" "os/signal" "syscall" "time" "ai-workflow-skill/packages/coord-core/db" "ai-workflow-skill/packages/operator-api/internal/app" "ai-workflow-skill/packages/operator-api/internal/httpapi" ) func Execute(args []string, stderr io.Writer) int { fs := flag.NewFlagSet("operator-api", flag.ContinueOnError) fs.SetOutput(stderr) var ( dbPath string listen string shutdown time.Duration ) fs.StringVar(&dbPath, "db", ".agents/coord.db", "SQLite database path") fs.StringVar(&listen, "listen", ":8080", "HTTP listen address") fs.DurationVar(&shutdown, "shutdown-timeout", 5*time.Second, "Graceful shutdown timeout") if err := fs.Parse(args); err != nil { return 2 } ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() sqlDB, err := db.Open(ctx, dbPath) if err != nil { _, _ = fmt.Fprintf(stderr, "open database: %v\n", err) return 1 } defer sqlDB.Close() if err := db.ApplyMigrations(ctx, sqlDB); err != nil { _, _ = fmt.Fprintf(stderr, "apply migrations: %v\n", err) return 1 } webApp := app.NewWebService(sqlDB) server := &http.Server{ Addr: listen, Handler: httpapi.NewRouter(webApp), ReadHeaderTimeout: 5 * time.Second, } logger := log.New(stderr, "", log.LstdFlags) go func() { <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdown) defer cancel() if err := server.Shutdown(shutdownCtx); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Printf("http shutdown: %v", err) } }() logger.Printf("operator-api listening on %s", listen) if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Printf("serve http api: %v", err) return 1 } return 0 }