commandmate 0.20.0 → 0.21.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 +234 -220
- package/.next/app-path-routes-manifest.json +34 -32
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +18 -0
- package/.next/server/app/_not-found/page_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_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.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.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.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.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.js +1 -1
- package/.next/server/app/api/auth/status/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.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_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_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/subscriptions/route.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/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route.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.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.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/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.js +2 -2
- 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.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.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.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.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]/cli-tool/route.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.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]/execution-logs/[logId]/route.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.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.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.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.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.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.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.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.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.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]/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_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.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.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.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.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]/prompt-response/route.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]/respond/route.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.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.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.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js.nft.json +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.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/route.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 +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]/start-polling/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.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.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.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.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.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.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/route.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]/route.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.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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/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.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_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 +5 -5
- 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 +34 -32
- package/.next/server/chunks/1029.js +1 -1
- package/.next/server/chunks/1503.js +1 -1
- package/.next/server/chunks/22.js +1 -1
- package/.next/server/chunks/2328.js +1 -1
- package/.next/server/chunks/2546.js +1 -0
- package/.next/server/chunks/2710.js +2 -2
- package/.next/server/chunks/28.js +2 -2
- package/.next/server/chunks/3417.js +1 -1
- package/.next/server/chunks/365.js +1 -0
- package/.next/server/chunks/3736.js +1 -1
- package/.next/server/chunks/3753.js +14 -5
- package/.next/server/chunks/4444.js +1 -1
- package/.next/server/chunks/5064.js +1 -1
- package/.next/server/chunks/5737.js +1 -1
- package/.next/server/chunks/5959.js +10 -3
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/848.js +1 -0
- package/.next/server/chunks/8536.js +1 -1
- package/.next/server/chunks/8584.js +1 -1
- package/.next/server/chunks/8807.js +1 -1
- package/.next/server/chunks/8856.js +1 -1
- package/.next/server/chunks/9058.js +1 -1
- package/.next/server/chunks/9360.js +2 -0
- package/.next/server/chunks/9687.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/{8562-6e2ab32452f4fa5b.js → 2053-52d3c82014e1cd54.js} +1 -1
- package/.next/static/chunks/2751-9ef0f4b450bd4ef0.js +1 -0
- package/.next/static/chunks/{6488-3e7539550c3ef358.js → 6488-b5e707dfdd33d633.js} +1 -1
- package/.next/static/chunks/app/api/app/update/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/app/update-check/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/conversation/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/current-output/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/messages/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/session/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/start/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/terminal/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/assistant/tools/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/auth/login/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/auth/status/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/status/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/external-apps/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/fs/browse/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/fs/recent-paths/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/hooks/agent-event/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/hooks/claude-done/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/metrics/vibe/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/ollama/models/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/push/subscriptions/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/push/vapid/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/excluded/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/restore/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/scan/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/sync/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/repositories/validate-path/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/sidebar/group-order/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/skills/[id]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/skills/installations/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/skills/operations/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/skills/reindex/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/skills/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/slash-commands/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/tasks/[taskId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/templates/[id]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/templates/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/todos/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/verification/runs/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/update-plan/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/proxy/[...path]/route-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/skills/[skillId]/page-10d6f983e5f06ffb.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/layout-be21650964d61b1d.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-b73ac6531829c698.js +1 -0
- package/.next/trace +12 -12
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/update/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/update-plan/route.ts +347 -0
- package/.next/types/routes.d.ts +3 -1
- package/.next/types/validator.ts +18 -0
- package/dist/cli/commands/auto-yes.js +1 -1
- package/dist/cli/commands/capture.d.ts +1 -0
- package/dist/cli/commands/capture.d.ts.map +1 -1
- package/dist/cli/commands/capture.js +109 -0
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +31 -6
- package/dist/cli/commands/send.js +2 -2
- package/dist/cli/commands/skill-format.d.ts +7 -1
- package/dist/cli/commands/skill-format.d.ts.map +1 -1
- package/dist/cli/commands/skill-format.js +67 -0
- package/dist/cli/commands/skill.d.ts +2 -0
- package/dist/cli/commands/skill.d.ts.map +1 -1
- package/dist/cli/commands/skill.js +149 -0
- package/dist/cli/commands/sync.d.ts +14 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +53 -0
- package/dist/cli/commands/wait.js +1 -1
- package/dist/cli/docs/agent-operations.d.ts +1 -1
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +25 -3
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +3 -0
- package/dist/cli/types/api-responses.d.ts +174 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +31 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.d.ts +9 -0
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +28 -2
- package/dist/config/history-display-config.d.ts +61 -0
- package/dist/config/history-display-config.d.ts.map +1 -0
- package/dist/config/history-display-config.js +62 -0
- package/dist/server/src/lib/auto-yes-poller.js +49 -0
- package/dist/server/src/lib/db/chat-db.js +75 -1
- package/dist/server/src/lib/db/db.js +4 -3
- package/dist/server/src/lib/polling/auto-yes-suppression-state.js +54 -0
- package/dist/server/src/lib/session/current-output-builder.js +2 -0
- package/dist/server/src/lib/skills/install-apply.js +63 -23
- package/dist/server/src/lib/skills/operation-audit.js +5 -6
- package/dist/server/src/lib/skills/startup-reconcile.js +26 -5
- package/dist/server/src/lib/skills/updater.js +830 -0
- package/package.json +1 -1
- package/.next/server/chunks/3042.js +0 -1
- package/.next/server/chunks/4440.js +0 -2
- package/.next/server/chunks/4548.js +0 -1
- package/.next/server/chunks/5793.js +0 -1
- package/.next/static/chunks/2751-7fabd91275b14ca3.js +0 -1
- package/.next/static/chunks/app/api/app/update/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/app/update-check/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/conversation/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/current-output/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/messages/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/session/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/start/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/terminal/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/assistant/tools/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/auth/login/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/auth/status/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/status/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/external-apps/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/fs/browse/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/fs/recent-paths/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/hooks/agent-event/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/hooks/claude-done/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/metrics/vibe/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/ollama/models/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/push/subscriptions/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/push/vapid/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/excluded/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/restore/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/scan/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/sync/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/repositories/validate-path/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/sidebar/group-order/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/skills/[id]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/skills/installations/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/skills/operations/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/skills/reindex/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/skills/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/slash-commands/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/cancel/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/tasks/[taskId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/templates/[id]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/templates/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/todos/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/[runId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/verification/runs/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tasks/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/[runId]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/verify/runs/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/proxy/[...path]/route-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-6008b904225ebb25.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/layout-5bdd550ed6386f57.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-214ee8b97dca856b.js +0 -1
- /package/.next/static/{C01FQOE5FCmB3I_GhMa4_ → _AR-TSCnEog5nL9JjjCna}/_buildManifest.js +0 -0
- /package/.next/static/{C01FQOE5FCmB3I_GhMa4_ → _AR-TSCnEog5nL9JjjCna}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Atomic commit of a planned Skill update into one worktree (Issue #1244)
|
|
4
|
+
*
|
|
5
|
+
* The write half of #1243's update plan/apply pair. The plan fixed the old
|
|
6
|
+
* receipt and tree, the new package and receipt exact bytes, and the target
|
|
7
|
+
* binding (branch, HEAD, tree hash); this module is the only place a managed
|
|
8
|
+
* Skill directory is switched from one version to another.
|
|
9
|
+
*
|
|
10
|
+
* The safety argument extends #1235's install commit rather than replacing it:
|
|
11
|
+
*
|
|
12
|
+
* - **Zero-write on any doubt about the old payload.** Every recorded root is
|
|
13
|
+
* re-assessed with the uninstall guard immediately before the first rename:
|
|
14
|
+
* one modified, unknown, missing or irregular path — or any drift from the
|
|
15
|
+
* receipt digest and tree hash the plan bound — and the operation stops
|
|
16
|
+
* having changed nothing under either root. There is no partial update.
|
|
17
|
+
* - **The new payload is written by install's own primitives.** Staging uses
|
|
18
|
+
* {@link stageSkillRootPayload} — the same exclusive no-follow writes, the
|
|
19
|
+
* same digest/mode re-verification, the same tree-hash gate, the same
|
|
20
|
+
* same-filesystem check — into the reserved `.commandmate-staging/`
|
|
21
|
+
* namespace of each destination root.
|
|
22
|
+
* - **The commit point is one rename.** Per root the switch is two renames —
|
|
23
|
+
* old directory aside into the staging namespace, then staged new payload
|
|
24
|
+
* into place — and the operation's single commit point is the rename that
|
|
25
|
+
* publishes the new payload at the *primary* root. Everything before it is
|
|
26
|
+
* undone in-process (the old directory is renamed back); everything after it
|
|
27
|
+
* converges forward from the new receipt via #1234 reconciliation, never
|
|
28
|
+
* backward. Both roots (#1460) ride one journaled operation.
|
|
29
|
+
* - **The old bytes outlive the switch.** Before anything moves, the old
|
|
30
|
+
* payload is copied into a digest-verified, service-owned backup under the
|
|
31
|
+
* CommandMate state root — never inside the repository. Retention and
|
|
32
|
+
* restore are #1245's verified-backup contract; this module only writes the
|
|
33
|
+
* backup and names it by the operation ID (the interface boundary).
|
|
34
|
+
* - **Nothing is executed.** No script, hook or archive-carried step runs at
|
|
35
|
+
* any point on this path.
|
|
36
|
+
*
|
|
37
|
+
* Crash recovery: {@link reconcileSkillUpdateTarget} converges an interrupted
|
|
38
|
+
* update's primary root to exactly one of "old complete" or "new complete" —
|
|
39
|
+
* an aside directory is renamed back, a fully verified staged payload whose
|
|
40
|
+
* receipt matches the journal claim is adopted — and
|
|
41
|
+
* {@link completeSecondarySkillUpdateRoots} then converges secondary roots
|
|
42
|
+
* forward from the committed primary.
|
|
43
|
+
*
|
|
44
|
+
* @module lib/skills/updater
|
|
45
|
+
*/
|
|
46
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
|
+
};
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.SKILL_UPDATE_BACKUP_MANIFEST_FILENAME = exports.SKILL_UPDATE_BACKUP_DIRNAME = exports.SKILL_UPDATE_ROLLBACK_MESSAGE_KEY = exports.SKILL_UPDATE_RELOAD_MESSAGE_KEYS = exports.SKILL_UPDATE_APPLY_NEXT_ACTION_KEYS = exports.SkillUpdateError = exports.SKILL_UPDATE_ERROR_STATUS = exports.SkillUpdateErrorCode = void 0;
|
|
51
|
+
exports.isSkillUpdateError = isSkillUpdateError;
|
|
52
|
+
exports.readSkillUpdateBackupManifest = readSkillUpdateBackupManifest;
|
|
53
|
+
exports.skillUpdateStagingNameFor = skillUpdateStagingNameFor;
|
|
54
|
+
exports.skillUpdateAsideNameFor = skillUpdateAsideNameFor;
|
|
55
|
+
exports.applySkillUpdate = applySkillUpdate;
|
|
56
|
+
exports.reconcileSkillUpdateTarget = reconcileSkillUpdateTarget;
|
|
57
|
+
exports.completeSecondarySkillUpdateRoots = completeSecondarySkillUpdateRoots;
|
|
58
|
+
const fs_1 = require("fs");
|
|
59
|
+
const crypto_1 = require("crypto");
|
|
60
|
+
const path_1 = __importDefault(require("path"));
|
|
61
|
+
const constants_1 = require("../../lib/skills/constants");
|
|
62
|
+
const integrity_1 = require("../../lib/skills/integrity");
|
|
63
|
+
const install_plan_1 = require("../../lib/skills/install-plan");
|
|
64
|
+
const install_apply_1 = require("../../lib/skills/install-apply");
|
|
65
|
+
const uninstall_apply_1 = require("../../lib/skills/uninstall-apply");
|
|
66
|
+
const uninstall_plan_1 = require("../../lib/skills/uninstall-plan");
|
|
67
|
+
const preview_diff_1 = require("../../lib/skills/preview-diff");
|
|
68
|
+
const operation_store_1 = require("../../lib/skills/operation-store");
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// Errors
|
|
71
|
+
// =============================================================================
|
|
72
|
+
/** Client-safe reasons an update refused to write or could not finish. */
|
|
73
|
+
exports.SkillUpdateErrorCode = {
|
|
74
|
+
/** The install root could not be derived safely from the worktree and Skill ID. */
|
|
75
|
+
TARGET_UNSAFE: 'SKILL_UPDATE_TARGET_UNSAFE',
|
|
76
|
+
/** An ancestor of an install root is a symlink or is not a directory. */
|
|
77
|
+
ANCESTOR_UNSAFE: 'SKILL_UPDATE_ANCESTOR_UNSAFE',
|
|
78
|
+
/** A recorded install root is not there to update. */
|
|
79
|
+
NOT_INSTALLED: 'SKILL_UPDATE_NOT_INSTALLED',
|
|
80
|
+
/** The old payload is not provably managed-and-unchanged. Nothing was written. */
|
|
81
|
+
LOCAL_CHANGES: 'SKILL_UPDATE_LOCAL_CHANGES',
|
|
82
|
+
/** The install changed between the plan and this apply. Nothing was written. */
|
|
83
|
+
DRIFT: 'SKILL_UPDATE_DRIFT',
|
|
84
|
+
/** Staging and destination are on different filesystems, so rename is not atomic. */
|
|
85
|
+
CROSS_DEVICE: 'SKILL_UPDATE_CROSS_DEVICE',
|
|
86
|
+
/** Staging could not be created or written. Nothing was written to either root. */
|
|
87
|
+
STAGING_IO: 'SKILL_UPDATE_STAGING_IO',
|
|
88
|
+
/** The service-owned old-payload backup could not be written or verified. */
|
|
89
|
+
BACKUP_IO: 'SKILL_UPDATE_BACKUP_IO',
|
|
90
|
+
/** What landed in staging (or was read back) is not what the plan fixed. */
|
|
91
|
+
PAYLOAD_MISMATCH: 'SKILL_UPDATE_PAYLOAD_MISMATCH',
|
|
92
|
+
/** A rename of the switch sequence failed before the commit point. Rolled back. */
|
|
93
|
+
COMMIT_FAILED: 'SKILL_UPDATE_COMMIT_FAILED',
|
|
94
|
+
/**
|
|
95
|
+
* The publish rename failed *and* the old directory could not be renamed
|
|
96
|
+
* back. The primary root is empty; the old payload survives in the aside
|
|
97
|
+
* directory and in the service-owned backup, and startup reconciliation
|
|
98
|
+
* retries the restore.
|
|
99
|
+
*/
|
|
100
|
+
RESTORE_FAILED: 'SKILL_UPDATE_RESTORE_FAILED',
|
|
101
|
+
};
|
|
102
|
+
/** HTTP status each reason maps to, so every caller answers alike. */
|
|
103
|
+
exports.SKILL_UPDATE_ERROR_STATUS = {
|
|
104
|
+
[exports.SkillUpdateErrorCode.TARGET_UNSAFE]: 400,
|
|
105
|
+
[exports.SkillUpdateErrorCode.ANCESTOR_UNSAFE]: 409,
|
|
106
|
+
[exports.SkillUpdateErrorCode.NOT_INSTALLED]: 409,
|
|
107
|
+
[exports.SkillUpdateErrorCode.LOCAL_CHANGES]: 409,
|
|
108
|
+
[exports.SkillUpdateErrorCode.DRIFT]: 409,
|
|
109
|
+
[exports.SkillUpdateErrorCode.CROSS_DEVICE]: 500,
|
|
110
|
+
[exports.SkillUpdateErrorCode.STAGING_IO]: 500,
|
|
111
|
+
[exports.SkillUpdateErrorCode.BACKUP_IO]: 500,
|
|
112
|
+
[exports.SkillUpdateErrorCode.PAYLOAD_MISMATCH]: 500,
|
|
113
|
+
[exports.SkillUpdateErrorCode.COMMIT_FAILED]: 500,
|
|
114
|
+
[exports.SkillUpdateErrorCode.RESTORE_FAILED]: 500,
|
|
115
|
+
};
|
|
116
|
+
/** An update rejection. The message is built from the code — never from a path. */
|
|
117
|
+
class SkillUpdateError extends Error {
|
|
118
|
+
code;
|
|
119
|
+
detail;
|
|
120
|
+
blockers;
|
|
121
|
+
constructor(code, detail,
|
|
122
|
+
/** Repository-relative paths that blocked the operation, for UX-07. */
|
|
123
|
+
blockers = []) {
|
|
124
|
+
super(`Skill update rejected: ${code}`);
|
|
125
|
+
this.code = code;
|
|
126
|
+
this.detail = detail;
|
|
127
|
+
this.blockers = blockers;
|
|
128
|
+
this.name = 'SkillUpdateError';
|
|
129
|
+
}
|
|
130
|
+
get status() {
|
|
131
|
+
return exports.SKILL_UPDATE_ERROR_STATUS[this.code];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.SkillUpdateError = SkillUpdateError;
|
|
135
|
+
function isSkillUpdateError(value) {
|
|
136
|
+
return value instanceof SkillUpdateError;
|
|
137
|
+
}
|
|
138
|
+
function fail(code, detail, blockers = []) {
|
|
139
|
+
throw new SkillUpdateError(code, detail, blockers);
|
|
140
|
+
}
|
|
141
|
+
/** Update code each install-layer staging failure maps onto. */
|
|
142
|
+
const STAGING_ERROR_TRANSLATION = {
|
|
143
|
+
[install_apply_1.SkillInstallErrorCode.TARGET_UNSAFE]: exports.SkillUpdateErrorCode.TARGET_UNSAFE,
|
|
144
|
+
[install_apply_1.SkillInstallErrorCode.ANCESTOR_UNSAFE]: exports.SkillUpdateErrorCode.ANCESTOR_UNSAFE,
|
|
145
|
+
[install_apply_1.SkillInstallErrorCode.CROSS_DEVICE]: exports.SkillUpdateErrorCode.CROSS_DEVICE,
|
|
146
|
+
[install_apply_1.SkillInstallErrorCode.PAYLOAD_MISMATCH]: exports.SkillUpdateErrorCode.PAYLOAD_MISMATCH,
|
|
147
|
+
};
|
|
148
|
+
/** Map an install-layer staging failure onto the update vocabulary. */
|
|
149
|
+
function translateStagingError(error) {
|
|
150
|
+
if ((0, install_apply_1.isSkillInstallError)(error)) {
|
|
151
|
+
fail(STAGING_ERROR_TRANSLATION[error.code] ?? exports.SkillUpdateErrorCode.STAGING_IO, error.detail);
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
// =============================================================================
|
|
156
|
+
// Next-action vocabulary
|
|
157
|
+
// =============================================================================
|
|
158
|
+
/** i18n key for what the user should do next, per apply outcome (UX-07). */
|
|
159
|
+
exports.SKILL_UPDATE_APPLY_NEXT_ACTION_KEYS = {
|
|
160
|
+
succeeded: 'skills.update.nextAction.succeeded',
|
|
161
|
+
committedReconciling: 'skills.update.nextAction.committedReconciling',
|
|
162
|
+
failed: 'skills.update.nextAction.failed',
|
|
163
|
+
};
|
|
164
|
+
/** i18n key per agent support level after an update, so UI and CLI agree (UX-01). */
|
|
165
|
+
exports.SKILL_UPDATE_RELOAD_MESSAGE_KEYS = {
|
|
166
|
+
native: 'skills.update.reload.native',
|
|
167
|
+
commandmate_runtime: 'skills.update.reload.commandmateRuntime',
|
|
168
|
+
unsupported: 'skills.update.reload.unsupported',
|
|
169
|
+
unknown: 'skills.update.reload.unknown',
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* i18n key naming the verified backup a successful update left behind.
|
|
173
|
+
*
|
|
174
|
+
* Lives here rather than in the route for the same reason the reload and
|
|
175
|
+
* next-action keys do: the apply *contract* owns the vocabulary, and the UI,
|
|
176
|
+
* the CLI and the i18n guard all read it from one place.
|
|
177
|
+
*/
|
|
178
|
+
exports.SKILL_UPDATE_ROLLBACK_MESSAGE_KEY = 'skills.update.rollbackAvailable';
|
|
179
|
+
// =============================================================================
|
|
180
|
+
// Service-owned verified backup (#1245 interface boundary)
|
|
181
|
+
// =============================================================================
|
|
182
|
+
/** Sub-directory of the Skill state root holding old-payload backups. */
|
|
183
|
+
exports.SKILL_UPDATE_BACKUP_DIRNAME = 'backups';
|
|
184
|
+
/** Filename of the manifest each backup carries next to its payload. */
|
|
185
|
+
exports.SKILL_UPDATE_BACKUP_MANIFEST_FILENAME = 'backup.json';
|
|
186
|
+
/** Where one backup's files live. Service-owned; never inside a repository. */
|
|
187
|
+
function backupDirFor(backupId, options) {
|
|
188
|
+
return path_1.default.join((0, operation_store_1.ensureSkillStateDir)(exports.SKILL_UPDATE_BACKUP_DIRNAME, options), backupId);
|
|
189
|
+
}
|
|
190
|
+
/** Read a backup's manifest, or null when the backup does not exist. */
|
|
191
|
+
function readSkillUpdateBackupManifest(backupId, options = {}) {
|
|
192
|
+
if (!install_apply_1.SKILL_INSTALL_OPERATION_ID_PATTERN.test(backupId))
|
|
193
|
+
return null;
|
|
194
|
+
return (0, operation_store_1.readSkillStateFile)(path_1.default.join(backupDirFor(backupId, options), exports.SKILL_UPDATE_BACKUP_MANIFEST_FILENAME));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Copy the old payload into the service-owned backup root and verify it.
|
|
198
|
+
*
|
|
199
|
+
* Runs before anything in the worktree moves, and reads only — a failure here
|
|
200
|
+
* aborts the update with both roots untouched. Every copied file is re-read
|
|
201
|
+
* and digest-compared against the receipt inventory, so the backup #1245 will
|
|
202
|
+
* be asked to restore from is proven, not assumed.
|
|
203
|
+
*/
|
|
204
|
+
function createVerifiedSkillUpdateBackup(input, options) {
|
|
205
|
+
const backupDir = backupDirFor(input.backupId, options);
|
|
206
|
+
const payloadDir = path_1.default.join(backupDir, 'payload');
|
|
207
|
+
try {
|
|
208
|
+
(0, fs_1.mkdirSync)(payloadDir, { recursive: true, mode: operation_store_1.SKILL_STATE_DIR_MODE });
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
fail(exports.SkillUpdateErrorCode.BACKUP_IO, { reason: 'mkdir' });
|
|
212
|
+
}
|
|
213
|
+
const files = [];
|
|
214
|
+
for (const file of input.existing.files) {
|
|
215
|
+
const target = path_1.default.join(payloadDir, file.path);
|
|
216
|
+
try {
|
|
217
|
+
(0, fs_1.mkdirSync)(path_1.default.dirname(target), { recursive: true, mode: operation_store_1.SKILL_STATE_DIR_MODE });
|
|
218
|
+
writeSkillStateBytes(target, file.bytes);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
fail(exports.SkillUpdateErrorCode.BACKUP_IO, { reason: 'write' });
|
|
222
|
+
}
|
|
223
|
+
let readBack;
|
|
224
|
+
try {
|
|
225
|
+
readBack = (0, fs_1.readFileSync)(target);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return fail(exports.SkillUpdateErrorCode.BACKUP_IO, { reason: 'readback' });
|
|
229
|
+
}
|
|
230
|
+
if (!(0, integrity_1.digestMatches)((0, integrity_1.computeSha256Hex)(readBack), file.sha256)) {
|
|
231
|
+
fail(exports.SkillUpdateErrorCode.BACKUP_IO, { reason: 'digest' });
|
|
232
|
+
}
|
|
233
|
+
files.push({
|
|
234
|
+
path: file.path,
|
|
235
|
+
sha256: file.sha256,
|
|
236
|
+
size: file.size,
|
|
237
|
+
executable: file.executable,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const manifest = {
|
|
241
|
+
schemaVersion: 1,
|
|
242
|
+
backupId: input.backupId,
|
|
243
|
+
worktreeId: input.worktreeId,
|
|
244
|
+
skillId: input.skillId,
|
|
245
|
+
fromVersion: input.fromVersion,
|
|
246
|
+
toVersion: input.toVersion,
|
|
247
|
+
receiptDigest: input.receiptDigest,
|
|
248
|
+
treeHash: input.treeHash,
|
|
249
|
+
files,
|
|
250
|
+
createdAt: Date.now(),
|
|
251
|
+
};
|
|
252
|
+
try {
|
|
253
|
+
(0, operation_store_1.writeSkillStateFile)(path_1.default.join(backupDir, exports.SKILL_UPDATE_BACKUP_MANIFEST_FILENAME), manifest);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
fail(exports.SkillUpdateErrorCode.BACKUP_IO, { reason: 'manifest' });
|
|
257
|
+
}
|
|
258
|
+
return manifest;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Plain 0600 byte write for backup payload files (service root, owner-only).
|
|
262
|
+
*
|
|
263
|
+
* `wx` refuses to overwrite: a colliding backup path means a colliding
|
|
264
|
+
* operation ID, which must surface rather than silently merge two backups.
|
|
265
|
+
*/
|
|
266
|
+
function writeSkillStateBytes(target, bytes) {
|
|
267
|
+
(0, fs_1.writeFileSync)(target, bytes, { flag: 'wx', mode: operation_store_1.SKILL_STATE_FILE_MODE });
|
|
268
|
+
}
|
|
269
|
+
// =============================================================================
|
|
270
|
+
// Path helpers
|
|
271
|
+
// =============================================================================
|
|
272
|
+
/** Name of the staged-new-payload directory for one update operation. */
|
|
273
|
+
function skillUpdateStagingNameFor(operationId) {
|
|
274
|
+
return `${operationId}-new`;
|
|
275
|
+
}
|
|
276
|
+
/** Name of the old-payload aside directory for one update operation. */
|
|
277
|
+
function skillUpdateAsideNameFor(operationId) {
|
|
278
|
+
return `${operationId}-old`;
|
|
279
|
+
}
|
|
280
|
+
/** Absolute install root for a validated Skill ID under one root prefix. */
|
|
281
|
+
function resolveUpdateTargetFor(worktreePath, rootPrefix, skillId) {
|
|
282
|
+
if (!constants_1.SKILL_ID_PATTERN.test(skillId) || skillId.length > constants_1.SKILL_ID_MAX_LENGTH) {
|
|
283
|
+
fail(exports.SkillUpdateErrorCode.TARGET_UNSAFE, { reason: 'skill-id' });
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
return (0, preview_diff_1.resolveSkillInstallRootFor)(worktreePath, rootPrefix, skillId);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return fail(exports.SkillUpdateErrorCode.TARGET_UNSAFE, { reason: 'install-root' });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/** Ancestor lstat walk, translated onto the update vocabulary. */
|
|
293
|
+
function assertUpdateAncestors(worktreeRealPath, worktreePath, rootPrefix) {
|
|
294
|
+
try {
|
|
295
|
+
(0, uninstall_apply_1.assertSkillUninstallAncestors)(worktreeRealPath, worktreePath, rootPrefix);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
if ((0, uninstall_apply_1.isSkillUninstallError)(error)) {
|
|
299
|
+
fail(exports.SkillUpdateErrorCode.ANCESTOR_UNSAFE, error.detail);
|
|
300
|
+
}
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** Root prefix a repository-relative install root records for this Skill ID. */
|
|
305
|
+
function rootPrefixOf(rootRel, skillId) {
|
|
306
|
+
return rootRel.slice(0, Math.max(0, rootRel.length - skillId.length - 1));
|
|
307
|
+
}
|
|
308
|
+
/** Remove the staging root once it is empty, so it does not linger in a worktree. */
|
|
309
|
+
function pruneStagingRoot(stagingRoot) {
|
|
310
|
+
try {
|
|
311
|
+
(0, fs_1.rmdirSync)(stagingRoot);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
// Still in use by a concurrent operation, or already gone.
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function removeQuietly(target) {
|
|
318
|
+
(0, fs_1.rmSync)(target, { recursive: true, force: true });
|
|
319
|
+
}
|
|
320
|
+
function isDirectory(target) {
|
|
321
|
+
try {
|
|
322
|
+
const stats = (0, fs_1.lstatSync)(target);
|
|
323
|
+
return stats.isDirectory() && !stats.isSymbolicLink();
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Stage, verify and atomically switch one Skill install to a new exact version.
|
|
331
|
+
*
|
|
332
|
+
* Order of operations, and what each failure means:
|
|
333
|
+
*
|
|
334
|
+
* 1. re-assess every recorded root (clean, digest- and hash-identical to the
|
|
335
|
+
* plan binding) — any finding is a zero-write refusal;
|
|
336
|
+
* 2. read and digest-verify the new payload from the snapshot — zero-write;
|
|
337
|
+
* 3. write the verified service-owned backup of the old payload — zero-write;
|
|
338
|
+
* 4. stage the new payload into every root's reserved namespace — zero-write
|
|
339
|
+
* (staging is removed on failure and the payload directories never moved);
|
|
340
|
+
* 5. switch the primary root: old aside, staged new in. The publish rename is
|
|
341
|
+
* the commit point; on a pre-publish failure the old directory is renamed
|
|
342
|
+
* back and the worktree is byte-for-byte what it was;
|
|
343
|
+
* 6. switch each secondary root the same way. A failure here leaves the
|
|
344
|
+
* operation *committed, reconciling* and the remaining roots to #1234.
|
|
345
|
+
*
|
|
346
|
+
* @throws SkillUpdateError — before the commit point the worktree is unchanged
|
|
347
|
+
* in every case except {@link SkillUpdateErrorCode.RESTORE_FAILED}, which is
|
|
348
|
+
* documented on the code itself.
|
|
349
|
+
*/
|
|
350
|
+
function applySkillUpdate(input) {
|
|
351
|
+
if (!install_apply_1.SKILL_INSTALL_OPERATION_ID_PATTERN.test(input.operationId)) {
|
|
352
|
+
fail(exports.SkillUpdateErrorCode.STAGING_IO, { reason: 'operation-id' });
|
|
353
|
+
}
|
|
354
|
+
if (!install_apply_1.SKILL_INSTALL_OPERATION_ID_PATTERN.test(skillUpdateStagingNameFor(input.operationId)) ||
|
|
355
|
+
!install_apply_1.SKILL_INSTALL_OPERATION_ID_PATTERN.test(skillUpdateAsideNameFor(input.operationId))) {
|
|
356
|
+
fail(exports.SkillUpdateErrorCode.STAGING_IO, { reason: 'operation-id-length' });
|
|
357
|
+
}
|
|
358
|
+
const storeOptions = input.stateRoot === undefined ? {} : { root: input.stateRoot };
|
|
359
|
+
const rootPrefixes = input.rootPrefixes && input.rootPrefixes.length > 0
|
|
360
|
+
? [...input.rootPrefixes]
|
|
361
|
+
: [constants_1.SKILL_INSTALL_ROOT_PREFIX];
|
|
362
|
+
// ---------------------------------------------------------------------------
|
|
363
|
+
// 1. Re-verify the old payload at every root, immediately before the switch.
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
const roots = [];
|
|
366
|
+
let fromVersion = null;
|
|
367
|
+
for (const prefix of rootPrefixes) {
|
|
368
|
+
const abs = resolveUpdateTargetFor(input.worktreePath, prefix, input.skillId);
|
|
369
|
+
assertUpdateAncestors(input.worktreeRealPath, input.worktreePath, prefix);
|
|
370
|
+
const existing = (0, preview_diff_1.readExistingSkillTree)(abs);
|
|
371
|
+
const assessment = (0, uninstall_plan_1.assessSkillUninstall)(abs, input.skillId, {
|
|
372
|
+
existing,
|
|
373
|
+
rootPrefix: prefix,
|
|
374
|
+
});
|
|
375
|
+
if (!assessment.present) {
|
|
376
|
+
fail(exports.SkillUpdateErrorCode.NOT_INSTALLED, { root: prefix });
|
|
377
|
+
}
|
|
378
|
+
// The uninstall guard is the strongest statement available about the old
|
|
379
|
+
// payload: every path recorded by the receipt and byte-identical to it.
|
|
380
|
+
// One finding and the update writes nothing (LOCAL_CHANGES fail-closed).
|
|
381
|
+
if (!assessment.removable || assessment.receipt === null) {
|
|
382
|
+
fail(exports.SkillUpdateErrorCode.LOCAL_CHANGES, undefined, assessment.blockers);
|
|
383
|
+
}
|
|
384
|
+
// Every root must still be exactly the install the plan was built against:
|
|
385
|
+
// the payload is byte-identical across roots (#1460), so each root must
|
|
386
|
+
// match the same receipt digest and tree hash the token was bound to.
|
|
387
|
+
if (assessment.receiptDigest === null ||
|
|
388
|
+
!(0, integrity_1.digestMatches)(assessment.receiptDigest, input.expectedReceiptDigest)) {
|
|
389
|
+
fail(exports.SkillUpdateErrorCode.DRIFT, { reason: 'receipt-digest', root: prefix });
|
|
390
|
+
}
|
|
391
|
+
if (assessment.currentTreeHash !== input.expectedTreeHash) {
|
|
392
|
+
fail(exports.SkillUpdateErrorCode.DRIFT, { reason: 'tree-hash', root: prefix });
|
|
393
|
+
}
|
|
394
|
+
fromVersion = assessment.receipt.version;
|
|
395
|
+
roots.push({ prefix, rel: (0, preview_diff_1.skillInstallRootFor)(prefix, input.skillId), abs, existing });
|
|
396
|
+
}
|
|
397
|
+
const primary = roots[0];
|
|
398
|
+
if (fromVersion === null) {
|
|
399
|
+
fail(exports.SkillUpdateErrorCode.NOT_INSTALLED);
|
|
400
|
+
}
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// 2. Read and prove the new payload once; stage the same bytes into each root.
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
const payload = input.snapshot.files.map((file) => {
|
|
405
|
+
const bytes = input.snapshot.readFile(file.path);
|
|
406
|
+
if (!(0, integrity_1.digestMatches)((0, integrity_1.computeSha256Hex)(bytes), file.sha256)) {
|
|
407
|
+
fail(exports.SkillUpdateErrorCode.PAYLOAD_MISMATCH, { reason: 'snapshot-digest' });
|
|
408
|
+
}
|
|
409
|
+
return { relativePath: file.path, bytes, executable: file.executable };
|
|
410
|
+
});
|
|
411
|
+
const directories = (0, install_apply_1.directoriesForSkillPayload)(input.snapshot.files.map((file) => file.path), input.snapshot.directories);
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
// 3. Verified service-owned backup of the old payload (#1245 boundary).
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
const backupManifest = createVerifiedSkillUpdateBackup({
|
|
416
|
+
backupId: input.operationId,
|
|
417
|
+
worktreeId: input.worktreeId,
|
|
418
|
+
skillId: input.skillId,
|
|
419
|
+
fromVersion,
|
|
420
|
+
toVersion: input.snapshot.version,
|
|
421
|
+
receiptDigest: input.expectedReceiptDigest,
|
|
422
|
+
treeHash: input.expectedTreeHash,
|
|
423
|
+
existing: primary.existing,
|
|
424
|
+
}, storeOptions);
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
// 4. Stage the new payload into every root before the primary commits, so a
|
|
427
|
+
// root that cannot even stage aborts the whole operation with zero writes.
|
|
428
|
+
// ---------------------------------------------------------------------------
|
|
429
|
+
const staged = new Map();
|
|
430
|
+
const stagingName = skillUpdateStagingNameFor(input.operationId);
|
|
431
|
+
try {
|
|
432
|
+
for (const root of roots) {
|
|
433
|
+
staged.set(root.prefix, (0, install_apply_1.stageSkillRootPayload)({
|
|
434
|
+
worktreePath: input.worktreePath,
|
|
435
|
+
worktreeRealPath: input.worktreeRealPath,
|
|
436
|
+
rootPrefix: root.prefix,
|
|
437
|
+
skillId: input.skillId,
|
|
438
|
+
stagingName,
|
|
439
|
+
payload,
|
|
440
|
+
directories,
|
|
441
|
+
receiptBytes: input.receiptBytes,
|
|
442
|
+
plannedTreeHash: input.plannedTreeHash,
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
for (const stagedRoot of staged.values()) {
|
|
448
|
+
removeQuietly(stagedRoot.stagingDir);
|
|
449
|
+
pruneStagingRoot(stagedRoot.stagingRoot);
|
|
450
|
+
}
|
|
451
|
+
translateStagingError(error);
|
|
452
|
+
}
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
// 5./6. Switch each root: old aside, staged new in. Primary publish = commit.
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
456
|
+
const committedRoots = [];
|
|
457
|
+
const pendingRoots = [];
|
|
458
|
+
let committed = false;
|
|
459
|
+
for (let index = 0; index < roots.length; index += 1) {
|
|
460
|
+
const root = roots[index];
|
|
461
|
+
const stagedRoot = staged.get(root.prefix);
|
|
462
|
+
const asideDir = path_1.default.join(stagedRoot.stagingRoot, skillUpdateAsideNameFor(input.operationId));
|
|
463
|
+
try {
|
|
464
|
+
switchSkillRoot({
|
|
465
|
+
root,
|
|
466
|
+
stagedRoot,
|
|
467
|
+
asideDir,
|
|
468
|
+
worktreeRealPath: input.worktreeRealPath,
|
|
469
|
+
worktreePath: input.worktreePath,
|
|
470
|
+
expectedReceiptDigest: input.expectedReceiptDigest,
|
|
471
|
+
expectedTreeHash: input.expectedTreeHash,
|
|
472
|
+
hooks: input.hooks,
|
|
473
|
+
});
|
|
474
|
+
committedRoots.push(root.rel);
|
|
475
|
+
if (index === 0) {
|
|
476
|
+
committed = true;
|
|
477
|
+
input.onCommitPoint?.();
|
|
478
|
+
}
|
|
479
|
+
input.hooks?.afterPublish?.(root.prefix);
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
if (!committed) {
|
|
483
|
+
// The primary never published: undo is complete (switchSkillRoot
|
|
484
|
+
// restored the aside directory or never moved it), so remove every
|
|
485
|
+
// root's staging and report an unchanged worktree.
|
|
486
|
+
for (const stagedEntry of staged.values()) {
|
|
487
|
+
removeQuietly(stagedEntry.stagingDir);
|
|
488
|
+
pruneStagingRoot(stagedEntry.stagingRoot);
|
|
489
|
+
}
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
// The primary committed. Whatever a secondary did, the operation is
|
|
493
|
+
// *committed, reconciling*: report every root that did not land and hand
|
|
494
|
+
// it to #1234 forward convergence. This root's own staging/aside
|
|
495
|
+
// leftovers are in the reserved namespace and are collected there.
|
|
496
|
+
for (let rest = index; rest < roots.length; rest += 1) {
|
|
497
|
+
if (!committedRoots.includes(roots[rest].rel))
|
|
498
|
+
pendingRoots.push(roots[rest].rel);
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
// The old directory of this root is switched out; the aside copy served
|
|
503
|
+
// its purpose (the durable copy lives in the service-owned backup).
|
|
504
|
+
removeQuietly(asideDir);
|
|
505
|
+
pruneStagingRoot(stagedRoot.stagingRoot);
|
|
506
|
+
}
|
|
507
|
+
const receiptSha256 = (0, integrity_1.computeSha256Hex)(input.receiptBytes);
|
|
508
|
+
return {
|
|
509
|
+
installRoot: primary.rel,
|
|
510
|
+
installRoots: roots.map((root) => root.rel),
|
|
511
|
+
committedRoots,
|
|
512
|
+
pendingRoots,
|
|
513
|
+
reconciling: pendingRoots.length > 0,
|
|
514
|
+
fromVersion,
|
|
515
|
+
toVersion: input.snapshot.version,
|
|
516
|
+
receiptPath: `${primary.rel}/${install_plan_1.SKILL_RECEIPT_FILENAME}`,
|
|
517
|
+
receiptSha256,
|
|
518
|
+
receiptSize: input.receiptBytes.byteLength,
|
|
519
|
+
files: input.snapshot.files.map((file) => ({
|
|
520
|
+
path: file.path,
|
|
521
|
+
sha256: file.sha256,
|
|
522
|
+
size: file.size,
|
|
523
|
+
executable: file.executable,
|
|
524
|
+
})),
|
|
525
|
+
treeHash: input.plannedTreeHash,
|
|
526
|
+
backup: {
|
|
527
|
+
backupId: backupManifest.backupId,
|
|
528
|
+
fromVersion: backupManifest.fromVersion,
|
|
529
|
+
fileCount: backupManifest.files.length,
|
|
530
|
+
verified: true,
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
/** One root's aside → publish switch, undone in-process when publish fails. */
|
|
535
|
+
function switchSkillRoot(input) {
|
|
536
|
+
const { root, stagedRoot, asideDir } = input;
|
|
537
|
+
// Re-checked immediately before the switch: the exclusive lock keeps
|
|
538
|
+
// CommandMate out, but nothing keeps the user's own tooling from swapping an
|
|
539
|
+
// ancestor or the payload while the staging was being written.
|
|
540
|
+
assertUpdateAncestors(input.worktreeRealPath, input.worktreePath, root.prefix);
|
|
541
|
+
const nowExisting = (0, preview_diff_1.readExistingSkillTree)(root.abs);
|
|
542
|
+
const receiptNow = nowExisting.files.find((file) => file.path === install_plan_1.SKILL_RECEIPT_FILENAME);
|
|
543
|
+
if (!nowExisting.present || receiptNow === undefined) {
|
|
544
|
+
fail(exports.SkillUpdateErrorCode.DRIFT, { reason: 'receipt-vanished', root: root.prefix });
|
|
545
|
+
}
|
|
546
|
+
if (!(0, integrity_1.digestMatches)(receiptNow.sha256, input.expectedReceiptDigest)) {
|
|
547
|
+
fail(exports.SkillUpdateErrorCode.DRIFT, { reason: 'receipt-digest', root: root.prefix });
|
|
548
|
+
}
|
|
549
|
+
if ((0, preview_diff_1.computeSkillTreeHash)(nowExisting.files) !== input.expectedTreeHash) {
|
|
550
|
+
fail(exports.SkillUpdateErrorCode.DRIFT, { reason: 'tree-hash', root: root.prefix });
|
|
551
|
+
}
|
|
552
|
+
if (isDirectory(asideDir)) {
|
|
553
|
+
fail(exports.SkillUpdateErrorCode.STAGING_IO, { reason: 'aside-occupied', root: root.prefix });
|
|
554
|
+
}
|
|
555
|
+
input.hooks?.beforeAside?.(root.prefix);
|
|
556
|
+
try {
|
|
557
|
+
(0, fs_1.renameSync)(root.abs, asideDir);
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
fail(exports.SkillUpdateErrorCode.COMMIT_FAILED, { reason: 'aside-rename', root: root.prefix });
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
input.hooks?.beforePublish?.(root.prefix);
|
|
564
|
+
(0, fs_1.renameSync)(stagedRoot.stagingDir, root.abs);
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
// The destination slot is empty and the old directory is intact in the
|
|
568
|
+
// aside location: renaming it back restores the exact previous state.
|
|
569
|
+
try {
|
|
570
|
+
(0, fs_1.renameSync)(asideDir, root.abs);
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
fail(exports.SkillUpdateErrorCode.RESTORE_FAILED, { root: root.prefix });
|
|
574
|
+
}
|
|
575
|
+
if (isSkillUpdateError(error))
|
|
576
|
+
throw error;
|
|
577
|
+
fail(exports.SkillUpdateErrorCode.COMMIT_FAILED, { reason: 'publish-rename', root: root.prefix });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Converge an interrupted update's primary root to old-complete or new-complete.
|
|
582
|
+
*
|
|
583
|
+
* The filesystem question #1234 reconciliation asks of an `update` entry —
|
|
584
|
+
* *did the switch land?* — with the convergence the two-rename sequence makes
|
|
585
|
+
* necessary: a crash can leave the root empty with the old payload in the
|
|
586
|
+
* aside directory, or leave a fully verified staged payload unpublished. This
|
|
587
|
+
* routine restores the former and adopts the latter, so by the time it
|
|
588
|
+
* answers, the root holds exactly one complete version and never a mixture.
|
|
589
|
+
*
|
|
590
|
+
* The new payload is recognised by the journal's own claim: a receipt whose
|
|
591
|
+
* version equals the entry's target version and whose digest matches the
|
|
592
|
+
* recorded one when there is one. An adoption additionally re-proves every
|
|
593
|
+
* staged byte against the staged receipt before the rename — a half-written
|
|
594
|
+
* staging directory is removed, not published.
|
|
595
|
+
*/
|
|
596
|
+
function reconcileSkillUpdateTarget(worktreePath, skillId, claim) {
|
|
597
|
+
if (!constants_1.SKILL_ID_PATTERN.test(skillId) ||
|
|
598
|
+
skillId.length > constants_1.SKILL_ID_MAX_LENGTH ||
|
|
599
|
+
!install_apply_1.SKILL_INSTALL_OPERATION_ID_PATTERN.test(claim.operationId)) {
|
|
600
|
+
return { committed: false, action: 'unrecoverable' };
|
|
601
|
+
}
|
|
602
|
+
let rootAbs;
|
|
603
|
+
try {
|
|
604
|
+
rootAbs = (0, preview_diff_1.resolveSkillInstallRootFor)(worktreePath, constants_1.SKILL_INSTALL_ROOT_PREFIX, skillId);
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
return { committed: false, action: 'unrecoverable' };
|
|
608
|
+
}
|
|
609
|
+
const stagingRoot = (0, operation_store_1.getSkillInstallStagingRoot)(worktreePath, constants_1.SKILL_INSTALL_ROOT_PREFIX);
|
|
610
|
+
const asideDir = path_1.default.join(stagingRoot, skillUpdateAsideNameFor(claim.operationId));
|
|
611
|
+
const stagedDir = path_1.default.join(stagingRoot, skillUpdateStagingNameFor(claim.operationId));
|
|
612
|
+
const matchesClaim = (bytes) => {
|
|
613
|
+
const receipt = (0, install_plan_1.parseInstalledReceipt)(bytes);
|
|
614
|
+
if (receipt === null || receipt.skill_id !== skillId)
|
|
615
|
+
return false;
|
|
616
|
+
if (claim.toVersion !== null && receipt.version !== claim.toVersion)
|
|
617
|
+
return false;
|
|
618
|
+
return (claim.receiptDigest === null || (0, integrity_1.digestMatches)((0, integrity_1.computeSha256Hex)(bytes), claim.receiptDigest));
|
|
619
|
+
};
|
|
620
|
+
if (isDirectory(rootAbs)) {
|
|
621
|
+
// The root is occupied: whichever version it holds, no restore is needed
|
|
622
|
+
// and the operation's leftovers in the reserved namespace are collectable.
|
|
623
|
+
let committed = false;
|
|
624
|
+
try {
|
|
625
|
+
committed = matchesClaim((0, fs_1.readFileSync)(path_1.default.join(rootAbs, install_plan_1.SKILL_RECEIPT_FILENAME)));
|
|
626
|
+
}
|
|
627
|
+
catch {
|
|
628
|
+
committed = false;
|
|
629
|
+
}
|
|
630
|
+
removeQuietly(asideDir);
|
|
631
|
+
removeQuietly(stagedDir);
|
|
632
|
+
pruneStagingRoot(stagingRoot);
|
|
633
|
+
return { committed, action: committed ? 'committed' : 'rolled_back' };
|
|
634
|
+
}
|
|
635
|
+
// The root is empty: the crash hit between the aside and publish renames.
|
|
636
|
+
// Restoring the old payload wins over adopting the new one — the journal
|
|
637
|
+
// never recorded a commit for this shape, so the honest convergence is the
|
|
638
|
+
// one that makes "nothing was committed" true again.
|
|
639
|
+
if (isDirectory(asideDir)) {
|
|
640
|
+
let asideReceipt = null;
|
|
641
|
+
try {
|
|
642
|
+
asideReceipt = (0, fs_1.readFileSync)(path_1.default.join(asideDir, install_plan_1.SKILL_RECEIPT_FILENAME));
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
asideReceipt = null;
|
|
646
|
+
}
|
|
647
|
+
const parsed = asideReceipt === null ? null : (0, install_plan_1.parseInstalledReceipt)(asideReceipt);
|
|
648
|
+
if (parsed !== null && parsed.skill_id === skillId) {
|
|
649
|
+
try {
|
|
650
|
+
(0, fs_1.renameSync)(asideDir, rootAbs);
|
|
651
|
+
}
|
|
652
|
+
catch {
|
|
653
|
+
return { committed: false, action: 'unrecoverable' };
|
|
654
|
+
}
|
|
655
|
+
removeQuietly(stagedDir);
|
|
656
|
+
pruneStagingRoot(stagingRoot);
|
|
657
|
+
return { committed: false, action: 'restored_old' };
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (isDirectory(stagedDir)) {
|
|
661
|
+
if (verifyStagedUpdatePayload(stagedDir, skillId, claim)) {
|
|
662
|
+
try {
|
|
663
|
+
(0, fs_1.renameSync)(stagedDir, rootAbs);
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
return { committed: false, action: 'unrecoverable' };
|
|
667
|
+
}
|
|
668
|
+
removeQuietly(asideDir);
|
|
669
|
+
pruneStagingRoot(stagingRoot);
|
|
670
|
+
return { committed: true, action: 'adopted_new' };
|
|
671
|
+
}
|
|
672
|
+
// A staging directory that does not prove itself is garbage, never payload.
|
|
673
|
+
removeQuietly(stagedDir);
|
|
674
|
+
pruneStagingRoot(stagingRoot);
|
|
675
|
+
}
|
|
676
|
+
return { committed: false, action: 'unrecoverable' };
|
|
677
|
+
}
|
|
678
|
+
/** Every staged byte must match the staged receipt, and the receipt the claim. */
|
|
679
|
+
function verifyStagedUpdatePayload(stagedDir, skillId, claim) {
|
|
680
|
+
let receiptBytes;
|
|
681
|
+
try {
|
|
682
|
+
receiptBytes = (0, fs_1.readFileSync)(path_1.default.join(stagedDir, install_plan_1.SKILL_RECEIPT_FILENAME));
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
const receipt = (0, install_plan_1.parseInstalledReceipt)(receiptBytes);
|
|
688
|
+
if (receipt === null || receipt.skill_id !== skillId)
|
|
689
|
+
return false;
|
|
690
|
+
if (claim.toVersion !== null && receipt.version !== claim.toVersion)
|
|
691
|
+
return false;
|
|
692
|
+
if (claim.receiptDigest !== null &&
|
|
693
|
+
!(0, integrity_1.digestMatches)((0, integrity_1.computeSha256Hex)(receiptBytes), claim.receiptDigest)) {
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
// The whole staged tree must be exactly the receipt's inventory plus the
|
|
697
|
+
// receipt itself: extra files, missing files or drifted bytes all disqualify.
|
|
698
|
+
const staged = (0, preview_diff_1.readExistingSkillTree)(stagedDir);
|
|
699
|
+
if (!staged.present || staged.truncated || staged.irregularPaths.length > 0)
|
|
700
|
+
return false;
|
|
701
|
+
const expected = (0, preview_diff_1.computeSkillTreeHash)([
|
|
702
|
+
...receipt.files.map((file) => ({
|
|
703
|
+
path: file.path,
|
|
704
|
+
sha256: file.sha256,
|
|
705
|
+
executable: file.executable,
|
|
706
|
+
})),
|
|
707
|
+
{
|
|
708
|
+
path: install_plan_1.SKILL_RECEIPT_FILENAME,
|
|
709
|
+
sha256: (0, integrity_1.computeSha256Hex)(receiptBytes),
|
|
710
|
+
executable: false,
|
|
711
|
+
},
|
|
712
|
+
]);
|
|
713
|
+
return (0, preview_diff_1.computeSkillTreeHash)(staged.files) === expected;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Converge a committed update's secondary roots forward from the primary (#1460).
|
|
717
|
+
*
|
|
718
|
+
* The update counterpart of {@link completeSecondarySkillInstallRoots}: after
|
|
719
|
+
* the primary root committed, a secondary root may still hold the *previous*
|
|
720
|
+
* version. A secondary that is a provably clean managed install (uninstall
|
|
721
|
+
* guard, per-file digests) is switched with the same aside → publish sequence
|
|
722
|
+
* apply uses; a root with any local finding is left alone and reported, so
|
|
723
|
+
* convergence never overwrites what it cannot prove. Absent roots are filled
|
|
724
|
+
* by the install-side converger. Idempotent.
|
|
725
|
+
*/
|
|
726
|
+
function completeSecondarySkillUpdateRoots(worktreePath, worktreeRealPath, skillId) {
|
|
727
|
+
const primaryAbs = resolveUpdateTargetFor(worktreePath, constants_1.SKILL_INSTALL_ROOT_PREFIX, skillId);
|
|
728
|
+
const primaryReceiptBytes = (0, fs_1.readFileSync)(path_1.default.join(primaryAbs, install_plan_1.SKILL_RECEIPT_FILENAME));
|
|
729
|
+
const primaryReceipt = (0, install_plan_1.parseInstalledReceipt)(primaryReceiptBytes);
|
|
730
|
+
if (primaryReceipt === null) {
|
|
731
|
+
fail(exports.SkillUpdateErrorCode.PAYLOAD_MISMATCH, { reason: 'primary-receipt-unreadable' });
|
|
732
|
+
}
|
|
733
|
+
const primaryDigest = (0, integrity_1.computeSha256Hex)(primaryReceiptBytes);
|
|
734
|
+
// Read the primary payload from disk and prove it against the receipt before
|
|
735
|
+
// it is copied anywhere: a copy of a tampered primary must not propagate.
|
|
736
|
+
const payload = primaryReceipt.files.map((file) => {
|
|
737
|
+
const bytes = (0, fs_1.readFileSync)(path_1.default.join(primaryAbs, file.path));
|
|
738
|
+
if (!(0, integrity_1.digestMatches)((0, integrity_1.computeSha256Hex)(bytes), file.sha256)) {
|
|
739
|
+
fail(exports.SkillUpdateErrorCode.PAYLOAD_MISMATCH, { reason: 'primary-file-digest' });
|
|
740
|
+
}
|
|
741
|
+
return { relativePath: file.path, bytes, executable: file.executable };
|
|
742
|
+
});
|
|
743
|
+
const directories = (0, install_apply_1.directoriesForSkillPayload)(primaryReceipt.files.map((file) => file.path));
|
|
744
|
+
const plannedTreeHash = (0, preview_diff_1.computeSkillTreeHash)([
|
|
745
|
+
...primaryReceipt.files.map((file) => ({
|
|
746
|
+
path: file.path,
|
|
747
|
+
sha256: file.sha256,
|
|
748
|
+
executable: file.executable,
|
|
749
|
+
})),
|
|
750
|
+
{ path: install_plan_1.SKILL_RECEIPT_FILENAME, sha256: primaryDigest, executable: false },
|
|
751
|
+
]);
|
|
752
|
+
const completed = [];
|
|
753
|
+
const skipped = [];
|
|
754
|
+
const convergeId = (0, crypto_1.randomBytes)(16).toString('hex');
|
|
755
|
+
for (const rootRel of (0, install_plan_1.receiptInstallRoots)(primaryReceipt)) {
|
|
756
|
+
const prefix = rootPrefixOf(rootRel, skillId);
|
|
757
|
+
if (prefix === constants_1.SKILL_INSTALL_ROOT_PREFIX)
|
|
758
|
+
continue;
|
|
759
|
+
let abs;
|
|
760
|
+
try {
|
|
761
|
+
abs = (0, install_apply_1.resolveSkillInstallTargetFor)(worktreePath, prefix, skillId);
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
skipped.push(rootRel);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
// Absent roots are the install-side convergence below; only an occupied
|
|
768
|
+
// root that still holds a different receipt is update's own case.
|
|
769
|
+
if (!(0, install_apply_1.inspectSkillDestination)(abs).present)
|
|
770
|
+
continue;
|
|
771
|
+
const assessment = (0, uninstall_plan_1.assessSkillUninstall)(abs, skillId, { rootPrefix: prefix });
|
|
772
|
+
if (assessment.receiptDigest !== null && (0, integrity_1.digestMatches)(assessment.receiptDigest, primaryDigest)) {
|
|
773
|
+
continue; // Already the new payload.
|
|
774
|
+
}
|
|
775
|
+
if (!assessment.removable) {
|
|
776
|
+
skipped.push(rootRel);
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
let stagedRoot;
|
|
780
|
+
try {
|
|
781
|
+
stagedRoot = (0, install_apply_1.stageSkillRootPayload)({
|
|
782
|
+
worktreePath,
|
|
783
|
+
worktreeRealPath,
|
|
784
|
+
rootPrefix: prefix,
|
|
785
|
+
skillId,
|
|
786
|
+
stagingName: skillUpdateStagingNameFor(convergeId),
|
|
787
|
+
payload,
|
|
788
|
+
directories,
|
|
789
|
+
receiptBytes: primaryReceiptBytes,
|
|
790
|
+
plannedTreeHash,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
catch {
|
|
794
|
+
skipped.push(rootRel);
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
const asideDir = path_1.default.join(stagedRoot.stagingRoot, skillUpdateAsideNameFor(convergeId));
|
|
798
|
+
try {
|
|
799
|
+
(0, fs_1.renameSync)(abs, asideDir);
|
|
800
|
+
}
|
|
801
|
+
catch {
|
|
802
|
+
removeQuietly(stagedRoot.stagingDir);
|
|
803
|
+
pruneStagingRoot(stagedRoot.stagingRoot);
|
|
804
|
+
skipped.push(rootRel);
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
try {
|
|
808
|
+
(0, fs_1.renameSync)(stagedRoot.stagingDir, abs);
|
|
809
|
+
}
|
|
810
|
+
catch {
|
|
811
|
+
try {
|
|
812
|
+
(0, fs_1.renameSync)(asideDir, abs);
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
// The aside directory keeps the old payload adjacent to the root; the
|
|
816
|
+
// next convergence pass retries from this exact shape.
|
|
817
|
+
}
|
|
818
|
+
removeQuietly(stagedRoot.stagingDir);
|
|
819
|
+
pruneStagingRoot(stagedRoot.stagingRoot);
|
|
820
|
+
skipped.push(rootRel);
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
removeQuietly(asideDir);
|
|
824
|
+
pruneStagingRoot(stagedRoot.stagingRoot);
|
|
825
|
+
completed.push(rootRel);
|
|
826
|
+
}
|
|
827
|
+
const filled = (0, install_apply_1.completeSecondarySkillInstallRoots)(worktreePath, worktreeRealPath, skillId);
|
|
828
|
+
completed.push(...filled.completed);
|
|
829
|
+
return { completed, skipped };
|
|
830
|
+
}
|