- ops-agent/src/lib/flow-runner.ts: loads YAML flows, validates all steps
against the command whitelist, executes sequentially; supports dry_run
(emits WOULD RUN lines) and on_failure: abort|continue per step
- ops-agent/src/routes/flow.ts: POST /agent/v1/flow { flow_key, dry_run }
streams step_start/stdout/stderr/step_done/done SSE events
- ops-agent/src/index.ts: register flow route, add FLOWS_PATH env var
- ops-agent/flows.example/: three flow definitions — update_scrum4me_web,
update_mcp_worker, update_caddy_config; deploy to /etc/ops-agent/flows/
- ops-agent/commands.yml.example: add curl_smoke_scrum4me_web and
docker_compose_ps_worker smoke-test commands
- app/api/flows/run/route.ts: Next.js proxy — creates FlowRun/FlowStep
DB records per step, forwards SSE stream to browser
- hooks/useFlowRun.ts: add startFlow(flowKey, dryRun) method; handle
step_start events to display step headers in the terminal
- components/StreamingTerminal.tsx: add 'info' line type (sky-400) for
step headers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Docker table: Restart and Stop buttons per container row (docker_compose_restart / docker_compose_stop)
- Git repos list: Fetch and Pull buttons per repo; Pull disabled when working tree is dirty
- systemd units list: Restart button per unit (systemctl_restart)
- Caddy: Edit link on /caddy page, new /caddy/edit page with textarea + 3-step Validate → Save+Reload flow
- All buttons open ConfirmDialog with exact agent-call preview, then stream output via StreamingTerminal
- Add docker_compose_stop to ops-agent/commands.yml.example
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds docker_compose_restart/build/up, git_pull (guarded by
git_status_clean precondition), systemctl_restart (via sudo),
caddy_validate, caddy_reload, and caddy_write_config (atomic
stdin→Caddyfile.new→Caddyfile write).
- CommandDef gains preconditions[] and stdin_from_body fields
- exec route checks git_status_clean before git_pull; returns 409 on
dirty tree with a clear message
- stdin field in ExecBody is piped to child stdin for caddy_write_config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add journalctl_recent command and scrum4me-web to whitelist in commands.yml.example
- Add SYSTEMD_UNITS env var to .env.example
- lib/parse-systemd.ts: parse activeState, subState, uptime, description
- /app/systemd: server page reading SYSTEMD_UNITS, client list with 10s polling and status badges
- /app/systemd/[unit]: server detail page, client component showing systemctl status + last 100 journal lines (polling 10s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add validateCwd() to whitelist.ts for dynamic-cwd validation, update
exec.ts to resolve first arg as cwd when cwd_pattern is set, and extend
commands.yml.example with git_status, git_log_ahead, git_diff, git_fetch.
Add REPO_PATHS to .env.example.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ops-agent/src/auth.ts: constant-time compare via timingSafeEqual to prevent timing attacks; store secret as Buffer
- ops-agent/src/index.ts + ops-agent.service: bind on 127.0.0.1:3099 (was 4242, per plan)
- app/api/agent/[...path]/route.ts: Next.js proxy route that verifies ops_session cookie then forwards requests to agent with Authorization: Bearer <secret>
- .env.example + deploy/ops-dashboard.env.example: add OPS_AGENT_SECRET and OPS_AGENT_URL
- README.md: rotation procedure for the shared secret
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CommandDef now uses cmd[] array instead of exec string — no shell splitting
- validateArgs() checks every request arg against allowed list; rejects unknown values
- spawn() called with shell:false (execFile semantics); cwd from config
- Audit log (JSON) per call to stdout → captured by systemd journal
- commands.yml.example updated to new schema with 4 read-only commands
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>