commandmate 0.32.0 → 0.32.1
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +187 -187
- package/.next/app-path-routes-manifest.json +36 -36
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +2 -2
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +36 -36
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/5043.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/6491.js +2 -2
- package/.next/server/chunks/6838.js +1 -1
- package/.next/server/chunks/7875.js +2 -2
- package/.next/server/chunks/8918.js +3 -3
- package/.next/server/chunks/9130.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{4718-0053d42fb635e700.js → 4718-9d63107c612a065d.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-06e1ff5ce2d126fd.js → page-3d82a9355d5612d9.js} +1 -1
- package/.next/trace +13 -13
- package/README.md +6 -2
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +44 -15
- package/dist/cli/commands/peers.d.ts.map +1 -1
- package/dist/cli/commands/peers.js +12 -0
- package/dist/cli/commands/whoami.d.ts +67 -0
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +106 -0
- package/dist/cli/utils/api-client.d.ts +30 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +28 -5
- package/dist/server/src/lib/detection/cli-patterns.js +206 -2
- package/dist/server/src/lib/hooks/agent-event-service.js +27 -5
- package/dist/server/src/lib/hooks/sources/codex/history.js +63 -3
- package/dist/server/src/lib/hooks/sources/codex/transcript.js +53 -1
- package/dist/server/src/lib/hooks/sources/launch-command.js +112 -15
- package/dist/server/src/lib/hooks/stop-history-capture.js +252 -50
- package/dist/server/src/lib/polling/response-checker.js +129 -5
- package/dist/server/src/lib/polling/response-dedup.js +136 -0
- package/dist/server/src/lib/relay/relay-delivery.js +160 -6
- package/package.json +1 -1
- /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → Lno4w_zWXdGdlfXg06Vp8}/_buildManifest.js +0 -0
- /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → Lno4w_zWXdGdlfXg06Vp8}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
**[commandmate website →](https://kewton.github.io/CommandMate/)**
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<img src="./docs/images/demo-
|
|
15
|
+
<img src="./docs/images/demo-hero.en.gif" width="600" alt="Switch repos from the tab bar, delegate to another agent from the chat, open the file it names, and do the same from a phone" />
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
> **From vibe coding to Vibe Engineering.**
|
|
@@ -30,8 +30,12 @@ npx commandmate@latest
|
|
|
30
30
|
CommandMate adds the machinery — a contract before the work, verification gates after it, Skills that carry the method — on top of the agent CLIs you already use.
|
|
31
31
|
It does not replace tmux, Git worktrees, your terminal, or your agent CLI. It puts a frame around them, so the work arrives verified instead of merely finished.
|
|
32
32
|
|
|
33
|
+
## Verified, not vibe-checked
|
|
34
|
+
|
|
35
|
+
Gates you declared decide whether the work is done, and the exit code is the verdict.
|
|
36
|
+
|
|
33
37
|
<p align="center">
|
|
34
|
-
<img src="./docs/images/demo-
|
|
38
|
+
<img src="./docs/images/demo-contract-verify.gif" width="600" alt="A task contract is sent, the verification gates run, and the exit code returns RESULT passed" />
|
|
35
39
|
</p>
|
|
36
40
|
|
|
37
41
|
Works on desktop and mobile — monitor and steer sessions from any browser, including your phone.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,SAAS,EAAkD,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"instances.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/instances.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EAAE,SAAS,EAAkD,MAAM,qBAAqB,CAAC;AAkIhG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG7B;AAiBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKzD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAE6D,CAAC;AAErG,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAE0B,CAAC;AAE/D,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;;;;;;;;;OAYG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,IAAI,GAAE,oBAA+B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAmCjC;AAyPD,wBAAgB,sBAAsB,IAAI,OAAO,CAgFhD"}
|
|
@@ -22,6 +22,10 @@ const command_helpers_1 = require("../utils/command-helpers");
|
|
|
22
22
|
const cli_tool_ids_1 = require("../config/cli-tool-ids");
|
|
23
23
|
const agent_instances_1 = require("../utils/agent-instances");
|
|
24
24
|
const session_target_1 = require("../utils/session-target");
|
|
25
|
+
// Issue #2404: "the worktree is not on this server" is a different failure from
|
|
26
|
+
// "the worktree id is wrong", and only the session's own identity can tell them
|
|
27
|
+
// apart.
|
|
28
|
+
const whoami_1 = require("./whoami");
|
|
25
29
|
// Issue #2317: the tmux session name each instance runs in, so `commandmate
|
|
26
30
|
// attach` / `tmux attach` need no hand-assembly of `mcbd-<tool>-<wt>[-<suffix>]`.
|
|
27
31
|
const session_name_1 = require("../../lib/cli-tools/session-name");
|
|
@@ -243,8 +247,7 @@ async function fetchOpencodeSessions(client, worktreeId, instances) {
|
|
|
243
247
|
* Probes GET .../current-output?cliTool=&instance= per instance (same
|
|
244
248
|
* endpoint capture.ts uses) since the roster itself carries no session state.
|
|
245
249
|
*/
|
|
246
|
-
async function listInstances(worktreeId, options) {
|
|
247
|
-
const client = new api_client_1.ApiClient({ token: options.token });
|
|
250
|
+
async function listInstances(client, worktreeId, options) {
|
|
248
251
|
const instances = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
|
|
249
252
|
// Issue #2038: one extra request for the whole worktree, and only when there
|
|
250
253
|
// is an opencode instance to ask about — the endpoint is opencode-only and a
|
|
@@ -286,7 +289,7 @@ async function listInstances(worktreeId, options) {
|
|
|
286
289
|
/**
|
|
287
290
|
* Add action: append a new instance to the roster (PATCH full replacement).
|
|
288
291
|
*/
|
|
289
|
-
async function addInstance(worktreeId, options) {
|
|
292
|
+
async function addInstance(client, worktreeId, options) {
|
|
290
293
|
if (!options.agent) {
|
|
291
294
|
console.error('Error: add requires --agent <tool>.');
|
|
292
295
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
@@ -303,7 +306,6 @@ async function addInstance(worktreeId, options) {
|
|
|
303
306
|
console.error(`Error: --alias exceeds ${agent_instances_1.MAX_AGENT_ALIAS_LENGTH} characters.`);
|
|
304
307
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
305
308
|
}
|
|
306
|
-
const client = new api_client_1.ApiClient({ token: options.token });
|
|
307
309
|
const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
|
|
308
310
|
if (existing.length >= agent_instances_1.MAX_AGENT_INSTANCES) {
|
|
309
311
|
console.error(`Error: worktree already has the maximum of ${agent_instances_1.MAX_AGENT_INSTANCES} agent instances.`);
|
|
@@ -332,12 +334,11 @@ async function addInstance(worktreeId, options) {
|
|
|
332
334
|
* Remove action: drop an instance from the roster. Kills the session first
|
|
333
335
|
* (when --kill) so the server can still resolve its CLI tool from the roster.
|
|
334
336
|
*/
|
|
335
|
-
async function removeInstance(worktreeId, instanceId, options) {
|
|
337
|
+
async function removeInstance(client, worktreeId, instanceId, options) {
|
|
336
338
|
if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
|
|
337
339
|
console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
|
|
338
340
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
339
341
|
}
|
|
340
|
-
const client = new api_client_1.ApiClient({ token: options.token });
|
|
341
342
|
const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
|
|
342
343
|
if (!existing.some(inst => inst.id === instanceId)) {
|
|
343
344
|
console.error(`Error: instance '${instanceId}' not found in roster.`);
|
|
@@ -363,7 +364,7 @@ async function removeInstance(worktreeId, instanceId, options) {
|
|
|
363
364
|
/**
|
|
364
365
|
* Alias action: rename an existing instance's display label.
|
|
365
366
|
*/
|
|
366
|
-
async function renameInstance(worktreeId, instanceId, alias
|
|
367
|
+
async function renameInstance(client, worktreeId, instanceId, alias) {
|
|
367
368
|
if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
|
|
368
369
|
console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
|
|
369
370
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
@@ -372,7 +373,6 @@ async function renameInstance(worktreeId, instanceId, alias, options) {
|
|
|
372
373
|
console.error(`Error: alias must be 1-${agent_instances_1.MAX_AGENT_ALIAS_LENGTH} characters.`);
|
|
373
374
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
374
375
|
}
|
|
375
|
-
const client = new api_client_1.ApiClient({ token: options.token });
|
|
376
376
|
const existing = await (0, agent_instances_1.fetchAgentInstances)(client, worktreeId);
|
|
377
377
|
const index = existing.findIndex(inst => inst.id === instanceId);
|
|
378
378
|
if (index === -1) {
|
|
@@ -388,15 +388,33 @@ async function renameInstance(worktreeId, instanceId, alias, options) {
|
|
|
388
388
|
* The server resolves the backing CLI tool from the roster via the `instance`
|
|
389
389
|
* query param (kill-session/route.ts), so no extra lookup is needed here.
|
|
390
390
|
*/
|
|
391
|
-
async function killInstance(worktreeId, instanceId
|
|
391
|
+
async function killInstance(client, worktreeId, instanceId) {
|
|
392
392
|
if (!(0, api_client_1.isValidInstanceId)(instanceId)) {
|
|
393
393
|
console.error('Error: Invalid instance id. Must be an alphanumeric/underscore/hyphen identifier (max 64 chars).');
|
|
394
394
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
395
395
|
}
|
|
396
|
-
const client = new api_client_1.ApiClient({ token: options.token });
|
|
397
396
|
await client.post(`/api/worktrees/${worktreeId}/kill-session?instance=${encodeURIComponent(instanceId)}`);
|
|
398
397
|
console.error(`Session killed: ${instanceId}`);
|
|
399
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Print the "you are on another server" hint when that is what a 404 means
|
|
401
|
+
* (Issue #2404).
|
|
402
|
+
*
|
|
403
|
+
* Runs only on a failure path, so the extra `tmux display-message` +
|
|
404
|
+
* `GET /api/worktrees` it costs buys the difference between a correct diagnosis
|
|
405
|
+
* and a wrong one. Never throws: {@link describeServerMismatch} swallows its own
|
|
406
|
+
* failures, and a diagnosis must not replace the error being diagnosed.
|
|
407
|
+
*
|
|
408
|
+
* @param client - The client whose request failed
|
|
409
|
+
* @param error - The caught error, diagnosed only when it is a 404
|
|
410
|
+
*/
|
|
411
|
+
async function warnIfDifferentServer(client, error) {
|
|
412
|
+
if (!(error instanceof api_client_1.ApiError) || error.statusCode !== 404)
|
|
413
|
+
return;
|
|
414
|
+
const mismatch = await (0, whoami_1.describeServerMismatch)(client);
|
|
415
|
+
if (mismatch)
|
|
416
|
+
console.error((0, whoami_1.formatServerMismatchHint)(mismatch));
|
|
417
|
+
}
|
|
400
418
|
function createInstancesCommand() {
|
|
401
419
|
const cmd = new commander_1.Command('instances');
|
|
402
420
|
cmd
|
|
@@ -411,6 +429,10 @@ function createInstancesCommand() {
|
|
|
411
429
|
.option('--kill', 'Also kill the running session when removing an instance')
|
|
412
430
|
.option('--token <token>', command_helpers_1.TOKEN_WARNING)
|
|
413
431
|
.action(async (worktreeId, action, rest, options) => {
|
|
432
|
+
// One client for the whole invocation (Issue #2404): every action built
|
|
433
|
+
// its own identical one, and the failure path below has to report the URL
|
|
434
|
+
// the request actually went to — a second construction is a second answer.
|
|
435
|
+
const client = new api_client_1.ApiClient({ token: options.token });
|
|
414
436
|
try {
|
|
415
437
|
if (!(0, api_client_1.isValidWorktreeId)(worktreeId)) {
|
|
416
438
|
console.error('Error: Invalid worktree ID format.');
|
|
@@ -418,10 +440,10 @@ function createInstancesCommand() {
|
|
|
418
440
|
}
|
|
419
441
|
switch (action ?? 'list') {
|
|
420
442
|
case 'list':
|
|
421
|
-
await listInstances(worktreeId, options);
|
|
443
|
+
await listInstances(client, worktreeId, options);
|
|
422
444
|
break;
|
|
423
445
|
case 'add':
|
|
424
|
-
await addInstance(worktreeId, options);
|
|
446
|
+
await addInstance(client, worktreeId, options);
|
|
425
447
|
break;
|
|
426
448
|
case 'remove': {
|
|
427
449
|
const [instanceId] = rest;
|
|
@@ -429,7 +451,7 @@ function createInstancesCommand() {
|
|
|
429
451
|
console.error('Error: remove requires an <instance-id> argument.');
|
|
430
452
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
431
453
|
}
|
|
432
|
-
await removeInstance(worktreeId, instanceId, options);
|
|
454
|
+
await removeInstance(client, worktreeId, instanceId, options);
|
|
433
455
|
break;
|
|
434
456
|
}
|
|
435
457
|
case 'alias': {
|
|
@@ -439,7 +461,7 @@ function createInstancesCommand() {
|
|
|
439
461
|
console.error('Error: alias requires <instance-id> and <new-alias> arguments.');
|
|
440
462
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
441
463
|
}
|
|
442
|
-
await renameInstance(worktreeId, instanceId, alias
|
|
464
|
+
await renameInstance(client, worktreeId, instanceId, alias);
|
|
443
465
|
break;
|
|
444
466
|
}
|
|
445
467
|
case 'kill': {
|
|
@@ -448,7 +470,7 @@ function createInstancesCommand() {
|
|
|
448
470
|
console.error('Error: kill requires an <instance-id> argument.');
|
|
449
471
|
process.exit(types_1.ExitCode.CONFIG_ERROR);
|
|
450
472
|
}
|
|
451
|
-
await killInstance(worktreeId, instanceId
|
|
473
|
+
await killInstance(client, worktreeId, instanceId);
|
|
452
474
|
break;
|
|
453
475
|
}
|
|
454
476
|
default:
|
|
@@ -457,6 +479,13 @@ function createInstancesCommand() {
|
|
|
457
479
|
}
|
|
458
480
|
}
|
|
459
481
|
catch (error) {
|
|
482
|
+
// Issue #2404: a 404 here says "no such worktree ON THIS SERVER", and
|
|
483
|
+
// `Resource not found. Check the worktree ID.` reads as "you typed it
|
|
484
|
+
// wrong". When the caller's own session belongs to a worktree this
|
|
485
|
+
// server does not list, the id was never the problem — say so before
|
|
486
|
+
// handleCommandError() exits, so nobody re-checks a correct id and then
|
|
487
|
+
// delegates to whatever this server does have.
|
|
488
|
+
await warnIfDifferentServer(client, error);
|
|
460
489
|
(0, command_helpers_1.handleCommandError)(error);
|
|
461
490
|
}
|
|
462
491
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/peers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAiB,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAIhG,OAAO,
|
|
1
|
+
{"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/peers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAiB,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAIhG,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB;;;;;;;;GAQG;AACH,UAAU,gBAAiB,SAAQ,YAAY;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,kCAAkC;AAClC,UAAU,OAAO;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAuED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,EAAE,CAuBX;AAoBD,wBAAgB,kBAAkB,IAAI,OAAO,CAkE5C"}
|
|
@@ -175,13 +175,25 @@ function createPeersCommand() {
|
|
|
175
175
|
// authority on the one string the caller pastes into a shell.
|
|
176
176
|
const binary = (0, command_reference_1.resolveCommandMateBinary)();
|
|
177
177
|
const rows = buildPeerRows(worktrees, identity, binary);
|
|
178
|
+
// Issue #2404: the listing above is empty precisely when the caller's own
|
|
179
|
+
// worktree is absent from `worktrees` — which is what "you are on another
|
|
180
|
+
// server" looks like from here, and which `No peer sessions found.` reads
|
|
181
|
+
// as "you have no siblings". The list is already in hand, so the check is
|
|
182
|
+
// free and applies whichever source named the worktree.
|
|
183
|
+
const mismatch = (0, whoami_1.detectServerMismatch)(identity, new Set(worktrees.map((wt) => wt.id)), client.serverUrl);
|
|
184
|
+
if (mismatch)
|
|
185
|
+
console.error((0, whoami_1.formatServerMismatchWarning)(mismatch));
|
|
178
186
|
if (options.json) {
|
|
179
187
|
console.log(JSON.stringify({
|
|
188
|
+
// `self` is unchanged: it answers "who is asking", and the server is
|
|
189
|
+
// not part of that. The connection target is its own top-level field.
|
|
180
190
|
self: {
|
|
181
191
|
worktreeId: identity.worktreeId,
|
|
182
192
|
instanceId: identity.instanceId,
|
|
183
193
|
cliToolId: identity.cliToolId,
|
|
184
194
|
},
|
|
195
|
+
serverUrl: client.serverUrl,
|
|
196
|
+
...(mismatch ? { serverMismatch: true } : {}),
|
|
185
197
|
peers: rows,
|
|
186
198
|
}, null, 2));
|
|
187
199
|
return;
|
|
@@ -57,6 +57,29 @@ export interface SessionIdentity {
|
|
|
57
57
|
source: IdentitySource;
|
|
58
58
|
/** The tmux session, when the identity came from (or could be checked against) one. */
|
|
59
59
|
sessionName: string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Every worktree id the server answered with while this identity was resolved,
|
|
62
|
+
* or null/undefined when no list was read (Issue #2404).
|
|
63
|
+
*
|
|
64
|
+
* Carried rather than discarded because it is the whole evidence for
|
|
65
|
+
* {@link detectServerMismatch}: the tmux path already fetches it to break the
|
|
66
|
+
* `-<n>` ambiguity, so "is my own worktree on the server I am dialling?" costs
|
|
67
|
+
* no second request. Absent on the env path, which never asks — and that is
|
|
68
|
+
* the reason a `source: 'env'` identity never reports a mismatch here.
|
|
69
|
+
*/
|
|
70
|
+
knownWorktreeIds?: Set<string> | null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The caller is on a session one server started and is talking to another
|
|
74
|
+
* (Issue #2404).
|
|
75
|
+
*/
|
|
76
|
+
export interface ServerMismatch {
|
|
77
|
+
/** The worktree this session belongs to, as {@link resolveSessionIdentity} read it. */
|
|
78
|
+
worktreeId: string;
|
|
79
|
+
/** The server this CLI is dialling — the one that does NOT list that worktree. */
|
|
80
|
+
serverUrl: string;
|
|
81
|
+
/** Where the worktree id came from, so the warning can say how it is known. */
|
|
82
|
+
source: IdentitySource;
|
|
60
83
|
}
|
|
61
84
|
/**
|
|
62
85
|
* The tmux session this process is running inside, or null.
|
|
@@ -95,6 +118,50 @@ export declare function parseSessionName(sessionName: string): ParsedSessionName
|
|
|
95
118
|
* @returns The identity, or null when nothing establishes one
|
|
96
119
|
*/
|
|
97
120
|
export declare function resolveSessionIdentity(client: ApiClient, env?: Readonly<Record<string, string | undefined>>): Promise<SessionIdentity | null>;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the caller's own worktree is missing from the server it is dialling.
|
|
123
|
+
*
|
|
124
|
+
* The contradiction this names was, until now, unnameable: `whoami` answers
|
|
125
|
+
* from the tmux session name and therefore succeeds, while `ls` / `instances` /
|
|
126
|
+
* `peers` answer from a *different* server's ledger and therefore say the
|
|
127
|
+
* worktree does not exist. Both are correct and they disagree, and an agent
|
|
128
|
+
* reading them concluded the reasonable, wrong thing — "I must not be in the
|
|
129
|
+
* ledger" — and delegated its work into an unrelated worktree on the other
|
|
130
|
+
* server (#2403). Exit codes said nothing: `ask` returned 0.
|
|
131
|
+
*
|
|
132
|
+
* Deliberately NOT gated on {@link IdentitySource}. The gate is having a list at
|
|
133
|
+
* all: the env path never fetches one, so it passes null and cannot flag, while
|
|
134
|
+
* `peers` — which has just fetched the whole list for its own listing — can
|
|
135
|
+
* check an env-derived identity for free. A missing list is never a mismatch:
|
|
136
|
+
* a stopped server must not be reported as the wrong server.
|
|
137
|
+
*
|
|
138
|
+
* @param identity - Who the caller is
|
|
139
|
+
* @param knownWorktreeIds - Worktree ids from the server, or null when unread
|
|
140
|
+
* @param serverUrl - The URL those ids came from ({@link ApiClient.serverUrl})
|
|
141
|
+
* @returns The mismatch, or null when there is nothing to report
|
|
142
|
+
*/
|
|
143
|
+
export declare function detectServerMismatch(identity: SessionIdentity, knownWorktreeIds: Set<string> | null | undefined, serverUrl: string): ServerMismatch | null;
|
|
144
|
+
/**
|
|
145
|
+
* The multi-line warning printed when {@link detectServerMismatch} fires.
|
|
146
|
+
*
|
|
147
|
+
* Says what is wrong, why it is not a typo, and what it costs to ignore —
|
|
148
|
+
* because the failure it describes is silent by construction: every command
|
|
149
|
+
* involved keeps exiting 0 while answering about the wrong machine's worktrees.
|
|
150
|
+
*/
|
|
151
|
+
export declare function formatServerMismatchWarning(mismatch: ServerMismatch): string;
|
|
152
|
+
/** The one-line form, for a command that has already failed for another reason. */
|
|
153
|
+
export declare function formatServerMismatchHint(mismatch: ServerMismatch): string;
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the caller's identity and report a mismatch, or null.
|
|
156
|
+
*
|
|
157
|
+
* For commands that only need this on a failure path: it costs a `tmux
|
|
158
|
+
* display-message` and one `GET /api/worktrees`, and it is called after the
|
|
159
|
+
* request that already failed. Never throws — a diagnosis that fails must not
|
|
160
|
+
* replace the error it was diagnosing.
|
|
161
|
+
*
|
|
162
|
+
* @param client - The client whose 404 is being explained
|
|
163
|
+
*/
|
|
164
|
+
export declare function describeServerMismatch(client: ApiClient): Promise<ServerMismatch | null>;
|
|
98
165
|
/**
|
|
99
166
|
* The roster row for this identity, or null.
|
|
100
167
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAsC,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKhD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAUhC,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,cAAc,CAAC;AAEpD,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/whoami.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAsC,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAKhD;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAUhC,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,cAAc,CAAC;AAEpD,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;IACvB,uFAAuF;IACvF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,MAAM,EAAE,cAAc,CAAC;CACxB;AAcD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,GAAG,IAAI,CASf;AAED,gFAAgF;AAChF,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAa9E;AAkDD;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,SAAS,EACjB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAqCjC;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EACzB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,EAChD,SAAS,EAAE,MAAM,GAChB,cAAc,GAAG,IAAI,CAIvB;AASD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAS5E;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAMzE;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAY9F;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAO/B;AAED,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,QAMmC,CAAC;AAEvE,wBAAgB,mBAAmB,IAAI,OAAO,CA6D7C"}
|
|
@@ -38,6 +38,10 @@ exports.NOT_IN_SESSION_MESSAGE = exports.NOT_IN_SESSION = void 0;
|
|
|
38
38
|
exports.readTmuxSessionName = readTmuxSessionName;
|
|
39
39
|
exports.parseSessionName = parseSessionName;
|
|
40
40
|
exports.resolveSessionIdentity = resolveSessionIdentity;
|
|
41
|
+
exports.detectServerMismatch = detectServerMismatch;
|
|
42
|
+
exports.formatServerMismatchWarning = formatServerMismatchWarning;
|
|
43
|
+
exports.formatServerMismatchHint = formatServerMismatchHint;
|
|
44
|
+
exports.describeServerMismatch = describeServerMismatch;
|
|
41
45
|
exports.readOwnRosterEntry = readOwnRosterEntry;
|
|
42
46
|
exports.createWhoamiCommand = createWhoamiCommand;
|
|
43
47
|
const child_process_1 = require("child_process");
|
|
@@ -205,8 +209,95 @@ async function resolveSessionIdentity(client, env = process.env) {
|
|
|
205
209
|
cliToolId: parsed.cliToolId,
|
|
206
210
|
source: 'tmux-session',
|
|
207
211
|
sessionName,
|
|
212
|
+
knownWorktreeIds: known,
|
|
208
213
|
};
|
|
209
214
|
}
|
|
215
|
+
// ===========================================================================
|
|
216
|
+
// "Am I even talking to the server that started me?" (Issue #2404)
|
|
217
|
+
// ===========================================================================
|
|
218
|
+
/**
|
|
219
|
+
* Whether the caller's own worktree is missing from the server it is dialling.
|
|
220
|
+
*
|
|
221
|
+
* The contradiction this names was, until now, unnameable: `whoami` answers
|
|
222
|
+
* from the tmux session name and therefore succeeds, while `ls` / `instances` /
|
|
223
|
+
* `peers` answer from a *different* server's ledger and therefore say the
|
|
224
|
+
* worktree does not exist. Both are correct and they disagree, and an agent
|
|
225
|
+
* reading them concluded the reasonable, wrong thing — "I must not be in the
|
|
226
|
+
* ledger" — and delegated its work into an unrelated worktree on the other
|
|
227
|
+
* server (#2403). Exit codes said nothing: `ask` returned 0.
|
|
228
|
+
*
|
|
229
|
+
* Deliberately NOT gated on {@link IdentitySource}. The gate is having a list at
|
|
230
|
+
* all: the env path never fetches one, so it passes null and cannot flag, while
|
|
231
|
+
* `peers` — which has just fetched the whole list for its own listing — can
|
|
232
|
+
* check an env-derived identity for free. A missing list is never a mismatch:
|
|
233
|
+
* a stopped server must not be reported as the wrong server.
|
|
234
|
+
*
|
|
235
|
+
* @param identity - Who the caller is
|
|
236
|
+
* @param knownWorktreeIds - Worktree ids from the server, or null when unread
|
|
237
|
+
* @param serverUrl - The URL those ids came from ({@link ApiClient.serverUrl})
|
|
238
|
+
* @returns The mismatch, or null when there is nothing to report
|
|
239
|
+
*/
|
|
240
|
+
function detectServerMismatch(identity, knownWorktreeIds, serverUrl) {
|
|
241
|
+
if (!knownWorktreeIds)
|
|
242
|
+
return null;
|
|
243
|
+
if (knownWorktreeIds.has(identity.worktreeId))
|
|
244
|
+
return null;
|
|
245
|
+
return { worktreeId: identity.worktreeId, serverUrl, source: identity.source };
|
|
246
|
+
}
|
|
247
|
+
/** How the worktree id was established, as a phrase for the warning. */
|
|
248
|
+
function describeSource(source) {
|
|
249
|
+
return source === 'tmux-session'
|
|
250
|
+
? 'read from the tmux session name'
|
|
251
|
+
: 'read from the CM_* variables on this session\'s launch line';
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The multi-line warning printed when {@link detectServerMismatch} fires.
|
|
255
|
+
*
|
|
256
|
+
* Says what is wrong, why it is not a typo, and what it costs to ignore —
|
|
257
|
+
* because the failure it describes is silent by construction: every command
|
|
258
|
+
* involved keeps exiting 0 while answering about the wrong machine's worktrees.
|
|
259
|
+
*/
|
|
260
|
+
function formatServerMismatchWarning(mismatch) {
|
|
261
|
+
return [
|
|
262
|
+
`Warning: this session is worktree '${mismatch.worktreeId}' (${describeSource(mismatch.source)}), `
|
|
263
|
+
+ `but the server this CLI is talking to (${mismatch.serverUrl}) does not list it.`,
|
|
264
|
+
' You are almost certainly connected to a DIFFERENT CommandMate server than the one that started this session.',
|
|
265
|
+
' Everything that reads the server ledger — ls / instances / peers / ask — is answering about that other server,',
|
|
266
|
+
' so anything delegated from here can land in an unrelated worktree while still exiting 0.',
|
|
267
|
+
' Aim the CLI at the right server (CM_PORT=<port> commandmate …) or check ~/.commandmate/.env, then re-run.',
|
|
268
|
+
].join('\n');
|
|
269
|
+
}
|
|
270
|
+
/** The one-line form, for a command that has already failed for another reason. */
|
|
271
|
+
function formatServerMismatchHint(mismatch) {
|
|
272
|
+
return (`Hint: this session is worktree '${mismatch.worktreeId}' (${describeSource(mismatch.source)}), `
|
|
273
|
+
+ `which the server at ${mismatch.serverUrl} does not list — this CLI is probably connected to a `
|
|
274
|
+
+ 'different CommandMate server than the one that started this session, so the id is not the problem.');
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Resolve the caller's identity and report a mismatch, or null.
|
|
278
|
+
*
|
|
279
|
+
* For commands that only need this on a failure path: it costs a `tmux
|
|
280
|
+
* display-message` and one `GET /api/worktrees`, and it is called after the
|
|
281
|
+
* request that already failed. Never throws — a diagnosis that fails must not
|
|
282
|
+
* replace the error it was diagnosing.
|
|
283
|
+
*
|
|
284
|
+
* @param client - The client whose 404 is being explained
|
|
285
|
+
*/
|
|
286
|
+
async function describeServerMismatch(client) {
|
|
287
|
+
try {
|
|
288
|
+
const identity = await resolveSessionIdentity(client);
|
|
289
|
+
if (!identity)
|
|
290
|
+
return null;
|
|
291
|
+
// The env path carries no list, so ask for one here: this runs only after a
|
|
292
|
+
// command has already failed, where one extra request buys the difference
|
|
293
|
+
// between "check the worktree ID" and the actual cause.
|
|
294
|
+
const known = identity.knownWorktreeIds ?? (await readWorktreeIds(client));
|
|
295
|
+
return detectServerMismatch(identity, known, client.serverUrl);
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
210
301
|
/**
|
|
211
302
|
* The roster row for this identity, or null.
|
|
212
303
|
*
|
|
@@ -245,6 +336,14 @@ function createWhoamiCommand() {
|
|
|
245
336
|
return;
|
|
246
337
|
}
|
|
247
338
|
const roster = await readOwnRosterEntry(client, identity);
|
|
339
|
+
// Issue #2404: no extra request — `resolveSessionIdentity` already read
|
|
340
|
+
// the list on the tmux path, which is the path that can be wrong.
|
|
341
|
+
const mismatch = detectServerMismatch(identity, identity.knownWorktreeIds, client.serverUrl);
|
|
342
|
+
// stderr in both modes: it must not corrupt `--json` stdout, and a skill
|
|
343
|
+
// that reports "I asked <url>" needs to see it whichever mode it ran in.
|
|
344
|
+
// Exit code stays 0 — whoami still answered the question it was asked.
|
|
345
|
+
if (mismatch)
|
|
346
|
+
console.error(formatServerMismatchWarning(mismatch));
|
|
248
347
|
if (options.json) {
|
|
249
348
|
console.log(JSON.stringify({
|
|
250
349
|
worktreeId: identity.worktreeId,
|
|
@@ -253,6 +352,12 @@ function createWhoamiCommand() {
|
|
|
253
352
|
alias: roster?.alias ?? null,
|
|
254
353
|
source: identity.source,
|
|
255
354
|
sessionName: identity.sessionName,
|
|
355
|
+
// Always present: "where did this answer come from" is half of what
|
|
356
|
+
// makes the answer checkable (Issue #2404).
|
|
357
|
+
serverUrl: client.serverUrl,
|
|
358
|
+
// Absent, not false, when everything agrees: a reader tests for the
|
|
359
|
+
// flag, and an ordinary session's output carries no trace of it.
|
|
360
|
+
...(mismatch ? { serverMismatch: true } : {}),
|
|
256
361
|
}, null, 2));
|
|
257
362
|
return;
|
|
258
363
|
}
|
|
@@ -262,6 +367,7 @@ function createWhoamiCommand() {
|
|
|
262
367
|
console.log(`alias: ${roster?.alias ?? '-'}`);
|
|
263
368
|
console.log(`source: ${identity.source}`);
|
|
264
369
|
console.log(`tmux: ${identity.sessionName ?? '-'}`);
|
|
370
|
+
console.log(`server: ${client.serverUrl}`);
|
|
265
371
|
}
|
|
266
372
|
catch (error) {
|
|
267
373
|
(0, command_helpers_1.handleCommandError)(error);
|
|
@@ -38,6 +38,22 @@ export interface ApiErrorResult {
|
|
|
38
38
|
message: string;
|
|
39
39
|
exitCode: number;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* What this function knows about the request, beyond its status (Issue #2404).
|
|
43
|
+
*
|
|
44
|
+
* `handleApiError` is a pure classifier over `(status, payload)` and has no way
|
|
45
|
+
* to know which server answered — which is exactly the fact a 404 needs. The
|
|
46
|
+
* caller's `Resource not found. Check the worktree ID.` sent an agent looking
|
|
47
|
+
* for a typo in an id that was correct: the CLI was dialling a *different*
|
|
48
|
+
* CommandMate server than the one that had started its session, and the id it
|
|
49
|
+
* was asking about lived on the other one. Optional so the two callers that
|
|
50
|
+
* classify without a client ({@link ApiClient} aside, `server-capabilities.ts`)
|
|
51
|
+
* keep compiling and keep their wording.
|
|
52
|
+
*/
|
|
53
|
+
export interface ApiErrorContext {
|
|
54
|
+
/** The base URL the failing request was actually sent to. */
|
|
55
|
+
serverUrl?: string;
|
|
56
|
+
}
|
|
41
57
|
/**
|
|
42
58
|
* Classify API errors into user-friendly messages and exit codes.
|
|
43
59
|
* [IA3-09] Covers: ECONNREFUSED, 400, 401/403, 404, 429, 500, timeout
|
|
@@ -47,9 +63,12 @@ export interface ApiErrorResult {
|
|
|
47
63
|
* @param payload - Parsed error body, when the response carried one (Issue #1637).
|
|
48
64
|
* Used for 5xx only: the 4xx messages below are already specific, and are
|
|
49
65
|
* pinned by tests as the CLI's own wording.
|
|
66
|
+
* @param context - Which server answered, when the caller knows (Issue #2404).
|
|
67
|
+
* Read by the 404 branch only; the other messages do not send anyone looking
|
|
68
|
+
* in the wrong place.
|
|
50
69
|
* @returns User-friendly error message and exit code
|
|
51
70
|
*/
|
|
52
|
-
export declare function handleApiError(error: unknown, status?: number, payload?: ApiErrorPayload): ApiErrorResult;
|
|
71
|
+
export declare function handleApiError(error: unknown, status?: number, payload?: ApiErrorPayload, context?: ApiErrorContext): ApiErrorResult;
|
|
53
72
|
/**
|
|
54
73
|
* API Client for CLI HTTP communication.
|
|
55
74
|
* Uses Node.js built-in fetch. Handles auth headers and error mapping.
|
|
@@ -61,6 +80,16 @@ export declare class ApiClient {
|
|
|
61
80
|
baseUrl?: string;
|
|
62
81
|
token?: string;
|
|
63
82
|
});
|
|
83
|
+
/**
|
|
84
|
+
* The base URL this client actually dials (Issue #2404).
|
|
85
|
+
*
|
|
86
|
+
* Resolved once in the constructor through the precedence in
|
|
87
|
+
* `loadClientEnv()` (`--base-url` > exported `CM_PORT` > `~/.commandmate/.env`
|
|
88
|
+
* > 3000), which is why it is read from here rather than re-derived by each
|
|
89
|
+
* caller: a second derivation is a second answer, and "which server did this
|
|
90
|
+
* command talk to" only helps if it is the one the request went to.
|
|
91
|
+
*/
|
|
92
|
+
get serverUrl(): string;
|
|
64
93
|
private getHeaders;
|
|
65
94
|
/**
|
|
66
95
|
* GET without any status handling, for callers that must read the raw response.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAK3C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAaD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CASjF;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAK3C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAaD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CASjF;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA0BD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,eAAe,GACxB,cAAc,CA6EhB;AAeD;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAC,CAAS;gBAEX,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAmB1D;;;;;;;;OAQG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,OAAO,CAAC,UAAU;IAUlB;;;;;;;;;;;;;;OAcG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQ1F;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAuBtC;;;OAGG;IACG,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IA2BvD;;;;OAIG;IACG,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAyBzD;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACxD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA0BD;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;aAGf,QAAQ,EAAE,MAAM;aAChB,UAAU,CAAC,EAAE,MAAM;IACnC,gEAAgE;aAChD,OAAO,CAAC,EAAE,eAAe;gBAJzC,OAAO,EAAE,MAAM,EACC,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,YAAA;IACnC,gEAAgE;IAChD,OAAO,CAAC,EAAE,eAAe,YAAA;IAM3C,uEAAuE;IACvE,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;CACF;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EACvC,OAAO,EAAE,MAAM,GACd,CAAC,CAiBH;AAOD;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CASvF;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CASxE"}
|
|
@@ -87,6 +87,10 @@ function serverErrorDetail(payload) {
|
|
|
87
87
|
? `${detail.slice(0, MAX_SERVER_ERROR_DETAIL)}…`
|
|
88
88
|
: detail;
|
|
89
89
|
}
|
|
90
|
+
/** Append `(server: <url>)` when the caller named one. */
|
|
91
|
+
function withServerUrl(message, context) {
|
|
92
|
+
return context?.serverUrl ? `${message} (server: ${context.serverUrl})` : message;
|
|
93
|
+
}
|
|
90
94
|
/**
|
|
91
95
|
* Classify API errors into user-friendly messages and exit codes.
|
|
92
96
|
* [IA3-09] Covers: ECONNREFUSED, 400, 401/403, 404, 429, 500, timeout
|
|
@@ -96,9 +100,12 @@ function serverErrorDetail(payload) {
|
|
|
96
100
|
* @param payload - Parsed error body, when the response carried one (Issue #1637).
|
|
97
101
|
* Used for 5xx only: the 4xx messages below are already specific, and are
|
|
98
102
|
* pinned by tests as the CLI's own wording.
|
|
103
|
+
* @param context - Which server answered, when the caller knows (Issue #2404).
|
|
104
|
+
* Read by the 404 branch only; the other messages do not send anyone looking
|
|
105
|
+
* in the wrong place.
|
|
99
106
|
* @returns User-friendly error message and exit code
|
|
100
107
|
*/
|
|
101
|
-
function handleApiError(error, status, payload) {
|
|
108
|
+
function handleApiError(error, status, payload, context) {
|
|
102
109
|
// HTTP status-based errors
|
|
103
110
|
if (status !== undefined) {
|
|
104
111
|
switch (status) {
|
|
@@ -114,8 +121,12 @@ function handleApiError(error, status, payload) {
|
|
|
114
121
|
exitCode: types_1.ExitCode.CONFIG_ERROR,
|
|
115
122
|
};
|
|
116
123
|
case 404:
|
|
124
|
+
// Issue #2404: the sentence stays, so everything matching on it keeps
|
|
125
|
+
// matching; the URL is appended because "check the worktree ID" is
|
|
126
|
+
// advice about the wrong thing whenever the id is fine and the SERVER
|
|
127
|
+
// is the one that is wrong.
|
|
117
128
|
return {
|
|
118
|
-
message: 'Resource not found. Check the worktree ID.',
|
|
129
|
+
message: withServerUrl('Resource not found. Check the worktree ID.', context),
|
|
119
130
|
exitCode: types_1.ExitCode.UNEXPECTED_ERROR,
|
|
120
131
|
};
|
|
121
132
|
case 429:
|
|
@@ -205,6 +216,18 @@ class ApiClient {
|
|
|
205
216
|
console.error('Warning: Connecting to remote server over HTTP. Auth token will be sent in plaintext. Use HTTPS for non-localhost connections.');
|
|
206
217
|
}
|
|
207
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* The base URL this client actually dials (Issue #2404).
|
|
221
|
+
*
|
|
222
|
+
* Resolved once in the constructor through the precedence in
|
|
223
|
+
* `loadClientEnv()` (`--base-url` > exported `CM_PORT` > `~/.commandmate/.env`
|
|
224
|
+
* > 3000), which is why it is read from here rather than re-derived by each
|
|
225
|
+
* caller: a second derivation is a second answer, and "which server did this
|
|
226
|
+
* command talk to" only helps if it is the one the request went to.
|
|
227
|
+
*/
|
|
228
|
+
get serverUrl() {
|
|
229
|
+
return this.baseUrl;
|
|
230
|
+
}
|
|
208
231
|
getHeaders() {
|
|
209
232
|
const headers = {
|
|
210
233
|
'Content-Type': 'application/json',
|
|
@@ -250,7 +273,7 @@ class ApiClient {
|
|
|
250
273
|
// Issue #1637: read the body first — handleApiError needs it to surface
|
|
251
274
|
// the server's reason for a 5xx instead of "check the logs".
|
|
252
275
|
const payload = await readErrorPayload(response);
|
|
253
|
-
const errResult = handleApiError(null, response.status, payload);
|
|
276
|
+
const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
|
|
254
277
|
throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
|
|
255
278
|
}
|
|
256
279
|
return (await response.json());
|
|
@@ -277,7 +300,7 @@ class ApiClient {
|
|
|
277
300
|
// Issue #1637: read the body first — handleApiError needs it to surface
|
|
278
301
|
// the server's reason for a 5xx instead of "check the logs".
|
|
279
302
|
const payload = await readErrorPayload(response);
|
|
280
|
-
const errResult = handleApiError(null, response.status, payload);
|
|
303
|
+
const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
|
|
281
304
|
throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
|
|
282
305
|
}
|
|
283
306
|
// Handle 204 No Content
|
|
@@ -309,7 +332,7 @@ class ApiClient {
|
|
|
309
332
|
// Issue #1637: read the body first — handleApiError needs it to surface
|
|
310
333
|
// the server's reason for a 5xx instead of "check the logs".
|
|
311
334
|
const payload = await readErrorPayload(response);
|
|
312
|
-
const errResult = handleApiError(null, response.status, payload);
|
|
335
|
+
const errResult = handleApiError(null, response.status, payload, { serverUrl: this.baseUrl });
|
|
313
336
|
throw new ApiError(errResult.message, errResult.exitCode, response.status, payload);
|
|
314
337
|
}
|
|
315
338
|
const text = await response.text();
|