Skip to content

Latest commit

 

History

History
564 lines (422 loc) · 13 KB

File metadata and controls

564 lines (422 loc) · 13 KB

Dashboard

Foreman includes a built-in web dashboard for monitoring pipeline state, inspecting LLM costs, and managing tickets. It exposes a REST API, WebSocket live event stream, and a Prometheus metrics endpoint.

Starting the Dashboard

The dashboard starts automatically with the daemon when enabled = true:

[dashboard]
enabled    = true
port       = 8080
host       = "127.0.0.1"
auth_token = "${FOREMAN_DASHBOARD_TOKEN}"

Generate a token:

./foreman token generate

Access the dashboard at http://localhost:8080. All endpoints require a bearer token.

Security note: The default host = "127.0.0.1" binds to loopback only. Do not set host = "0.0.0.0" without placing the dashboard behind a reverse proxy with TLS.

Authentication

All REST API endpoints require a bearer token in the Authorization header:

Authorization: Bearer <your-token>

Tokens are stored as SHA-256 hashes in the database. They can be revoked via the API.

The WebSocket endpoint supports three auth modes (in priority order):

  1. Sec-WebSocket-Protocol: bearer.<token> (preferred)
  2. Authorization: Bearer <token>
  3. ?token=<token> query parameter (deprecated)

Example (preferred):

const token = '<your-token>';
const ws = new WebSocket('ws://localhost:8080/ws/events', [`bearer.${token}`]);

Deprecated query-param form still works:

ws://localhost:8080/ws/events?token=<your-token>

REST API

Status

GET /api/status

Returns dashboard process status, daemon runtime state, and optional channel/MCP connectivity.

{
  "status": "running",
  "version": "0.1.0",
  "uptime": "3h24m12s",
  "daemon_state": "running",
  "channels": {
    "whatsapp": { "connected": true }
  },
  "mcp_servers": {
    "filesystem": { "healthy": true }
  }
}

Tickets

GET /api/tickets

Returns a list of all tickets. Optional query parameter:

Parameter Values Description
status queued, clarification_needed, planning, plan_validating, implementing, reviewing, pr_created, awaiting_merge, merged, pr_closed, decomposing, decomposed, done, failed, blocked, partial Filter by status
[
  {
    "id": "uuid",
    "external_id": "PROJ-123",
    "title": "Add user authentication",
    "status": "implementing",
    "cost_usd": 1.23,
    "created_at": "2026-03-05T10:00:00Z"
  }
]

GET /api/tickets/{id}

Returns full details for a single ticket by its internal UUID.

{
  "id": "uuid",
  "external_id": "PROJ-123",
  "title": "Add user authentication",
  "description": "...",
  "acceptance_criteria": "...",
  "status": "implementing",
  "branch_name": "foreman/PROJ-123-add-auth",
  "pr_url": "",
  "cost_usd": 1.23,
  "tokens_input": 45000,
  "tokens_output": 8000,
  "total_llm_calls": 4,
  "is_partial": false,
  "last_completed_task_seq": 2,
  "created_at": "2026-03-05T10:00:00Z",
  "started_at": "2026-03-05T10:01:00Z"
}

GET /api/tickets/{id}/tasks

Returns the task list for a ticket.

[
  {
    "id": "task-uuid",
    "ticket_id": "uuid",
    "sequence": 1,
    "title": "Create User model and migration",
    "status": "done",
    "implementation_attempts": 1,
    "spec_review_attempts": 1,
    "quality_review_attempts": 1,
    "total_llm_calls": 3,
    "commit_sha": "abc123",
    "cost_usd": 0.45
  }
]

GET /api/tickets/{id}/events

Returns the last 100 events for a ticket.

[
  {
    "id": "evt-uuid",
    "ticket_id": "uuid",
    "task_id": "task-uuid",
    "event_type": "task_tdd_verify_pass",
    "severity": "info",
    "message": "TDD verification passed",
    "details": null,
    "created_at": "2026-03-05T10:05:00Z"
  }
]

GET /api/ticket-summaries

Returns compact ticket cards used by the dashboard list view.


GET /api/events?limit=50&offset=0

Returns global event feed entries across tickets.


DELETE /api/tickets/{id}

Permanently deletes a ticket and associated records.

{ "status": "deleted", "ticket_id": "uuid" }

GET /api/tickets/{id}/llm-calls

Returns all recorded LLM calls for a ticket.

[
  {
    "id": "call-uuid",
    "ticket_id": "uuid",
    "task_id": "task-uuid",
    "role": "implementer",
    "provider": "anthropic",
    "model": "claude-sonnet-4-6",
    "attempt": 1,
    "tokens_input": 12000,
    "tokens_output": 2500,
    "cost_usd": 0.21,
    "duration_ms": 8400,
    "status": "success",
    "created_at": "2026-03-05T10:03:00Z"
  }
]

POST /api/tickets/{id}/retry

Resets a failed or blocked ticket to queued so it will be picked up on the next daemon poll cycle.

{ "status": "retrying", "ticket_id": "uuid" }

POST /api/tasks/{id}/retry

Resets a failed task to pending so the pipeline can retry it.

{ "status": "retrying", "task_id": "task-uuid" }

GET /api/tasks/{id}/context

Returns context budget/utilization stats for a task.

{
  "budget": 18000,
  "used": 12450,
  "utilization_pct": 69.1,
  "files_selected": 14,
  "files_touched": 6,
  "cache_hits": 3
}

Active Pipelines

GET /api/pipeline/active

Returns currently executing pipelines with their current stage.

[
  {
    "ticket_id": "uuid",
    "external_id": "PROJ-123",
    "title": "Add user authentication",
    "current_stage": "implementing",
    "current_task_seq": 2,
    "total_tasks": 4,
    "started_at": "2026-03-05T10:01:00Z"
  }
]

Costs

GET /api/costs/today

Returns total spend for today.

{
  "date": "2026-03-05",
  "cost_usd": 12.45
}

GET /api/costs/week

Returns spend for the past 7 days as a flat day-by-day array.

[
  { "date": "2026-02-27", "cost_usd": 11.20 },
  { "date": "2026-02-28", "cost_usd": 9.80 }
]

GET /api/costs/month

Returns spend for the current calendar month.

{
  "month": "2026-03",
  "cost_usd": 245.10
}

GET /api/costs/budgets

Returns configured budget limits.

{
  "max_daily_usd": 150,
  "max_monthly_usd": 3000,
  "max_ticket_usd": 100,
  "alert_threshold_pct": 80
}

Team and PR Stats

GET /api/stats/team returns 7-day submitter aggregates.

GET /api/stats/recent-prs returns recent merged/created PR-linked tickets.


Prompt Versions

GET /api/prompts/versions returns prompt-template snapshot hashes for auditability.


Daemon Control

POST /api/daemon/pause

Pauses the daemon scheduler. Active pipelines finish their current task then stop.

{ "message": "daemon paused" }

POST /api/daemon/resume

Resumes a paused daemon.

{ "message": "daemon resumed" }

Config & Usage

GET /api/config/summary

Returns a redacted summary of the active configuration (global + project) — useful for the settings drawer in the dashboard UI. API keys are shown truncated (first 8 chars + ...).

{
  "llm": {
    "provider": "anthropic",
    "models": {
      "planner": "anthropic:claude-sonnet-4-6",
      "implementer": "anthropic:claude-sonnet-4-6",
      "spec_reviewer": "anthropic:claude-haiku-4-5",
      "quality_reviewer": "anthropic:claude-haiku-4-5",
      "final_reviewer": "anthropic:claude-sonnet-4-6"
    },
    "api_key": "sk-ant-a..."
  },
  "tracker": { "provider": "github", "poll_interval": "60s" },
  "git": { "provider": "github", "clone_url": "git@github.com:org/repo.git", "branch_prefix": "foreman", "auto_merge": false },
  "agent_runner": { "provider": "builtin", "max_turns": 20, "token_budget": 0 },
  "daemon": { "work_dir": "~/.foreman/work", "log_level": "info", "max_parallel_tickets": 3, "max_parallel_tasks": 3 },
  "database": { "driver": "sqlite", "path": "~/.foreman/foreman.db" },
  "mcp": { "servers": [] },
  "cost": { "daily_budget": 150.0, "monthly_budget": 3000.0, "per_ticket_budget": 15.0, "alert_threshold": 80 },
  "rate_limit": { "requests_per_minute": 50 }
}

GET /api/usage/activity

Returns LLM call attribution broken down by runner, model, and role for the last 7 days, plus the 20 most recent calls.

{
  "by_runner": [
    { "runner": "builtin", "calls": 142, "tokens_in": 1820000, "tokens_out": 310000, "cost_usd": 8.23 }
  ],
  "by_model": [
    { "model": "anthropic:claude-sonnet-4-6", "calls": 98, "tokens_in": 1200000, "tokens_out": 210000, "cost_usd": 6.75 }
  ],
  "by_role": [
    { "role": "implementer", "runner": "builtin", "model": "anthropic:claude-sonnet-4-6", "calls": 60, "cost_usd": 4.20 }
  ],
  "recent_calls": [
    {
      "ticket_id": "uuid",
      "ticket_title": "Add user auth",
      "task_title": "Write auth middleware",
      "role": "implementer",
      "runner": "builtin",
      "model": "anthropic:claude-sonnet-4-6",
      "status": "success",
      "timestamp": "2026-03-09T10:00:00Z",
      "cost_usd": 0.14,
      "tokens_in": 18000,
      "tokens_out": 3200,
      "duration_ms": 4200
    }
  ]
}

GET /api/usage/claude-code

Returns aggregated usage data parsed from Claude Code's local session log files (~/.claude/). Returns { "available": false } when the logs are not present on the host.

{
  "available": true,
  "total_sessions": 24,
  "today": {
    "sessions": 3,
    "cost_usd": 0.84
  },
  "last_7_days": [
    { "date": "2026-03-09", "sessions": 3, "cost_usd": 0.84 }
  ],
  "estimate_note": "Costs are estimates derived from local Claude Code session logs."
}

Connect to /ws/events to receive real-time pipeline events as JSON objects.

const token = '<your-token>';
const ws = new WebSocket('ws://localhost:8080/ws/events', [`bearer.${token}`]);

ws.onmessage = (event) => {
  const evt = JSON.parse(event.data);
  console.log(evt.event_type, evt.message);
};

Each message has the same shape as events returned by GET /api/tickets/{id}/events, enriched with ticket context fields:

{
  "id": "evt-uuid",
  "ticket_id": "uuid",
  "task_id": "task-uuid",
  "seq": 42,
  "event_type": "task_spec_review_pass",
  "severity": "info",
  "message": "Spec review passed",
  "details": null,
  "created_at": "2026-03-05T10:06:00Z",
  "ticket_title": "Add user authentication",
  "submitter": "123456789@s.whatsapp.net"
}

The WebSocket endpoint broadcasts all pipeline events to all connected clients. There is no per-ticket subscription filtering on the server side.


Prometheus Metrics

The /api/metrics endpoint exposes Prometheus-compatible metrics. It requires the same bearer token authentication as other API routes.

# prometheus.yml scrape config
scrape_configs:
  - job_name: foreman
    static_configs:
      - targets: ['localhost:8080']
    metrics_path: /api/metrics

Available Metrics

Metric Type Labels Description
foreman_tickets_total Counter status Total tickets by final status
foreman_tickets_active Gauge Currently active pipelines
foreman_tasks_total Counter status Total tasks by final status
foreman_llm_calls_total Counter role, model, status Total LLM calls
foreman_llm_tokens_total Counter direction, model Total tokens (input/output)
foreman_llm_duration_seconds Histogram role, model LLM call latency
foreman_cost_usd_total Counter model Total cost by model
foreman_pipeline_duration_seconds Histogram End-to-end pipeline duration
foreman_test_runs_total Counter result Test run outcomes
foreman_retries_total Counter role Retry counts by pipeline role
foreman_rate_limits_total Counter provider Rate limit hits by provider
foreman_tdd_verify_total Counter result TDD verification outcomes
foreman_partial_prs_total Counter Partial PR count
foreman_clarifications_total Counter Clarification requests issued
foreman_secrets_detected_total Counter Secrets detected and excluded
foreman_hook_executions_total Counter hook Hook point executions
foreman_skill_executions_total Counter skill, status Skill executions by outcome

Web UI

The web UI is a Svelte 5 + TypeScript single-page app built with Vite and Tailwind CSS 4. Build output is embedded into the binary at build time (//go:embed dist) and served from /.

Developer commands:

make dashboard-dev    # run Vite dev server
make dashboard-build  # build dashboard assets
make build            # builds dashboard assets + foreman binary

The UI provides:

  • Ticket list with status indicators and filtering
  • Ticket detail view with task breakdown, cost summary, and event log
  • Live event feed via WebSocket
  • Cost overview (today, week, month) with budget indicators
  • Active pipeline monitor

See Also