commandmate 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +178 -165
- package/.next/app-path-routes-manifest.json +29 -29
- 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.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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +1 -1
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +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.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.nft.json +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 +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- 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.nft.json +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.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +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.js.nft.json +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_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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +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.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/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +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.js.nft.json +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page.js +2 -2
- package/.next/server/app/chat/page.js.nft.json +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.js +2 -2
- package/.next/server/app/more/page.js.nft.json +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.js +1 -1
- package/.next/server/app/page.js.nft.json +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.js +2 -2
- package/.next/server/app/repositories/page.js.nft.json +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page.js +1 -1
- package/.next/server/app/review/page.js.nft.json +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +2 -2
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page.js +2 -2
- package/.next/server/app/skills/installed/page.js.nft.json +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page.js +1 -1
- package/.next/server/app/skills/page.js.nft.json +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 +11 -11
- package/.next/server/app/worktrees/[id]/page.js.nft.json +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 +29 -29
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/{6286.js → 1467.js} +6 -6
- package/.next/server/chunks/1886.js +1 -1
- package/.next/server/chunks/2980.js +1 -1
- package/.next/server/chunks/3077.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3323.js +1 -1
- package/.next/server/chunks/434.js +1 -0
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/450.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/650.js +1 -1
- package/.next/server/chunks/6677.js +3 -3
- package/.next/server/chunks/7075.js +1 -1
- package/.next/server/chunks/7506.js +1 -1
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8728.js +8 -0
- package/.next/server/chunks/8807.js +10 -10
- package/.next/server/chunks/8918.js +5 -5
- package/.next/server/chunks/9123.js +2 -2
- 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/server/src/middleware.js +1 -1
- package/.next/server/src/middleware.js.map +1 -1
- package/.next/static/chunks/2407-b9001cc6041876e1.js +1 -0
- package/.next/static/chunks/{4322-dfd5a7119b5ba4fc.js → 4322-59a7dbd77a7763cc.js} +1 -1
- package/.next/static/chunks/4388-410b1d8ed0d8d98b.js +1 -0
- package/.next/static/chunks/4718-56ebb6e55073ed5e.js +1 -0
- package/.next/static/chunks/{2652-18b8fea64413467e.js → 4809-145884b958e7d9a6.js} +1 -1
- package/.next/static/chunks/app/chat/{page-e60d07c4c79602f0.js → page-609c9a0f5ce44799.js} +1 -1
- package/.next/static/chunks/app/error-c1dc1038d0a84a33.js +1 -0
- package/.next/static/chunks/app/global-error-49f765ecb7fd6527.js +1 -0
- package/.next/static/chunks/app/{layout-4c2f21e3f653ee4c.js → layout-114dc6e93e332392.js} +1 -1
- package/.next/static/chunks/app/more/{page-fc94f86aa881489e.js → page-7c5380e4a7561935.js} +1 -1
- package/.next/static/chunks/app/{page-26a42881c8648cd3.js → page-a4f03c188610c6c8.js} +1 -1
- package/.next/static/chunks/app/repositories/{page-2576158c8420fda0.js → page-a15efb4ce1d1721e.js} +1 -1
- package/.next/static/chunks/app/review/{page-35b99baaf3c93b38.js → page-90ac618489676cbc.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-a3ab6f122ec60d5a.js → page-6486d9a643c1dc6f.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/{page-06799157652e8fa9.js → page-844872df04f52943.js} +1 -1
- package/.next/static/chunks/app/skills/installed/{page-2f3987e3a131c8d0.js → page-566dfde26d7e63ba.js} +1 -1
- package/.next/static/chunks/app/skills/{page-e70bfcf5ed4e4865.js → page-7859540e8092aace.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3a58ae66e203f935.js +1 -0
- package/.next/trace +14 -13
- package/README.md +141 -24
- package/dist/cli/commands/remote.d.ts +93 -3
- package/dist/cli/commands/remote.d.ts.map +1 -1
- package/dist/cli/commands/remote.js +232 -9
- package/dist/cli/types/index.d.ts +9 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +11 -1
- package/dist/cli/utils/remote-state.d.ts +32 -0
- package/dist/cli/utils/remote-state.d.ts.map +1 -1
- package/dist/cli/utils/remote-state.js +12 -0
- package/dist/lib/detection/tools/verified-against.d.ts +20 -0
- package/dist/lib/detection/tools/verified-against.d.ts.map +1 -1
- package/dist/lib/detection/tools/verified-against.js +20 -0
- package/dist/server/server.js +204 -16
- package/dist/server/src/config/websocket-config.js +67 -0
- package/dist/server/src/lib/auto-yes-poller.js +40 -7
- package/dist/server/src/lib/db/agent-instances-db.js +11 -0
- package/dist/server/src/lib/detection/selection-shape.js +361 -3
- package/dist/server/src/lib/detection/tools/command-code/detect.js +96 -0
- package/dist/server/src/lib/detection/tools/command-code/dialog.js +314 -0
- package/dist/server/src/lib/detection/tools/verified-against.js +20 -0
- package/dist/server/src/lib/hooks/sources/opencode/ports.js +6 -0
- package/dist/server/src/lib/polling/response-checker.js +48 -2
- package/dist/server/src/lib/relay/relay-session-ref.js +15 -8
- package/dist/server/src/lib/ws-server.js +185 -6
- package/package.json +1 -1
- package/public/sw.js +12 -0
- package/.next/server/chunks/1049.js +0 -1
- package/.next/server/chunks/595.js +0 -8
- package/.next/static/chunks/4718-2d9f0397a8f931c4.js +0 -1
- package/.next/static/chunks/8361-0142beb59be2abf3.js +0 -1
- package/.next/static/chunks/app/error-27ab04bce6545dae.js +0 -1
- package/.next/static/chunks/app/global-error-7b761b8e181ffc1f.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-a39ec2a0732d11a6.js +0 -1
- /package/.next/static/{gniPBCKoXtbIOQey8kNcx → 0vChXhzA5YWj3f0D6A-Cn}/_buildManifest.js +0 -0
- /package/.next/static/{gniPBCKoXtbIOQey8kNcx → 0vChXhzA5YWj3f0D6A-Cn}/_ssgManifest.js +0 -0
package/dist/server/server.js
CHANGED
|
@@ -87,6 +87,83 @@ const session_cleanup_1 = require("./src/lib/session-cleanup");
|
|
|
87
87
|
const dev = process.env.NODE_ENV !== 'production';
|
|
88
88
|
const hostname = (0, env_1.getEnvByKey)('CM_BIND') || '127.0.0.1';
|
|
89
89
|
const port = parseInt((0, env_1.getEnvByKey)('CM_PORT') || '3000', 10);
|
|
90
|
+
/**
|
|
91
|
+
* Issue #2489: the `CM_AUTH_SCOPE` value that exempts the local listener.
|
|
92
|
+
*
|
|
93
|
+
* Written out here rather than imported from `src/lib/ws-server.ts`, which
|
|
94
|
+
* exports the same constant for the Node-runtime side, for the same reason the
|
|
95
|
+
* `x-cm-raw-url` header name below is written out: this is read at MODULE SCOPE,
|
|
96
|
+
* before `app.prepare()`, and everything server.ts evaluates that early has to
|
|
97
|
+
* stand on its own. `tests/integration/remote-ingress-auth-2489.test.ts` holds
|
|
98
|
+
* the two spellings equal, so a rename cannot leave one of them behind.
|
|
99
|
+
*/
|
|
100
|
+
const REMOTE_ONLY_AUTH_SCOPE = 'remote-only';
|
|
101
|
+
/**
|
|
102
|
+
* Issue #2489: the loopback address the Provider's upstream is pointed at.
|
|
103
|
+
*
|
|
104
|
+
* `commandmate remote --auth remote-only` asks for "the phone authenticates,
|
|
105
|
+
* the PC in front of the machine does not". The listener a request landed on is
|
|
106
|
+
* the only unforgeable way to tell those apart (see the header of
|
|
107
|
+
* `src/lib/ws-server.ts`'s ingress section), so this bind is deliberately the
|
|
108
|
+
* literal loopback address rather than `hostname`: the second listener must
|
|
109
|
+
* never be reachable from the LAN, whatever `CM_BIND` says.
|
|
110
|
+
*/
|
|
111
|
+
const REMOTE_INGRESS_BIND = '127.0.0.1';
|
|
112
|
+
/**
|
|
113
|
+
* Issue #2489: whether `CM_BIND` keeps the local listener off the network.
|
|
114
|
+
*
|
|
115
|
+
* `remote-only` makes the local listener unauthenticated, so a non-loopback
|
|
116
|
+
* bind would extend "local means trusted" to every host on the LAN. The CLI
|
|
117
|
+
* refuses that combination up front (exit 2); this is the second, independent
|
|
118
|
+
* check, because `CM_BIND` can also be set in `.env` or exported after the fact.
|
|
119
|
+
*/
|
|
120
|
+
function isLoopbackBind(bind) {
|
|
121
|
+
return bind === '127.0.0.1' || bind === 'localhost' || bind === '::1' || bind === '[::1]';
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Fall back to authenticating every listener, and say why.
|
|
125
|
+
*
|
|
126
|
+
* Writes the decision back into `process.env` rather than only returning it:
|
|
127
|
+
* `middleware.ts` and `ws-server.ts` read `CM_AUTH_SCOPE` per request, so a
|
|
128
|
+
* degradation that lived in a local variable would leave the local listener
|
|
129
|
+
* exempt while this process believed it had closed the door.
|
|
130
|
+
*/
|
|
131
|
+
function refuseRemoteOnly(reason) {
|
|
132
|
+
console.error(`[Security] CM_AUTH_SCOPE=${REMOTE_ONLY_AUTH_SCOPE} refused: ${reason}. ` +
|
|
133
|
+
'Every listener will require authentication.');
|
|
134
|
+
process.env.CM_AUTH_SCOPE = 'all';
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Resolve the port of the remote-only ingress listener, or null for one listener.
|
|
139
|
+
*
|
|
140
|
+
* Fail-closed at every branch: anything this cannot verify turns into
|
|
141
|
+
* {@link refuseRemoteOnly}, which is the pre-#2489 behaviour (auth everywhere).
|
|
142
|
+
*
|
|
143
|
+
* @returns The loopback port to open for the Provider, or null
|
|
144
|
+
*/
|
|
145
|
+
function resolveRemoteIngressPort() {
|
|
146
|
+
if (process.env.CM_AUTH_SCOPE !== REMOTE_ONLY_AUTH_SCOPE)
|
|
147
|
+
return null;
|
|
148
|
+
if (!isLoopbackBind(hostname)) {
|
|
149
|
+
return refuseRemoteOnly(`CM_BIND is ${hostname}, which is not loopback`);
|
|
150
|
+
}
|
|
151
|
+
const raw = process.env.CM_REMOTE_INGRESS_PORT;
|
|
152
|
+
if (!raw) {
|
|
153
|
+
return refuseRemoteOnly('CM_REMOTE_INGRESS_PORT is not set');
|
|
154
|
+
}
|
|
155
|
+
const parsed = Number.parseInt(raw, 10);
|
|
156
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
|
|
157
|
+
return refuseRemoteOnly(`CM_REMOTE_INGRESS_PORT is not a valid port: ${raw}`);
|
|
158
|
+
}
|
|
159
|
+
if (parsed === port) {
|
|
160
|
+
// One socket cannot be two ingresses, and "the Provider dials the port the
|
|
161
|
+
// user dials" is exactly the confusion this whole mechanism exists to avoid.
|
|
162
|
+
return refuseRemoteOnly('CM_REMOTE_INGRESS_PORT is the same port the server serves on');
|
|
163
|
+
}
|
|
164
|
+
return parsed;
|
|
165
|
+
}
|
|
166
|
+
const remoteIngressPort = resolveRemoteIngressPort();
|
|
90
167
|
// Issue #331: HTTPS configuration
|
|
91
168
|
const certPath = process.env.CM_HTTPS_CERT;
|
|
92
169
|
const keyPath = process.env.CM_HTTPS_KEY;
|
|
@@ -162,14 +239,27 @@ async function resolveWorktreeAliasRedirect(url) {
|
|
|
162
239
|
// All WebSocket upgrades are handled by ws-server.ts (our own upgrade listener).
|
|
163
240
|
app.setupWebSocketHandler = () => { };
|
|
164
241
|
app.prepare().then(() => {
|
|
165
|
-
// Request handler for both HTTP and HTTPS
|
|
166
|
-
|
|
242
|
+
// Request handler for both HTTP and HTTPS.
|
|
243
|
+
//
|
|
244
|
+
// Issue #2489: a factory rather than a single handler, because the ONE thing a
|
|
245
|
+
// request cannot lie about is the listener it arrived on, and that fact is
|
|
246
|
+
// only known here — at the point the listener is constructed. Every listener
|
|
247
|
+
// is built through this function, so every request is stamped; a listener
|
|
248
|
+
// wired up any other way would leave the caller's own `x-cm-ingress` in place,
|
|
249
|
+
// which is the single way this scheme can fail open.
|
|
250
|
+
const requestHandlerFor = (ingress) => async (req, res) => {
|
|
167
251
|
// Guard: res must be a proper HTTP ServerResponse (not a raw net.Socket).
|
|
168
252
|
// Defense-in-depth: normally not needed after the setupWebSocketHandler fix above,
|
|
169
253
|
// but kept for safety in case any other path passes a non-ServerResponse object.
|
|
170
254
|
if (typeof res?.setHeader !== 'function') {
|
|
171
255
|
return;
|
|
172
256
|
}
|
|
257
|
+
// Issue #2489: overwrite, unconditionally and before anything reads it.
|
|
258
|
+
// `middleware.ts` and `ws-server.ts` decide whether to demand a token from
|
|
259
|
+
// this value alone — deliberately NOT from `X-Real-IP` / `X-Forwarded-For` /
|
|
260
|
+
// `Host`, all of which a tunnelled request carries identically to a local
|
|
261
|
+
// one (a Provider's upstream IS 127.0.0.1).
|
|
262
|
+
(0, ws_server_1.stampIngress)(req.headers, ingress);
|
|
173
263
|
// Issue #332: Inject X-Real-IP header for IP restriction
|
|
174
264
|
// [S3-005] This applies to HTTP requests only. WebSocket upgrade requests
|
|
175
265
|
// are skipped below and handled by ws-server.ts (which uses socket.remoteAddress directly).
|
|
@@ -241,15 +331,16 @@ app.prepare().then(() => {
|
|
|
241
331
|
}
|
|
242
332
|
};
|
|
243
333
|
// Issue #331: Create HTTP or HTTPS server
|
|
244
|
-
let
|
|
334
|
+
let tlsOptions = null;
|
|
245
335
|
let protocol = 'http';
|
|
246
336
|
if (certPath && keyPath) {
|
|
247
337
|
const validatedCertPath = validateCertPath(certPath, 'Certificate');
|
|
248
338
|
const validatedKeyPath = validateCertPath(keyPath, 'Key');
|
|
249
339
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
340
|
+
tlsOptions = {
|
|
341
|
+
cert: (0, fs_1.readFileSync)(validatedCertPath),
|
|
342
|
+
key: (0, fs_1.readFileSync)(validatedKeyPath),
|
|
343
|
+
};
|
|
253
344
|
protocol = 'https';
|
|
254
345
|
console.log('HTTPS server created with TLS certificates');
|
|
255
346
|
}
|
|
@@ -258,11 +349,35 @@ app.prepare().then(() => {
|
|
|
258
349
|
process.exit(2);
|
|
259
350
|
}
|
|
260
351
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Issue #2489: build one listener, stamped with its own ingress.
|
|
354
|
+
*
|
|
355
|
+
* The TLS material is read once above and shared, so the remote listener is
|
|
356
|
+
* the same server in every respect except which socket it answers on — which
|
|
357
|
+
* is the only difference that is allowed to matter.
|
|
358
|
+
*/
|
|
359
|
+
const createListener = (ingress) => tlsOptions === null
|
|
360
|
+
? (0, http_1.createServer)(requestHandlerFor(ingress))
|
|
361
|
+
: (0, https_1.createServer)(tlsOptions, requestHandlerFor(ingress));
|
|
362
|
+
const server = createListener('local');
|
|
363
|
+
/**
|
|
364
|
+
* Issue #2489: the listener `commandmate remote --auth remote-only` points the
|
|
365
|
+
* Provider at. Null in every other configuration, which is what keeps
|
|
366
|
+
* `--auth all` (and a plain `commandmate start`) on exactly one socket.
|
|
367
|
+
*/
|
|
368
|
+
const remoteServer = remoteIngressPort === null ? null : createListener('remote');
|
|
369
|
+
// Issue #2489: stamp WebSocket upgrades too, and BEFORE `setupWebSocket`
|
|
370
|
+
// registers its own 'upgrade' listener — Node fires listeners in registration
|
|
371
|
+
// order, so this is what guarantees the auth check downstream reads a value
|
|
372
|
+
// this process wrote. HTTP and WS must agree: a phone drives the terminal over
|
|
373
|
+
// this socket, so a WS path that exempted more than the HTTP path would hand
|
|
374
|
+
// the tunnel a live shell while the screen behind it still asked for a token.
|
|
375
|
+
server.on('upgrade', (request) => (0, ws_server_1.stampIngress)(request.headers, 'local'));
|
|
376
|
+
remoteServer?.on('upgrade', (request) => (0, ws_server_1.stampIngress)(request.headers, 'remote'));
|
|
264
377
|
// Setup WebSocket server
|
|
265
|
-
(0, ws_server_1.setupWebSocket)(server
|
|
378
|
+
(0, ws_server_1.setupWebSocket)(server, {
|
|
379
|
+
additionalServers: remoteServer === null ? [] : [remoteServer],
|
|
380
|
+
});
|
|
266
381
|
// Scan and sync worktrees on startup
|
|
267
382
|
async function initializeWorktrees() {
|
|
268
383
|
try {
|
|
@@ -466,9 +581,35 @@ app.prepare().then(() => {
|
|
|
466
581
|
}
|
|
467
582
|
process.exit(1);
|
|
468
583
|
});
|
|
584
|
+
// Issue #2489: open the Provider's door on its own loopback socket.
|
|
585
|
+
//
|
|
586
|
+
// Started before the main `listen` so the CLI's readiness probe on this port
|
|
587
|
+
// cannot outrun it. An error here is NOT fatal to the process — killing the
|
|
588
|
+
// server would take the user's local session down with the remote one, which
|
|
589
|
+
// is the same reason `--expires` closes only the Provider (§5.3) — but it IS
|
|
590
|
+
// fail-closed: without this socket there is no "remote" ingress, so the local
|
|
591
|
+
// exemption is withdrawn and every listener goes back to demanding a token.
|
|
592
|
+
// `commandmate remote` then fails its readiness probe on this port and rolls
|
|
593
|
+
// the whole session back, which is where the user sees the error.
|
|
594
|
+
if (remoteServer !== null && remoteIngressPort !== null) {
|
|
595
|
+
remoteServer.on('error', (err) => {
|
|
596
|
+
console.error(`[Security] Remote ingress listener on ${REMOTE_INGRESS_BIND}:${remoteIngressPort} failed ` +
|
|
597
|
+
`(${err.code ?? err.message}). Every listener will require authentication.`);
|
|
598
|
+
process.env.CM_AUTH_SCOPE = 'all';
|
|
599
|
+
});
|
|
600
|
+
remoteServer.listen(remoteIngressPort, REMOTE_INGRESS_BIND, () => {
|
|
601
|
+
console.log(`> Remote ingress ready on ${protocol}://${REMOTE_INGRESS_BIND}:${remoteIngressPort} (authentication required)`);
|
|
602
|
+
});
|
|
603
|
+
}
|
|
469
604
|
server.listen(port, hostname, async () => {
|
|
470
605
|
console.log(`> Ready on ${protocol}://${hostname}:${port}`);
|
|
471
606
|
console.log(`> WebSocket server ready`);
|
|
607
|
+
if (remoteIngressPort !== null) {
|
|
608
|
+
// Say it on the main "Ready" line too: "the PC does not need to log in" is
|
|
609
|
+
// a security-relevant departure from the default, and a server log that
|
|
610
|
+
// only mentions it in a line above is a log nobody reads.
|
|
611
|
+
console.log(`> Auth scope: ${REMOTE_ONLY_AUTH_SCOPE} — ${protocol}://${hostname}:${port} does not require a token`);
|
|
612
|
+
}
|
|
472
613
|
// Issue #2113: verify that the URL the documentation advertises
|
|
473
614
|
// (`http://localhost:<port>`) actually reaches THIS process. It does not when
|
|
474
615
|
// another process holds `::1:<port>`: `localhost` resolves ::1 before 127.0.0.1
|
|
@@ -647,12 +788,59 @@ app.prepare().then(() => {
|
|
|
647
788
|
console.log('Graceful shutdown timeout, forcing exit...');
|
|
648
789
|
process.exit(1);
|
|
649
790
|
}, 3000);
|
|
650
|
-
// Try graceful HTTP server close
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
791
|
+
// Try graceful HTTP server close.
|
|
792
|
+
//
|
|
793
|
+
// Issue #2489: EVERY listener this process opened, not just the main one.
|
|
794
|
+
// `remoteServer` was absent from this function entirely, and the cost was
|
|
795
|
+
// asymmetry on the door the user cannot see: `server.close()`'s callback
|
|
796
|
+
// fires once the LOCAL connections have drained, and the `process.exit(0)`
|
|
797
|
+
// inside it then cut off any request still in flight through the provider.
|
|
798
|
+
// The local listener had three seconds of grace and the remote one had
|
|
799
|
+
// none. (The accept window between the teardown above and this point is the
|
|
800
|
+
// same for both listeners, and effectively zero — everything between the
|
|
801
|
+
// signal and here is synchronous, so the event loop never turns to accept a
|
|
802
|
+
// connection. The in-flight requests are the real loss.)
|
|
803
|
+
//
|
|
804
|
+
// The invariant, rather than the fix: whatever shutdown does to one
|
|
805
|
+
// listener it does to all of them. Anything added here belongs INSIDE this
|
|
806
|
+
// loop rather than on `server` alone — applying it to `server` only would
|
|
807
|
+
// put the asymmetry straight back on the listener nobody is looking at.
|
|
808
|
+
const listeners = remoteServer === null ? [server] : [server, remoteServer];
|
|
809
|
+
// `close()` stops each accept loop immediately and calls back once that
|
|
810
|
+
// listener's own connections have ended, so both doors shut now and the
|
|
811
|
+
// exit waits for the last one to drain. A listener that never reached
|
|
812
|
+
// `listen()` (EADDRINUSE on the remote port) calls its callback with an
|
|
813
|
+
// Error instead of throwing, which counts as drained here — which is what
|
|
814
|
+
// we want: a door that never opened cannot be holding anything.
|
|
815
|
+
let draining = listeners.length;
|
|
816
|
+
for (const listener of listeners) {
|
|
817
|
+
listener.close(() => {
|
|
818
|
+
draining -= 1;
|
|
819
|
+
if (draining > 0)
|
|
820
|
+
return;
|
|
821
|
+
clearTimeout(forceExitTimeout);
|
|
822
|
+
console.log('Server closed gracefully');
|
|
823
|
+
process.exit(0);
|
|
824
|
+
});
|
|
825
|
+
// Issue #2488: close() stops accepting immediately, but its callback only
|
|
826
|
+
// fires once every ESTABLISHED connection on that listener is gone. A
|
|
827
|
+
// browser tab left on the dashboard holds keep-alive sockets with no
|
|
828
|
+
// request in flight, and nothing ever closes them, so the callback never
|
|
829
|
+
// ran and the process lived the full 3 seconds to the force-exit — with
|
|
830
|
+
// its listening socket already closed. That window is what the stop
|
|
831
|
+
// scripts used to mistake for "stopped" (they looked for a listener,
|
|
832
|
+
// found none, and returned while this process was still alive and still
|
|
833
|
+
// named by logs/server.pid).
|
|
834
|
+
//
|
|
835
|
+
// closeIdleConnections() ends exactly those sockets. A connection with a
|
|
836
|
+
// request in flight is NOT idle and is left alone: it still gets the 3
|
|
837
|
+
// seconds above, so an in-flight response is no more truncated than
|
|
838
|
+
// before. The stop scripts no longer depend on this being fast — they
|
|
839
|
+
// wait for the PID — but the common case now exits in milliseconds
|
|
840
|
+
// instead of 3 seconds. Per #2489's invariant this runs on EVERY
|
|
841
|
+
// listener, so the remote door drains on the same terms as the local one.
|
|
842
|
+
listener.closeIdleConnections();
|
|
843
|
+
}
|
|
656
844
|
}
|
|
657
845
|
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
658
846
|
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket heartbeat / reconnect constants shared by the server and the browser.
|
|
4
|
+
*
|
|
5
|
+
* Issue #2502: the socket had no liveness probe at all, so a half-open path — a
|
|
6
|
+
* phone that walked out of range, a laptop resuming from sleep, a Wi-Fi/LTE
|
|
7
|
+
* handover — stayed `connected` on both ends while every frame fell on the
|
|
8
|
+
* floor. TCP alone does not notice: without traffic neither side gets a RST,
|
|
9
|
+
* and `readyState` keeps answering OPEN. The only way out is to put bytes on
|
|
10
|
+
* the wire on a schedule and treat silence as a disconnect.
|
|
11
|
+
*
|
|
12
|
+
* Server and client read the same numbers from here because the two halves are
|
|
13
|
+
* a single protocol: the client's patience has to be a multiple of the server's
|
|
14
|
+
* beat, or a healthy connection gets killed between beats.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.WS_HALF_OPEN_CLOSE_CODE = exports.WS_RECONNECT_JITTER_RATIO = exports.WS_RECONNECT_MAX_DELAY_MS = exports.WS_RECONNECT_BASE_DELAY_MS = exports.WS_CLIENT_LIVENESS_TIMEOUT_MS = exports.WS_CLIENT_LIVENESS_CHECK_INTERVAL_MS = exports.WS_HEARTBEAT_MESSAGE_TYPE = exports.WS_SERVER_HEARTBEAT_INTERVAL_MS = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* How often the server pings every client and emits an application-level beat.
|
|
20
|
+
*
|
|
21
|
+
* 30s is the `ws` library's own recipe cadence and stays well inside the idle
|
|
22
|
+
* timeouts of the proxies CommandMate is tunnelled through (Cloudflare Quick
|
|
23
|
+
* Tunnel, Tailscale Serve), which is a second reason to send it at all.
|
|
24
|
+
*/
|
|
25
|
+
exports.WS_SERVER_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
26
|
+
/**
|
|
27
|
+
* Server → client application-level beat (Issue #2502).
|
|
28
|
+
*
|
|
29
|
+
* A protocol-level ping is invisible to a browser: the WebSocket API answers
|
|
30
|
+
* pings automatically and gives page JavaScript no way to observe either the
|
|
31
|
+
* ping or its own pong. So the server sends this frame alongside the protocol
|
|
32
|
+
* ping — same cadence, same sweep — purely so the tab has something it CAN see.
|
|
33
|
+
* The client consumes it for liveness and does not forward it to listeners.
|
|
34
|
+
*/
|
|
35
|
+
exports.WS_HEARTBEAT_MESSAGE_TYPE = 'heartbeat';
|
|
36
|
+
/** How often the client checks how long it has been since anything arrived. */
|
|
37
|
+
exports.WS_CLIENT_LIVENESS_CHECK_INTERVAL_MS = 5_000;
|
|
38
|
+
/**
|
|
39
|
+
* Silence, in ms, after which the client declares the socket half-open.
|
|
40
|
+
*
|
|
41
|
+
* 2.5x the server beat: one lost or late heartbeat must not tear down a healthy
|
|
42
|
+
* connection, but two in a row is not something a live path does. Anything
|
|
43
|
+
* inbound refreshes the clock — a broadcast, a terminal chunk, the beat — so a
|
|
44
|
+
* busy socket never gets near this.
|
|
45
|
+
*/
|
|
46
|
+
exports.WS_CLIENT_LIVENESS_TIMEOUT_MS = 75_000;
|
|
47
|
+
/** Default base delay for the client's reconnect backoff. */
|
|
48
|
+
exports.WS_RECONNECT_BASE_DELAY_MS = 1_000;
|
|
49
|
+
/** Default ceiling for the client's reconnect backoff. */
|
|
50
|
+
exports.WS_RECONNECT_MAX_DELAY_MS = 30_000;
|
|
51
|
+
/**
|
|
52
|
+
* Fraction of the computed backoff the client randomises by, +/- (Issue #2502).
|
|
53
|
+
*
|
|
54
|
+
* Without it every tab that was connected to a restarting server retries on the
|
|
55
|
+
* identical 1s/2s/4s grid and the thundering herd lands on the first socket the
|
|
56
|
+
* new process opens. +/-20% is enough to smear the herd across the window while
|
|
57
|
+
* keeping the backoff's shape recognisable.
|
|
58
|
+
*/
|
|
59
|
+
exports.WS_RECONNECT_JITTER_RATIO = 0.2;
|
|
60
|
+
/**
|
|
61
|
+
* Close code the client uses when it gives up on a silent socket.
|
|
62
|
+
*
|
|
63
|
+
* In the application-private 4000-4999 range (1000-2999 are reserved by the
|
|
64
|
+
* protocol and browsers throw on most of them), so a server-side close log can
|
|
65
|
+
* tell "the tab decided this path was dead" apart from an ordinary goodbye.
|
|
66
|
+
*/
|
|
67
|
+
exports.WS_HALF_OPEN_CLOSE_CODE = 4001;
|
|
@@ -49,6 +49,7 @@ exports.clearAllPollerStates = clearAllPollerStates;
|
|
|
49
49
|
exports.getLastServerResponseTimestamp = getLastServerResponseTimestamp;
|
|
50
50
|
exports.isPollerActive = isPollerActive;
|
|
51
51
|
exports.validatePollingContext = validatePollingContext;
|
|
52
|
+
exports.capturePollerFrame = capturePollerFrame;
|
|
52
53
|
exports.captureAndCleanOutput = captureAndCleanOutput;
|
|
53
54
|
exports.processStopConditionDelta = processStopConditionDelta;
|
|
54
55
|
exports.detectAndRespondToPrompt = detectAndRespondToPrompt;
|
|
@@ -224,9 +225,28 @@ function validatePollingContext(compositeKey, pollerState) {
|
|
|
224
225
|
}
|
|
225
226
|
return 'valid';
|
|
226
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Capture tmux session output once, keeping both spellings (Issue #2522).
|
|
230
|
+
*
|
|
231
|
+
* @internal Exported for testing purposes only.
|
|
232
|
+
* @param worktreeId - Worktree identifier
|
|
233
|
+
* @param cliToolId - CLI tool type being polled
|
|
234
|
+
* @param captureLines - Optional number of lines to capture
|
|
235
|
+
* @param instanceId - Optional agent instance ID (Issue #896; defaults to primary session)
|
|
236
|
+
* @returns The tick's raw capture and its cleaned form
|
|
237
|
+
*/
|
|
238
|
+
async function capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId) {
|
|
239
|
+
const lines = captureLines ?? auto_yes_config_1.FULL_CAPTURE_LINES;
|
|
240
|
+
const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, lines, instanceId);
|
|
241
|
+
return { raw: output, clean: (0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output)) };
|
|
242
|
+
}
|
|
227
243
|
/**
|
|
228
244
|
* Capture tmux session output and strip ANSI escape codes.
|
|
229
245
|
*
|
|
246
|
+
* Kept as the cleaned half of {@link capturePollerFrame} so the many callers and
|
|
247
|
+
* tests that only ever wanted that string are untouched; `pollAutoYes` takes the
|
|
248
|
+
* pair. `stripBoxDrawing` still runs exactly once per capture either way.
|
|
249
|
+
*
|
|
230
250
|
* @internal Exported for testing purposes only.
|
|
231
251
|
* @param worktreeId - Worktree identifier
|
|
232
252
|
* @param cliToolId - CLI tool type being polled
|
|
@@ -235,9 +255,7 @@ function validatePollingContext(compositeKey, pollerState) {
|
|
|
235
255
|
* @returns Cleaned output string (ANSI stripped)
|
|
236
256
|
*/
|
|
237
257
|
async function captureAndCleanOutput(worktreeId, cliToolId, captureLines, instanceId) {
|
|
238
|
-
|
|
239
|
-
const output = await (0, cli_session_1.captureSessionOutput)(worktreeId, cliToolId, lines, instanceId);
|
|
240
|
-
return (0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output));
|
|
258
|
+
return (await capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId)).clean;
|
|
241
259
|
}
|
|
242
260
|
/**
|
|
243
261
|
* Process stop condition check using delta-based approach.
|
|
@@ -276,9 +294,12 @@ function processStopConditionDelta(compositeKey, pollerState, cleanOutput) {
|
|
|
276
294
|
* @param cleanOutput - ANSI-stripped terminal output
|
|
277
295
|
* @param precomputedLines - Optional pre-split lines to reuse
|
|
278
296
|
* @param instanceId - Optional agent instance ID (Issue #896; defaults to primary session)
|
|
297
|
+
* @param rawOutput - The SAME tick's capture with its box drawing intact
|
|
298
|
+
* (Issue #2522). Omitting it costs Command Code's `AskUserQuestion` and
|
|
299
|
+
* nothing else; see {@link capturePollerFrame}.
|
|
279
300
|
* @returns 'responded' | 'no_prompt' | 'duplicate' | 'no_answer' | 'error'
|
|
280
301
|
*/
|
|
281
|
-
async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId) {
|
|
302
|
+
async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, precomputedLines, instanceId, rawOutput) {
|
|
282
303
|
const compositeKey = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
283
304
|
try {
|
|
284
305
|
// 1. Detect prompt.
|
|
@@ -301,7 +322,15 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
301
322
|
// `cleanOutput` is handed over already stripped, and the clean-frame entry
|
|
302
323
|
// is the one that does not strip again (`stripBoxDrawing` is not
|
|
303
324
|
// idempotent), so this poller and the response poller judge identical text.
|
|
304
|
-
|
|
325
|
+
//
|
|
326
|
+
// Issue #2522 adds the fourth argument, and it is a WIRING change rather
|
|
327
|
+
// than a reader one: Command Code's `AskUserQuestion` is anchored on the
|
|
328
|
+
// rule row `captureAndCleanOutput` blanks, so teaching
|
|
329
|
+
// `detectPromptOnCleanFrame` to read it changed nothing on this path until
|
|
330
|
+
// the same tick's raw capture reached it. `precomputedLines` stays the split
|
|
331
|
+
// of `cleanOutput` — the stop-condition delta and every other tool's reading
|
|
332
|
+
// are measured on that string and must not move.
|
|
333
|
+
const promptDetection = (0, response_checker_1.detectPromptOnCleanFrame)(cleanOutput, cliToolId, precomputedLines, rawOutput);
|
|
305
334
|
if (!promptDetection.isPrompt || !promptDetection.promptData) {
|
|
306
335
|
pollerState.lastAnsweredPromptKey = null;
|
|
307
336
|
pollerState.lastAnsweredAt = null;
|
|
@@ -519,14 +548,18 @@ async function pollAutoYes(worktreeId, cliToolId, instanceId) {
|
|
|
519
548
|
const captureLines = autoYesState?.stopPattern
|
|
520
549
|
? auto_yes_config_1.FULL_CAPTURE_LINES
|
|
521
550
|
: auto_yes_config_1.REDUCED_CAPTURE_LINES;
|
|
522
|
-
|
|
551
|
+
// Issue #2522: one capture, both spellings. The cleaned string drives every
|
|
552
|
+
// step below exactly as it did; the raw one is carried to the prompt reading
|
|
553
|
+
// so a dialog anchored on box drawing is not invisible to the one path that
|
|
554
|
+
// can answer it without a human.
|
|
555
|
+
const { raw: rawOutput, clean: cleanOutput } = await capturePollerFrame(worktreeId, cliToolId, captureLines, instanceId);
|
|
523
556
|
const lines = cleanOutput.split('\n');
|
|
524
557
|
// 3. Stop condition delta check (Issue #314)
|
|
525
558
|
if (processStopConditionDelta(compositeKey, pollerState, cleanOutput)) {
|
|
526
559
|
return;
|
|
527
560
|
}
|
|
528
561
|
// 4. Detect and respond to prompt
|
|
529
|
-
const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId);
|
|
562
|
+
const result = await detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, cleanOutput, lines, instanceId, rawOutput);
|
|
530
563
|
if (result === 'responded') {
|
|
531
564
|
scheduleNextPoll(worktreeId, cliToolId, instanceId, auto_yes_state_1.COOLDOWN_INTERVAL_MS);
|
|
532
565
|
return;
|
|
@@ -136,6 +136,17 @@ function getAgentInstance(db, worktreeId, instanceId) {
|
|
|
136
136
|
* conflict rather than silently overriding it: the roster is user-maintained
|
|
137
137
|
* and a mismatch means one of the two is wrong. Callers surface it as an error.
|
|
138
138
|
*
|
|
139
|
+
* @deprecated Issue #1925 made `resolveSessionTarget`
|
|
140
|
+
* (`src/lib/session/resolve-session-target.ts`) the one authority, and Issue
|
|
141
|
+
* #2491 moved the last two callers — `POST /send` and `resolveRelaySession` —
|
|
142
|
+
* onto it. **Do not give this function a new caller.** Step 2 above is exactly
|
|
143
|
+
* what #2487 corrected: with no roster row it takes `requestedCliTool` even
|
|
144
|
+
* when `instanceId` itself names a different tool, so a caller wired here gets
|
|
145
|
+
* `mcbd-<requested>-<wt>-<tool-named-id>` back instead of a conflict. It is
|
|
146
|
+
* kept only because its #1629 unit test
|
|
147
|
+
* (`tests/unit/db/agent-instances-resolve-cli-tool.test.ts`) still pins that
|
|
148
|
+
* behaviour; removing both is a separate change.
|
|
149
|
+
*
|
|
139
150
|
* @param db - Database instance
|
|
140
151
|
* @param worktreeId - Worktree ID
|
|
141
152
|
* @param instanceId - Targeted agent instance ID (omitted for the primary instance)
|