Files
Claude-Code-Monitor/server/openapi-extra/misc.js
T
nntrivi2001 57dc91585d feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
2026-07-30 14:39:03 +07:00

1261 lines
46 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file Supplementary OpenAPI 3.0 fragments for endpoints that were previously
* undocumented in the base spec (server/openapi.js). Covers:
* - GET /api/sessions/facets (Sessions)
* - GET /api/settings/claude-home (Settings)
* - PUT /api/settings/claude-home (Settings)
* - GET /api/workflows/runs (Workflows)
* - GET /api/workflows/runs/{runId} (Workflows)
* - GET /api/remote-sources (Remote Sources)
* - POST /api/remote-sources (Remote Sources)
* - PATCH /api/remote-sources/{id} (Remote Sources)
* - DELETE /api/remote-sources/{id} (Remote Sources)
* - POST /api/remote-sources/{id}/test (Remote Sources)
* - POST /api/remote-sources/{id}/sync (Remote Sources)
*
* Exports `{ tags, schemas, paths }` and is combined into the base spec by
* server/openapi-extra.js. Schema names are prefixed (Sessions / Settings /
* Workflow / RemoteSource) so they never collide with the base
* `components.schemas`. The Sessions/Settings/Workflows tags — plus the
* `Remote Sources` tag now declared in the base literal — are all present in the
* base spec, so `tags` here is intentionally empty. Error bodies reference the
* base-defined `ErrorResponse` (shape `{ error: { code, message } }`); the
* run-detail agents and events arrays reference the base `Agent` /
* `DashboardEvent` schemas. The `SessionsFacetsResponse` schema additionally
* exposes a `sources` array (the distinct `sessions.source` values).
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const tags = [];
const schemas = {
SessionsFacetsResponse: {
type: "object",
description:
"Facet values for the Sessions page filter UI: the distinct working directories (cwd) and the distinct data-source ids seen across all sessions.",
required: ["cwds", "sources"],
properties: {
cwds: {
type: "array",
description:
"Distinct, non-empty session working directories (the `cwd` column), sorted ascending. Powers the cwd filter dropdown.",
items: { type: "string" },
example: [
"/Users/son/WebstormProjects/Claude-Code-Agent-Monitor",
"/Users/son/code/another-project",
],
},
sources: {
type: "array",
description:
"Distinct data-source ids seen across all sessions (the `sessions.source` column). Always includes the built-in `local` history; each configured remote SSH machine contributes its `remote_sources.id`. Powers the source filter dropdown and the `sources` query param on the list/analytics endpoints.",
items: { type: "string" },
example: ["local", "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11"],
},
},
},
RemoteSource: {
type: "object",
description:
"A configured remote SSH machine the dashboard pulls Claude Code history from. NO secrets are stored on this record — SSH authentication defers entirely to the host's SSH stack (ssh-agent, `~/.ssh/config`, and key files). `host` is an SSH destination (`user@host`) or a `~/.ssh/config` alias.",
required: [
"id",
"label",
"host",
"ssh_port",
"identity_file",
"remote_home",
"enabled",
"status",
"last_error",
"last_sync_at",
"last_sync_counts",
"created_at",
"updated_at",
],
properties: {
id: {
type: "string",
description: "Primary key — the remote-source id (also used as `sessions.source`).",
example: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
},
label: {
type: "string",
description: "Human-readable name shown in the UI.",
example: "Work laptop",
},
host: {
type: "string",
description:
"SSH destination (`user@host`) or a `~/.ssh/config` alias resolved by the host SSH stack.",
example: "son@studio.local",
},
ssh_port: {
type: "integer",
nullable: true,
description: "Optional SSH port; null defers to the SSH default / `~/.ssh/config`.",
example: 22,
},
identity_file: {
type: "string",
nullable: true,
description: "Optional path to a private-key file passed to ssh (`-i`); null to omit.",
example: "~/.ssh/id_ed25519",
},
remote_home: {
type: "string",
nullable: true,
description:
"Optional remote Claude home to read transcripts from; null defaults to the remote `~/.claude`.",
example: "~/.claude",
},
enabled: {
type: "boolean",
description: "Whether this source is eligible for scheduled/manual syncs.",
example: true,
},
status: {
type: "string",
enum: ["idle", "syncing", "ok", "error"],
description: "Last known sync status of the source.",
example: "ok",
},
last_error: {
type: "string",
nullable: true,
description: "Error message from the last failed sync/test, or null.",
example: null,
},
last_sync_at: {
type: "string",
format: "date-time",
nullable: true,
description: "ISO-8601 timestamp of the last successful sync, or null.",
example: "2026-07-22T18:41:55.117Z",
},
last_sync_counts: {
type: "object",
nullable: true,
additionalProperties: true,
description:
"Counters from the last sync (imported / skipped / backfilled / errors / sessions_seen / sessions_tagged), or null if never synced.",
example: {
imported: 9,
skipped: 41,
backfilled: 0,
errors: 0,
sessions_seen: 50,
sessions_tagged: 50,
},
},
created_at: {
type: "string",
format: "date-time",
description: "ISO-8601 creation timestamp.",
example: "2026-07-20T09:15:00.000Z",
},
updated_at: {
type: "string",
format: "date-time",
description: "ISO-8601 timestamp of the last edit.",
example: "2026-07-22T18:41:55.117Z",
},
},
},
RemoteSourceCreateRequest: {
type: "object",
description:
"Request body to register a remote SSH source. `label` and `host` are required; the rest are optional. No credentials are ever accepted or stored — auth defers to the host SSH stack.",
required: ["label", "host"],
properties: {
label: { type: "string", description: "Human-readable name.", example: "Work laptop" },
host: {
type: "string",
description: "SSH destination (`user@host`) or a `~/.ssh/config` alias.",
example: "son@studio.local",
},
ssh_port: {
type: "integer",
description: "Optional SSH port.",
example: 22,
},
identity_file: {
type: "string",
description: "Optional private-key path passed to ssh (`-i`).",
example: "~/.ssh/id_ed25519",
},
remote_home: {
type: "string",
description: "Optional remote Claude home; defaults to the remote `~/.claude`.",
example: "~/.claude",
},
enabled: {
type: "boolean",
description: "Whether the source is enabled for syncing (default true).",
example: true,
},
},
},
RemoteSourceUpdateRequest: {
type: "object",
description:
"Partial update for a remote source. Only the keys present in the body are changed; omitted keys are left as-is. Same field set as create; both `label` and `host` are optional here.",
properties: {
label: { type: "string", example: "Studio Mac" },
host: { type: "string", example: "son@studio.local" },
ssh_port: { type: "integer", nullable: true, example: 2222 },
identity_file: { type: "string", nullable: true, example: "~/.ssh/id_ed25519" },
remote_home: { type: "string", nullable: true, example: "~/.claude" },
enabled: { type: "boolean", example: false },
},
},
RemoteSourceResponse: {
type: "object",
required: ["source"],
properties: { source: { $ref: "#/components/schemas/RemoteSource" } },
},
RemoteSourceListResponse: {
type: "object",
required: ["sources"],
properties: {
sources: {
type: "array",
items: { $ref: "#/components/schemas/RemoteSource" },
},
},
},
RemoteSourceTestResponse: {
type: "object",
description: "Result of an SSH connectivity probe.",
required: ["ok", "message"],
properties: {
ok: { type: "boolean", example: true },
message: {
type: "string",
description: "Human-readable probe result.",
example: "Connected; found 24 project directories under ~/.claude/projects.",
},
remoteProjects: {
type: "array",
description:
"Optional list of remote project directories discovered during the probe (present on success).",
items: { type: "string" },
example: ["-Users-son-code-foo", "-Users-son-code-bar"],
},
},
},
RemoteSourceSyncResponse: {
type: "object",
description: "Counters from a pull-now sync against the remote source.",
required: [
"ok",
"imported",
"skipped",
"backfilled",
"errors",
"sessions_seen",
"sessions_tagged",
],
properties: {
ok: { type: "boolean", example: true },
imported: { type: "integer", example: 9 },
skipped: { type: "integer", example: 41 },
backfilled: { type: "integer", example: 0 },
errors: { type: "integer", example: 0 },
sessions_seen: { type: "integer", example: 50 },
sessions_tagged: {
type: "integer",
description: "Number of imported sessions stamped with this source's id.",
example: 50,
},
},
},
SettingsClaudeHomeResponse: {
type: "object",
description:
"The Claude Code home directory the dashboard reads transcripts and settings from. Defaults to `~/.claude` unless overridden via the CLAUDE_HOME environment variable.",
required: ["claude_home"],
properties: {
claude_home: {
type: "string",
description:
"Absolute path to the active Claude Code home directory (CLAUDE_HOME, or `<homedir>/.claude` when unset).",
example: "/Users/son/.claude",
},
},
},
SettingsClaudeHomeUpdateRequest: {
type: "object",
description:
"Request body for changing the Claude Code home directory. A leading `~` is expanded to the user's home directory; the resolved path must be absolute and point to an existing directory.",
required: ["path"],
properties: {
path: {
type: "string",
description:
"New Claude Code home directory. A leading `~/` is expanded to the OS home directory before validation. Must resolve to an absolute path that exists and is a directory.",
example: "~/.codefuse/engine/cc",
},
},
},
SettingsClaudeHomeUpdateResponse: {
type: "object",
description:
"Confirmation that CLAUDE_HOME was updated. The new value is applied to process.env immediately and persisted to the project `.env` file.",
required: ["ok", "claude_home"],
properties: {
ok: { type: "boolean", enum: [true] },
claude_home: {
type: "string",
description: "The resolved absolute path now in effect (after `~` expansion).",
example: "/Users/son/.codefuse/engine/cc",
},
},
},
WorkflowToolRun: {
type: "object",
description:
"A Claude Code Workflow-tool run (issue #167): a fleet of sub-agents spawned by the 'Workflow' tool (or self-paced /loop). These emit no hooks; the source of truth is the on-disk run journal, ingested into the `workflows` table (see server/lib/workflow-ingest.js). Keyed by `run_id` and parented to the launching session. The JSON-blob columns `phases` and `progress` are parsed into arrays before serialization.",
required: [
"run_id",
"session_id",
"status",
"agent_count",
"total_tokens",
"total_tool_calls",
"phases",
"progress",
"source",
"created_at",
"updated_at",
],
properties: {
run_id: {
type: "string",
description: "Primary key — the workflow run id.",
example: "wf_a1b2c3d4",
},
session_id: {
type: "string",
description: "The session that launched this run (FK into sessions.id).",
example: "5f3c0e2a-1b9d-4c77-8a21-9e0f7b6d4c11",
},
task_id: {
type: "string",
nullable: true,
description: "Optional task/issue identifier associated with the run.",
example: "ISSUE-167",
},
name: {
type: "string",
nullable: true,
description: "Human-readable run name from the journal, if present.",
example: "Refactor pricing engine",
},
status: {
type: "string",
description:
"Open status string (e.g. running | completed | error | failed). Intentionally not constrained to an enum so new harness states never trip a stale constraint.",
example: "completed",
},
default_model: {
type: "string",
nullable: true,
description: "Default model the run delegated work to, when recorded.",
example: "claude-opus-4-8",
},
started_at: {
type: "string",
format: "date-time",
nullable: true,
description: "When the run started, if known.",
example: "2026-06-25T18:04:11.122Z",
},
ended_at: {
type: "string",
format: "date-time",
nullable: true,
description: "When the run finished, if known.",
example: "2026-06-25T18:09:47.530Z",
},
duration_ms: {
type: "integer",
nullable: true,
description: "Total run duration in milliseconds, if known.",
example: 336408,
},
agent_count: {
type: "integer",
minimum: 0,
description: "Number of inner agents in this run.",
example: 6,
},
total_tokens: {
type: "integer",
minimum: 0,
description: "Aggregate token usage across the run's inner agents.",
example: 1284750,
},
total_tool_calls: {
type: "integer",
minimum: 0,
description: "Aggregate tool-call count across the run's inner agents.",
example: 412,
},
phases: {
type: "array",
description:
"Parsed `phases[]` array from the run journal (verbatim journal payload, opaque to this API). Empty array when absent or unparseable.",
items: { type: "object", additionalProperties: true },
example: [
{ name: "plan", status: "completed" },
{ name: "implement", status: "completed" },
],
},
progress: {
type: "array",
description:
"Parsed `workflowProgress[]` array from the run journal (verbatim journal payload, opaque to this API). Empty array when absent or unparseable.",
items: { type: "object", additionalProperties: true },
example: [{ step: 1, label: "scaffold", done: true }],
},
script_path: {
type: "string",
nullable: true,
description: "Path to the run's driving script, if recorded.",
example: "/Users/son/.claude/projects/-Users-son-code/wf_a1b2c3d4.sh",
},
journal_path: {
type: "string",
nullable: true,
description: "Path to the on-disk run journal this row was ingested from.",
example: "/Users/son/.claude/projects/-Users-son-code/5f3c0e2a/workflows/wf_a1b2c3d4.json",
},
source: {
type: "string",
description: "Ingestion source for the row (defaults to 'journal').",
example: "journal",
},
created_at: {
type: "string",
format: "date-time",
description: "Row creation timestamp.",
example: "2026-06-25T18:09:48.001Z",
},
updated_at: {
type: "string",
format: "date-time",
description: "Row last-update timestamp.",
example: "2026-06-25T18:09:48.001Z",
},
},
},
WorkflowRunsListResponse: {
type: "object",
description:
"Paginated list of Workflow-tool runs with status counts. `total` reflects the active filter (status when supplied, otherwise the full table); `counts` is always the whole-table breakdown by status.",
required: ["runs", "total", "counts", "limit", "offset"],
properties: {
runs: {
type: "array",
items: { $ref: "#/components/schemas/WorkflowToolRun" },
},
total: {
type: "integer",
minimum: 0,
description:
"Total runs matching the current filter (independent of limit/offset). Equals the status-filtered count when `status` is supplied, otherwise the full-table count. Note: not narrowed by `session_id`.",
example: 42,
},
counts: {
type: "object",
description: "Whole-table run counts grouped by status (not affected by filters).",
additionalProperties: { type: "integer", minimum: 0 },
example: { completed: 30, error: 5, running: 7 },
},
limit: { type: "integer", description: "Effective page size used.", example: 50 },
offset: { type: "integer", description: "Effective pagination offset used.", example: 0 },
},
},
WorkflowRunDetailResponse: {
type: "object",
description:
"A single Workflow-tool run with its linked inner agents and the events attributed to those agents (chronological, capped at 5000).",
required: ["workflow", "agents", "events"],
properties: {
workflow: { $ref: "#/components/schemas/WorkflowToolRun" },
agents: {
type: "array",
description: "Inner agents linked to this run via agents.workflow_run_id.",
items: { $ref: "#/components/schemas/Agent" },
},
events: {
type: "array",
description:
"Events attributed to this run's inner agents, ordered by created_at then id. Capped at 5000 rows.",
items: { $ref: "#/components/schemas/DashboardEvent" },
},
},
},
};
const paths = {
"/api/sessions/facets": {
get: {
tags: ["Sessions"],
summary: "List session facet values",
description:
"Returns the distinct facet values for the Sessions page filters: the non-empty working directories (the `cwd` column, sorted ascending) in `cwds`, and the distinct data-source ids (the `sessions.source` column) in `sources`. `sources` always includes the built-in `local` history plus any configured remote SSH machines. Always returns a 200 with (possibly empty) arrays.",
operationId: "listSessionFacets",
responses: {
200: {
description: "Distinct session working directories and data-source ids",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/SessionsFacetsResponse" },
example: {
cwds: [
"/Users/son/WebstormProjects/Claude-Code-Agent-Monitor",
"/Users/son/code/another-project",
],
sources: ["local", "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11"],
},
},
},
},
},
},
},
"/api/remote-sources": {
get: {
tags: ["Remote Sources"],
summary: "List remote data sources",
description:
"Returns every configured remote SSH source the dashboard pulls Claude Code history from. NO secrets are ever returned — these records store none (SSH auth defers to the host SSH stack). Each entry carries its last sync `status`, `last_error`, `last_sync_at`, and `last_sync_counts`. Read-only; always 200.",
operationId: "listRemoteSources",
responses: {
200: {
description: "All configured remote sources",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceListResponse" },
example: {
sources: [
{
id: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
label: "Work laptop",
host: "son@studio.local",
ssh_port: 22,
identity_file: "~/.ssh/id_ed25519",
remote_home: "~/.claude",
enabled: true,
status: "ok",
last_error: null,
last_sync_at: "2026-07-22T18:41:55.117Z",
last_sync_counts: {
imported: 9,
skipped: 41,
backfilled: 0,
errors: 0,
sessions_seen: 50,
sessions_tagged: 50,
},
created_at: "2026-07-20T09:15:00.000Z",
updated_at: "2026-07-22T18:41:55.117Z",
},
],
},
},
},
},
},
},
post: {
tags: ["Remote Sources"],
summary: "Register a remote data source",
description:
"Registers a remote SSH source. `label` and `host` are required; `host` is an SSH destination (`user@host`) or a `~/.ssh/config` alias. Optional `ssh_port`, `identity_file`, `remote_home`, and `enabled` fine-tune the connection. No credentials are accepted or stored — auth defers to the host SSH stack. Returns the created source (201). Validation failures return 400 `{ error: { code, message } }` with one of the codes INVALID_LABEL, INVALID_HOST, INVALID_PORT, INVALID_IDENTITY_FILE, INVALID_REMOTE_HOME.",
operationId: "createRemoteSource",
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceCreateRequest" },
examples: {
minimal: {
summary: "Minimal — label + SSH config alias",
value: { label: "Work laptop", host: "studio" },
},
full: {
summary: "Full — explicit port, key, and remote home",
value: {
label: "Work laptop",
host: "son@studio.local",
ssh_port: 22,
identity_file: "~/.ssh/id_ed25519",
remote_home: "~/.claude",
enabled: true,
},
},
},
},
},
},
responses: {
201: {
description: "Remote source created",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceResponse" },
example: {
source: {
id: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
label: "Work laptop",
host: "son@studio.local",
ssh_port: 22,
identity_file: "~/.ssh/id_ed25519",
remote_home: "~/.claude",
enabled: true,
status: "idle",
last_error: null,
last_sync_at: null,
last_sync_counts: null,
created_at: "2026-07-22T18:41:55.117Z",
updated_at: "2026-07-22T18:41:55.117Z",
},
},
},
},
},
400: {
description:
"Validation error (codes: INVALID_LABEL, INVALID_HOST, INVALID_PORT, INVALID_IDENTITY_FILE, INVALID_REMOTE_HOME).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
examples: {
label: {
summary: "Missing/blank label",
value: { error: { code: "INVALID_LABEL", message: "`label` is required" } },
},
host: {
summary: "Missing/invalid host",
value: { error: { code: "INVALID_HOST", message: "`host` is required" } },
},
port: {
summary: "Port out of range",
value: {
error: { code: "INVALID_PORT", message: "`ssh_port` must be 165535" },
},
},
},
},
},
},
},
},
},
"/api/remote-sources/{id}": {
patch: {
tags: ["Remote Sources"],
summary: "Update a remote data source (partial)",
description:
"Partially updates a remote source. Only the keys present in the body are changed; omitted keys are left as-is. The same validation as create applies to any field that is present. Returns the updated source, or 404 when the id is unknown.",
operationId: "updateRemoteSource",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
description: "Remote source id.",
example: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
},
],
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceUpdateRequest" },
examples: {
disable: {
summary: "Disable a source without touching anything else",
value: { enabled: false },
},
rename: {
summary: "Rename and change port",
value: { label: "Studio Mac", ssh_port: 2222 },
},
},
},
},
},
responses: {
200: {
description: "Updated remote source",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceResponse" },
example: {
source: {
id: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
label: "Studio Mac",
host: "son@studio.local",
ssh_port: 2222,
identity_file: "~/.ssh/id_ed25519",
remote_home: "~/.claude",
enabled: false,
status: "ok",
last_error: null,
last_sync_at: "2026-07-22T18:41:55.117Z",
last_sync_counts: {
imported: 9,
skipped: 41,
backfilled: 0,
errors: 0,
sessions_seen: 50,
sessions_tagged: 50,
},
created_at: "2026-07-20T09:15:00.000Z",
updated_at: "2026-07-22T19:02:10.400Z",
},
},
},
},
},
400: {
description:
"Validation error (codes: INVALID_LABEL, INVALID_HOST, INVALID_PORT, INVALID_IDENTITY_FILE, INVALID_REMOTE_HOME).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "INVALID_PORT", message: "`ssh_port` must be 165535" },
},
},
},
},
404: {
description: "Remote source not found",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "NOT_FOUND", message: "Remote source not found" },
},
},
},
},
},
},
delete: {
tags: ["Remote Sources"],
summary: "Delete a remote data source",
description:
"Deletes a remote source. By default its imported sessions are DETACHED — reassigned to the built-in `local` source — so history is preserved. Pass `?purge=true` to instead permanently DELETE that source's imported sessions along with the source. The response reports whether a purge occurred.",
operationId: "deleteRemoteSource",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
description: "Remote source id.",
example: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
},
{
name: "purge",
in: "query",
required: false,
schema: { type: "boolean", default: false },
description:
"When true, also delete this source's imported sessions. When false/omitted, those sessions are reattached to `local`.",
example: true,
},
],
responses: {
200: {
description: "Deleted (with the purge outcome)",
content: {
"application/json": {
schema: {
type: "object",
required: ["ok", "purged"],
properties: {
ok: { type: "boolean", example: true },
purged: {
type: "boolean",
description:
"True when the source's imported sessions were deleted (purge=true); false when they were detached to `local`.",
example: false,
},
},
},
examples: {
detached: {
summary: "Default — sessions detached to local",
value: { ok: true, purged: false },
},
purged: {
summary: "purge=true — sessions deleted",
value: { ok: true, purged: true },
},
},
},
},
},
404: {
description: "Remote source not found",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "NOT_FOUND", message: "Remote source not found" },
},
},
},
},
},
},
},
"/api/remote-sources/{id}/test": {
post: {
tags: ["Remote Sources"],
summary: "Probe SSH connectivity to a remote source",
description:
"Runs an SSH connectivity probe against the source and reports the outcome synchronously. The `ok` flag carries the probe result and `message` is a human-readable summary; on success `remoteProjects` may list the remote project directories discovered under the remote Claude home. This does not import anything — use POST /{id}/sync to pull.",
operationId: "testRemoteSource",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
description: "Remote source id.",
example: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
},
],
responses: {
200: {
description: "Probe result (ok flag carries the connectivity outcome)",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceTestResponse" },
examples: {
success: {
summary: "Reachable",
value: {
ok: true,
message: "Connected; found 24 project directories under ~/.claude/projects.",
remoteProjects: ["-Users-son-code-foo", "-Users-son-code-bar"],
},
},
failure: {
summary: "Unreachable / auth failed",
value: {
ok: false,
message: "ssh: connect to host studio.local port 22: Connection refused",
},
},
},
},
},
},
404: {
description: "Remote source not found",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "NOT_FOUND", message: "Remote source not found" },
},
},
},
},
},
},
},
"/api/remote-sources/{id}/sync": {
post: {
tags: ["Remote Sources"],
summary: "Pull Claude Code history from a remote source now",
description:
"Triggers an immediate pull of Claude Code history from the remote source over SSH, importing new transcripts through the same idempotent, baseline-preserving pipeline used for local imports and tagging imported sessions with this source's id. The response reports the per-run counters. Sync progress/completion is also broadcast over the WebSocket as `remote_source.status` frames.",
operationId: "syncRemoteSource",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "string" },
description: "Remote source id.",
example: "4d1f0e2a-7b9c-4c33-8a21-9e0f7b6d4c11",
},
],
responses: {
200: {
description: "Sync result",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/RemoteSourceSyncResponse" },
example: {
ok: true,
imported: 9,
skipped: 41,
backfilled: 0,
errors: 0,
sessions_seen: 50,
sessions_tagged: 50,
},
},
},
},
404: {
description: "Remote source not found",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "NOT_FOUND", message: "Remote source not found" },
},
},
},
},
500: {
description: "Sync failed",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "SYNC_FAILED", message: "ssh exited with code 255" },
},
},
},
},
},
},
},
"/api/remote-sources/sync-all": {
post: {
tags: ["Remote Sources"],
summary: "Sync all enabled remote sources now",
description:
"Pulls Claude Code history from every enabled remote source over SSH, sequentially (one connection at a time). Per-source failures are isolated — one unreachable source never aborts the others — and each outcome is returned in `results`. Progress/completion is also broadcast over the WebSocket as `remote_source.status` frames. Always returns 200.",
operationId: "syncAllRemoteSources",
responses: {
200: {
description: "Per-source sync outcomes",
content: {
"application/json": {
schema: {
type: "object",
properties: {
ok: { type: "boolean" },
synced: {
type: "integer",
description: "Number of enabled sources that were attempted.",
},
results: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
ok: { type: "boolean" },
error: { type: "string" },
},
},
},
},
},
example: { ok: true, synced: 2, results: [{ id: "src_a", ok: true }] },
},
},
},
},
},
},
"/api/settings/claude-home": {
get: {
tags: ["Settings"],
summary: "Get the active Claude Code home directory",
description:
"Returns the Claude Code home directory the dashboard uses to locate transcripts and settings. Resolves to the CLAUDE_HOME environment variable when set, otherwise `<homedir>/.claude`. Always returns 200.",
operationId: "getClaudeHome",
responses: {
200: {
description: "Current Claude Code home directory",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/SettingsClaudeHomeResponse" },
example: { claude_home: "/Users/son/.claude" },
},
},
},
},
},
put: {
tags: ["Settings"],
summary: "Update the Claude Code home directory",
description:
"Changes the Claude Code home directory used for transcript/settings discovery. A leading `~/` in `path` is expanded to the OS home directory; the resolved value must be an absolute path that exists and is a directory. On success the new value is applied to process.env immediately (so subsequent reads use it) and persisted to the project `.env` file. Returns 400 INVALID_PATH when `path` is missing/not a string, or when the resolved path is not absolute, does not exist, or is not a directory.",
operationId: "updateClaudeHome",
requestBody: {
required: true,
content: {
"application/json": {
schema: { $ref: "#/components/schemas/SettingsClaudeHomeUpdateRequest" },
example: { path: "~/.codefuse/engine/cc" },
},
},
},
responses: {
200: {
description: "Claude Code home updated",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/SettingsClaudeHomeUpdateResponse" },
example: { ok: true, claude_home: "/Users/son/.codefuse/engine/cc" },
},
},
},
400: {
description:
"Invalid path — `path` missing or not a string, or the resolved path is not absolute / does not exist / is not a directory (code INVALID_PATH).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: {
code: "INVALID_PATH",
message: "Directory does not exist: /Users/son/.codefuse/engine/cc",
},
},
},
},
},
},
},
},
"/api/workflows/runs": {
get: {
tags: ["Workflows"],
summary: "List Workflow-tool runs",
description:
"Returns a paginated list of Workflow-tool runs (fleets of sub-agents spawned by the Claude Code 'Workflow' tool / self-paced /loop), newest first. Filter by `status` (the literal `all` is treated as no filter) or by `session_id`; `session_id` takes precedence over `status` when both are supplied. `counts` is always the whole-table breakdown by status. JSON-blob columns (`phases`, `progress`) are parsed into arrays in each run.",
operationId: "listWorkflowRuns",
parameters: [
{
name: "limit",
in: "query",
required: false,
schema: { type: "integer", minimum: 1, maximum: 1000, default: 50 },
description: "Page size, clamped to 11000 (default 50).",
},
{
name: "offset",
in: "query",
required: false,
schema: { type: "integer", minimum: 0, default: 0 },
description: "Pagination offset (clamped to >= 0).",
},
{
name: "status",
in: "query",
required: false,
schema: { type: "string" },
description:
"Filter by run status (open string, e.g. running | completed | error | failed). The literal value `all` is treated as no filter.",
},
{
name: "session_id",
in: "query",
required: false,
schema: { type: "string" },
description:
"Filter to runs launched by this session. Takes precedence over `status` when both are provided.",
},
],
responses: {
200: {
description: "Paginated list of workflow runs with status counts",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/WorkflowRunsListResponse" },
example: {
runs: [
{
run_id: "wf_a1b2c3d4",
session_id: "5f3c0e2a-1b9d-4c77-8a21-9e0f7b6d4c11",
task_id: "ISSUE-167",
name: "Refactor pricing engine",
status: "completed",
default_model: "claude-opus-4-8",
started_at: "2026-06-25T18:04:11.122Z",
ended_at: "2026-06-25T18:09:47.530Z",
duration_ms: 336408,
agent_count: 6,
total_tokens: 1284750,
total_tool_calls: 412,
phases: [{ name: "plan", status: "completed" }],
progress: [{ step: 1, label: "scaffold", done: true }],
script_path: null,
journal_path:
"/Users/son/.claude/projects/-Users-son-code/5f3c0e2a/workflows/wf_a1b2c3d4.json",
source: "journal",
created_at: "2026-06-25T18:09:48.001Z",
updated_at: "2026-06-25T18:09:48.001Z",
},
],
total: 42,
counts: { completed: 30, error: 5, running: 7 },
limit: 50,
offset: 0,
},
},
},
},
500: {
description: "Failed to list workflow runs (code WORKFLOW_LIST_FAILED).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "WORKFLOW_LIST_FAILED", message: "no such table: workflows" },
},
},
},
},
},
},
},
"/api/workflows/runs/{runId}": {
get: {
tags: ["Workflows"],
summary: "Get a Workflow-tool run with its agents and events",
description:
"Returns one Workflow-tool run (by `run_id`) together with its linked inner agents and the events attributed to those agents (chronological, capped at 5000 rows). The run's JSON-blob columns (`phases`, `progress`) are parsed into arrays. Returns 404 WORKFLOW_NOT_FOUND when no run matches the id.",
operationId: "getWorkflowRun",
parameters: [
{
name: "runId",
in: "path",
required: true,
schema: { type: "string" },
description: "The workflow run id (workflows.run_id).",
},
],
responses: {
200: {
description: "Workflow run with inner agents and their events",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/WorkflowRunDetailResponse" },
example: {
workflow: {
run_id: "wf_a1b2c3d4",
session_id: "5f3c0e2a-1b9d-4c77-8a21-9e0f7b6d4c11",
task_id: "ISSUE-167",
name: "Refactor pricing engine",
status: "completed",
default_model: "claude-opus-4-8",
started_at: "2026-06-25T18:04:11.122Z",
ended_at: "2026-06-25T18:09:47.530Z",
duration_ms: 336408,
agent_count: 6,
total_tokens: 1284750,
total_tool_calls: 412,
phases: [{ name: "plan", status: "completed" }],
progress: [{ step: 1, label: "scaffold", done: true }],
script_path: null,
journal_path:
"/Users/son/.claude/projects/-Users-son-code/5f3c0e2a/workflows/wf_a1b2c3d4.json",
source: "journal",
created_at: "2026-06-25T18:09:48.001Z",
updated_at: "2026-06-25T18:09:48.001Z",
},
agents: [
{
id: "agent-7f1c",
session_id: "5f3c0e2a-1b9d-4c77-8a21-9e0f7b6d4c11",
name: "implementer",
type: "subagent",
subagent_type: "general-purpose",
status: "completed",
task: "Implement pricing changes",
current_tool: null,
started_at: "2026-06-25T18:04:30.000Z",
ended_at: "2026-06-25T18:08:12.000Z",
parent_agent_id: null,
metadata: null,
updated_at: "2026-06-25T18:08:12.000Z",
awaiting_input_since: null,
awaiting_reason: null,
},
],
events: [
{
id: 90211,
session_id: "5f3c0e2a-1b9d-4c77-8a21-9e0f7b6d4c11",
agent_id: "agent-7f1c",
event_type: "PostToolUse",
tool_name: "Edit",
summary: "Edited server/routes/pricing.js",
data: null,
created_at: "2026-06-25T18:05:02.144Z",
},
],
},
},
},
},
404: {
description: "No workflow run matches the id (code WORKFLOW_NOT_FOUND).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "WORKFLOW_NOT_FOUND", message: "Workflow run not found" },
},
},
},
},
500: {
description: "Failed to load workflow run detail (code WORKFLOW_DETAIL_FAILED).",
content: {
"application/json": {
schema: { $ref: "#/components/schemas/ErrorResponse" },
example: {
error: { code: "WORKFLOW_DETAIL_FAILED", message: "database is locked" },
},
},
},
},
},
},
},
};
module.exports = { tags, schemas, paths };