@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,200 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { createJiti } from "jiti";
|
|
7
|
+
|
|
8
|
+
const jiti = createJiti(import.meta.url);
|
|
9
|
+
const {
|
|
10
|
+
normalizeModelsConfigCosts,
|
|
11
|
+
readModelsConfig,
|
|
12
|
+
writeModelsConfig,
|
|
13
|
+
} = await jiti.import("./models-config-store.ts");
|
|
14
|
+
const { invalidateModelsCache, loadModelsWithCache } = await jiti.import("./models-cache.ts");
|
|
15
|
+
const { buildSessionContext, getSessionEntries } = await jiti.import("./session-reader.ts");
|
|
16
|
+
|
|
17
|
+
function createTempRoot(t) {
|
|
18
|
+
const root = mkdtempSync(join(tmpdir(), "pi-web-models-config-"));
|
|
19
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
20
|
+
return root;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function modelsData(id) {
|
|
24
|
+
return {
|
|
25
|
+
models: { [`provider:${id}`]: id },
|
|
26
|
+
modelList: [{ id, name: id, provider: "provider" }],
|
|
27
|
+
defaultModel: null,
|
|
28
|
+
thinkingLevels: {},
|
|
29
|
+
thinkingLevelMaps: {},
|
|
30
|
+
thinkingLevelPins: {},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("saving models.json atomically invalidates the model-list cache", async (t) => {
|
|
35
|
+
const root = createTempRoot(t);
|
|
36
|
+
const modelsPath = join(root, "agent", "models.json");
|
|
37
|
+
const config = {
|
|
38
|
+
providers: {
|
|
39
|
+
acme: {
|
|
40
|
+
baseUrl: "https://models.example.test/v1",
|
|
41
|
+
api: "openai-completions",
|
|
42
|
+
models: [{ id: "acme-2" }],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
let loads = 0;
|
|
47
|
+
|
|
48
|
+
invalidateModelsCache();
|
|
49
|
+
await loadModelsWithCache(root, async () => modelsData(`load-${++loads}`));
|
|
50
|
+
writeModelsConfig(config, modelsPath);
|
|
51
|
+
const reloaded = await loadModelsWithCache(root, async () => modelsData(`load-${++loads}`));
|
|
52
|
+
|
|
53
|
+
assert.equal(loads, 2);
|
|
54
|
+
assert.equal(reloaded.modelList[0].id, "load-2");
|
|
55
|
+
assert.deepEqual(readModelsConfig(modelsPath), config);
|
|
56
|
+
assert.deepEqual(readdirSync(join(root, "agent")), ["models.json"]);
|
|
57
|
+
if (process.platform !== "win32") {
|
|
58
|
+
assert.equal(statSync(modelsPath).mode & 0o777, 0o600);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("models.json writes fill partial cost groups with zero and remove empty groups", (t) => {
|
|
63
|
+
const root = createTempRoot(t);
|
|
64
|
+
const modelsPath = join(root, "agent", "models.json");
|
|
65
|
+
const config = {
|
|
66
|
+
providers: {
|
|
67
|
+
acme: {
|
|
68
|
+
models: [
|
|
69
|
+
{ id: "empty-cost", cost: {} },
|
|
70
|
+
{ id: "partial-cost", cost: { input: 1, output: 2, cacheRead: 0.1 } },
|
|
71
|
+
{ id: "complete-cost", cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 1.1 } },
|
|
72
|
+
],
|
|
73
|
+
modelOverrides: {
|
|
74
|
+
inherited: { cost: { input: 3 } },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const normalized = normalizeModelsConfigCosts(config);
|
|
81
|
+
assert.deepEqual(normalized.providers.acme.models, [
|
|
82
|
+
{ id: "empty-cost" },
|
|
83
|
+
{ id: "partial-cost", cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 0 } },
|
|
84
|
+
{ id: "complete-cost", cost: { input: 1, output: 2, cacheRead: 0.1, cacheWrite: 1.1 } },
|
|
85
|
+
]);
|
|
86
|
+
assert.deepEqual(normalized.providers.acme.modelOverrides, {
|
|
87
|
+
inherited: { cost: { input: 3 } },
|
|
88
|
+
});
|
|
89
|
+
assert.deepEqual(config.providers.acme.models[0], { id: "empty-cost", cost: {} });
|
|
90
|
+
|
|
91
|
+
writeModelsConfig(config, modelsPath);
|
|
92
|
+
assert.deepEqual(readModelsConfig(modelsPath), normalized);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("saving models.json drops blank model rows without hiding other schema errors", (t) => {
|
|
96
|
+
const root = createTempRoot(t);
|
|
97
|
+
const modelsPath = join(root, "agent", "models.json");
|
|
98
|
+
|
|
99
|
+
writeModelsConfig({
|
|
100
|
+
providers: {
|
|
101
|
+
acme: {
|
|
102
|
+
baseUrl: "https://models.example.test/v1",
|
|
103
|
+
api: "openai-completions",
|
|
104
|
+
models: [
|
|
105
|
+
{ id: "working-model", cost: { input: 1 } },
|
|
106
|
+
{ id: "" },
|
|
107
|
+
{ id: " " },
|
|
108
|
+
{ id: 42 },
|
|
109
|
+
{ name: "Missing identifier" },
|
|
110
|
+
null,
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}, modelsPath);
|
|
115
|
+
|
|
116
|
+
assert.deepEqual(readModelsConfig(modelsPath), {
|
|
117
|
+
providers: {
|
|
118
|
+
acme: {
|
|
119
|
+
baseUrl: "https://models.example.test/v1",
|
|
120
|
+
api: "openai-completions",
|
|
121
|
+
models: [
|
|
122
|
+
{ id: "working-model", cost: { input: 1, output: 0, cacheRead: 0, cacheWrite: 0 } },
|
|
123
|
+
{ id: 42 },
|
|
124
|
+
{ name: "Missing identifier" },
|
|
125
|
+
null,
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("an existing session opens after its historical model is removed from config", (t) => {
|
|
133
|
+
const root = createTempRoot(t);
|
|
134
|
+
const sessionPath = join(root, "session.jsonl");
|
|
135
|
+
const modelsPath = join(root, "models.json");
|
|
136
|
+
const records = [
|
|
137
|
+
{
|
|
138
|
+
type: "session",
|
|
139
|
+
version: 3,
|
|
140
|
+
id: "existing-session",
|
|
141
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
142
|
+
cwd: root,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: "model_change",
|
|
146
|
+
id: "model-old",
|
|
147
|
+
parentId: null,
|
|
148
|
+
provider: "retired-provider",
|
|
149
|
+
modelId: "retired-model",
|
|
150
|
+
timestamp: "2026-01-01T00:00:01.000Z",
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: "message",
|
|
154
|
+
id: "user-1",
|
|
155
|
+
parentId: "model-old",
|
|
156
|
+
timestamp: "2026-01-01T00:00:02.000Z",
|
|
157
|
+
message: { role: "user", content: "keep this conversation" },
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
type: "message",
|
|
161
|
+
id: "assistant-1",
|
|
162
|
+
parentId: "user-1",
|
|
163
|
+
timestamp: "2026-01-01T00:00:03.000Z",
|
|
164
|
+
message: {
|
|
165
|
+
role: "assistant",
|
|
166
|
+
provider: "retired-provider",
|
|
167
|
+
model: "retired-model",
|
|
168
|
+
content: [{ type: "text", text: "still readable" }],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
writeFileSync(sessionPath, `${records.map((record) => JSON.stringify(record)).join("\n")}\n`);
|
|
173
|
+
|
|
174
|
+
writeModelsConfig({
|
|
175
|
+
providers: {
|
|
176
|
+
"retired-provider": {
|
|
177
|
+
baseUrl: "https://retired.example.test/v1",
|
|
178
|
+
api: "openai-completions",
|
|
179
|
+
models: [{ id: "retired-model" }],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
}, modelsPath);
|
|
183
|
+
const beforeChange = buildSessionContext(getSessionEntries(sessionPath));
|
|
184
|
+
assert.equal(beforeChange.messages[1].content[0].text, "still readable");
|
|
185
|
+
|
|
186
|
+
writeModelsConfig({
|
|
187
|
+
providers: {
|
|
188
|
+
replacement: {
|
|
189
|
+
baseUrl: "https://replacement.example.test/v1",
|
|
190
|
+
api: "openai-completions",
|
|
191
|
+
models: [{ id: "replacement-model" }],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
}, modelsPath);
|
|
195
|
+
|
|
196
|
+
const afterChange = buildSessionContext(getSessionEntries(sessionPath));
|
|
197
|
+
assert.deepEqual(afterChange.entryIds, ["user-1", "assistant-1"]);
|
|
198
|
+
assert.equal(afterChange.messages[0].content, "keep this conversation");
|
|
199
|
+
assert.equal(afterChange.messages[1].content[0].text, "still readable");
|
|
200
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { writePrivateFileAtomicSync } from "./atomic-file";
|
|
5
|
+
import { invalidateModelsCache } from "./models-cache";
|
|
6
|
+
|
|
7
|
+
const MODEL_COST_KEYS = ["input", "output", "cacheRead", "cacheWrite"] as const;
|
|
8
|
+
|
|
9
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeModelCost(value: unknown): Record<string, unknown> | undefined {
|
|
14
|
+
if (!isRecord(value)) return undefined;
|
|
15
|
+
const providedKeys = MODEL_COST_KEYS.filter((key) => value[key] !== undefined);
|
|
16
|
+
if (providedKeys.length === 0) return undefined;
|
|
17
|
+
if (providedKeys.some((key) => (
|
|
18
|
+
typeof value[key] !== "number" || !Number.isFinite(value[key])
|
|
19
|
+
))) return undefined;
|
|
20
|
+
|
|
21
|
+
return Object.fromEntries([
|
|
22
|
+
...Object.entries(value),
|
|
23
|
+
...MODEL_COST_KEYS.map((key) => [key, value[key] ?? 0]),
|
|
24
|
+
]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Complete partial cost groups with zero; omit a cost group only when it is empty. */
|
|
28
|
+
export function normalizeModelsConfigCosts(
|
|
29
|
+
data: Record<string, unknown>,
|
|
30
|
+
): Record<string, unknown> {
|
|
31
|
+
const normalized = structuredClone(data);
|
|
32
|
+
if (!isRecord(normalized.providers)) return normalized;
|
|
33
|
+
|
|
34
|
+
for (const provider of Object.values(normalized.providers)) {
|
|
35
|
+
if (!isRecord(provider) || !Array.isArray(provider.models)) continue;
|
|
36
|
+
for (const model of provider.models) {
|
|
37
|
+
if (!isRecord(model) || !("cost" in model)) continue;
|
|
38
|
+
const cost = normalizeModelCost(model.cost);
|
|
39
|
+
if (cost) model.cost = cost;
|
|
40
|
+
else delete model.cost;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function sanitizeModelsConfig(data: Record<string, unknown>): Record<string, unknown> {
|
|
47
|
+
if (!isRecord(data.providers)) return data;
|
|
48
|
+
|
|
49
|
+
const providers = Object.fromEntries(Object.entries(data.providers).map(([providerId, provider]) => {
|
|
50
|
+
if (!isRecord(provider) || !Array.isArray(provider.models)) return [providerId, provider];
|
|
51
|
+
const models = provider.models.filter((model) => (
|
|
52
|
+
!isRecord(model) || typeof model.id !== "string" || model.id.trim().length > 0
|
|
53
|
+
));
|
|
54
|
+
return [providerId, { ...provider, models }];
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
return { ...data, providers };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getModelsConfigPath(): string {
|
|
61
|
+
return join(getAgentDir(), "models.json");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function readModelsConfig(
|
|
65
|
+
modelsPath = getModelsConfigPath(),
|
|
66
|
+
): Record<string, unknown> {
|
|
67
|
+
if (!existsSync(modelsPath)) return { providers: {} };
|
|
68
|
+
try {
|
|
69
|
+
return JSON.parse(readFileSync(modelsPath, "utf8")) as Record<string, unknown>;
|
|
70
|
+
} catch {
|
|
71
|
+
return { providers: {} };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function writeModelsConfig(
|
|
76
|
+
data: Record<string, unknown>,
|
|
77
|
+
modelsPath = getModelsConfigPath(),
|
|
78
|
+
): void {
|
|
79
|
+
const dir = dirname(modelsPath);
|
|
80
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
81
|
+
const normalized = normalizeModelsConfigCosts(sanitizeModelsConfig(data));
|
|
82
|
+
writePrivateFileAtomicSync(modelsPath, JSON.stringify(normalized, null, 2));
|
|
83
|
+
invalidateModelsCache();
|
|
84
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
|
|
5
|
+
const configUrl = new URL("../next.config.ts", import.meta.url);
|
|
6
|
+
const source = await readFile(configUrl, "utf8");
|
|
7
|
+
|
|
8
|
+
test("resolves config paths without CommonJS globals", async () => {
|
|
9
|
+
assert.doesNotMatch(source, /\b__dirname\b/);
|
|
10
|
+
assert.match(source, /dirname\(fileURLToPath\(import\.meta\.url\)\)/);
|
|
11
|
+
|
|
12
|
+
const config = await import(`${configUrl.href}?esm-test`);
|
|
13
|
+
assert.equal(typeof config.default.env.NEXT_PUBLIC_APP_VERSION, "string");
|
|
14
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { createJiti } from "jiti";
|
|
6
|
+
|
|
7
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
+
|
|
9
|
+
test("scopes Next.js output file tracing to the pi-web package", async () => {
|
|
10
|
+
const config = await createJiti(import.meta.url).import("../next.config.ts", { default: true });
|
|
11
|
+
|
|
12
|
+
assert.equal(config.outputFileTracingRoot, projectRoot);
|
|
13
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const packageJson = require("../package.json");
|
|
7
|
+
const {
|
|
8
|
+
MIN_NODE_VERSION,
|
|
9
|
+
getUnsupportedNodeVersionMessage,
|
|
10
|
+
isNodeVersionSupported,
|
|
11
|
+
} = require("../bin/node-version.js");
|
|
12
|
+
|
|
13
|
+
test("accepts the minimum supported Node.js version and newer versions", () => {
|
|
14
|
+
for (const version of ["22.19.0", "v22.19.0", "22.19.1", "23.0.0"]) {
|
|
15
|
+
assert.equal(isNodeVersionSupported(version), true, version);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("rejects older and invalid Node.js versions", () => {
|
|
20
|
+
for (const version of ["20.19.5", "22.18.99", "invalid"]) {
|
|
21
|
+
assert.equal(isNodeVersionSupported(version), false, version);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("keeps the package engine aligned with the startup check", () => {
|
|
26
|
+
assert.equal(packageJson.engines.node, `>=${MIN_NODE_VERSION}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("reports both the required and current Node.js versions", () => {
|
|
30
|
+
const message = getUnsupportedNodeVersionMessage("20.19.5");
|
|
31
|
+
assert.match(message, /requires Node\.js 22\.19\.0 or newer/);
|
|
32
|
+
assert.match(message, /Current Node\.js version: 20\.19\.5/);
|
|
33
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const {
|
|
7
|
+
normalizeStreamingToolCalls,
|
|
8
|
+
normalizeToolCalls,
|
|
9
|
+
} = await jiti.import("./normalize.ts");
|
|
10
|
+
|
|
11
|
+
function assistant(block) {
|
|
12
|
+
return {
|
|
13
|
+
role: "assistant",
|
|
14
|
+
provider: "anthropic",
|
|
15
|
+
model: "claude-test",
|
|
16
|
+
content: [block],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test("keeps SDK scratch input only for an in-flight snapshot", () => {
|
|
21
|
+
const message = assistant({
|
|
22
|
+
type: "toolCall",
|
|
23
|
+
id: "call-1",
|
|
24
|
+
name: "write",
|
|
25
|
+
arguments: { path: "/tmp/file" },
|
|
26
|
+
partialJson: '{"path":"/tmp/file","content":"hel',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
assert.deepEqual(normalizeStreamingToolCalls(message).content[0], {
|
|
30
|
+
type: "toolCall",
|
|
31
|
+
toolCallId: "call-1",
|
|
32
|
+
toolName: "write",
|
|
33
|
+
input: { path: "/tmp/file" },
|
|
34
|
+
rawInput: '{"path":"/tmp/file","content":"hel',
|
|
35
|
+
});
|
|
36
|
+
assert.deepEqual(normalizeToolCalls(message).content[0], {
|
|
37
|
+
type: "toolCall",
|
|
38
|
+
toolCallId: "call-1",
|
|
39
|
+
toolName: "write",
|
|
40
|
+
input: { path: "/tmp/file" },
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("removes a client raw buffer when normalizing a completed message", () => {
|
|
45
|
+
const message = assistant({
|
|
46
|
+
type: "toolCall",
|
|
47
|
+
toolCallId: "call-2",
|
|
48
|
+
toolName: "write",
|
|
49
|
+
input: { path: "/tmp/file", content: "complete" },
|
|
50
|
+
rawInput: "temporary",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
assert.equal(Object.hasOwn(normalizeToolCalls(message).content[0], "rawInput"), false);
|
|
54
|
+
});
|
package/lib/normalize.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { AgentMessage, AssistantMessage, ToolCallContent } from "./types";
|
|
2
|
+
|
|
3
|
+
function isObject(val: unknown): val is Record<string, unknown> {
|
|
4
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function streamingRawInput(block: Record<string, unknown>): string | undefined {
|
|
8
|
+
if (typeof block.rawInput === "string") return block.rawInput;
|
|
9
|
+
if (typeof block.partialJson === "string") return block.partialJson;
|
|
10
|
+
if (typeof block.partialArgs === "string") return block.partialArgs;
|
|
11
|
+
|
|
12
|
+
const customInput = isObject(block.customInput) ? block.customInput : null;
|
|
13
|
+
const property = customInput && typeof customInput.property === "string"
|
|
14
|
+
? customInput.property
|
|
15
|
+
: null;
|
|
16
|
+
const args = isObject(block.arguments) ? block.arguments : null;
|
|
17
|
+
return property && args && typeof args[property] === "string"
|
|
18
|
+
? args[property]
|
|
19
|
+
: undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeToolCallBlock(
|
|
23
|
+
block: unknown,
|
|
24
|
+
options: { includeStreamingRawInput?: boolean } = {},
|
|
25
|
+
): ToolCallContent | null {
|
|
26
|
+
if (!isObject(block) || block.type !== "toolCall") return null;
|
|
27
|
+
const normalized: ToolCallContent = {
|
|
28
|
+
type: "toolCall",
|
|
29
|
+
toolCallId: typeof block.toolCallId === "string" ? block.toolCallId : (typeof block.id === "string" ? block.id : ""),
|
|
30
|
+
toolName: typeof block.toolName === "string" ? block.toolName : (typeof block.name === "string" ? block.name : ""),
|
|
31
|
+
input: typeof block.input === "object" && block.input !== null && !Array.isArray(block.input)
|
|
32
|
+
? block.input as Record<string, unknown>
|
|
33
|
+
: (typeof block.arguments === "object" && block.arguments !== null && !Array.isArray(block.arguments)
|
|
34
|
+
? block.arguments as Record<string, unknown>
|
|
35
|
+
: {}),
|
|
36
|
+
};
|
|
37
|
+
const rawInput = options.includeStreamingRawInput ? streamingRawInput(block) : undefined;
|
|
38
|
+
return rawInput === undefined ? normalized : { ...normalized, rawInput };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeAssistantToolCalls(
|
|
42
|
+
msg: AgentMessage,
|
|
43
|
+
options: { includeStreamingRawInput?: boolean } = {},
|
|
44
|
+
): AgentMessage {
|
|
45
|
+
// Non-assistant roles (user, toolResult, bashExecution, custom) are returned
|
|
46
|
+
// unchanged — only assistant messages go through tool-call field normalization.
|
|
47
|
+
if (msg.role !== "assistant") return msg;
|
|
48
|
+
const content = (msg as AssistantMessage).content;
|
|
49
|
+
if (!Array.isArray(content)) return msg;
|
|
50
|
+
const normalized = content.map((block) => {
|
|
51
|
+
const result = normalizeToolCallBlock(block, options);
|
|
52
|
+
return result ?? block;
|
|
53
|
+
});
|
|
54
|
+
return { ...msg, content: normalized } as AgentMessage;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function normalizeToolCalls(msg: AgentMessage): AgentMessage {
|
|
58
|
+
return normalizeAssistantToolCalls(msg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeStreamingToolCalls(msg: AgentMessage): AgentMessage {
|
|
62
|
+
return normalizeAssistantToolCalls(msg, { includeStreamingRawInput: true });
|
|
63
|
+
}
|
package/lib/npx.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { execPath } from "process";
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Locate `npx-cli.js` shipped with the running Node.js installation.
|
|
11
|
+
*
|
|
12
|
+
* On Windows the `npx` on PATH is actually `npx.cmd`, which Node.js (since
|
|
13
|
+
* 20.12 due to CVE-2024-27980) refuses to spawn from `execFile`/`spawn`
|
|
14
|
+
* without `shell: true`. Going through a shell reintroduces quoting bugs for
|
|
15
|
+
* user-supplied args. Instead we find the real `npx-cli.js` and invoke it
|
|
16
|
+
* directly via the current `node` binary, which works identically on every
|
|
17
|
+
* platform and needs no shell.
|
|
18
|
+
*/
|
|
19
|
+
function findNpxCli(): string | null {
|
|
20
|
+
const nodeDir = dirname(execPath);
|
|
21
|
+
const candidates = [
|
|
22
|
+
// Windows MSI installer layout: node.exe and node_modules share a dir
|
|
23
|
+
join(nodeDir, "node_modules", "npm", "bin", "npx-cli.js"),
|
|
24
|
+
// Unix layout: .../bin/node + .../lib/node_modules/npm/bin/npx-cli.js
|
|
25
|
+
join(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npx-cli.js"),
|
|
26
|
+
];
|
|
27
|
+
for (const p of candidates) {
|
|
28
|
+
try {
|
|
29
|
+
if (existsSync(p)) return p;
|
|
30
|
+
} catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RunNpxOptions {
|
|
38
|
+
timeout?: number;
|
|
39
|
+
cwd?: string;
|
|
40
|
+
env?: NodeJS.ProcessEnv;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RunNpxResult {
|
|
44
|
+
stdout: string;
|
|
45
|
+
stderr: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Cross-platform wrapper for invoking `npx <args>` without ever using a
|
|
50
|
+
* shell, so user-controlled arguments are never interpreted as shell syntax.
|
|
51
|
+
*/
|
|
52
|
+
export async function runNpx(args: string[], opts: RunNpxOptions = {}): Promise<RunNpxResult> {
|
|
53
|
+
const npxCli = findNpxCli();
|
|
54
|
+
const { command, commandArgs } = npxCli
|
|
55
|
+
? { command: execPath, commandArgs: [npxCli, ...args] }
|
|
56
|
+
: { command: "npx", commandArgs: args };
|
|
57
|
+
return execFileAsync(command, commandArgs, {
|
|
58
|
+
timeout: opts.timeout,
|
|
59
|
+
cwd: opts.cwd,
|
|
60
|
+
env: opts.env,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
|
|
5
|
+
const jiti = createJiti(import.meta.url);
|
|
6
|
+
const {
|
|
7
|
+
clampPanelWidth,
|
|
8
|
+
getDefaultRightPanelWidth,
|
|
9
|
+
getRightPanelMaxWidth,
|
|
10
|
+
getSidebarMaxWidth,
|
|
11
|
+
} = await jiti.import("./panel-layout.ts");
|
|
12
|
+
|
|
13
|
+
test("clamps panel widths to finite bounds", () => {
|
|
14
|
+
assert.equal(clampPanelWidth(420.4, 180, 480), 420);
|
|
15
|
+
assert.equal(clampPanelWidth(120, 180, 480), 180);
|
|
16
|
+
assert.equal(clampPanelWidth(600, 180, 480), 480);
|
|
17
|
+
assert.equal(clampPanelWidth(Number.NaN, 180, 480), 180);
|
|
18
|
+
assert.equal(clampPanelWidth(200, 300, 250), 300);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("keeps the responsive right panel default within useful limits", () => {
|
|
22
|
+
assert.equal(getDefaultRightPanelWidth(700), 360);
|
|
23
|
+
assert.equal(getDefaultRightPanelWidth(1366), 574);
|
|
24
|
+
assert.equal(getDefaultRightPanelWidth(1920), 640);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("reserves chat space while split panels are visible", () => {
|
|
28
|
+
assert.equal(getSidebarMaxWidth({
|
|
29
|
+
viewportWidth: 700,
|
|
30
|
+
rightPanelOpen: true,
|
|
31
|
+
rightPanelWidth: 560,
|
|
32
|
+
}), 380);
|
|
33
|
+
assert.equal(getSidebarMaxWidth({
|
|
34
|
+
viewportWidth: 1366,
|
|
35
|
+
rightPanelOpen: true,
|
|
36
|
+
rightPanelWidth: 686,
|
|
37
|
+
}), 260);
|
|
38
|
+
assert.equal(getRightPanelMaxWidth({
|
|
39
|
+
viewportWidth: 1024,
|
|
40
|
+
sidebarOpen: true,
|
|
41
|
+
sidebarWidth: 260,
|
|
42
|
+
}), 344);
|
|
43
|
+
assert.equal(getRightPanelMaxWidth({
|
|
44
|
+
viewportWidth: 1366,
|
|
45
|
+
sidebarOpen: true,
|
|
46
|
+
sidebarWidth: 260,
|
|
47
|
+
}), 686);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("does not rewrite desktop widths while the file panel is in overlay mode", () => {
|
|
51
|
+
assert.equal(getRightPanelMaxWidth({
|
|
52
|
+
viewportWidth: 900,
|
|
53
|
+
sidebarOpen: true,
|
|
54
|
+
sidebarWidth: 480,
|
|
55
|
+
}), 1200);
|
|
56
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const MOBILE_MAX_WIDTH = 640;
|
|
2
|
+
export const SPLIT_PANEL_MIN_WIDTH = 960;
|
|
3
|
+
|
|
4
|
+
export const SIDEBAR_DEFAULT_WIDTH = 260;
|
|
5
|
+
export const SIDEBAR_MIN_WIDTH = 180;
|
|
6
|
+
export const SIDEBAR_MAX_WIDTH = 480;
|
|
7
|
+
|
|
8
|
+
export const RIGHT_PANEL_FALLBACK_WIDTH = 560;
|
|
9
|
+
export const RIGHT_PANEL_MIN_WIDTH = 300;
|
|
10
|
+
export const RIGHT_PANEL_MAX_WIDTH = 1200;
|
|
11
|
+
|
|
12
|
+
const COMPACT_CHAT_MIN_WIDTH = 320;
|
|
13
|
+
const DESKTOP_CHAT_MIN_WIDTH = 420;
|
|
14
|
+
|
|
15
|
+
export function clampPanelWidth(width: number, minWidth: number, maxWidth: number): number {
|
|
16
|
+
const finiteWidth = Number.isFinite(width) ? width : minWidth;
|
|
17
|
+
const effectiveMax = Math.max(minWidth, maxWidth);
|
|
18
|
+
return Math.round(Math.max(minWidth, Math.min(effectiveMax, finiteWidth)));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getDefaultRightPanelWidth(viewportWidth: number): number {
|
|
22
|
+
return clampPanelWidth(viewportWidth * 0.42, 360, 640);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getSidebarMaxWidth(options: {
|
|
26
|
+
viewportWidth: number;
|
|
27
|
+
rightPanelOpen: boolean;
|
|
28
|
+
rightPanelWidth: number;
|
|
29
|
+
}): number {
|
|
30
|
+
const { viewportWidth, rightPanelOpen, rightPanelWidth } = options;
|
|
31
|
+
if (viewportWidth <= MOBILE_MAX_WIDTH) return SIDEBAR_MAX_WIDTH;
|
|
32
|
+
|
|
33
|
+
const compact = viewportWidth < SPLIT_PANEL_MIN_WIDTH;
|
|
34
|
+
const chatWidth = compact ? COMPACT_CHAT_MIN_WIDTH : DESKTOP_CHAT_MIN_WIDTH;
|
|
35
|
+
const visibleRightPanelWidth = !compact && rightPanelOpen ? rightPanelWidth : 0;
|
|
36
|
+
return Math.min(SIDEBAR_MAX_WIDTH, viewportWidth - chatWidth - visibleRightPanelWidth);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getRightPanelMaxWidth(options: {
|
|
40
|
+
viewportWidth: number;
|
|
41
|
+
sidebarOpen: boolean;
|
|
42
|
+
sidebarWidth: number;
|
|
43
|
+
}): number {
|
|
44
|
+
const { viewportWidth, sidebarOpen, sidebarWidth } = options;
|
|
45
|
+
if (viewportWidth < SPLIT_PANEL_MIN_WIDTH) return RIGHT_PANEL_MAX_WIDTH;
|
|
46
|
+
|
|
47
|
+
const visibleSidebarWidth = sidebarOpen ? sidebarWidth : 0;
|
|
48
|
+
return Math.min(
|
|
49
|
+
RIGHT_PANEL_MAX_WIDTH,
|
|
50
|
+
viewportWidth - DESKTOP_CHAT_MIN_WIDTH - visibleSidebarWidth,
|
|
51
|
+
);
|
|
52
|
+
}
|