@atolis-hq/wake 0.2.97 → 0.3.0

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 (442) hide show
  1. package/dist/src/activities/agent/agent-activity-definition.js +26 -0
  2. package/dist/src/activities/agent/agent-activity.js +171 -0
  3. package/dist/src/activities/agent/agent-result.js +149 -0
  4. package/dist/src/activities/contracts/activity.js +10 -0
  5. package/dist/src/activities/contracts/config.js +2 -0
  6. package/dist/src/activities/contracts/event-schema.js +302 -0
  7. package/dist/src/activities/contracts/events.js +42 -0
  8. package/dist/src/activities/contracts/identifiers.js +29 -0
  9. package/dist/src/activities/contracts/registry.js +71 -0
  10. package/dist/src/activities/contracts/streams.js +17 -0
  11. package/dist/src/activities/contracts/vocabulary.js +58 -0
  12. package/dist/src/activities/index.js +21 -0
  13. package/dist/src/activities/pr/activity-support.js +111 -0
  14. package/dist/src/activities/pr/application.js +158 -0
  15. package/dist/src/activities/pr/approve.js +85 -0
  16. package/dist/src/activities/pr/capability.js +11 -0
  17. package/dist/src/activities/pr/decision-claim.js +76 -0
  18. package/dist/src/activities/pr/event-drafts.js +66 -0
  19. package/dist/src/activities/pr/intent.js +37 -0
  20. package/dist/src/activities/pr/merge-authority-gate.js +14 -0
  21. package/dist/src/activities/pr/merge.js +110 -0
  22. package/dist/src/activities/pr/policy.js +135 -0
  23. package/dist/src/activities/pr/projection.js +130 -0
  24. package/dist/src/activities/pr/vocabulary.js +30 -0
  25. package/dist/src/activities/review/authorization.js +22 -0
  26. package/dist/src/activities/review/contracts.js +22 -0
  27. package/dist/src/activities/review/signals.js +3 -0
  28. package/dist/src/bootstrap/analytics-projection.js +26 -0
  29. package/dist/src/bootstrap/board-projection.js +337 -0
  30. package/dist/src/bootstrap/composition-root.js +382 -0
  31. package/dist/src/bootstrap/config/load-config.js +51 -0
  32. package/dist/src/bootstrap/config/root-schema.js +134 -0
  33. package/dist/src/bootstrap/external-ref.js +12 -0
  34. package/dist/src/bootstrap/fake-provider-files.js +44 -0
  35. package/dist/src/bootstrap/fake-scenarios.js +22 -0
  36. package/dist/src/bootstrap/index.js +48 -0
  37. package/dist/src/bootstrap/initialise.js +434 -0
  38. package/dist/src/bootstrap/paths.js +15 -0
  39. package/dist/src/bootstrap/projection-runtime.js +27 -0
  40. package/dist/src/bootstrap/runner-quota-reporter.js +21 -0
  41. package/dist/src/bootstrap/runner-registry.js +30 -0
  42. package/dist/src/bootstrap/schedule-checkpoint-store.js +49 -0
  43. package/dist/src/bootstrap/self-update-application.js +195 -0
  44. package/dist/src/bootstrap/self-update-failure-log.js +33 -0
  45. package/dist/src/bootstrap/source-update-port.js +47 -0
  46. package/dist/src/bootstrap/status-publish-activity.js +42 -0
  47. package/dist/src/bootstrap/surface-api-applications.js +325 -0
  48. package/dist/src/bootstrap/surface-api-execution-applications.js +95 -0
  49. package/dist/src/bootstrap/surface-api-metadata.js +12 -0
  50. package/dist/src/bootstrap/surface-api-projection-pages.js +22 -0
  51. package/dist/src/bootstrap/surface-api-run-context.js +5 -0
  52. package/dist/src/bootstrap/surface-api-transcripts.js +57 -0
  53. package/dist/src/bootstrap/surface-api-work-applications.js +199 -0
  54. package/dist/src/bootstrap/surface-applications.js +7 -0
  55. package/dist/src/bootstrap/surface-cli-applications.js +701 -0
  56. package/dist/src/bootstrap/update-ledger.js +61 -0
  57. package/dist/src/bootstrap/update-maintenance-lease.js +176 -0
  58. package/dist/src/{version.js → bootstrap/version.js} +35 -51
  59. package/dist/src/control-plane/application/advance-once.js +184 -0
  60. package/dist/src/control-plane/application/control-plane-projection.js +42 -0
  61. package/dist/src/control-plane/application/control-plane-service.js +42 -0
  62. package/dist/src/control-plane/application/intake-pipeline.js +29 -0
  63. package/dist/src/control-plane/application/runner-control-service.js +66 -0
  64. package/dist/src/control-plane/application/runner-pipeline.js +57 -0
  65. package/dist/src/control-plane/application/schedule-service.js +47 -0
  66. package/dist/src/control-plane/application/work-cancellation-policy.js +31 -0
  67. package/dist/src/control-plane/contracts/commands.js +10 -0
  68. package/dist/src/control-plane/contracts/config.js +22 -0
  69. package/dist/src/control-plane/contracts/events.js +81 -0
  70. package/dist/src/control-plane/contracts/streams.js +5 -0
  71. package/dist/src/control-plane/domain/dispatch-policy.js +15 -0
  72. package/dist/src/control-plane/domain/quota-policy.js +26 -0
  73. package/dist/src/control-plane/domain/runner-quota.js +35 -0
  74. package/dist/src/control-plane/domain/schedule-policy.js +46 -0
  75. package/dist/src/control-plane/index.js +20 -0
  76. package/dist/src/control-plane/infrastructure/intake-host.js +24 -0
  77. package/dist/src/control-plane/infrastructure/resident-host.js +45 -0
  78. package/dist/src/control-plane/infrastructure/tick-host.js +35 -0
  79. package/dist/src/execution/application/activation-claim.js +50 -0
  80. package/dist/src/execution/application/active-run-cancellation.js +14 -0
  81. package/dist/src/execution/application/execution-activity.js +97 -0
  82. package/dist/src/execution/application/execution-projection.js +17 -0
  83. package/dist/src/execution/application/execution-service.js +372 -0
  84. package/dist/src/execution/application/execution-validation.js +26 -0
  85. package/dist/src/execution/application/recovery-service.js +212 -0
  86. package/dist/src/execution/application/run-lifecycle.js +115 -0
  87. package/dist/src/execution/application/run-liveness-service.js +80 -0
  88. package/dist/src/execution/application/run-repository.js +27 -0
  89. package/dist/src/execution/contracts/commands.js +1 -0
  90. package/dist/src/execution/contracts/config.js +35 -0
  91. package/dist/src/execution/contracts/control-plane.js +1 -0
  92. package/dist/src/execution/contracts/event-factory.js +53 -0
  93. package/dist/src/execution/contracts/events.js +274 -0
  94. package/dist/src/execution/contracts/identifiers.js +5 -0
  95. package/dist/src/execution/contracts/liveness.js +1 -0
  96. package/dist/src/execution/contracts/results.js +1 -0
  97. package/dist/src/execution/contracts/runner.js +15 -0
  98. package/dist/src/execution/contracts/streams.js +10 -0
  99. package/dist/src/execution/contracts/views.js +1 -0
  100. package/dist/src/execution/contracts/vocabulary.js +43 -0
  101. package/dist/src/execution/contracts/workspace.js +1 -0
  102. package/dist/src/execution/domain/run-result.js +34 -0
  103. package/dist/src/execution/domain/run.js +142 -0
  104. package/dist/src/execution/index.js +31 -0
  105. package/dist/src/execution/infrastructure/agent-runner-adapter.js +114 -0
  106. package/dist/src/execution/infrastructure/process-execution.js +29 -0
  107. package/dist/src/execution/infrastructure/prompt-templates.js +42 -0
  108. package/dist/src/execution/infrastructure/runners/claude.js +120 -0
  109. package/dist/src/execution/infrastructure/runners/codex.js +143 -0
  110. package/dist/src/execution/infrastructure/runners/command.js +4 -0
  111. package/dist/src/execution/infrastructure/runners/cursor.js +69 -0
  112. package/dist/src/execution/infrastructure/runners/fake-scenarios.js +99 -0
  113. package/dist/src/execution/infrastructure/runners/fake.js +65 -0
  114. package/dist/src/execution/infrastructure/runners/registry.js +26 -0
  115. package/dist/src/execution/infrastructure/transcript-store.js +223 -0
  116. package/dist/src/execution/infrastructure/transcripts.js +11 -0
  117. package/dist/src/execution/infrastructure/workspace/fake-workspace.js +19 -0
  118. package/dist/src/execution/infrastructure/workspace/git-workspace.js +241 -0
  119. package/dist/src/integrations/application/agent-run-publication-reactor.js +111 -0
  120. package/dist/src/integrations/application/artifact-registration-reactor.js +159 -0
  121. package/dist/src/integrations/application/poll-service.js +25 -0
  122. package/dist/src/integrations/application/terminal-agent-run-report.js +40 -0
  123. package/dist/src/integrations/application/work-admission.js +31 -0
  124. package/dist/src/integrations/application/work-conclusion.js +19 -0
  125. package/dist/src/integrations/contracts/artifact-events.js +60 -0
  126. package/dist/src/integrations/contracts/artifact-vocabulary.js +9 -0
  127. package/dist/src/integrations/contracts/config.js +14 -0
  128. package/dist/src/integrations/contracts/identifiers.js +5 -0
  129. package/dist/src/integrations/contracts/intake-rules.js +14 -0
  130. package/dist/src/integrations/contracts/intake.js +1 -0
  131. package/dist/src/integrations/contracts/outcome-vocabulary.js +7 -0
  132. package/dist/src/integrations/contracts/provider.js +37 -0
  133. package/dist/src/integrations/contracts/streams.js +10 -0
  134. package/dist/src/integrations/delivery/application/delivery-outcome-reactor.js +75 -0
  135. package/dist/src/integrations/delivery/application/delivery-projector.js +165 -0
  136. package/dist/src/integrations/delivery/application/delivery-service.js +173 -0
  137. package/dist/src/integrations/delivery/contracts/commands.js +1 -0
  138. package/dist/src/integrations/delivery/contracts/config.js +1 -0
  139. package/dist/src/integrations/delivery/contracts/event-factory.js +18 -0
  140. package/dist/src/integrations/delivery/contracts/events.js +86 -0
  141. package/dist/src/integrations/delivery/contracts/intents.js +89 -0
  142. package/dist/src/integrations/delivery/contracts/views.js +1 -0
  143. package/dist/src/integrations/delivery/contracts/vocabulary.js +22 -0
  144. package/dist/src/integrations/fake/durable-delivery-provider.js +48 -0
  145. package/dist/src/integrations/fake/external-sink.js +9 -0
  146. package/dist/src/integrations/fake/external-source.js +44 -0
  147. package/dist/src/integrations/fake/inbound-translator.js +130 -0
  148. package/dist/src/integrations/fake/provider.js +95 -0
  149. package/dist/src/integrations/github/application/agent-context-reader.js +54 -0
  150. package/dist/src/integrations/github/application/agent-run-comment.js +94 -0
  151. package/dist/src/integrations/github/application/comment-history-reader.js +43 -0
  152. package/dist/src/integrations/github/application/inbound-context.js +10 -0
  153. package/dist/src/integrations/github/application/inbound-review-signals.js +159 -0
  154. package/dist/src/integrations/github/application/inbound-translator.js +231 -0
  155. package/dist/src/integrations/github/application/inbound-watch-gate-signals.js +84 -0
  156. package/dist/src/integrations/github/application/intake-policy.js +23 -0
  157. package/dist/src/integrations/github/application/outbound-translator.js +47 -0
  158. package/dist/src/integrations/github/application/poll-service.js +29 -0
  159. package/dist/src/integrations/github/application/pull-request-translation.js +10 -0
  160. package/dist/src/integrations/github/application/resource-links.js +8 -0
  161. package/dist/src/integrations/github/application/review-command-translator.js +16 -0
  162. package/dist/src/integrations/github/application/wake-labels.js +95 -0
  163. package/dist/src/integrations/github/contracts/config.js +47 -0
  164. package/dist/src/integrations/github/contracts/events.js +127 -0
  165. package/dist/src/integrations/github/contracts/external-key.js +19 -0
  166. package/dist/src/integrations/github/contracts/payloads.js +9 -0
  167. package/dist/src/integrations/github/contracts/vocabulary.js +55 -0
  168. package/dist/src/integrations/github/index.js +31 -0
  169. package/dist/src/integrations/github/infrastructure/client-reads.js +214 -0
  170. package/dist/src/integrations/github/infrastructure/client.js +132 -0
  171. package/dist/src/integrations/github/infrastructure/content-fingerprint.js +10 -0
  172. package/dist/src/integrations/github/infrastructure/delivery.js +31 -0
  173. package/dist/src/integrations/github/infrastructure/etag-cache.js +56 -0
  174. package/dist/src/integrations/github/infrastructure/gh-auth.js +19 -0
  175. package/dist/src/integrations/github/infrastructure/issue-source.js +109 -0
  176. package/dist/src/integrations/github/infrastructure/pr-source.js +186 -0
  177. package/dist/src/integrations/github/infrastructure/review-source.js +70 -0
  178. package/dist/src/integrations/github/infrastructure/source.js +108 -0
  179. package/dist/src/integrations/github/provider.js +96 -0
  180. package/dist/src/integrations/index.js +30 -0
  181. package/dist/src/kernel/contracts/checkpoint-store.js +1 -0
  182. package/dist/src/kernel/contracts/clock.js +1 -0
  183. package/dist/src/kernel/contracts/commands.js +1 -0
  184. package/dist/src/kernel/contracts/event-journal.js +2 -0
  185. package/dist/src/kernel/contracts/event-schema.js +54 -0
  186. package/dist/src/kernel/contracts/events.js +11 -0
  187. package/dist/src/kernel/contracts/id-generator.js +1 -0
  188. package/dist/src/kernel/contracts/identifiers.js +8 -0
  189. package/dist/src/kernel/contracts/projection-store.js +1 -0
  190. package/dist/src/kernel/contracts/relations.js +1 -0
  191. package/dist/src/kernel/contracts/schema.js +17 -0
  192. package/dist/src/kernel/contracts/vocabulary.js +6 -0
  193. package/dist/src/kernel/domain/event-envelope.js +29 -0
  194. package/dist/src/kernel/domain/match-mode.js +11 -0
  195. package/dist/src/kernel/domain/relation.js +10 -0
  196. package/dist/src/kernel/index.js +17 -0
  197. package/dist/src/{lib/clock.js → kernel/infrastructure/system-clock.js} +3 -3
  198. package/dist/src/kernel/infrastructure/ulid-id-generator.js +9 -0
  199. package/dist/src/main.js +137 -1030
  200. package/dist/src/orchestration/application/accept-activity-outcome.js +25 -0
  201. package/dist/src/orchestration/application/accept-signal.js +35 -0
  202. package/dist/src/orchestration/application/advance-workflow.js +160 -0
  203. package/dist/src/orchestration/application/coordination-claims.js +107 -0
  204. package/dist/src/orchestration/application/group-budget-recorder.js +43 -0
  205. package/dist/src/orchestration/application/orchestration-projection.js +20 -0
  206. package/dist/src/orchestration/application/orchestration-repository.js +36 -0
  207. package/dist/src/orchestration/application/orchestration-service.js +82 -0
  208. package/dist/src/orchestration/application/request-child.js +118 -0
  209. package/dist/src/orchestration/application/signal-reactor.js +17 -0
  210. package/dist/src/orchestration/application/start-workflow.js +51 -0
  211. package/dist/src/orchestration/application/watch-reactor.js +82 -0
  212. package/dist/src/orchestration/contracts/activity-outcome.js +18 -0
  213. package/dist/src/orchestration/contracts/commands.js +1 -0
  214. package/dist/src/orchestration/contracts/config.js +128 -0
  215. package/dist/src/orchestration/contracts/event-decoder.js +263 -0
  216. package/dist/src/orchestration/contracts/events.js +41 -0
  217. package/dist/src/orchestration/contracts/identifiers.js +35 -0
  218. package/dist/src/orchestration/contracts/streams.js +38 -0
  219. package/dist/src/orchestration/contracts/views.js +1 -0
  220. package/dist/src/orchestration/contracts/vocabulary.js +30 -0
  221. package/dist/src/orchestration/domain/activation-policy.js +55 -0
  222. package/dist/src/orchestration/domain/approval-defaults.js +17 -0
  223. package/dist/src/orchestration/domain/child-policy.js +63 -0
  224. package/dist/src/orchestration/domain/compiler.js +162 -0
  225. package/dist/src/orchestration/domain/coordination-events.js +25 -0
  226. package/dist/src/orchestration/domain/decision-events.js +47 -0
  227. package/dist/src/orchestration/domain/interpreter.js +49 -0
  228. package/dist/src/orchestration/domain/operator-retry-policy.js +34 -0
  229. package/dist/src/orchestration/domain/retry-policy.js +26 -0
  230. package/dist/src/orchestration/domain/signal-policy.js +120 -0
  231. package/dist/src/orchestration/domain/supplemental-policy.js +61 -0
  232. package/dist/src/orchestration/domain/transition.js +55 -0
  233. package/dist/src/orchestration/domain/workflow-graph.js +41 -0
  234. package/dist/src/orchestration/domain/workflow-instance-events.js +236 -0
  235. package/dist/src/orchestration/domain/workflow-instance.js +41 -0
  236. package/dist/src/orchestration/domain/workflow-selector.js +21 -0
  237. package/dist/src/orchestration/index.js +19 -0
  238. package/dist/src/persistence/application/projection-runner.js +79 -0
  239. package/dist/src/persistence/filesystem/file-checkpoint-store.js +35 -0
  240. package/dist/src/persistence/filesystem/file-event-journal.js +143 -0
  241. package/dist/src/persistence/filesystem/file-lock.js +91 -0
  242. package/dist/src/persistence/filesystem/file-projection-store.js +64 -0
  243. package/dist/src/persistence/index.js +8 -0
  244. package/dist/src/persistence/memory/in-memory-checkpoint-store.js +14 -0
  245. package/dist/src/persistence/memory/in-memory-event-journal.js +88 -0
  246. package/dist/src/persistence/memory/in-memory-projection-store.js +17 -0
  247. package/dist/src/resources/application/lookup-projections.js +48 -0
  248. package/dist/src/resources/application/resource-lookup.js +16 -0
  249. package/dist/src/resources/application/resource-projections.js +81 -0
  250. package/dist/src/resources/application/resource-repository.js +18 -0
  251. package/dist/src/resources/application/resource-service.js +83 -0
  252. package/dist/src/resources/contracts/commands.js +1 -0
  253. package/dist/src/resources/contracts/config.js +2 -0
  254. package/dist/src/resources/contracts/events.js +83 -0
  255. package/dist/src/resources/contracts/identifiers.js +15 -0
  256. package/dist/src/resources/contracts/streams.js +6 -0
  257. package/dist/src/resources/contracts/views.js +1 -0
  258. package/dist/src/resources/contracts/vocabulary.js +67 -0
  259. package/dist/src/resources/domain/correlation.js +8 -0
  260. package/dist/src/resources/domain/resource.js +58 -0
  261. package/dist/src/resources/index.js +14 -0
  262. package/dist/src/surfaces/api/contracts/activities.js +1 -0
  263. package/dist/src/surfaces/api/contracts/board.js +2 -0
  264. package/dist/src/surfaces/api/contracts/common.js +46 -0
  265. package/dist/src/surfaces/api/contracts/control-plane.js +5 -0
  266. package/dist/src/surfaces/api/contracts/events.js +1 -0
  267. package/dist/src/surfaces/api/contracts/execution.js +1 -0
  268. package/dist/src/surfaces/api/contracts/index.js +13 -0
  269. package/dist/src/surfaces/api/contracts/observability.js +1 -0
  270. package/dist/src/surfaces/api/contracts/orchestration.js +1 -0
  271. package/dist/src/surfaces/api/contracts/resources.js +1 -0
  272. package/dist/src/surfaces/api/contracts/status.js +1 -0
  273. package/dist/src/surfaces/api/contracts/system.js +1 -0
  274. package/dist/src/surfaces/api/contracts/transport-values.js +39 -0
  275. package/dist/src/surfaces/api/contracts/work.js +17 -0
  276. package/dist/src/surfaces/api/http-server.js +121 -0
  277. package/dist/src/surfaces/api/presenters/activities.js +1 -0
  278. package/dist/src/surfaces/api/presenters/board.js +3 -0
  279. package/dist/src/surfaces/api/presenters/control-plane.js +1 -0
  280. package/dist/src/surfaces/api/presenters/events.js +1 -0
  281. package/dist/src/surfaces/api/presenters/execution.js +59 -0
  282. package/dist/src/surfaces/api/presenters/observability.js +1 -0
  283. package/dist/src/surfaces/api/presenters/orchestration.js +19 -0
  284. package/dist/src/surfaces/api/presenters/resources.js +15 -0
  285. package/dist/src/surfaces/api/presenters/status.js +3 -0
  286. package/dist/src/surfaces/api/presenters/system.js +14 -0
  287. package/dist/src/surfaces/api/presenters/work.js +16 -0
  288. package/dist/src/surfaces/api/problem-details.js +9 -0
  289. package/dist/src/surfaces/api/router.js +13 -0
  290. package/dist/src/surfaces/api/routes/activities.js +1 -0
  291. package/dist/src/surfaces/api/routes/applications.js +1 -0
  292. package/dist/src/surfaces/api/routes/commands.js +82 -0
  293. package/dist/src/surfaces/api/routes/control-plane.js +6 -0
  294. package/dist/src/surfaces/api/routes/events.js +1 -0
  295. package/dist/src/surfaces/api/routes/execution.js +8 -0
  296. package/dist/src/surfaces/api/routes/index.js +18 -0
  297. package/dist/src/surfaces/api/routes/observability.js +1 -0
  298. package/dist/src/surfaces/api/routes/orchestration.js +1 -0
  299. package/dist/src/surfaces/api/routes/pagination.js +78 -0
  300. package/dist/src/surfaces/api/routes/read.js +135 -0
  301. package/dist/src/surfaces/api/routes/resources.js +1 -0
  302. package/dist/src/surfaces/api/routes/responses.js +67 -0
  303. package/dist/src/surfaces/api/routes/system.js +1 -0
  304. package/dist/src/surfaces/api/routes/work.js +9 -0
  305. package/dist/src/surfaces/cli/commands/api.js +1 -0
  306. package/dist/src/surfaces/cli/commands/audit.js +10 -0
  307. package/dist/src/surfaces/cli/commands/correlate.js +2 -0
  308. package/dist/src/surfaces/cli/commands/doctor.js +6 -0
  309. package/dist/src/surfaces/cli/commands/init.js +39 -0
  310. package/dist/src/surfaces/cli/commands/sandbox-entrypoint.js +33 -0
  311. package/dist/src/surfaces/cli/commands/sandbox-setup.js +29 -0
  312. package/dist/src/surfaces/cli/commands/sandbox.js +105 -0
  313. package/dist/src/surfaces/cli/commands/self-update.js +38 -0
  314. package/dist/src/surfaces/cli/commands/smoke.js +16 -0
  315. package/dist/src/surfaces/cli/commands/start.js +1 -0
  316. package/dist/src/surfaces/cli/commands/stop.js +13 -0
  317. package/dist/src/surfaces/cli/commands/tick.js +1 -0
  318. package/dist/src/surfaces/cli/commands/ui.js +2 -0
  319. package/dist/src/surfaces/cli/commands/validate-state.js +5 -0
  320. package/dist/src/surfaces/cli/infrastructure/docker-cli.js +221 -0
  321. package/dist/src/surfaces/cli/infrastructure/process-log.js +86 -0
  322. package/dist/src/surfaces/cli/main.js +140 -0
  323. package/dist/src/surfaces/cli/usage.js +33 -0
  324. package/dist/src/surfaces/contracts/config.js +34 -0
  325. package/dist/src/surfaces/index.js +47 -0
  326. package/dist/src/surfaces/web-assets/assets/index-Bfn4FwZG.css +1 -0
  327. package/dist/src/surfaces/web-assets/assets/index-ipB6JPGU.js +1090 -0
  328. package/dist/src/surfaces/web-assets/index.html +13 -0
  329. package/dist/src/surfaces/web-host/asset-source.js +1 -0
  330. package/dist/src/surfaces/web-host/packaged-assets.js +44 -0
  331. package/dist/src/work/application/work-projection.js +59 -0
  332. package/dist/src/work/application/work-repository.js +18 -0
  333. package/dist/src/work/application/work-service.js +117 -0
  334. package/dist/src/work/contracts/commands.js +1 -0
  335. package/dist/src/work/contracts/config.js +2 -0
  336. package/dist/src/work/contracts/events.js +103 -0
  337. package/dist/src/work/contracts/identifiers.js +5 -0
  338. package/dist/src/work/contracts/streams.js +6 -0
  339. package/dist/src/work/contracts/views.js +1 -0
  340. package/dist/src/work/contracts/vocabulary.js +6 -0
  341. package/dist/src/work/domain/work-item.js +89 -0
  342. package/dist/src/work/index.js +11 -0
  343. package/docker/Dockerfile +10 -6
  344. package/docker/Dockerfile.packaged +3 -3
  345. package/package.json +29 -4
  346. package/prompts/implement.md +2 -2
  347. package/templates/SETUP.md +7 -7
  348. package/dist/src/adapters/claude/claude-runner.js +0 -529
  349. package/dist/src/adapters/codex/codex-runner.js +0 -503
  350. package/dist/src/adapters/cursor/cursor-runner.js +0 -418
  351. package/dist/src/adapters/docker/docker-cli.js +0 -150
  352. package/dist/src/adapters/fake/fake-artifact-verifier.js +0 -14
  353. package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +0 -77
  354. package/dist/src/adapters/fake/fake-resource-index.js +0 -21
  355. package/dist/src/adapters/fake/fake-runner.js +0 -77
  356. package/dist/src/adapters/fake/fake-ticketing-system.js +0 -185
  357. package/dist/src/adapters/fake/fake-workspace-manager.js +0 -81
  358. package/dist/src/adapters/fs/resource-index.js +0 -126
  359. package/dist/src/adapters/fs/self-update-ledger.js +0 -17
  360. package/dist/src/adapters/fs/state-store.js +0 -775
  361. package/dist/src/adapters/git/git-workspace-manager.js +0 -289
  362. package/dist/src/adapters/github/github-artifact-verifier.js +0 -38
  363. package/dist/src/adapters/github/github-auth.js +0 -16
  364. package/dist/src/adapters/github/github-client.js +0 -249
  365. package/dist/src/adapters/github/github-etag-cache.js +0 -57
  366. package/dist/src/adapters/github/github-issues-work-source.js +0 -692
  367. package/dist/src/adapters/github/github-pull-request-activity-source.js +0 -584
  368. package/dist/src/adapters/github/github-pull-request-merge-actor.js +0 -45
  369. package/dist/src/adapters/http/ui-assets.js +0 -1188
  370. package/dist/src/adapters/http/ui-data.js +0 -1107
  371. package/dist/src/adapters/http/ui-server.js +0 -458
  372. package/dist/src/adapters/runner/cli-command.js +0 -73
  373. package/dist/src/adapters/runner/prompt-templates.js +0 -76
  374. package/dist/src/adapters/runner/runner-cli-adapter.js +0 -112
  375. package/dist/src/adapters/runner/runner-registry.js +0 -139
  376. package/dist/src/adapters/runner/runtime-events.js +0 -32
  377. package/dist/src/adapters/runner/stage-prompt.js +0 -268
  378. package/dist/src/adapters/runner/transcripts.js +0 -25
  379. package/dist/src/cli/audit-command.js +0 -36
  380. package/dist/src/cli/correlate-command.js +0 -62
  381. package/dist/src/cli/doctor-command.js +0 -48
  382. package/dist/src/cli/init-command.js +0 -18
  383. package/dist/src/cli/sandbox-command.js +0 -258
  384. package/dist/src/cli/sandbox-entrypoint-command.js +0 -88
  385. package/dist/src/cli/sandbox-exec-logging.js +0 -7
  386. package/dist/src/cli/sandbox-resume.js +0 -79
  387. package/dist/src/cli/sandbox-setup-command.js +0 -31
  388. package/dist/src/cli/scaffold-assets.js +0 -100
  389. package/dist/src/cli/self-update-command.js +0 -198
  390. package/dist/src/cli/startup-preflight.js +0 -134
  391. package/dist/src/cli/stop-command.js +0 -46
  392. package/dist/src/cli/ui-command.js +0 -27
  393. package/dist/src/config/defaults.js +0 -10
  394. package/dist/src/config/discover-config-files.js +0 -11
  395. package/dist/src/config/load-config.js +0 -69
  396. package/dist/src/config/split-config.js +0 -14
  397. package/dist/src/core/active-run-recovery.js +0 -44
  398. package/dist/src/core/approval-intents.js +0 -16
  399. package/dist/src/core/audit-events.js +0 -82
  400. package/dist/src/core/control-plane.js +0 -192
  401. package/dist/src/core/event-builders.js +0 -153
  402. package/dist/src/core/event-resolver.js +0 -249
  403. package/dist/src/core/lifecycle-service.js +0 -8
  404. package/dist/src/core/live-execution.js +0 -81
  405. package/dist/src/core/outbox.js +0 -198
  406. package/dist/src/core/policy-engine.js +0 -389
  407. package/dist/src/core/projection-updater.js +0 -693
  408. package/dist/src/core/quota-backoff.js +0 -69
  409. package/dist/src/core/run-lease.js +0 -38
  410. package/dist/src/core/scheduled-workflow-source.js +0 -199
  411. package/dist/src/core/sink-router.js +0 -107
  412. package/dist/src/core/stale-run-reconciler.js +0 -352
  413. package/dist/src/core/tick-runner.js +0 -2442
  414. package/dist/src/core/workspace-cleanup.js +0 -117
  415. package/dist/src/domain/branch-naming.js +0 -14
  416. package/dist/src/domain/custom-commands.js +0 -58
  417. package/dist/src/domain/event-types.js +0 -158
  418. package/dist/src/domain/manual-labels.js +0 -1
  419. package/dist/src/domain/resource-uri.js +0 -38
  420. package/dist/src/domain/runner-routing.js +0 -127
  421. package/dist/src/domain/runtime-events.js +0 -36
  422. package/dist/src/domain/schema.js +0 -1233
  423. package/dist/src/domain/sources.js +0 -16
  424. package/dist/src/domain/stages.js +0 -36
  425. package/dist/src/domain/work-item-labels.js +0 -48
  426. package/dist/src/domain/work-item-lifecycle.js +0 -10
  427. package/dist/src/domain/work-item-status.js +0 -33
  428. package/dist/src/domain/workflows.js +0 -159
  429. package/dist/src/lib/deep-merge.js +0 -14
  430. package/dist/src/lib/detached-process-logging.js +0 -46
  431. package/dist/src/lib/event-log.js +0 -21
  432. package/dist/src/lib/format.js +0 -41
  433. package/dist/src/lib/json-file.js +0 -23
  434. package/dist/src/lib/lock.js +0 -181
  435. package/dist/src/lib/log-rotation.js +0 -42
  436. package/dist/src/lib/paths.js +0 -52
  437. package/dist/src/lib/process-identity.js +0 -54
  438. package/dist/src/lib/state-health.js +0 -30
  439. package/dist/src/lib/work-id.js +0 -19
  440. package/dist/src/lib/yaml-file.js +0 -20
  441. /package/dist/src/{core → activities/pr}/contracts.js +0 -0
  442. /package/dist/src/{domain/types.js → control-plane/contracts/views.js} +0 -0
@@ -1,1188 +0,0 @@
1
- import { wakeVersion } from '../../version.js';
2
- const logoSvg = `<svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg">
3
- <g transform="translate(55 55)">
4
- <circle r="54" fill="none" stroke="#0F766E" opacity=".10"></circle>
5
- <path d="M-42.5-11.5 A44 44 0 1 0 42.5-11.5" fill="none" stroke="#2DD4BF" stroke-width="7.5" stroke-linecap="round"></path>
6
- <path d="M-36-25.2 A44 44 0 0 1-25.2-36" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
7
- <path d="M25.2-36 A44 44 0 0 1 36-25.2" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
8
- <circle r="25" fill="#2DD4BF" opacity=".08"></circle>
9
- <circle r="11" fill="#2DD4BF" opacity=".25"></circle>
10
- <path d="M-18 23 C-9 22 -8 2 0-2 C8 2 9 22 18 23" fill="none" stroke="#2DD4BF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"></path>
11
- </g>
12
- </svg>`;
13
- const faviconHref = `data:image/svg+xml,${encodeURIComponent(logoSvg)}`;
14
- export const indexHtml = `<!DOCTYPE html>
15
- <html lang="en">
16
- <head>
17
- <meta charset="utf-8" />
18
- <title>Wake control plane</title>
19
- <link rel="icon" type="image/svg+xml" href="${faviconHref}" />
20
- <meta name="viewport" content="width=device-width, initial-scale=1" />
21
- <style>
22
- :root {
23
- color-scheme: dark light;
24
- --brand: #0f766e;
25
- --brand-dark: #134e4a;
26
- --brand-darker: #103a37;
27
- --accent: #2dd4bf;
28
- --accent-light: #5eead4;
29
- }
30
- body { font-family: -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; background: #14161a; color: #e8e8e8; }
31
- .topbar { display: flex; align-items: center; gap: 0.55rem; padding: 0.55rem 1rem; background: var(--brand); }
32
- .topbar .logo { display: flex; width: 24px; height: 24px; flex-shrink: 0; }
33
- .topbar .logo svg { width: 100%; height: 100%; display: block; }
34
- .topbar .brand-name { font-size: 1.05rem; font-weight: 700; color: #fff; letter-spacing: 0.01em; }
35
- .topbar .version { color: rgba(255, 255, 255, 0.7); font-size: 0.78rem; }
36
- .statusbar { display: flex; align-items: center; gap: 1rem; padding: 0.45rem 1rem; background: var(--brand-dark); border-top: 1px solid rgba(0, 0, 0, 0.18); flex-wrap: wrap; font-size: 0.8rem; }
37
- .statusbar .meta { color: rgba(255, 255, 255, 0.72); }
38
- .statusbar button { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.22rem 0.55rem; cursor: pointer; font-size: 0.78rem; }
39
- .statusbar button:hover:not(:disabled) { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
40
- .statusbar button:disabled { cursor: wait; opacity: 0.62; }
41
- .statusbar .pause-control { background: #5c1f1a; border-color: #ff8f7f; font-weight: 700; }
42
- .statusbar .pause-control:hover:not(:disabled) { background: #733027; border-color: #ffd0c8; }
43
- .statusbar .pause-control.is-paused { background: #1f4b34; border-color: #7fe3a3; }
44
- .statusbar .pause-control.is-paused:hover:not(:disabled) { background: #276340; border-color: #c3f3d2; }
45
- .pill { padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; }
46
- .pill-idle { background: #1f3d2c; color: #7fe3a3; }
47
- .pill-polling { background: #1f3350; color: #7fb3ff; }
48
- .pill-working { background: #2d1f50; color: #c79bff; }
49
- .pill-paused { background: #4a3510; color: #ffcf7f; }
50
- .pill-ready { background: #1f3d2c; color: #7fe3a3; }
51
- .pill-active { background: #2d1f50; color: #c79bff; }
52
- .pill-scheduled { background: #1f3350; color: #7fb3ff; }
53
- .pill-needs-human { background: #4a3510; color: #ffcf7f; }
54
- .pill-error { background: #3d1f1f; color: #ff8f7f; }
55
- .pill-finished { background: #252830; color: #9aa2ad; }
56
- .pill-frozen { background: #102a43; color: #9bd8ff; border: 1px solid #38bdf8; }
57
- nav { display: flex; gap: 0.25rem; padding: 0.4rem 1rem 0 0.3rem; background: var(--brand-darker); border-bottom: 1px solid #2c313a; }
58
- nav button { background: none; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.65); padding: 0.4rem 0.7rem 0.45rem; margin-bottom: -1px; cursor: pointer; font-size: 0.85rem; transition: color 0.12s ease; }
59
- nav button:hover { color: #fff; }
60
- nav button.active { color: var(--accent-light); border-bottom-color: var(--accent); }
61
- main { padding: 1rem; }
62
- .columns { display: grid; grid-template-columns: repeat(6, minmax(180px, 1fr)); gap: 0.6rem; overflow-x: auto; }
63
- .col { background: #1a1d23; border-radius: 10px; padding: 0.5rem; min-height: 200px; }
64
- .col-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin: 0.2rem 0.4rem 0.5rem; }
65
- .col h2 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: #9aa2ad; margin: 0; }
66
- .col-toggle { display: none; width: 1.65rem; height: 1.65rem; flex: 0 0 auto; align-items: center; justify-content: center; background: transparent; border: 1px solid #3a4150; border-radius: 6px; color: #cbd5e1; cursor: pointer; font-size: 1rem; line-height: 1; }
67
- .col-toggle:hover { border-color: var(--accent); color: var(--accent-light); }
68
- .card { background: #22262e; border: 1px solid #2c313a; border-radius: 8px; padding: 0.5rem; margin-bottom: 0.5rem; cursor: pointer; font-size: 0.8rem; transition: border-color 0.12s ease; }
69
- .card:hover { border-color: var(--accent); }
70
- .card .title { font-weight: 600; margin-bottom: 0.25rem; }
71
- .card .meta { color: #9aa2ad; font-size: 0.72rem; }
72
- .item-summary { margin: 0.45rem 0 0.75rem; }
73
- .item-summary .meta { color: #9aa2ad; font-size: 0.78rem; }
74
- .item-summary .card-stats { font-size: 0.76rem; }
75
- .item-summary .child-run { max-width: 560px; }
76
- .detail-kv { display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 0.7rem; align-items: baseline; margin: 0.75rem 0 1rem; font-size: 0.8rem; }
77
- .detail-kv dt { margin: 0; color: #9aa2ad; }
78
- .detail-kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
79
- .child-run { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 0.45rem; align-items: center; margin-top: 0.45rem; padding: 0.4rem; border-radius: 6px; background: #181c22; border: 1px solid #334155; color: #cbd5e1; font-size: 0.72rem; }
80
- .child-run .dot { width: 0.48rem; height: 0.48rem; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.14); }
81
- .child-run .dot-watch { background: #ffcf7f; box-shadow: 0 0 0 3px rgba(255, 207, 127, 0.14); }
82
- .child-run .run-title { font-weight: 650; color: #e5e7eb; }
83
- .child-run .run-meta { grid-column: 2; color: #94a3b8; }
84
- .card-stats { color: #8892a0; font-size: 0.7rem; margin-top: 0.3rem; }
85
- .card-ready { border-left: 3px solid #7fe3a3; }
86
- .card-active { border-left: 3px solid #c79bff; }
87
- .card-scheduled { border-left: 3px solid #7fb3ff; }
88
- .card-needs-human { border-left: 3px solid #ffcf7f; }
89
- .card-error { border-left: 3px solid #ff8f7f; }
90
- .chip { display: inline-block; background: #2c313a; border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.68rem; margin-right: 0.2rem; }
91
- .chip-label { background: transparent; border: 1px solid #3a4150; color: #9aa2ad; margin-bottom: 0.2rem; }
92
- .chip-meta { border: 1px solid #3a4150; color: #cbd5e1; margin-bottom: 0.2rem; }
93
- table { border-collapse: collapse; width: 100%; font-size: 0.8rem; }
94
- th, td { text-align: left; padding: 0.35rem 0.5rem; border-bottom: 1px solid #2c313a; }
95
- th { color: #9aa2ad; font-weight: 600; }
96
- pre { background: #1a1d23; padding: 0.75rem; border-radius: 6px; overflow: auto; font-size: 0.75rem; }
97
- .modal-overlay { position: fixed; inset: 0; display: none; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.58); padding: 1.25rem; z-index: 10; }
98
- .modal-overlay.open { display: flex; }
99
- .modal { width: calc(100vw - 3rem); height: calc(100vh - 3rem); background: #191c22; border: 1px solid #2c313a; border-radius: 8px; display: flex; flex-direction: column; box-shadow: 0 18px 60px rgba(0, 0, 0, 0.38); }
100
- .modal-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.85rem 1rem 0.55rem; border-bottom: 1px solid #2c313a; }
101
- .modal-header h2 { margin: 0; font-size: 1rem; }
102
- .modal .close { cursor: pointer; color: #9aa2ad; background: none; border: 0; font-size: 0.9rem; padding: 0.2rem 0; }
103
- .modal .close:hover { color: var(--accent-light); }
104
- .modal-tabs { display: flex; gap: 0.25rem; padding: 0 1rem; border-bottom: 1px solid #2c313a; }
105
- .modal-tabs .nav-button { background: none; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.65); padding: 0.45rem 0.7rem 0.5rem; margin-bottom: -1px; cursor: pointer; font-size: 0.85rem; }
106
- .modal-tabs .nav-button:hover { color: #fff; }
107
- .modal-tabs .nav-button.active { color: var(--accent-light); border-bottom-color: var(--accent); }
108
- .modal-body { flex: 1; min-height: 0; overflow-y: auto; padding: 1rem; }
109
- .modal-body h3:first-child { margin-top: 0; }
110
- .event-list { display: flex; flex-direction: column; gap: 0.45rem; }
111
- .event-card { background: #20242c; border: 1px solid #2c313a; border-radius: 6px; overflow: hidden; }
112
- .event-card:hover { border-color: #3a4150; }
113
- .event-summary { display: grid; grid-template-columns: minmax(11rem, 14rem) minmax(10rem, 1fr) minmax(8rem, 18rem) 2.4rem; gap: 0.65rem; align-items: center; width: 100%; min-height: 2.25rem; background: transparent; border: 0; color: inherit; padding: 0.45rem 0.65rem; text-align: left; cursor: pointer; font: inherit; }
114
- .event-summary:hover { background: rgba(255, 255, 255, 0.03); }
115
- .event-time, .event-type, .event-id, .event-direction { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
116
- .event-time, .event-id { color: #9aa2ad; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.72rem; }
117
- .event-type { font-weight: 650; font-size: 0.8rem; }
118
- .event-direction { justify-self: end; width: 1.7rem; height: 1.7rem; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; background: #151922; border: 1px solid #334155; color: var(--accent-light); font-size: 0.9rem; }
119
- .event-json { display: none; position: relative; border-top: 1px solid #2c313a; background: #14171d; }
120
- .event-card.expanded .event-json { display: block; }
121
- .event-copy { position: absolute; top: 0.55rem; right: 0.55rem; z-index: 1; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.16rem 0.48rem; cursor: pointer; font-size: 0.72rem; }
122
- .event-copy:hover { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
123
- .event-json pre { margin: 0; padding-top: 2.35rem; border-radius: 0; background: transparent; }
124
- .transcript-session { margin: 0.75rem 0; color: #9aa2ad; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.75rem; text-align: center; }
125
- .transcript-entry { background: #101216; border: 1px solid #2c313a; border-radius: 6px; margin-bottom: 0.75rem; overflow: hidden; }
126
- .transcript-head { color: #9aa2ad; background: #171a20; border-bottom: 1px solid #2c313a; padding: 0.45rem 0.6rem; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.72rem; }
127
- .transcript-text { white-space: pre-wrap; margin: 0; background: transparent; border-radius: 0; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.76rem; }
128
- @media (max-width: 767px) {
129
- .modal-overlay { padding: 0; }
130
- .modal { width: 100%; height: 100%; max-height: none; border-radius: 0; border-left: 0; border-right: 0; }
131
- .columns { display: flex; flex-direction: column; overflow-x: unset; }
132
- .col { min-height: unset; }
133
- .col-toggle { display: inline-flex; }
134
- .col.is-collapsed .col-cards { display: none; }
135
- .col-empty { padding: 0.25rem 0.5rem; }
136
- .col-empty .col-header { margin: 0.1rem 0.4rem; }
137
- .event-summary { grid-template-columns: minmax(8.5rem, 10rem) minmax(8rem, 1fr) 2.2rem; gap: 0.5rem; }
138
- .event-id { display: none; }
139
- }
140
- .tiles { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1rem; }
141
- .tile { background: #1a1d23; border-radius: 10px; padding: 0.6rem 0.9rem; min-width: 120px; }
142
- .tile .n { font-size: 1.3rem; font-weight: 700; }
143
- .tile .l { color: #9aa2ad; font-size: 0.72rem; text-transform: uppercase; }
144
- .toolbar { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
145
- .toolbar label { display: inline-flex; align-items: center; gap: 0.35rem; color: #9aa2ad; font-size: 0.8rem; }
146
- select { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; font-size: 0.8rem; }
147
- select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.15); }
148
- .metric-bar { width: 160px; height: 0.5rem; background: #101216; border-radius: 999px; overflow: hidden; }
149
- .metric-bar-fill { height: 100%; background: var(--accent); border-radius: 999px; }
150
- .stacked-bar { display: flex; width: min(520px, 45vw); min-width: 220px; height: 0.75rem; background: #101216; border-radius: 999px; overflow: hidden; }
151
- .stacked-segment { height: 100%; min-width: 1px; }
152
- .metric-table-value { display: flex; align-items: center; gap: 0.55rem; }
153
- .amber { color: #ffcf7f; }
154
- .red { color: #ff8f7f; }
155
- .ok { color: #7fe3a3; }
156
- input[type=text] { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; margin-bottom: 0.6rem; width: 260px; transition: border-color 0.12s ease, box-shadow 0.12s ease; }
157
- input[type=text]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.15); }
158
- a { color: var(--accent-light); text-decoration: none; }
159
- a:hover { text-decoration: underline; }
160
- .resource-list { list-style: none; padding: 0; margin: 0 0 1rem; }
161
- .resource-list li { display: flex; align-items: baseline; gap: 0.4rem; margin-bottom: 0.35rem; font-size: 0.8rem; }
162
- .action-bar { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; margin: 0.6rem 0 0.2rem; }
163
- .btn { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.22rem 0.55rem; cursor: pointer; font-size: 0.78rem; }
164
- .btn:hover:not(:disabled) { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
165
- .btn.danger { background: #5c1f1a; border-color: #ff8f7f; }
166
- .btn.danger:hover:not(:disabled) { background: #733027; border-color: #ffd0c8; }
167
- .btn:disabled { cursor: default; opacity: 0.62; }
168
- </style>
169
- </head>
170
- <body>
171
- <header class="topbar">
172
- <span class="logo">${logoSvg}</span>
173
- <span class="brand-name">Wake</span>
174
- <span class="version">${wakeVersion}</span>
175
- </header>
176
- <div class="statusbar">
177
- <span id="loop-pill" class="pill">…</span>
178
- <button id="pause-toggle" type="button" class="pause-control">Pause</button>
179
- <button id="force-tick" type="button">Tick now</button>
180
- <span id="status-summary" class="meta"></span>
181
- </div>
182
- <nav>
183
- <button data-view="board" class="active">Board</button>
184
- <button data-view="activity">Activity</button>
185
- <button data-view="runs">Runs</button>
186
- <button data-view="analytics">Analytics</button>
187
- <button data-view="config">Config</button>
188
- <button data-view="health">Health</button>
189
- </nav>
190
- <main id="main"></main>
191
- <div id="modal-overlay" class="modal-overlay">
192
- <section class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
193
- <div class="modal-header">
194
- <h2 id="modal-title"></h2>
195
- <button type="button" class="close" id="modal-close">close &#x2715;</button>
196
- </div>
197
- <div id="modal-tabs" class="modal-tabs"></div>
198
- <div id="modal-body" class="modal-body"></div>
199
- </section>
200
- </div>
201
- <script>
202
- const API = '/api/v1';
203
- const CONDITIONS = ['ready', 'scheduled', 'active', 'needs-human', 'error', 'finished'];
204
- const COND_PILL = {
205
- ready: 'pill-ready',
206
- active: 'pill-active',
207
- scheduled: 'pill-scheduled',
208
- 'needs-human': 'pill-needs-human',
209
- error: 'pill-error',
210
- finished: 'pill-finished',
211
- };
212
- const COND_CARD = {
213
- ready: 'card-ready',
214
- active: 'card-active',
215
- scheduled: 'card-scheduled',
216
- 'needs-human': 'card-needs-human',
217
- error: 'card-error',
218
- };
219
- const BOARD_COLLAPSE_STORAGE_KEY = 'wake:board:collapsed-columns';
220
- let currentView = 'board';
221
- let analyticsWindow = '1d';
222
- let analyticsMetric = 'runs-over-time';
223
- let activeViewRequest = null;
224
- let activeViewRequestId = 0;
225
-
226
- const METRIC_OPTIONS = [
227
- ['runs-over-time', 'Runs over time'],
228
- ['runs-by-status-over-time', 'Runs by status over time'],
229
- ['runs-by-status', 'Runs by status'],
230
- ['runs-by-action', 'Runs by action/stage'],
231
- ['runs-by-repo', 'Runs by repo'],
232
- ['runs-by-runner', 'Runs by runner'],
233
- ['runs-by-model', 'Runs by model'],
234
- ['runs-by-tier', 'Runs by tier'],
235
- ['tokens-over-time', 'Tokens over time'],
236
- ['tokens-by-action', 'Tokens by action/stage'],
237
- ['tokens-by-runner', 'Tokens by runner'],
238
- ['tokens-by-model', 'Tokens by model'],
239
- ['duration-by-action', 'Duration by action/stage'],
240
- ['duration-over-time', 'Duration over time'],
241
- ['work-items-over-time', 'Work items completed over time'],
242
- ['work-item-durations', 'Work item e2e duration'],
243
- ['work-item-totals', 'Work item totals'],
244
- ];
245
-
246
- async function getJson(path, signal) {
247
- const res = await fetch(API + path, signal ? { signal } : undefined);
248
- if (!res.ok) throw new Error(path + ' -> ' + res.status);
249
- return res.json();
250
- }
251
-
252
- async function postJson(path) {
253
- const res = await fetch(API + path, { method: 'POST' });
254
- if (!res.ok) throw new Error(path + ' -> ' + res.status);
255
- return res.json();
256
- }
257
-
258
- async function deleteJson(path) {
259
- const res = await fetch(API + path, { method: 'DELETE' });
260
- if (!res.ok) throw new Error(path + ' -> ' + res.status);
261
- return res.json();
262
- }
263
-
264
- function fmtMs(ms) {
265
- if (ms === undefined || ms === null) return '—';
266
- const s = Math.floor(ms / 1000);
267
- if (s < 60) return s + 's';
268
- const m = Math.floor(s / 60);
269
- if (m < 60) return m + 'm';
270
- const h = Math.floor(m / 60);
271
- if (h < 24) return h + 'h';
272
- return Math.floor(h / 24) + 'd';
273
- }
274
-
275
- function el(tag, attrs, children) {
276
- const node = document.createElement(tag);
277
- for (const [k, v] of Object.entries(attrs || {})) {
278
- if (k === 'text') node.textContent = v;
279
- else if (k.startsWith('on')) node.addEventListener(k.slice(2), v);
280
- else node.setAttribute(k, v);
281
- }
282
- for (const child of children || []) node.appendChild(child);
283
- return node;
284
- }
285
-
286
- function isActiveRequest(requestId) {
287
- return activeViewRequest && activeViewRequest.id === requestId;
288
- }
289
-
290
- function readCollapsedColumns() {
291
- try {
292
- const raw = window.localStorage.getItem(BOARD_COLLAPSE_STORAGE_KEY);
293
- if (!raw) return new Set();
294
- const values = JSON.parse(raw);
295
- if (!Array.isArray(values)) return new Set();
296
- return new Set(values.filter((value) => CONDITIONS.includes(value)));
297
- } catch {
298
- return new Set();
299
- }
300
- }
301
-
302
- function writeCollapsedColumns(collapsed) {
303
- try {
304
- window.localStorage.setItem(BOARD_COLLAPSE_STORAGE_KEY, JSON.stringify([...collapsed]));
305
- } catch {
306
- // Ignore storage failures; the toggle should still work for this render.
307
- }
308
- }
309
-
310
- function setColumnCollapsed(column, button, collapsed) {
311
- const label = collapsed ? 'Expand column' : 'Collapse column';
312
- column.classList.toggle('is-collapsed', collapsed);
313
- button.textContent = collapsed ? '+' : '-';
314
- button.setAttribute('aria-label', label);
315
- button.setAttribute('aria-expanded', String(!collapsed));
316
- button.setAttribute('title', label);
317
- }
318
-
319
- function loadingNode(label) {
320
- return el('p', { class: 'meta', text: 'Loading ' + label + '...' });
321
- }
322
-
323
- async function renderStatusBar() {
324
- try {
325
- const status = await getJson('/status');
326
- const pill = document.getElementById('loop-pill');
327
- pill.textContent = status.loopState;
328
- pill.className = 'pill pill-' + status.loopState;
329
- const pauseButton = document.getElementById('pause-toggle');
330
- pauseButton.dataset.paused = String(status.paused);
331
- pauseButton.textContent = status.paused ? 'Resume' : 'Pause';
332
- pauseButton.classList.toggle('is-paused', status.paused);
333
- const freshness = status.sourceFreshness.level;
334
- const summary = document.getElementById('status-summary');
335
- summary.textContent =
336
- 'runs today: ' + status.runsToday + ' · failures today: ' + status.failuresToday +
337
- ' · cost today: $' + Number(status.costUsdToday ?? 0).toFixed(2) +
338
- ' · source freshness: ' + freshness +
339
- (status.lastRun ? ' · last run: ' + status.lastRun.repo + '#' + status.lastRun.issueNumber + ' ' + status.lastRun.action + ' → ' + (status.lastRun.sentinel ?? status.lastRun.status) : '');
340
- } catch (err) {
341
- document.getElementById('status-summary').textContent = 'status unavailable: ' + err.message;
342
- }
343
- }
344
-
345
- async function togglePause() {
346
- const button = document.getElementById('pause-toggle');
347
- const paused = button.dataset.paused === 'true';
348
- button.disabled = true;
349
- button.textContent = paused ? 'Resuming...' : 'Pausing...';
350
- try {
351
- if (paused) await deleteJson('/pause');
352
- else await postJson('/pause');
353
- await renderStatusBar();
354
- switchView(currentView, { showLoading: false });
355
- } catch (err) {
356
- document.getElementById('status-summary').textContent = 'pause toggle failed: ' + err.message;
357
- } finally {
358
- button.disabled = false;
359
- }
360
- }
361
-
362
- async function forceTickNow() {
363
- const button = document.getElementById('force-tick');
364
- button.disabled = true;
365
- const original = button.textContent;
366
- button.textContent = 'Requested';
367
- try {
368
- await postJson('/tick');
369
- await renderStatusBar();
370
- } catch (err) {
371
- document.getElementById('status-summary').textContent = 'tick request failed: ' + err.message;
372
- } finally {
373
- setTimeout(() => {
374
- button.disabled = false;
375
- button.textContent = original;
376
- }, 900);
377
- }
378
- }
379
-
380
- async function renderBoard(context) {
381
- const board = await getJson('/board', context.signal);
382
- if (!isActiveRequest(context.requestId)) return;
383
- const main = document.getElementById('main');
384
- const collapsedColumns = readCollapsedColumns();
385
- const columns = el('div', { class: 'columns' }, CONDITIONS.map((cond) => {
386
- const items = board.filter((c) => c.condition === cond);
387
- if (cond === 'finished') items.sort((a, b) => a.timeInStageMs - b.timeInStageMs);
388
- const cards = items.map((item) => {
389
- const cardCondClass = COND_CARD[item.condition] ?? '';
390
- return el('div', {
391
- class: 'card' + (cardCondClass ? ' ' + cardCondClass : ''),
392
- onclick: () => openItemModal(item.repo, item.number, item),
393
- }, [
394
- el('div', { class: 'title', text: item.repo + '#' + item.number + ' ' + item.title }),
395
- ...renderCardSummaryNodes(item),
396
- ]);
397
- });
398
- const isCollapsed = collapsedColumns.has(cond);
399
- const toggle = el('button', {
400
- class: 'col-toggle',
401
- type: 'button',
402
- onclick: () => {
403
- const nextCollapsed = !collapsedColumns.has(cond);
404
- if (nextCollapsed) collapsedColumns.add(cond);
405
- else collapsedColumns.delete(cond);
406
- writeCollapsedColumns(collapsedColumns);
407
- setColumnCollapsed(column, toggle, nextCollapsed);
408
- },
409
- });
410
- const column = el('div', { class: 'col' + (items.length === 0 ? ' col-empty' : '') }, [
411
- el('div', { class: 'col-header' }, [
412
- el('h2', { text: cond + ' (' + items.length + ')' }),
413
- toggle,
414
- ]),
415
- el('div', { class: 'col-cards' }, cards),
416
- ]);
417
- setColumnCollapsed(column, toggle, isCollapsed);
418
- return column;
419
- }));
420
- main.replaceChildren(columns);
421
- }
422
-
423
- function renderChildRun(run) {
424
- return el('div', { class: 'child-run' }, [
425
- el('span', { class: 'dot' + (run.isWatcher ? ' dot-watch' : '') }),
426
- el('div', { class: 'run-title', text: (run.isWatcher ? 'watcher ' : '') + run.action + ' running' }),
427
- el('div', {
428
- class: 'run-meta',
429
- text: [run.runnerName, run.tier, fmtMs(run.ageMs)].filter(Boolean).join(' | '),
430
- }),
431
- ]);
432
- }
433
-
434
- function renderCardSummaryNodes(item) {
435
- const nonWakeLabels = (item.labels || []).filter((l) => !l.startsWith('wake:'));
436
- const pillClass = 'pill ' + (COND_PILL[item.condition] || 'pill-finished');
437
- const statsText = item.totalRuns + ' runs | ' + fmtCost(item.totalCostUsd) + ' | ' + fmtCompact(item.totalTokens) + ' tokens';
438
- const isFinished = item.condition === 'finished';
439
- const displayStatus = item.displayStatus || item.lastRunStatus || item.stage;
440
- const metaPills = [
441
- el('span', { class: pillClass, text: displayStatus.replace(/-/g, ' ') }),
442
- ...(item.isFrozen ? [el('span', { class: 'pill pill-frozen', text: 'Frozen' })] : []),
443
- el('span', { class: 'chip chip-meta', text: item.workflow }),
444
- el('span', { class: 'chip chip-meta', text: item.stage }),
445
- ];
446
- if (!isFinished) {
447
- metaPills.push(el('span', { class: 'chip chip-meta', text: fmtMs(item.timeInStageMs) + ' in stage' }));
448
- }
449
- return [
450
- el('div', { class: 'meta', style: 'display:flex;align-items:center;gap:0.3rem;flex-wrap:wrap;margin-top:0.2rem;' }, metaPills),
451
- ...(nonWakeLabels.length > 0
452
- ? [el('div', { class: 'meta', style: 'margin-top:0.2rem;' }, nonWakeLabels.map((label) => el('span', { class: 'chip chip-label', text: label })))]
453
- : []),
454
- el('div', { class: 'card-stats', text: statsText }),
455
- ...(item.activeMainRun ? [renderChildRun(item.activeMainRun)] : []),
456
- ...((item.activeChildRuns || []).map(renderChildRun)),
457
- ];
458
- }
459
-
460
- function resourceUriToUrl(resourceUri) {
461
- const parts = resourceUri.split(':');
462
- if (parts.length < 3) return null;
463
- const provider = parts[0];
464
- const type = parts[1];
465
- const locator = parts.slice(2).join(':');
466
- const m = locator.match(/^(.+)#([0-9]+)$/);
467
- if (!m) return null;
468
- if (provider === 'github') {
469
- if (type === 'issue') return 'https://github.com/' + m[1] + '/issues/' + m[2];
470
- if (type === 'pr') return 'https://github.com/' + m[1] + '/pull/' + m[2];
471
- }
472
- return null;
473
- }
474
-
475
- async function openItemModal(repo, number, boardItem) {
476
- const overlay = document.getElementById('modal-overlay');
477
- const title = document.getElementById('modal-title');
478
- const tabs = document.getElementById('modal-tabs');
479
- const body = document.getElementById('modal-body');
480
- const loaded = new Map();
481
- body.textContent = 'Loading...';
482
- tabs.innerHTML = '';
483
- title.textContent = repo + '#' + number;
484
- overlay.classList.add('open');
485
-
486
- for (const tab of ['details', 'events', 'transcripts']) {
487
- tabs.appendChild(el('button', {
488
- type: 'button',
489
- class: 'nav-button',
490
- text: tab[0].toUpperCase() + tab.slice(1),
491
- onclick: () => switchItemTab(tab),
492
- }));
493
- }
494
-
495
- async function ensureDetail() {
496
- if (!loaded.has('detail')) {
497
- const itemPath =
498
- boardItem && boardItem.workItemKey
499
- ? '/items/' + encodeURIComponent(boardItem.workItemKey)
500
- : '/items/' + encodeURIComponent(repo) + '/' + number;
501
- loaded.set('detail', await getJson(itemPath));
502
- }
503
- return loaded.get('detail');
504
- }
505
-
506
- async function switchItemTab(tab) {
507
- for (const button of tabs.querySelectorAll('button')) {
508
- button.classList.toggle('active', button.textContent.toLowerCase() === tab);
509
- }
510
- body.scrollTop = 0;
511
- if (!loaded.has(tab)) {
512
- body.textContent = 'Loading...';
513
- loaded.set(tab, await renderItemTab(tab));
514
- }
515
- body.replaceChildren(loaded.get(tab));
516
- if (tab === 'transcripts') {
517
- body.scrollTop = body.scrollHeight;
518
- }
519
- }
520
-
521
- async function renderItemTab(tab) {
522
- const detail = await ensureDetail();
523
- if (!detail) return el('p', { text: 'Not found' });
524
- if (tab === 'events') {
525
- return renderItemEvents(detail.events || []);
526
- }
527
- if (tab === 'transcripts') {
528
- const transcripts = await getJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/transcripts');
529
- return renderTranscripts(transcripts);
530
- }
531
- return renderItemDetails(detail, boardItem);
532
- }
533
-
534
- await switchItemTab('details');
535
- }
536
-
537
- function eventTimestamp(event) {
538
- return event.ingestedAt || event.occurredAt || '';
539
- }
540
-
541
- function eventDirectionSymbol(direction) {
542
- if (direction === 'inbound') return '\\u2193';
543
- if (direction === 'outbound') return '\\u2191';
544
- return '\\u2194';
545
- }
546
-
547
- function sortedEventsNewestFirst(events) {
548
- return [...events].sort((left, right) => eventTimestamp(right).localeCompare(eventTimestamp(left)));
549
- }
550
-
551
- async function copyText(text, button) {
552
- const original = button.textContent;
553
- try {
554
- if (navigator.clipboard && window.isSecureContext) {
555
- await navigator.clipboard.writeText(text);
556
- } else {
557
- const input = document.createElement('textarea');
558
- input.value = text;
559
- input.setAttribute('readonly', 'readonly');
560
- input.style.position = 'fixed';
561
- input.style.top = '-1000px';
562
- document.body.appendChild(input);
563
- input.select();
564
- document.execCommand('copy');
565
- input.remove();
566
- }
567
- button.textContent = 'Copied';
568
- } catch (err) {
569
- button.textContent = 'Copy failed';
570
- } finally {
571
- setTimeout(() => {
572
- button.textContent = original;
573
- }, 1100);
574
- }
575
- }
576
-
577
- function renderItemEvents(events) {
578
- if (events.length === 0) {
579
- return el('p', { class: 'meta', text: 'No events available for this item.' });
580
- }
581
-
582
- const root = el('div', { class: 'event-list' });
583
- for (const event of sortedEventsNewestFirst(events)) {
584
- const json = JSON.stringify(event, null, 2);
585
- const card = el('article', { class: 'event-card' });
586
- const summary = el('button', { type: 'button', class: 'event-summary' }, [
587
- el('span', { class: 'event-time', text: eventTimestamp(event) }),
588
- el('span', { class: 'event-type', text: event.sourceEventType || 'unknown event' }),
589
- el('span', { class: 'event-id', text: event.eventId || '' }),
590
- el('span', { class: 'event-direction', title: event.direction || 'unknown', text: eventDirectionSymbol(event.direction) }),
591
- ]);
592
- summary.addEventListener('click', () => {
593
- card.classList.toggle('expanded');
594
- summary.setAttribute('aria-expanded', String(card.classList.contains('expanded')));
595
- });
596
- const copyButton = el('button', { type: 'button', class: 'event-copy', text: 'Copy' });
597
- copyButton.addEventListener('click', (clickEvent) => {
598
- clickEvent.stopPropagation();
599
- copyText(json, copyButton);
600
- });
601
- const jsonArea = el('div', { class: 'event-json' }, [
602
- copyButton,
603
- el('pre', { text: json }),
604
- ]);
605
- card.appendChild(summary);
606
- card.appendChild(jsonArea);
607
- root.appendChild(card);
608
- }
609
- return root;
610
- }
611
-
612
- function renderItemDetails(detail, boardItem) {
613
- const body = el('div');
614
- const repo = detail.item.issue.repo;
615
- const number = detail.item.issue.number;
616
- const headLink = el('a', { href: detail.item.issue.url, target: '_blank', rel: 'noopener noreferrer', text: repo + '#' + number });
617
- body.appendChild(el('h3', {}, [headLink]));
618
- body.appendChild(el('p', { text: detail.item.issue.title }));
619
- if (boardItem) {
620
- body.appendChild(el('div', { class: 'item-summary' }, renderCardSummaryNodes(boardItem)));
621
- }
622
- const details = [];
623
- if (detail.item.wake.sessionId) {
624
- details.push(['Session', detail.item.wake.sessionId]);
625
- }
626
- if (detail.item.wake.workspacePath) {
627
- details.push(['Workspace', detail.item.wake.workspacePath]);
628
- }
629
- if (details.length > 0) {
630
- body.appendChild(el('dl', { class: 'detail-kv' }, details.flatMap(([label, value]) => [
631
- el('dt', { text: label }),
632
- el('dd', { text: value }),
633
- ])));
634
- }
635
- const isFrozen = detail.item.context.frozen !== undefined && detail.item.context.frozen !== null;
636
- const actionBar = el('div', { class: 'action-bar' });
637
- const lastRun = detail.runs.at(-1);
638
- if (lastRun && lastRun.sentinel === 'FAILED') {
639
- const retryBtn = el('button', { type: 'button', class: 'btn', text: 'Retry' });
640
- retryBtn.addEventListener('click', async () => {
641
- retryBtn.disabled = true;
642
- retryBtn.textContent = 'Queuing retry...';
643
- try {
644
- await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/retry');
645
- retryBtn.textContent = 'Retry queued';
646
- } catch (err) {
647
- retryBtn.disabled = false;
648
- retryBtn.textContent = 'Retry';
649
- document.getElementById('status-summary').textContent = 'retry failed: ' + err.message;
650
- }
651
- });
652
- actionBar.appendChild(retryBtn);
653
- }
654
- if (detail.item.issue.labels.includes('wake:scheduled-workflow')) {
655
- const runNowBtn = el('button', { type: 'button', class: 'btn', text: 'Run now' });
656
- runNowBtn.addEventListener('click', async () => {
657
- runNowBtn.disabled = true;
658
- runNowBtn.textContent = 'Requesting run...';
659
- try {
660
- await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/run');
661
- runNowBtn.textContent = 'Run requested';
662
- } catch (err) {
663
- runNowBtn.disabled = false;
664
- runNowBtn.textContent = 'Run now';
665
- document.getElementById('status-summary').textContent = 'run request failed: ' + err.message;
666
- }
667
- });
668
- actionBar.appendChild(runNowBtn);
669
- }
670
- const freezeBtn = el('button', { type: 'button', class: 'btn', text: isFrozen ? 'Unfreeze' : 'Freeze' });
671
- freezeBtn.addEventListener('click', async () => {
672
- const action = isFrozen ? 'unfreeze' : 'freeze';
673
- freezeBtn.disabled = true;
674
- freezeBtn.textContent = isFrozen ? 'Unfreezing...' : 'Freezing...';
675
- try {
676
- await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/' + action);
677
- freezeBtn.textContent = isFrozen ? 'Unfrozen' : 'Frozen';
678
- document.getElementById('status-summary').textContent = isFrozen ? 'work item unfrozen' : 'work item frozen';
679
- } catch (err) {
680
- freezeBtn.disabled = false;
681
- freezeBtn.textContent = isFrozen ? 'Unfreeze' : 'Freeze';
682
- document.getElementById('status-summary').textContent = action + ' failed: ' + err.message;
683
- }
684
- });
685
- actionBar.appendChild(freezeBtn);
686
-
687
- const deleteBtn = el('button', { type: 'button', class: 'btn danger', text: 'Delete' });
688
- deleteBtn.addEventListener('click', async () => {
689
- if (!confirm('Delete this work item from the board and remove its resource correlations?')) return;
690
- deleteBtn.disabled = true;
691
- deleteBtn.textContent = 'Deleting...';
692
- try {
693
- await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/delete');
694
- deleteBtn.textContent = 'Deleted';
695
- document.getElementById('status-summary').textContent = 'work item deleted';
696
- switchView(currentView, { showLoading: false });
697
- } catch (err) {
698
- deleteBtn.disabled = false;
699
- deleteBtn.textContent = 'Delete';
700
- document.getElementById('status-summary').textContent = 'delete failed: ' + err.message;
701
- }
702
- });
703
- actionBar.appendChild(deleteBtn);
704
- if (actionBar.childNodes.length > 0) {
705
- body.appendChild(actionBar);
706
- }
707
- const resources = detail.item.correlatedResources || [];
708
- if (resources.length > 0) {
709
- body.appendChild(el('h3', { text: 'Resources' }));
710
- const resourceList = el('ul', { class: 'resource-list' });
711
- for (const res of resources) {
712
- const resUrl = resourceUriToUrl(res.resourceUri);
713
- const badge = el('span', { class: 'chip', text: res.role });
714
- const linkOrText = resUrl
715
- ? el('a', { href: resUrl, target: '_blank', rel: 'noopener noreferrer', text: res.resourceUri })
716
- : el('span', { class: 'meta', text: res.resourceUri });
717
- resourceList.appendChild(el('li', {}, [badge, linkOrText]));
718
- }
719
- body.appendChild(resourceList);
720
- }
721
- body.appendChild(el('h3', { text: 'Runs' }));
722
- body.appendChild(el('table', {}, [
723
- el('tr', {}, ['action', 'status', 'sentinel', 'started', 'runId'].map((h) => el('th', { text: h }))),
724
- ...detail.runs.map((r) => el('tr', {}, [
725
- el('td', { text: r.action }), el('td', { text: r.status }), el('td', { text: r.sentinel || '' }),
726
- el('td', { text: r.startedAt }), el('td', { text: r.runId }),
727
- ])),
728
- ]));
729
- body.appendChild(el('h3', { text: 'Context' }));
730
- body.appendChild(el('pre', { text: JSON.stringify(detail.item.context, null, 2) }));
731
- return body;
732
- }
733
-
734
- function renderTranscripts(transcripts) {
735
- if (!transcripts.enabled) {
736
- return el('p', { class: 'meta', text: 'Transcripts are not enabled \\u2014 set transcripts.enabled: true in config to turn them on.' });
737
- }
738
- if (!transcripts.sessions || transcripts.sessions.length === 0) {
739
- return el('p', { class: 'meta', text: 'No transcripts available for this item.' });
740
- }
741
- const root = el('div');
742
- for (const session of transcripts.sessions) {
743
- const firstStartedAt = session.entries[0] ? session.entries[0].startedAt : '';
744
- const sessionLabel = session.sessionId || session.sessionKey;
745
- root.appendChild(
746
- el('div', {
747
- class: 'transcript-session',
748
- text:
749
- '\\u2014 new session \\u00b7 ' +
750
- sessionLabel +
751
- ' \\u00b7 ' +
752
- firstStartedAt +
753
- ' \\u2014',
754
- }),
755
- );
756
- for (const entry of session.entries) {
757
- root.appendChild(el('article', { class: 'transcript-entry' }, [
758
- el('div', { class: 'transcript-head', text: entry.role + ' \\u00b7 ' + entry.action + ' \\u00b7 ' + entry.cli + ' \\u00b7 ' + entry.startedAt }),
759
- el('pre', { class: 'transcript-text', text: entry.text }),
760
- ]));
761
- }
762
- }
763
- return root;
764
- }
765
-
766
- async function renderActivity(context) {
767
- const events = await getJson('/events?limit=200', context.signal);
768
- if (!isActiveRequest(context.requestId)) return;
769
- const main = document.getElementById('main');
770
- main.replaceChildren(el('table', {}, [
771
- el('tr', {}, ['time', 'direction', 'type', 'work item'].map((h) => el('th', { text: h }))),
772
- ...events.map((ev) => el('tr', {}, [
773
- el('td', { text: ev.ingestedAt }), el('td', { text: ev.direction }),
774
- el('td', { text: ev.sourceEventType }), el('td', { text: ev.workItemKey }),
775
- ])),
776
- ]));
777
- }
778
-
779
- function fmtCost(usd) {
780
- if (usd === undefined || usd === null) return '';
781
- return '$' + Number(usd).toFixed(usd < 1 ? 4 : 2);
782
- }
783
-
784
- function fmtTokens(tokenUsage) {
785
- if (!tokenUsage) return '';
786
- const total =
787
- (tokenUsage.inputTokens || 0) + (tokenUsage.outputTokens || 0) +
788
- (tokenUsage.cacheCreationInputTokens || 0) + (tokenUsage.cacheReadInputTokens || 0);
789
- return total >= 1000 ? (total / 1000).toFixed(1) + 'k' : String(total);
790
- }
791
-
792
- function fmtCompact(value) {
793
- const n = Number(value || 0);
794
- if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
795
- if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
796
- return String(n);
797
- }
798
-
799
- function fmtNumber(value) {
800
- return Number(value || 0).toLocaleString();
801
- }
802
-
803
- function fmtDuration(ms) {
804
- return ms === undefined || ms === null ? '' : fmtMs(ms);
805
- }
806
-
807
- function metricBar(value, max) {
808
- const pct = max > 0 ? Math.max(2, Math.round((value / max) * 100)) : 0;
809
- return el('div', { class: 'metric-bar' }, [
810
- el('div', { class: 'metric-bar-fill', style: 'width:' + pct + '%' }),
811
- ]);
812
- }
813
-
814
- function stackedBar(segments) {
815
- const total = segments.reduce((sum, segment) => sum + segment.value, 0);
816
- return el('div', { class: 'stacked-bar' }, segments.filter((segment) => segment.value > 0).map((segment) =>
817
- el('div', {
818
- class: 'stacked-segment',
819
- title: segment.label + ': ' + fmtNumber(segment.value),
820
- style: 'width:' + ((segment.value / total) * 100).toFixed(2) + '%;background:' + segment.color,
821
- }),
822
- ));
823
- }
824
-
825
- function selectBox(value, options, onChange) {
826
- return el('select', { onchange: (event) => onChange(event.target.value) }, options.map((option) =>
827
- el('option', {
828
- value: option[0],
829
- text: option[1],
830
- ...(option[0] === value ? { selected: 'selected' } : {}),
831
- }),
832
- ));
833
- }
834
-
835
- function metricValueCell(value, max, format) {
836
- return el('td', {}, [
837
- el('div', { class: 'metric-table-value' }, [
838
- el('span', { text: format(value) }),
839
- metricBar(value, max),
840
- ]),
841
- ]);
842
- }
843
-
844
- function renderCountTable(rows, label) {
845
- if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
846
- const max = Math.max(...rows.map((row) => row.count));
847
- return el('table', {}, [
848
- el('tr', {}, [el('th', { text: label }), el('th', { text: 'runs' })]),
849
- ...rows.map((row) => el('tr', {}, [
850
- el('td', { text: row.key }),
851
- metricValueCell(row.count, max, fmtNumber),
852
- ])),
853
- ]);
854
- }
855
-
856
- function renderTokenTable(rows, label) {
857
- if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
858
- const max = Math.max(...rows.map((row) => row.tokens));
859
- return el('table', {}, [
860
- el('tr', {}, [
861
- el('th', { text: label }),
862
- el('th', { text: 'tokens' }),
863
- el('th', { text: 'avg/run' }),
864
- el('th', { text: 'cost' }),
865
- el('th', { text: 'avg cost/run' }),
866
- el('th', { text: 'runs' }),
867
- ]),
868
- ...rows.map((row) => el('tr', {}, [
869
- el('td', { text: row.key }),
870
- metricValueCell(row.tokens, max, fmtNumber),
871
- el('td', { text: fmtNumber(row.averageTokens || 0) }),
872
- el('td', { text: fmtCost(row.costUsd) }),
873
- el('td', { text: fmtCost(row.averageCostUsd) }),
874
- el('td', { text: fmtNumber(row.count || 0) }),
875
- ])),
876
- ]);
877
- }
878
-
879
- function renderDurationTable(rows, label) {
880
- if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
881
- const max = Math.max(...rows.map((row) => row.totalMs || row.medianMs));
882
- return el('table', {}, [
883
- el('tr', {}, [
884
- el('th', { text: label }),
885
- el('th', { text: 'total' }),
886
- el('th', { text: 'median' }),
887
- el('th', { text: 'average' }),
888
- el('th', { text: 'runs' }),
889
- ]),
890
- ...rows.map((row) => el('tr', {}, [
891
- el('td', { text: row.key || row.label }),
892
- metricValueCell(row.totalMs || 0, max, fmtDuration),
893
- el('td', { text: fmtDuration(row.medianMs) }),
894
- el('td', { text: fmtDuration(row.averageMs) }),
895
- el('td', { text: fmtNumber(row.count) }),
896
- ])),
897
- ]);
898
- }
899
-
900
- function renderRunsOverTime(rows) {
901
- return el('table', {}, [
902
- el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'runs' }), el('th', { text: 'status split' })]),
903
- ...rows.map((row) => el('tr', {}, [
904
- el('td', { text: row.label }),
905
- el('td', { text: fmtNumber(row.total) }),
906
- el('td', {}, [stackedBar([
907
- { label: 'completed', value: row.completed, color: '#7fe3a3' },
908
- { label: 'blocked', value: row.blocked, color: '#ffcf7f' },
909
- { label: 'awaiting approval', value: row.awaitingApproval, color: '#7fb3ff' },
910
- { label: 'failed', value: row.failed, color: '#ff8f7f' },
911
- { label: 'other', value: row.other, color: '#9aa2ad' },
912
- ])]),
913
- ])),
914
- ]);
915
- }
916
-
917
- function renderRunsByStatusOverTime(rows) {
918
- return el('table', {}, [
919
- el('tr', {}, [
920
- el('th', { text: 'bucket' }),
921
- el('th', { text: 'completed' }),
922
- el('th', { text: 'blocked' }),
923
- el('th', { text: 'awaiting approval' }),
924
- el('th', { text: 'failed' }),
925
- el('th', { text: 'other' }),
926
- el('th', { text: 'total' }),
927
- ]),
928
- ...rows.map((row) => el('tr', {}, [
929
- el('td', { text: row.label }),
930
- el('td', { text: fmtNumber(row.completed) }),
931
- el('td', { text: fmtNumber(row.blocked) }),
932
- el('td', { text: fmtNumber(row.awaitingApproval) }),
933
- el('td', { text: fmtNumber(row.failed) }),
934
- el('td', { text: fmtNumber(row.other) }),
935
- el('td', { text: fmtNumber(row.total) }),
936
- ])),
937
- ]);
938
- }
939
-
940
- function renderTokensOverTime(rows) {
941
- return el('table', {}, [
942
- el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'tokens' }), el('th', { text: 'token split' }), el('th', { text: 'cost' })]),
943
- ...rows.map((row) => el('tr', {}, [
944
- el('td', { text: row.label }),
945
- el('td', { text: fmtNumber(row.tokens) }),
946
- el('td', {}, [stackedBar([
947
- { label: 'input', value: row.inputTokens, color: '#7fb3ff' },
948
- { label: 'output', value: row.outputTokens, color: '#7fe3a3' },
949
- { label: 'cache creation', value: row.cacheCreationInputTokens, color: '#c79bff' },
950
- { label: 'cache read', value: row.cacheReadInputTokens, color: '#ffcf7f' },
951
- ])]),
952
- el('td', { text: fmtCost(row.costUsd) }),
953
- ])),
954
- ]);
955
- }
956
-
957
- function renderWorkItemsOverTime(rows) {
958
- const max = Math.max(0, ...rows.map((row) => row.completed));
959
- return el('table', {}, [
960
- el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'completed' })]),
961
- ...rows.map((row) => el('tr', {}, [
962
- el('td', { text: row.label }),
963
- metricValueCell(row.completed, max, fmtNumber),
964
- ])),
965
- ]);
966
- }
967
-
968
- function renderWorkItemDurations(rows) {
969
- if (rows.length === 0) return el('p', { class: 'meta', text: 'No completed work items for this window.' });
970
- const max = Math.max(...rows.map((row) => row.durationMs));
971
- return el('table', {}, [
972
- el('tr', {}, ['work item', 'duration', 'completed'].map((h) => el('th', { text: h }))),
973
- ...rows.map((row) => el('tr', {}, [
974
- el('td', { text: row.repo + '#' + row.issueNumber }),
975
- metricValueCell(row.durationMs, max, fmtDuration),
976
- el('td', { text: row.completedAt }),
977
- ])),
978
- ]);
979
- }
980
-
981
- function renderWorkItemTotals(rows) {
982
- if (rows.length === 0) return el('p', { class: 'meta', text: 'No runs for this window.' });
983
- const maxTokens = Math.max(...rows.map((row) => row.totalTokens));
984
- return el('table', {}, [
985
- el('tr', {}, ['work item', 'runs', 'total tokens', 'total duration'].map((h) => el('th', { text: h }))),
986
- ...rows.map((row) => el('tr', {}, [
987
- el('td', { text: row.key }),
988
- el('td', { text: String(row.runCount) }),
989
- metricValueCell(row.totalTokens, maxTokens, fmtNumber),
990
- el('td', { text: fmtMs(row.totalDurationMs) }),
991
- ])),
992
- ]);
993
- }
994
-
995
- function renderMetricDetail(detail) {
996
- if (detail.kind === 'runs-over-time') return renderRunsOverTime(detail.rows);
997
- if (detail.kind === 'runs-by-status-over-time') return renderRunsByStatusOverTime(detail.rows);
998
- if (detail.kind === 'run-counts') return renderCountTable(detail.rows, detail.group);
999
- if (detail.kind === 'tokens-over-time') return renderTokensOverTime(detail.rows);
1000
- if (detail.kind === 'token-counts') return renderTokenTable(detail.rows, detail.group);
1001
- if (detail.kind === 'durations') return renderDurationTable(detail.rows, detail.group);
1002
- if (detail.kind === 'duration-over-time') return renderDurationTable(detail.rows, 'bucket');
1003
- if (detail.kind === 'work-items-over-time') return renderWorkItemsOverTime(detail.rows);
1004
- if (detail.kind === 'work-item-durations') return renderWorkItemDurations(detail.rows);
1005
- if (detail.kind === 'work-item-totals') return renderWorkItemTotals(detail.rows);
1006
- return el('p', { class: 'meta', text: 'Unsupported metric.' });
1007
- }
1008
-
1009
- async function renderAnalytics(context) {
1010
- const metrics = await getJson('/metrics?window=' + encodeURIComponent(analyticsWindow) + '&metric=' + encodeURIComponent(analyticsMetric), context.signal);
1011
- if (!isActiveRequest(context.requestId)) return;
1012
- const main = document.getElementById('main');
1013
- main.replaceChildren(
1014
- el('div', { class: 'toolbar' }, [
1015
- el('label', {}, [
1016
- document.createTextNode('Window'),
1017
- selectBox(analyticsWindow, [['1d', '1d'], ['3d', '3d'], ['5d', '5d'], ['7d', '7d']], (value) => {
1018
- analyticsWindow = value;
1019
- switchView('analytics');
1020
- }),
1021
- ]),
1022
- el('label', {}, [
1023
- document.createTextNode('Metric'),
1024
- selectBox(analyticsMetric, METRIC_OPTIONS, (value) => {
1025
- analyticsMetric = value;
1026
- switchView('analytics');
1027
- }),
1028
- ]),
1029
- ]),
1030
- el('div', { class: 'tiles' }, [
1031
- tile('Runs', fmtNumber(metrics.summary.totalRuns)),
1032
- tile('Completed', fmtNumber(metrics.summary.completedRuns)),
1033
- tile('Blocked/approval', fmtNumber(metrics.summary.blockedRuns + metrics.summary.awaitingApprovalRuns)),
1034
- tile('Failed', fmtNumber(metrics.summary.failedRuns)),
1035
- tile('Tokens', fmtNumber(metrics.summary.totalTokens)),
1036
- tile('Cost', fmtCost(metrics.summary.totalCostUsd)),
1037
- tile('Median run', fmtDuration(metrics.summary.medianRunDurationMs) || '—'),
1038
- tile('Work done', fmtNumber(metrics.summary.completedWorkItems)),
1039
- tile('Median e2e', fmtDuration(metrics.summary.medianWorkItemDurationMs) || '—'),
1040
- ]),
1041
- renderMetricDetail(metrics.detail),
1042
- );
1043
- }
1044
-
1045
- async function renderRuns(context) {
1046
- const runs = await getJson('/runs', context.signal);
1047
- if (!isActiveRequest(context.requestId)) return;
1048
- const main = document.getElementById('main');
1049
- main.replaceChildren(el('table', {}, [
1050
- el('tr', {}, ['repo#issue', 'action', 'status', 'sentinel', 'runner', 'tokens', 'cost', 'started', 'finished'].map((h) => el('th', { text: h }))),
1051
- ...runs.map((r) => el('tr', {}, [
1052
- el('td', { text: r.repo + '#' + r.issueNumber }), el('td', { text: r.action }), el('td', { text: r.status }),
1053
- el('td', { text: r.sentinel || '' }), el('td', { text: r.routing ? r.routing.runnerName : '' }),
1054
- el('td', { text: fmtTokens(r.tokenUsage) }), el('td', { text: fmtCost(r.tokenUsage && r.tokenUsage.costUsd) }),
1055
- el('td', { text: r.startedAt }), el('td', { text: r.finishedAt || '' }),
1056
- ])),
1057
- ]));
1058
- }
1059
-
1060
- async function renderConfig(context) {
1061
- const data = await getJson('/config', context.signal);
1062
- if (!isActiveRequest(context.requestId)) return;
1063
- const main = document.getElementById('main');
1064
- main.replaceChildren(
1065
- el('h3', { text: 'Routing table' }),
1066
- el('table', {}, [
1067
- el('tr', {}, ['stage', 'action', 'tier', 'runner', 'model', 'fallback order'].map((h) => el('th', { text: h }))),
1068
- ...data.routingTable.map((r) => el('tr', {}, [
1069
- el('td', { text: r.stage }), el('td', { text: r.action || '' }), el('td', { text: r.tier || '' }),
1070
- el('td', { text: r.runnerName || '' }), el('td', { text: r.model || '' }),
1071
- el('td', {}, (r.candidates || []).map((c) => {
1072
- if (!c.paused) return el('span', { class: 'chip', text: c.runnerName });
1073
- const btn = el('button', { type: 'button', class: 'btn', text: 'Unpause', style: 'margin-top:0;font-size:0.7rem;padding:0.1rem 0.4rem;' });
1074
- btn.addEventListener('click', async () => {
1075
- btn.disabled = true;
1076
- btn.textContent = 'Unpausing…';
1077
- try {
1078
- await postJson('/runners/' + encodeURIComponent(c.runnerName) + '/unpause');
1079
- switchView('config');
1080
- } catch (err) {
1081
- btn.disabled = false;
1082
- btn.textContent = 'Unpause';
1083
- document.getElementById('status-summary').textContent = 'unpause failed: ' + err.message;
1084
- }
1085
- });
1086
- return el('span', { style: 'display:inline-flex;align-items:center;gap:0.25rem;margin-right:0.25rem;' }, [
1087
- el('span', { class: 'chip amber', text: c.runnerName + ' (paused)' }),
1088
- btn,
1089
- ]);
1090
- })),
1091
- ])),
1092
- ]),
1093
- el('h3', { text: 'Effective config (redacted)' }),
1094
- el('pre', { text: JSON.stringify(data.config, null, 2) }),
1095
- );
1096
- }
1097
-
1098
- async function renderHealth(context) {
1099
- const health = await getJson('/health', context.signal);
1100
- if (!isActiveRequest(context.requestId)) return;
1101
- const main = document.getElementById('main');
1102
- const runnerNames = Object.keys(health.pause.runnerHealth || {});
1103
- const runnerSection = runnerNames.length === 0
1104
- ? el('p', { class: 'meta', text: 'No quota failures recorded.' })
1105
- : el('table', {}, [
1106
- el('tr', {}, ['runner', 'status', 'paused until', 'failure count', 'last failure'].map((h) => el('th', { text: h }))),
1107
- ...runnerNames.map((name) => {
1108
- const entry = health.pause.runnerHealth[name];
1109
- const paused = entry.pausedUntil && Date.parse(entry.pausedUntil) > Date.now();
1110
- return el('tr', {}, [
1111
- el('td', { text: name }),
1112
- el('td', {}, [el('span', { class: 'chip' + (paused ? ' amber' : ' ok') , text: paused ? 'paused' : 'available' })]),
1113
- el('td', { text: entry.pausedUntil || '' }),
1114
- el('td', { text: String(entry.failureCount || 0) }),
1115
- el('td', { text: entry.lastFailureAt || '' }),
1116
- ]);
1117
- }),
1118
- ]);
1119
- main.replaceChildren(
1120
- el('div', { class: 'tiles' }, [
1121
- tile('Tick lock', health.lock.present ? (health.lock.stale ? 'stale' : 'held') : 'free'),
1122
- tile('Paused', String(health.pause.paused)),
1123
- tile('Runners paused now', String(runnerNames.filter((name) => {
1124
- const until = health.pause.runnerHealth[name].pausedUntil;
1125
- return until && Date.parse(until) > Date.now();
1126
- }).length)),
1127
- tile('Integrity issues', String(health.integrityIssues.length)),
1128
- ]),
1129
- el('h3', { text: 'Runner health (quota fallback, #67)' }),
1130
- runnerSection,
1131
- el('h3', { text: 'Storage' }),
1132
- el('pre', { text: JSON.stringify(health.storage, null, 2) }),
1133
- el('h3', { text: 'Source polling' }),
1134
- el('pre', { text: JSON.stringify(health.sources, null, 2) }),
1135
- el('h3', { text: 'Integrity issues' }),
1136
- el('pre', { text: JSON.stringify(health.integrityIssues, null, 2) }),
1137
- );
1138
- }
1139
-
1140
- function tile(label, value) {
1141
- return el('div', { class: 'tile' }, [el('div', { class: 'n', text: value }), el('div', { class: 'l', text: label })]);
1142
- }
1143
-
1144
- const renderers = { board: renderBoard, activity: renderActivity, runs: renderRuns, analytics: renderAnalytics, config: renderConfig, health: renderHealth };
1145
-
1146
- function switchView(view, options) {
1147
- if (activeViewRequest) activeViewRequest.controller.abort();
1148
- const controller = new AbortController();
1149
- const requestId = ++activeViewRequestId;
1150
- activeViewRequest = { id: requestId, controller };
1151
- currentView = view;
1152
- for (const btn of document.querySelectorAll('nav button')) {
1153
- btn.classList.toggle('active', btn.dataset.view === view);
1154
- }
1155
- const main = document.getElementById('main');
1156
- if (!options || options.showLoading !== false) {
1157
- main.replaceChildren(loadingNode(view));
1158
- }
1159
- renderers[view]({ signal: controller.signal, requestId }).catch((err) => {
1160
- if (err.name === 'AbortError' || !isActiveRequest(requestId)) return;
1161
- main.replaceChildren(el('p', { class: 'meta red', text: 'Failed to load ' + view + ': ' + err.message }));
1162
- });
1163
- }
1164
-
1165
- for (const btn of document.querySelectorAll('nav button')) {
1166
- btn.addEventListener('click', () => switchView(btn.dataset.view));
1167
- }
1168
- document.getElementById('modal-close').addEventListener('click', () => {
1169
- document.getElementById('modal-overlay').classList.remove('open');
1170
- });
1171
- document.getElementById('modal-overlay').addEventListener('click', (event) => {
1172
- if (event.target.id === 'modal-overlay') {
1173
- event.currentTarget.classList.remove('open');
1174
- }
1175
- });
1176
- document.getElementById('force-tick').addEventListener('click', forceTickNow);
1177
- document.getElementById('pause-toggle').addEventListener('click', togglePause);
1178
-
1179
- renderStatusBar();
1180
- switchView('board');
1181
- setInterval(() => {
1182
- renderStatusBar();
1183
- switchView(currentView, { showLoading: false });
1184
- }, 7000);
1185
- </script>
1186
- </body>
1187
- </html>
1188
- `;