commandmate 0.11.4 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +406 -314
- package/.next/app-path-routes-manifest.json +35 -26
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +15 -15
- package/.next/react-loadable-manifest.json +8 -6
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +66 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- 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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route.js +1 -1
- package/.next/server/app/api/daily-summary/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/daily-summary/status/route.js.nft.json +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/hooks/claude-done/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_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/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route.js +1 -0
- package/.next/server/app/api/skills/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/route.js +1 -0
- package/.next/server/app/api/skills/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/slash-commands/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/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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route.js.nft.json +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.js.nft.json +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.js.nft.json +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.js.nft.json +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.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route.js.nft.json +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.js.nft.json +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.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.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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.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.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.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]/install/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/route.js +23 -0
- package/.next/server/app/api/worktrees/[id]/skills/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -0
- 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]/terminal/route.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]/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.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.js.nft.json +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.js.nft.json +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 +1 -1
- 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 -0
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -0
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -0
- package/.next/server/app/skills/page.js +2 -0
- package/.next/server/app/skills/page.js.nft.json +1 -0
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -0
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +2 -2
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +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 +9 -9
- 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.js +3 -3
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +35 -26
- package/.next/server/chunks/1117.js +1 -1
- package/.next/server/chunks/120.js +1 -0
- package/.next/server/chunks/1808.js +1 -0
- package/.next/server/chunks/186.js +1 -0
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/22.js +1 -0
- package/.next/server/chunks/2328.js +1 -0
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3042.js +2 -0
- package/.next/server/chunks/3543.js +1 -1
- package/.next/server/chunks/3708.js +1 -0
- package/.next/server/chunks/3945.js +1 -1
- package/.next/server/chunks/5064.js +1 -0
- package/.next/server/chunks/5226.js +28 -0
- package/.next/server/chunks/5306.js +2 -0
- package/.next/server/chunks/5312.js +1 -0
- package/.next/server/chunks/5326.js +1 -0
- package/.next/server/chunks/5381.js +1 -1
- package/.next/server/chunks/5456.js +1 -0
- package/.next/server/chunks/5571.js +1 -1
- package/.next/server/chunks/5959.js +3 -3
- package/.next/server/chunks/6065.js +1 -0
- package/.next/server/chunks/6130.js +1 -0
- package/.next/server/chunks/6283.js +1 -1
- package/.next/server/chunks/6285.js +1 -1
- package/.next/server/chunks/646.js +6 -6
- package/.next/server/chunks/{9968.js → 7587.js} +1 -1
- package/.next/server/chunks/8584.js +1 -0
- package/.next/server/chunks/8681.js +1 -1
- package/.next/server/chunks/8807.js +950 -804
- package/.next/server/chunks/9123.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/ZYti9g9gICKnjtmkfvvB2/_buildManifest.js +1 -0
- package/.next/static/chunks/1668-7484081c45a44f89.js +1 -0
- package/.next/static/chunks/3418-7e47651340d940b0.js +1 -0
- package/.next/static/chunks/4027-0c529b322be7d4d8.js +1 -0
- package/.next/static/chunks/{4560-64b1461fd996127a.js → 4560-04e5aebcab96bfa1.js} +1 -1
- package/.next/static/chunks/6007-de36119357dcd418.js +1 -0
- package/.next/static/chunks/{6488-76295bf0184b101f.js → 6488-624d4b6d2d4cc236.js} +1 -1
- package/.next/static/chunks/{8965-872d8de254196cec.js → 6497-dc26c6c6b0b15adf.js} +1 -1
- package/.next/static/chunks/790-d8e6acd26e03679c.js +1 -0
- package/.next/static/chunks/8361.4bf062be2d2962f4.js +1 -0
- package/.next/static/chunks/app/api/app/update/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/app/update-check/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/conversation/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/current-output/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/messages/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/session/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/start/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/terminal/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/assistant/tools/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/auth/login/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/auth/status/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/daily-summary/status/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/external-apps/[id]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/external-apps/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/hooks/claude-done/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/ollama/models/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/push/subscriptions/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/push/vapid/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/clone/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/excluded/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/restore/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/scan/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/repositories/sync/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/sidebar/group-order/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/skills/[id]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/skills/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/slash-commands/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/templates/[id]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/templates/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/todos/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/install/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/plan/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/skills/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/chat/{page-9ed2ffd98eb4ae18.js → page-9cf4ba62e21d9074.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/more/{page-a496425bba427b48.js → page-082d4ae496f9a36e.js} +1 -1
- package/.next/static/chunks/app/{page-15bfd76bcf65842d.js → page-af457ca5876ed616.js} +1 -1
- package/.next/static/chunks/app/proxy/[...path]/route-56dad0523d3c4a92.js +1 -0
- package/.next/static/chunks/app/repositories/{page-d214522ab8792ce7.js → page-75f0d70452c6b6c0.js} +1 -1
- package/.next/static/chunks/app/review/{page-1dadf0b5d7129598.js → page-f431d630097c2e85.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-96f007d986c5f327.js → page-20f07a99db64d2ca.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/page-68ed5e9186efe56d.js +1 -0
- package/.next/static/chunks/app/skills/page-46242c428ee8bccf.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-7dd929bfc978742e.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-a75fe180a7233b2b.js +1 -0
- package/.next/static/chunks/webpack-0f9275e279c058eb.js +1 -0
- package/.next/static/css/b4bdb1f3457fe9a7.css +1 -0
- package/.next/trace +11 -10
- package/.next/types/app/api/skills/[id]/route.ts +347 -0
- package/.next/types/app/api/skills/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/install/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/plan/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/uninstall/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route.ts +347 -0
- package/.next/types/app/api/worktrees/[id]/skills/route.ts +347 -0
- package/.next/types/app/skills/[skillId]/page.ts +84 -0
- package/.next/types/app/skills/page.ts +84 -0
- package/.next/types/routes.d.ts +11 -2
- package/.next/types/validator.ts +81 -0
- package/dist/cli/commands/skill-format.d.ts +28 -0
- package/dist/cli/commands/skill-format.d.ts.map +1 -0
- package/dist/cli/commands/skill-format.js +163 -0
- package/dist/cli/commands/skill-guards.d.ts +53 -0
- package/dist/cli/commands/skill-guards.d.ts.map +1 -0
- package/dist/cli/commands/skill-guards.js +188 -0
- package/dist/cli/commands/skill.d.ts +24 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +365 -0
- 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 +36 -0
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +11 -0
- package/dist/cli/types/api-responses.d.ts +215 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +60 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/types/index.js +23 -1
- package/dist/cli/utils/api-client.d.ts +19 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +41 -4
- package/dist/server/server.js +57 -0
- package/dist/server/src/config/activity-bar-config.js +2 -0
- package/dist/server/src/config/git-status-config.js +83 -0
- package/dist/server/src/config/skill-security-config.js +139 -0
- package/dist/server/src/lib/cli-tools/antigravity.js +8 -13
- package/dist/server/src/lib/cli-tools/codex.js +8 -14
- package/dist/server/src/lib/cli-tools/copilot.js +12 -13
- package/dist/server/src/lib/cli-tools/gemini.js +8 -13
- package/dist/server/src/lib/cli-tools/opencode.js +8 -13
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +174 -0
- package/dist/server/src/lib/cli-tools/vibe-local.js +12 -17
- package/dist/server/src/lib/db/migrations/index.js +8 -0
- package/dist/server/src/lib/db/migrations/runner.js +1 -1
- package/dist/server/src/lib/db/migrations/v44-skill-operations.js +81 -0
- package/dist/server/src/lib/db/migrations/v45-skill-installations.js +59 -0
- package/dist/server/src/lib/db/migrations/v46-skill-installations-cascade.js +107 -0
- package/dist/server/src/lib/db/migrations/v47-skill-installations-roots.js +70 -0
- package/dist/server/src/lib/db/worktree-db.js +9 -4
- package/dist/server/src/lib/git/git-errors.js +336 -0
- package/dist/server/src/lib/git/git-exec.js +285 -0
- package/dist/server/src/lib/session-key-sender.js +11 -9
- package/dist/server/src/lib/skills/compatibility.js +194 -0
- package/dist/server/src/lib/skills/constants.js +252 -0
- package/dist/server/src/lib/skills/errors.js +82 -0
- package/dist/server/src/lib/skills/index.js +91 -0
- package/dist/server/src/lib/skills/install-apply.js +670 -0
- package/dist/server/src/lib/skills/install-plan.js +582 -0
- package/dist/server/src/lib/skills/installed-state.js +127 -0
- package/dist/server/src/lib/skills/integrity.js +225 -0
- package/dist/server/src/lib/skills/json-schema.js +365 -0
- package/dist/server/src/lib/skills/operation-audit.js +120 -0
- package/dist/server/src/lib/skills/operation-journal.js +286 -0
- package/dist/server/src/lib/skills/operation-lock.js +339 -0
- package/dist/server/src/lib/skills/operation-reconciler.js +107 -0
- package/dist/server/src/lib/skills/operation-store.js +153 -0
- package/dist/server/src/lib/skills/package-reader.js +495 -0
- package/dist/server/src/lib/skills/package-validator.js +451 -0
- package/dist/server/src/lib/skills/preview-diff.js +627 -0
- package/dist/server/src/lib/skills/safe-yaml.js +593 -0
- package/dist/server/src/lib/skills/schema.js +1248 -0
- package/dist/server/src/lib/skills/semver.js +193 -0
- package/dist/server/src/lib/skills/snapshot-store.js +334 -0
- package/dist/server/src/lib/skills/startup-reconcile.js +138 -0
- package/dist/server/src/lib/skills/uninstall-apply.js +495 -0
- package/dist/server/src/lib/skills/uninstall-plan.js +479 -0
- package/dist/server/src/types/skills.js +17 -0
- package/package.json +1 -1
- package/.next/server/chunks/129.js +0 -1
- package/.next/server/chunks/2478.js +0 -1
- package/.next/server/chunks/9886.js +0 -1
- package/.next/static/0_lnKsT0DH-MnHhnV5n-l/_buildManifest.js +0 -1
- package/.next/static/chunks/3165-87bf884f7f3d15cf.js +0 -1
- package/.next/static/chunks/6416.6756b7ad06dd96f7.js +0 -1
- package/.next/static/chunks/8676.57260a06f6554e7e.js +0 -1
- package/.next/static/chunks/app/api/app/update/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/app/update-check/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/conversation/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/current-output/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/messages/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/session/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/start/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/terminal/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/assistant/tools/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/auth/login/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/auth/status/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/daily-summary/status/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/health/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/external-apps/[id]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/external-apps/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/hooks/claude-done/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/ollama/models/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/push/subscriptions/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/push/vapid/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/[todoId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/[id]/todos/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/[jobId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/clone/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/excluded/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/restore/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/scan/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/repositories/sync/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/sidebar/group-order/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/slash-commands/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/templates/[id]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/templates/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/todos/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/auto-yes/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/capture/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cli-tool/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/cmate/schedules/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/current-output/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/[logId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/execution-logs/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/files/[...path]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/create/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branch/delete/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/branches/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/checkout/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/commit/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/diff/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/fetch/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/log/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/pull/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/push/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/reset/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/revert/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/show/[commitHash]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stage/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/staged/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/[index]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/apply/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/pop/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/push/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/stash/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/status/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/unstage/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/git/working-diff/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/interrupt/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/kill-session/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/[filename]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/logs/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/marp-render/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/[memoId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/memos/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/messages/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/prompt-response/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/respond/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/[scheduleId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/active/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/schedules/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/search/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/send/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/slash-commands/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/special-keys/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/start-polling/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/terminal/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/history/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/timers/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/[todoId]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/todos/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/[...path]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/tree/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/upload/[...path]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/[id]/viewed/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/proxy/[...path]/route-7536041a7a89616d.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-9a4ce51e697baac6.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-ed4590b024edd8d6.js +0 -1
- package/.next/static/chunks/webpack-2e3739a831868daa.js +0 -1
- package/.next/static/css/d7e2e8d1b8d69d36.css +0 -1
- package/dist/server/src/lib/pasted-text-helper.js +0 -58
- /package/.next/static/{0_lnKsT0DH-MnHhnV5n-l → ZYti9g9gICKnjtmkfvvB2}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Operation journal and idempotency for Skill operations (Issue #1234)
|
|
4
|
+
*
|
|
5
|
+
* The journal is the crash-recovery record that lets install/uninstall converge
|
|
6
|
+
* on one answer after a restart. Its central rule is that the **filesystem
|
|
7
|
+
* atomic rename is the commit point**: once an operation reaches FS_COMMITTED,
|
|
8
|
+
* the payload exists and a later DB or index failure must not be reported as a
|
|
9
|
+
* rollback. Such an operation moves to FAILED_RECONCILABLE and is driven
|
|
10
|
+
* forward from the receipt, never backwards.
|
|
11
|
+
*
|
|
12
|
+
* ```text
|
|
13
|
+
* PREPARING ──▶ FS_COMMITTED ──▶ INDEXED ──▶ SUCCEEDED
|
|
14
|
+
* │ │ │
|
|
15
|
+
* └──────────────┴──────────────┴──▶ FAILED_RECONCILABLE ──▶ INDEXED/SUCCEEDED
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The reverse edge out of FAILED_RECONCILABLE is guarded: it is only legal when
|
|
19
|
+
* the entry recorded a filesystem commit. A failure before the commit point is
|
|
20
|
+
* genuinely rolled back and stays terminal.
|
|
21
|
+
*
|
|
22
|
+
* Idempotency keys are bound to actor, operation, target and plan. Replaying a
|
|
23
|
+
* request returns the recorded entry instead of writing the payload twice; the
|
|
24
|
+
* *same* key with a *different* binding is a conflict rather than a silent
|
|
25
|
+
* substitution of someone else's plan.
|
|
26
|
+
*
|
|
27
|
+
* @module lib/skills/operation-journal
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.SKILL_JOURNAL_RETENTION_MS = exports.SkillOperationTransitionError = exports.SKILL_OPERATION_STATES = void 0;
|
|
31
|
+
exports.computeSkillOperationBindingHash = computeSkillOperationBindingHash;
|
|
32
|
+
exports.deriveSkillOperationIdempotencyKey = deriveSkillOperationIdempotencyKey;
|
|
33
|
+
exports.readSkillOperationJournal = readSkillOperationJournal;
|
|
34
|
+
exports.listSkillOperationJournal = listSkillOperationJournal;
|
|
35
|
+
exports.writeSkillOperationJournal = writeSkillOperationJournal;
|
|
36
|
+
exports.deleteSkillOperationJournal = deleteSkillOperationJournal;
|
|
37
|
+
exports.pruneExpiredSkillOperationJournal = pruneExpiredSkillOperationJournal;
|
|
38
|
+
exports.beginSkillOperation = beginSkillOperation;
|
|
39
|
+
exports.transitionSkillOperation = transitionSkillOperation;
|
|
40
|
+
exports.hasSkillFilesystemCommit = hasSkillFilesystemCommit;
|
|
41
|
+
exports.isSkillOperationTerminal = isSkillOperationTerminal;
|
|
42
|
+
const fs_1 = require("fs");
|
|
43
|
+
const path_1 = require("path");
|
|
44
|
+
const crypto_1 = require("crypto");
|
|
45
|
+
const operation_store_1 = require("../../lib/skills/operation-store");
|
|
46
|
+
/**
|
|
47
|
+
* Journal states.
|
|
48
|
+
*
|
|
49
|
+
* Local to this module rather than added to the #1228 contract: these describe
|
|
50
|
+
* CommandMate's internal execution, not the published distribution documents.
|
|
51
|
+
*/
|
|
52
|
+
exports.SKILL_OPERATION_STATES = [
|
|
53
|
+
'PREPARING',
|
|
54
|
+
'FS_COMMITTED',
|
|
55
|
+
'INDEXED',
|
|
56
|
+
'SUCCEEDED',
|
|
57
|
+
'FAILED_RECONCILABLE',
|
|
58
|
+
];
|
|
59
|
+
/** Legal forward edges. Anything else is a programming error. */
|
|
60
|
+
const ALLOWED_TRANSITIONS = {
|
|
61
|
+
PREPARING: ['FS_COMMITTED', 'FAILED_RECONCILABLE'],
|
|
62
|
+
FS_COMMITTED: ['INDEXED', 'FAILED_RECONCILABLE'],
|
|
63
|
+
INDEXED: ['SUCCEEDED', 'FAILED_RECONCILABLE'],
|
|
64
|
+
FAILED_RECONCILABLE: ['INDEXED', 'SUCCEEDED', 'FAILED_RECONCILABLE'],
|
|
65
|
+
SUCCEEDED: [],
|
|
66
|
+
};
|
|
67
|
+
/** Raised on an illegal state transition. */
|
|
68
|
+
class SkillOperationTransitionError extends Error {
|
|
69
|
+
from;
|
|
70
|
+
to;
|
|
71
|
+
constructor(from, to, reason) {
|
|
72
|
+
super(`Illegal Skill operation transition ${from} -> ${to}: ${reason}`);
|
|
73
|
+
this.from = from;
|
|
74
|
+
this.to = to;
|
|
75
|
+
this.name = 'SkillOperationTransitionError';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.SkillOperationTransitionError = SkillOperationTransitionError;
|
|
79
|
+
function canonicalJson(value) {
|
|
80
|
+
if (value === null || typeof value !== 'object')
|
|
81
|
+
return JSON.stringify(value) ?? 'null';
|
|
82
|
+
if (Array.isArray(value))
|
|
83
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
84
|
+
const entries = Object.entries(value)
|
|
85
|
+
.filter(([, v]) => v !== undefined)
|
|
86
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
87
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Digest of everything an idempotency key is bound to.
|
|
91
|
+
*
|
|
92
|
+
* Two requests may only share a key when they mean the same thing: same actor,
|
|
93
|
+
* same operation, same target and same resolved plan.
|
|
94
|
+
*/
|
|
95
|
+
function computeSkillOperationBindingHash(binding) {
|
|
96
|
+
return (0, crypto_1.createHash)('sha256')
|
|
97
|
+
.update(canonicalJson({
|
|
98
|
+
actor: binding.actor,
|
|
99
|
+
operation: binding.operation,
|
|
100
|
+
target: binding.target,
|
|
101
|
+
planHash: binding.planHash,
|
|
102
|
+
}))
|
|
103
|
+
.digest('hex');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Derive an idempotency key when the client did not supply one.
|
|
107
|
+
* Identical requests then collapse onto one operation by construction.
|
|
108
|
+
*/
|
|
109
|
+
function deriveSkillOperationIdempotencyKey(binding) {
|
|
110
|
+
return computeSkillOperationBindingHash(binding);
|
|
111
|
+
}
|
|
112
|
+
function getJournalDir(options) {
|
|
113
|
+
return (0, operation_store_1.ensureSkillStateDir)(operation_store_1.SKILL_JOURNAL_DIRNAME, options);
|
|
114
|
+
}
|
|
115
|
+
/** Journal filename. The key is hashed so a client-supplied string never names a file. */
|
|
116
|
+
function getJournalPath(idempotencyKey, options) {
|
|
117
|
+
const digest = (0, crypto_1.createHash)('sha256').update(idempotencyKey).digest('hex');
|
|
118
|
+
return (0, path_1.join)(getJournalDir(options), `${digest}.json`);
|
|
119
|
+
}
|
|
120
|
+
function isJournalEntry(value) {
|
|
121
|
+
if (typeof value !== 'object' || value === null)
|
|
122
|
+
return false;
|
|
123
|
+
const entry = value;
|
|
124
|
+
return (typeof entry.operationId === 'string' &&
|
|
125
|
+
typeof entry.idempotencyKey === 'string' &&
|
|
126
|
+
typeof entry.bindingHash === 'string' &&
|
|
127
|
+
typeof entry.state === 'string' &&
|
|
128
|
+
exports.SKILL_OPERATION_STATES.includes(entry.state));
|
|
129
|
+
}
|
|
130
|
+
/** Read the entry recorded for an idempotency key. */
|
|
131
|
+
function readSkillOperationJournal(idempotencyKey, options = {}) {
|
|
132
|
+
const entry = (0, operation_store_1.readSkillStateFile)(getJournalPath(idempotencyKey, options));
|
|
133
|
+
return isJournalEntry(entry) ? entry : null;
|
|
134
|
+
}
|
|
135
|
+
/** Every entry currently on disk, oldest first. */
|
|
136
|
+
function listSkillOperationJournal(options = {}) {
|
|
137
|
+
const dir = getJournalDir(options);
|
|
138
|
+
const entries = [];
|
|
139
|
+
for (const name of (0, fs_1.readdirSync)(dir)) {
|
|
140
|
+
if (!name.endsWith('.json'))
|
|
141
|
+
continue;
|
|
142
|
+
const parsed = (0, operation_store_1.readSkillStateFile)((0, path_1.join)(dir, name));
|
|
143
|
+
if (isJournalEntry(parsed))
|
|
144
|
+
entries.push(parsed);
|
|
145
|
+
}
|
|
146
|
+
return entries.sort((a, b) => a.createdAt - b.createdAt);
|
|
147
|
+
}
|
|
148
|
+
/** Persist an entry. */
|
|
149
|
+
function writeSkillOperationJournal(entry, options = {}) {
|
|
150
|
+
(0, operation_store_1.writeSkillStateFile)(getJournalPath(entry.idempotencyKey, options), entry);
|
|
151
|
+
}
|
|
152
|
+
/** Drop an entry once it is past its retention window. */
|
|
153
|
+
function deleteSkillOperationJournal(idempotencyKey, options = {}) {
|
|
154
|
+
try {
|
|
155
|
+
(0, fs_1.unlinkSync)(getJournalPath(idempotencyKey, options));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Already collected.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* How long a terminal journal entry is kept before it becomes collectable.
|
|
163
|
+
*
|
|
164
|
+
* The journal is the crash-recovery and idempotency record, not the audit trail
|
|
165
|
+
* (that lives in the append-only `skill_operations` table). A terminal entry is
|
|
166
|
+
* only still useful for the window during which a client might retry the same
|
|
167
|
+
* idempotency key; past it, the entry is pure growth. Seven days comfortably
|
|
168
|
+
* covers realistic retry and support windows while bounding the directory to the
|
|
169
|
+
* operations of one window rather than of all time (Issue #1428).
|
|
170
|
+
*/
|
|
171
|
+
exports.SKILL_JOURNAL_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
172
|
+
/**
|
|
173
|
+
* Collect terminal journal entries whose retention window has elapsed.
|
|
174
|
+
*
|
|
175
|
+
* Only {@link isSkillOperationTerminal} entries are eligible: SUCCEEDED, and a
|
|
176
|
+
* pre-commit FAILED_RECONCILABLE that rolled back cleanly. A FAILED_RECONCILABLE
|
|
177
|
+
* that *did* commit to the filesystem is non-terminal — it still owes the
|
|
178
|
+
* reconciler an index write — and is never pruned, so a genuine
|
|
179
|
+
* payload-on-disk/no-index inconsistency is not silently discarded. Retention is
|
|
180
|
+
* measured from `updatedAt`, which for a terminal entry is the moment it reached
|
|
181
|
+
* its terminal state and never moves again.
|
|
182
|
+
*
|
|
183
|
+
* Uniform retention across both terminal outcomes is deliberate (Issue #1428):
|
|
184
|
+
* a SUCCEEDED entry that were deleted immediately would let a delayed retry of
|
|
185
|
+
* the *same* idempotency key re-run the install and hit a spurious
|
|
186
|
+
* DESTINATION_EXISTS, so success and failure expire on the same clock and a key
|
|
187
|
+
* becomes reusable at a single, predictable time regardless of outcome.
|
|
188
|
+
*
|
|
189
|
+
* @returns the idempotency keys of the entries that were pruned.
|
|
190
|
+
*/
|
|
191
|
+
function pruneExpiredSkillOperationJournal(options = {}) {
|
|
192
|
+
const now = options.now ?? Date.now();
|
|
193
|
+
const retentionMs = options.retentionMs ?? exports.SKILL_JOURNAL_RETENTION_MS;
|
|
194
|
+
const pruned = [];
|
|
195
|
+
for (const entry of listSkillOperationJournal(options)) {
|
|
196
|
+
if (!isSkillOperationTerminal(entry))
|
|
197
|
+
continue;
|
|
198
|
+
if (now - entry.updatedAt <= retentionMs)
|
|
199
|
+
continue;
|
|
200
|
+
deleteSkillOperationJournal(entry.idempotencyKey, options);
|
|
201
|
+
pruned.push(entry.idempotencyKey);
|
|
202
|
+
}
|
|
203
|
+
return pruned;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Open an operation, or return the one this request already started.
|
|
207
|
+
*
|
|
208
|
+
* A replay hands back the recorded entry so the caller can answer with the
|
|
209
|
+
* original outcome instead of writing the payload a second time.
|
|
210
|
+
*/
|
|
211
|
+
function beginSkillOperation(input, options = {}) {
|
|
212
|
+
const bindingHash = computeSkillOperationBindingHash(input.binding);
|
|
213
|
+
const idempotencyKey = input.idempotencyKey ?? deriveSkillOperationIdempotencyKey(input.binding);
|
|
214
|
+
const existing = readSkillOperationJournal(idempotencyKey, options);
|
|
215
|
+
if (existing !== null) {
|
|
216
|
+
if (existing.bindingHash !== bindingHash) {
|
|
217
|
+
return { ok: false, reason: 'IDEMPOTENCY_KEY_CONFLICT', entry: existing };
|
|
218
|
+
}
|
|
219
|
+
return { ok: true, entry: existing, replayed: true };
|
|
220
|
+
}
|
|
221
|
+
const now = options.now ?? Date.now();
|
|
222
|
+
const entry = {
|
|
223
|
+
schemaVersion: 1,
|
|
224
|
+
operationId: (0, crypto_1.randomUUID)(),
|
|
225
|
+
idempotencyKey,
|
|
226
|
+
bindingHash,
|
|
227
|
+
operation: input.binding.operation,
|
|
228
|
+
state: 'PREPARING',
|
|
229
|
+
actor: input.binding.actor,
|
|
230
|
+
target: input.binding.target,
|
|
231
|
+
source: input.source ?? null,
|
|
232
|
+
lockKey: input.lockKey,
|
|
233
|
+
createdAt: now,
|
|
234
|
+
updatedAt: now,
|
|
235
|
+
fsCommittedAt: null,
|
|
236
|
+
receiptDigest: null,
|
|
237
|
+
error: null,
|
|
238
|
+
history: [{ state: 'PREPARING', at: now }],
|
|
239
|
+
};
|
|
240
|
+
writeSkillOperationJournal(entry, options);
|
|
241
|
+
return { ok: true, entry, replayed: false };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Advance an operation to its next state and persist it.
|
|
245
|
+
*
|
|
246
|
+
* @throws SkillOperationTransitionError when the edge is not legal, or when a
|
|
247
|
+
* recovery edge is attempted on an operation that never reached the commit
|
|
248
|
+
* point (there is no receipt to converge from).
|
|
249
|
+
*/
|
|
250
|
+
function transitionSkillOperation(entry, next, patch = {}, options = {}) {
|
|
251
|
+
if (!ALLOWED_TRANSITIONS[entry.state].includes(next)) {
|
|
252
|
+
throw new SkillOperationTransitionError(entry.state, next, 'edge is not defined');
|
|
253
|
+
}
|
|
254
|
+
const now = options.now ?? Date.now();
|
|
255
|
+
const fsCommittedAt = next === 'FS_COMMITTED' ? (entry.fsCommittedAt ?? now) : entry.fsCommittedAt;
|
|
256
|
+
if ((next === 'INDEXED' || next === 'SUCCEEDED') && fsCommittedAt === null) {
|
|
257
|
+
throw new SkillOperationTransitionError(entry.state, next, 'no filesystem commit point was recorded');
|
|
258
|
+
}
|
|
259
|
+
const updated = {
|
|
260
|
+
...entry,
|
|
261
|
+
state: next,
|
|
262
|
+
updatedAt: now,
|
|
263
|
+
fsCommittedAt,
|
|
264
|
+
source: patch.source !== undefined ? patch.source : entry.source,
|
|
265
|
+
receiptDigest: patch.receiptDigest !== undefined ? patch.receiptDigest : entry.receiptDigest,
|
|
266
|
+
error: patch.error !== undefined
|
|
267
|
+
? patch.error === null
|
|
268
|
+
? null
|
|
269
|
+
: { code: patch.error.code, message: (0, operation_store_1.redactSkillOperationText)(patch.error.message) }
|
|
270
|
+
: entry.error,
|
|
271
|
+
history: [...entry.history, { state: next, at: now }],
|
|
272
|
+
};
|
|
273
|
+
writeSkillOperationJournal(updated, options);
|
|
274
|
+
return updated;
|
|
275
|
+
}
|
|
276
|
+
/** Whether the payload is known to exist, regardless of how the DB side ended. */
|
|
277
|
+
function hasSkillFilesystemCommit(entry) {
|
|
278
|
+
return entry.fsCommittedAt !== null;
|
|
279
|
+
}
|
|
280
|
+
/** Whether an entry still needs work from the reconciler. */
|
|
281
|
+
function isSkillOperationTerminal(entry) {
|
|
282
|
+
if (entry.state === 'SUCCEEDED')
|
|
283
|
+
return true;
|
|
284
|
+
// A failure before the commit point rolled back cleanly; nothing to converge.
|
|
285
|
+
return entry.state === 'FAILED_RECONCILABLE' && !hasSkillFilesystemCommit(entry);
|
|
286
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cross-process exclusive lock for Skill operations (Issue #1234)
|
|
4
|
+
*
|
|
5
|
+
* Extends the O_EXCL pattern of `lib/app-update/update-lock` with the three
|
|
6
|
+
* properties a Skill operation needs and a self-update does not:
|
|
7
|
+
*
|
|
8
|
+
* - **Owner nonce.** Release and renew require the caller to present the nonce
|
|
9
|
+
* written at acquisition, so no process can drop a lock it does not hold.
|
|
10
|
+
* - **Lease heartbeat.** {@link renewSkillOperationLease} can extend a lease so
|
|
11
|
+
* a long operation is not reclaimed by age. It is not yet wired into the
|
|
12
|
+
* routes, so owner liveness below — not the heartbeat — is what currently
|
|
13
|
+
* keeps a live operation safe (Issue #1427).
|
|
14
|
+
* - **Owner liveness.** A lock whose lease expired is reclaimed only when the
|
|
15
|
+
* owning PID is verifiably gone on this host. A live owner that missed a
|
|
16
|
+
* heartbeat keeps its lock, including a same-process concurrent operation:
|
|
17
|
+
* reclaiming it would let two processes write the same payload.
|
|
18
|
+
*
|
|
19
|
+
* The lock key is a digest of the *resolved* worktree path and the validated
|
|
20
|
+
* Skill ID, so no untrusted name ever reaches a filename.
|
|
21
|
+
*
|
|
22
|
+
* @module lib/skills/operation-lock
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.SKILL_LOCK_FOREIGN_HOST_GRACE_MS = exports.SKILL_LOCK_LEASE_MS = void 0;
|
|
26
|
+
exports.buildSkillOperationLockKey = buildSkillOperationLockKey;
|
|
27
|
+
exports.readSkillOperationLock = readSkillOperationLock;
|
|
28
|
+
exports.describeSkillLockHolder = describeSkillLockHolder;
|
|
29
|
+
exports.evaluateSkillLock = evaluateSkillLock;
|
|
30
|
+
exports.acquireSkillOperationLock = acquireSkillOperationLock;
|
|
31
|
+
exports.renewSkillOperationLease = renewSkillOperationLease;
|
|
32
|
+
exports.releaseSkillOperationLock = releaseSkillOperationLock;
|
|
33
|
+
exports.listSkillOperationLockKeys = listSkillOperationLockKeys;
|
|
34
|
+
exports.releaseOrphanSkillLocks = releaseOrphanSkillLocks;
|
|
35
|
+
const fs_1 = require("fs");
|
|
36
|
+
const os_1 = require("os");
|
|
37
|
+
const path_1 = require("path");
|
|
38
|
+
const crypto_1 = require("crypto");
|
|
39
|
+
const operation_store_1 = require("../../lib/skills/operation-store");
|
|
40
|
+
/** Lease length. A holder renews well before this; a crash expires it. */
|
|
41
|
+
exports.SKILL_LOCK_LEASE_MS = 30_000;
|
|
42
|
+
/**
|
|
43
|
+
* Extra wait before reclaiming a lock owned by another host.
|
|
44
|
+
* Liveness cannot be checked across hosts, so time is the only evidence there.
|
|
45
|
+
*/
|
|
46
|
+
exports.SKILL_LOCK_FOREIGN_HOST_GRACE_MS = 10 * 60_000;
|
|
47
|
+
/**
|
|
48
|
+
* Identifies this process across restarts. A PID alone is reusable; combined
|
|
49
|
+
* with this value a record can be recognised as "written by me".
|
|
50
|
+
*/
|
|
51
|
+
const PROCESS_GENERATION = (0, crypto_1.randomUUID)();
|
|
52
|
+
function defaultIsProcessAlive(pid) {
|
|
53
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
54
|
+
return false;
|
|
55
|
+
try {
|
|
56
|
+
process.kill(pid, 0);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// EPERM means the process exists but belongs to another user.
|
|
61
|
+
return error.code === 'EPERM';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Lock key for a (worktree, skill) pair.
|
|
66
|
+
*
|
|
67
|
+
* @param worktreeRealPath - Server-resolved, symlink-free worktree path. Two
|
|
68
|
+
* registrations pointing at the same directory must produce the same key, so
|
|
69
|
+
* the caller is responsible for having called realpath.
|
|
70
|
+
* @param skillId - Skill ID already validated against the contract grammar.
|
|
71
|
+
*/
|
|
72
|
+
function buildSkillOperationLockKey(worktreeRealPath, skillId) {
|
|
73
|
+
return (0, crypto_1.createHash)('sha256').update(`${worktreeRealPath}\x00${skillId}`).digest('hex');
|
|
74
|
+
}
|
|
75
|
+
function getLockDir(options) {
|
|
76
|
+
return (0, operation_store_1.ensureSkillStateDir)(operation_store_1.SKILL_LOCK_DIRNAME, options);
|
|
77
|
+
}
|
|
78
|
+
function getLockPath(key, options) {
|
|
79
|
+
return (0, path_1.join)(getLockDir(options), `${key}.lock`);
|
|
80
|
+
}
|
|
81
|
+
function isLockRecord(value) {
|
|
82
|
+
if (typeof value !== 'object' || value === null)
|
|
83
|
+
return false;
|
|
84
|
+
const record = value;
|
|
85
|
+
const owner = record.owner;
|
|
86
|
+
return (typeof record.key === 'string' &&
|
|
87
|
+
typeof record.operationId === 'string' &&
|
|
88
|
+
typeof record.leaseExpiresAt === 'number' &&
|
|
89
|
+
Number.isFinite(record.leaseExpiresAt) &&
|
|
90
|
+
typeof owner === 'object' &&
|
|
91
|
+
owner !== null &&
|
|
92
|
+
typeof owner.nonce === 'string' &&
|
|
93
|
+
typeof owner.pid === 'number' &&
|
|
94
|
+
typeof owner.host === 'string');
|
|
95
|
+
}
|
|
96
|
+
/** Read a lock record. Returns null when absent or unparseable. */
|
|
97
|
+
function readSkillOperationLock(key, options = {}) {
|
|
98
|
+
const record = (0, operation_store_1.readSkillStateFile)(getLockPath(key, options));
|
|
99
|
+
return isLockRecord(record) ? record : null;
|
|
100
|
+
}
|
|
101
|
+
/** Public, non-sensitive view of a lock record. */
|
|
102
|
+
function describeSkillLockHolder(record) {
|
|
103
|
+
return {
|
|
104
|
+
operationId: record.operationId,
|
|
105
|
+
acquiredAt: record.acquiredAt,
|
|
106
|
+
leaseExpiresAt: record.leaseExpiresAt,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Decide how an observed record may be treated, given the clock and liveness.
|
|
111
|
+
*
|
|
112
|
+
* Ordering matters: a valid lease short-circuits every other consideration, so
|
|
113
|
+
* a live holder is never reclaimed regardless of what its PID looks like. Once
|
|
114
|
+
* the lease has lapsed on this host, PID liveness is the *only* remaining
|
|
115
|
+
* evidence — a lock is reclaimed only when its owning process is verifiably gone.
|
|
116
|
+
*
|
|
117
|
+
* Same-process locks are deliberately not shortcut to RECLAIMABLE (Issue #1427).
|
|
118
|
+
* Every Next.js route handler runs in one shared process and none renews its
|
|
119
|
+
* lease ({@link renewSkillOperationLease} has no production caller), so a lapsed
|
|
120
|
+
* lease is not evidence that a *concurrent* same-process operation abandoned its
|
|
121
|
+
* lock. Shortcutting it to reclaimable let a second request steal a live
|
|
122
|
+
* install's lock ~one lease window in and write the same payload underneath it.
|
|
123
|
+
* A same-process owner that is still alive now resolves to HELD_BY_LIVE_OWNER
|
|
124
|
+
* and is left alone.
|
|
125
|
+
*
|
|
126
|
+
* Tradeoff: this process can no longer instantly reclaim a lock it truly leaked
|
|
127
|
+
* (acquired but never released). While the process stays alive such a lock reads
|
|
128
|
+
* as HELD_BY_LIVE_OWNER; it clears only on an explicit release or after the
|
|
129
|
+
* process exits — a later process then sees the dead PID and reclaims it. Note
|
|
130
|
+
* this is stronger than "next acquire waits at most one lease window": with no
|
|
131
|
+
* heartbeat there is no time-based signal, so within one live process the lock
|
|
132
|
+
* is held until release/exit, not merely until the lease lapses. In practice the
|
|
133
|
+
* acquire path always releases in a `finally`, so the only real remnant is a
|
|
134
|
+
* crash — and crash orphans are unaffected: a new process has a different
|
|
135
|
+
* generation and finds the old PID gone, so the liveness branch below reclaims
|
|
136
|
+
* them exactly as before.
|
|
137
|
+
*/
|
|
138
|
+
function evaluateSkillLock(record, options = {}) {
|
|
139
|
+
if (record === null)
|
|
140
|
+
return 'FREE';
|
|
141
|
+
const now = options.now ?? Date.now();
|
|
142
|
+
if (now < record.leaseExpiresAt)
|
|
143
|
+
return 'HELD';
|
|
144
|
+
const isAlive = options.isProcessAlive ?? defaultIsProcessAlive;
|
|
145
|
+
if (record.owner.host !== (0, os_1.hostname)()) {
|
|
146
|
+
const expiredFor = now - record.leaseExpiresAt;
|
|
147
|
+
return expiredFor > exports.SKILL_LOCK_FOREIGN_HOST_GRACE_MS
|
|
148
|
+
? 'RECLAIMABLE'
|
|
149
|
+
: 'HELD_UNVERIFIABLE_OWNER';
|
|
150
|
+
}
|
|
151
|
+
return isAlive(record.owner.pid) ? 'HELD_BY_LIVE_OWNER' : 'RECLAIMABLE';
|
|
152
|
+
}
|
|
153
|
+
function buildRecord(key, operationId, now, leaseMs) {
|
|
154
|
+
return {
|
|
155
|
+
schemaVersion: 1,
|
|
156
|
+
key,
|
|
157
|
+
operationId,
|
|
158
|
+
owner: {
|
|
159
|
+
nonce: (0, crypto_1.randomBytes)(24).toString('hex'),
|
|
160
|
+
pid: process.pid,
|
|
161
|
+
host: (0, os_1.hostname)(),
|
|
162
|
+
processGeneration: PROCESS_GENERATION,
|
|
163
|
+
},
|
|
164
|
+
acquiredAt: now,
|
|
165
|
+
renewedAt: now,
|
|
166
|
+
leaseExpiresAt: now + leaseMs,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Create the lock file, failing when it already exists. */
|
|
170
|
+
function tryCreate(lockPath, record) {
|
|
171
|
+
let fd;
|
|
172
|
+
try {
|
|
173
|
+
fd = (0, fs_1.openSync)(lockPath, fs_1.constants.O_CREAT | fs_1.constants.O_EXCL | fs_1.constants.O_WRONLY, operation_store_1.SKILL_STATE_FILE_MODE);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
(0, fs_1.writeSync)(fd, JSON.stringify(record));
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
(0, fs_1.closeSync)(fd);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Move a reclaimable lock aside atomically, then re-check it.
|
|
188
|
+
*
|
|
189
|
+
* The rename is the mutual exclusion: only one reclaimer can move the file, so
|
|
190
|
+
* a second reclaimer fails and reports RACE_LOST instead of also proceeding.
|
|
191
|
+
* The post-rename re-check closes the window where the holder renewed its lease
|
|
192
|
+
* between our read and our rename; in that case the file is put back.
|
|
193
|
+
*/
|
|
194
|
+
function reclaim(lockPath, observed, options) {
|
|
195
|
+
const stagePath = `${lockPath}.reclaim-${(0, crypto_1.randomBytes)(8).toString('hex')}`;
|
|
196
|
+
try {
|
|
197
|
+
(0, fs_1.renameSync)(lockPath, stagePath);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return { ok: false, reason: 'RACE_LOST' };
|
|
201
|
+
}
|
|
202
|
+
const staged = (0, operation_store_1.readSkillStateFile)(stagePath);
|
|
203
|
+
const stagedRecord = isLockRecord(staged) ? staged : null;
|
|
204
|
+
const stillReclaimable = observed === null
|
|
205
|
+
? // The file was unparseable; only proceed if it still is.
|
|
206
|
+
stagedRecord === null
|
|
207
|
+
: stagedRecord === null ||
|
|
208
|
+
(stagedRecord.owner.nonce === observed.owner.nonce &&
|
|
209
|
+
evaluateSkillLock(stagedRecord, options) === 'RECLAIMABLE');
|
|
210
|
+
if (!stillReclaimable) {
|
|
211
|
+
try {
|
|
212
|
+
(0, fs_1.renameSync)(stagePath, lockPath);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// The holder already re-created its own lock; nothing was lost.
|
|
216
|
+
}
|
|
217
|
+
return { ok: false, reason: 'RACE_LOST' };
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
(0, fs_1.unlinkSync)(stagePath);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Already gone; the lock path is what matters.
|
|
224
|
+
}
|
|
225
|
+
return { ok: true };
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Take the exclusive lock for one (worktree, skill) pair.
|
|
229
|
+
*
|
|
230
|
+
* @returns the record on success. The caller must keep it: renew and release
|
|
231
|
+
* both require the nonce it carries.
|
|
232
|
+
*/
|
|
233
|
+
function acquireSkillOperationLock(input, options = {}) {
|
|
234
|
+
const now = options.now ?? Date.now();
|
|
235
|
+
const leaseMs = input.leaseMs ?? exports.SKILL_LOCK_LEASE_MS;
|
|
236
|
+
const lockPath = getLockPath(input.key, options);
|
|
237
|
+
const record = buildRecord(input.key, input.operationId, now, leaseMs);
|
|
238
|
+
if (tryCreate(lockPath, record)) {
|
|
239
|
+
return { ok: true, lock: record, reclaimed: false };
|
|
240
|
+
}
|
|
241
|
+
const observed = readSkillOperationLock(input.key, options);
|
|
242
|
+
// A file that exists but does not parse can never expire on its own, so it is
|
|
243
|
+
// reclaimable rather than "free" — free would mean the create below succeeds.
|
|
244
|
+
const disposition = observed === null
|
|
245
|
+
? (0, fs_1.existsSync)(lockPath)
|
|
246
|
+
? 'RECLAIMABLE'
|
|
247
|
+
: 'FREE'
|
|
248
|
+
: evaluateSkillLock(observed, options);
|
|
249
|
+
const heldBy = observed ? describeSkillLockHolder(observed) : null;
|
|
250
|
+
if (disposition === 'HELD' || disposition === 'HELD_BY_LIVE_OWNER') {
|
|
251
|
+
return { ok: false, reason: disposition, heldBy };
|
|
252
|
+
}
|
|
253
|
+
if (disposition === 'HELD_UNVERIFIABLE_OWNER') {
|
|
254
|
+
return { ok: false, reason: 'HELD_UNVERIFIABLE_OWNER', heldBy };
|
|
255
|
+
}
|
|
256
|
+
if (disposition === 'FREE') {
|
|
257
|
+
// The file vanished between the failed create and the read.
|
|
258
|
+
return tryCreate(lockPath, record)
|
|
259
|
+
? { ok: true, lock: record, reclaimed: false }
|
|
260
|
+
: { ok: false, reason: 'RACE_LOST', heldBy: null };
|
|
261
|
+
}
|
|
262
|
+
// RECLAIMABLE.
|
|
263
|
+
const reclaimed = reclaim(lockPath, observed, options);
|
|
264
|
+
if (!reclaimed.ok) {
|
|
265
|
+
return { ok: false, reason: reclaimed.reason, heldBy };
|
|
266
|
+
}
|
|
267
|
+
return tryCreate(lockPath, record)
|
|
268
|
+
? { ok: true, lock: record, reclaimed: true }
|
|
269
|
+
: { ok: false, reason: 'RACE_LOST', heldBy: null };
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Extend the lease. Fails when the caller is not the current owner, which is
|
|
273
|
+
* how a process that was reclaimed learns it must stop writing.
|
|
274
|
+
*
|
|
275
|
+
* @returns the refreshed record, or null when the lock was lost.
|
|
276
|
+
*/
|
|
277
|
+
function renewSkillOperationLease(lock, options = {}) {
|
|
278
|
+
const current = readSkillOperationLock(lock.key, options);
|
|
279
|
+
if (current === null || current.owner.nonce !== lock.owner.nonce)
|
|
280
|
+
return null;
|
|
281
|
+
const now = options.now ?? Date.now();
|
|
282
|
+
const leaseMs = lock.leaseExpiresAt - lock.renewedAt;
|
|
283
|
+
const renewed = {
|
|
284
|
+
...current,
|
|
285
|
+
renewedAt: now,
|
|
286
|
+
leaseExpiresAt: now + (leaseMs > 0 ? leaseMs : exports.SKILL_LOCK_LEASE_MS),
|
|
287
|
+
};
|
|
288
|
+
(0, operation_store_1.writeSkillStateFile)(getLockPath(lock.key, options), renewed);
|
|
289
|
+
return renewed;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Drop the lock. Owner-only: presenting a stale or foreign nonce is refused so
|
|
293
|
+
* no operation can release another operation's resource.
|
|
294
|
+
*/
|
|
295
|
+
function releaseSkillOperationLock(lock, options = {}) {
|
|
296
|
+
const current = readSkillOperationLock(lock.key, options);
|
|
297
|
+
if (current === null || current.owner.nonce !== lock.owner.nonce)
|
|
298
|
+
return false;
|
|
299
|
+
try {
|
|
300
|
+
(0, fs_1.unlinkSync)(getLockPath(lock.key, options));
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/** Keys of every lock file currently present. */
|
|
308
|
+
function listSkillOperationLockKeys(options = {}) {
|
|
309
|
+
return (0, fs_1.readdirSync)(getLockDir(options))
|
|
310
|
+
.filter((name) => name.endsWith('.lock'))
|
|
311
|
+
.map((name) => name.slice(0, -'.lock'.length));
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Remove locks whose owner is verifiably gone.
|
|
315
|
+
*
|
|
316
|
+
* Used by startup reconciliation. A lock is only removed when
|
|
317
|
+
* {@link evaluateSkillLock} says RECLAIMABLE, so a live owner — even one whose
|
|
318
|
+
* lease lapsed — keeps its lock.
|
|
319
|
+
*
|
|
320
|
+
* @returns keys of the locks that were removed.
|
|
321
|
+
*/
|
|
322
|
+
function releaseOrphanSkillLocks(options = {}) {
|
|
323
|
+
const released = [];
|
|
324
|
+
for (const key of listSkillOperationLockKeys(options)) {
|
|
325
|
+
const record = readSkillOperationLock(key, options);
|
|
326
|
+
// A file that exists but does not parse can never expire on its own.
|
|
327
|
+
const disposition = record === null ? 'RECLAIMABLE' : evaluateSkillLock(record, options);
|
|
328
|
+
if (disposition !== 'RECLAIMABLE')
|
|
329
|
+
continue;
|
|
330
|
+
try {
|
|
331
|
+
(0, fs_1.unlinkSync)(getLockPath(key, options));
|
|
332
|
+
released.push(key);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Someone else cleaned it up first.
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return released;
|
|
339
|
+
}
|