code-coordinator 0.5.46__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_coordinator-0.5.46.dist-info/METADATA +625 -0
- code_coordinator-0.5.46.dist-info/RECORD +295 -0
- code_coordinator-0.5.46.dist-info/WHEEL +5 -0
- code_coordinator-0.5.46.dist-info/entry_points.txt +2 -0
- code_coordinator-0.5.46.dist-info/licenses/LICENSE +110 -0
- code_coordinator-0.5.46.dist-info/top_level.txt +1 -0
- coord/__init__.py +176 -0
- coord/_board_mapping.py +229 -0
- coord/acceptance.py +468 -0
- coord/acceptance_drivers.py +632 -0
- coord/agent.py +7517 -0
- coord/agent_app.py +1555 -0
- coord/agent_update.py +417 -0
- coord/agents/opencode/.gitignore +13 -0
- coord/agents/opencode/agents/work.md +129 -0
- coord/agents/opencode/routing.jsonc +49 -0
- coord/audit.py +301 -0
- coord/auto_loop.py +1440 -0
- coord/board_bool_guard.py +72 -0
- coord/board_service.py +141 -0
- coord/board_wire.py +309 -0
- coord/brain.py +581 -0
- coord/branch_model.py +214 -0
- coord/cargo_cache.py +258 -0
- coord/ci_github.py +386 -0
- coord/ci_store.py +560 -0
- coord/claim.py +353 -0
- coord/cli.py +454 -0
- coord/client.py +610 -0
- coord/commands/__init__.py +1 -0
- coord/commands/_common.py +329 -0
- coord/commands/acceptance.py +916 -0
- coord/commands/agent_ops.py +1339 -0
- coord/commands/audit.py +131 -0
- coord/commands/chat.py +320 -0
- coord/commands/dispatch.py +1780 -0
- coord/commands/dispatch_workers.py +4894 -0
- coord/commands/drive.py +616 -0
- coord/commands/drive_queue.py +1203 -0
- coord/commands/gate_a.py +217 -0
- coord/commands/gates.py +89 -0
- coord/commands/issues.py +681 -0
- coord/commands/lifecycle.py +513 -0
- coord/commands/merge.py +1900 -0
- coord/commands/milestone.py +2081 -0
- coord/commands/plan_followup.py +1243 -0
- coord/commands/plans.py +156 -0
- coord/commands/release.py +2232 -0
- coord/commands/report.py +341 -0
- coord/commands/review.py +1523 -0
- coord/commands/scorecard.py +252 -0
- coord/commands/sessions.py +1930 -0
- coord/commands/setup.py +576 -0
- coord/commands/status.py +2089 -0
- coord/commands/terminal.py +385 -0
- coord/commands/test_gate.py +775 -0
- coord/commands/tui.py +288 -0
- coord/comments.py +718 -0
- coord/config.py +3032 -0
- coord/conflict_fix.py +633 -0
- coord/dao.py +483 -0
- coord/dashboard/__init__.py +0 -0
- coord/dashboard/fixture.py +376 -0
- coord/dashboard/index.html +658 -0
- coord/dashboard/server.py +1894 -0
- coord/dashboard/terminal.py +382 -0
- coord/dashboard/webapp/.gitignore +9 -0
- coord/dashboard/webapp/components.json +17 -0
- coord/dashboard/webapp/dist/assets/Gallery-da3qNiIw.js +71 -0
- coord/dashboard/webapp/dist/assets/Terminal-9CEnUXvW.css +32 -0
- coord/dashboard/webapp/dist/assets/Terminal-skVFCxPU.js +63 -0
- coord/dashboard/webapp/dist/assets/index-DltfZR5f.js +184 -0
- coord/dashboard/webapp/dist/assets/index-Dq4kwTdw.css +1 -0
- coord/dashboard/webapp/dist/assets/workbox-window.prod.es5-BqEJf4Xk.js +2 -0
- coord/dashboard/webapp/dist/icons/icon-192.png +0 -0
- coord/dashboard/webapp/dist/icons/icon-512.png +0 -0
- coord/dashboard/webapp/dist/icons/icon.svg +5 -0
- coord/dashboard/webapp/dist/index.html +38 -0
- coord/dashboard/webapp/dist/manifest.webmanifest +1 -0
- coord/dashboard/webapp/dist/sw.js +1 -0
- coord/dashboard/webapp/dist/workbox-e4022e15.js +1 -0
- coord/dashboard/webapp/e2e/available-gates-terminal.spec.ts +75 -0
- coord/dashboard/webapp/e2e/deep-link.spec.ts +172 -0
- coord/dashboard/webapp/e2e/fixtureServer.ts +155 -0
- coord/dashboard/webapp/e2e/live-update-fixture.spec.ts +113 -0
- coord/dashboard/webapp/e2e/realtime.spec.ts +238 -0
- coord/dashboard/webapp/e2e/shell.spec.ts +309 -0
- coord/dashboard/webapp/e2e/smoke.spec.ts +191 -0
- coord/dashboard/webapp/e2e/terminal.spec.ts +420 -0
- coord/dashboard/webapp/e2e/theme.spec.ts +138 -0
- coord/dashboard/webapp/eslint.config.js +20 -0
- coord/dashboard/webapp/index.html +37 -0
- coord/dashboard/webapp/node_modules/flatted/python/flatted.py +144 -0
- coord/dashboard/webapp/package-lock.json +10584 -0
- coord/dashboard/webapp/package.json +63 -0
- coord/dashboard/webapp/playwright.acceptance.config.ts +166 -0
- coord/dashboard/webapp/playwright.config.ts +93 -0
- coord/dashboard/webapp/postcss.config.js +6 -0
- coord/dashboard/webapp/public/icons/icon-192.png +0 -0
- coord/dashboard/webapp/public/icons/icon-512.png +0 -0
- coord/dashboard/webapp/public/icons/icon.svg +5 -0
- coord/dashboard/webapp/src/App.tsx +140 -0
- coord/dashboard/webapp/src/api/client.ts +199 -0
- coord/dashboard/webapp/src/api/generated.ts +176 -0
- coord/dashboard/webapp/src/components/ConnectionBadge.tsx +52 -0
- coord/dashboard/webapp/src/components/Detail.tsx +800 -0
- coord/dashboard/webapp/src/components/Gallery.tsx +341 -0
- coord/dashboard/webapp/src/components/Home.tsx +435 -0
- coord/dashboard/webapp/src/components/MobileKeyBar.tsx +280 -0
- coord/dashboard/webapp/src/components/PanelHeader.tsx +59 -0
- coord/dashboard/webapp/src/components/PipelineCard.tsx +168 -0
- coord/dashboard/webapp/src/components/SessionCard.tsx +99 -0
- coord/dashboard/webapp/src/components/SessionDetail.tsx +140 -0
- coord/dashboard/webapp/src/components/SessionsList.tsx +81 -0
- coord/dashboard/webapp/src/components/Terminal.tsx +376 -0
- coord/dashboard/webapp/src/components/__tests__/ConnectionBadge.test.tsx +81 -0
- coord/dashboard/webapp/src/components/__tests__/Detail.test.tsx +680 -0
- coord/dashboard/webapp/src/components/__tests__/Gallery.test.tsx +83 -0
- coord/dashboard/webapp/src/components/__tests__/Home.test.tsx +271 -0
- coord/dashboard/webapp/src/components/__tests__/MobileKeyBar.test.tsx +197 -0
- coord/dashboard/webapp/src/components/__tests__/PipelineCard.test.tsx +143 -0
- coord/dashboard/webapp/src/components/__tests__/SessionCard.test.tsx +106 -0
- coord/dashboard/webapp/src/components/__tests__/Terminal.test.tsx +504 -0
- coord/dashboard/webapp/src/components/ui/badge.tsx +41 -0
- coord/dashboard/webapp/src/components/ui/button.tsx +54 -0
- coord/dashboard/webapp/src/components/ui/card.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/dialog.tsx +99 -0
- coord/dashboard/webapp/src/components/ui/dropdown-menu.tsx +189 -0
- coord/dashboard/webapp/src/components/ui/empty-state.tsx +35 -0
- coord/dashboard/webapp/src/components/ui/sheet.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/skeleton.tsx +9 -0
- coord/dashboard/webapp/src/components/ui/tabs.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/theme-provider.tsx +78 -0
- coord/dashboard/webapp/src/components/ui/theme-toggle.tsx +20 -0
- coord/dashboard/webapp/src/components/ui/toast.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/toaster.tsx +30 -0
- coord/dashboard/webapp/src/components/ui/tooltip.tsx +26 -0
- coord/dashboard/webapp/src/components/ui/use-toast.ts +134 -0
- coord/dashboard/webapp/src/index.css +210 -0
- coord/dashboard/webapp/src/lib/pipeline.ts +29 -0
- coord/dashboard/webapp/src/lib/utils.ts +6 -0
- coord/dashboard/webapp/src/main.tsx +46 -0
- coord/dashboard/webapp/src/realtime/RealtimeProvider.tsx +112 -0
- coord/dashboard/webapp/src/realtime/__tests__/RealtimeProvider.test.tsx +189 -0
- coord/dashboard/webapp/src/realtime/__tests__/connection.test.ts +255 -0
- coord/dashboard/webapp/src/realtime/connection.ts +227 -0
- coord/dashboard/webapp/src/realtime/events.ts +100 -0
- coord/dashboard/webapp/src/routes/__tests__/paths.test.ts +92 -0
- coord/dashboard/webapp/src/routes/paths.ts +92 -0
- coord/dashboard/webapp/src/shell/ActivityRail.tsx +335 -0
- coord/dashboard/webapp/src/shell/AppShell.tsx +276 -0
- coord/dashboard/webapp/src/shell/ComingSoon.tsx +33 -0
- coord/dashboard/webapp/src/shell/EmptyDetail.tsx +26 -0
- coord/dashboard/webapp/src/shell/RouteNotFound.tsx +33 -0
- coord/dashboard/webapp/src/shell/ShellLayout.tsx +147 -0
- coord/dashboard/webapp/src/shell/StatusBar.tsx +46 -0
- coord/dashboard/webapp/src/shell/__tests__/ShellLayout.test.tsx +520 -0
- coord/dashboard/webapp/src/shell/__tests__/shellState.test.ts +95 -0
- coord/dashboard/webapp/src/shell/__tests__/stubViewport.ts +40 -0
- coord/dashboard/webapp/src/shell/breakpoints.ts +87 -0
- coord/dashboard/webapp/src/shell/railItems.ts +105 -0
- coord/dashboard/webapp/src/shell/shellState.ts +174 -0
- coord/dashboard/webapp/src/shell/useRegionFocus.ts +95 -0
- coord/dashboard/webapp/src/test-setup.ts +41 -0
- coord/dashboard/webapp/src/vite-env.d.ts +2 -0
- coord/dashboard/webapp/tailwind.config.js +140 -0
- coord/dashboard/webapp/tsconfig.json +25 -0
- coord/dashboard/webapp/tsconfig.node.json +11 -0
- coord/dashboard/webapp/vite.config.ts +71 -0
- coord/db.py +1076 -0
- coord/dead_end.py +332 -0
- coord/deploy/README.md +33 -0
- coord/deploy/coord-agent.service +89 -0
- coord/deploy/coord-db-backup.service +60 -0
- coord/deploy/coord-db-backup.sh +74 -0
- coord/deploy/coord-db-backup.timer +18 -0
- coord/deploy/coord-drive-queue.service +117 -0
- coord/deploy/coord-drive-queue.timer +39 -0
- coord/deploy/coord-notify.service +48 -0
- coord/deploy/coord-notify.timer +24 -0
- coord/deploy/coord-release-propagate.service +83 -0
- coord/deploy/coord-release-propagate.timer +38 -0
- coord/deploy/coord-release-window.service +119 -0
- coord/deploy/coord-release-window.timer +36 -0
- coord/deploy/coord-serve.service +82 -0
- coord/deploy/coord-web-dist-build.service +43 -0
- coord/deploy/coord-web-dist-build.timer +36 -0
- coord/deploy/coord-web.service +125 -0
- coord/deploy_manifest.py +80 -0
- coord/deploy_units.py +384 -0
- coord/deps.py +115 -0
- coord/diagnose.py +1623 -0
- coord/dispatch.py +1009 -0
- coord/dist_name.py +123 -0
- coord/drive.py +3101 -0
- coord/drive_queue.py +2298 -0
- coord/drive_state.py +870 -0
- coord/events.py +381 -0
- coord/failure_class.py +914 -0
- coord/filelock.py +168 -0
- coord/fleet_config_health.py +300 -0
- coord/freshness.py +206 -0
- coord/gate_a.py +469 -0
- coord/gate_b.py +411 -0
- coord/gate_snapshot.py +385 -0
- coord/gates.py +582 -0
- coord/github_ops.py +1954 -0
- coord/goal.py +125 -0
- coord/graph_health.py +348 -0
- coord/health/__init__.py +69 -0
- coord/health/aggregate.py +129 -0
- coord/health/checks/__init__.py +13 -0
- coord/health/checks/agent_install.py +280 -0
- coord/health/checks/cargo_targets.py +171 -0
- coord/health/checks/claude_binary.py +65 -0
- coord/health/checks/deploy_lane_facts.py +458 -0
- coord/health/checks/disk.py +99 -0
- coord/health/checks/fleet_board.py +89 -0
- coord/health/checks/fleet_deploy_lanes.py +469 -0
- coord/health/checks/fleet_phantom.py +69 -0
- coord/health/checks/fleet_unit_drift.py +151 -0
- coord/health/checks/graph.py +192 -0
- coord/health/checks/plan_usage.py +88 -0
- coord/health/checks/repo_state.py +161 -0
- coord/health/checks/spawned_coord.py +465 -0
- coord/health/checks/timer_active.py +254 -0
- coord/health/checks/toolchain.py +547 -0
- coord/health/checks/unit_drift.py +648 -0
- coord/health/checks/unit_enablement.py +171 -0
- coord/health/checks/worktrees.py +96 -0
- coord/health/cli.py +121 -0
- coord/health/context.py +106 -0
- coord/health/fleet_snapshot.py +477 -0
- coord/health/models.py +250 -0
- coord/health/pypi.py +231 -0
- coord/health/registry.py +240 -0
- coord/health/render.py +82 -0
- coord/health/units.py +60 -0
- coord/hooks.py +106 -0
- coord/housekeeping.py +204 -0
- coord/interactive.py +4286 -0
- coord/issue_store.py +1496 -0
- coord/liveness_auditor.py +293 -0
- coord/machine_pause.py +755 -0
- coord/merge_queue.py +4681 -0
- coord/milestone_chat.py +600 -0
- coord/milestone_dispatch.py +943 -0
- coord/milestone_gate.py +709 -0
- coord/milestone_order.py +840 -0
- coord/mock_author.py +334 -0
- coord/models.py +891 -0
- coord/network.py +269 -0
- coord/new_issue_chat.py +229 -0
- coord/notify.py +3226 -0
- coord/openapi.py +404 -0
- coord/overlap_fence.py +133 -0
- coord/parentage.py +200 -0
- coord/parentage_github.py +58 -0
- coord/pipeline.py +481 -0
- coord/plan_parser.py +266 -0
- coord/plans.py +543 -0
- coord/platform_paths.py +43 -0
- coord/pr_body_lint.py +67 -0
- coord/prereqs.py +533 -0
- coord/progress.py +425 -0
- coord/providers/__init__.py +683 -0
- coord/providers/base.py +218 -0
- coord/providers/claude.py +284 -0
- coord/providers/claude_pty.py +610 -0
- coord/providers/opencode.py +896 -0
- coord/reconcile.py +2233 -0
- coord/refine_chat.py +485 -0
- coord/release_cordon.py +525 -0
- coord/release_propagate.py +1176 -0
- coord/release_verify.py +777 -0
- coord/release_window.py +322 -0
- coord/reports.py +1643 -0
- coord/revalidate.py +1101 -0
- coord/review.py +3317 -0
- coord/scorecard.py +484 -0
- coord/serve_app.py +7192 -0
- coord/skills/update-issue/SKILL.md +93 -0
- coord/smoke.py +1030 -0
- coord/split_work.py +210 -0
- coord/stage_projection.py +650 -0
- coord/state.py +5720 -0
- coord/test_author.py +1064 -0
- coord/test_chat.py +352 -0
- coord/test_orchestrator.py +494 -0
- coord/test_report.py +178 -0
- coord/tui_release.py +271 -0
- coord/usage.py +753 -0
- coord/usage_limits.py +358 -0
- coord/usage_rollup.py +709 -0
- coord/worker_events.py +954 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed API client for coord/dashboard/server.py.
|
|
3
|
+
*
|
|
4
|
+
* The wire types themselves (`Assignment`, `PipelineStage`, `PipelineGate`,
|
|
5
|
+
* `PipelineView`, `AssignmentStatus`, `AssignmentType`, `TestVerdict`,
|
|
6
|
+
* `PipelineAction`) are generated from the dashboard's OpenAPI spec — see
|
|
7
|
+
* `./generated.ts` and `scripts/codegen.py` (#750, moved onto the OpenAPI
|
|
8
|
+
* spec by #1550). Regenerate with `.venv/bin/python scripts/codegen.py`
|
|
9
|
+
* after any Python dataclass field change; do not hand-edit the generated
|
|
10
|
+
* file.
|
|
11
|
+
*
|
|
12
|
+
* Actions marked "(forthcoming)" in `PipelineAction`'s doc comment are
|
|
13
|
+
* defined ahead of their backend implementation so TypeScript callers can
|
|
14
|
+
* reference them; they will return HTTP 501 until the matching server PR
|
|
15
|
+
* merges.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
Assignment,
|
|
20
|
+
AssignmentStatus,
|
|
21
|
+
AssignmentType,
|
|
22
|
+
PipelineAction,
|
|
23
|
+
PipelineGate,
|
|
24
|
+
PipelineStage,
|
|
25
|
+
PipelineView,
|
|
26
|
+
TestVerdict,
|
|
27
|
+
} from './generated'
|
|
28
|
+
|
|
29
|
+
export type {
|
|
30
|
+
Assignment,
|
|
31
|
+
AssignmentStatus,
|
|
32
|
+
AssignmentType,
|
|
33
|
+
PipelineAction,
|
|
34
|
+
PipelineGate,
|
|
35
|
+
PipelineStage,
|
|
36
|
+
PipelineView,
|
|
37
|
+
TestVerdict,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── GET /api/board ────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export interface BoardData {
|
|
43
|
+
round_number: number
|
|
44
|
+
active: Assignment[]
|
|
45
|
+
/** Last 20 completed assignments. */
|
|
46
|
+
completed: Assignment[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── GET /api/sessions ────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* One live `coord-*` interactive tmux session the phone can attach to via
|
|
53
|
+
* `GET /ws/terminal/{session_id}` (#1066). Hand-written rather than
|
|
54
|
+
* generated: the server builds this as a plain dict in `api_sessions`
|
|
55
|
+
* (`coord/dashboard/server.py`), not from a Python dataclass, so it isn't a
|
|
56
|
+
* `scripts/codegen.py` (#750) target — keep this in sync with the
|
|
57
|
+
* `session_response` OpenAPI schema in `_openapi_spec()` by hand.
|
|
58
|
+
*/
|
|
59
|
+
export interface SessionInfo {
|
|
60
|
+
/** == the assignment_id; also the `/ws/terminal/{session_id}` path param. */
|
|
61
|
+
session_id: string
|
|
62
|
+
/** The tmux session name, `coord-<session_id>`. */
|
|
63
|
+
session_name: string
|
|
64
|
+
machine: string | null
|
|
65
|
+
/** The machine's Tailscale host. */
|
|
66
|
+
host: string | null
|
|
67
|
+
repo: string | null
|
|
68
|
+
issue: number | null
|
|
69
|
+
issue_title: string | null
|
|
70
|
+
/** Assignment type — see `AssignmentType` in `./generated` for the real value set. */
|
|
71
|
+
stage: string | null
|
|
72
|
+
/** Assignment status — running/done/failed/advisory/... */
|
|
73
|
+
status: string | null
|
|
74
|
+
/** Is a client currently attached to the tmux session. */
|
|
75
|
+
attached: boolean
|
|
76
|
+
/** claude has exited but the tmux session is still up. */
|
|
77
|
+
pane_dead: boolean
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── GET /api/diff/{id} ────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export interface DiffResult {
|
|
83
|
+
diff: string
|
|
84
|
+
/** "pr" when fetched from a GitHub PR; "compare" when fetched from the compare API. */
|
|
85
|
+
source: 'pr' | 'compare'
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── POST /api/pipeline/action ─────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
export interface PipelineActionRequest {
|
|
91
|
+
assignment_id: string
|
|
92
|
+
action: PipelineAction
|
|
93
|
+
/** Additional payload fields for specific actions (e.g. verdict for test-verdict). */
|
|
94
|
+
[key: string]: unknown
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface PipelineActionResult {
|
|
98
|
+
ok: boolean
|
|
99
|
+
error?: string
|
|
100
|
+
/** Machine that accepted the dispatched assignment (dispatch_review / dispatch_smoke). */
|
|
101
|
+
machine_name?: string
|
|
102
|
+
/** Assignment ID created by a dispatch action. */
|
|
103
|
+
assignment_id?: string
|
|
104
|
+
/** Events produced by a merge action. */
|
|
105
|
+
events?: Array<{ kind: string; message: string }>
|
|
106
|
+
/** Human-readable detail (post_findings). */
|
|
107
|
+
detail?: string
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/** Same-origin base — webapp is served by coord/dashboard/server.py. */
|
|
113
|
+
const API_BASE = ''
|
|
114
|
+
|
|
115
|
+
async function apiFetch<T>(path: string, init?: RequestInit): Promise<T> {
|
|
116
|
+
const res = await fetch(`${API_BASE}${path}`, init)
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
const text = await res.text().catch(() => '')
|
|
119
|
+
throw new Error(`${init?.method ?? 'GET'} ${path} → HTTP ${res.status}: ${text}`)
|
|
120
|
+
}
|
|
121
|
+
return res.json() as Promise<T>
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
/** Fetch the full board state (active + last-20 completed assignments). */
|
|
127
|
+
export async function fetchBoard(): Promise<BoardData> {
|
|
128
|
+
return apiFetch<BoardData>('/api/board')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Fetch pipeline views for all work-type assignments. */
|
|
132
|
+
export async function fetchPipeline(): Promise<PipelineView[]> {
|
|
133
|
+
return apiFetch<PipelineView[]>('/api/pipeline')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Fetch live coord-* interactive sessions the phone can take over (#1066). */
|
|
137
|
+
export async function fetchSessions(): Promise<SessionInfo[]> {
|
|
138
|
+
return apiFetch<SessionInfo[]>('/api/sessions')
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Fetch the diff for a completed work assignment.
|
|
143
|
+
* Prefers the GitHub PR diff; falls back to the compare API.
|
|
144
|
+
*/
|
|
145
|
+
export async function fetchDiff(assignmentId: string): Promise<DiffResult> {
|
|
146
|
+
return apiFetch<DiffResult>(`/api/diff/${encodeURIComponent(assignmentId)}`)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Advance an assignment through a pipeline gate. */
|
|
150
|
+
export async function pipelineAction(
|
|
151
|
+
body: PipelineActionRequest,
|
|
152
|
+
): Promise<PipelineActionResult> {
|
|
153
|
+
const res = await fetch(`${API_BASE}/api/pipeline/action`, {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
body: JSON.stringify(body),
|
|
157
|
+
})
|
|
158
|
+
const data = (await res.json()) as PipelineActionResult
|
|
159
|
+
if (!res.ok) {
|
|
160
|
+
return { ok: false, error: data.error ?? `HTTP ${res.status}` }
|
|
161
|
+
}
|
|
162
|
+
return data
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── WS /ws/terminal/{session_id} ────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Key `coord web`'s optional bearer token (`COORD_WEB_TOKEN` /
|
|
169
|
+
* `~/.coord/web_token`, see `coord.dashboard.terminal.resolve_web_token`)
|
|
170
|
+
* is stashed in `localStorage`, when the operator has set one.
|
|
171
|
+
*
|
|
172
|
+
* There's no in-app UI to populate this yet (#1068 is the terminal pane
|
|
173
|
+
* itself, not a settings screen) — for now it's set by hand from the
|
|
174
|
+
* browser devtools console:
|
|
175
|
+
* `localStorage.setItem('coord_web_token', '<token>')`
|
|
176
|
+
* When unset, the WS connects without `?token=`, which matches the
|
|
177
|
+
* server's "no token configured => open" convention.
|
|
178
|
+
*/
|
|
179
|
+
const WEB_TOKEN_STORAGE_KEY = 'coord_web_token'
|
|
180
|
+
|
|
181
|
+
function resolveWebToken(): string | null {
|
|
182
|
+
try {
|
|
183
|
+
return window.localStorage.getItem(WEB_TOKEN_STORAGE_KEY)
|
|
184
|
+
} catch {
|
|
185
|
+
return null
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Build the `/ws/terminal/{session_id}` URL (#1065's PTY<->WebSocket
|
|
191
|
+
* bridge) for the given session, same-origin, ws/wss matching the page's
|
|
192
|
+
* http/https scheme, with `?token=` appended when one is configured.
|
|
193
|
+
*/
|
|
194
|
+
export function terminalWebSocketUrl(sessionId: string): string {
|
|
195
|
+
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
196
|
+
const token = resolveWebToken()
|
|
197
|
+
const query = token ? `?token=${encodeURIComponent(token)}` : ''
|
|
198
|
+
return `${proto}//${window.location.host}/ws/terminal/${encodeURIComponent(sessionId)}${query}`
|
|
199
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED — DO NOT EDIT BY HAND.
|
|
3
|
+
*
|
|
4
|
+
* Generated by `scripts/codegen.py` from the dashboard's OpenAPI 3 spec
|
|
5
|
+
* (`coord.dashboard.server.openapi_spec()`, itself built by
|
|
6
|
+
* `coord/openapi.py` from `coord/models.py` / `coord/pipeline.py`) — #1550
|
|
7
|
+
* (originally #750). Regenerate after any field change:
|
|
8
|
+
*
|
|
9
|
+
* .venv/bin/python scripts/codegen.py
|
|
10
|
+
*
|
|
11
|
+
* `tests/test_generated_types_fixture.py` fails CI if this file drifts from
|
|
12
|
+
* what the generator produces right now, so a stale checkout can't merge.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export type AssignmentStatus =
|
|
16
|
+
| 'pending'
|
|
17
|
+
| 'running'
|
|
18
|
+
| 'done'
|
|
19
|
+
| 'failed'
|
|
20
|
+
| 'cancelled'
|
|
21
|
+
| 'advisory'
|
|
22
|
+
| 'merged'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* coord/models.py Assignment.type's real value set — #1550 found this had
|
|
26
|
+
* drifted: the hand-authored enum this replaces listed 'merge' and 'fix',
|
|
27
|
+
* neither of which is ever a literal `type=` value (coord/config.py's #1137
|
|
28
|
+
* audit note: a dedicated `type="merge"` was tried and reverted; `type="fix"`
|
|
29
|
+
* was deliberately never introduced — both share `type="work"` with their
|
|
30
|
+
* headless counterpart and are distinguished by `provider_name`/
|
|
31
|
+
* `review_of_assignment_id` instead, see `attention_threshold_for`) — while
|
|
32
|
+
* missing seven values that are real: 'audit' (coord/models.py docstring,
|
|
33
|
+
* #885 --audit-of), and the six interactive session types from
|
|
34
|
+
* coord/config.py's `INTERACTIVE_SESSION_TYPES` plus the two headless
|
|
35
|
+
* lightweight-worker types from `_DEFAULT_ATTENTION_THRESHOLDS`.
|
|
36
|
+
*/
|
|
37
|
+
export type AssignmentType =
|
|
38
|
+
| 'work'
|
|
39
|
+
| 'review'
|
|
40
|
+
| 'plan'
|
|
41
|
+
| 'smoke'
|
|
42
|
+
| 'conflict-fix'
|
|
43
|
+
| 'mock-author'
|
|
44
|
+
| 'test-author'
|
|
45
|
+
| 'audit'
|
|
46
|
+
| 'chat'
|
|
47
|
+
| 'troubleshoot'
|
|
48
|
+
| 'milestone-chat'
|
|
49
|
+
| 'refinement'
|
|
50
|
+
| 'new-issue-chat'
|
|
51
|
+
| 'test-chat'
|
|
52
|
+
|
|
53
|
+
export type TestVerdict = 'passed' | 'failed' | 'skipped' | 'running'
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Actions supported by POST /api/pipeline/action.
|
|
57
|
+
*
|
|
58
|
+
* dispatch_review — kick off an adversarial review assignment
|
|
59
|
+
* dispatch_smoke — kick off a smoke-test assignment
|
|
60
|
+
* enqueue — add to merge queue
|
|
61
|
+
* merge — merge a queued PR (must be in "pending" state)
|
|
62
|
+
* post_findings — post orphaned review findings to GitHub
|
|
63
|
+
* unstick — cancel a stuck assignment and mark it failed
|
|
64
|
+
* retry — (forthcoming) retry a failed work assignment
|
|
65
|
+
* dispatch_fix — (forthcoming) dispatch a fix for a test failure / review request-changes
|
|
66
|
+
* test-verdict — (forthcoming) record passed/failed/skipped test verdict
|
|
67
|
+
* record-review-verdict — (forthcoming) record an approved/changes-requested review verdict
|
|
68
|
+
*/
|
|
69
|
+
export type PipelineAction =
|
|
70
|
+
| 'dispatch_review'
|
|
71
|
+
| 'dispatch_smoke'
|
|
72
|
+
| 'enqueue'
|
|
73
|
+
| 'merge'
|
|
74
|
+
| 'post_findings'
|
|
75
|
+
| 'unstick'
|
|
76
|
+
| 'retry'
|
|
77
|
+
| 'dispatch_fix'
|
|
78
|
+
| 'test-verdict'
|
|
79
|
+
| 'record-review-verdict'
|
|
80
|
+
|
|
81
|
+
export interface PipelineStage {
|
|
82
|
+
name: string
|
|
83
|
+
status: 'active' | 'completed' | 'skipped' | 'waiting'
|
|
84
|
+
is_current: boolean
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface PipelineGate {
|
|
88
|
+
action: PipelineAction
|
|
89
|
+
label: string
|
|
90
|
+
endpoint: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface PipelineView {
|
|
94
|
+
assignment_id: string
|
|
95
|
+
issue_number: number
|
|
96
|
+
issue_title: string
|
|
97
|
+
repo_name: string
|
|
98
|
+
machine_name: string
|
|
99
|
+
stages: PipelineStage[]
|
|
100
|
+
current_stage: string
|
|
101
|
+
available_gates: PipelineGate[]
|
|
102
|
+
progress_pct: number
|
|
103
|
+
review_findings_pending: boolean
|
|
104
|
+
review_verdict: 'approve' | 'request-changes' | null
|
|
105
|
+
review_verdict_original: string | null
|
|
106
|
+
review_verdict_override_reason: string | null
|
|
107
|
+
review_findings_body: string | null
|
|
108
|
+
test_verdict: TestVerdict | null
|
|
109
|
+
needs_attention: boolean
|
|
110
|
+
needs_attention_reason: string | null
|
|
111
|
+
needs_attention_detail: string | null
|
|
112
|
+
finished_at: number | null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface Assignment {
|
|
116
|
+
machine_name: string
|
|
117
|
+
repo_name: string
|
|
118
|
+
issue_number: number
|
|
119
|
+
issue_title: string
|
|
120
|
+
files_allowed: string[]
|
|
121
|
+
files_forbidden: string[]
|
|
122
|
+
briefing: string
|
|
123
|
+
assignment_id: string | null
|
|
124
|
+
status: AssignmentStatus
|
|
125
|
+
branch: string | null
|
|
126
|
+
pr_url: string | null
|
|
127
|
+
dispatched_at: number | null
|
|
128
|
+
finished_at: number | null
|
|
129
|
+
smoke_test: 'pass' | 'fail' | null
|
|
130
|
+
smoke_test_reason: string | null
|
|
131
|
+
type: AssignmentType
|
|
132
|
+
review_target: string | null
|
|
133
|
+
review_of_assignment_id: string | null
|
|
134
|
+
unreachable_count: number
|
|
135
|
+
model: string | null
|
|
136
|
+
plan: Record<string, unknown> | null
|
|
137
|
+
review_state: 'pending' | 'dispatched' | 'done' | null
|
|
138
|
+
review_dispatch_reason: string | null
|
|
139
|
+
required_gates: string[]
|
|
140
|
+
review_iteration: number
|
|
141
|
+
review_posted_at: number | null
|
|
142
|
+
test_state: TestVerdict | null
|
|
143
|
+
test_reason: string | null
|
|
144
|
+
test_head_sha: string | null
|
|
145
|
+
test_patch_id: string | null
|
|
146
|
+
test_base_sha: string | null
|
|
147
|
+
test_toolchain: string | null
|
|
148
|
+
review_verdict: 'approve' | 'request-changes' | null
|
|
149
|
+
review_verdict_original: string | null
|
|
150
|
+
review_verdict_override_reason: string | null
|
|
151
|
+
verdict_source: string | null
|
|
152
|
+
verdict_source_reason: string | null
|
|
153
|
+
review_head_sha: string | null
|
|
154
|
+
review_patch_id: string | null
|
|
155
|
+
review_scoped: boolean
|
|
156
|
+
review_scope_base_sha: string | null
|
|
157
|
+
cost_usd: number | null
|
|
158
|
+
smoke_tests: string[] | null
|
|
159
|
+
provider_name: string | null
|
|
160
|
+
input_tokens: number
|
|
161
|
+
output_tokens: number
|
|
162
|
+
cache_creation_tokens: number
|
|
163
|
+
cache_read_tokens: number
|
|
164
|
+
failure_reason: string | null
|
|
165
|
+
acceptance_state: string | null
|
|
166
|
+
acceptance_reason: string | null
|
|
167
|
+
acceptance_sha: string | null
|
|
168
|
+
acceptance_total: number | null
|
|
169
|
+
acceptance_passed: number | null
|
|
170
|
+
completion_summary: string | null
|
|
171
|
+
audit_goals_json: string | null
|
|
172
|
+
audit_bottom_line: string | null
|
|
173
|
+
audit_run_number: number | null
|
|
174
|
+
for_issue_number: number | null
|
|
175
|
+
driven_by: string | null
|
|
176
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Honest connection-state indicator (#1549) — live / reconnecting /
|
|
3
|
+
* stale-since-<time>. Never silently shows stale data as if it were live:
|
|
4
|
+
* the moment the SSE stream drops, this says so.
|
|
5
|
+
*/
|
|
6
|
+
import { useConnectionStatus } from '@/realtime/RealtimeProvider'
|
|
7
|
+
import { cn } from '@/lib/utils'
|
|
8
|
+
|
|
9
|
+
// Explicit options (not the bare `toLocaleTimeString()` default) so this
|
|
10
|
+
// small badge doesn't grow seconds or an AM/PM suffix in locales where the
|
|
11
|
+
// default format includes them -- hour:minute is all the precision a "how
|
|
12
|
+
// stale is this" label needs.
|
|
13
|
+
function formatTime(epochMs: number): string {
|
|
14
|
+
return new Date(epochMs).toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function ConnectionBadge() {
|
|
18
|
+
const { state, lastLiveAt } = useConnectionStatus()
|
|
19
|
+
|
|
20
|
+
let label: string
|
|
21
|
+
let dotClass: string
|
|
22
|
+
switch (state) {
|
|
23
|
+
case 'live':
|
|
24
|
+
label = 'Live'
|
|
25
|
+
dotClass = 'bg-green-500'
|
|
26
|
+
break
|
|
27
|
+
case 'reconnecting':
|
|
28
|
+
label = 'Reconnecting…'
|
|
29
|
+
dotClass = 'bg-yellow-500 animate-pulse'
|
|
30
|
+
break
|
|
31
|
+
case 'disconnected':
|
|
32
|
+
label = lastLiveAt ? `Stale since ${formatTime(lastLiveAt)}` : 'Disconnected'
|
|
33
|
+
dotClass = 'bg-destructive'
|
|
34
|
+
break
|
|
35
|
+
case 'connecting':
|
|
36
|
+
default:
|
|
37
|
+
label = 'Connecting…'
|
|
38
|
+
dotClass = 'bg-muted-foreground animate-pulse'
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
className="flex items-center gap-1.5 text-xs text-muted-foreground"
|
|
45
|
+
role="status"
|
|
46
|
+
aria-label={`Connection: ${label}`}
|
|
47
|
+
>
|
|
48
|
+
<span className={cn('h-2 w-2 shrink-0 rounded-full', dotClass)} aria-hidden="true" />
|
|
49
|
+
{label}
|
|
50
|
+
</span>
|
|
51
|
+
)
|
|
52
|
+
}
|