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.
Files changed (295) hide show
  1. code_coordinator-0.5.46.dist-info/METADATA +625 -0
  2. code_coordinator-0.5.46.dist-info/RECORD +295 -0
  3. code_coordinator-0.5.46.dist-info/WHEEL +5 -0
  4. code_coordinator-0.5.46.dist-info/entry_points.txt +2 -0
  5. code_coordinator-0.5.46.dist-info/licenses/LICENSE +110 -0
  6. code_coordinator-0.5.46.dist-info/top_level.txt +1 -0
  7. coord/__init__.py +176 -0
  8. coord/_board_mapping.py +229 -0
  9. coord/acceptance.py +468 -0
  10. coord/acceptance_drivers.py +632 -0
  11. coord/agent.py +7517 -0
  12. coord/agent_app.py +1555 -0
  13. coord/agent_update.py +417 -0
  14. coord/agents/opencode/.gitignore +13 -0
  15. coord/agents/opencode/agents/work.md +129 -0
  16. coord/agents/opencode/routing.jsonc +49 -0
  17. coord/audit.py +301 -0
  18. coord/auto_loop.py +1440 -0
  19. coord/board_bool_guard.py +72 -0
  20. coord/board_service.py +141 -0
  21. coord/board_wire.py +309 -0
  22. coord/brain.py +581 -0
  23. coord/branch_model.py +214 -0
  24. coord/cargo_cache.py +258 -0
  25. coord/ci_github.py +386 -0
  26. coord/ci_store.py +560 -0
  27. coord/claim.py +353 -0
  28. coord/cli.py +454 -0
  29. coord/client.py +610 -0
  30. coord/commands/__init__.py +1 -0
  31. coord/commands/_common.py +329 -0
  32. coord/commands/acceptance.py +916 -0
  33. coord/commands/agent_ops.py +1339 -0
  34. coord/commands/audit.py +131 -0
  35. coord/commands/chat.py +320 -0
  36. coord/commands/dispatch.py +1780 -0
  37. coord/commands/dispatch_workers.py +4894 -0
  38. coord/commands/drive.py +616 -0
  39. coord/commands/drive_queue.py +1203 -0
  40. coord/commands/gate_a.py +217 -0
  41. coord/commands/gates.py +89 -0
  42. coord/commands/issues.py +681 -0
  43. coord/commands/lifecycle.py +513 -0
  44. coord/commands/merge.py +1900 -0
  45. coord/commands/milestone.py +2081 -0
  46. coord/commands/plan_followup.py +1243 -0
  47. coord/commands/plans.py +156 -0
  48. coord/commands/release.py +2232 -0
  49. coord/commands/report.py +341 -0
  50. coord/commands/review.py +1523 -0
  51. coord/commands/scorecard.py +252 -0
  52. coord/commands/sessions.py +1930 -0
  53. coord/commands/setup.py +576 -0
  54. coord/commands/status.py +2089 -0
  55. coord/commands/terminal.py +385 -0
  56. coord/commands/test_gate.py +775 -0
  57. coord/commands/tui.py +288 -0
  58. coord/comments.py +718 -0
  59. coord/config.py +3032 -0
  60. coord/conflict_fix.py +633 -0
  61. coord/dao.py +483 -0
  62. coord/dashboard/__init__.py +0 -0
  63. coord/dashboard/fixture.py +376 -0
  64. coord/dashboard/index.html +658 -0
  65. coord/dashboard/server.py +1894 -0
  66. coord/dashboard/terminal.py +382 -0
  67. coord/dashboard/webapp/.gitignore +9 -0
  68. coord/dashboard/webapp/components.json +17 -0
  69. coord/dashboard/webapp/dist/assets/Gallery-da3qNiIw.js +71 -0
  70. coord/dashboard/webapp/dist/assets/Terminal-9CEnUXvW.css +32 -0
  71. coord/dashboard/webapp/dist/assets/Terminal-skVFCxPU.js +63 -0
  72. coord/dashboard/webapp/dist/assets/index-DltfZR5f.js +184 -0
  73. coord/dashboard/webapp/dist/assets/index-Dq4kwTdw.css +1 -0
  74. coord/dashboard/webapp/dist/assets/workbox-window.prod.es5-BqEJf4Xk.js +2 -0
  75. coord/dashboard/webapp/dist/icons/icon-192.png +0 -0
  76. coord/dashboard/webapp/dist/icons/icon-512.png +0 -0
  77. coord/dashboard/webapp/dist/icons/icon.svg +5 -0
  78. coord/dashboard/webapp/dist/index.html +38 -0
  79. coord/dashboard/webapp/dist/manifest.webmanifest +1 -0
  80. coord/dashboard/webapp/dist/sw.js +1 -0
  81. coord/dashboard/webapp/dist/workbox-e4022e15.js +1 -0
  82. coord/dashboard/webapp/e2e/available-gates-terminal.spec.ts +75 -0
  83. coord/dashboard/webapp/e2e/deep-link.spec.ts +172 -0
  84. coord/dashboard/webapp/e2e/fixtureServer.ts +155 -0
  85. coord/dashboard/webapp/e2e/live-update-fixture.spec.ts +113 -0
  86. coord/dashboard/webapp/e2e/realtime.spec.ts +238 -0
  87. coord/dashboard/webapp/e2e/shell.spec.ts +309 -0
  88. coord/dashboard/webapp/e2e/smoke.spec.ts +191 -0
  89. coord/dashboard/webapp/e2e/terminal.spec.ts +420 -0
  90. coord/dashboard/webapp/e2e/theme.spec.ts +138 -0
  91. coord/dashboard/webapp/eslint.config.js +20 -0
  92. coord/dashboard/webapp/index.html +37 -0
  93. coord/dashboard/webapp/node_modules/flatted/python/flatted.py +144 -0
  94. coord/dashboard/webapp/package-lock.json +10584 -0
  95. coord/dashboard/webapp/package.json +63 -0
  96. coord/dashboard/webapp/playwright.acceptance.config.ts +166 -0
  97. coord/dashboard/webapp/playwright.config.ts +93 -0
  98. coord/dashboard/webapp/postcss.config.js +6 -0
  99. coord/dashboard/webapp/public/icons/icon-192.png +0 -0
  100. coord/dashboard/webapp/public/icons/icon-512.png +0 -0
  101. coord/dashboard/webapp/public/icons/icon.svg +5 -0
  102. coord/dashboard/webapp/src/App.tsx +140 -0
  103. coord/dashboard/webapp/src/api/client.ts +199 -0
  104. coord/dashboard/webapp/src/api/generated.ts +176 -0
  105. coord/dashboard/webapp/src/components/ConnectionBadge.tsx +52 -0
  106. coord/dashboard/webapp/src/components/Detail.tsx +800 -0
  107. coord/dashboard/webapp/src/components/Gallery.tsx +341 -0
  108. coord/dashboard/webapp/src/components/Home.tsx +435 -0
  109. coord/dashboard/webapp/src/components/MobileKeyBar.tsx +280 -0
  110. coord/dashboard/webapp/src/components/PanelHeader.tsx +59 -0
  111. coord/dashboard/webapp/src/components/PipelineCard.tsx +168 -0
  112. coord/dashboard/webapp/src/components/SessionCard.tsx +99 -0
  113. coord/dashboard/webapp/src/components/SessionDetail.tsx +140 -0
  114. coord/dashboard/webapp/src/components/SessionsList.tsx +81 -0
  115. coord/dashboard/webapp/src/components/Terminal.tsx +376 -0
  116. coord/dashboard/webapp/src/components/__tests__/ConnectionBadge.test.tsx +81 -0
  117. coord/dashboard/webapp/src/components/__tests__/Detail.test.tsx +680 -0
  118. coord/dashboard/webapp/src/components/__tests__/Gallery.test.tsx +83 -0
  119. coord/dashboard/webapp/src/components/__tests__/Home.test.tsx +271 -0
  120. coord/dashboard/webapp/src/components/__tests__/MobileKeyBar.test.tsx +197 -0
  121. coord/dashboard/webapp/src/components/__tests__/PipelineCard.test.tsx +143 -0
  122. coord/dashboard/webapp/src/components/__tests__/SessionCard.test.tsx +106 -0
  123. coord/dashboard/webapp/src/components/__tests__/Terminal.test.tsx +504 -0
  124. coord/dashboard/webapp/src/components/ui/badge.tsx +41 -0
  125. coord/dashboard/webapp/src/components/ui/button.tsx +54 -0
  126. coord/dashboard/webapp/src/components/ui/card.tsx +55 -0
  127. coord/dashboard/webapp/src/components/ui/dialog.tsx +99 -0
  128. coord/dashboard/webapp/src/components/ui/dropdown-menu.tsx +189 -0
  129. coord/dashboard/webapp/src/components/ui/empty-state.tsx +35 -0
  130. coord/dashboard/webapp/src/components/ui/sheet.tsx +123 -0
  131. coord/dashboard/webapp/src/components/ui/skeleton.tsx +9 -0
  132. coord/dashboard/webapp/src/components/ui/tabs.tsx +55 -0
  133. coord/dashboard/webapp/src/components/ui/theme-provider.tsx +78 -0
  134. coord/dashboard/webapp/src/components/ui/theme-toggle.tsx +20 -0
  135. coord/dashboard/webapp/src/components/ui/toast.tsx +123 -0
  136. coord/dashboard/webapp/src/components/ui/toaster.tsx +30 -0
  137. coord/dashboard/webapp/src/components/ui/tooltip.tsx +26 -0
  138. coord/dashboard/webapp/src/components/ui/use-toast.ts +134 -0
  139. coord/dashboard/webapp/src/index.css +210 -0
  140. coord/dashboard/webapp/src/lib/pipeline.ts +29 -0
  141. coord/dashboard/webapp/src/lib/utils.ts +6 -0
  142. coord/dashboard/webapp/src/main.tsx +46 -0
  143. coord/dashboard/webapp/src/realtime/RealtimeProvider.tsx +112 -0
  144. coord/dashboard/webapp/src/realtime/__tests__/RealtimeProvider.test.tsx +189 -0
  145. coord/dashboard/webapp/src/realtime/__tests__/connection.test.ts +255 -0
  146. coord/dashboard/webapp/src/realtime/connection.ts +227 -0
  147. coord/dashboard/webapp/src/realtime/events.ts +100 -0
  148. coord/dashboard/webapp/src/routes/__tests__/paths.test.ts +92 -0
  149. coord/dashboard/webapp/src/routes/paths.ts +92 -0
  150. coord/dashboard/webapp/src/shell/ActivityRail.tsx +335 -0
  151. coord/dashboard/webapp/src/shell/AppShell.tsx +276 -0
  152. coord/dashboard/webapp/src/shell/ComingSoon.tsx +33 -0
  153. coord/dashboard/webapp/src/shell/EmptyDetail.tsx +26 -0
  154. coord/dashboard/webapp/src/shell/RouteNotFound.tsx +33 -0
  155. coord/dashboard/webapp/src/shell/ShellLayout.tsx +147 -0
  156. coord/dashboard/webapp/src/shell/StatusBar.tsx +46 -0
  157. coord/dashboard/webapp/src/shell/__tests__/ShellLayout.test.tsx +520 -0
  158. coord/dashboard/webapp/src/shell/__tests__/shellState.test.ts +95 -0
  159. coord/dashboard/webapp/src/shell/__tests__/stubViewport.ts +40 -0
  160. coord/dashboard/webapp/src/shell/breakpoints.ts +87 -0
  161. coord/dashboard/webapp/src/shell/railItems.ts +105 -0
  162. coord/dashboard/webapp/src/shell/shellState.ts +174 -0
  163. coord/dashboard/webapp/src/shell/useRegionFocus.ts +95 -0
  164. coord/dashboard/webapp/src/test-setup.ts +41 -0
  165. coord/dashboard/webapp/src/vite-env.d.ts +2 -0
  166. coord/dashboard/webapp/tailwind.config.js +140 -0
  167. coord/dashboard/webapp/tsconfig.json +25 -0
  168. coord/dashboard/webapp/tsconfig.node.json +11 -0
  169. coord/dashboard/webapp/vite.config.ts +71 -0
  170. coord/db.py +1076 -0
  171. coord/dead_end.py +332 -0
  172. coord/deploy/README.md +33 -0
  173. coord/deploy/coord-agent.service +89 -0
  174. coord/deploy/coord-db-backup.service +60 -0
  175. coord/deploy/coord-db-backup.sh +74 -0
  176. coord/deploy/coord-db-backup.timer +18 -0
  177. coord/deploy/coord-drive-queue.service +117 -0
  178. coord/deploy/coord-drive-queue.timer +39 -0
  179. coord/deploy/coord-notify.service +48 -0
  180. coord/deploy/coord-notify.timer +24 -0
  181. coord/deploy/coord-release-propagate.service +83 -0
  182. coord/deploy/coord-release-propagate.timer +38 -0
  183. coord/deploy/coord-release-window.service +119 -0
  184. coord/deploy/coord-release-window.timer +36 -0
  185. coord/deploy/coord-serve.service +82 -0
  186. coord/deploy/coord-web-dist-build.service +43 -0
  187. coord/deploy/coord-web-dist-build.timer +36 -0
  188. coord/deploy/coord-web.service +125 -0
  189. coord/deploy_manifest.py +80 -0
  190. coord/deploy_units.py +384 -0
  191. coord/deps.py +115 -0
  192. coord/diagnose.py +1623 -0
  193. coord/dispatch.py +1009 -0
  194. coord/dist_name.py +123 -0
  195. coord/drive.py +3101 -0
  196. coord/drive_queue.py +2298 -0
  197. coord/drive_state.py +870 -0
  198. coord/events.py +381 -0
  199. coord/failure_class.py +914 -0
  200. coord/filelock.py +168 -0
  201. coord/fleet_config_health.py +300 -0
  202. coord/freshness.py +206 -0
  203. coord/gate_a.py +469 -0
  204. coord/gate_b.py +411 -0
  205. coord/gate_snapshot.py +385 -0
  206. coord/gates.py +582 -0
  207. coord/github_ops.py +1954 -0
  208. coord/goal.py +125 -0
  209. coord/graph_health.py +348 -0
  210. coord/health/__init__.py +69 -0
  211. coord/health/aggregate.py +129 -0
  212. coord/health/checks/__init__.py +13 -0
  213. coord/health/checks/agent_install.py +280 -0
  214. coord/health/checks/cargo_targets.py +171 -0
  215. coord/health/checks/claude_binary.py +65 -0
  216. coord/health/checks/deploy_lane_facts.py +458 -0
  217. coord/health/checks/disk.py +99 -0
  218. coord/health/checks/fleet_board.py +89 -0
  219. coord/health/checks/fleet_deploy_lanes.py +469 -0
  220. coord/health/checks/fleet_phantom.py +69 -0
  221. coord/health/checks/fleet_unit_drift.py +151 -0
  222. coord/health/checks/graph.py +192 -0
  223. coord/health/checks/plan_usage.py +88 -0
  224. coord/health/checks/repo_state.py +161 -0
  225. coord/health/checks/spawned_coord.py +465 -0
  226. coord/health/checks/timer_active.py +254 -0
  227. coord/health/checks/toolchain.py +547 -0
  228. coord/health/checks/unit_drift.py +648 -0
  229. coord/health/checks/unit_enablement.py +171 -0
  230. coord/health/checks/worktrees.py +96 -0
  231. coord/health/cli.py +121 -0
  232. coord/health/context.py +106 -0
  233. coord/health/fleet_snapshot.py +477 -0
  234. coord/health/models.py +250 -0
  235. coord/health/pypi.py +231 -0
  236. coord/health/registry.py +240 -0
  237. coord/health/render.py +82 -0
  238. coord/health/units.py +60 -0
  239. coord/hooks.py +106 -0
  240. coord/housekeeping.py +204 -0
  241. coord/interactive.py +4286 -0
  242. coord/issue_store.py +1496 -0
  243. coord/liveness_auditor.py +293 -0
  244. coord/machine_pause.py +755 -0
  245. coord/merge_queue.py +4681 -0
  246. coord/milestone_chat.py +600 -0
  247. coord/milestone_dispatch.py +943 -0
  248. coord/milestone_gate.py +709 -0
  249. coord/milestone_order.py +840 -0
  250. coord/mock_author.py +334 -0
  251. coord/models.py +891 -0
  252. coord/network.py +269 -0
  253. coord/new_issue_chat.py +229 -0
  254. coord/notify.py +3226 -0
  255. coord/openapi.py +404 -0
  256. coord/overlap_fence.py +133 -0
  257. coord/parentage.py +200 -0
  258. coord/parentage_github.py +58 -0
  259. coord/pipeline.py +481 -0
  260. coord/plan_parser.py +266 -0
  261. coord/plans.py +543 -0
  262. coord/platform_paths.py +43 -0
  263. coord/pr_body_lint.py +67 -0
  264. coord/prereqs.py +533 -0
  265. coord/progress.py +425 -0
  266. coord/providers/__init__.py +683 -0
  267. coord/providers/base.py +218 -0
  268. coord/providers/claude.py +284 -0
  269. coord/providers/claude_pty.py +610 -0
  270. coord/providers/opencode.py +896 -0
  271. coord/reconcile.py +2233 -0
  272. coord/refine_chat.py +485 -0
  273. coord/release_cordon.py +525 -0
  274. coord/release_propagate.py +1176 -0
  275. coord/release_verify.py +777 -0
  276. coord/release_window.py +322 -0
  277. coord/reports.py +1643 -0
  278. coord/revalidate.py +1101 -0
  279. coord/review.py +3317 -0
  280. coord/scorecard.py +484 -0
  281. coord/serve_app.py +7192 -0
  282. coord/skills/update-issue/SKILL.md +93 -0
  283. coord/smoke.py +1030 -0
  284. coord/split_work.py +210 -0
  285. coord/stage_projection.py +650 -0
  286. coord/state.py +5720 -0
  287. coord/test_author.py +1064 -0
  288. coord/test_chat.py +352 -0
  289. coord/test_orchestrator.py +494 -0
  290. coord/test_report.py +178 -0
  291. coord/tui_release.py +271 -0
  292. coord/usage.py +753 -0
  293. coord/usage_limits.py +358 -0
  294. coord/usage_rollup.py +709 -0
  295. 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
+ }