@open-mercato/cezar 0.9.2 → 0.9.3

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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -2
  3. package/dist/contract/health.d.ts +2 -0
  4. package/dist/contract/index.js +23 -10
  5. package/dist/git-diff-base.d.ts +51 -26
  6. package/dist/git-diff-base.js +163 -37
  7. package/dist/git-diff-base.js.map +1 -1
  8. package/dist/git-worktree.d.ts +15 -6
  9. package/dist/git-worktree.js +10 -3
  10. package/dist/git-worktree.js.map +1 -1
  11. package/dist/server/capabilities.d.ts +15 -1
  12. package/dist/server/capabilities.js +16 -1
  13. package/dist/server/capabilities.js.map +1 -1
  14. package/dist/server/git-changes.d.ts +7 -5
  15. package/dist/server/git-changes.js +15 -10
  16. package/dist/server/git-changes.js.map +1 -1
  17. package/dist/server/server.d.ts +59 -58
  18. package/dist/server/server.js +54 -3
  19. package/dist/server/server.js.map +1 -1
  20. package/dist/workflows/run.d.ts +27 -7
  21. package/dist/workflows/run.js +106 -43
  22. package/dist/workflows/run.js.map +1 -1
  23. package/dist/workspace/config.d.ts +8 -0
  24. package/dist/workspace/config.js +29 -2
  25. package/dist/workspace/config.js.map +1 -1
  26. package/dist/workspace/semaphore.d.ts +7 -1
  27. package/dist/workspace/semaphore.js +10 -4
  28. package/dist/workspace/semaphore.js.map +1 -1
  29. package/dist/workspace/ui-state.d.ts +3 -2
  30. package/dist/workspace/ui-state.js +3 -2
  31. package/dist/workspace/ui-state.js.map +1 -1
  32. package/package.json +3 -3
  33. package/web/dist/assets/{alert-dialog-ghffK6g9.js → alert-dialog-V9cigyAO.js} +1 -1
  34. package/web/dist/assets/arrow-down-DflHdNlN.js +1 -0
  35. package/web/dist/assets/arrow-left-IIL4uR-C.js +1 -0
  36. package/web/dist/assets/centered-state-BkAJVLJE.js +43 -0
  37. package/web/dist/assets/chevron-right-CxAoD1eD.js +1 -0
  38. package/web/dist/assets/circle-check-CdwcIbkx.js +1 -0
  39. package/web/dist/assets/{circle-x-C3EFZ3xf.js → circle-x-Cnc43xJ2.js} +1 -1
  40. package/web/dist/assets/{collapsible-B0JVzd0B.js → collapsible-BlF-7CeC.js} +1 -1
  41. package/web/dist/assets/{commit-list-wm6DPHNs.js → commit-list-D2mKElUx.js} +1 -1
  42. package/web/dist/assets/{compare-variants-DbdP4Y5T.js → compare-variants-CFBY16Rc.js} +1 -1
  43. package/web/dist/assets/{diff-Bizc96qe.js → diff-DE6u5dTe.js} +2 -2
  44. package/web/dist/assets/{diff-stat-Dyfg3ltF.js → diff-stat-B1j6L7C6.js} +1 -1
  45. package/web/dist/assets/{diff-view-CG8ek6tC.js → diff-view-Gb2kk3Le.js} +1 -1
  46. package/web/dist/assets/{dropdown-menu-C1Dlf35P.js → dropdown-menu-Ns-rY6sm.js} +1 -1
  47. package/web/dist/assets/editable-title-BGuiDxFT.js +1 -0
  48. package/web/dist/assets/{ellipsis-vertical-DpGTRqC2.js → ellipsis-vertical-BbxtXb6s.js} +1 -1
  49. package/web/dist/assets/{file-DlUGpUlb.js → file-j5O4NryU.js} +1 -1
  50. package/web/dist/assets/{folder-BAY6Vddy.js → folder-w9Lwj9x9.js} +1 -1
  51. package/web/dist/assets/{git-pull-request-agOG0iGM.js → git-pull-request-DlSop3Ch.js} +1 -1
  52. package/web/dist/assets/{git-toolbar-DGiNMmtY.js → git-toolbar-BdJ8XSxI.js} +1 -1
  53. package/web/dist/assets/github-CCLn2OAU.js +1 -0
  54. package/web/dist/assets/{image-preview-BWGk5fPX.js → image-preview-CcFdkZXW.js} +1 -1
  55. package/web/dist/assets/index-BmHHLbwc.js +6 -0
  56. package/web/dist/assets/index-u8OAnq0r.css +2 -0
  57. package/web/dist/assets/{markdown-BGBOGCA_.js → markdown-q9lm7kQ2.js} +1 -1
  58. package/web/dist/assets/{new-task-form-BpAw_Dks.js → new-task-form-BuYxAWN7.js} +1 -1
  59. package/web/dist/assets/{pill-Dvs-P0xy.js → pill-t97MvPW_.js} +1 -1
  60. package/web/dist/assets/{project-router-Cx8NPoqn.js → project-router-DudOmkHf.js} +1 -1
  61. package/web/dist/assets/{prompt-templates-CSBYBR1I.js → prompt-templates-C5iO3Lda.js} +1 -1
  62. package/web/dist/assets/{refresh-cw-BoGdAykg.js → refresh-cw-_C2EGJK5.js} +1 -1
  63. package/web/dist/assets/{repo-git-PmX-BNMo.js → repo-git-DqQzj33d.js} +1 -1
  64. package/web/dist/assets/{run-diff-EcOkf3ti.js → run-diff-BLoKEnTx.js} +1 -1
  65. package/web/dist/assets/run-header-Dho4eurS.js +1 -0
  66. package/web/dist/assets/{search-x-X3EpVhgs.js → search-x-BgxaJgro.js} +1 -1
  67. package/web/dist/assets/{skill-empty-hint-CZLALx6l.js → skill-empty-hint-D5MOmycv.js} +1 -1
  68. package/web/dist/assets/skills-B5g064CC.js +1 -0
  69. package/web/dist/assets/{sparkles-Dt4q7pbG.js → sparkles-ANSka5bH.js} +1 -1
  70. package/web/dist/assets/{square-terminal-hwBL8XsC.js → square-terminal-qojq20cH.js} +1 -1
  71. package/web/dist/assets/{tab-link-DIlR5T07.js → tab-link-CIcwFJtC.js} +1 -1
  72. package/web/dist/assets/task-changes-yRfsEsnh.js +1 -0
  73. package/web/dist/assets/{task-commits-BTTA5k5T.js → task-commits-yXmDrCoE.js} +1 -1
  74. package/web/dist/assets/{task-files-Da8_4C9g.js → task-files-BDRmCCPN.js} +2 -2
  75. package/web/dist/assets/task-thread-C9AnIz4Z.js +9 -0
  76. package/web/dist/assets/{textarea-CUdokhkv.js → textarea-DVO7pg8Y.js} +1 -1
  77. package/web/dist/assets/{thread-loading-B8q8ukXJ.js → thread-loading-DJDl701Z.js} +1 -1
  78. package/web/dist/assets/{trash-2-DhVIdWv8.js → trash-2-DKcnmg6e.js} +1 -1
  79. package/web/dist/assets/{triangle-alert-XYT0DGCI.js → triangle-alert-CPUEVSZ3.js} +1 -1
  80. package/web/dist/assets/{upload-BKjN59-1.js → upload-D5Jrq7QK.js} +1 -1
  81. package/web/dist/assets/{use-desktop-Bf5LS_P4.js → use-desktop-CXae6uyk.js} +1 -1
  82. package/web/dist/assets/{use-submit-shortcut-CzVyoAMd.js → use-submit-shortcut-RKJLKdsW.js} +1 -1
  83. package/web/dist/assets/{utils-DsLuRXwV.js → utils-Wtkd5E6j.js} +1 -1
  84. package/web/dist/assets/{workflows-o2QU_pvU.js → workflows-CGyTdyds.js} +3 -3
  85. package/web/dist/assets/{zoomable-image-7WyUiaVD.js → zoomable-image-hcqwUIlQ.js} +1 -1
  86. package/web/dist/index.html +22 -22
  87. package/web/dist/assets/arrow-down-Cz8qb71e.js +0 -1
  88. package/web/dist/assets/arrow-left-wcYKQXI_.js +0 -1
  89. package/web/dist/assets/centered-state-ZtnFHA-n.js +0 -43
  90. package/web/dist/assets/chevron-right-G4caF2QV.js +0 -1
  91. package/web/dist/assets/circle-check--KvBxXWp.js +0 -1
  92. package/web/dist/assets/editable-title-5rC_QRBZ.js +0 -1
  93. package/web/dist/assets/github-Vp7lxYGQ.js +0 -1
  94. package/web/dist/assets/index-DSxyJpuK.css +0 -2
  95. package/web/dist/assets/index-DiawI6RX.js +0 -6
  96. package/web/dist/assets/run-header-BGiv6_qX.js +0 -1
  97. package/web/dist/assets/skills-CC91ouGH.js +0 -1
  98. package/web/dist/assets/task-changes-XkHDxzQA.js +0 -1
  99. package/web/dist/assets/task-thread-DoNXUsHr.js +0 -9
@@ -146,6 +146,8 @@ export function projectRouteManifest(app) {
146
146
  }
147
147
  /** 409 body for the inbox mutators while the follow-up inbox is off (#471). */
148
148
  const FOLLOWUPS_OFF = 'the follow-up inbox is disabled — set CEZ_FOLLOWUPS=1 to enable it';
149
+ /** 409 body for every automations route while GitHub automations are off (#801). */
150
+ const AUTOMATIONS_OFF = 'GitHub automations are disabled — set CEZ_AUTOMATIONS=1 to enable them';
149
151
  /**
150
152
  * The in-process bus for workspace-level SSE events. The registry-mutating
151
153
  * routes (`POST /api/projects` — step 4.2, emits `project-added` for a
@@ -312,6 +314,12 @@ const workspaceUiStateSchema = z
312
314
  appearance: appearanceSchema.optional(),
313
315
  notifications: z.object({ enabled: z.boolean().optional() }).passthrough().optional(),
314
316
  dismissedProviderAuthFailures: providerAuthDismissalsSchema.optional(),
317
+ // LEGACY, like `sidebar` below: both moved into the browser's own
318
+ // localStorage (packages/web/src/lib/{last-location,sidebar-collapse}.ts),
319
+ // because one workspace-wide answer meant the last client to navigate — a
320
+ // phone, a second window — decided where every other client's next launch
321
+ // landed, and whose sidebar groups were shut. Kept named and bounded here
322
+ // so a cockpit from before that change still round-trips and validates.
315
323
  lastLocation: z
316
324
  .object({
317
325
  projectId: z.string().min(1).max(64),
@@ -2014,12 +2022,35 @@ export function createApp(deps) {
2014
2022
  return c.json(await planChain(repoRoot, parsed.data.task));
2015
2023
  });
2016
2024
  const manualChecks = new Map();
2025
+ /**
2026
+ * The automations gate (#801): with `CEZ_AUTOMATIONS` unset, every route of the feature
2027
+ * answers 409 before touching a store, a lease or GitHub.
2028
+ *
2029
+ * Written as MIDDLEWARE rather than a line in each handler so the family cannot drift: a route
2030
+ * added to either chain below inherits the gate from its path, where a per-handler check is one
2031
+ * omission away from an ungated endpoint.
2032
+ *
2033
+ * Registered against EXPLICIT paths, never `use('*')`. Both chains are mounted with
2034
+ * `.route('/', …)` alongside a dozen unrelated sub-apps, and `route()` re-registers a sub-app's
2035
+ * middleware under the mount prefix — so a `'*'` here would gate the entire `/api/v1` surface,
2036
+ * including `/health`. The two-line pairing (`/automations` and `/automations/*`) is what makes
2037
+ * a path match both the collection and everything under it.
2038
+ */
2039
+ const requireAutomations = async (c, next) => {
2040
+ if (!capabilities().automations)
2041
+ return c.json({ error: AUTOMATIONS_OFF }, 409);
2042
+ await next();
2043
+ };
2017
2044
  // ---- chained family: GitHub automations (project-scoped) ----
2018
2045
  // Every handler below reads `c.get('project')` — the definitions, their runtime state and the
2019
2046
  // execution log are per-project files — so the family is project-scoped and mounted with the
2020
2047
  // rest of the mirrored table. The one exception is the manual-check read, which touches no
2021
2048
  // project at all; it is its own workspace-level family below.
2022
2049
  const automationsRoutes = new Hono()
2050
+ .use('/automations', requireAutomations)
2051
+ .use('/automations/*', requireAutomations)
2052
+ .use('/automation-log', requireAutomations)
2053
+ .use('/automation-log/*', requireAutomations)
2023
2054
  .get('/automations', async (c) => {
2024
2055
  const { root, automationStore } = c.get('project');
2025
2056
  const forge = resolveForge(await getRepoInfo(root));
@@ -2243,6 +2274,7 @@ export function createApp(deps) {
2243
2274
  // above, keyed by an unguessable id that the project-scoped POST hands back. Mounting it under
2244
2275
  // `/api/v1/p/:projectId` too would be a second spelling of a lookup that consults no project.
2245
2276
  const automationChecksRoutes = new Hono()
2277
+ .use('/automation-checks/*', requireAutomations)
2246
2278
  .get('/automation-checks/:checkId', (c) => {
2247
2279
  const check = manualChecks.get(c.req.param('checkId'));
2248
2280
  return check ? c.json(check) : c.json({ error: 'not found' }, 404);
@@ -2750,6 +2782,8 @@ export function createApp(deps) {
2750
2782
  return c.json({ error: NO_WORKTREE }, 409);
2751
2783
  const result = await collectChanges(workingDirectory, run.baseBranch ?? 'HEAD', {
2752
2784
  taskBranch: run.branch,
2785
+ // Anchors a repointed worktree at the branch as this run found it (#751).
2786
+ runStartedAt: run.startedAt,
2753
2787
  // A read-only GET against the user's real checkout must never modify its index.
2754
2788
  intentToAdd: run.worktreePath ? undefined : false,
2755
2789
  });
@@ -3870,6 +3904,11 @@ export function startServer(deps, port) {
3870
3904
  semaphore: deps.semaphore,
3871
3905
  automationStore: (projectId, root) => automationCoordinator.store(projectId, root),
3872
3906
  });
3907
+ // #801: GitHub automations are opt-in. Off, the flag must remove the BEHAVIOR and not merely
3908
+ // the UI — no scheduler, no GitHub polling, no launched runs — so every entry point into the
3909
+ // workspace scheduler below is gated on it. Read per call rather than captured, for the same
3910
+ // reason `capabilities()` is inside `createApp`: tests flip the variable between apps.
3911
+ const automationsEnabled = () => resolveCapabilities(process.env, deps.bindHost).automations;
3873
3912
  let rescheduleAutomations = () => { };
3874
3913
  const app = createApp({
3875
3914
  ...deps,
@@ -3922,7 +3961,14 @@ export function startServer(deps, port) {
3922
3961
  };
3923
3962
  },
3924
3963
  });
3925
- rescheduleAutomations = () => { void automationScheduler.reschedule(); };
3964
+ // The scheduler is inert until `start()` anyway (it constructs `stopped`), but the gate is
3965
+ // stated here rather than inherited from that detail: a definition saved while the flag is off
3966
+ // must not even ask the coordinator to refresh.
3967
+ rescheduleAutomations = () => {
3968
+ if (!automationsEnabled())
3969
+ return;
3970
+ void automationScheduler.reschedule();
3971
+ };
3926
3972
  const unsubscribe = workspaceEvents.on((event, data) => {
3927
3973
  if (event === 'project-added') {
3928
3974
  const project = data.project;
@@ -3932,7 +3978,7 @@ export function startServer(deps, port) {
3932
3978
  const parsed = parseRemote(info?.remote ?? '');
3933
3979
  if (parsed?.host === 'github.com')
3934
3980
  automationProjects.set(project.id, { root: project.root, owner: parsed.owner, repo: parsed.repo });
3935
- return automationScheduler.reschedule();
3981
+ return rescheduleAutomations();
3936
3982
  });
3937
3983
  }
3938
3984
  }
@@ -3943,7 +3989,7 @@ export function startServer(deps, port) {
3943
3989
  if (typeof id === 'string') {
3944
3990
  automationCoordinator.remove(id);
3945
3991
  automationProjects.delete(id);
3946
- void automationScheduler.reschedule();
3992
+ rescheduleAutomations();
3947
3993
  }
3948
3994
  }
3949
3995
  });
@@ -3952,6 +3998,11 @@ export function startServer(deps, port) {
3952
3998
  const all = projects.some((project) => project.root === deps.repoRoot)
3953
3999
  ? projects : [{ id: deps.bootProjectId ?? 'default', root: deps.repoRoot, status: 'ok' }, ...projects];
3954
4000
  coordinator.start(all);
4001
+ // #801: with automations off there is nothing to warm — no remote to resolve, no receipts
4002
+ // to reconcile, and above all no scheduler to start. The skills-update coordinator above is
4003
+ // a separate feature and starts either way.
4004
+ if (!automationsEnabled())
4005
+ return;
3955
4006
  void Promise.all(all.map(async (project) => {
3956
4007
  const parsed = parseRemote((await getRepoInfo(project.root))?.remote ?? '');
3957
4008
  if (parsed?.host === 'github.com')