diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-02-08 21:35:45 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-02-08 21:35:45 -1000 |
| commit | 2e2b2187b957e9af78797a67ec5c6874615fae02 (patch) | |
| tree | 1181dbb7e43f5d30cb025fa4d50fd4e7a2c893b3 /internal/cli/serve.go | |
Initial project: task model, executor, API server, CLI, storage, reporter
Claudomator automation toolkit for Claude Code with:
- Task model with YAML parsing, validation, state machine (49 tests, 0 races)
- SQLite storage for tasks and executions
- Executor pool with bounded concurrency, timeout, cancellation
- REST API + WebSocket for mobile PWA integration
- Webhook/multi-notifier system
- CLI: init, run, serve, list, status commands
- Console, JSON, HTML reporters with cost tracking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/cli/serve.go')
| -rw-r--r-- | internal/cli/serve.go | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/internal/cli/serve.go b/internal/cli/serve.go new file mode 100644 index 0000000..5d41395 --- /dev/null +++ b/internal/cli/serve.go @@ -0,0 +1,86 @@ +package cli + +import ( + "context" + "fmt" + "log/slog" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/claudomator/claudomator/internal/api" + "github.com/claudomator/claudomator/internal/executor" + "github.com/claudomator/claudomator/internal/storage" + "github.com/spf13/cobra" +) + +func newServeCmd() *cobra.Command { + var addr string + + cmd := &cobra.Command{ + Use: "serve", + Short: "Start the Claudomator API server", + RunE: func(cmd *cobra.Command, args []string) error { + return serve(addr) + }, + } + + cmd.Flags().StringVar(&addr, "addr", ":8484", "listen address") + + return cmd +} + +func serve(addr string) error { + if err := cfg.EnsureDirs(); err != nil { + return fmt.Errorf("creating dirs: %w", err) + } + + store, err := storage.Open(cfg.DBPath) + if err != nil { + return fmt.Errorf("opening db: %w", err) + } + defer store.Close() + + level := slog.LevelInfo + if verbose { + level = slog.LevelDebug + } + logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})) + + runner := &executor.ClaudeRunner{ + BinaryPath: cfg.ClaudeBinaryPath, + Logger: logger, + LogDir: cfg.LogDir, + } + pool := executor.NewPool(cfg.MaxConcurrent, runner, store, logger) + + srv := api.NewServer(store, pool, logger) + srv.StartHub() + + httpSrv := &http.Server{ + Addr: addr, + Handler: srv.Handler(), + } + + // Graceful shutdown. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + go func() { + <-sigCh + logger.Info("shutting down server...") + shutdownCtx, shutdownCancel := context.WithTimeout(ctx, 5*time.Second) + defer shutdownCancel() + httpSrv.Shutdown(shutdownCtx) + }() + + fmt.Printf("Claudomator server listening on %s\n", addr) + if err := httpSrv.ListenAndServe(); err != http.ErrServerClosed { + return err + } + return nil +} |
