@wjarka/cezarion 0.15.1 → 0.16.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 (462) hide show
  1. package/README.md +167 -46
  2. package/dist/agent-config/catalog.d.ts +6 -2
  3. package/dist/agent-config/catalog.js +78 -3
  4. package/dist/agent-config/catalog.js.map +1 -1
  5. package/dist/agent-config/model-settings/shared.js +6 -3
  6. package/dist/agent-config/model-settings/shared.js.map +1 -1
  7. package/dist/agent-config/models.js +4 -0
  8. package/dist/agent-config/models.js.map +1 -1
  9. package/dist/agent-config/validate.js +6 -0
  10. package/dist/agent-config/validate.js.map +1 -1
  11. package/dist/artifacts/cli.js +10 -2
  12. package/dist/artifacts/cli.js.map +1 -1
  13. package/dist/automations/coordinator.d.ts +7 -0
  14. package/dist/automations/coordinator.js +22 -2
  15. package/dist/automations/coordinator.js.map +1 -1
  16. package/dist/automations/github-poller.d.ts +4 -4
  17. package/dist/automations/github-poller.js.map +1 -1
  18. package/dist/automations/schedule-runner.d.ts +130 -0
  19. package/dist/automations/schedule-runner.js +326 -0
  20. package/dist/automations/schedule-runner.js.map +1 -0
  21. package/dist/automations/scheduler.d.ts +38 -7
  22. package/dist/automations/scheduler.js +90 -22
  23. package/dist/automations/scheduler.js.map +1 -1
  24. package/dist/automations/store.d.ts +80 -3
  25. package/dist/automations/store.js +208 -16
  26. package/dist/automations/store.js.map +1 -1
  27. package/dist/automations/task-template.d.ts +42 -6
  28. package/dist/automations/task-template.js +152 -24
  29. package/dist/automations/task-template.js.map +1 -1
  30. package/dist/automations/types.d.ts +142 -6
  31. package/dist/automations/types.js +64 -6
  32. package/dist/automations/types.js.map +1 -1
  33. package/dist/autosave-holders.js +8 -12
  34. package/dist/autosave-holders.js.map +1 -1
  35. package/dist/ci-wait/client.d.ts +8 -1
  36. package/dist/ci-wait/client.js +43 -1
  37. package/dist/ci-wait/client.js.map +1 -1
  38. package/dist/ci-wait/controller.d.ts +83 -4
  39. package/dist/ci-wait/controller.js +76 -11
  40. package/dist/ci-wait/controller.js.map +1 -1
  41. package/dist/ci-wait/errors.d.ts +7 -1
  42. package/dist/ci-wait/errors.js +36 -0
  43. package/dist/ci-wait/errors.js.map +1 -1
  44. package/dist/ci-wait/github.d.ts +2 -0
  45. package/dist/ci-wait/github.js +3 -1
  46. package/dist/ci-wait/github.js.map +1 -1
  47. package/dist/ci-wait/mcp.d.ts +2 -98
  48. package/dist/ci-wait/mcp.js +11 -25
  49. package/dist/ci-wait/mcp.js.map +1 -1
  50. package/dist/ci-wait/supervisor.d.ts +2 -1
  51. package/dist/ci-wait/supervisor.js +2 -1
  52. package/dist/ci-wait/supervisor.js.map +1 -1
  53. package/dist/ci-wait/tools.d.ts +307 -0
  54. package/dist/ci-wait/tools.js +63 -0
  55. package/dist/ci-wait/tools.js.map +1 -0
  56. package/dist/config.d.ts +3 -0
  57. package/dist/config.js +7 -0
  58. package/dist/config.js.map +1 -1
  59. package/dist/contract/agent-config.d.ts +5 -0
  60. package/dist/contract/agent-profiles.d.ts +15 -0
  61. package/dist/contract/automation-schedule.d.ts +60 -0
  62. package/dist/contract/automations.d.ts +208 -36
  63. package/dist/contract/delegation.d.ts +96 -0
  64. package/dist/contract/discovery.d.ts +7 -0
  65. package/dist/contract/github.d.ts +1 -0
  66. package/dist/contract/health.d.ts +6 -0
  67. package/dist/contract/index.d.ts +3 -0
  68. package/dist/contract/index.js +1445 -745
  69. package/dist/contract/preview.d.ts +508 -0
  70. package/dist/contract/runs.d.ts +716 -15
  71. package/dist/contract/skills.d.ts +39 -0
  72. package/dist/contract/workflows.d.ts +6 -0
  73. package/dist/contract/workspace.d.ts +65 -0
  74. package/dist/contract/zoned-time.d.ts +35 -0
  75. package/dist/core/agent-env.d.ts +11 -0
  76. package/dist/core/agent-env.js +27 -5
  77. package/dist/core/agent-env.js.map +1 -1
  78. package/dist/core/agent-profiles.js +7 -0
  79. package/dist/core/agent-profiles.js.map +1 -1
  80. package/dist/core/agent-runner.d.ts +49 -1
  81. package/dist/core/agent-runner.js +2 -1
  82. package/dist/core/agent-runner.js.map +1 -1
  83. package/dist/core/backend-detect.d.ts +3 -2
  84. package/dist/core/backend-detect.js +27 -1
  85. package/dist/core/backend-detect.js.map +1 -1
  86. package/dist/core/claude-cli-runner.d.ts +1 -0
  87. package/dist/core/claude-cli-runner.js +69 -19
  88. package/dist/core/claude-cli-runner.js.map +1 -1
  89. package/dist/core/codex-app-server-runner.d.ts +1 -0
  90. package/dist/core/codex-app-server-runner.js +200 -25
  91. package/dist/core/codex-app-server-runner.js.map +1 -1
  92. package/dist/core/codex-app-server-transport.d.ts +7 -3
  93. package/dist/core/codex-app-server-transport.js +11 -9
  94. package/dist/core/codex-app-server-transport.js.map +1 -1
  95. package/dist/core/cursor-acp-runner.d.ts +1 -0
  96. package/dist/core/cursor-acp-runner.js +24 -9
  97. package/dist/core/cursor-acp-runner.js.map +1 -1
  98. package/dist/core/host-model-catalog.js +2 -0
  99. package/dist/core/host-model-catalog.js.map +1 -1
  100. package/dist/core/managed-session.js +2 -0
  101. package/dist/core/managed-session.js.map +1 -1
  102. package/dist/core/model-identity.js +2 -0
  103. package/dist/core/model-identity.js.map +1 -1
  104. package/dist/core/model-presets.js +3 -0
  105. package/dist/core/model-presets.js.map +1 -1
  106. package/dist/core/omp-model-catalog.d.ts +43 -0
  107. package/dist/core/omp-model-catalog.js +205 -0
  108. package/dist/core/omp-model-catalog.js.map +1 -0
  109. package/dist/core/omp-runner.d.ts +73 -0
  110. package/dist/core/omp-runner.js +990 -0
  111. package/dist/core/omp-runner.js.map +1 -0
  112. package/dist/core/omp-ui-mapper.d.ts +106 -0
  113. package/dist/core/omp-ui-mapper.js +882 -0
  114. package/dist/core/omp-ui-mapper.js.map +1 -0
  115. package/dist/core/opencode-server-runner.d.ts +18 -4
  116. package/dist/core/opencode-server-runner.js +337 -96
  117. package/dist/core/opencode-server-runner.js.map +1 -1
  118. package/dist/core/opencode-session-error.d.ts +11 -0
  119. package/dist/core/opencode-session-error.js +26 -0
  120. package/dist/core/opencode-session-error.js.map +1 -0
  121. package/dist/core/opencode-ui-mapper.js +4 -36
  122. package/dist/core/opencode-ui-mapper.js.map +1 -1
  123. package/dist/core/pi-runner.d.ts +1 -0
  124. package/dist/core/pi-runner.js +75 -12
  125. package/dist/core/pi-runner.js.map +1 -1
  126. package/dist/core/provider-auth.d.ts +9 -2
  127. package/dist/core/provider-auth.js +43 -6
  128. package/dist/core/provider-auth.js.map +1 -1
  129. package/dist/core/runner-factory.js +3 -0
  130. package/dist/core/runner-factory.js.map +1 -1
  131. package/dist/core/runner-model-catalog.js +1 -0
  132. package/dist/core/runner-model-catalog.js.map +1 -1
  133. package/dist/core/runner-stderr.d.ts +2 -0
  134. package/dist/core/runner-stderr.js +20 -0
  135. package/dist/core/runner-stderr.js.map +1 -0
  136. package/dist/core/session-process.d.ts +22 -0
  137. package/dist/core/session-process.js +202 -0
  138. package/dist/core/session-process.js.map +1 -0
  139. package/dist/core/ui-events.d.ts +1 -1
  140. package/dist/core/usage-limit.js +3 -60
  141. package/dist/core/usage-limit.js.map +1 -1
  142. package/dist/data-gitignore.d.ts +6 -0
  143. package/dist/data-gitignore.js +57 -0
  144. package/dist/data-gitignore.js.map +1 -0
  145. package/dist/delegation/capacity.d.ts +19 -0
  146. package/dist/delegation/capacity.js +34 -0
  147. package/dist/delegation/capacity.js.map +1 -0
  148. package/dist/delegation/cli.js +13 -4
  149. package/dist/delegation/cli.js.map +1 -1
  150. package/dist/delegation/conversations.d.ts +4 -2
  151. package/dist/delegation/conversations.js +8 -12
  152. package/dist/delegation/conversations.js.map +1 -1
  153. package/dist/delegation/credentials.d.ts +2 -0
  154. package/dist/delegation/credentials.js +10 -0
  155. package/dist/delegation/credentials.js.map +1 -1
  156. package/dist/delegation/destroy-observation.d.ts +42 -0
  157. package/dist/delegation/destroy-observation.js +115 -0
  158. package/dist/delegation/destroy-observation.js.map +1 -0
  159. package/dist/delegation/execution-identity.d.ts +6 -0
  160. package/dist/delegation/execution-identity.js +1 -1
  161. package/dist/delegation/execution-identity.js.map +1 -1
  162. package/dist/delegation/policy.d.ts +3 -2
  163. package/dist/delegation/policy.js +7 -5
  164. package/dist/delegation/policy.js.map +1 -1
  165. package/dist/delegation/process-liveness.d.ts +57 -20
  166. package/dist/delegation/process-liveness.js +114 -92
  167. package/dist/delegation/process-liveness.js.map +1 -1
  168. package/dist/delegation/provision.js +6 -6
  169. package/dist/delegation/provision.js.map +1 -1
  170. package/dist/delegation/results.js +4 -2
  171. package/dist/delegation/results.js.map +1 -1
  172. package/dist/delegation/retry-backoff.d.ts +23 -0
  173. package/dist/delegation/retry-backoff.js +18 -0
  174. package/dist/delegation/retry-backoff.js.map +1 -0
  175. package/dist/delegation/routes.d.ts +64 -58
  176. package/dist/delegation/scratch-cleanup.d.ts +29 -0
  177. package/dist/delegation/scratch-cleanup.js +143 -0
  178. package/dist/delegation/scratch-cleanup.js.map +1 -0
  179. package/dist/delegation/service.d.ts +20 -1
  180. package/dist/delegation/service.js +233 -70
  181. package/dist/delegation/service.js.map +1 -1
  182. package/dist/delegation/transport.d.ts +12 -6
  183. package/dist/delegation/workspace.d.ts +23 -4
  184. package/dist/delegation/workspace.js +147 -31
  185. package/dist/delegation/workspace.js.map +1 -1
  186. package/dist/git-worktree-lock-helper.js +8 -3
  187. package/dist/git-worktree-lock-helper.js.map +1 -1
  188. package/dist/git-worktree-lock.d.ts +6 -1
  189. package/dist/git-worktree-lock.js +10 -4
  190. package/dist/git-worktree-lock.js.map +1 -1
  191. package/dist/git-worktree-release.d.ts +20 -0
  192. package/dist/git-worktree-release.js +27 -0
  193. package/dist/git-worktree-release.js.map +1 -0
  194. package/dist/git-worktree.d.ts +8 -2
  195. package/dist/git-worktree.js +19 -2
  196. package/dist/git-worktree.js.map +1 -1
  197. package/dist/handoff.d.ts +2 -2
  198. package/dist/handoff.js +1 -1
  199. package/dist/index.js +109 -76
  200. package/dist/index.js.map +1 -1
  201. package/dist/paths.d.ts +1 -1
  202. package/dist/paths.js +5 -1
  203. package/dist/paths.js.map +1 -1
  204. package/dist/preview/cdp.d.ts +13 -0
  205. package/dist/preview/cdp.js +69 -0
  206. package/dist/preview/cdp.js.map +1 -0
  207. package/dist/preview/chromium.d.ts +75 -0
  208. package/dist/preview/chromium.js +394 -0
  209. package/dist/preview/chromium.js.map +1 -0
  210. package/dist/preview/dev-server.d.ts +68 -0
  211. package/dist/preview/dev-server.js +341 -0
  212. package/dist/preview/dev-server.js.map +1 -0
  213. package/dist/preview/host.d.ts +154 -0
  214. package/dist/preview/host.js +681 -0
  215. package/dist/preview/host.js.map +1 -0
  216. package/dist/preview/registration.d.ts +63 -0
  217. package/dist/preview/registration.js +109 -0
  218. package/dist/preview/registration.js.map +1 -0
  219. package/dist/preview/session.d.ts +73 -0
  220. package/dist/preview/session.js +347 -0
  221. package/dist/preview/session.js.map +1 -0
  222. package/dist/preview/url.d.ts +6 -0
  223. package/dist/preview/url.js +17 -0
  224. package/dist/preview/url.js.map +1 -0
  225. package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
  226. package/dist/runs/agent-tmpdir-removal.js +60 -0
  227. package/dist/runs/agent-tmpdir-removal.js.map +1 -0
  228. package/dist/runs/agent-tmpdir.d.ts +12 -1
  229. package/dist/runs/agent-tmpdir.js +123 -34
  230. package/dist/runs/agent-tmpdir.js.map +1 -1
  231. package/dist/runs/delegation-state.d.ts +5 -1
  232. package/dist/runs/delegation-state.js +9 -0
  233. package/dist/runs/delegation-state.js.map +1 -1
  234. package/dist/runs/event-history.d.ts +4 -4
  235. package/dist/runs/event-history.js +97 -117
  236. package/dist/runs/event-history.js.map +1 -1
  237. package/dist/runs/history-compressor.d.ts +29 -0
  238. package/dist/runs/history-compressor.js +116 -0
  239. package/dist/runs/history-compressor.js.map +1 -0
  240. package/dist/runs/history-file.d.ts +36 -0
  241. package/dist/runs/history-file.js +463 -0
  242. package/dist/runs/history-file.js.map +1 -0
  243. package/dist/runs/human-ask-summary.js +18 -13
  244. package/dist/runs/human-ask-summary.js.map +1 -1
  245. package/dist/runs/legacy-index.d.ts +62 -0
  246. package/dist/runs/legacy-index.js +168 -0
  247. package/dist/runs/legacy-index.js.map +1 -0
  248. package/dist/runs/raw-record.d.ts +52 -0
  249. package/dist/runs/raw-record.js +145 -0
  250. package/dist/runs/raw-record.js.map +1 -0
  251. package/dist/runs/retention.d.ts +13 -6
  252. package/dist/runs/retention.js +42 -13
  253. package/dist/runs/retention.js.map +1 -1
  254. package/dist/runs/run-claims.d.ts +23 -0
  255. package/dist/runs/run-claims.js +24 -0
  256. package/dist/runs/run-claims.js.map +1 -0
  257. package/dist/runs/run-database.d.ts +385 -0
  258. package/dist/runs/run-database.js +930 -0
  259. package/dist/runs/run-database.js.map +1 -0
  260. package/dist/runs/run-index.d.ts +35 -9
  261. package/dist/runs/run-index.js +200 -25
  262. package/dist/runs/run-index.js.map +1 -1
  263. package/dist/runs/run-row.d.ts +44 -0
  264. package/dist/runs/run-row.js +84 -0
  265. package/dist/runs/run-row.js.map +1 -0
  266. package/dist/runs/run-search.d.ts +29 -0
  267. package/dist/runs/run-search.js +63 -0
  268. package/dist/runs/run-search.js.map +1 -0
  269. package/dist/runs/store-open-error.d.ts +24 -0
  270. package/dist/runs/store-open-error.js +61 -0
  271. package/dist/runs/store-open-error.js.map +1 -0
  272. package/dist/runs/store.d.ts +667 -32
  273. package/dist/runs/store.js +2153 -324
  274. package/dist/runs/store.js.map +1 -1
  275. package/dist/runs/transcript-facts.d.ts +77 -0
  276. package/dist/runs/transcript-facts.js +302 -0
  277. package/dist/runs/transcript-facts.js.map +1 -0
  278. package/dist/runs/webhook.js +5 -4
  279. package/dist/runs/webhook.js.map +1 -1
  280. package/dist/runs/worker-execution.d.ts +22 -0
  281. package/dist/runs/worker-execution.js +30 -0
  282. package/dist/runs/worker-execution.js.map +1 -0
  283. package/dist/server/capabilities.d.ts +1 -0
  284. package/dist/server/capabilities.js +2 -0
  285. package/dist/server/capabilities.js.map +1 -1
  286. package/dist/server/cockpit-ownership.d.ts +8 -0
  287. package/dist/server/cockpit-ownership.js +19 -0
  288. package/dist/server/cockpit-ownership.js.map +1 -1
  289. package/dist/server/forge/github.d.ts +1 -4
  290. package/dist/server/forge/github.js +47 -23
  291. package/dist/server/forge/github.js.map +1 -1
  292. package/dist/server/git.js +1 -1
  293. package/dist/server/git.js.map +1 -1
  294. package/dist/server/open-in-app.js +1 -0
  295. package/dist/server/open-in-app.js.map +1 -1
  296. package/dist/server/preview-socket.d.ts +36 -0
  297. package/dist/server/preview-socket.js +126 -0
  298. package/dist/server/preview-socket.js.map +1 -0
  299. package/dist/server/project-context.d.ts +31 -4
  300. package/dist/server/project-context.js +73 -9
  301. package/dist/server/project-context.js.map +1 -1
  302. package/dist/server/provider-action-gate.js +1 -0
  303. package/dist/server/provider-action-gate.js.map +1 -1
  304. package/dist/server/repo-branches.d.ts +4 -3
  305. package/dist/server/repo-branches.js +5 -4
  306. package/dist/server/repo-branches.js.map +1 -1
  307. package/dist/server/server.d.ts +4500 -2727
  308. package/dist/server/server.js +651 -202
  309. package/dist/server/server.js.map +1 -1
  310. package/dist/server/upgrade-router.d.ts +23 -0
  311. package/dist/server/upgrade-router.js +53 -0
  312. package/dist/server/upgrade-router.js.map +1 -0
  313. package/dist/server/validators.d.ts +6 -1
  314. package/dist/server/validators.js +4 -4
  315. package/dist/server/validators.js.map +1 -1
  316. package/dist/server/ws.d.ts +16 -16
  317. package/dist/server/ws.js +56 -54
  318. package/dist/server/ws.js.map +1 -1
  319. package/dist/skills-remote.d.ts +14 -0
  320. package/dist/skills-remote.js +91 -15
  321. package/dist/skills-remote.js.map +1 -1
  322. package/dist/task-cli/cli.js +40 -16
  323. package/dist/task-cli/cli.js.map +1 -1
  324. package/dist/task-cli/discovery.d.ts +4 -3
  325. package/dist/task-cli/discovery.js +4 -3
  326. package/dist/task-cli/discovery.js.map +1 -1
  327. package/dist/task-cli/http.d.ts +2 -0
  328. package/dist/task-cli/http.js +10 -4
  329. package/dist/task-cli/http.js.map +1 -1
  330. package/dist/task-cli/projections.d.ts +5 -2
  331. package/dist/task-cli/projections.js +13 -0
  332. package/dist/task-cli/projections.js.map +1 -1
  333. package/dist/task-cli/watch.d.ts +15 -2
  334. package/dist/task-cli/watch.js +31 -9
  335. package/dist/task-cli/watch.js.map +1 -1
  336. package/dist/ui-state.d.ts +4 -3
  337. package/dist/ui-state.js +8 -3
  338. package/dist/ui-state.js.map +1 -1
  339. package/dist/workflows/grouped-command.d.ts +31 -0
  340. package/dist/workflows/grouped-command.js +143 -0
  341. package/dist/workflows/grouped-command.js.map +1 -0
  342. package/dist/workflows/run.d.ts +223 -42
  343. package/dist/workflows/run.js +1316 -398
  344. package/dist/workflows/run.js.map +1 -1
  345. package/dist/workflows/types.d.ts +12 -0
  346. package/dist/workflows/types.js +20 -0
  347. package/dist/workflows/types.js.map +1 -1
  348. package/dist/workspace/agent-accounts.d.ts +5 -1
  349. package/dist/workspace/agent-accounts.js +1 -0
  350. package/dist/workspace/agent-accounts.js.map +1 -1
  351. package/dist/workspace/agent-profiles.js +1 -1
  352. package/dist/workspace/agent-profiles.js.map +1 -1
  353. package/dist/workspace/config.d.ts +3 -1
  354. package/dist/workspace/config.js +1 -0
  355. package/dist/workspace/config.js.map +1 -1
  356. package/dist/worktree-setup.d.ts +85 -0
  357. package/dist/worktree-setup.js +164 -0
  358. package/dist/worktree-setup.js.map +1 -0
  359. package/package.json +7 -5
  360. package/scripts/benchmark-run-store-fixture.ts +428 -0
  361. package/scripts/benchmark-run-store.ts +743 -0
  362. package/scripts/benchmark-stuck-destroys.ts +143 -0
  363. package/scripts/mock-autonomous.mjs +11 -0
  364. package/scripts/mock-ci-tool.mjs +20 -5
  365. package/scripts/mock-claude.mjs +110 -1
  366. package/scripts/mock-codex-app-server.mjs +117 -2
  367. package/scripts/mock-codex-coalesce-steer.mjs +59 -0
  368. package/scripts/mock-codex-reverse-steer.mjs +130 -0
  369. package/scripts/mock-cursor-acp.mjs +75 -1
  370. package/scripts/mock-omp-rpc.mjs +605 -0
  371. package/scripts/mock-opencode-serve.mjs +312 -22
  372. package/scripts/mock-pi-rpc.mjs +109 -3
  373. package/scripts/mock-runner-crash.mjs +42 -0
  374. package/scripts/mock-silent-tail.mjs +78 -0
  375. package/scripts/mock-turn-messages.mjs +5 -0
  376. package/scripts/omp-ci-wait.mjs +29 -0
  377. package/scripts/omp-restrict-delegation.yml +4 -0
  378. package/scripts/pi-ci-wait.mjs +12 -9
  379. package/scripts/seed-archive-profile.ts +3 -3
  380. package/web/dist/assets/agent-accounts-CFOBMVHc.js +1 -0
  381. package/web/dist/assets/alert-dialog-BrpNUwMt.js +1 -0
  382. package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
  383. package/web/dist/assets/button-C_rd7CPp.js +43 -0
  384. package/web/dist/assets/compare-variants-D6WXhvXA.js +1 -0
  385. package/web/dist/assets/diff-DrEARXTV.js +3 -0
  386. package/web/dist/assets/diff-controls-B1-j7nFg.js +1 -0
  387. package/web/dist/assets/{diff-view-DPn6bQ5W.js → diff-view-DvGLjdTk.js} +3 -3
  388. package/web/dist/assets/dropdown-menu-BegOZFrj.js +1 -0
  389. package/web/dist/assets/format-DpNe3S0A.js +1 -0
  390. package/web/dist/assets/git-toolbar-Cw4ljmhB.js +1 -0
  391. package/web/dist/assets/github-CfV5sqL0.js +1 -0
  392. package/web/dist/assets/{image-preview-DJ8cgb1Z.js → image-preview-FOEfqpZP.js} +1 -1
  393. package/web/dist/assets/index-DgWWlOLy.js +13 -0
  394. package/web/dist/assets/index-fBFnuvxu.css +1 -0
  395. package/web/dist/assets/{nav-row-styles-3j64kH5s.js → nav-row-styles-vp2JbhuJ.js} +1 -1
  396. package/web/dist/assets/play-CmVAj_fu.js +1 -0
  397. package/web/dist/assets/popover-BohP6WTx.js +1 -0
  398. package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +3 -0
  399. package/web/dist/assets/preview-state-vuMviq6q.js +1 -0
  400. package/web/dist/assets/prompt-templates-Cubdut_C.js +15 -0
  401. package/web/dist/assets/repo-git-Cf4t5PD0.js +1 -0
  402. package/web/dist/assets/{run-diff-5-RRdcVz.js → run-diff-BpkUW37Z.js} +3 -3
  403. package/web/dist/assets/run-header-68WHf9wV.js +1 -0
  404. package/web/dist/assets/skills-DiVIgKeU.js +1 -0
  405. package/web/dist/assets/square-terminal-B6RYYieR.js +1 -0
  406. package/web/dist/assets/status-dot-CqTJHSP9.js +1 -0
  407. package/web/dist/assets/{tab-link-K0hjzEXR.js → tab-link-B07Hu4QX.js} +1 -1
  408. package/web/dist/assets/task-changes--lQF6EYf.js +1 -0
  409. package/web/dist/assets/task-commits-Cx0qy4DN.js +1 -0
  410. package/web/dist/assets/task-files-D50s-gKW.js +2 -0
  411. package/web/dist/assets/task-github-item-DaqmSwYT.js +1 -0
  412. package/web/dist/assets/task-thread-BPQ64lui.js +7 -0
  413. package/web/dist/assets/{task-thread-Bw_nQuTx.css → task-thread-zNE2Q9E-.css} +1 -1
  414. package/web/dist/assets/textarea-D20O4Pw0.js +1 -0
  415. package/web/dist/assets/thread-state-DWcFMrvb.js +4 -0
  416. package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +1 -0
  417. package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +1 -0
  418. package/web/dist/assets/{utils-fzNfbPzA.js → utils-JsL3ZsJf.js} +1 -1
  419. package/web/dist/assets/workflows-jwG7uxqi.js +11 -0
  420. package/web/dist/assets/{zoomable-image-CDmZ_Rtl.js → zoomable-image-BJJ64KGc.js} +1 -1
  421. package/web/dist/cezarion-lockup-dark.svg +1 -13
  422. package/web/dist/cezarion-lockup-light.svg +1 -13
  423. package/web/dist/cezarion-mark-dark.svg +1 -12
  424. package/web/dist/cezarion-mark-light.svg +1 -12
  425. package/web/dist/index.html +16 -21
  426. package/web/dist/assets/agent-accounts-CU2ZDv24.js +0 -1
  427. package/web/dist/assets/alert-dialog-C-xYE_dg.js +0 -43
  428. package/web/dist/assets/chevron-right-BENZ-1ZN.js +0 -1
  429. package/web/dist/assets/collapsible-DD9J91Iv.js +0 -1
  430. package/web/dist/assets/compare-variants-5FmqpFSN.js +0 -1
  431. package/web/dist/assets/diff-controls-Bj0YrioE.js +0 -1
  432. package/web/dist/assets/diff-rE8rm75J2.js +0 -3
  433. package/web/dist/assets/diff-stat-BKDCSVfJ.js +0 -1
  434. package/web/dist/assets/dropdown-menu-Ch5BYQh_.js +0 -1
  435. package/web/dist/assets/format-dBiNDNVe.js +0 -1
  436. package/web/dist/assets/git-toolbar-ClVvf5iF.js +0 -1
  437. package/web/dist/assets/github-BkNOyour.css +0 -1
  438. package/web/dist/assets/github-DeZMeMwJ.js +0 -1
  439. package/web/dist/assets/github-item-detail-W5F6GVjF.js +0 -1
  440. package/web/dist/assets/github-sidebar-model-DcMu-IK2.js +0 -1
  441. package/web/dist/assets/highlighter-CdHqIwFr.js +0 -3
  442. package/web/dist/assets/index-CatemBEL.js +0 -8
  443. package/web/dist/assets/index-MvfD8bZJ.css +0 -1
  444. package/web/dist/assets/markdown-zdKeLIYA.js +0 -2
  445. package/web/dist/assets/mobile-run-bar-BCfmFi8V.js +0 -1
  446. package/web/dist/assets/prompt-templates-CBPp39IU.js +0 -15
  447. package/web/dist/assets/reference-chip-D6owncJT.js +0 -1
  448. package/web/dist/assets/repo-git-CtdkZFey.js +0 -1
  449. package/web/dist/assets/run-header-BcjoDi0W.js +0 -1
  450. package/web/dist/assets/sheet-ChnYVrp1.js +0 -1
  451. package/web/dist/assets/skills-C8YKp2vr.js +0 -1
  452. package/web/dist/assets/task-changes-BW8YdWG8.js +0 -1
  453. package/web/dist/assets/task-commits-q2kB2y6L.js +0 -1
  454. package/web/dist/assets/task-files-pPL6_3Cr.js +0 -2
  455. package/web/dist/assets/task-github-item-BXF5R52U.js +0 -1
  456. package/web/dist/assets/task-thread-DtkFL3EC.js +0 -10
  457. package/web/dist/assets/textarea-DxlkE-Yd.js +0 -1
  458. package/web/dist/assets/token-metrics-DIAjrPlM.js +0 -1
  459. package/web/dist/assets/use-desktop-CrDny2uG.js +0 -1
  460. package/web/dist/assets/use-submit-shortcut-KWJsC906.js +0 -1
  461. package/web/dist/assets/workflows-TmcPEj2K.js +0 -11
  462. /package/web/dist/assets/{lib-BQXq3kEf2.js → lib-BQXq3kEf.js} +0 -0
@@ -0,0 +1,681 @@
1
+ import { readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { connectCdp } from './cdp.js';
5
+ import { ChromiumError, downloadChromium, downloadTarget, installCommand, launchChromium, resolveChromium } from './chromium.js';
6
+ import { DevServer, probePort } from './dev-server.js';
7
+ import { previewStopResult } from './registration.js';
8
+ import { PreviewSession } from './session.js';
9
+ import { normalizePreviewUrl } from './url.js';
10
+ /**
11
+ * The workspace-wide preview host (#781, spec 2026-10-02-live-preview-v1): one per server process,
12
+ * because ports are host-global. Keyed by run id, it owns every cezar-started dev server and every
13
+ * task's Chromium, and every exit in the spec's lifecycle table: idle timers, release on worktree
14
+ * removal or run deletion, and shutdown. It starts commands only on owner approval or agent reuse of that unchanged approval.
15
+ */
16
+ export const PREVIEW_SERVER_IDLE_MS = 15 * 60_000;
17
+ export const PREVIEW_BROWSER_IDLE_MS = 2 * 60_000;
18
+ const PROFILE_RELEASE_WAIT_MS = 5_000;
19
+ const PROGRESS_EVERY_MS = 250;
20
+ const STDERR_TAIL_CHARS = 4096;
21
+ /** No Chromium anywhere `resolveChromium` looks: the pane offers the download or the OS command. */
22
+ export class ChromiumMissingError extends Error {
23
+ }
24
+ const live = (server) => server !== undefined && server !== 'adopted' && server.state !== 'exited' && server.state !== 'stopped';
25
+ const owned = (server) => server !== undefined && server !== 'adopted';
26
+ const serverUrl = (server) => `http://localhost:${server.port}${server.path ?? '/'}`;
27
+ function sameOrigin(a, b) {
28
+ try {
29
+ return new URL(a).origin === new URL(b).origin;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ const targetsSchema = z.array(z.object({ type: z.string(), webSocketDebuggerUrl: z.string().optional() }).passthrough());
36
+ /** A closed browser's exit, bounded: a profile is deleted only once Chromium let go of it. */
37
+ async function waitForExit(browser) {
38
+ let timer;
39
+ await Promise.race([browser.exited, new Promise(resolve => (timer = setTimeout(resolve, PROFILE_RELEASE_WAIT_MS)))]);
40
+ clearTimeout(timer);
41
+ }
42
+ /** resolve → launch → the page target's CDP socket. */
43
+ async function launchBrowser(profileDir, env) {
44
+ const bin = resolveChromium(undefined, env);
45
+ if (!bin)
46
+ throw new ChromiumMissingError('No Chromium found');
47
+ const { proc, port } = await launchChromium(bin, profileDir, env);
48
+ let tail = '';
49
+ proc.stderr?.on('data', (chunk) => {
50
+ tail = (tail + chunk.toString('utf8')).slice(-STDERR_TAIL_CHARS);
51
+ });
52
+ const exited = new Promise(resolve => proc.once('exit', (_code, signal) => resolve({ ...(signal ? { signal } : {}), stderrTail: tail })));
53
+ try {
54
+ const targets = targetsSchema.parse(await (await fetch(`http://127.0.0.1:${port}/json/list`)).json());
55
+ const page = targets.find(target => target.type === 'page' && target.webSocketDebuggerUrl);
56
+ if (!page?.webSocketDebuggerUrl)
57
+ throw new ChromiumError('exited', tail, 'Chromium opened no page');
58
+ const cdp = await connectCdp(page.webSocketDebuggerUrl);
59
+ return { cdp, exited, close: () => { cdp.close(); proc.kill(); } };
60
+ }
61
+ catch (error) {
62
+ proc.kill('SIGKILL');
63
+ throw error;
64
+ }
65
+ }
66
+ function readOsRelease() {
67
+ try {
68
+ return readFileSync('/etc/os-release', 'utf8');
69
+ }
70
+ catch {
71
+ return '';
72
+ }
73
+ }
74
+ export class PreviewHost {
75
+ stopping = new Map();
76
+ entries = new Map();
77
+ deps;
78
+ /** Chromium lands in one shared cache, so there is one download for the whole host. */
79
+ download;
80
+ constructor(deps = {}) {
81
+ const env = deps.env ?? process.env;
82
+ this.deps = {
83
+ probe: deps.probe ?? probePort,
84
+ createServer: deps.createServer ?? (opts => new DevServer(opts)),
85
+ launchBrowser: deps.launchBrowser ?? (profileDir => launchBrowser(profileDir, env)),
86
+ download: deps.download ?? (opts => downloadChromium(opts)),
87
+ osRelease: deps.osRelease ?? readOsRelease,
88
+ env,
89
+ platform: deps.platform ?? process.platform,
90
+ arch: deps.arch ?? process.arch,
91
+ };
92
+ }
93
+ /**
94
+ * A pane asks for a server or a URL. A registered port is probed again now: answering is
95
+ * adopted and streamed, silent asks for approval. Nothing here spawns a command.
96
+ */
97
+ async open(ctx, viewer, target) {
98
+ const entry = this.entryFor(ctx);
99
+ if (entry.released)
100
+ return;
101
+ this.claim(entry, viewer);
102
+ // Whatever this open shows, a show still waiting on Chromium for an older target is stale.
103
+ entry.targetGen += 1;
104
+ if ('url' in target) {
105
+ let url;
106
+ try {
107
+ url = normalizePreviewUrl(target.url);
108
+ }
109
+ catch {
110
+ return;
111
+ }
112
+ entry.port = undefined;
113
+ return this.show(entry, url, false);
114
+ }
115
+ const server = this.registration(entry, target.port);
116
+ if (!server) {
117
+ entry.port = undefined;
118
+ return this.show(entry, `http://localhost:${target.port}/`, false);
119
+ }
120
+ entry.port = server.port;
121
+ const current = entry.servers.get(server.port);
122
+ // A terminal state can precede process-group teardown. Keep its owned identity until stop settles.
123
+ if (live(current) || (owned(current) && this.stopping.has(current)))
124
+ return this.report(entry, server.port, current);
125
+ // Another task's server would answer the probe: streaming it here would show that task's app.
126
+ if (this.heldByOther(entry, server))
127
+ return;
128
+ const answering = await this.deps.probe(server.port).catch(() => false);
129
+ if (this.entries.get(ctx.runId) !== entry || entry.viewer !== viewer || entry.port !== server.port || entry.servers.get(server.port) !== current)
130
+ return;
131
+ if (owned(current) && this.stopping.has(current))
132
+ return this.report(entry, server.port, current);
133
+ if (answering) {
134
+ entry.servers.set(server.port, 'adopted');
135
+ return this.showServer(entry, server, true, true);
136
+ }
137
+ if (current === 'adopted')
138
+ entry.servers.delete(server.port);
139
+ // A server cezar ran and lost says so, with its log; Start again is the same approval.
140
+ if (owned(current))
141
+ return this.report(entry, server.port, current);
142
+ this.tell(entry, { t: 'state', stage: 'needs-approval', server, wasRunning: current === 'adopted' || server.answeredAtRegistration });
143
+ }
144
+ /** The owner's approval, also reused by stopPreview after checking the saved approval. */
145
+ async run(runId, port) {
146
+ const entry = this.entries.get(runId);
147
+ const server = entry && this.registration(entry, port);
148
+ if (!entry || entry.released || !server || live(entry.servers.get(port)))
149
+ return;
150
+ const previous = entry.servers.get(port);
151
+ if (owned(previous) && this.stopping.has(previous))
152
+ return;
153
+ if (this.heldByOther(entry, server))
154
+ return;
155
+ const dev = this.deps.createServer({ server, worktreePath: entry.ctx.worktreePath, dir: this.runDir(entry.ctx) });
156
+ entry.servers.set(port, dev);
157
+ entry.approvals.set(port, { ...server });
158
+ entry.startedAt.set(port, new Date().toISOString());
159
+ entry.port = port;
160
+ dev.on('state', (state) => this.onServerState(entry, port, dev, state));
161
+ // Attempts reach the pane only; the event log records transitions.
162
+ dev.on('attempt', () => {
163
+ if (entry.servers.get(port) === dev && dev.state === 'starting')
164
+ this.report(entry, port, dev);
165
+ });
166
+ // A DevServer is born `starting` without emitting it, so the host records that transition.
167
+ this.recordState(entry, port, 'starting', dev);
168
+ this.report(entry, port, dev);
169
+ dev.start();
170
+ this.armServerIdle(entry);
171
+ }
172
+ /**
173
+ * The agent re-registered `port` with a different command, cwd or path. A server cezar runs
174
+ * from the old registration stops, an adoption is forgotten, and a pane on that port is asked
175
+ * to approve the new command: nothing runs it until the owner does.
176
+ */
177
+ async replaced(runId, port) {
178
+ const entry = this.entries.get(runId);
179
+ if (!entry)
180
+ return;
181
+ const current = entry.servers.get(port);
182
+ entry.approvals.delete(port);
183
+ // Keep the old process reachable until teardown finishes: release must await it too.
184
+ if (owned(current))
185
+ await this.stopServer(current, 'release');
186
+ if (entry.servers.get(port) === current)
187
+ entry.servers.delete(port);
188
+ const server = this.registration(entry, port);
189
+ if (server && entry.viewer && entry.port === port) {
190
+ this.tell(entry, { t: 'state', stage: 'needs-approval', server, wasRunning: false });
191
+ }
192
+ }
193
+ /** Stops a cezar-owned server. An adopted one is not cezar's to stop: refused with `false`. */
194
+ async stop(runId, port, reason) {
195
+ const dev = this.entries.get(runId)?.servers.get(port);
196
+ if (!live(dev))
197
+ return false;
198
+ await this.stopServer(dev, reason);
199
+ return true;
200
+ }
201
+ /** Reuse only the approval attached to this exact owned process, including after a crash. */
202
+ async stopPreview(runId, request, signal) {
203
+ const { port, restart } = request;
204
+ const owner = this.portOwner(port);
205
+ if (owner && owner.runId !== runId)
206
+ return previewStopResult('port_held');
207
+ const entry = this.entries.get(runId);
208
+ const server = entry && this.registration(entry, port);
209
+ if (signal?.aborted || entry?.released)
210
+ return previewStopResult('unavailable');
211
+ if (!entry)
212
+ return previewStopResult('approval_required');
213
+ if (!server)
214
+ return previewStopResult('not_registered');
215
+ const dev = entry.servers.get(port);
216
+ if (dev === 'adopted')
217
+ return previewStopResult('adopted');
218
+ const approval = entry.approvals.get(port);
219
+ const approved = () => {
220
+ const current = this.registration(entry, port);
221
+ return !!approval && entry.approvals.get(port) === approval && !!current
222
+ && current.command === approval.command && current.cwd === approval.cwd && current.path === approval.path;
223
+ };
224
+ if (!owned(dev))
225
+ return previewStopResult('approval_required');
226
+ await this.stopServer(dev, 'user');
227
+ if (signal?.aborted || entry.released || this.entries.get(runId) !== entry)
228
+ return previewStopResult('unavailable');
229
+ if (!restart)
230
+ return previewStopResult('stopped');
231
+ if (!approved())
232
+ return previewStopResult('approval_required');
233
+ // Another process can take the port after stop/crash. Never adopt it or spawn over it.
234
+ const answering = await this.deps.probe(port);
235
+ if (signal?.aborted || entry.released || this.entries.get(runId) !== entry)
236
+ return previewStopResult('unavailable');
237
+ if (!approved())
238
+ return previewStopResult('approval_required');
239
+ if (entry.servers.get(port) !== dev)
240
+ return previewStopResult('unavailable');
241
+ const holder = this.portOwner(port);
242
+ if (holder && holder.runId !== runId)
243
+ return previewStopResult('port_held');
244
+ if (answering)
245
+ return previewStopResult('port_in_use');
246
+ await this.run(runId, port);
247
+ return previewStopResult('restarted');
248
+ }
249
+ stopServer(dev, reason) {
250
+ const pending = this.stopping.get(dev);
251
+ if (pending)
252
+ return pending;
253
+ const stop = dev.stop(reason).finally(() => { this.stopping.delete(dev); });
254
+ this.stopping.set(dev, stop);
255
+ return stop;
256
+ }
257
+ keepWaiting(runId, port) {
258
+ const dev = this.entries.get(runId)?.servers.get(port);
259
+ if (live(dev))
260
+ dev.keepWaiting();
261
+ }
262
+ /** The socket closed. The last viewer leaving starts both idle timers. */
263
+ detach(runId, viewer) {
264
+ const entry = this.entries.get(runId);
265
+ if (!entry || entry.viewer !== viewer)
266
+ return;
267
+ entry.viewer = undefined;
268
+ entry.session?.detach(viewer);
269
+ this.clearIdle(entry);
270
+ entry.idleTimers.browser = setTimeout(() => this.closeBrowser(entry), PREVIEW_BROWSER_IDLE_MS);
271
+ this.armServerIdle(entry);
272
+ }
273
+ /** Agent restarts without a viewer need a bound even after the previous idle timer fired. */
274
+ armServerIdle(entry) {
275
+ if (entry.viewer || entry.released || entry.idleTimers.server)
276
+ return;
277
+ entry.idleTimers.server = setTimeout(() => {
278
+ entry.idleTimers.server = undefined;
279
+ for (const server of entry.servers.values())
280
+ if (live(server))
281
+ void this.stopServer(server, 'idle');
282
+ }, PREVIEW_SERVER_IDLE_MS);
283
+ }
284
+ /** Every message a connected pane sends, already validated against the contract. */
285
+ async handle(ctx, viewer, msg) {
286
+ if (msg.t === 'open')
287
+ return this.open(ctx, viewer, msg.target);
288
+ const entry = this.entries.get(ctx.runId);
289
+ if (!entry || entry.viewer !== viewer)
290
+ return;
291
+ switch (msg.t) {
292
+ case 'run': return this.run(ctx.runId, msg.port);
293
+ case 'stop':
294
+ await this.stop(ctx.runId, msg.port, 'user');
295
+ return;
296
+ case 'keepWaiting': return this.keepWaiting(ctx.runId, msg.port);
297
+ case 'retryBrowser': return this.retryBrowser(entry);
298
+ case 'download': return this.startDownload(entry);
299
+ case 'cancelDownload':
300
+ this.download?.controller.abort();
301
+ return;
302
+ case 'ping':
303
+ viewer.send({ t: 'pong', ts: msg.ts });
304
+ return;
305
+ default:
306
+ // A refused URL or a CDP call on a page that just went away changes nothing for the pane.
307
+ await entry.session?.handle(msg).catch(() => { });
308
+ }
309
+ }
310
+ /**
311
+ * The run's worktree is going, or the run itself: stop its servers, close its browser, tell the
312
+ * pane. The profile survives unless `deleteProfile` (run deletion), which removes the whole
313
+ * `<dataDir>/preview/<runId>/`. `dataDir` covers a run this process never opened.
314
+ */
315
+ async release(runId, opts = {}) {
316
+ const entry = this.entries.get(runId);
317
+ if (entry)
318
+ await this.releaseEntry(entry, true, opts.deleteProfile === true);
319
+ const dataDir = opts.dataDir ?? entry?.ctx.dataDir;
320
+ if (opts.deleteProfile && dataDir) {
321
+ try {
322
+ // Chromium may still be flushing the profile as it exits.
323
+ rmSync(join(dataDir, 'preview', runId), { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
324
+ }
325
+ catch { /* a profile we cannot delete is left behind, never a failed run deletion */ }
326
+ }
327
+ }
328
+ /** The task whose cezar-owned dev server holds `port`. Adopted ports belong to nobody. */
329
+ portOwner(port) {
330
+ for (const entry of this.entries.values()) {
331
+ const server = entry.servers.get(port);
332
+ if (live(server) || (owned(server) && this.stopping.has(server)))
333
+ return { runId: entry.ctx.runId, title: entry.ctx.title };
334
+ }
335
+ return undefined;
336
+ }
337
+ /** Whether this host runs the dev server `<runId>/<port>.pid.json` records: the sweep spares it. */
338
+ ownsServer(runId, port) {
339
+ return live(this.entries.get(runId)?.servers.get(port));
340
+ }
341
+ /** Whether this host runs the run's Chromium (`<runId>/chromium.pid.json`): the sweep spares it. */
342
+ ownsBrowser(runId) {
343
+ const entry = this.entries.get(runId);
344
+ return entry !== undefined && (entry.browser !== undefined || entry.launching !== undefined);
345
+ }
346
+ probe(port) {
347
+ return this.deps.probe(port);
348
+ }
349
+ /** cezar shuts down: every run is released and every pane closed. */
350
+ async close() {
351
+ this.download?.controller.abort();
352
+ await Promise.all([...this.entries.values()].map(entry => this.releaseEntry(entry, false, false)));
353
+ }
354
+ entryFor(ctx) {
355
+ const existing = this.entries.get(ctx.runId);
356
+ if (existing) {
357
+ existing.ctx = ctx;
358
+ return existing;
359
+ }
360
+ const entry = { ctx, servers: new Map(), startedAt: new Map(), approvals: new Map(), browserGen: 0, targetGen: 0, adopted: false, idleTimers: {}, released: false };
361
+ this.entries.set(ctx.runId, entry);
362
+ return entry;
363
+ }
364
+ /** One viewer per run: a second one takes over, and the first is told by whom. */
365
+ claim(entry, viewer) {
366
+ this.clearIdle(entry);
367
+ const previous = entry.viewer;
368
+ if (previous === viewer)
369
+ return;
370
+ if (previous) {
371
+ entry.session?.detach(previous);
372
+ previous.send({ t: 'replaced', by: viewer.userAgent });
373
+ previous.close(4001, 'replaced');
374
+ }
375
+ entry.viewer = viewer;
376
+ entry.session?.attach(viewer);
377
+ }
378
+ /** Tells the pane `port-held` when another task's cezar-owned server holds `server.port`. */
379
+ heldByOther(entry, server) {
380
+ const owner = this.portOwner(server.port);
381
+ if (!owner || owner.runId === entry.ctx.runId)
382
+ return false;
383
+ if (entry.servers.get(server.port) === 'adopted')
384
+ entry.servers.delete(server.port);
385
+ this.tell(entry, { t: 'state', stage: 'port-held', server, ownerTitle: owner.title });
386
+ return true;
387
+ }
388
+ registration(entry, port) {
389
+ return entry.ctx.store.getRun(entry.ctx.runId)?.previewServers?.find(server => server.port === port);
390
+ }
391
+ runDir(ctx) {
392
+ return join(ctx.dataDir, 'preview', ctx.runId);
393
+ }
394
+ onServerState(entry, port, dev, state) {
395
+ if (entry.servers.get(port) !== dev)
396
+ return;
397
+ this.recordState(entry, port, state, dev);
398
+ if (entry.port !== port || entry.released)
399
+ return;
400
+ const server = this.registration(entry, port);
401
+ // Chromium starts for a pane that is open, never for nobody.
402
+ if (state === 'up') {
403
+ // A (re)started server: load the page again, at the one the owner was last on (5.9).
404
+ if (server && entry.viewer)
405
+ void this.showServer(entry, server, false, false);
406
+ }
407
+ else {
408
+ this.report(entry, port, dev);
409
+ }
410
+ }
411
+ /** `preview.server-state`, on transitions only. A run already deleted takes no events. */
412
+ recordState(entry, port, state, dev) {
413
+ const { runId, store } = entry.ctx;
414
+ if (!store.getRun(runId))
415
+ return;
416
+ const reason = dev.stopReason;
417
+ store.appendEvent(runId, {
418
+ type: 'preview.server-state',
419
+ port,
420
+ state,
421
+ ...(state === 'exited' && dev.exitCode !== undefined ? { exitCode: dev.exitCode } : {}),
422
+ ...(state === 'stopped' && (reason === 'user' || reason === 'idle') ? { reason } : {}),
423
+ });
424
+ }
425
+ /** The pane's view of a cezar-owned server that is not streaming. */
426
+ report(entry, port, dev) {
427
+ const server = this.registration(entry, port);
428
+ if (!server || entry.port !== port)
429
+ return;
430
+ const logTail = () => dev.logTail().join('\n');
431
+ switch (dev.state) {
432
+ case 'starting':
433
+ return this.tell(entry, { t: 'state', stage: 'server-starting', server, attempt: dev.attempts, startedAt: entry.startedAt.get(port) ?? new Date().toISOString(), logTail: dev.logTail() });
434
+ case 'stalled':
435
+ return this.tell(entry, { t: 'state', stage: 'server-stalled', server, logTail: logTail() });
436
+ case 'exited':
437
+ return this.tell(entry, { t: 'state', stage: 'server-exited', server, exitCode: dev.exitCode ?? null, logTail: logTail() });
438
+ case 'stopped':
439
+ return this.tell(entry, {
440
+ t: 'state',
441
+ stage: 'server-stopped',
442
+ server,
443
+ reason: dev.stopReason === 'idle' ? 'idle' : 'user',
444
+ lastUrl: entry.session?.url ?? entry.lastUrl ?? serverUrl(server),
445
+ });
446
+ case 'up':
447
+ if (entry.viewer)
448
+ void this.showServer(entry, server, false, true);
449
+ return;
450
+ }
451
+ }
452
+ /** Where a server's page reopens: the page the owner was last on when it is this server's, else the registered root. */
453
+ serverPage(entry, server) {
454
+ const root = serverUrl(server);
455
+ const last = entry.session?.url ?? entry.lastUrl;
456
+ return last && sameOrigin(last, root) ? last : root;
457
+ }
458
+ /**
459
+ * Streams a registered server. With `resume`, a live page already on that server is kept as it
460
+ * is: a reconnect, a takeover or a reopen inside the browser idle window must not throw the owner
461
+ * back to the root and lose their form state. Re-attaching restarts the screencast, so the first
462
+ * frame still announces `streaming`.
463
+ */
464
+ async showServer(entry, server, adopted, resume) {
465
+ const url = this.serverPage(entry, server);
466
+ const { session, viewer } = entry;
467
+ if (resume && session?.url && viewer && !entry.browserFailure && sameOrigin(session.url, url)) {
468
+ entry.adopted = adopted;
469
+ entry.lastUrl = session.url;
470
+ session.attach(viewer);
471
+ return;
472
+ }
473
+ return this.show(entry, url, adopted);
474
+ }
475
+ /** Browser, then page, then the first frame (which the session announces as `streaming`). */
476
+ async show(entry, url, adopted) {
477
+ // While Chromium is down, the URL it last showed is the one Retry reopens.
478
+ if (entry.browserFailure)
479
+ return this.tell(entry, entry.browserFailure);
480
+ entry.lastUrl = url;
481
+ entry.adopted = adopted;
482
+ const generation = ++entry.targetGen;
483
+ const session = await this.ensureSession(entry);
484
+ if (!session || entry.targetGen !== generation)
485
+ return;
486
+ this.tell(entry, { t: 'state', stage: 'loading', step: 'page' });
487
+ await session.navigate(url).catch(() => { });
488
+ this.tell(entry, { t: 'state', stage: 'loading', step: 'frame' });
489
+ }
490
+ ensureSession(entry) {
491
+ if (entry.session)
492
+ return Promise.resolve(entry.session);
493
+ if (entry.browserFailure) {
494
+ this.tell(entry, entry.browserFailure);
495
+ return Promise.resolve(undefined);
496
+ }
497
+ entry.launching ??= this.launch(entry).finally(() => {
498
+ entry.launching = undefined;
499
+ });
500
+ return entry.launching;
501
+ }
502
+ async launch(entry) {
503
+ const generation = entry.browserGen;
504
+ this.tell(entry, { t: 'state', stage: 'loading', step: 'browser' });
505
+ let browser;
506
+ try {
507
+ browser = await this.deps.launchBrowser(join(this.runDir(entry.ctx), 'profile'));
508
+ }
509
+ catch (error) {
510
+ this.launchFailed(entry, error);
511
+ return undefined;
512
+ }
513
+ let session;
514
+ try {
515
+ session = await PreviewSession.create(browser.cdp, {
516
+ onFirstFrame: viewer => viewer.send({ t: 'state', stage: 'streaming', adopted: entry.adopted }),
517
+ });
518
+ }
519
+ catch (error) {
520
+ browser.close();
521
+ this.launchFailed(entry, error);
522
+ return undefined;
523
+ }
524
+ if (entry.released || entry.browserGen !== generation) {
525
+ session.close();
526
+ browser.close();
527
+ // A release waiting on this launch deletes the profile next: the browser must be gone first.
528
+ if (entry.released)
529
+ await waitForExit(browser);
530
+ return undefined;
531
+ }
532
+ entry.browser = browser;
533
+ entry.session = session;
534
+ if (entry.viewer)
535
+ session.attach(entry.viewer);
536
+ void browser.exited.then(exit => this.browserExited(entry, browser, exit));
537
+ return session;
538
+ }
539
+ launchFailed(entry, error) {
540
+ // `not-installed`: the binary vanished between resolution and spawn. Same pane as none at all.
541
+ if (error instanceof ChromiumMissingError || (error instanceof ChromiumError && error.kind === 'not-installed')) {
542
+ return this.tell(entry, this.missingState());
543
+ }
544
+ if (error instanceof ChromiumError && error.kind === 'sandbox') {
545
+ entry.browserFailure = { t: 'state', stage: 'sandbox-failed', stderrTail: error.stderrTail };
546
+ return this.tell(entry, entry.browserFailure);
547
+ }
548
+ // A timeout or an early exit says why first: Chromium's own stderr is often unrelated noise.
549
+ const stderrTail = error instanceof ChromiumError
550
+ ? [error.message, error.stderrTail].filter(Boolean).join('\n')
551
+ : error instanceof Error ? error.message : String(error);
552
+ entry.browserFailure = { t: 'state', stage: 'browser-exited', stderrTail, serverUp: this.serverUp(entry) };
553
+ this.tell(entry, entry.browserFailure);
554
+ }
555
+ /** Chromium died under us: say so with the server's state, and wait for the owner's Retry. */
556
+ browserExited(entry, browser, exit) {
557
+ if (entry.browser !== browser)
558
+ return;
559
+ entry.lastUrl = entry.session?.url ?? entry.lastUrl;
560
+ entry.session = entry.browser = undefined;
561
+ entry.browserFailure = {
562
+ t: 'state',
563
+ stage: 'browser-exited',
564
+ ...(exit.signal ? { signal: exit.signal } : {}),
565
+ stderrTail: exit.stderrTail,
566
+ serverUp: this.serverUp(entry),
567
+ };
568
+ this.tell(entry, entry.browserFailure);
569
+ }
570
+ async retryBrowser(entry) {
571
+ entry.browserFailure = undefined;
572
+ if (entry.lastUrl)
573
+ return this.show(entry, entry.lastUrl, entry.adopted);
574
+ await this.ensureSession(entry);
575
+ }
576
+ serverUp(entry) {
577
+ const server = entry.port === undefined ? undefined : entry.servers.get(entry.port);
578
+ return server === 'adopted' || server?.state === 'up';
579
+ }
580
+ missingState() {
581
+ return {
582
+ t: 'state',
583
+ stage: 'chromium-missing',
584
+ installCommand: installCommand(this.deps.platform, this.deps.osRelease()),
585
+ canDownload: downloadTarget(this.deps.platform, this.deps.arch) !== undefined,
586
+ };
587
+ }
588
+ async startDownload(entry) {
589
+ this.tell(entry, { t: 'state', stage: 'downloading', received: 0, total: 0 });
590
+ const download = this.download ?? this.beginDownload(entry);
591
+ download.watchers.add(entry);
592
+ try {
593
+ await download.promise;
594
+ }
595
+ catch (error) {
596
+ if (entry.released)
597
+ return;
598
+ this.tell(entry, download.controller.signal.aborted
599
+ ? this.missingState()
600
+ : { t: 'state', stage: 'download-failed', error: error instanceof Error ? error.message : String(error), installCommand: installCommand(this.deps.platform, this.deps.osRelease()) });
601
+ return;
602
+ }
603
+ if (entry.released)
604
+ return;
605
+ if (entry.lastUrl)
606
+ await this.show(entry, entry.lastUrl, entry.adopted);
607
+ else
608
+ await this.ensureSession(entry);
609
+ }
610
+ /** `first` watches before the download starts, so no progress report is lost. */
611
+ beginDownload(first) {
612
+ const controller = new AbortController();
613
+ const watchers = new Set([first]);
614
+ let last = 0;
615
+ const download = { controller, watchers, promise: Promise.resolve('') };
616
+ this.download = download;
617
+ download.promise = this.deps.download({
618
+ signal: controller.signal,
619
+ onProgress: (received, total) => {
620
+ const now = Date.now();
621
+ if (now - last < PROGRESS_EVERY_MS && received < total)
622
+ return;
623
+ last = now;
624
+ for (const watcher of watchers)
625
+ this.tell(watcher, { t: 'downloadProgress', received, total });
626
+ },
627
+ });
628
+ void download.promise.catch(() => { }).finally(() => {
629
+ if (this.download === download)
630
+ this.download = undefined;
631
+ });
632
+ return download;
633
+ }
634
+ closeBrowser(entry) {
635
+ const { browser, session } = entry;
636
+ entry.browserGen += 1;
637
+ // The page the owner was on outlives the browser: 5.9 shows it, and Start again reopens it.
638
+ entry.lastUrl = session?.url ?? entry.lastUrl;
639
+ entry.browser = entry.session = undefined;
640
+ session?.close();
641
+ browser?.close();
642
+ return browser;
643
+ }
644
+ clearIdle(entry) {
645
+ clearTimeout(entry.idleTimers.server);
646
+ clearTimeout(entry.idleTimers.browser);
647
+ entry.idleTimers = {};
648
+ }
649
+ async releaseEntry(entry, notify, awaitBrowserExit) {
650
+ entry.released = true;
651
+ // Retain ownership while stopping so no run can reuse this port during teardown.
652
+ this.clearIdle(entry);
653
+ this.download?.watchers.delete(entry);
654
+ // A launch in flight has no browser on the entry yet; it sees `released` and closes its own.
655
+ const launching = entry.launching;
656
+ const browser = this.closeBrowser(entry);
657
+ const stops = [...entry.servers.values()].filter(owned).map(server => this.stopServer(server, 'release'));
658
+ await Promise.all(stops);
659
+ if (this.entries.get(entry.ctx.runId) === entry)
660
+ this.entries.delete(entry.ctx.runId);
661
+ if (awaitBrowserExit) {
662
+ if (browser)
663
+ await waitForExit(browser);
664
+ if (launching)
665
+ await launching.catch(() => undefined);
666
+ }
667
+ const viewer = entry.viewer;
668
+ entry.viewer = undefined;
669
+ if (!viewer)
670
+ return;
671
+ if (notify)
672
+ viewer.send({ t: 'state', stage: 'worktree-removed' });
673
+ else
674
+ viewer.close(1001, 'cezar is shutting down');
675
+ }
676
+ tell(entry, msg) {
677
+ if (!entry.released)
678
+ entry.viewer?.send(msg);
679
+ }
680
+ }
681
+ //# sourceMappingURL=host.js.map