@baique/pi-web-sky 0.1.39 → 0.1.41
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 +7 -12
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +51 -51
- package/.next/required-server-files.js +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +3 -45
- 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]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/route.js +2 -2
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/app-update/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/boards/[id]/reconcile/route.js +1 -0
- package/.next/server/app/api/boards/[id]/reconcile/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/boards/[id]/route.js +1 -1
- package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
- package/.next/server/app/api/boards/reorder/route.js +1 -1
- package/.next/server/app/api/boards/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +2 -2
- package/.next/server/app/api/default-cwd/route.js +2 -2
- package/.next/server/app/api/file-index/route.js +2 -2
- package/.next/server/app/api/files/[...path]/route.js +3 -3
- package/.next/server/app/api/files/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/mcp/route.js +1 -1
- package/.next/server/app/api/models/route.js +2 -2
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/route.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/plugins/route.js +2 -2
- package/.next/server/app/api/project-trust/route.js +2 -2
- package/.next/server/app/api/quota/route.js +1 -1
- package/.next/server/app/api/search/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
- package/.next/server/app/api/sessions/route.js +2 -2
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/route.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/update/route.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/route.js +1 -1
- package/.next/server/app/api/task-cards/[id]/route.js +1 -1
- package/.next/server/app/api/task-cards/route.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/route.js +1 -1
- package/.next/server/app/api/tasks/reorder/route.js +1 -1
- package/.next/server/app/api/tasks/route.js +1 -1
- package/.next/server/app/api/terminal/[id]/events/route.js +2 -2
- package/.next/server/app/api/terminal/[id]/route.js +2 -2
- package/.next/server/app/api/terminal/route.js +2 -2
- package/.next/server/app/api/worktrees/route.js +2 -2
- 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/page.js +7 -7
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +7 -12
- package/.next/server/chunks/1094.js +2 -2
- package/.next/server/chunks/1601.js +1 -1
- package/.next/server/chunks/1860.js +1 -1
- package/.next/server/chunks/2075.js +1 -1
- package/.next/server/chunks/2169.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2524.js +1 -0
- package/.next/server/chunks/3012.js +1 -1
- package/.next/server/chunks/{5760.js → 3379.js} +1 -1
- package/.next/server/chunks/3500.js +1 -1
- package/.next/server/chunks/4226.js +5 -2
- package/.next/server/chunks/4267.js +1 -1
- package/.next/server/chunks/4563.js +1 -1
- package/.next/server/chunks/5318.js +1 -1
- package/.next/server/chunks/542.js +3 -44
- package/.next/server/chunks/5794.js +1 -1
- package/.next/server/chunks/6052.js +1 -1
- package/.next/server/chunks/6253.js +1 -1
- package/.next/server/chunks/6288.js +1 -1
- package/.next/server/chunks/6366.js +1 -1
- package/.next/server/chunks/673.js +1 -1
- package/.next/server/chunks/6916.js +2 -2
- package/.next/server/chunks/7128.js +43 -0
- package/.next/server/chunks/7265.js +2 -2
- package/.next/server/chunks/7799.js +1 -1
- package/.next/server/chunks/8106.js +1 -1
- package/.next/server/chunks/8333.js +1 -1
- package/.next/server/chunks/8604.js +1 -1
- package/.next/server/chunks/861.js +1 -0
- package/.next/server/chunks/8713.js +1 -1
- package/.next/server/chunks/8782.js +2 -2
- package/.next/server/chunks/898.js +1 -1
- package/.next/server/chunks/922.js +2 -2
- package/.next/server/chunks/9280.js +6 -6
- package/.next/server/chunks/9510.js +1 -1
- package/.next/server/instrumentation.js +1 -1
- package/.next/server/instrumentation.js.nft.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +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/{103.23142a67b02246a8.js → 103.6c91a059aa01d92c.js} +2 -2
- package/.next/static/chunks/{1032.12b382a9096ab2a4.js → 1032.9e8f793d46f463f8.js} +1 -1
- package/.next/static/chunks/{1063.775c442bdbeef08b.js → 1063.470df8a037dc8409.js} +1 -1
- package/.next/static/chunks/1a258343.f4fc2c133917ded8.js +2 -0
- package/.next/static/chunks/{1ba63609.b2ae90f43319a29e.js → 1ba63609.569928c7234f832c.js} +1 -1
- package/.next/static/chunks/{2181.ba21239d3ac39f81.js → 2181.b8dda21843ef5e74.js} +1 -1
- package/.next/static/chunks/{2325.23caf22075e51aa5.js → 2325.23e538886331cf64.js} +1 -1
- package/.next/static/chunks/{2356.523277bb8c6bf72a.js → 2356.b7312589c345edec.js} +1 -1
- package/.next/static/chunks/{268.72794ee2773c2060.js → 268.a1d90aca268be18a.js} +1 -1
- package/.next/static/chunks/{2695.6f49435bae1e6f33.js → 2695.5624aa3aae540e87.js} +1 -1
- package/.next/static/chunks/{2703.495a65ed14b2f094.js → 2703.097bc88286733740.js} +1 -1
- package/.next/static/chunks/{2821.934bcf60fbdc28c6.js → 2821.d2392a93d192fe92.js} +1 -1
- package/.next/static/chunks/3308.2cd6f20e6c0f4e92.js +73 -0
- package/.next/static/chunks/{3336.a76f9485fe7cb762.js → 3336.9b1dc9ec3507d64f.js} +1 -1
- package/.next/static/chunks/3546.779d2c7646aa77cc.js +1 -0
- package/.next/static/chunks/{370.8e8f3cf8eb0e29dd.js → 370.cde2a84caa84054f.js} +1 -1
- package/.next/static/chunks/{3716.72bf4c9d7325debd.js → 3716.5bac496cea32ca8f.js} +1 -1
- package/.next/static/chunks/{3995.eca0f5bef1fc3ae5.js → 3995.cd112debcd64c610.js} +1 -1
- package/.next/static/chunks/{4004.74b9bcf5478b1663.js → 4004.a8f79ef62f32aa30.js} +2 -2
- package/.next/static/chunks/{4202.8440832386f023c7.js → 4202.96f5b1beb8b3e3f5.js} +1 -1
- package/.next/static/chunks/{4238.6f102dcbcf2ae339.js → 4238.28efbf9aa0c82d90.js} +1 -1
- package/.next/static/chunks/{4405.7a19458960a1058e.js → 4405.cda46a596a02aef7.js} +1 -1
- package/.next/static/chunks/54a60aa6.5e4421e7afde4de8.js +78 -0
- package/.next/static/chunks/{6121.eb9023b1cbc78ee2.js → 6121.5e650e714b402b6f.js} +1 -1
- package/.next/static/chunks/{6195.1153369f5f8b13a2.js → 6195.1b7f9dc127381bbc.js} +1 -1
- package/.next/static/chunks/6380.d148a8e2c701ad78.js +68 -0
- package/.next/static/chunks/6582.fb2c709abc2c5981.js +1 -0
- package/.next/static/chunks/6be7e44c.00d0163f409986f0.js +1 -0
- package/.next/static/chunks/{7331.2add3c4e125a2dba.js → 7331.4afa66d6353c8e07.js} +1 -1
- package/.next/static/chunks/752.a05a2904b96af4d0.js +1 -0
- package/.next/static/chunks/{8200.a36b9f8c983c6b9e.js → 8200.350d650649b977eb.js} +1 -1
- package/.next/static/chunks/{8885.cf60cac98bf8b70c.js → 8885.2c060cb45c85547b.js} +1 -1
- package/.next/static/chunks/{918.6a9bbe564e4a2d0f.js → 918.593eb466d35cb8ad.js} +1 -1
- package/.next/static/chunks/{9698.4d09a99b9d99f3e5.js → 9698.758755d0387e0e22.js} +2 -2
- package/.next/static/chunks/97.fb73c551e14b3612.js +2 -0
- package/.next/static/chunks/{993e5495.8797001fcfcf2889.js → 993e5495.12cdfbc3af5c79e9.js} +1 -1
- package/.next/static/chunks/{9974.9a0f79beca65e8c4.js → 9974.5ee982e074ad047e.js} +1 -1
- package/.next/static/chunks/app/_global-error/page-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/app-update/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/boards/[id]/reconcile/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/boards/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/boards/purge-orphans/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/boards/reorder/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/boards/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/home/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/models/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/quota/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/search/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/todos/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/skills/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/task-card-questions/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/task-cards/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/task-cards/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/task-scheduler/status/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/tasks/[id]/board/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/tasks/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/tasks/reorder/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/tasks/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/terminal/[id]/events/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/terminal/[id]/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/terminal/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/{layout-7761e430ed68b347.js → layout-2bec2662143a3d1b.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/app/page-e38c6cf3174fa143.js +89 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-bb8f79f321e26750.js +1 -0
- package/.next/static/chunks/webpack-b4924d2a22ad3650.js +1 -0
- package/.next/static/css/0c7244f7214af81f.css +1 -0
- package/.next/static/css/12456255969ac8de.css +31 -0
- package/.next/trace +8 -49
- package/.next/trace-build +1 -1
- package/.next/types/app/api/boards/[id]/{reconcile-task-edges → reconcile}/route.ts +3 -3
- package/.next/types/routes.d.ts +2 -7
- package/.next/types/validator.ts +3 -48
- package/README.md +6 -9
- package/THIRD_PARTY_NOTICES.md +10 -82
- package/bin/pi-web-sky.js +3 -3
- package/lib/board-reconcile-scheduler.ts +56 -0
- package/lib/board-reconcile.test.mjs +252 -0
- package/lib/board-reconcile.ts +421 -0
- package/lib/board-store.test.mjs +26 -4
- package/lib/board-store.ts +44 -6
- package/lib/board-types.ts +7 -2
- package/lib/i18n/messages/en.ts +1 -1
- package/lib/i18n/messages/zh-CN.ts +1 -1
- package/lib/session-delete.ts +5 -0
- package/lib/sqlite-db.ts +13 -1
- package/lib/task-card-store.test.mjs +16 -1
- package/lib/task-card-store.ts +37 -1
- package/lib/task-scheduler.test.mjs +79 -0
- package/lib/task-scheduler.ts +195 -21
- package/lib/yjs-room-server.mjs +139 -0
- package/package.json +12 -9
- package/server.mjs +7 -7
- package/.next/server/app/api/boards/[id]/canvas/route.js +0 -1
- package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +0 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route.js.nft.json +0 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/boards/[id]/edges/route.js +0 -1
- package/.next/server/app/api/boards/[id]/edges/route.js.nft.json +0 -1
- package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +0 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js.nft.json +0 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/boards/[id]/nodes/route.js +0 -1
- package/.next/server/app/api/boards/[id]/nodes/route.js.nft.json +0 -1
- package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +0 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js.nft.json +0 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +0 -1
- package/.next/static/chunks/3308.857c554428b3294e.js +0 -73
- package/.next/static/chunks/333.bf0651c789b2eb33.js +0 -1
- package/.next/static/chunks/5315.dba7515ae6e139ae.js +0 -4
- package/.next/static/chunks/54a60aa6.0e43cde7298ebcb7.js +0 -78
- package/.next/static/chunks/7999.879d5a20e8641752.js +0 -259
- package/.next/static/chunks/app/_global-error/page-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/app-update/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/canvas/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/edges/[eid]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/edges/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/nodes/[nid]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/nodes/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/reconcile-task-edges/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/purge-orphans/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/reorder/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/boards/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/home/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/mcp/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/models/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/models-config/catalog/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/project-trust/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/quota/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/search/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/todos/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/skills/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/task-card-questions/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/task-cards/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/task-cards/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/task-scheduler/status/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/tasks/[id]/board/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/tasks/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/tasks/reorder/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/tasks/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/terminal/[id]/events/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/terminal/[id]/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/terminal/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/app/page-2c87c41e7f848f45.js +0 -89
- package/.next/static/chunks/e3e7ddb2.a35db430b3564999.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d6f52627eddf9119.js +0 -1
- package/.next/static/chunks/webpack-4bbf04891e910713.js +0 -1
- package/.next/static/css/80c818c5781fbadc.css +0 -4
- package/.next/static/css/d3a2b5077b52c727.css +0 -1
- package/.next/types/app/api/boards/[id]/canvas/route.ts +0 -353
- package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +0 -353
- package/.next/types/app/api/boards/[id]/edges/route.ts +0 -353
- package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +0 -353
- package/.next/types/app/api/boards/[id]/nodes/route.ts +0 -353
- package/lib/board-search.test.mjs +0 -110
- package/lib/board-search.ts +0 -63
- package/lib/file-explorer-state.test.mjs +0 -63
- package/lib/file-explorer-state.ts +0 -36
- package/lib/sync-room-server.mjs +0 -137
- /package/.next/server/app/api/boards/[id]/{canvas → reconcile}/route.js.nft.json +0 -0
- /package/.next/static/{IzmOG04jJst1VpUvr_fr5 → 8V884w_cp5HEp_rRpjnCR}/_buildManifest.js +0 -0
- /package/.next/static/{IzmOG04jJst1VpUvr_fr5 → 8V884w_cp5HEp_rRpjnCR}/_ssgManifest.js +0 -0
|
@@ -1,110 +0,0 @@
|
|
|
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, { interopDefault: true, moduleCache: false });
|
|
6
|
-
const { collectSearchable, filterMatches, makeSnippet } = await jiti.import("./board-search.ts");
|
|
7
|
-
|
|
8
|
-
/** 构造 fake tldraw editor(单测只依赖 getCurrentPageShapes/getShape/getShapePageBounds) */
|
|
9
|
-
function fakeEditor(shapes) {
|
|
10
|
-
return {
|
|
11
|
-
getCurrentPageShapes: () => shapes,
|
|
12
|
-
getShape: (id) => shapes.find((s) => s.id === id),
|
|
13
|
-
getShapePageBounds: (id) => {
|
|
14
|
-
const s = shapes.find((x) => x.id === id);
|
|
15
|
-
return s ? { x: s.x ?? 0, y: s.y ?? 0, w: s.w ?? 100, h: s.h ?? 100 } : undefined;
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const shape = (id, type, props) => ({ id, type, props });
|
|
21
|
-
|
|
22
|
-
test("collectSearchable 收集会话卡标题 + 便笺正文,跳过空文本与无关 shape", () => {
|
|
23
|
-
const editor = fakeEditor([
|
|
24
|
-
shape("shape:a", "session-card", { title: "登录重构", sessionId: "s1" }),
|
|
25
|
-
shape("shape:b", "session-card", { title: " ", sessionId: "s2" }), // 空标题跳过
|
|
26
|
-
shape("shape:c", "sticky-note", { text: "便笺:token 校验" }),
|
|
27
|
-
shape("shape:d", "sticky-note", { text: "" }), // 空便笺跳过
|
|
28
|
-
shape("shape:e", "arrow", { label: "不能搜连线" }), // 无关 type 跳过
|
|
29
|
-
shape("shape:f", "text", { text: "普通文本不搜" }), // 无关 type 跳过
|
|
30
|
-
]);
|
|
31
|
-
const items = collectSearchable(editor);
|
|
32
|
-
assert.deepEqual(items.map((i) => i.text), ["登录重构", "便笺:token 校验"]);
|
|
33
|
-
assert.deepEqual(items.map((i) => i.kind), ["session-card", "sticky-note"]);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("filterMatches 中文子串命中", () => {
|
|
37
|
-
const editor = fakeEditor([
|
|
38
|
-
shape("a", "session-card", { title: "修复登录接口的 bug" }),
|
|
39
|
-
shape("b", "sticky-note", { text: "记录 token 过期逻辑" }),
|
|
40
|
-
]);
|
|
41
|
-
const items = collectSearchable(editor);
|
|
42
|
-
const hits = filterMatches(items, "登录");
|
|
43
|
-
assert.equal(hits.length, 1);
|
|
44
|
-
assert.equal(hits[0].item.shapeId, "a");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("filterMatches 大小写不敏感(英文)", () => {
|
|
48
|
-
const editor = fakeEditor([
|
|
49
|
-
shape("a", "session-card", { title: "Refactor Token Logic" }),
|
|
50
|
-
shape("b", "sticky-note", { text: "another note" }),
|
|
51
|
-
]);
|
|
52
|
-
const items = collectSearchable(editor);
|
|
53
|
-
assert.equal(filterMatches(items, "token").length, 1);
|
|
54
|
-
assert.equal(filterMatches(items, "TOKEN").length, 1);
|
|
55
|
-
assert.equal(filterMatches(items, "Token").length, 1);
|
|
56
|
-
assert.equal(filterMatches(items, "tokenz").length, 0);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("filterMatches 空 query 返回空", () => {
|
|
60
|
-
const editor = fakeEditor([shape("a", "session-card", { title: "anything" })]);
|
|
61
|
-
const items = collectSearchable(editor);
|
|
62
|
-
assert.equal(filterMatches(items, "").length, 0);
|
|
63
|
-
assert.equal(filterMatches(items, " ").length, 0);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("filterMatches 多命中保持画布遍历顺序", () => {
|
|
67
|
-
const editor = fakeEditor([
|
|
68
|
-
shape("a", "sticky-note", { text: "zzz 搜索 zzz" }),
|
|
69
|
-
shape("b", "session-card", { title: "aaa 搜索 bbb" }),
|
|
70
|
-
shape("c", "sticky-note", { text: "搜索收尾" }),
|
|
71
|
-
]);
|
|
72
|
-
const items = collectSearchable(editor);
|
|
73
|
-
const hits = filterMatches(items, "搜索");
|
|
74
|
-
assert.deepEqual(hits.map((h) => h.item.shapeId), ["a", "b", "c"]);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("filterMatches 多关键字空格分隔 AND 关系", () => {
|
|
78
|
-
const editor = fakeEditor([
|
|
79
|
-
shape("a", "session-card", { title: "修复登录接口 token 过期" }),
|
|
80
|
-
shape("b", "sticky-note", { text: "登录流程重构" }),
|
|
81
|
-
shape("c", "session-card", { title: "部署配置调整" }),
|
|
82
|
-
shape("d", "session-card", { title: "登录接口优化" }),
|
|
83
|
-
]);
|
|
84
|
-
const items = collectSearchable(editor);
|
|
85
|
-
// 两个词都在同一节点文本里才命中(AND)
|
|
86
|
-
assert.deepEqual(filterMatches(items, "登录 token").map((h) => h.item.shapeId), ["a"]);
|
|
87
|
-
assert.deepEqual(filterMatches(items, "登录 重构").map((h) => h.item.shapeId), ["b"]);
|
|
88
|
-
assert.deepEqual(filterMatches(items, "登录 接口").map((h) => h.item.shapeId), ["a", "d"]);
|
|
89
|
-
// 缺任一词不命中
|
|
90
|
-
assert.equal(filterMatches(items, "登录 不存在词").length, 0);
|
|
91
|
-
// 多个空格/首尾空格容忍
|
|
92
|
-
assert.deepEqual(filterMatches(items, " 登录 接口 ").map((h) => h.item.shapeId), ["a", "d"]);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("makeSnippet 命中居中截断 + 两端省略号", () => {
|
|
96
|
-
const long = "这是一段很长的便笺文本,中间藏着关键词,后面还有更多内容需要被截断显示以便下拉列表保持整洁。";
|
|
97
|
-
const s = makeSnippet(long, "关键词", 20);
|
|
98
|
-
assert.ok(s.includes("关键词"), `snippet should contain the hit: ${s}`);
|
|
99
|
-
assert.ok(s.startsWith("…"), "head ellipsis");
|
|
100
|
-
assert.ok(s.endsWith("…"), "tail ellipsis");
|
|
101
|
-
assert.ok(s.length <= 24, `bounded length: ${s.length}`);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("makeSnippet 无命中时首段截断", () => {
|
|
105
|
-
const short = "hello";
|
|
106
|
-
assert.equal(makeSnippet(short, "zzz"), "hello");
|
|
107
|
-
const long = "x".repeat(100);
|
|
108
|
-
const s = makeSnippet(long, "zzz");
|
|
109
|
-
assert.equal(s, `${"x".repeat(44)}…`);
|
|
110
|
-
});
|
package/lib/board-search.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 看板内搜索定位(Ctrl+F)的纯函数。
|
|
3
|
-
*
|
|
4
|
-
* 只遍历当前画布上的 shape(session-card 标题 + sticky-note 便笺文字),
|
|
5
|
-
* 不做会话正文索引(正文搜索属另一需求,见 .agent/spec/2026-08-30-board-search.md 不做项)。
|
|
6
|
-
* 画布节点量级小(几十个),includes 子串匹配足够,无需索引。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { Editor, TLShapeId } from "tldraw";
|
|
10
|
-
|
|
11
|
-
export type SearchableKind = "session-card" | "sticky-note";
|
|
12
|
-
|
|
13
|
-
export interface SearchableItem {
|
|
14
|
-
shapeId: TLShapeId;
|
|
15
|
-
kind: SearchableKind;
|
|
16
|
-
/** 完整原文(匹配用全文) */
|
|
17
|
-
text: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface SearchMatch {
|
|
21
|
-
item: SearchableItem;
|
|
22
|
-
/** 命中片段(下拉展示用,前后截断) */
|
|
23
|
-
snippet: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** 收集当前画布上可搜索的节点。空文本节点跳过(无搜索价值)。 */
|
|
27
|
-
export function collectSearchable(editor: Editor): SearchableItem[] {
|
|
28
|
-
const out: SearchableItem[] = [];
|
|
29
|
-
for (const shape of editor.getCurrentPageShapes()) {
|
|
30
|
-
if (shape.type === "session-card") {
|
|
31
|
-
const title = String((shape.props as { title?: unknown }).title ?? "").trim();
|
|
32
|
-
if (title) out.push({ shapeId: shape.id, kind: "session-card", text: title });
|
|
33
|
-
} else if (shape.type === "sticky-note") {
|
|
34
|
-
const text = String((shape.props as { text?: unknown }).text ?? "").trim();
|
|
35
|
-
if (text) out.push({ shapeId: shape.id, kind: "sticky-note", text });
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** 按 query 过滤:空格分隔多个关键字,全部命中(AND)才算匹配。空 query 返回空结果。 */
|
|
42
|
-
export function filterMatches(items: SearchableItem[], query: string): SearchMatch[] {
|
|
43
|
-
const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
44
|
-
if (terms.length === 0) return [];
|
|
45
|
-
return items
|
|
46
|
-
.filter((it) => {
|
|
47
|
-
const text = it.text.toLowerCase();
|
|
48
|
-
return terms.every((term) => text.includes(term));
|
|
49
|
-
})
|
|
50
|
-
.map((it) => ({ item: it, snippet: makeSnippet(it.text, terms[0]) }));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** 命中片段:以首个命中位置为中心截取 ~44 字符,两端加省略号。 */
|
|
54
|
-
export function makeSnippet(text: string, query: string, maxLen = 44): string {
|
|
55
|
-
const q = query.trim().toLowerCase();
|
|
56
|
-
const idx = text.toLowerCase().indexOf(q);
|
|
57
|
-
if (idx < 0) return text.length > maxLen ? `${text.slice(0, maxLen)}…` : text;
|
|
58
|
-
const start = Math.max(0, idx - Math.floor((maxLen - q.length) / 2));
|
|
59
|
-
const end = Math.min(text.length, start + maxLen);
|
|
60
|
-
const head = start > 0 ? "…" : "";
|
|
61
|
-
const tail = end < text.length ? "…" : "";
|
|
62
|
-
return `${head}${text.slice(start, end)}${tail}`;
|
|
63
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
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 { loadExplorerOpen, saveExplorerOpen } = await jiti.import("./file-explorer-state.ts");
|
|
7
|
-
|
|
8
|
-
function createStorage(initial = {}) {
|
|
9
|
-
const values = new Map(Object.entries(initial));
|
|
10
|
-
return {
|
|
11
|
-
values,
|
|
12
|
-
getItem(key) {
|
|
13
|
-
return values.get(key) ?? null;
|
|
14
|
-
},
|
|
15
|
-
setItem(key, value) {
|
|
16
|
-
values.set(key, value);
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
test("defaults to an open file explorer", () => {
|
|
22
|
-
assert.equal(loadExplorerOpen(createStorage()), true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("saves and restores the file explorer panel state", () => {
|
|
26
|
-
const storage = createStorage();
|
|
27
|
-
|
|
28
|
-
saveExplorerOpen(false, storage);
|
|
29
|
-
assert.equal(loadExplorerOpen(storage), false);
|
|
30
|
-
|
|
31
|
-
saveExplorerOpen(true, storage);
|
|
32
|
-
assert.equal(loadExplorerOpen(storage), true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("falls back to open when browser storage is unavailable", () => {
|
|
36
|
-
const unavailable = {
|
|
37
|
-
getItem() { throw new Error("blocked"); },
|
|
38
|
-
setItem() { throw new Error("blocked"); },
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
assert.equal(loadExplorerOpen(unavailable), true);
|
|
42
|
-
assert.doesNotThrow(() => saveExplorerOpen(false, unavailable));
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("falls back when accessing browser storage throws", () => {
|
|
46
|
-
const previousWindow = Object.getOwnPropertyDescriptor(globalThis, "window");
|
|
47
|
-
const blockedWindow = {};
|
|
48
|
-
Object.defineProperty(blockedWindow, "localStorage", {
|
|
49
|
-
get() { throw new DOMException("blocked", "SecurityError"); },
|
|
50
|
-
});
|
|
51
|
-
Object.defineProperty(globalThis, "window", {
|
|
52
|
-
configurable: true,
|
|
53
|
-
value: blockedWindow,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
assert.equal(loadExplorerOpen(), true);
|
|
58
|
-
assert.doesNotThrow(() => saveExplorerOpen(false));
|
|
59
|
-
} finally {
|
|
60
|
-
if (previousWindow) Object.defineProperty(globalThis, "window", previousWindow);
|
|
61
|
-
else delete globalThis.window;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const EXPLORER_OPEN_STORAGE_KEY = "pi-web:file-explorer:open";
|
|
2
|
-
|
|
3
|
-
interface StorageLike {
|
|
4
|
-
getItem(key: string): string | null;
|
|
5
|
-
setItem(key: string, value: string): void;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function getBrowserStorage(): StorageLike | null {
|
|
9
|
-
if (typeof window === "undefined") return null;
|
|
10
|
-
try {
|
|
11
|
-
return window.localStorage;
|
|
12
|
-
} catch {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function loadExplorerOpen(storage: StorageLike | null = getBrowserStorage()): boolean {
|
|
18
|
-
if (!storage) return true;
|
|
19
|
-
try {
|
|
20
|
-
return storage.getItem(EXPLORER_OPEN_STORAGE_KEY) !== "false";
|
|
21
|
-
} catch {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function saveExplorerOpen(
|
|
27
|
-
open: boolean,
|
|
28
|
-
storage: StorageLike | null = getBrowserStorage(),
|
|
29
|
-
): void {
|
|
30
|
-
if (!storage) return;
|
|
31
|
-
try {
|
|
32
|
-
storage.setItem(EXPLORER_OPEN_STORAGE_KEY, String(open));
|
|
33
|
-
} catch {
|
|
34
|
-
// Persistence is best-effort; privacy mode and storage quotas must not break the explorer.
|
|
35
|
-
}
|
|
36
|
-
}
|
package/lib/sync-room-server.mjs
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// tldraw sync 房间服务器核心(可内嵌 Next.js server,也可独立进程)
|
|
3
|
-
// - 每看板一个 TLSocketRoom + SQLiteSyncStorage(sync.db,tablePrefix 按 board 隔离)
|
|
4
|
-
// - 纯文档同步:不懂业务,派生边/补卡由前端 reconcile(读业务数据 → CRDT 写文档)
|
|
5
|
-
// - 自定义 shape(session-card / task-card / sticky-note)两端注册
|
|
6
|
-
//
|
|
7
|
-
// 用法(内嵌):server.on("upgrade", handleSyncUpgrade) —— 返回 false 表示未处理,调用方转交 Next
|
|
8
|
-
// 用法(独立):见 scripts/sync-server.mjs
|
|
9
|
-
// ============================================================================
|
|
10
|
-
import { createRequire } from "node:module";
|
|
11
|
-
const require = createRequire(import.meta.url);
|
|
12
|
-
import { randomUUID } from "node:crypto";
|
|
13
|
-
import { homedir } from "node:os";
|
|
14
|
-
import { join, normalize } from "node:path";
|
|
15
|
-
|
|
16
|
-
const { DatabaseSync } = require("node:sqlite");
|
|
17
|
-
const { WebSocketServer } = require("ws");
|
|
18
|
-
const core = require("@tldraw/sync-core");
|
|
19
|
-
const tlschema = require("@tldraw/tlschema");
|
|
20
|
-
const T = require("@tldraw/validate").T;
|
|
21
|
-
|
|
22
|
-
// ---- agent dir(与 lib/sqlite-db.ts 同语义)----
|
|
23
|
-
export function getAgentDir() {
|
|
24
|
-
const envDir = process.env.PI_CODING_AGENT_DIR;
|
|
25
|
-
if (envDir) {
|
|
26
|
-
const expanded = envDir.startsWith("~") ? join(homedir(), envDir.slice(1)) : envDir;
|
|
27
|
-
return normalize(expanded);
|
|
28
|
-
}
|
|
29
|
-
return join(homedir(), ".pi", "agent");
|
|
30
|
-
}
|
|
31
|
-
export const SYNC_DB_FILE = join(getAgentDir(), "sync.db");
|
|
32
|
-
|
|
33
|
-
// ---- 自定义 shape props 校验器(与 components/canvas/{SessionCardShape,StickyNoteShape,TaskCardShape}.tsx 保持一致)----
|
|
34
|
-
const sessionCardProps = {
|
|
35
|
-
sessionId: T.string, title: T.string, projectName: T.string,
|
|
36
|
-
messageCount: T.number, lastReply: T.string, phase: T.string,
|
|
37
|
-
runningMs: T.number, endedAt: T.number, lastActivityAt: T.number,
|
|
38
|
-
stale: T.boolean, expanded: T.boolean, cwd: T.string, taskId: T.string,
|
|
39
|
-
w: T.number, h: T.number, expandedW: T.number, expandedH: T.number,
|
|
40
|
-
collapsedW: T.number, collapsedH: T.number,
|
|
41
|
-
};
|
|
42
|
-
const stickyNoteProps = {
|
|
43
|
-
text: T.string, w: T.number, h: T.number,
|
|
44
|
-
badge: T.string.optional(), createdAt: T.number.optional(),
|
|
45
|
-
};
|
|
46
|
-
const taskCardProps = {
|
|
47
|
-
cardId: T.string, number: T.number, name: T.string,
|
|
48
|
-
description: T.string.optional(), readyStatus: T.string, execStatus: T.string, priority: T.number,
|
|
49
|
-
// due:业务层允许 number | null(无截止=null)。shape 写入可能带 null(草稿初始化/API 回读),
|
|
50
|
-
// 必须 nullable 否则 sync 校验失败 → INVALID_RECORD → 画布永远 loading(派发后看板死掉根因)
|
|
51
|
-
due: T.number.nullable().optional(), expanded: T.boolean,
|
|
52
|
-
w: T.number, h: T.number, expandedW: T.number, expandedH: T.number,
|
|
53
|
-
collapsedW: T.number, collapsedH: T.number,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const syncSchema = tlschema.createTLSchema({
|
|
57
|
-
shapes: {
|
|
58
|
-
...tlschema.defaultShapeSchemas,
|
|
59
|
-
"session-card": { props: sessionCardProps },
|
|
60
|
-
"sticky-note": { props: stickyNoteProps },
|
|
61
|
-
"task-card": { props: taskCardProps },
|
|
62
|
-
},
|
|
63
|
-
bindings: { ...tlschema.defaultBindingSchemas },
|
|
64
|
-
assets: { ...tlschema.defaultAssetSchemas },
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// ---- 房间管理:每 boardId 一个 TLSocketRoom;SQLite 持久化(同一 sync.db,tablePrefix 隔离)----
|
|
68
|
-
// 单一 DatabaseSync 连接(node:sqlite 同步,单连接线程安全)
|
|
69
|
-
const db = new DatabaseSync(SYNC_DB_FILE);
|
|
70
|
-
const rooms = new Map();
|
|
71
|
-
|
|
72
|
-
function safePrefix(boardId) {
|
|
73
|
-
return `board_${boardId.replace(/[^a-zA-Z0-9_]/g, "_")}_`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function getSyncRoom(boardId) {
|
|
77
|
-
let room = rooms.get(boardId);
|
|
78
|
-
if (room) return room;
|
|
79
|
-
const storage = new core.SQLiteSyncStorage({
|
|
80
|
-
sql: new core.NodeSqliteWrapper(db, { tablePrefix: safePrefix(boardId) }),
|
|
81
|
-
});
|
|
82
|
-
room = new core.TLSocketRoom({
|
|
83
|
-
// clientTimeout 不能传 0:0 会让 pruneSessions 把所有 Connected 会话当超时踢掉
|
|
84
|
-
// (timeSince > 0 恒真)→ 客户端重连风暴。传 undefined 用默认 20s 空闲超时,
|
|
85
|
-
// ping 每 5s 保活,正常不会触发。
|
|
86
|
-
storage, schema: syncSchema, clientTimeout: undefined,
|
|
87
|
-
// INVALID_RECORD 等致命错误会走 log.error(带 TLSyncError 对象)——hook 它打印详情
|
|
88
|
-
log: {
|
|
89
|
-
warn: (...a) => console.warn("[sync:room]", ...a),
|
|
90
|
-
error: (...a) => {
|
|
91
|
-
const err = a[0];
|
|
92
|
-
const detail = err instanceof Error ? `${err.message}${err.reason ? ` [reason=${err.reason}]` : ""}` : JSON.stringify(a).slice(0, 500);
|
|
93
|
-
console.error("[sync:room]", detail);
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
rooms.set(boardId, room);
|
|
98
|
-
return room;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function getSyncRoomCount() {
|
|
102
|
-
return rooms.size;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// noServer WSS:不做端口监听,只负责把 upgrade 的 net.Socket 握手成 ws 实例
|
|
106
|
-
const wss = new WebSocketServer({ noServer: true });
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 处理一条 WebSocket upgrade 请求(挂在 Node http server 的 "upgrade" 事件)。
|
|
110
|
-
* 路径匹配 /connect/:boardId → 房间接管;否则返回 false(调用方转交 Next 自身 handler,如 HMR)。
|
|
111
|
-
* @returns {boolean} 是否已处理
|
|
112
|
-
*/
|
|
113
|
-
export function handleSyncUpgrade(req, socket, head) {
|
|
114
|
-
let url;
|
|
115
|
-
try {
|
|
116
|
-
url = new URL(req.url, "http://localhost");
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
const m = url.pathname.match(/^\/connect\/([^/]+)$/);
|
|
121
|
-
if (!m) return false;
|
|
122
|
-
|
|
123
|
-
const boardId = decodeURIComponent(m[1]);
|
|
124
|
-
const room = getSyncRoom(boardId);
|
|
125
|
-
// sessionId 必须复用客户端提供的(useSync 每次重连带固定 TAB_ID)——
|
|
126
|
-
// 若随机生成,重连被当成新会话,服务器端 session 堆积、协议错乱。
|
|
127
|
-
const sessionId = url.searchParams.get("sessionId") ?? randomUUID();
|
|
128
|
-
|
|
129
|
-
// 握手成功后 ws 即为 EventTarget 兼容实例;handleSocketConnect 自动挂
|
|
130
|
-
// message/close/error 监听。绝不手动再 ws.on("message")——否则每条消息处理
|
|
131
|
-
// 两次 → push_result 双发 → 重连死循环。
|
|
132
|
-
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
133
|
-
room.handleSocketConnect({ sessionId, socket: ws, isReadonly: false });
|
|
134
|
-
console.log(`[sync] ${boardId}: client ${sessionId.slice(0, 8)} connected (${room.getNumActiveSessions()})`);
|
|
135
|
-
});
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|