@open-mercato/cezar 0.9.3 → 0.10.0-pr774.1303
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.
- package/README.md +178 -40
- package/dist/agent-config/account-identity.d.ts +44 -0
- package/dist/agent-config/account-identity.js +128 -0
- package/dist/agent-config/account-identity.js.map +1 -0
- package/dist/agent-config/catalog.d.ts +8 -0
- package/dist/agent-config/catalog.js +18 -0
- package/dist/agent-config/catalog.js.map +1 -1
- package/dist/agent-config/model-settings/claude.d.ts +2 -0
- package/dist/agent-config/model-settings/claude.js +11 -0
- package/dist/agent-config/model-settings/claude.js.map +1 -0
- package/dist/agent-config/model-settings/codex.d.ts +2 -0
- package/dist/agent-config/model-settings/codex.js +16 -0
- package/dist/agent-config/model-settings/codex.js.map +1 -0
- package/dist/agent-config/model-settings/opencode.d.ts +2 -0
- package/dist/agent-config/model-settings/opencode.js +8 -0
- package/dist/agent-config/model-settings/opencode.js.map +1 -0
- package/dist/agent-config/model-settings/pi.d.ts +12 -0
- package/dist/agent-config/model-settings/pi.js +18 -0
- package/dist/agent-config/model-settings/pi.js.map +1 -0
- package/dist/agent-config/model-settings/shared.d.ts +9 -0
- package/dist/agent-config/model-settings/shared.js +93 -0
- package/dist/agent-config/model-settings/shared.js.map +1 -0
- package/dist/agent-config/model-settings/types.d.ts +9 -0
- package/dist/agent-config/model-settings/types.js +2 -0
- package/dist/agent-config/model-settings/types.js.map +1 -0
- package/dist/agent-config/models.d.ts +9 -0
- package/dist/agent-config/models.js +32 -0
- package/dist/agent-config/models.js.map +1 -0
- package/dist/agent-config/service.js +4 -4
- package/dist/agent-config/service.js.map +1 -1
- package/dist/automations/coordinator.d.ts +12 -0
- package/dist/automations/coordinator.js +5 -0
- package/dist/automations/coordinator.js.map +1 -1
- package/dist/automations/types.d.ts +4 -0
- package/dist/automations/types.js +2 -1
- package/dist/automations/types.js.map +1 -1
- package/dist/config.d.ts +10 -1
- package/dist/config.js +48 -5
- package/dist/config.js.map +1 -1
- package/dist/contract/agent-config.d.ts +2 -0
- package/dist/contract/agent-profiles.d.ts +347 -0
- package/dist/contract/automations.d.ts +24 -0
- package/dist/contract/events.d.ts +59 -0
- package/dist/contract/github.d.ts +88 -0
- package/dist/contract/health.d.ts +5 -1
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.js +987 -544
- package/dist/contract/projects.d.ts +37 -5
- package/dist/contract/runs.d.ts +247 -0
- package/dist/contract/skills.d.ts +7 -0
- package/dist/contract/workflows.d.ts +6 -0
- package/dist/contract/workspace.d.ts +137 -2
- package/dist/core/agent-env.js +41 -16
- package/dist/core/agent-env.js.map +1 -1
- package/dist/core/agent-model-policy.d.ts +10 -0
- package/dist/core/agent-model-policy.js +36 -0
- package/dist/core/agent-model-policy.js.map +1 -0
- package/dist/core/agent-profiles.d.ts +62 -0
- package/dist/core/agent-profiles.js +90 -0
- package/dist/core/agent-profiles.js.map +1 -0
- package/dist/core/agent-runner.d.ts +37 -6
- package/dist/core/agent-runner.js +35 -3
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/backend-detect.d.ts +5 -5
- package/dist/core/backend-detect.js +44 -7
- package/dist/core/backend-detect.js.map +1 -1
- package/dist/core/claude-cli-runner.js +10 -5
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.js +13 -3
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.js +7 -2
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/model-identity.d.ts +23 -11
- package/dist/core/model-identity.js +41 -24
- package/dist/core/model-identity.js.map +1 -1
- package/dist/core/model-presets.d.ts +8 -2
- package/dist/core/model-presets.js +51 -8
- package/dist/core/model-presets.js.map +1 -1
- package/dist/core/opencode-model-catalog.d.ts +32 -0
- package/dist/core/opencode-model-catalog.js +153 -0
- package/dist/core/opencode-model-catalog.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +2 -0
- package/dist/core/opencode-server-runner.js +42 -11
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/pi-runner.d.ts +24 -0
- package/dist/core/pi-runner.js +349 -0
- package/dist/core/pi-runner.js.map +1 -0
- package/dist/core/pi-ui-mapper.d.ts +34 -0
- package/dist/core/pi-ui-mapper.js +270 -0
- package/dist/core/pi-ui-mapper.js.map +1 -0
- package/dist/core/provider-auth.d.ts +95 -3
- package/dist/core/provider-auth.js +234 -15
- package/dist/core/provider-auth.js.map +1 -1
- package/dist/core/provider-availability.d.ts +1 -1
- package/dist/core/provider-availability.js +2 -1
- package/dist/core/provider-availability.js.map +1 -1
- package/dist/core/runner-factory.js +3 -0
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/shell-env.d.ts +28 -0
- package/dist/core/shell-env.js +56 -0
- package/dist/core/shell-env.js.map +1 -0
- package/dist/core/ui-events.d.ts +1 -1
- package/dist/core/usage-limit.d.ts +42 -0
- package/dist/core/usage-limit.js +209 -0
- package/dist/core/usage-limit.js.map +1 -0
- package/dist/index.js +4 -32
- package/dist/index.js.map +1 -1
- package/dist/paths.d.ts +53 -5
- package/dist/paths.js +63 -9
- package/dist/paths.js.map +1 -1
- package/dist/planner.js +6 -0
- package/dist/planner.js.map +1 -1
- package/dist/release/snapshot.d.ts +13 -2
- package/dist/release/snapshot.js +25 -1
- package/dist/release/snapshot.js.map +1 -1
- package/dist/runs/agent-tmpdir.d.ts +41 -0
- package/dist/runs/agent-tmpdir.js +183 -0
- package/dist/runs/agent-tmpdir.js.map +1 -0
- package/dist/runs/auto-name.js +6 -1
- package/dist/runs/auto-name.js.map +1 -1
- package/dist/runs/event-history.d.ts +50 -0
- package/dist/runs/event-history.js +613 -0
- package/dist/runs/event-history.js.map +1 -0
- package/dist/runs/run-index.d.ts +19 -0
- package/dist/runs/run-index.js +43 -0
- package/dist/runs/run-index.js.map +1 -0
- package/dist/runs/store.d.ts +78 -19
- package/dist/runs/store.js +175 -43
- package/dist/runs/store.js.map +1 -1
- package/dist/server/forge/github.d.ts +169 -0
- package/dist/server/forge/github.js +511 -0
- package/dist/server/forge/github.js.map +1 -1
- package/dist/server/forge/index.d.ts +9 -0
- package/dist/server/forge/index.js +14 -0
- package/dist/server/forge/index.js.map +1 -1
- package/dist/server/github.d.ts +2 -2
- package/dist/server/github.js +1 -1
- package/dist/server/github.js.map +1 -1
- package/dist/server/open-in-app.d.ts +2 -1
- package/dist/server/open-in-app.js +3 -1
- package/dist/server/open-in-app.js.map +1 -1
- package/dist/server/open-in-terminal.d.ts +46 -1
- package/dist/server/open-in-terminal.js +72 -13
- package/dist/server/open-in-terminal.js.map +1 -1
- package/dist/server/provider-action-gate.js +3 -1
- package/dist/server/provider-action-gate.js.map +1 -1
- package/dist/server/server.d.ts +2866 -1090
- package/dist/server/server.js +1261 -157
- package/dist/server/server.js.map +1 -1
- package/dist/server-install/engine.js +5 -18
- package/dist/server-install/engine.js.map +1 -1
- package/dist/server-install/steps.js +6 -2
- package/dist/server-install/steps.js.map +1 -1
- package/dist/skills.js +4 -0
- package/dist/skills.js.map +1 -1
- package/dist/workflows/run.d.ts +202 -2
- package/dist/workflows/run.js +782 -115
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/types.d.ts +3 -0
- package/dist/workflows/types.js +11 -2
- package/dist/workflows/types.js.map +1 -1
- package/dist/workspace/agent-accounts.d.ts +153 -0
- package/dist/workspace/agent-accounts.js +304 -0
- package/dist/workspace/agent-accounts.js.map +1 -0
- package/dist/workspace/agent-profiles.d.ts +78 -0
- package/dist/workspace/agent-profiles.js +115 -0
- package/dist/workspace/agent-profiles.js.map +1 -0
- package/dist/workspace/boot.d.ts +27 -0
- package/dist/workspace/boot.js +41 -0
- package/dist/workspace/boot.js.map +1 -0
- package/dist/workspace/config.d.ts +27 -5
- package/dist/workspace/config.js +65 -7
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/projects-cli.js +60 -8
- package/dist/workspace/projects-cli.js.map +1 -1
- package/dist/workspace/projects.d.ts +53 -14
- package/dist/workspace/projects.js +80 -6
- package/dist/workspace/projects.js.map +1 -1
- package/dist/workspace/semaphore.d.ts +39 -0
- package/dist/workspace/semaphore.js +31 -0
- package/dist/workspace/semaphore.js.map +1 -1
- package/package.json +3 -3
- package/scripts/mock-claude.mjs +20 -0
- package/scripts/mock-pi-rpc.mjs +83 -0
- package/web/dist/assets/{alert-dialog-V9cigyAO.js → alert-dialog-hkNPaZRi.js} +1 -1
- package/web/dist/assets/arrow-down-D2iIUQAW.js +1 -0
- package/web/dist/assets/arrow-left-DEE028uP.js +1 -0
- package/web/dist/assets/centered-state-BDz6BPZk.js +43 -0
- package/web/dist/assets/chevron-right-DoEQW9ej.js +1 -0
- package/web/dist/assets/{collapsible-BlF-7CeC.js → collapsible-0iis9pcm.js} +1 -1
- package/web/dist/assets/{commit-list-D2mKElUx.js → commit-list-DBoeVzR6.js} +1 -1
- package/web/dist/assets/{compare-variants-CFBY16Rc.js → compare-variants-5O8L23qo.js} +1 -1
- package/web/dist/assets/{diff-DE6u5dTe.js → diff-C6--rd0G.js} +2 -2
- package/web/dist/assets/{diff-stat-B1j6L7C6.js → diff-stat-dE68IlKv.js} +1 -1
- package/web/dist/assets/{diff-view-Gb2kk3Le.js → diff-view-BeEFNLb6.js} +1 -1
- package/web/dist/assets/dropdown-menu-B9U3zF1L.js +1 -0
- package/web/dist/assets/editable-title-DrBXWtYd.js +1 -0
- package/web/dist/assets/{ellipsis-vertical-BbxtXb6s.js → ellipsis-vertical-CCFCFkWk.js} +1 -1
- package/web/dist/assets/{file-j5O4NryU.js → file-B4grjx2C.js} +1 -1
- package/web/dist/assets/{folder-w9Lwj9x9.js → folder-CvpB57Yb.js} +1 -1
- package/web/dist/assets/git-toolbar-B6sgRUzW.js +1 -0
- package/web/dist/assets/github-97NMg0uv.js +1 -0
- package/web/dist/assets/{image-preview-CcFdkZXW.js → image-preview-lZB635bM.js} +1 -1
- package/web/dist/assets/index-Bx-grqa9.css +2 -0
- package/web/dist/assets/index-CVluUmb0.js +7 -0
- package/web/dist/assets/{markdown-q9lm7kQ2.js → markdown-BAm1TcjO.js} +1 -1
- package/web/dist/assets/new-task-form-Bo5FKX0z.js +1 -0
- package/web/dist/assets/pill-CYOPEmp0.js +1 -0
- package/web/dist/assets/{project-router-DudOmkHf.js → project-router-cnAERE2i.js} +1 -1
- package/web/dist/assets/prompt-templates-r-nZtS7T.js +15 -0
- package/web/dist/assets/{refresh-cw-_C2EGJK5.js → refresh-cw-o3Wn7PyH.js} +1 -1
- package/web/dist/assets/repo-git-C_DXDdi4.js +1 -0
- package/web/dist/assets/{run-diff-BLoKEnTx.js → run-diff-B3BOoloA.js} +2 -2
- package/web/dist/assets/run-header-BCIJLsNS.js +1 -0
- package/web/dist/assets/{search-x-BgxaJgro.js → search-x-DScLr245.js} +1 -1
- package/web/dist/assets/{skill-empty-hint-D5MOmycv.js → skill-empty-hint-CZj2AX7U.js} +1 -1
- package/web/dist/assets/skills-D20VwW8x.js +1 -0
- package/web/dist/assets/{sparkles-ANSka5bH.js → sparkles-Y9BJG5RV.js} +1 -1
- package/web/dist/assets/{square-terminal-qojq20cH.js → square-terminal-BsfX_P1h.js} +1 -1
- package/web/dist/assets/tab-link-CkkMT1ow.js +1 -0
- package/web/dist/assets/task-changes-r2usbype.js +1 -0
- package/web/dist/assets/{task-commits-yXmDrCoE.js → task-commits-tQTEChg7.js} +1 -1
- package/web/dist/assets/{task-files-BDRmCCPN.js → task-files-DLMGNPyl.js} +2 -2
- package/web/dist/assets/task-thread-CLB_YU08.js +9 -0
- package/web/dist/assets/{textarea-DVO7pg8Y.js → textarea-zitC1Vpd.js} +1 -1
- package/web/dist/assets/thread-loading-Df63bvNE.js +1 -0
- package/web/dist/assets/{trash-2-DKcnmg6e.js → trash-2-LjIQweMh.js} +1 -1
- package/web/dist/assets/{triangle-alert-CPUEVSZ3.js → triangle-alert-BFPaZoow.js} +1 -1
- package/web/dist/assets/{upload-D5Jrq7QK.js → upload-CIRk4UwY.js} +1 -1
- package/web/dist/assets/{use-desktop-CXae6uyk.js → use-desktop-NYBqEVzY.js} +1 -1
- package/web/dist/assets/{use-submit-shortcut-RKJLKdsW.js → use-submit-shortcut-Cf3K6RlM.js} +1 -1
- package/web/dist/assets/utils-tQh4-6ps.js +64 -0
- package/web/dist/assets/{workflows-CGyTdyds.js → workflows-Y0bRutk3.js} +3 -3
- package/web/dist/assets/{zoomable-image-hcqwUIlQ.js → zoomable-image-Bkx-ILIF.js} +1 -1
- package/web/dist/index.html +24 -23
- package/web/dist/assets/arrow-down-DflHdNlN.js +0 -1
- package/web/dist/assets/arrow-left-IIL4uR-C.js +0 -1
- package/web/dist/assets/centered-state-BkAJVLJE.js +0 -43
- package/web/dist/assets/chevron-right-CxAoD1eD.js +0 -1
- package/web/dist/assets/circle-check-CdwcIbkx.js +0 -1
- package/web/dist/assets/circle-x-Cnc43xJ2.js +0 -1
- package/web/dist/assets/dropdown-menu-Ns-rY6sm.js +0 -1
- package/web/dist/assets/editable-title-BGuiDxFT.js +0 -1
- package/web/dist/assets/git-pull-request-DlSop3Ch.js +0 -1
- package/web/dist/assets/git-toolbar-BdJ8XSxI.js +0 -1
- package/web/dist/assets/github-CCLn2OAU.js +0 -1
- package/web/dist/assets/index-BmHHLbwc.js +0 -6
- package/web/dist/assets/index-u8OAnq0r.css +0 -2
- package/web/dist/assets/new-task-form-BuYxAWN7.js +0 -1
- package/web/dist/assets/pill-t97MvPW_.js +0 -1
- package/web/dist/assets/prompt-templates-C5iO3Lda.js +0 -15
- package/web/dist/assets/repo-git-DqQzj33d.js +0 -1
- package/web/dist/assets/run-header-Dho4eurS.js +0 -1
- package/web/dist/assets/skills-B5g064CC.js +0 -1
- package/web/dist/assets/tab-link-CIcwFJtC.js +0 -1
- package/web/dist/assets/task-changes-yRfsEsnh.js +0 -1
- package/web/dist/assets/task-thread-C9AnIz4Z.js +0 -9
- package/web/dist/assets/thread-loading-DJDl701Z.js +0 -1
- package/web/dist/assets/utils-Wtkd5E6j.js +0 -64
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
**Parallel coding agents orchestrator** — a local cockpit for running and
|
|
6
6
|
tracking AI coding-agent tasks in your repo.
|
|
7
7
|
|
|
8
|
-
Type a task, pick a workflow and an agent — **Claude Code, Codex or
|
|
9
|
-
(experimental), or a mix of them per step** — and watch it work live: steps, tool calls,
|
|
8
|
+
Type a task, pick a workflow and an agent — **Claude Code, Codex, OpenCode or pi
|
|
9
|
+
(the latter two experimental), or a mix of them per step** — and watch it work live: steps, tool calls,
|
|
10
10
|
tokens, diffs, in a browser cockpit that runs entirely on your machine.
|
|
11
11
|
Your CLI logins, your `gh`, your files. No accounts, no database, no cloud.
|
|
12
12
|
|
|
@@ -60,7 +60,7 @@ it does better than any of them:
|
|
|
60
60
|
|
|
61
61
|
- 🪶 **Genuinely zero config.** `npx cezar-cli` in your repo and you're running —
|
|
62
62
|
no wizard, no API keys, no env vars, no schema, no database. It rides the
|
|
63
|
-
`claude` / `codex` / `opencode` logins and the `gh` you already have, and every
|
|
63
|
+
`claude` / `codex` / `opencode` / `pi` logins and the `gh` you already have, and every
|
|
64
64
|
missing piece degrades gracefully instead of blocking you.
|
|
65
65
|
- 🖥️ **Built for a server (VPS mode).** cezar is made to live on a **VPS, cloud,
|
|
66
66
|
or dedicated box** as an always-on janitor for your repo — headless-first, with
|
|
@@ -169,22 +169,47 @@ CLIs you are already logged into, `claude` by default.
|
|
|
169
169
|
> instead of the real CLI — the whole cockpit works with no `claude` login, so
|
|
170
170
|
> you can explore runs, diffs, variants and the review gate offline.
|
|
171
171
|
|
|
172
|
+
### Nightly builds — help us shape cezar 🌙
|
|
173
|
+
|
|
174
|
+
Every night we publish the trunk to npm, so the features landing in the next
|
|
175
|
+
release are one command away:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npx cezar-cli@nightly # everything merged as of last night
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Come build this with us.** cezar is shaped by the people who run it on real
|
|
182
|
+
repos: if you try a nightly and something feels wrong — a workflow that stalls, a
|
|
183
|
+
diff that reads badly, a runner that should exist — [open an
|
|
184
|
+
issue](https://github.com/open-mercato/cezar/issues) and tell us. That feedback,
|
|
185
|
+
early, is worth more than a bug report six weeks after a release, and it is how
|
|
186
|
+
most of the features here got their final shape.
|
|
187
|
+
|
|
188
|
+
**Know what you're installing.** A nightly is verified (typecheck, unit suites,
|
|
189
|
+
packaged-CLI e2e — the same gate a release runs) but it is *not* a release: it
|
|
190
|
+
can be rough, a flag or a screen may change under you, and something occasionally
|
|
191
|
+
breaks in a way no test caught. Nothing is at risk beyond your patience — every
|
|
192
|
+
task runs in its own git worktree and cezar never auto-merges — but if you need a
|
|
193
|
+
boring day, stay on the stable release. Pin a nightly you liked with its exact
|
|
194
|
+
version (`npx cezar-cli@0.9.2-nightly.20260813.126` — the cockpit prints the
|
|
195
|
+
version it booted, and the date in it tells you how old the build is), and drop
|
|
196
|
+
back to stable any time with a plain `npx cezar-cli`.
|
|
197
|
+
|
|
172
198
|
### Preview builds
|
|
173
199
|
|
|
174
|
-
Every green CI run publishes an installable npm snapshot
|
|
175
|
-
([how it works](docs/publishing.md)), so you can try
|
|
176
|
-
|
|
200
|
+
Every green CI run also publishes an installable npm snapshot
|
|
201
|
+
([how it works](docs/publishing.md)), so you can try code that has not even
|
|
202
|
+
merged yet:
|
|
177
203
|
|
|
178
204
|
```bash
|
|
179
205
|
npx cezar-cli@develop # current develop head
|
|
180
|
-
npx cezar-cli@main # current main head (ahead of the latest stable release)
|
|
181
206
|
```
|
|
182
207
|
|
|
183
208
|
Every pull request gets its own preview too — the CI bot posts a sticky comment
|
|
184
209
|
on the PR with the exact pinned version to copy-paste
|
|
185
|
-
(`npx cezar-cli@<version>-pr<N>.<run>`).
|
|
186
|
-
their own dist-tags; a plain `npx cezar-cli` always
|
|
187
|
-
stable release.
|
|
210
|
+
(`npx cezar-cli@<version>-pr<N>.<run>`). Nightlies and previews are all
|
|
211
|
+
prerelease versions under their own dist-tags; a plain `npx cezar-cli` always
|
|
212
|
+
resolves to the latest stable release.
|
|
188
213
|
|
|
189
214
|
---
|
|
190
215
|
|
|
@@ -207,7 +232,7 @@ event live and parks the run at a review gate when there's a diff to inspect.
|
|
|
207
232
|
▼
|
|
208
233
|
┌──────────────────────────────┐ ┌───────────────────────────────┐
|
|
209
234
|
│ git worktree per task │ │ agent CLI (your login) │
|
|
210
|
-
│ (isolated branch, parallel) │◄───►│
|
|
235
|
+
│ (isolated branch, parallel) │◄───►│ claude · codex · opencode · pi│
|
|
211
236
|
└──────────────────────────────┘ │ Bash open · no prompts │
|
|
212
237
|
│ └───────────────────────────────┘
|
|
213
238
|
│ agent text · tool calls · tool results · tokens · cost
|
|
@@ -285,11 +310,12 @@ Five moves that make the cockpit worth the browser tab:
|
|
|
285
310
|
|
|
286
311
|
## Cockpit tour
|
|
287
312
|
|
|
288
|
-
|
|
313
|
+
Eight views, one browser window, all live over Server-Sent Events (seven until you opt into the Inbox):
|
|
289
314
|
|
|
290
315
|
| View | What's in it |
|
|
291
316
|
|---|---|
|
|
292
317
|
| **Tasks** | Every task with its status, live event stream (agent text · tool calls · tool results · pasted/generated screenshots), tokens and cost. Continue, cancel, open in terminal (`claude --resume`), review the diff, or push a draft PR. |
|
|
318
|
+
| **All tasks** | Every *registered project's* tasks in one table, filtered and grouped by tag, project, status or workflow — see [Grouping connected repositories](#grouping-connected-repositories-tags-and-the-all-tasks-page). Appears once a second project is registered. |
|
|
293
319
|
| **Inbox** | **Opt-in** (`CEZ_FOLLOWUPS=1`; hidden by default). Follow-ups an agent left behind (`todos.json`) — one click turns a suggestion into the next task, pre-wired to its suggested skill. Off, agents are never asked to leave follow-ups; each task's own **Notes** handoff journal is unaffected. |
|
|
294
320
|
| **Git** | Branch, working-tree status, diff vs HEAD, recent commits (click one for its inline patch + GitHub link), and the configurable base branch that worktrees fork from and PRs target. |
|
|
295
321
|
| **GitHub** | Open issues and PRs of the repo's origin, read through your logged-in `gh`. Hand an issue straight to the agent — pick a workflow and skills, one click runs it. |
|
|
@@ -307,12 +333,21 @@ a GitHub page.
|
|
|
307
333
|
## Multiple projects, one cockpit
|
|
308
334
|
|
|
309
335
|
One `cezar serve` hosts **every repo you work in**, not just the one you started
|
|
310
|
-
it in.
|
|
311
|
-
|
|
336
|
+
it in. Projects live in a per-user registry at `~/.cezar/config.json` — the
|
|
337
|
+
workspace file that also holds the global knobs
|
|
312
338
|
(the parallel cap, the memory ceiling, the browse root, and the checkout root). Nothing is added to
|
|
313
339
|
the repo: per-project state stays exactly where it was, in that repo's
|
|
314
340
|
`.ai/cezar/`.
|
|
315
341
|
|
|
342
|
+
**Your first run registers the repo you start it in** — that is the whole setup.
|
|
343
|
+
After that the registry is yours to curate: starting cezar somewhere else serves
|
|
344
|
+
that folder as usual (its own tasks, its own `.ai/cezar/`) but does not add it to
|
|
345
|
+
the list behind your back. It shows up at the top of the sidebar marked **not
|
|
346
|
+
saved**, and **Settings → Projects** lists it as *not registered* with an
|
|
347
|
+
**Add project** button — so the folder you launched in is always one click from
|
|
348
|
+
being kept, and never kept without the click. `cezar projects add` does the same
|
|
349
|
+
from a terminal.
|
|
350
|
+
|
|
316
351
|
Every view is project-scoped:
|
|
317
352
|
|
|
318
353
|
```
|
|
@@ -336,25 +371,85 @@ and task list — and the new-task composer names the project it will run in.
|
|
|
336
371
|
|
|
337
372
|
Removing a project (**Settings → Projects**) drops the registry entry only — the
|
|
338
373
|
repo and its `.ai/cezar/` are never touched, so re-adding it later finds all its
|
|
339
|
-
tasks intact. The project cezar is currently serving can't be removed
|
|
340
|
-
|
|
374
|
+
tasks intact. The project cezar is currently serving can't be removed from the
|
|
375
|
+
cockpit — stop the server and use `cezar projects remove <id>` instead.
|
|
341
376
|
|
|
342
377
|
**From the terminal** — the same registry, no cockpit required (handy over ssh):
|
|
343
378
|
|
|
344
379
|
```bash
|
|
345
|
-
cezar projects # list: id, branch or status, path
|
|
380
|
+
cezar projects # list: id, branch or status, path, tags
|
|
346
381
|
cezar projects add ~/code/api # register a folder (defaults to the current repo)
|
|
347
382
|
cezar projects remove api # drop the registry entry; the repo is untouched
|
|
383
|
+
cezar projects tag api storefront backend # set the grouping tags (no tags clears them)
|
|
348
384
|
```
|
|
349
385
|
|
|
350
386
|
These read and write `~/.cezar/config.json` directly, so they work with the
|
|
351
387
|
server stopped, and `CEZ_HOME` selects which workspace they operate on.
|
|
352
388
|
|
|
353
|
-
Settings split along the same line: **
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
389
|
+
Settings split along the same line: **General** (the project's folder, its
|
|
390
|
+
registry facts, its parallel-task ceiling, and Remove), **Agents**,
|
|
391
|
+
**Worktrees**, **Bookmarklets**, **Prompt templates** and **MCP** describe one
|
|
392
|
+
repo and live under `/p/<projectId>/settings`; **Appearance**,
|
|
393
|
+
**Notifications**, **Resources**, **Projects** and **Keyboard** are yours or the
|
|
394
|
+
machine's and live at `/settings/global`.
|
|
395
|
+
|
|
396
|
+
### Grouping connected repositories: tags and the All tasks page
|
|
397
|
+
|
|
398
|
+
Work rarely stops at a repo boundary. A storefront is an API, a web app and a
|
|
399
|
+
design system; a platform is a handful of services plus the infra that runs
|
|
400
|
+
them. **Tags** are how you say so, and **All tasks** is where saying so pays off.
|
|
401
|
+
|
|
402
|
+
**Tag a repo** in **Settings → Projects**: type into the *Tags* cell on its row
|
|
403
|
+
and press Enter (comma works too; the × on a chip, or Backspace in an empty
|
|
404
|
+
field, removes one). The field **autocompletes from the tags already used in the
|
|
405
|
+
workspace** — click the field to see them all, arrow keys and Enter to pick —
|
|
406
|
+
which is what keeps the second repo landing on the first one's spelling instead
|
|
407
|
+
of inventing `store-front` next to `storefront`. Anything not on the list is
|
|
408
|
+
just typed. A tag is a free-form label — `storefront`, `infra`, `client-acme` —
|
|
409
|
+
and a project can carry several, because a repo can belong to more than one
|
|
410
|
+
piece of work. Tags are trimmed, deduplicated case-insensitively (`API` and
|
|
411
|
+
`api` are one tag) and stored in `~/.cezar/config.json` beside the rest of the
|
|
412
|
+
registry, so they are yours and this machine's, never something added to the
|
|
413
|
+
repo.
|
|
414
|
+
|
|
415
|
+
**All tasks** — the top item in the sidebar, `/tasks`, or `⌘K → All tasks` —
|
|
416
|
+
then shows every registered project's work in one table:
|
|
417
|
+
|
|
418
|
+
- **Filter** by tag, status and workflow. Tags are one-click chips; status and
|
|
419
|
+
workflow are searchable multi-selects. Every facet ORs inside itself and ANDs
|
|
420
|
+
across, so *"anything running or waiting in storefront or infra"* is one set
|
|
421
|
+
of clicks. Each option carries how many tasks it would leave, so a filter that
|
|
422
|
+
would empty the table says so before you click it. The search box matches
|
|
423
|
+
title, project, workflow, branch and tags.
|
|
424
|
+
- **Group by** tag, project, status or workflow — click the pressed one again to
|
|
425
|
+
ungroup. Grouping by tag is the reason tags exist: three repos tagged
|
|
426
|
+
`storefront` become one section, and a repo tagged twice appears under both —
|
|
427
|
+
it genuinely belongs to both.
|
|
428
|
+
|
|
429
|
+
The filters, the grouping and the Active/Archived tab live in the **URL**, so a
|
|
430
|
+
filtered view survives a refresh, pastes into a chat, and sits in a bookmark —
|
|
431
|
+
`/tasks?tag=storefront&status=running&group=tag` is a link to exactly what you
|
|
432
|
+
were looking at. Only what you changed shows up: Active is the default, so the
|
|
433
|
+
Archived view is `?archived=1` and a normal link carries no key for it.
|
|
434
|
+
|
|
435
|
+
Each row shows **every** PR and issue it references — a task opened on an issue
|
|
436
|
+
that landed a PR shows both — plus its cost and live CPU/memory, and can be
|
|
437
|
+
marked **read/unread** (the eye) or **archived** (or restored) right there. Every task title, project name and project group heading links into that
|
|
438
|
+
project, so the thread, its diff and its worktree are one click away and stay
|
|
439
|
+
exactly where they were.
|
|
440
|
+
|
|
441
|
+
There is deliberately **no project filter**: narrowing this page to one project
|
|
442
|
+
is that project's own Tasks page, which is a better version of the same answer
|
|
443
|
+
(live updates, the full column set, the composer). So picking a project *leaves*
|
|
444
|
+
for it rather than turning the global view into a worse local one.
|
|
445
|
+
|
|
446
|
+
Nothing else in cezar reads tags, on purpose: a tag is a lens, not a permission,
|
|
447
|
+
a queue or a routing rule. Removing one changes what you see and nothing else.
|
|
448
|
+
|
|
449
|
+
> The page reads a workspace-wide index capped at the newest 200 tasks per
|
|
450
|
+
> project — it says so, and names the projects it capped, rather than showing a
|
|
451
|
+
> short list as if it were complete. Older tasks are always in that project's own
|
|
452
|
+
> Tasks page.
|
|
358
453
|
|
|
359
454
|
**Old page URLs keep working.** Every unprefixed page path — `/`, `/tasks/<id>`,
|
|
360
455
|
`/settings` — still answers, bound to the project cezar was started in; the
|
|
@@ -383,7 +478,7 @@ steps:
|
|
|
383
478
|
prompt: "{{task}}"
|
|
384
479
|
skill: project-conventions # optional — from .ai/skills or .ai/cezar/skills
|
|
385
480
|
# model: opus # optional per-step model override
|
|
386
|
-
# runner: codex # optional per-step backend: claude · codex · opencode
|
|
481
|
+
# runner: codex # optional per-step backend: claude · codex · opencode · pi
|
|
387
482
|
# allowedTools: [Read, Edit, Write, Grep, Glob, Bash]
|
|
388
483
|
- id: verify
|
|
389
484
|
name: Verify
|
|
@@ -427,6 +522,7 @@ Useful environment variables:
|
|
|
427
522
|
| Var | Effect |
|
|
428
523
|
|---|---|
|
|
429
524
|
| `CEZ_DRY_RUN=1` | Use the bundled mock instead of the real `claude` CLI — the entire cockpit works offline, for demos and development. |
|
|
525
|
+
| `CEZ_AGENT_MODELS_LOCKED=1` | Globally lock each runner to the model configured in its native Claude/Codex/OpenCode settings while keeping runner selection available. Exact `1` also delegates authentication and provider enablement to those native agents, so Cezar skips its credential probes and provider-disable preferences. Existing Cezar presets are preserved but ignored, and an environment change requires a restart. The config-file equivalent is `"modelsLocked": true` in global `~/.cezar/config.json` or one repository's `.ai/cezar/config.json`; config-file locks do not disable provider checks. |
|
|
430
526
|
| `CEZ_APPROVAL_GATE=1` | Opt into Claude's interactive approval UI; by default, unapproved tools are denied without interrupting the run. |
|
|
431
527
|
| `CEZ_FOLLOWUPS=1` | Turn on the global follow-up **Inbox**: agents are asked to leave follow-ups in `todos.json` when they finish, and the Inbox view appears. Off by default — each task's own **Notes** handoff journal runs either way. |
|
|
432
528
|
| `CEZ_AUTOMATIONS=1` | Turn on **GitHub automations**: the Automations view appears and cezar polls GitHub on each enabled automation's interval, launching tasks from what it finds. Off by default, and only the exact value `1` enables it — without it nothing polls GitHub, the automations endpoints answer `409`, and the nav item is absent. Read at boot, so restart after changing it; definitions, receipts and high-watermarks are retained, so unsetting it and restarting restores the feature without migration or data loss. |
|
|
@@ -434,6 +530,8 @@ Useful environment variables:
|
|
|
434
530
|
| `CEZ_CLAUDE_BIN=/path/to/claude` | Override which `claude` binary is used. |
|
|
435
531
|
| `CEZ_CODEX_BIN=/path/to/codex` | Override which `codex` binary is used. |
|
|
436
532
|
| `CEZ_OPENCODE_BIN=/path/to/opencode` | Override which `opencode` binary is used. |
|
|
533
|
+
| `CEZ_PI_BIN=/path/to/pi` | Override which `pi` binary is used. |
|
|
534
|
+
| `CLAUDE_CONFIG_DIR`, `CODEX_HOME` | The agents' **own** variables, honoured where the vendor documents one. Setting one moves that agent's **default account** — the config folder cezar discovers. A *second* login of the same CLI is deliberately not an environment setting, since one process-wide value cannot differ per project: add it under **Settings → Agent accounts** and pick it per project. |
|
|
437
535
|
| `CEZ_BROWSE_ROOT=~/` | Default root for **Add project → Open local folder…**. The picker cannot navigate above it; a saved workspace value overrides the environment default and must name an existing folder. |
|
|
438
536
|
| `CEZ_PROJECTS_DIR=~/cezar/projects` | Default destination for **Clone from GitHub**. Saved workspace settings override it, and missing directories are created recursively. |
|
|
439
537
|
| `CEZ_SKILLS_AUTO_UPDATE=0` | Disable automatic checks and updates for upstream-CLI-tracked Open Mercato skill installations. On by default; a saved global Skills setting overrides this environment default. Checks are delayed, bounded, cached, and non-blocking. |
|
|
@@ -447,6 +545,7 @@ Useful environment variables:
|
|
|
447
545
|
| `GITHUB_TOKEN` | Fallback for GitHub reads/PRs when `gh` isn't authenticated. |
|
|
448
546
|
| `CEZ_ENV_PASSTHROUGH=A,B` | Forward these extra host env vars to spawned agents. By default agents get a least-privilege env (safe shell/toolchain vars + the backend's own auth + `GITHUB_TOKEN` + `CEZ_*`), not your full environment — use this to add a var an agent needs. |
|
|
449
547
|
| `CEZ_AGENT_ENV_FULL=1` | Escape hatch: give spawned agents the full host environment (pre-hardening behavior). Off by default; only set it if you understand that this hands every host secret to the agent process. |
|
|
548
|
+
| `CEZ_AGENT_TMPDIR=0` | Stop giving each task its own temp directory and hand agents the host `TMPDIR` again (pre-#785 behavior). On by default: every run gets `TMPDIR`/`TEMP`/`TMP` pointing at `.ai/cezar/tmp/<task-id>`, created and write-probed before the agent spawns and reaped when the run ends, so concurrent tasks stop sharing one directory and a task refuses to start rather than run against a temp directory that silently swallows its shell output (see Troubleshooting below). Only an exact `0` disables it, and it disables the whole thing — the pre-spawn check included, so this stays an escape hatch you can actually take. |
|
|
450
549
|
| `CEZ_REDACT_SECRETS=0` | Disable scrubbing of credential values/token shapes from the on-disk state (the NDJSON transcript and the free-text fields of `runs.json`). On by default; leave it on. Best-effort defense-in-depth, not a guarantee: it catches known token shapes and the values of your own secret-named env vars, so a credential in neither category can still get through. |
|
|
451
550
|
| `CEZ_TITLE_UPDATES=0` | Turn off the live task-title refresh (namer re-runs on each turn end). The Settings → Agents toggle overrides this default. |
|
|
452
551
|
| `CEZ_AUTONAME=0` | Disable ALL LLM task naming (creation + live) — titles stay heuristic (`437: /om-auto-review-pr`). Under `CEZ_DRY_RUN=1` naming is already off unless forced with `CEZ_AUTONAME=1`. |
|
|
@@ -454,25 +553,56 @@ Useful environment variables:
|
|
|
454
553
|
| `CEZ_NO_BANNER=1` | Skip the `open-mercato/skills` banner on `cezar serve` startup. (The cockpit no longer shows a banner — its skills now live on the Skills page's Manage panel — so this env var is the terminal banner's only switch.) |
|
|
455
554
|
| `VITE_CEZ_API_BASE=http://localhost:4321` | **Build time only**, and only when the cockpit bundle is deployed apart from the service it talks to. Empty (the default) means "the origin that served this page", which is right for both normal cases: the CLI serves the bundle itself, and `npm run dev` proxies `/api` to the local service. A deployment that must be configured without a rebuild can put `<meta name="cez-api-base" content="…">` in the served HTML instead, which wins over this. |
|
|
456
555
|
|
|
556
|
+
### Troubleshooting: the agent's shell returns nothing
|
|
557
|
+
|
|
558
|
+
**Symptom.** A task on the Claude backend keeps working, but every shell command
|
|
559
|
+
comes back with no output and a spurious non-zero exit status — `echo hello`
|
|
560
|
+
included. Redirecting into a file inside the worktree still produces the right
|
|
561
|
+
content, so the commands genuinely run; only the *capture* is lost. Codex tasks
|
|
562
|
+
on the same machine are unaffected, because that backend streams over stdio
|
|
563
|
+
pipes instead of round-tripping a command's output through a temp file.
|
|
564
|
+
|
|
565
|
+
**Diagnosis.** The temp directory the agent was given is out of space or out of
|
|
566
|
+
quota. One line tells you:
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
echo probe > "${TMPDIR:-/tmp}/probe" # "Disk quota exceeded" / "No space left on device"
|
|
570
|
+
df -i "${TMPDIR:-/tmp}" # a tmpfs can exhaust inodes long before bytes
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Under quota the file is *created* and the write then fails, so the backend reads
|
|
574
|
+
back a zero-byte capture file and hands the agent an empty result.
|
|
575
|
+
|
|
576
|
+
**Fix.** Since #785 cezar gives each task its own `TMPDIR` under
|
|
577
|
+
`.ai/cezar/tmp/<task-id>` and write-probes it before spawning, so a broken temp
|
|
578
|
+
directory fails the task with `agent temp directory is not writable: …` on the
|
|
579
|
+
task thread instead of corrupting its work. If you see that error, free space on
|
|
580
|
+
the disk holding the repo. `CEZ_AGENT_TMPDIR=0` turns the whole mechanism off —
|
|
581
|
+
per-task directory and pre-spawn check alike — and hands agents the host
|
|
582
|
+
`TMPDIR` again, which is the way out if the check itself is wrong on your
|
|
583
|
+
platform.
|
|
584
|
+
|
|
457
585
|
---
|
|
458
586
|
|
|
459
587
|
## Coding agent backends
|
|
460
588
|
|
|
461
589
|
cezar is not married to one vendor. Every agent step runs through a single
|
|
462
|
-
`AgentRunner` seam with
|
|
590
|
+
`AgentRunner` seam with four built-in backends:
|
|
463
591
|
|
|
464
592
|
| Backend | CLI | How cezar drives it | Tool access |
|
|
465
593
|
|---|---|---|---|
|
|
466
594
|
| **Claude Code** (default) | [`claude`](https://github.com/anthropics/claude-code) | Headless `stream-json` mode. | Per-tool `--allowedTools` (`bashAllowlist` scopes `Bash`); `dontAsk` denies unapproved tools without prompting (`CEZ_APPROVAL_GATE=1` → `acceptEdits` + approval UI). |
|
|
467
595
|
| **Codex** | [`codex`](https://github.com/openai/codex) | `codex app-server` — JSON-RPC over stdio, the same transport the Codex IDE extensions use. | Ignores `allowedTools`; the default auto mode uses `danger-full-access` with `approvalPolicy: never` (`CEZ_CODEX_NETWORK=0` opts into the network-blocked `workspace-write` sandbox). |
|
|
468
596
|
| **OpenCode** _(experimental)_ | [`opencode`](https://opencode.ai) | `opencode serve` — a local HTTP server with an SSE event stream. | Ignores `allowedTools` entirely; every permission is auto-approved. |
|
|
597
|
+
| **pi** _(experimental)_ | [`pi`](https://github.com/badlogic/pi-mono) | Persistent `--mode rpc` over JSONL; models are picked with the `provider/model` convention. | Maps `allowedTools` onto pi's `--tools` allowlist; a configured `bashAllowlist` disables Bash because pi cannot express command-prefix rules. |
|
|
469
598
|
|
|
470
|
-
> ⚠️ **OpenCode support
|
|
471
|
-
> than the Claude Code and Codex backends, and OpenCode auto-approves
|
|
472
|
-
> (it ignores `allowedTools`). Treat
|
|
599
|
+
> ⚠️ **OpenCode and pi support are experimental.** Both runners work but are less
|
|
600
|
+
> battle-tested than the Claude Code and Codex backends, and OpenCode auto-approves
|
|
601
|
+
> every permission (it ignores `allowedTools`). Treat them as previews and expect
|
|
602
|
+
> rough edges.
|
|
473
603
|
|
|
474
604
|
On startup cezar probes which CLIs are installed and the cockpit only offers
|
|
475
|
-
the backends it found — install any one of the
|
|
605
|
+
the backends it found — install any one of the four and you're operational.
|
|
476
606
|
|
|
477
607
|
**Pick a backend at three levels** (most specific wins):
|
|
478
608
|
|
|
@@ -503,6 +633,17 @@ steps:
|
|
|
503
633
|
Parallel variants (×2/×3) of one task share that task's backend — mixing
|
|
504
634
|
happens per task and per step, not inside a variant group.
|
|
505
635
|
|
|
636
|
+
**Models come from your own machine.** For Codex and OpenCode, the model picker
|
|
637
|
+
is not a list cezar ships — it asks the installed CLI what it can actually run
|
|
638
|
+
(`codex app-server`'s `model/list`, and `opencode models`), caches the answer in
|
|
639
|
+
memory for five minutes, and shows it. A model your provider rolled out
|
|
640
|
+
yesterday is selectable without a cezar release, and one it retired stops being
|
|
641
|
+
offered. Claude Code has no equivalent local catalog, so it keeps a short list
|
|
642
|
+
of tier aliases and pinned versions. `auto` (let the agent decide) is always
|
|
643
|
+
available, including when the CLI is missing, logged out, or slow — discovery
|
|
644
|
+
never blocks the cockpit, and a model you pinned yourself stays selectable even
|
|
645
|
+
if it is absent from the discovered list.
|
|
646
|
+
|
|
506
647
|
The seam is deliberately small: a backend is one class implementing the
|
|
507
648
|
`AgentRunner` interface (`packages/cezar/src/core/agent-runner.ts`) that turns a prompt into
|
|
508
649
|
a stream of normalized events. Other CLIs — pi, aider, whatever ships next —
|
|
@@ -572,12 +713,20 @@ never blocks startup):
|
|
|
572
713
|
// the source against a moving branch head — cezar verifies it resolves to
|
|
573
714
|
// exactly that commit, and reports it as `team.commit`.
|
|
574
715
|
"worktreeRetention": 10, // keep the last N finished worktrees on disk; 0 = unlimited (branch always kept)
|
|
575
|
-
"defaultRunner": "claude", // agent backend: "claude" (default) · "codex" · "opencode"
|
|
716
|
+
"defaultRunner": "claude", // agent backend: "claude" (default) · "codex" · "opencode" · "pi"
|
|
717
|
+
"modelsLocked": true, // optional: native per-runner model is fixed/read-only; runner stays selectable
|
|
576
718
|
"plannerModel": "sonnet", // model the "Plan first" button uses to draft chains
|
|
577
719
|
"baseBranch": "develop" // branch worktrees fork from + PRs target (also settable in the Git tab)
|
|
578
720
|
}
|
|
579
721
|
```
|
|
580
722
|
|
|
723
|
+
Put the same `"modelsLocked": true` key in `~/.cezar/config.json` to apply it
|
|
724
|
+
to every registered project. When the key is absent or `false` in both config
|
|
725
|
+
files (and `CEZ_AGENT_MODELS_LOCKED` is not `1`), each runner's normal model
|
|
726
|
+
selector uses that runner's discovered model list. While locked, the model is
|
|
727
|
+
shown read-only and follows the selected runner's native settings; the runner
|
|
728
|
+
itself remains selectable.
|
|
729
|
+
|
|
581
730
|
Run data (`runs.json`, NDJSON event logs, worktrees, `todos.json`) is
|
|
582
731
|
git-ignored automatically; your workflows and skills stay committable.
|
|
583
732
|
|
|
@@ -691,17 +840,6 @@ Every module is meant to be read in one sitting.
|
|
|
691
840
|
|
|
692
841
|
---
|
|
693
842
|
|
|
694
|
-
## Relationship to cezar (the SaaS)
|
|
695
|
-
|
|
696
|
-
cez is the radically-simple, single-user sibling of
|
|
697
|
-
[**cezar**](https://github.com/comerito/cezar) — the team SaaS cockpit for
|
|
698
|
-
running agents across the whole GitHub issue lifecycle (auto-triage, webhooks,
|
|
699
|
-
Supabase, multi-repo). Same core ideas — agent runner, skills, declarative
|
|
700
|
-
workflows, a live run cockpit — with none of the accounts, database or cloud.
|
|
701
|
-
Start here; graduate to cezar when a team needs shared visibility.
|
|
702
|
-
|
|
703
|
-
---
|
|
704
|
-
|
|
705
843
|
## License
|
|
706
844
|
|
|
707
845
|
**MIT** © Patryk Lewczuk — full text in [LICENSE](LICENSE).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ProviderId } from '../core/provider-auth.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Who an agent account is logged in AS — the "Show details" read (spec
|
|
4
|
+
* `2026-07-29-agent-profiles.md`).
|
|
5
|
+
*
|
|
6
|
+
* This is the second place vendor knowledge lives about an agent's home, beside
|
|
7
|
+
* `catalog.ts` (which config FILES exist) and `core/agent-profiles.ts` (which env var relocates
|
|
8
|
+
* the home). This one knows where each agent writes its own identity. Facts verified against the
|
|
9
|
+
* files on disk 2026-07-29; re-verify before changing them.
|
|
10
|
+
*
|
|
11
|
+
* ## Two rules that are not negotiable
|
|
12
|
+
*
|
|
13
|
+
* 1. **Named fields only, never pass-through.** `~/.codex/auth.json` holds `OPENAI_API_KEY`,
|
|
14
|
+
* `access_token` and `refresh_token` right beside the identity claims. Every reader below picks
|
|
15
|
+
* fields by name and builds a fresh object; nothing here spreads, forwards or stringifies a
|
|
16
|
+
* parsed vendor object, so a key the vendor adds tomorrow cannot leak through. The JWT is read
|
|
17
|
+
* for its CLAIMS and its signature is never a credential we hold onto.
|
|
18
|
+
* 2. **Read on demand, answered to exactly one route.** This never joins the accounts listing,
|
|
19
|
+
* never enters `runs.json` or the NDJSON, and is never logged. `provider-auth.ts` keeps account
|
|
20
|
+
* identity out of its own boundary on purpose; this is the deliberate, opt-in exception —
|
|
21
|
+
* localHandoff-gated, and only when the user asks for it — not a widening of that rule.
|
|
22
|
+
*/
|
|
23
|
+
/** One labelled row, as the pane renders it. Deliberately not a fixed per-provider shape: what an
|
|
24
|
+
* agent knows about its own login differs, and inventing an empty "Organization" for one that has
|
|
25
|
+
* no concept of it would be a worse answer than omitting the row. */
|
|
26
|
+
export interface AccountIdentityField {
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AccountIdentity {
|
|
31
|
+
/** False when there is nothing to show — not signed in, no file, or a file we cannot parse. */
|
|
32
|
+
available: boolean;
|
|
33
|
+
/** Why, in the user's terms, when `available` is false. */
|
|
34
|
+
reason?: string;
|
|
35
|
+
fields: AccountIdentityField[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What this account is logged in as, or an honest reason there is nothing to show.
|
|
39
|
+
*
|
|
40
|
+
* Never throws. OpenCode answers "unsupported": its credentials live in a SQLite DB outside the
|
|
41
|
+
* config dir (see `core/agent-profiles.ts`), so there is nothing in a config folder to read — and
|
|
42
|
+
* guessing from the default login would attribute one account's identity to another.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readAccountIdentity(provider: ProviderId, configDir: string): Promise<AccountIdentity>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { claudeStateFilePath } from '../paths.js';
|
|
4
|
+
/** `.claude.json` can carry per-project history; cap the read like `readUserMcpServers` does. */
|
|
5
|
+
const READ_CAP = 2 * 1024 * 1024;
|
|
6
|
+
const NOT_SIGNED_IN = 'Not signed in on this account yet — use Connect.';
|
|
7
|
+
const UNREADABLE = 'Could not read this account’s details.';
|
|
8
|
+
/** Read a JSON file under the cap. `null` for absent, unreadable, oversized or malformed. */
|
|
9
|
+
async function readJsonCapped(path) {
|
|
10
|
+
try {
|
|
11
|
+
const raw = await readFile(path, 'utf8');
|
|
12
|
+
if (raw.length > READ_CAP)
|
|
13
|
+
return null;
|
|
14
|
+
const parsed = JSON.parse(raw);
|
|
15
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** A non-empty display string, or undefined — so a blank vendor value never renders as an empty row. */
|
|
22
|
+
function text(value) {
|
|
23
|
+
if (typeof value === 'string' && value.trim() !== '')
|
|
24
|
+
return value.trim();
|
|
25
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
26
|
+
return String(value);
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Push a row only when the value is really there. */
|
|
30
|
+
function push(fields, label, value) {
|
|
31
|
+
const shown = text(value);
|
|
32
|
+
if (shown !== undefined)
|
|
33
|
+
fields.push({ label, value: shown });
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Claude Code keeps its login in `.claude.json`'s `oauthAccount` — a *sibling* of `~/.claude` by
|
|
37
|
+
* default, but INSIDE an overridden config dir (`claudeStateFilePath` owns that rule, and getting
|
|
38
|
+
* it wrong is how one account reports another's email).
|
|
39
|
+
*/
|
|
40
|
+
async function readClaudeIdentity(configDir) {
|
|
41
|
+
const state = await readJsonCapped(claudeStateFilePath(configDir));
|
|
42
|
+
if (state === null)
|
|
43
|
+
return { available: false, reason: NOT_SIGNED_IN, fields: [] };
|
|
44
|
+
const account = state.oauthAccount;
|
|
45
|
+
if (!account || typeof account !== 'object') {
|
|
46
|
+
return { available: false, reason: NOT_SIGNED_IN, fields: [] };
|
|
47
|
+
}
|
|
48
|
+
const a = account;
|
|
49
|
+
const fields = [];
|
|
50
|
+
push(fields, 'Email', a.emailAddress);
|
|
51
|
+
push(fields, 'Name', a.displayName);
|
|
52
|
+
push(fields, 'Organization', a.organizationName);
|
|
53
|
+
push(fields, 'Role', a.organizationRole);
|
|
54
|
+
// `seatTier`/`billingType` are the closest thing to a plan the file states; neither is
|
|
55
|
+
// documented, so they are shown under a label that promises no more than they are.
|
|
56
|
+
push(fields, 'Seat', a.seatTier);
|
|
57
|
+
push(fields, 'Billing', a.billingType);
|
|
58
|
+
return fields.length > 0
|
|
59
|
+
? { available: true, fields }
|
|
60
|
+
: { available: false, reason: UNREADABLE, fields: [] };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Codex keeps its login in `auth.json`'s `id_token` — a JWT whose payload carries the identity
|
|
64
|
+
* claims. Read for its claims only: the same file holds `OPENAI_API_KEY`, `access_token` and
|
|
65
|
+
* `refresh_token`, none of which this function so much as names.
|
|
66
|
+
*
|
|
67
|
+
* The signature is NOT verified, and that is correct here: this is a local file the user's own CLI
|
|
68
|
+
* wrote, read to display who they are — not a token cezar is accepting as proof of anything.
|
|
69
|
+
*/
|
|
70
|
+
async function readCodexIdentity(configDir) {
|
|
71
|
+
const auth = await readJsonCapped(join(configDir, 'auth.json'));
|
|
72
|
+
if (auth === null)
|
|
73
|
+
return { available: false, reason: NOT_SIGNED_IN, fields: [] };
|
|
74
|
+
const tokens = auth.tokens;
|
|
75
|
+
const idToken = tokens && typeof tokens === 'object'
|
|
76
|
+
? tokens.id_token
|
|
77
|
+
: undefined;
|
|
78
|
+
const fields = [];
|
|
79
|
+
const claims = typeof idToken === 'string' ? decodeJwtClaims(idToken) : null;
|
|
80
|
+
if (claims !== null) {
|
|
81
|
+
push(fields, 'Email', claims.email);
|
|
82
|
+
push(fields, 'Name', claims.name);
|
|
83
|
+
const openai = claims['https://api.openai.com/auth'];
|
|
84
|
+
if (openai && typeof openai === 'object') {
|
|
85
|
+
push(fields, 'Plan', openai.chatgpt_plan_type);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// An API-key login has no id_token at all — say which kind of login this is rather than
|
|
89
|
+
// reporting "not signed in" for an account that is perfectly usable.
|
|
90
|
+
if (fields.length === 0 && typeof auth.OPENAI_API_KEY === 'string' && auth.OPENAI_API_KEY !== '') {
|
|
91
|
+
return { available: true, fields: [{ label: 'Login', value: 'API key' }] };
|
|
92
|
+
}
|
|
93
|
+
return fields.length > 0
|
|
94
|
+
? { available: true, fields }
|
|
95
|
+
: { available: false, reason: NOT_SIGNED_IN, fields: [] };
|
|
96
|
+
}
|
|
97
|
+
/** A JWT's payload claims, or null when it is not a readable three-part token. */
|
|
98
|
+
function decodeJwtClaims(token) {
|
|
99
|
+
const parts = token.split('.');
|
|
100
|
+
if (parts.length !== 3 || !parts[1])
|
|
101
|
+
return null;
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
104
|
+
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* What this account is logged in as, or an honest reason there is nothing to show.
|
|
112
|
+
*
|
|
113
|
+
* Never throws. OpenCode answers "unsupported": its credentials live in a SQLite DB outside the
|
|
114
|
+
* config dir (see `core/agent-profiles.ts`), so there is nothing in a config folder to read — and
|
|
115
|
+
* guessing from the default login would attribute one account's identity to another.
|
|
116
|
+
*/
|
|
117
|
+
export async function readAccountIdentity(provider, configDir) {
|
|
118
|
+
if (provider === 'claude')
|
|
119
|
+
return readClaudeIdentity(configDir);
|
|
120
|
+
if (provider === 'codex')
|
|
121
|
+
return readCodexIdentity(configDir);
|
|
122
|
+
return {
|
|
123
|
+
available: false,
|
|
124
|
+
reason: 'OpenCode keeps its login outside its config folder, so cezar cannot read it.',
|
|
125
|
+
fields: [],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=account-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-identity.js","sourceRoot":"","sources":["../../src/agent-config/account-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,6 +47,14 @@ export interface ConfigFileDef {
|
|
|
47
47
|
seeded?: boolean;
|
|
48
48
|
/** True when this file holds MCP server definitions (drives the MCP section's filter). */
|
|
49
49
|
holdsMcp?: boolean;
|
|
50
|
+
/** Top-level native setting that supplies the agent's new-session model, when present. */
|
|
51
|
+
modelKey?: string;
|
|
52
|
+
/** Native model keys checked in precedence order, including nested `env.*` settings. */
|
|
53
|
+
modelKeys?: readonly string[];
|
|
54
|
+
/** Native provider key paired with the model, when the vendor separates the two. */
|
|
55
|
+
modelProviderKey?: string;
|
|
56
|
+
/** Higher values win when resolving a native default model across config scopes. */
|
|
57
|
+
modelPriority?: number;
|
|
50
58
|
/** VERBATIM from the vendor docs. Never computed, never generic. */
|
|
51
59
|
precedence: string;
|
|
52
60
|
/** Documented mid-run reload behaviour, or undefined when the vendor is silent. */
|
|
@@ -21,6 +21,9 @@ export const CONFIG_FILES = [
|
|
|
21
21
|
label: '~/.claude/settings.json',
|
|
22
22
|
format: 'json',
|
|
23
23
|
tracked: 'outside-repo',
|
|
24
|
+
modelKey: 'model',
|
|
25
|
+
modelKeys: ['env.ANTHROPIC_MODEL', 'model'],
|
|
26
|
+
modelPriority: 1,
|
|
24
27
|
precedence: 'Lowest priority. Project and local settings override it key by key — except permission rules, which merge across all scopes.',
|
|
25
28
|
hotReload: 'Edits to most keys — including permissions and hooks — apply to a running session without a restart.',
|
|
26
29
|
docsUrl: CLAUDE_SETTINGS_DOCS,
|
|
@@ -34,6 +37,9 @@ export const CONFIG_FILES = [
|
|
|
34
37
|
label: '.claude/settings.json',
|
|
35
38
|
format: 'json',
|
|
36
39
|
tracked: 'tracked',
|
|
40
|
+
modelKey: 'model',
|
|
41
|
+
modelKeys: ['env.ANTHROPIC_MODEL', 'model'],
|
|
42
|
+
modelPriority: 2,
|
|
37
43
|
precedence: 'Overrides user settings key by key (permission rules merge). Local settings override this.',
|
|
38
44
|
hotReload: 'Edits to most keys — including permissions and hooks — apply to a running session without a restart.',
|
|
39
45
|
docsUrl: CLAUDE_SETTINGS_DOCS,
|
|
@@ -48,6 +54,9 @@ export const CONFIG_FILES = [
|
|
|
48
54
|
format: 'json',
|
|
49
55
|
tracked: 'gitignored',
|
|
50
56
|
seeded: true,
|
|
57
|
+
modelKey: 'model',
|
|
58
|
+
modelKeys: ['env.ANTHROPIC_MODEL', 'model'],
|
|
59
|
+
modelPriority: 3,
|
|
51
60
|
precedence: 'Highest of the file scopes — overrides project and user (permission rules merge). Git-ignored; copied into each run’s worktree so it takes effect immediately.',
|
|
52
61
|
hotReload: 'Edits to most keys — including permissions and hooks — apply to a running session without a restart.',
|
|
53
62
|
docsUrl: CLAUDE_SETTINGS_DOCS,
|
|
@@ -113,6 +122,9 @@ export const CONFIG_FILES = [
|
|
|
113
122
|
format: 'toml',
|
|
114
123
|
tracked: 'outside-repo',
|
|
115
124
|
holdsMcp: true,
|
|
125
|
+
modelKey: 'model',
|
|
126
|
+
modelProviderKey: 'model_provider',
|
|
127
|
+
modelPriority: 1,
|
|
116
128
|
precedence: 'User-level defaults. A trusted project’s .codex/config.toml overrides these; some keys (provider, auth, telemetry) cannot be overridden at project scope. MCP servers live here under [mcp_servers.<id>].',
|
|
117
129
|
docsUrl: CODEX_CONFIG_DOCS,
|
|
118
130
|
},
|
|
@@ -125,6 +137,8 @@ export const CONFIG_FILES = [
|
|
|
125
137
|
label: '.codex/config.toml',
|
|
126
138
|
format: 'toml',
|
|
127
139
|
tracked: 'tracked',
|
|
140
|
+
modelKey: 'model',
|
|
141
|
+
modelPriority: 2,
|
|
128
142
|
holdsMcp: true,
|
|
129
143
|
precedence: 'Applies only in projects you have trusted. Some keys (provider, auth, telemetry) cannot be overridden here. MCP servers go under [mcp_servers.<id>]. Runs read the committed copy.',
|
|
130
144
|
docsUrl: CODEX_CONFIG_DOCS,
|
|
@@ -152,6 +166,8 @@ export const CONFIG_FILES = [
|
|
|
152
166
|
format: 'jsonc',
|
|
153
167
|
tracked: 'outside-repo',
|
|
154
168
|
holdsMcp: true,
|
|
169
|
+
modelKey: 'model',
|
|
170
|
+
modelPriority: 1,
|
|
155
171
|
precedence: 'Global config. Merged with the project config, not replaced — later configs override earlier ones only for conflicting keys. MCP servers live under the "mcp" key.',
|
|
156
172
|
docsUrl: OPENCODE_CONFIG_DOCS,
|
|
157
173
|
},
|
|
@@ -165,6 +181,8 @@ export const CONFIG_FILES = [
|
|
|
165
181
|
format: 'jsonc',
|
|
166
182
|
tracked: 'tracked',
|
|
167
183
|
holdsMcp: true,
|
|
184
|
+
modelKey: 'model',
|
|
185
|
+
modelPriority: 2,
|
|
168
186
|
precedence: 'Merged over the global config per conflicting key (not a wholesale replace). MCP servers live under the "mcp" key. Runs read the committed copy.',
|
|
169
187
|
docsUrl: OPENCODE_CONFIG_DOCS,
|
|
170
188
|
},
|