@baique/pi-web-sky 0.1.35 → 0.1.37
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-path-routes-manifest.json +11 -11
- package/.next/build-manifest.json +4 -4
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/required-server-files.js +6 -6
- package/.next/required-server-files.json +6 -6
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +3 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
- package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/events/route.js +3 -3
- package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js +2 -2
- package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app-update/route.js +1 -1
- package/.next/server/app/api/app-update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +3 -3
- package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/canvas/route.js +1 -1
- package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/route.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/route.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +1 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
- package/.next/server/app/api/boards/purge-orphans/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/reorder/route.js +1 -1
- package/.next/server/app/api/boards/reorder/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/route.js +1 -1
- package/.next/server/app/api/boards/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +2 -2
- package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/default-cwd/route.js +2 -2
- package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/file-index/route.js +2 -2
- package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/files/[...path]/route.js +2 -2
- package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/home/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models/route.js +2 -2
- package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/route.js +1 -1
- package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/plugins/route.js +2 -2
- package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/project-trust/route.js +2 -2
- package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/quota/route.js +1 -1
- package/.next/server/app/api/quota/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/search/route.js +2 -2
- package/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/route.js +2 -2
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/check/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route.js +2 -31
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/skills/update/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
- package/.next/server/app/api/task-card-questions/[id]/answer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-card-questions/route.js +1 -1
- package/.next/server/app/api/task-card-questions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-cards/[id]/route.js +1 -1
- package/.next/server/app/api/task-cards/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-cards/route.js +1 -1
- package/.next/server/app/api/task-cards/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/board/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[id]/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/reorder/route.js +1 -1
- package/.next/server/app/api/tasks/reorder/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/route.js +1 -1
- package/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/[id]/events/route.js +3 -3
- package/.next/server/app/api/terminal/[id]/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/[id]/route.js +2 -2
- package/.next/server/app/api/terminal/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/route.js +2 -2
- package/.next/server/app/api/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +2 -2
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +11 -11
- package/.next/server/chunks/2404.js +2 -2
- package/.next/server/chunks/2430.js +4 -4
- package/.next/server/chunks/3500.js +30 -0
- package/.next/server/chunks/4226.js +2 -2
- package/.next/server/chunks/542.js +11 -6
- package/.next/server/chunks/5794.js +2 -2
- package/.next/server/chunks/6429.js +1 -1
- package/.next/server/chunks/898.js +2 -2
- package/.next/server/instrumentation.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/middleware.js +3 -3
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/5315.dba7515ae6e139ae.js +4 -0
- package/.next/static/chunks/{7999.f4ab221861378788.js → 7999.879d5a20e8641752.js} +1 -1
- package/.next/static/chunks/app/_not-found/{page-af4fe23994cb3d66.js → page-8cea8d8de35f4da4.js} +1 -1
- package/.next/static/chunks/app/{layout-1d9a7a44fbc92d2e.js → layout-c4ca28ccc08bc975.js} +1 -1
- package/.next/static/chunks/app/{page-6d6779a81a0a7639.js → page-c9ead38525a85686.js} +3 -3
- package/.next/static/chunks/{main-app-b3e8a8d15d83e1f0.js → main-app-dd2385a1a5a588df.js} +1 -1
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-be943dcb89867ab9.js +1 -0
- package/.next/static/chunks/{webpack-fa4f40085b1ec1ac.js → webpack-4bbf04891e910713.js} +1 -1
- package/.next/static/css/422c7dc0faa04d89.css +1 -0
- package/.next/trace +8 -9
- package/.next/trace-build +1 -1
- package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/route.ts +1 -1
- package/.next/types/app/api/agent/new/route.ts +1 -1
- package/.next/types/app/api/agent/running/route.ts +1 -1
- package/.next/types/app/api/app-update/route.ts +1 -1
- package/.next/types/app/api/auth/all-providers/route.ts +1 -1
- package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/providers/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/canvas/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/edges/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/nodes/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/reconcile-task-edges/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/route.ts +1 -1
- package/.next/types/app/api/boards/purge-orphans/route.ts +1 -1
- package/.next/types/app/api/boards/reorder/route.ts +1 -1
- package/.next/types/app/api/boards/route.ts +1 -1
- package/.next/types/app/api/cwd/browse/route.ts +1 -1
- package/.next/types/app/api/cwd/validate/route.ts +1 -1
- package/.next/types/app/api/default-cwd/route.ts +1 -1
- package/.next/types/app/api/file-index/route.ts +1 -1
- package/.next/types/app/api/files/[...path]/route.ts +1 -1
- package/.next/types/app/api/git/diff/route.ts +1 -1
- package/.next/types/app/api/git/status/route.ts +1 -1
- package/.next/types/app/api/home/route.ts +1 -1
- package/.next/types/app/api/mcp/route.ts +1 -1
- package/.next/types/app/api/models/route.ts +1 -1
- package/.next/types/app/api/models-config/catalog/route.ts +1 -1
- package/.next/types/app/api/models-config/discover/route.ts +1 -1
- package/.next/types/app/api/models-config/route.ts +1 -1
- package/.next/types/app/api/models-config/test/route.ts +1 -1
- package/.next/types/app/api/plugins/route.ts +1 -1
- package/.next/types/app/api/project-trust/route.ts +1 -1
- package/.next/types/app/api/quota/route.ts +1 -1
- package/.next/types/app/api/search/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/todos/route.ts +1 -1
- package/.next/types/app/api/sessions/route.ts +1 -1
- package/.next/types/app/api/skills/check/route.ts +1 -1
- package/.next/types/app/api/skills/install/route.ts +1 -1
- package/.next/types/app/api/skills/route.ts +1 -1
- package/.next/types/app/api/skills/search/route.ts +1 -1
- package/.next/types/app/api/skills/update/route.ts +1 -1
- package/.next/types/app/api/task-card-questions/[id]/answer/route.ts +1 -1
- package/.next/types/app/api/task-card-questions/route.ts +1 -1
- package/.next/types/app/api/task-cards/[id]/route.ts +1 -1
- package/.next/types/app/api/task-cards/route.ts +1 -1
- package/.next/types/app/api/task-scheduler/status/route.ts +1 -1
- package/.next/types/app/api/tasks/[id]/board/route.ts +1 -1
- package/.next/types/app/api/tasks/[id]/route.ts +1 -1
- package/.next/types/app/api/tasks/reorder/route.ts +1 -1
- package/.next/types/app/api/tasks/route.ts +1 -1
- package/.next/types/app/api/terminal/[id]/events/route.ts +1 -1
- package/.next/types/app/api/terminal/[id]/route.ts +1 -1
- package/.next/types/app/api/terminal/route.ts +1 -1
- package/.next/types/app/api/worktrees/route.ts +1 -1
- package/.next/types/app/layout.ts +1 -1
- package/.next/types/app/page.ts +1 -1
- package/bin/pi-web-sky.js +15 -20
- package/lib/agent-client.test.mjs +64 -0
- package/lib/agent-client.ts +53 -0
- package/lib/agent-event-connection.test.mjs +193 -0
- package/lib/agent-event-connection.ts +202 -0
- package/lib/agent-event-stream.test.mjs +155 -0
- package/lib/agent-event-stream.ts +132 -0
- package/lib/agent-event-wire.test.mjs +278 -0
- package/lib/agent-event-wire.ts +107 -0
- package/lib/agent-phase.ts +24 -0
- package/lib/allowed-roots.ts +31 -0
- package/lib/ansi.test.mjs +52 -0
- package/lib/ansi.ts +211 -0
- package/lib/api-types.ts +139 -0
- package/lib/app-update.test.mjs +27 -0
- package/lib/app-update.ts +28 -0
- package/lib/atomic-file.test.mjs +37 -0
- package/lib/atomic-file.ts +34 -0
- package/lib/audit-session.ts +235 -0
- package/lib/bash-output.test.mjs +63 -0
- package/lib/bash-output.ts +53 -0
- package/lib/bg-image.ts +129 -0
- package/lib/board-events.ts +83 -0
- package/lib/board-purge.test.mjs +66 -0
- package/lib/board-purge.ts +65 -0
- package/lib/board-search.test.mjs +110 -0
- package/lib/board-search.ts +63 -0
- package/lib/board-store.test.mjs +542 -0
- package/lib/board-store.ts +794 -0
- package/lib/board-types.ts +96 -0
- package/lib/board-utils.ts +17 -0
- package/lib/bounded-form-data.test.mjs +54 -0
- package/lib/bounded-form-data.ts +49 -0
- package/lib/browser-notifications.test.mjs +167 -0
- package/lib/browser-notifications.ts +104 -0
- package/lib/chat-lazy-load.test.mjs +140 -0
- package/lib/chat-lazy-load.ts +64 -0
- package/lib/clipboard-paths.test.mjs +140 -0
- package/lib/clipboard-paths.ts +192 -0
- package/lib/clipboard.ts +18 -0
- package/lib/compaction-summary.test.mjs +44 -0
- package/lib/compaction-summary.ts +34 -0
- package/lib/custom-ui-terminal.test.mjs +32 -0
- package/lib/custom-ui-terminal.ts +27 -0
- package/lib/deepseek-pricing.test.mjs +120 -0
- package/lib/deepseek-pricing.ts +71 -0
- package/lib/directory-browser.test.mjs +60 -0
- package/lib/directory-browser.ts +83 -0
- package/lib/draft-stash.test.mjs +91 -0
- package/lib/draft-stash.ts +102 -0
- package/lib/draft-store.ts +105 -0
- package/lib/dropdown-direction.ts +27 -0
- package/lib/extension-command.test.mjs +63 -0
- package/lib/extension-command.ts +81 -0
- package/lib/file-access.test.mjs +31 -0
- package/lib/file-access.ts +59 -0
- package/lib/file-dirent.test.mjs +56 -0
- package/lib/file-dirent.ts +17 -0
- package/lib/file-explorer-state.test.mjs +63 -0
- package/lib/file-explorer-state.ts +36 -0
- package/lib/file-fuzzy.test.mjs +29 -0
- package/lib/file-fuzzy.ts +189 -0
- package/lib/file-links.test.mjs +92 -0
- package/lib/file-links.ts +148 -0
- package/lib/file-paths.ts +43 -0
- package/lib/file-types.test.mjs +34 -0
- package/lib/file-types.ts +73 -0
- package/lib/file-upload.test.mjs +70 -0
- package/lib/file-upload.ts +59 -0
- package/lib/file-viewer-state.test.mjs +20 -0
- package/lib/file-viewer-state.ts +15 -0
- package/lib/frontmatter.test.mjs +55 -0
- package/lib/frontmatter.ts +87 -0
- package/lib/git-changes.test.mjs +52 -0
- package/lib/git-changes.ts +232 -0
- package/lib/git-status.ts +47 -0
- package/lib/git-types.ts +29 -0
- package/lib/http-dispatcher.test.mjs +83 -0
- package/lib/http-dispatcher.ts +86 -0
- package/lib/i18n/format.test.mjs +19 -0
- package/lib/i18n/format.ts +60 -0
- package/lib/i18n/messages/en.ts +662 -0
- package/lib/i18n/messages/zh-CN.ts +661 -0
- package/lib/i18n/registry.test.mjs +32 -0
- package/lib/i18n/registry.ts +43 -0
- package/lib/i18n/types.ts +15 -0
- package/lib/image-attachments.test.mjs +27 -0
- package/lib/image-attachments.ts +56 -0
- package/lib/initial-navigation.test.mjs +65 -0
- package/lib/initial-navigation.ts +15 -0
- package/lib/markdown.test.mjs +104 -0
- package/lib/markdown.ts +347 -0
- package/lib/message-display.test.mjs +130 -0
- package/lib/message-display.ts +47 -0
- package/lib/model-catalog.test.mjs +327 -0
- package/lib/model-catalog.ts +414 -0
- package/lib/model-discovery-auth.ts +58 -0
- package/lib/model-discovery.test.mjs +50 -0
- package/lib/model-discovery.ts +75 -0
- package/lib/model-id-preference.ts +46 -0
- package/lib/model-scope.test.mjs +193 -0
- package/lib/model-scope.ts +198 -0
- package/lib/models-cache.test.mjs +122 -0
- package/lib/models-cache.ts +90 -0
- package/lib/models-config-store.test.mjs +200 -0
- package/lib/models-config-store.ts +84 -0
- package/lib/next-config-esm.test.mjs +14 -0
- package/lib/next-config.test.mjs +13 -0
- package/lib/node-version.test.mjs +33 -0
- package/lib/normalize.test.mjs +54 -0
- package/lib/normalize.ts +63 -0
- package/lib/npx.ts +62 -0
- package/lib/panel-layout.test.mjs +56 -0
- package/lib/panel-layout.ts +52 -0
- package/lib/patch.test.mjs +110 -0
- package/lib/patch.ts +134 -0
- package/lib/path-security.ts +42 -0
- package/lib/paths.test.mjs +58 -0
- package/lib/paths.ts +71 -0
- package/lib/pi-types.ts +183 -0
- package/lib/pi-web-options.test.mjs +63 -0
- package/lib/process-lifecycle.test.mjs +83 -0
- package/lib/project-command-env.test.mjs +298 -0
- package/lib/project-command-env.ts +129 -0
- package/lib/project-groups.test.mjs +60 -0
- package/lib/project-groups.ts +53 -0
- package/lib/project-identity.test.mjs +37 -0
- package/lib/project-identity.ts +26 -0
- package/lib/project-tree.test.mjs +115 -0
- package/lib/project-tree.ts +176 -0
- package/lib/project-trust.test.mjs +117 -0
- package/lib/project-trust.ts +48 -0
- package/lib/prompt-recovery.test.mjs +33 -0
- package/lib/prompt-recovery.ts +47 -0
- package/lib/provider-api-key-route.test.mjs +12 -0
- package/lib/provider-credential-store.test.mjs +126 -0
- package/lib/provider-credential-store.ts +114 -0
- package/lib/provider-listing-runtime.ts +37 -0
- package/lib/provider-listing.test.mjs +114 -0
- package/lib/provider-listing.ts +118 -0
- package/lib/provider-services.ts +64 -0
- package/lib/request-security.test.mjs +256 -0
- package/lib/request-security.ts +136 -0
- package/lib/rpc-manager-shutdown.test.mjs +372 -0
- package/lib/rpc-manager-widgets.test.mjs +342 -0
- package/lib/rpc-manager.test.mjs +139 -0
- package/lib/rpc-manager.ts +1703 -0
- package/lib/rpc-session-info.test.mjs +93 -0
- package/lib/session-delete.test.mjs +53 -0
- package/lib/session-delete.ts +86 -0
- package/lib/session-file-references-core.ts +82 -0
- package/lib/session-file-references.test.mjs +91 -0
- package/lib/session-file-references.ts +29 -0
- package/lib/session-path.test.mjs +24 -0
- package/lib/session-path.ts +11 -0
- package/lib/session-reader.pagination.test.mjs +115 -0
- package/lib/session-reader.test.mjs +446 -0
- package/lib/session-reader.ts +548 -0
- package/lib/session-resolve.test.mjs +65 -0
- package/lib/session-row-context-menu.test.mjs +46 -0
- package/lib/session-row-context-menu.ts +29 -0
- package/lib/session-scanner.test.mjs +151 -0
- package/lib/session-scanner.ts +238 -0
- package/lib/session-search.test.mjs +173 -0
- package/lib/session-search.ts +284 -0
- package/lib/session-stats.ts +26 -0
- package/lib/session-timing.test.mjs +107 -0
- package/lib/session-timing.ts +45 -0
- package/lib/session-title.test.mjs +284 -0
- package/lib/session-title.ts +255 -0
- package/lib/skill-frontmatter.test.mjs +75 -0
- package/lib/skill-frontmatter.ts +40 -0
- package/lib/skill-lock.test.mjs +182 -0
- package/lib/skill-lock.ts +146 -0
- package/lib/skill-updates.test.mjs +182 -0
- package/lib/skill-updates.ts +263 -0
- package/lib/skills-service.ts +16 -0
- package/lib/slash-display.test.mjs +122 -0
- package/lib/slash-display.ts +30 -0
- package/lib/sqlite-db.test.mjs +140 -0
- package/lib/sqlite-db.ts +205 -0
- package/lib/startup-preferences.test.mjs +130 -0
- package/lib/startup-preferences.ts +65 -0
- package/lib/streaming-message.test.mjs +212 -0
- package/lib/streaming-message.ts +151 -0
- package/lib/subagent-widget.test.mjs +87 -0
- package/lib/subagent-widget.ts +109 -0
- package/lib/sync-room-server.mjs +137 -0
- package/lib/task-card-store.test.mjs +309 -0
- package/lib/task-card-store.ts +559 -0
- package/lib/task-scheduler.ts +498 -0
- package/lib/task-store.test.mjs +216 -0
- package/lib/task-store.ts +271 -0
- package/lib/terminal-input.test.mjs +46 -0
- package/lib/terminal-input.ts +63 -0
- package/lib/terminal-manager.test.mjs +71 -0
- package/lib/terminal-manager.ts +245 -0
- package/lib/tool-execution-progress.test.mjs +32 -0
- package/lib/tool-execution-progress.ts +29 -0
- package/lib/tool-names.ts +25 -0
- package/lib/tool-preset-preference.test.mjs +72 -0
- package/lib/tool-preset-preference.ts +41 -0
- package/lib/tool-presets.test.mjs +56 -0
- package/lib/tool-presets.ts +42 -0
- package/lib/turn-written-files.test.mjs +137 -0
- package/lib/turn-written-files.ts +60 -0
- package/lib/types.ts +349 -0
- package/lib/wallpaper-settings.ts +191 -0
- package/lib/web-auth.test.mjs +50 -0
- package/lib/web-auth.ts +45 -0
- package/lib/workspace-memory.test.mjs +129 -0
- package/lib/workspace-memory.ts +98 -0
- package/lib/worktree.test.mjs +50 -0
- package/lib/worktree.ts +250 -0
- package/package.json +3 -1
- package/server.mjs +34 -0
- package/.next/server/chunks/7513.js +0 -30
- package/.next/static/chunks/5315.03de3c6abd8b283e.js +0 -4
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-00a4badaccb388e6.js +0 -1
- package/.next/static/css/cf25164cb7c04d95.css +0 -1
- /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_buildManifest.js +0 -0
- /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import { getDb } from "./sqlite-db";
|
|
3
|
+
import { SYSTEM_RUNNING_BOARD_ID } from "./board-types";
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// 任务卡(工作项卡)元数据 —— SDK-free,参照 task-store 模式。
|
|
7
|
+
// 业务字段真相源;画布布局走 board_nodes(kind=taskcard, ref_id=cardId)。
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export type ReadyStatus = "draft" | "todo";
|
|
11
|
+
export type ExecStatus =
|
|
12
|
+
| "not_started"
|
|
13
|
+
| "running"
|
|
14
|
+
| "review"
|
|
15
|
+
| "done"
|
|
16
|
+
| "failed"
|
|
17
|
+
| "abandoned"
|
|
18
|
+
| "waiting_reply";
|
|
19
|
+
export type LinkKind = "prerequisite" | "related";
|
|
20
|
+
|
|
21
|
+
export interface TaskCard {
|
|
22
|
+
id: string;
|
|
23
|
+
boardId: string;
|
|
24
|
+
projectKey: string;
|
|
25
|
+
/** 项目内自增编号(#N,跨看板统一) */
|
|
26
|
+
number: number;
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
readyStatus: ReadyStatus;
|
|
30
|
+
execStatus: ExecStatus;
|
|
31
|
+
/** 高1 / 中0 / 低-1 */
|
|
32
|
+
priority: number;
|
|
33
|
+
/** ms epoch;null = 无截止 */
|
|
34
|
+
due: number | null;
|
|
35
|
+
/** 引用文件路径数组 */
|
|
36
|
+
attachments: string[];
|
|
37
|
+
/** 执行工作目录;null = 项目根 */
|
|
38
|
+
cwd: string | null;
|
|
39
|
+
useWorktree: boolean;
|
|
40
|
+
maxRetries: number;
|
|
41
|
+
retryCount: number;
|
|
42
|
+
/** 专属执行会话 id(调度器派发后写入) */
|
|
43
|
+
sessionId: string | null;
|
|
44
|
+
/** 跨进程派发互斥 token:谁抢到谁执行(多实例共库防双会话) */
|
|
45
|
+
dispatchToken: string | null;
|
|
46
|
+
created: number;
|
|
47
|
+
updated: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TaskCardLink {
|
|
51
|
+
id: string;
|
|
52
|
+
cardId: string;
|
|
53
|
+
targetCardId: string;
|
|
54
|
+
kind: LinkKind;
|
|
55
|
+
created: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface TaskCardRow {
|
|
59
|
+
id: string;
|
|
60
|
+
boardId: string;
|
|
61
|
+
projectKey: string;
|
|
62
|
+
number: number;
|
|
63
|
+
name: string;
|
|
64
|
+
description: string;
|
|
65
|
+
readyStatus: ReadyStatus;
|
|
66
|
+
execStatus: ExecStatus;
|
|
67
|
+
priority: number;
|
|
68
|
+
due: number | null;
|
|
69
|
+
attachments: string;
|
|
70
|
+
cwd: string | null;
|
|
71
|
+
useWorktree: number;
|
|
72
|
+
maxRetries: number;
|
|
73
|
+
retryCount: number;
|
|
74
|
+
sessionId: string | null;
|
|
75
|
+
dispatchToken: string | null;
|
|
76
|
+
created: number;
|
|
77
|
+
updated: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const now = () => Date.now();
|
|
81
|
+
|
|
82
|
+
const READY_STATUSES = new Set<ReadyStatus>(["draft", "todo"]);
|
|
83
|
+
const EXEC_STATUSES = new Set<ExecStatus>([
|
|
84
|
+
"not_started",
|
|
85
|
+
"running",
|
|
86
|
+
"review",
|
|
87
|
+
"done",
|
|
88
|
+
"failed",
|
|
89
|
+
"abandoned",
|
|
90
|
+
"waiting_reply",
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
function assertReadyStatus(v: ReadyStatus | undefined): void {
|
|
94
|
+
if (v !== undefined && !READY_STATUSES.has(v)) throw new Error(`invalid readyStatus: ${String(v)}`);
|
|
95
|
+
}
|
|
96
|
+
function assertExecStatus(v: ExecStatus | undefined): void {
|
|
97
|
+
if (v !== undefined && !EXEC_STATUSES.has(v)) throw new Error(`invalid execStatus: ${String(v)}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function rowToCard(row: TaskCardRow): TaskCard {
|
|
101
|
+
return {
|
|
102
|
+
...row,
|
|
103
|
+
useWorktree: row.useWorktree === 1,
|
|
104
|
+
attachments: JSON.parse(row.attachments) as string[],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const CARD_COLUMNS =
|
|
109
|
+
"id, board_id AS boardId, project_key AS projectKey, number, name, description, ready_status AS readyStatus, exec_status AS execStatus, priority, due, attachments, cwd, use_worktree AS useWorktree, max_retries AS maxRetries, retry_count AS retryCount, session_id AS sessionId, dispatch_token AS dispatchToken, created, updated";
|
|
110
|
+
|
|
111
|
+
function getCardRow(id: string): TaskCardRow | undefined {
|
|
112
|
+
return getDb()
|
|
113
|
+
.prepare(`SELECT ${CARD_COLUMNS} FROM task_cards WHERE id = ?`)
|
|
114
|
+
.get(id) as TaskCardRow | undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** 单张任务卡,或 undefined。 */
|
|
118
|
+
export function getCard(id: string): TaskCard | undefined {
|
|
119
|
+
const row = getCardRow(id);
|
|
120
|
+
return row ? rowToCard(row) : undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** 某看板的全部任务卡,按编号升序。 */
|
|
124
|
+
export function listCards(boardId: string): TaskCard[] {
|
|
125
|
+
const rows = getDb()
|
|
126
|
+
.prepare(`SELECT ${CARD_COLUMNS} FROM task_cards WHERE board_id = ? ORDER BY number`)
|
|
127
|
+
.all(boardId) as unknown as TaskCardRow[];
|
|
128
|
+
return rows.map(rowToCard);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** 项目内下一编号(MAX(number)+1;空表从 1 起)。 */
|
|
132
|
+
function nextCardNumber(projectKey: string): number {
|
|
133
|
+
const row = getDb()
|
|
134
|
+
.prepare("SELECT COALESCE(MAX(number), 0) AS maxNumber FROM task_cards WHERE project_key = ?")
|
|
135
|
+
.get(projectKey) as { maxNumber: number };
|
|
136
|
+
return row.maxNumber + 1;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** 建卡。名称必填非空;编号项目内自增。 */
|
|
140
|
+
export function createCard(input: {
|
|
141
|
+
boardId: string;
|
|
142
|
+
projectKey: string;
|
|
143
|
+
name: string;
|
|
144
|
+
description?: string;
|
|
145
|
+
readyStatus?: ReadyStatus;
|
|
146
|
+
priority?: number;
|
|
147
|
+
due?: number | null;
|
|
148
|
+
attachments?: string[];
|
|
149
|
+
cwd?: string | null;
|
|
150
|
+
useWorktree?: boolean;
|
|
151
|
+
maxRetries?: number;
|
|
152
|
+
}): TaskCard {
|
|
153
|
+
const name = input.name.trim();
|
|
154
|
+
if (!input.projectKey) throw new Error("projectKey is required");
|
|
155
|
+
if (!input.boardId) throw new Error("boardId is required");
|
|
156
|
+
if (!name) throw new Error("name must not be empty");
|
|
157
|
+
assertReadyStatus(input.readyStatus);
|
|
158
|
+
|
|
159
|
+
const id = randomUUID();
|
|
160
|
+
const ts = now();
|
|
161
|
+
const number = nextCardNumber(input.projectKey);
|
|
162
|
+
getDb()
|
|
163
|
+
.prepare(
|
|
164
|
+
"INSERT INTO task_cards (id, board_id, project_key, number, name, description, ready_status, exec_status, priority, due, attachments, cwd, use_worktree, max_retries, retry_count, created, updated) " +
|
|
165
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
166
|
+
)
|
|
167
|
+
.run(
|
|
168
|
+
id,
|
|
169
|
+
input.boardId,
|
|
170
|
+
input.projectKey,
|
|
171
|
+
number,
|
|
172
|
+
name,
|
|
173
|
+
input.description ?? "",
|
|
174
|
+
input.readyStatus ?? "todo",
|
|
175
|
+
"not_started",
|
|
176
|
+
input.priority ?? 0,
|
|
177
|
+
input.due ?? null,
|
|
178
|
+
JSON.stringify(input.attachments ?? []),
|
|
179
|
+
input.cwd ?? null,
|
|
180
|
+
input.useWorktree ? 1 : 0,
|
|
181
|
+
input.maxRetries ?? 3,
|
|
182
|
+
0,
|
|
183
|
+
ts,
|
|
184
|
+
ts,
|
|
185
|
+
);
|
|
186
|
+
return rowToCard(getCardRow(id)!);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** 改字段。返回更新后的卡;任务卡不存在返回 null。 */
|
|
190
|
+
export function updateCard(
|
|
191
|
+
id: string,
|
|
192
|
+
patch: {
|
|
193
|
+
name?: string;
|
|
194
|
+
description?: string;
|
|
195
|
+
readyStatus?: ReadyStatus;
|
|
196
|
+
execStatus?: ExecStatus;
|
|
197
|
+
priority?: number;
|
|
198
|
+
due?: number | null;
|
|
199
|
+
attachments?: string[];
|
|
200
|
+
cwd?: string | null;
|
|
201
|
+
useWorktree?: boolean;
|
|
202
|
+
maxRetries?: number;
|
|
203
|
+
sessionId?: string | null;
|
|
204
|
+
retryCount?: number;
|
|
205
|
+
},
|
|
206
|
+
): TaskCard | null {
|
|
207
|
+
const db = getDb();
|
|
208
|
+
const existing = getCardRow(id);
|
|
209
|
+
if (!existing) return null;
|
|
210
|
+
|
|
211
|
+
const sets: string[] = [];
|
|
212
|
+
const values: Array<string | number | null> = [];
|
|
213
|
+
const bump = (column: string, value: string | number | null) => {
|
|
214
|
+
sets.push(`${column} = ?`);
|
|
215
|
+
values.push(value);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
if (patch.name !== undefined) {
|
|
219
|
+
const trimmed = patch.name.trim();
|
|
220
|
+
if (!trimmed) throw new Error("name must not be empty");
|
|
221
|
+
bump("name", trimmed);
|
|
222
|
+
}
|
|
223
|
+
if (patch.description !== undefined) bump("description", patch.description);
|
|
224
|
+
if (patch.readyStatus !== undefined) { assertReadyStatus(patch.readyStatus); bump("ready_status", patch.readyStatus); }
|
|
225
|
+
if (patch.execStatus !== undefined) { assertExecStatus(patch.execStatus); bump("exec_status", patch.execStatus); }
|
|
226
|
+
if (patch.priority !== undefined) bump("priority", patch.priority);
|
|
227
|
+
if (patch.due !== undefined) bump("due", patch.due);
|
|
228
|
+
if (patch.attachments !== undefined) bump("attachments", JSON.stringify(patch.attachments));
|
|
229
|
+
if (patch.cwd !== undefined) bump("cwd", patch.cwd);
|
|
230
|
+
if (patch.useWorktree !== undefined) bump("use_worktree", patch.useWorktree ? 1 : 0);
|
|
231
|
+
if (patch.maxRetries !== undefined) bump("max_retries", patch.maxRetries);
|
|
232
|
+
if (patch.sessionId !== undefined) bump("session_id", patch.sessionId);
|
|
233
|
+
if (patch.retryCount !== undefined) bump("retry_count", patch.retryCount);
|
|
234
|
+
if (sets.length === 0) return rowToCard(existing);
|
|
235
|
+
|
|
236
|
+
bump("updated", now());
|
|
237
|
+
db.prepare(`UPDATE task_cards SET ${sets.join(", ")} WHERE id = ?`).run(...values, id);
|
|
238
|
+
return rowToCard(getCardRow(id)!);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 删任务卡:事务内级联删 task_card_links / task_card_questions 与该卡的
|
|
243
|
+
* taskcard 画布节点(含以它为端点的依赖边),bump 所属看板 updated。
|
|
244
|
+
* node/边清理用内联 SQL(board-store.deleteNode 自开事务,不能嵌套调用)。
|
|
245
|
+
*/
|
|
246
|
+
export function deleteCard(id: string): void {
|
|
247
|
+
const db = getDb();
|
|
248
|
+
const card = getCardRow(id);
|
|
249
|
+
if (!card) return;
|
|
250
|
+
|
|
251
|
+
db.exec("BEGIN");
|
|
252
|
+
try {
|
|
253
|
+
db.prepare("DELETE FROM task_card_links WHERE card_id = ? OR target_card_id = ?").run(id, id);
|
|
254
|
+
db.prepare("DELETE FROM task_card_questions WHERE card_id = ?").run(id);
|
|
255
|
+
|
|
256
|
+
// 找该卡的 taskcard 画布节点(可能多个/可能没有;排除只读系统看板)
|
|
257
|
+
const nodes = db
|
|
258
|
+
.prepare("SELECT id, board_id AS boardId FROM board_nodes WHERE kind = 'taskcard' AND ref_id = ? AND board_id != ?")
|
|
259
|
+
.all(id, SYSTEM_RUNNING_BOARD_ID) as Array<{ id: string; boardId: string }>;
|
|
260
|
+
for (const node of nodes) {
|
|
261
|
+
db.prepare("DELETE FROM board_edges WHERE board_id = ? AND (from_id = ? OR to_id = ?)").run(
|
|
262
|
+
node.boardId,
|
|
263
|
+
node.id,
|
|
264
|
+
node.id,
|
|
265
|
+
);
|
|
266
|
+
db.prepare("DELETE FROM board_nodes WHERE id = ?").run(node.id);
|
|
267
|
+
db.prepare("UPDATE boards SET updated = ? WHERE id = ?").run(now(), node.boardId);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
db.prepare("DELETE FROM task_cards WHERE id = ?").run(id);
|
|
271
|
+
db.exec("COMMIT");
|
|
272
|
+
} catch (error) {
|
|
273
|
+
db.exec("ROLLBACK");
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// 依赖(前置/关联)—— 同看板,真相源在 task_card_links,画布边由 syncCardEdges 派生
|
|
280
|
+
// ============================================================================
|
|
281
|
+
|
|
282
|
+
const LINK_COLUMNS =
|
|
283
|
+
"id, card_id AS cardId, target_card_id AS targetCardId, kind, created";
|
|
284
|
+
|
|
285
|
+
/** 本卡作为依赖方(出边)的全部依赖,按创建序。 */
|
|
286
|
+
export function listLinks(cardId: string): TaskCardLink[] {
|
|
287
|
+
return getDb()
|
|
288
|
+
.prepare(`SELECT ${LINK_COLUMNS} FROM task_card_links WHERE card_id = ? ORDER BY created, rowid`)
|
|
289
|
+
.all(cardId) as unknown as TaskCardLink[];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** 其他卡引用本卡(入边:被依赖方视角),按创建序。 */
|
|
293
|
+
export function listInboundLinks(cardId: string): TaskCardLink[] {
|
|
294
|
+
return getDb()
|
|
295
|
+
.prepare(`SELECT ${LINK_COLUMNS} FROM task_card_links WHERE target_card_id = ? ORDER BY created, rowid`)
|
|
296
|
+
.all(cardId) as unknown as TaskCardLink[];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function assertLinkKind(kind: LinkKind): void {
|
|
300
|
+
if (kind !== "prerequisite" && kind !== "related") {
|
|
301
|
+
throw new Error(`invalid link kind: ${String(kind)}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 加依赖边。跨看板 / 自环 / 目标卡不存在返回 null;重复(UNIQUE 冲突)幂等返回已有行。
|
|
307
|
+
*/
|
|
308
|
+
export function addLink(
|
|
309
|
+
cardId: string,
|
|
310
|
+
targetCardId: string,
|
|
311
|
+
kind: LinkKind,
|
|
312
|
+
): TaskCardLink | null {
|
|
313
|
+
assertLinkKind(kind);
|
|
314
|
+
if (cardId === targetCardId) return null;
|
|
315
|
+
const card = getCard(cardId);
|
|
316
|
+
const target = getCard(targetCardId);
|
|
317
|
+
if (!card || !target) return null;
|
|
318
|
+
if (card.boardId !== target.boardId) return null; // 任务以看板为界,不允许跨看板
|
|
319
|
+
|
|
320
|
+
getDb()
|
|
321
|
+
.prepare(
|
|
322
|
+
"INSERT OR IGNORE INTO task_card_links (id, card_id, target_card_id, kind, created) VALUES (?, ?, ?, ?, ?)",
|
|
323
|
+
)
|
|
324
|
+
.run(randomUUID(), cardId, targetCardId, kind, now());
|
|
325
|
+
return getDb()
|
|
326
|
+
.prepare(`SELECT ${LINK_COLUMNS} FROM task_card_links WHERE card_id = ? AND target_card_id = ? AND kind = ?`)
|
|
327
|
+
.get(cardId, targetCardId, kind) as TaskCardLink | undefined ?? null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** 删依赖边。 */
|
|
331
|
+
export function removeLink(id: string): void {
|
|
332
|
+
getDb().prepare("DELETE FROM task_card_links WHERE id = ?").run(id);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 全量替换某卡的依赖(prerequisite + related)。事务内删旧插新。
|
|
337
|
+
* 目标卡必须存在且同看板,否则抛错(防脏数据)。
|
|
338
|
+
*/
|
|
339
|
+
export function replaceLinks(
|
|
340
|
+
cardId: string,
|
|
341
|
+
prerequisites: string[],
|
|
342
|
+
related: string[],
|
|
343
|
+
): void {
|
|
344
|
+
const db = getDb();
|
|
345
|
+
const card = getCard(cardId);
|
|
346
|
+
if (!card) throw new Error(`task card not found: ${cardId}`);
|
|
347
|
+
const validateTarget = (targetId: string, kind: LinkKind) => {
|
|
348
|
+
if (targetId === cardId) throw new Error(`${kind} target 不能是自身`);
|
|
349
|
+
const target = getCard(targetId);
|
|
350
|
+
if (!target) throw new Error(`${kind} target not found: ${targetId}`);
|
|
351
|
+
if (target.boardId !== card.boardId) {
|
|
352
|
+
throw new Error(`${kind} target 不允许跨看板: ${targetId}`);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
for (const t of prerequisites) validateTarget(t, "prerequisite");
|
|
356
|
+
for (const t of related) validateTarget(t, "related");
|
|
357
|
+
|
|
358
|
+
const ts = now();
|
|
359
|
+
db.exec("BEGIN");
|
|
360
|
+
try {
|
|
361
|
+
db.prepare("DELETE FROM task_card_links WHERE card_id = ?").run(cardId);
|
|
362
|
+
const ins = db.prepare(
|
|
363
|
+
"INSERT OR IGNORE INTO task_card_links (id, card_id, target_card_id, kind, created) VALUES (?, ?, ?, ?, ?)",
|
|
364
|
+
);
|
|
365
|
+
for (const t of prerequisites) ins.run(randomUUID(), cardId, t, "prerequisite", ts);
|
|
366
|
+
for (const t of related) ins.run(randomUUID(), cardId, t, "related", ts);
|
|
367
|
+
db.exec("COMMIT");
|
|
368
|
+
} catch (error) {
|
|
369
|
+
db.exec("ROLLBACK");
|
|
370
|
+
throw error;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ============================================================================
|
|
375
|
+
// 派发查询(调度器用)
|
|
376
|
+
// ============================================================================
|
|
377
|
+
|
|
378
|
+
function prerequisitesDone(card: TaskCard): boolean {
|
|
379
|
+
for (const link of listLinks(card.id)) {
|
|
380
|
+
if (link.kind !== "prerequisite") continue;
|
|
381
|
+
const target = getCard(link.targetCardId);
|
|
382
|
+
// 前置卡已被删除视为依赖解除(容忍脏数据)
|
|
383
|
+
if (target && target.execStatus !== "done") return false;
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 可派发卡:就绪=todo & (未开始 | 失败且重试未超上限) & 无前置或前置均 done。
|
|
390
|
+
* 按优先级降序、编号升序(高优先级先调度)。
|
|
391
|
+
* 排除 dispatch_token 非空的卡(另一实例正在派发中,等它完成/释放)。
|
|
392
|
+
*/
|
|
393
|
+
export function listDispatchableCards(): TaskCard[] {
|
|
394
|
+
const rows = getDb()
|
|
395
|
+
.prepare(
|
|
396
|
+
`SELECT ${CARD_COLUMNS} FROM task_cards
|
|
397
|
+
WHERE ready_status = 'todo' AND (exec_status = 'not_started' OR (exec_status = 'failed' AND retry_count < max_retries))
|
|
398
|
+
AND dispatch_token IS NULL
|
|
399
|
+
ORDER BY priority DESC, number`,
|
|
400
|
+
)
|
|
401
|
+
.all() as unknown as TaskCardRow[];
|
|
402
|
+
return rows.map(rowToCard).filter(prerequisitesDone);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 原子抢派发锁:条件 UPDATE 把卡从「可派发」翻转为「本实例持有 token 的派发中”。
|
|
407
|
+
* SQLite 单写者保证并发(多实例共库)下只有一条 UPDATE 成功(changes=1),
|
|
408
|
+
* 其余实例 changes=0 → 抢锁失败。返回 true = 抢到(本实例负责派发)。
|
|
409
|
+
* token 在派发完成后由 clearDispatchToken 释放(成功写 sessionId / 失败重试均释放)。
|
|
410
|
+
*/
|
|
411
|
+
export function acquireDispatchToken(cardId: string, token: string): boolean {
|
|
412
|
+
const res = getDb()
|
|
413
|
+
.prepare(
|
|
414
|
+
`UPDATE task_cards SET dispatch_token = ?1, updated = ?2
|
|
415
|
+
WHERE id = ?3
|
|
416
|
+
AND ready_status = 'todo'
|
|
417
|
+
AND (exec_status = 'not_started' OR (exec_status = 'failed' AND retry_count < max_retries))
|
|
418
|
+
AND dispatch_token IS NULL`,
|
|
419
|
+
)
|
|
420
|
+
.run(token, now(), cardId);
|
|
421
|
+
return Number(res.changes) > 0;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** 释放派发锁:token 匹配才清(防误清他人新抢到的锁)。 */
|
|
425
|
+
export function clearDispatchToken(cardId: string, token: string): void {
|
|
426
|
+
getDb()
|
|
427
|
+
.prepare(`UPDATE task_cards SET dispatch_token = NULL, updated = ?2 WHERE id = ?1 AND dispatch_token = ?3`)
|
|
428
|
+
.run(cardId, now(), token);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** 调度器已派发且正在运行的任务卡数(全局并发闸门用)。 */
|
|
432
|
+
export function countRunningDispatched(): number {
|
|
433
|
+
const row = getDb()
|
|
434
|
+
.prepare("SELECT COUNT(*) AS n FROM task_cards WHERE exec_status = 'running'")
|
|
435
|
+
.get() as { n: number };
|
|
436
|
+
return row.n;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** 调度器已派发且正在运行的任务卡列表(看板调度状态展示用),按编号升序。 */
|
|
440
|
+
export function listRunningDispatched(): TaskCard[] {
|
|
441
|
+
const rows = getDb()
|
|
442
|
+
.prepare(`SELECT ${CARD_COLUMNS} FROM task_cards WHERE exec_status = 'running' ORDER BY number`)
|
|
443
|
+
.all() as unknown as TaskCardRow[];
|
|
444
|
+
return rows.map(rowToCard);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/** 按执行状态枚举取卡列表(S3 审核/巡检用),按编号升序。 */
|
|
448
|
+
export function listCardsByExecStatus(statuses: ExecStatus[]): TaskCard[] {
|
|
449
|
+
const placeholders = statuses.map(() => "?").join(",");
|
|
450
|
+
const rows = getDb()
|
|
451
|
+
.prepare(`SELECT ${CARD_COLUMNS} FROM task_cards WHERE exec_status IN (${placeholders}) ORDER BY number`)
|
|
452
|
+
.all(...statuses) as unknown as TaskCardRow[];
|
|
453
|
+
return rows.map(rowToCard);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ============================================================================
|
|
457
|
+
// 待回答队列(S3)—— task_card_questions 表
|
|
458
|
+
// ============================================================================
|
|
459
|
+
|
|
460
|
+
export type QuestionStatus = "pending" | "answered";
|
|
461
|
+
|
|
462
|
+
export interface TaskCardQuestion {
|
|
463
|
+
id: string;
|
|
464
|
+
cardId: string;
|
|
465
|
+
sessionId: string;
|
|
466
|
+
question: string;
|
|
467
|
+
status: QuestionStatus;
|
|
468
|
+
answer: string | null;
|
|
469
|
+
created: number;
|
|
470
|
+
answered: number | null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
interface TaskCardQuestionRow {
|
|
474
|
+
id: string;
|
|
475
|
+
cardId: string;
|
|
476
|
+
sessionId: string;
|
|
477
|
+
question: string;
|
|
478
|
+
status: QuestionStatus;
|
|
479
|
+
answer: string | null;
|
|
480
|
+
created: number;
|
|
481
|
+
answered: number | null;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const QUESTION_COLUMNS =
|
|
485
|
+
"id, card_id AS cardId, session_id AS sessionId, question, status, answer, created, answered";
|
|
486
|
+
|
|
487
|
+
function rowToQuestion(row: TaskCardQuestionRow): TaskCardQuestion {
|
|
488
|
+
return { ...row };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/** 建问答记录(AI 提问入队)。返回新记录。 */
|
|
492
|
+
export function createQuestion(
|
|
493
|
+
cardId: string,
|
|
494
|
+
sessionId: string,
|
|
495
|
+
question: string,
|
|
496
|
+
): TaskCardQuestion {
|
|
497
|
+
const id = randomUUID();
|
|
498
|
+
const ts = Date.now();
|
|
499
|
+
getDb()
|
|
500
|
+
.prepare(
|
|
501
|
+
"INSERT INTO task_card_questions (id, card_id, session_id, question, status, created) VALUES (?, ?, ?, ?, 'pending', ?)",
|
|
502
|
+
)
|
|
503
|
+
.run(id, cardId, sessionId, question, ts);
|
|
504
|
+
return rowToQuestion(getDb().prepare(`SELECT ${QUESTION_COLUMNS} FROM task_card_questions WHERE id = ?`).get(id) as unknown as TaskCardQuestionRow);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** 问答列表:status 过滤(pending/answered/全部),按创建序。 */
|
|
508
|
+
export function listQuestions(status?: QuestionStatus | "all"): TaskCardQuestion[] {
|
|
509
|
+
const where = status && status !== "all" ? "WHERE status = ?" : "";
|
|
510
|
+
const params = status && status !== "all" ? [status] : [];
|
|
511
|
+
const rows = getDb()
|
|
512
|
+
.prepare(`SELECT ${QUESTION_COLUMNS} FROM task_card_questions ${where} ORDER BY created`)
|
|
513
|
+
.all(...params) as unknown as TaskCardQuestionRow[];
|
|
514
|
+
return rows.map(rowToQuestion);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** 某卡的全部问答,按创建序。 */
|
|
518
|
+
export function listCardQuestions(cardId: string): TaskCardQuestion[] {
|
|
519
|
+
const rows = getDb()
|
|
520
|
+
.prepare(`SELECT ${QUESTION_COLUMNS} FROM task_card_questions WHERE card_id = ? ORDER BY created`)
|
|
521
|
+
.all(cardId) as unknown as TaskCardQuestionRow[];
|
|
522
|
+
return rows.map(rowToQuestion);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** 待回答数(侧栏/看板角标用)。 */
|
|
526
|
+
export function countPendingQuestions(): number {
|
|
527
|
+
const row = getDb()
|
|
528
|
+
.prepare("SELECT COUNT(*) AS n FROM task_card_questions WHERE status = 'pending'")
|
|
529
|
+
.get() as { n: number };
|
|
530
|
+
return row.n;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/** 用户回答:status→answered + answer 落库 + answered 时间。记录不存在返回 null。 */
|
|
534
|
+
export function answerQuestion(id: string, answer: string): TaskCardQuestion | null {
|
|
535
|
+
const db = getDb();
|
|
536
|
+
const existing = getDb().prepare(`SELECT ${QUESTION_COLUMNS} FROM task_card_questions WHERE id = ?`).get(id) as TaskCardQuestionRow | undefined;
|
|
537
|
+
if (!existing) return null;
|
|
538
|
+
if (existing.status === "answered") return rowToQuestion(existing);
|
|
539
|
+
const ts = Date.now();
|
|
540
|
+
db.prepare("UPDATE task_card_questions SET status = 'answered', answer = ?, answered = ? WHERE id = ?")
|
|
541
|
+
.run(answer, ts, id);
|
|
542
|
+
return rowToQuestion(getDb().prepare(`SELECT ${QUESTION_COLUMNS} FROM task_card_questions WHERE id = ?`).get(id) as unknown as TaskCardQuestionRow);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/** 取一张卡最新的待回答记录(回复队列续会话用),无则 null。 */
|
|
546
|
+
export function listAnswerableQuestions(): TaskCardQuestion[] {
|
|
547
|
+
// 卡 exec_status='waiting_reply' 且其最新 answered 记录尚未续会话
|
|
548
|
+
const rows = getDb()
|
|
549
|
+
.prepare(
|
|
550
|
+
`SELECT q.id, q.card_id AS cardId, q.session_id AS sessionId, q.question, q.status, q.answer, q.created, q.answered
|
|
551
|
+
FROM task_card_questions q
|
|
552
|
+
JOIN task_cards c ON c.id = q.card_id
|
|
553
|
+
WHERE q.status = 'answered' AND c.exec_status = 'waiting_reply'
|
|
554
|
+
AND q.id = (SELECT q2.id FROM task_card_questions q2 WHERE q2.card_id = q.card_id AND q2.status='answered' ORDER BY q2.answered DESC, q2.created DESC LIMIT 1)
|
|
555
|
+
ORDER BY q.answered`,
|
|
556
|
+
)
|
|
557
|
+
.all() as unknown as TaskCardQuestionRow[];
|
|
558
|
+
return rows.map(rowToQuestion);
|
|
559
|
+
}
|