wiki-viewer 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +7 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +32 -3
- package/.next/standalone/.next/routes-manifest.json +46 -0
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/agent/activity/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js +7 -7
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js +5 -5
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js +5 -5
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +8 -7
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +9 -8
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +8 -7
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +7 -7
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +6 -5
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +7 -6
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +6 -5
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/settings/route.js +7 -7
- package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +8 -7
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js +2 -2
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/assets/[...path]/route.js +8 -2
- package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/[...all]/route.js +5 -5
- package/.next/standalone/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/admins/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/admins/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/admins/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/admins/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/admins/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/admins/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/admins/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js +6 -6
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/auth-settings/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/auth-settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/browse/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/clear-root/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/config/route.js +6 -6
- package/.next/standalone/.next/server/app/api/system/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/pins/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/pins/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/reveal/route.js +6 -6
- package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/root-status/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/set-root/route.js +6 -6
- package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/users/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/users/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/users/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/users/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/users/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/users/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/users/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route.js +14 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/system/workspaces/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/upload/[...path]/route.js +8 -2
- package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/app/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/content/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/download/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/file/route.js +8 -2
- package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/move/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/page/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/search/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route.js +15 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/wiki/search/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js +7 -9
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +5 -5
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/manifest.webmanifest/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route.js +9 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route.js.map +5 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest.body +1 -0
- package/.next/standalone/.next/server/app/manifest.webmanifest.meta +1 -0
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/signin/page.js +1 -1
- package/.next/standalone/.next/server/app/signin/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +7 -0
- package/.next/standalone/.next/server/chunks/0p91_next_dist_1195ub1._.js +18 -0
- package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0d2jbo5.js +1 -1
- package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0ytral2.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-244bv._.js +16 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xz-_0v._.js → [root-of-the-server]__0-inabx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-ao6a_._.js → [root-of-the-server]__0.-9jt0._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0~-r-8f._.js → [root-of-the-server]__00c1-dr._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00lep-u._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00mtevo._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00qu86p._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__012753j._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01hp8ax._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0jhr.ex._.js → [root-of-the-server]__02n6z97._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__036mhjp._.js +5 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03p0ob1._.js +16 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05t~v6l._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__068ni_k._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06yp.08._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__082nhm6._.js → [root-of-the-server]__07vphbd._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0pg6dn-._.js → [root-of-the-server]__08lkuwa._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09cqzem._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0p_ox-k._.js → [root-of-the-server]__09k4q9e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0_601._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ae3dk6._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0s28.4-._.js → [root-of-the-server]__0af-x45._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cqu-wm._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d75a5q._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d7x3e1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d7zxm_._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e7vs98._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fxzg4v._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gda_iu._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hu~q20._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jk1uf4._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-8clq._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lhka0x._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0kdn1le._.js → [root-of-the-server]__0na11h0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nd.b_c._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0otb8ra._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0p0-p__._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0piofm3._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rrf-jr._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s6_lzw._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sh13v5._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ta~flu._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0v4im4p._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0qn8e~s._.js → [root-of-the-server]__0v_kyp-._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vatqe-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vdb2nr._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wdh63w._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z3wg9x._.js +12 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0mzo03s._.js → [root-of-the-server]__0~.-3yk._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__123._fe._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__12800b1._.js → [root-of-the-server]__12ee_s6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12onytt._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04udrya._.js → [root-of-the-server]__12q8ch8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1377.79._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1378-ox._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13aid91._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13hp4tw._.js +12 -0
- package/.next/standalone/.next/server/chunks/_079xuln._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0dr1r1g._.js +452 -0
- package/.next/standalone/.next/server/chunks/_101exke._.js +452 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_admins_route_actions_0dsm7cx.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_users_route_actions_0whj5cy.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_[id]_open_route_actions_1087xu7.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_[id]_route_actions_0~h7-8s.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_route_actions_0mkalc3.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wiki_search_route_actions_0inxu93.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_manifest_webmanifest_route_actions_04wfv0d.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules__pnpm_0aj5g-w._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_lib_search_0d-_j3u._.js +46 -0
- package/.next/standalone/.next/server/chunks/src_lib_search_0ymmp0c._.js +46 -0
- package/.next/standalone/.next/server/chunks/ssr/0p91_next_dist_esm_build_templates_app-page_042apsk.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/0p91_next_dist_esm_build_templates_app-page_08q3u1e.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/0p91_next_dist_esm_build_templates_app-page_0nflz3~.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/0p91_next_dist_esm_build_templates_app-page_0p5k29e.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_0b8ra51._.js → 0~w0_katex_dist_katex_mjs_04mhawr._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_erDiagram-TEJ5UH35_mjs_0v6rnqy._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_mermaid_core_mjs_0hj_n-~._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0btcd4o._.js → [root-of-the-server]__02twzph._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ytvlzs._.js → [root-of-the-server]__09~x2v~._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hvvps3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +18 -18
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0c71i8a._.js → node_modules__pnpm_0.96tmo._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0lm8e2c._.js → node_modules__pnpm_011vy_q._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ezm2us._.js → node_modules__pnpm_01o_5qt._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j3mvgg._.js → node_modules__pnpm_04n0h6.._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0~ln0by._.js → node_modules__pnpm_093dp2q._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_11e~j49._.js → node_modules__pnpm_09ej62_._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0de8xt-._.js → node_modules__pnpm_09ljbdz._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-x6zex._.js → node_modules__pnpm_09~_ob2._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0yq5z.h._.js → node_modules__pnpm_0a01ng7._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0kwc-vs._.js → node_modules__pnpm_0b8qx0g._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hrbz84._.js → node_modules__pnpm_0bbc---._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hobcbs._.js → node_modules__pnpm_0c.309.._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0rw91-f._.js → node_modules__pnpm_0c8aunj._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0o.c1z8._.js → node_modules__pnpm_0hk6xys._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0dboa_e._.js → node_modules__pnpm_0hlzy8f._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j53hti._.js → node_modules__pnpm_0nw5ou2._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xrmzh1._.js → node_modules__pnpm_0o.dn8o._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0cxwhfd._.js → node_modules__pnpm_0s0ur~d._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_07rxv2x._.js → node_modules__pnpm_0shlang._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i.pw_m._.js → node_modules__pnpm_0sq3c2r._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ajzar9._.js → node_modules__pnpm_0t9jb7d._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_012nt.s._.js → node_modules__pnpm_0wmx9kq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0z~h2ow._.js → node_modules__pnpm_0xfx0sf._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0cbsnwf._.js → node_modules__pnpm_0yiz.-t._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0bum3-3._.js → node_modules__pnpm_11sut1u._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0mdo4i3._.js → node_modules__pnpm_124u4o2._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0il8j.j._.js → node_modules__pnpm_13tajgx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_app_signin_00yk0vm._.js +2 -2
- package/.next/standalone/.next/server/edge/chunks/{08yl_next_dist_esm_build_templates_edge-wrapper_0o9h3w_.js → 08yl_next_dist_esm_build_templates_edge-wrapper_0xbpts6.js} +2 -2
- package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0.pmgt2._.js +3 -0
- package/.next/standalone/.next/server/functions-config-manifest.json +6 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +8 -8
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0c_~zevf2n0r4.css +2 -0
- package/.next/standalone/.next/static/chunks/0l4842ez-0_66.js +129 -0
- package/.next/standalone/README.md +46 -2
- package/.next/standalone/docs/workspacing-plan.md +364 -0
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/packages/wiki-viewer-mcp/package.json +1 -1
- package/.next/standalone/public/icon-192.png +0 -0
- package/.next/standalone/public/icon-512.png +0 -0
- package/.next/standalone/public/icon-maskable-512.png +0 -0
- package/.next/standalone/src/app/api/agent/activity/route.ts +9 -8
- package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +20 -8
- package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +16 -9
- package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +28 -18
- package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +12 -7
- package/.next/standalone/src/app/api/agent/fs/move/route.ts +16 -11
- package/.next/standalone/src/app/api/agent/fs/search/route.ts +49 -13
- package/.next/standalone/src/app/api/agent/internal/span/route.ts +9 -5
- package/.next/standalone/src/app/api/agent/register/route.ts +9 -1
- package/.next/standalone/src/app/api/agent/settings/route.ts +7 -3
- package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +8 -4
- package/.next/standalone/src/app/api/assets/[...path]/route.ts +9 -4
- package/.next/standalone/src/app/api/system/admins/route.ts +103 -0
- package/.next/standalone/src/app/api/system/reveal/route.ts +7 -6
- package/.next/standalone/src/app/api/system/set-root/route.ts +24 -1
- package/.next/standalone/src/app/api/system/users/route.ts +120 -0
- package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +34 -0
- package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +74 -0
- package/.next/standalone/src/app/api/system/workspaces/route.ts +67 -0
- package/.next/standalone/src/app/api/upload/[...path]/route.ts +10 -5
- package/.next/standalone/src/app/api/wiki/app/route.ts +10 -9
- package/.next/standalone/src/app/api/wiki/content/route.ts +14 -14
- package/.next/standalone/src/app/api/wiki/download/route.ts +7 -6
- package/.next/standalone/src/app/api/wiki/file/route.ts +7 -2
- package/.next/standalone/src/app/api/wiki/folder/route.ts +6 -5
- package/.next/standalone/src/app/api/wiki/move/route.ts +7 -7
- package/.next/standalone/src/app/api/wiki/new-file/route.ts +7 -6
- package/.next/standalone/src/app/api/wiki/page/route.ts +6 -5
- package/.next/standalone/src/app/api/wiki/presence/route.ts +8 -7
- package/.next/standalone/src/app/api/wiki/route.ts +13 -11
- package/.next/standalone/src/app/api/wiki/search/route.ts +44 -0
- package/.next/standalone/src/app/api/wiki/slugs/route.ts +8 -8
- package/.next/standalone/src/app/api/wiki/upload/route.ts +6 -5
- package/.next/standalone/src/app/api/wiki/watch/route.ts +16 -40
- package/.next/standalone/src/app/globals.css +484 -484
- package/.next/standalone/src/app/layout.tsx +15 -1
- package/.next/standalone/src/app/manifest.ts +30 -0
- package/.next/standalone/src/app/page.tsx +163 -49
- package/.next/standalone/src/components/auth-settings-sheet.tsx +171 -2
- package/.next/standalone/src/components/dir-picker.tsx +8 -6
- package/.next/standalone/src/components/editor/comment-thread.tsx +2 -1
- package/.next/standalone/src/components/editor/csv-viewer.tsx +3 -2
- package/.next/standalone/src/components/editor/editor-toolbar.tsx +2 -2
- package/.next/standalone/src/components/editor/editor.tsx +6 -5
- package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +3 -2
- package/.next/standalone/src/components/editor/folder-index.tsx +2 -1
- package/.next/standalone/src/components/editor/image-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/media-popover.tsx +2 -1
- package/.next/standalone/src/components/editor/media-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/mermaid-viewer.tsx +3 -2
- package/.next/standalone/src/components/editor/node-app-viewer.tsx +4 -3
- package/.next/standalone/src/components/editor/notebook-viewer.tsx +3 -2
- package/.next/standalone/src/components/editor/office/docx-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/office/office-chrome.tsx +3 -2
- package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/pdf-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/proof-span-popover.tsx +2 -1
- package/.next/standalone/src/components/editor/source-viewer.tsx +3 -2
- package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +2 -1
- package/.next/standalone/src/components/editor/suggestion-card.tsx +2 -1
- package/.next/standalone/src/components/editor/website-viewer.tsx +2 -1
- package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +2 -1
- package/.next/standalone/src/components/search/search-command-dialog.tsx +109 -0
- package/.next/standalone/src/components/search/sidebar-search-box.tsx +121 -0
- package/.next/standalone/src/components/search/snippet-text.tsx +48 -0
- package/.next/standalone/src/components/ui/command.tsx +3 -2
- package/.next/standalone/src/lib/auth/admin.ts +112 -0
- package/.next/standalone/src/lib/auth/server.ts +13 -2
- package/.next/standalone/src/lib/config.ts +56 -4
- package/.next/standalone/src/lib/proof/audit.ts +19 -10
- package/.next/standalone/src/lib/proof/auth.ts +11 -0
- package/.next/standalone/src/lib/proof/collab-state.ts +4 -2
- package/.next/standalone/src/lib/proof/file-lock.ts +5 -0
- package/.next/standalone/src/lib/proof/idempotency.ts +5 -0
- package/.next/standalone/src/lib/proof/lease.ts +40 -27
- package/.next/standalone/src/lib/proof/mutex.ts +4 -0
- package/.next/standalone/src/lib/proof/raw-fs.ts +2 -3
- package/.next/standalone/src/lib/proof/registry.ts +5 -0
- package/.next/standalone/src/lib/search/indexable-exts.ts +25 -0
- package/.next/standalone/src/lib/search/indexer.ts +567 -0
- package/.next/standalone/src/lib/search/sanitize.ts +35 -0
- package/.next/standalone/src/lib/search/search-db.ts +57 -0
- package/.next/standalone/src/lib/search/watcher-pool.ts +79 -0
- package/.next/standalone/src/lib/workspace-client.ts +82 -0
- package/.next/standalone/src/lib/workspace-context.ts +142 -0
- package/.next/standalone/src/lib/workspaces.ts +196 -0
- package/.next/standalone/src/stores/ai-panel-store.ts +3 -2
- package/.next/standalone/src/stores/editor-store.ts +4 -3
- package/.next/standalone/src/stores/proof-store.ts +4 -3
- package/.next/standalone/src/stores/search-store.ts +86 -0
- package/.next/standalone/src/stores/wiki-slugs-store.ts +2 -1
- package/.next/standalone/src/tests/proof/admin-create-user.test.ts +43 -0
- package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +134 -0
- package/.next/standalone/src/tests/proof/collab-state.test.ts +43 -41
- package/.next/standalone/src/tests/proof/preload.ts +21 -0
- package/.next/standalone/src/tests/proof/search-index.test.ts +339 -0
- package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +87 -0
- package/.next/standalone/src/tests/proof/workspaces-api.test.ts +227 -0
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/README.md +46 -2
- package/package.json +2 -2
- package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0o~h4zq.js +0 -5
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.hb9q2._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.uux34._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0092hkp._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05f6x0i._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05idwm9._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06-dtd0._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06sek6_._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07fpqks._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__094q5bj._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0awzyon._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_da6q._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hhhvm7._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kk26wc._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0km8suj._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kur4ro._.js +0 -15
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0mkwoul._.js +0 -12
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qc2~6l._.js +0 -15
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qyd-o8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rkve~v._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rzo40z._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sjocwy._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ssj9~d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s~i040._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t220d-._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t5mg.n._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0trsqf4._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xq124z._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__106uw_c._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10w2q~o._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13w-0iy._.js +0 -3
- package/.next/standalone/.next/server/chunks/src_lib_auth_server_ts_00f4e0h._.js +0 -452
- package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0ug2rvl._.js +0 -3
- package/.next/standalone/.next/static/chunks/0vwkqw_rofm-c.js +0 -129
- package/.next/standalone/.next/static/chunks/0zay6349~v_wc.css +0 -2
- /package/.next/standalone/.next/static/{H0E5wVJm9tAuN5CrU6zG2 → t7B6nXaoD1jo9f61t3aeK}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{H0E5wVJm9tAuN5CrU6zG2 → t7B6nXaoD1jo9f61t3aeK}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{H0E5wVJm9tAuN5CrU6zG2 → t7B6nXaoD1jo9f61t3aeK}/_ssgManifest.js +0 -0
|
@@ -67,7 +67,7 @@ The wizard walks you through every option and writes `~/.wiki-viewer/config.json
|
|
|
67
67
|
|
|
68
68
|
Open **http://localhost:3000** (or **https://localhost:3000** with `--https`).
|
|
69
69
|
|
|
70
|
-
To switch
|
|
70
|
+
To switch between directories later, use the **workspace switcher** at the bottom of the sidebar. Each _workspace_ is a registered root directory; you can run several at once with no state bleed (open different workspaces in different browser tabs). See [Workspaces](#workspaces) below. No restart needed.
|
|
71
71
|
|
|
72
72
|
On first run with no users in the database, the app works in single-user mode and any visitor on `localhost` can sign up. Set `AUTH_ALLOWED_DOMAIN` or `AUTH_ALLOWED_EMAILS` before exposing the server to anyone else.
|
|
73
73
|
|
|
@@ -123,7 +123,7 @@ wiki-viewer service restart
|
|
|
123
123
|
wiki-viewer service uninstall
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
The run config is saved to `~/.wiki-viewer/config.json`. Edit that file and run `wiki-viewer service restart` to change settings without reinstalling. To change
|
|
126
|
+
The run config is saved to `~/.wiki-viewer/config.json`. Edit that file and run `wiki-viewer service restart` to change settings without reinstalling. To change the served directory at runtime, use the workspace switcher in the sidebar (see [Workspaces](#workspaces)).
|
|
127
127
|
|
|
128
128
|
On Linux, install enables lingering (`loginctl enable-linger`) so the service runs without an active login session and survives reboot. If that step needs privileges, the installer prints the command to run manually.
|
|
129
129
|
|
|
@@ -174,6 +174,49 @@ Updates the global install to the latest version (npm/pnpm/yarn auto-detected) a
|
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
177
|
+
## Workspaces
|
|
178
|
+
|
|
179
|
+
A **workspace** is a registered root directory. One running wiki-viewer can
|
|
180
|
+
serve many workspaces at once, fully isolated from each other:
|
|
181
|
+
|
|
182
|
+
- Two browser tabs can each open a different workspace (via a `?ws=<id>` URL
|
|
183
|
+
param) and edit files concurrently. Edit leases, write locks, idempotency
|
|
184
|
+
keys, collab sidecars, and the audit log are all namespaced per workspace —
|
|
185
|
+
a `notes.md` in workspace A never collides with a `notes.md` in workspace B.
|
|
186
|
+
- AI agents target a workspace with an `X-Workspace: <id>` header (or `?ws=`).
|
|
187
|
+
An agent's grant may be pinned to one workspace; requests that resolve to a
|
|
188
|
+
different workspace are rejected `403 FORBIDDEN`. Agents registered without a
|
|
189
|
+
workspace id keep working across all workspaces (wildcard).
|
|
190
|
+
|
|
191
|
+
### Switching and creating
|
|
192
|
+
|
|
193
|
+
The sidebar footer shows the active workspace. Click it to switch between the
|
|
194
|
+
workspaces you can access, or (admins only) **Add workspace…** to register a
|
|
195
|
+
new root directory. Switching navigates to `?ws=<id>` and resets the view.
|
|
196
|
+
|
|
197
|
+
The legacy single-directory flow is unchanged: launching with `ROOT_DIR` or
|
|
198
|
+
`wiki-viewer <dir>` auto-registers that directory as the first workspace, and a
|
|
199
|
+
single-workspace install behaves exactly as before (no `?ws=` needed).
|
|
200
|
+
|
|
201
|
+
### Admins and access control
|
|
202
|
+
|
|
203
|
+
Workspace management is **admin-gated** (organizational access control, not
|
|
204
|
+
OS-level permissions):
|
|
205
|
+
|
|
206
|
+
- The **first user to sign up** becomes an admin automatically. Admins can
|
|
207
|
+
promote/demote other users from the settings sheet (gear icon → **Admins**).
|
|
208
|
+
Seed or override admins headlessly with `WIKI_ADMIN_EMAILS` (csv).
|
|
209
|
+
- Only admins can create/delete workspaces and edit a workspace's allowed-user
|
|
210
|
+
list (`allowedUserIds`). An empty allow-list means any signed-in user may
|
|
211
|
+
open the workspace (the default). Non-admins with access get full file
|
|
212
|
+
read/write on the workspaces they can see.
|
|
213
|
+
- Admin state lives in `~/.wiki-viewer/config.json` (`adminUserIds`), workspace
|
|
214
|
+
records under `workspaces[]`.
|
|
215
|
+
|
|
216
|
+
> Note: removing a workspace only unregisters it — the directory on disk is
|
|
217
|
+
> never touched. The last admin cannot be demoted unless `WIKI_ADMIN_EMAILS`
|
|
218
|
+
> provides a fallback.
|
|
219
|
+
|
|
177
220
|
## Auth and multi-user mode
|
|
178
221
|
|
|
179
222
|
wiki-viewer uses [Better Auth](https://better-auth.com) with SQLite. State lives at `~/.wiki-viewer/auth.db` (WAL mode) and `~/.wiki-viewer/auth.secret` (chmod 0600). Both are created on first start. Set `BETTER_AUTH_SECRET` in the environment to override the file-stored secret.
|
|
@@ -528,6 +571,7 @@ The dev server supports hot reload.
|
|
|
528
571
|
| `AUTH_ALLOWED_DOMAIN` | csv: only emails on these domains can sign up (overridden by UI allowlist if set) | unset (open) |
|
|
529
572
|
| `WIKI_OWNER_HOSTS` | csv: extra hostnames trusted for CSRF Origin check | `localhost,127.0.0.1` |
|
|
530
573
|
| `WIKI_ALLOW_INSECURE` | Set to `1` to bypass the prod-https guard (dev / CI only) | unset |
|
|
574
|
+
| `WIKI_ADMIN_EMAILS` | csv: emails treated as admins (seed/override; otherwise first signup is admin) | unset |
|
|
531
575
|
| `AGENT_RATE_LIMIT` | Max mutation ops per minute per agent identity | `60` |
|
|
532
576
|
|
|
533
577
|
---
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Workspacing plan
|
|
2
|
+
|
|
3
|
+
> **Status: IMPLEMENTED.** Phases A–E complete. 294 tests pass, tsc clean.
|
|
4
|
+
> Key files: `src/lib/workspaces.ts`, `src/lib/workspace-context.ts`,
|
|
5
|
+
> `src/lib/auth/admin.ts`, `src/lib/workspace-client.ts`, workspace + admin
|
|
6
|
+
> APIs under `src/app/api/system/`, agent scope via `scope.workspaceId`.
|
|
7
|
+
> The legacy `src/lib/root-dir.ts` global is retained as a fallback source
|
|
8
|
+
> (synthetic default workspace) for `ROOT_DIR`/CLI/tests — not yet deleted.
|
|
9
|
+
|
|
10
|
+
## Goal
|
|
11
|
+
|
|
12
|
+
Let one running wiki-viewer serve multiple root directories ("workspaces")
|
|
13
|
+
concurrently, with zero state bleed between them. A user can have two tabs open
|
|
14
|
+
on two different workspaces at once. AI agents target a specific workspace.
|
|
15
|
+
|
|
16
|
+
Decisions (locked):
|
|
17
|
+
|
|
18
|
+
- **Selection**: URL/query carries `ws=<wsId>` for browser routes; agents send
|
|
19
|
+
an `X-Workspace: <wsId>` header. No process-global "current root".
|
|
20
|
+
- **Agents are workspace-scoped**: every agent request resolves a workspace and
|
|
21
|
+
is denied if it has no grant for it.
|
|
22
|
+
- **Isolation type**: organizational, not OS-level ACL. We add an explicit
|
|
23
|
+
per-workspace access list (which users / which agents may use a workspace).
|
|
24
|
+
We do NOT add per-user filesystem path restrictions on the directory browser.
|
|
25
|
+
|
|
26
|
+
## Why the current code bleeds
|
|
27
|
+
|
|
28
|
+
`src/lib/root-dir.ts` keeps a single `globalThis.__wikiRootDir`. Every route
|
|
29
|
+
calls `getRootDir()` / `safeRootPath()` against that one value, so:
|
|
30
|
+
|
|
31
|
+
- "Change" (`/api/system/clear-root`) wipes the root for every user/tab.
|
|
32
|
+
- Only one root can be active per process. No concurrency at all.
|
|
33
|
+
|
|
34
|
+
In-memory stores keyed by **relPath only** (collide across roots once more than
|
|
35
|
+
one root is live):
|
|
36
|
+
|
|
37
|
+
- `src/lib/proof/lease.ts` — `leases` + `generations` maps keyed by relPath.
|
|
38
|
+
- `src/lib/proof/idempotency.ts` — single LRU keyed by idempotency key.
|
|
39
|
+
- `src/lib/proof/mutex.ts` — in-process lock map keyed by relPath / mdPath.
|
|
40
|
+
- `src/lib/proof/file-lock.ts` — cross-process sentinel hashed from lockKey
|
|
41
|
+
(relPath). Two workspaces with `notes.md` share one lock.
|
|
42
|
+
|
|
43
|
+
Config is single-root shaped:
|
|
44
|
+
|
|
45
|
+
- `src/lib/config.ts` — one `lastOpenedPath`, one flat `pinnedPaths`.
|
|
46
|
+
|
|
47
|
+
Already correctly scoped (these take `rootDir` as a param, keep as-is):
|
|
48
|
+
|
|
49
|
+
- `src/lib/proof/sidecar.ts`, `activity.ts` — read/write under
|
|
50
|
+
`<rootDir>/.proof/`. Naturally isolated once `rootDir` is per-workspace.
|
|
51
|
+
|
|
52
|
+
Global, needs a `workspaceId` column / field:
|
|
53
|
+
|
|
54
|
+
- `src/lib/proof/audit.ts` — `agent_fs_audit` table mixes paths from all roots.
|
|
55
|
+
- `src/lib/proof/registry.ts` — `agents.json`; agent scope is path globs with
|
|
56
|
+
no workspace dimension.
|
|
57
|
+
|
|
58
|
+
## Target architecture
|
|
59
|
+
|
|
60
|
+
### 0. Admin role (new prerequisite)
|
|
61
|
+
|
|
62
|
+
No admin concept exists today: every signed-in user is equal, and the settings
|
|
63
|
+
routes just `requireUser`. Workspace access management is admin-gated, so we
|
|
64
|
+
introduce a minimal global admin role.
|
|
65
|
+
|
|
66
|
+
New `src/lib/auth/admin.ts`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
isAdmin(userId, email): Promise<boolean>
|
|
70
|
+
listAdmins(): Promise<string[]> // user ids
|
|
71
|
+
addAdmin(userId): Promise<void>
|
|
72
|
+
removeAdmin(userId): Promise<void> // refuse to remove the last admin
|
|
73
|
+
requireAdmin(req): Promise<{ ok: true; user } | { ok: false; status; code }>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Resolution:
|
|
77
|
+
|
|
78
|
+
- `adminUserIds: string[]` stored in `config.json`.
|
|
79
|
+
- `WIKI_ADMIN_EMAILS` (csv) env: any matching signed-in user is treated as
|
|
80
|
+
admin regardless of config (seed/override, headless-friendly).
|
|
81
|
+
- **Bootstrap**: if `adminUserIds` is empty AND no `WIKI_ADMIN_EMAILS`, the
|
|
82
|
+
first user to sign up is written into `adminUserIds`. Hook the better-auth
|
|
83
|
+
signup path, or do it lazily on the first authenticated request when the
|
|
84
|
+
admin set is empty (behind the config write mutex). Log once.
|
|
85
|
+
- `requireAdmin` returns 403 `ADMIN_REQUIRED` for non-admins.
|
|
86
|
+
|
|
87
|
+
Admin management API: `GET/POST/DELETE /api/system/admins` (admin-gated; list
|
|
88
|
+
users, promote, demote). `DELETE` refuses to remove the last admin (lockout
|
|
89
|
+
guard). A small Admins section in the settings sheet lists users (from the
|
|
90
|
+
better-auth `user` table) with promote/demote toggles.
|
|
91
|
+
|
|
92
|
+
Non-admin users keep full file access to workspaces they are granted; admin is
|
|
93
|
+
only about creating workspaces and editing access lists (and promoting admins).
|
|
94
|
+
|
|
95
|
+
### 1. Workspace registry (replaces single rootDir)
|
|
96
|
+
|
|
97
|
+
New `src/lib/workspaces.ts`. Persisted in `~/.wiki-viewer/config.json` under a
|
|
98
|
+
new `workspaces` array. Each entry:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interface Workspace {
|
|
102
|
+
id: string; // "ws_" + short random; stable, URL-safe
|
|
103
|
+
name: string; // display label, defaults to basename(rootDir)
|
|
104
|
+
rootDir: string; // absolute, resolved
|
|
105
|
+
createdAt: string;
|
|
106
|
+
lastOpenedAt?: string;
|
|
107
|
+
pinnedPaths?: string[]; // moved here from the flat top-level list
|
|
108
|
+
createdBy?: string; // user id of the admin who created it
|
|
109
|
+
// access management (organizational, admin-edited only):
|
|
110
|
+
allowedUserIds?: string[]; // empty/undefined = any signed-in user
|
|
111
|
+
// agent grants live in the agent registry, keyed by workspaceId
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
API surface (pure functions, no globalThis root):
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
listWorkspaces(): Promise<Workspace[]>
|
|
119
|
+
getWorkspace(id): Promise<Workspace | null>
|
|
120
|
+
createWorkspace({ rootDir, name? }): Promise<Workspace>
|
|
121
|
+
renameWorkspace(id, name): Promise<void>
|
|
122
|
+
removeWorkspace(id): Promise<void> // does NOT touch the directory
|
|
123
|
+
setWorkspaceAccess(id, userIds): Promise<void> // admin-only, edits allowedUserIds
|
|
124
|
+
userCanAccess(ws, userId): boolean // admins always pass; else allowedUserIds check
|
|
125
|
+
resolveWorkspaceRoot(id): Promise<string|null>
|
|
126
|
+
safeWorkspacePath(rootDir, rel): string|null // the old safeRootPath logic
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`safeRootPath` logic moves here as `safeWorkspacePath(rootDir, rel)` (takes an
|
|
130
|
+
explicit root instead of reading the global). Keep `root-dir.ts` as a thin
|
|
131
|
+
deprecated shim during migration if helpful, then delete.
|
|
132
|
+
|
|
133
|
+
### 2. Request-scoped workspace resolution
|
|
134
|
+
|
|
135
|
+
New `src/lib/workspace-context.ts`:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// Browser/session routes
|
|
139
|
+
async function resolveWorkspaceForUser(
|
|
140
|
+
req,
|
|
141
|
+
): Promise<
|
|
142
|
+
| { ok: true; ws: Workspace; rootDir: string }
|
|
143
|
+
| { ok: false; status: number; code: string }
|
|
144
|
+
>;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Resolution order for browser routes:
|
|
148
|
+
|
|
149
|
+
1. `ws` query param (preferred) or `x-workspace` header.
|
|
150
|
+
2. If absent, fall back to the user's `lastOpenedAt` workspace (back-compat,
|
|
151
|
+
single-workspace users never notice).
|
|
152
|
+
3. 400 `WORKSPACE_REQUIRED` if none and more than one workspace exists.
|
|
153
|
+
|
|
154
|
+
Then enforce `userCanAccess(ws, user.id)` → 403 `WORKSPACE_FORBIDDEN`.
|
|
155
|
+
|
|
156
|
+
For agent routes, `checkAuth` (in `src/lib/proof/auth.ts`) additionally reads
|
|
157
|
+
`X-Workspace`, looks up the workspace, and checks the agent's grant for it
|
|
158
|
+
(see §5). Returns `{ ok, agent, workspace, rootDir }`.
|
|
159
|
+
|
|
160
|
+
### 3. Namespacing the in-memory stores
|
|
161
|
+
|
|
162
|
+
Every store keyed by relPath becomes keyed by `wsId + "\0" + relPath` (the
|
|
163
|
+
workspace id is a stable prefix, NUL separator avoids collisions).
|
|
164
|
+
|
|
165
|
+
- `lease.ts`: `setLease`, `hasActiveLease`, `clearLease`, `leaseGeneration`
|
|
166
|
+
gain a `wsId` first arg → internal key `${wsId}\0${relPath}`.
|
|
167
|
+
`computeCollabState(rootDir, relPath)` already has rootDir; add `wsId` (or
|
|
168
|
+
derive a key from rootDir — but explicit wsId is cleaner). Update
|
|
169
|
+
`collab-state.ts` and `presence/route.ts` callers.
|
|
170
|
+
- `mutex.ts` / `file-lock.ts`: callers pass a key already; change every call
|
|
171
|
+
site to prefix with `wsId` (e.g. `withFileMutex(`${wsId}\0${relPath}`, ...)`).
|
|
172
|
+
Cross-process sentinel hash then differs per workspace automatically.
|
|
173
|
+
- `idempotency.ts`: prefix the key with `wsId` at the call site
|
|
174
|
+
(`events/[...path]/route.ts`, `files/[...path]/route.ts`).
|
|
175
|
+
- `registry.ts` `lastSeenWriteAt` throttle map is keyed by agentId — fine, no
|
|
176
|
+
change.
|
|
177
|
+
|
|
178
|
+
### 4. Threading rootDir through routes (29 files)
|
|
179
|
+
|
|
180
|
+
Pattern, every session route:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const ctx = await resolveWorkspaceForUser(request);
|
|
184
|
+
if (!ctx.ok)
|
|
185
|
+
return NextResponse.json({ error: ctx.code }, { status: ctx.status });
|
|
186
|
+
const { ws, rootDir } = ctx;
|
|
187
|
+
const filePath = safeWorkspacePath(rootDir, rel);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Replace all `getRootDir()` → `ctx.rootDir`, `safeRootPath(rel)` →
|
|
191
|
+
`safeWorkspacePath(rootDir, rel)`, `isRootDirSet()` → existence of ctx.
|
|
192
|
+
|
|
193
|
+
Route inventory (all under `src/app/api`):
|
|
194
|
+
|
|
195
|
+
- `wiki/route.ts`, `wiki/content/route.ts`, `wiki/page/route.ts`,
|
|
196
|
+
`wiki/slugs/route.ts`, `wiki/file/route.ts`, `wiki/new-file/route.ts`,
|
|
197
|
+
`wiki/folder/route.ts`, `wiki/move/route.ts`, `wiki/upload/route.ts`,
|
|
198
|
+
`wiki/download/route.ts`, `wiki/app/route.ts`, `wiki/watch/route.ts`,
|
|
199
|
+
`wiki/presence/route.ts`
|
|
200
|
+
- `assets/[...path]/route.ts`, `upload/[...path]/route.ts`
|
|
201
|
+
- `system/reveal/route.ts`
|
|
202
|
+
- agent: `fs/file`, `fs/ls`, `fs/move`, `fs/search`, `files`, `events`,
|
|
203
|
+
`sidecar`, `activity`, `settings`, `internal/span`
|
|
204
|
+
|
|
205
|
+
`watch/route.ts` (SSE / chokidar): watcher must watch `ws.rootDir`. Each EventSource
|
|
206
|
+
connection is per-workspace; the client opens `/api/wiki/watch?ws=<id>`.
|
|
207
|
+
|
|
208
|
+
### 5. Workspace-scoped agents
|
|
209
|
+
|
|
210
|
+
Agent grants gain a workspace dimension. In `registry.ts`:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
interface AgentScope {
|
|
214
|
+
workspaceId: string; // NEW — the workspace this grant applies to
|
|
215
|
+
paths: string[];
|
|
216
|
+
ops: Array<"read" | "mutate" | "delete">;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
(An agent id may appear once per workspace, or scope becomes an array of
|
|
221
|
+
per-workspace grants. Simpler: one registry row per (agentId, workspaceId).
|
|
222
|
+
Recommend: keep `Agent.id` unique, add `workspaceId` to the record; an agent
|
|
223
|
+
that needs two workspaces registers twice.)
|
|
224
|
+
|
|
225
|
+
- `register` route: request body includes target `workspaceId`. Approval UI in
|
|
226
|
+
the AI panel shows which workspace the request is for.
|
|
227
|
+
- `checkAuth`: resolve `X-Workspace`, then `enforceScope` also checks
|
|
228
|
+
`agent.scope.workspaceId === ws.id`. Mismatch → 403.
|
|
229
|
+
- Browser session users (`user:<id>`) are synthesized in `checkAuth` with full
|
|
230
|
+
scope; gate them with `userCanAccess(ws, userId)` instead.
|
|
231
|
+
- `audit.ts`: add `workspace_id` column to `agent_fs_audit`
|
|
232
|
+
(`ALTER TABLE ... ADD COLUMN`; tolerate existing DBs). Write it on every row.
|
|
233
|
+
|
|
234
|
+
### 6. Config migration
|
|
235
|
+
|
|
236
|
+
On first read of `config.json` with the new code:
|
|
237
|
+
|
|
238
|
+
- If `workspaces` is absent but `lastOpenedPath` exists, synthesize one
|
|
239
|
+
workspace `{ id: ws_*, rootDir: lastOpenedPath, name: basename, pinnedPaths:
|
|
240
|
+
config.pinnedPaths }` and mark it lastOpened. Keep old fields for one release
|
|
241
|
+
for rollback safety; new writes use `workspaces`.
|
|
242
|
+
- `agents.json`: existing agents have no `workspaceId`. On load, backfill them
|
|
243
|
+
to the migrated default workspace id (and log once, mirroring the existing
|
|
244
|
+
legacy `ownerUserId` handling in the README checklist).
|
|
245
|
+
|
|
246
|
+
### 7. API: workspace management routes
|
|
247
|
+
|
|
248
|
+
New under `src/app/api/system/workspaces`:
|
|
249
|
+
|
|
250
|
+
- `GET /api/system/workspaces` — list workspaces visible to the user
|
|
251
|
+
(filtered by `userCanAccess`).
|
|
252
|
+
- `POST /api/system/workspaces` — create `{ rootDir, name? }` (validates dir
|
|
253
|
+
exists, isDirectory). Replaces `set-root` semantics.
|
|
254
|
+
- `PATCH /api/system/workspaces/<id>` — rename / pin-unpin allowed for any
|
|
255
|
+
user with access; **editing `allowedUserIds` is admin-only** (enforced field
|
|
256
|
+
by field in the handler).
|
|
257
|
+
- `DELETE /api/system/workspaces/<id>` — **admin-only**. Unregister (directory
|
|
258
|
+
untouched).
|
|
259
|
+
|
|
260
|
+
Workspace create (`POST`) is **admin-only** and sets `createdBy`. `GET` is open
|
|
261
|
+
to any signed-in user but filtered: admins see all, others see only workspaces
|
|
262
|
+
they can access. Admins management: `GET/POST/DELETE /api/system/admins`.
|
|
263
|
+
|
|
264
|
+
Deprecate / re-point: `set-root`, `clear-root`, `root-status`, `pins`. Keep
|
|
265
|
+
thin back-compat shims that operate on "the user's current/last workspace" so
|
|
266
|
+
nothing 404s mid-migration, then remove in a follow-up.
|
|
267
|
+
|
|
268
|
+
### 8. Client (browser)
|
|
269
|
+
|
|
270
|
+
- `src/app/page.tsx`: hold `activeWorkspaceId` in state. Read from URL `?ws=`
|
|
271
|
+
first, else last opened. Put `ws` into every fetch (`/api/wiki?ws=...`,
|
|
272
|
+
watch URL, presence body). Cleanest: a small `wsFetch(path, init)` helper /
|
|
273
|
+
thin client store (`workspace-store.ts`) that injects `ws` automatically so
|
|
274
|
+
we are not editing dozens of `fetch` calls by hand.
|
|
275
|
+
- Sidebar footer "Change" → opens a workspace switcher (list of workspaces the
|
|
276
|
+
user may access). "Add workspace" (reuses `DirPicker`) is shown **only to
|
|
277
|
+
admins**. Switching navigates to `?ws=<id>` (and `path` resets). Two tabs
|
|
278
|
+
with different `?ws=` are fully independent.
|
|
279
|
+
- `DirPicker` (`src/components/dir-picker.tsx`): admin-only path. "Select" now
|
|
280
|
+
POSTs `/api/system/workspaces` (create) instead of `set-root`, then routes
|
|
281
|
+
to `?ws=<newId>`. Pins move under the chosen workspace.
|
|
282
|
+
- Settings sheet, admins only: an **Admins** section (promote/demote users)
|
|
283
|
+
and a per-workspace **Access** editor (`allowedUserIds`). The client reads an
|
|
284
|
+
`isAdmin` flag (from `/api/system/admins` or root-status) to gate this UI.
|
|
285
|
+
- `wiki-slugs-store`, `editor-store`, `tree-store`: any cached state that is
|
|
286
|
+
keyed by path must also be reset on workspace switch (cheap: remount the
|
|
287
|
+
page subtree on `ws` change via a React `key={activeWorkspaceId}`).
|
|
288
|
+
|
|
289
|
+
### 9. CLI / startup (`bin/wiki-viewer.js`, `ROOT_DIR`)
|
|
290
|
+
|
|
291
|
+
- `ROOT_DIR` (and `wiki-viewer ~/dir`) still works: on boot, if no workspaces
|
|
292
|
+
exist, auto-create one from `ROOT_DIR` and mark it default/lastOpened. So the
|
|
293
|
+
zero-config single-dir path is unchanged for existing users.
|
|
294
|
+
- README "switch the served directory" copy updates to mention multiple
|
|
295
|
+
concurrent workspaces.
|
|
296
|
+
|
|
297
|
+
## Isolation guarantees (the "no bleed" contract)
|
|
298
|
+
|
|
299
|
+
| Surface | Before | After |
|
|
300
|
+
| -------------------- | ----------------- | ------------------------------- |
|
|
301
|
+
| Root dir | one global | per request, from `ws` |
|
|
302
|
+
| Sidecars / activity | `<root>/.proof/` | unchanged (already per-root) |
|
|
303
|
+
| Edit leases | key=relPath | key=`wsId\0relPath` |
|
|
304
|
+
| In-proc + file locks | key=relPath | key=`wsId\0relPath` |
|
|
305
|
+
| Idempotency LRU | key=idemKey | key=`wsId\0idemKey` |
|
|
306
|
+
| Audit DB | path only | `+ workspace_id` column |
|
|
307
|
+
| Agents | global path globs | `+ workspaceId` grant, enforced |
|
|
308
|
+
| Access control | any signed-in | admin-set `allowedUserIds`/ws |
|
|
309
|
+
|
|
310
|
+
Two workspaces sharing the same relative path (`notes.md`) can be edited
|
|
311
|
+
simultaneously without lock contention, lease confusion, or idempotency
|
|
312
|
+
cross-talk.
|
|
313
|
+
|
|
314
|
+
## Testing
|
|
315
|
+
|
|
316
|
+
Extend `src/tests/proof/*`:
|
|
317
|
+
|
|
318
|
+
- Two-workspace lease test: lease on `(wsA, x.md)` does not make
|
|
319
|
+
`(wsB, x.md)` active.
|
|
320
|
+
- Mutex/lock test: concurrent writes to same relPath in two workspaces do not
|
|
321
|
+
serialize against each other (or at least do not corrupt).
|
|
322
|
+
- Idempotency: same key in two workspaces are independent.
|
|
323
|
+
- Scope test: agent granted `wsA` is 403 on `X-Workspace: wsB`.
|
|
324
|
+
- Access test: user not in `allowedUserIds` is 403; admin always passes.
|
|
325
|
+
- Admin test: non-admin POST /workspaces is 403; last-admin DELETE refused;
|
|
326
|
+
first-signup bootstrap promotes one admin.
|
|
327
|
+
- Migration test: old config.json (lastOpenedPath + pinnedPaths) yields one
|
|
328
|
+
workspace; old agents.json backfills workspaceId.
|
|
329
|
+
- Update existing tests: `setRootDir(tmpRoot)` calls in test setup become
|
|
330
|
+
"create a workspace + resolve it" (helper).
|
|
331
|
+
|
|
332
|
+
## Rollout / sequencing
|
|
333
|
+
|
|
334
|
+
0. Admin role: `auth/admin.ts`, `/api/system/admins`, bootstrap, settings UI
|
|
335
|
+
section. Independent, shippable alone.
|
|
336
|
+
1. `workspaces.ts` + `workspace-context.ts` + config migration (no route
|
|
337
|
+
changes yet; default workspace mirrors old behavior).
|
|
338
|
+
2. Namespacing stores (lease, mutex, file-lock, idempotency) with `wsId` arg,
|
|
339
|
+
defaulting to the single default workspace so nothing breaks.
|
|
340
|
+
3. Thread `ctx.rootDir` through all 29 routes; delete `getRootDir` global.
|
|
341
|
+
4. Agent workspace scoping + audit column.
|
|
342
|
+
5. Workspace management API + client switcher + DirPicker repoint.
|
|
343
|
+
6. Tests, README, CLI copy.
|
|
344
|
+
|
|
345
|
+
Steps 1-2 are non-breaking and shippable alone. Step 3 is the big mechanical
|
|
346
|
+
diff (good candidate for a `worker` pass with explicit outcomes per route).
|
|
347
|
+
|
|
348
|
+
## Open questions / risks
|
|
349
|
+
|
|
350
|
+
- **`pnpm-workspace.yaml` naming clash**: this repo is itself a pnpm workspace.
|
|
351
|
+
Use "workspace" in UI/API, but consider an internal code name like `space` or
|
|
352
|
+
keep `workspace` and just be careful in docs. Low risk, naming only.
|
|
353
|
+
- **SSE watcher fan-out**: one chokidar watcher per (connection, workspace).
|
|
354
|
+
Many open tabs on the same workspace = many watchers. Acceptable now;
|
|
355
|
+
optimize later with a shared per-workspace watcher + ref count if needed.
|
|
356
|
+
- **Admin bootstrap timing**: writing the first user into `adminUserIds` must
|
|
357
|
+
happen exactly once. Better-auth has no built-in "first user" hook, so
|
|
358
|
+
either tap the signup callback or do it lazily on the first authenticated
|
|
359
|
+
request when the admin set is empty (race-safe behind the config write
|
|
360
|
+
mutex). Decide during step 0.
|
|
361
|
+
- **`allowedUserIds` default**: empty = any signed-in user (matches today's
|
|
362
|
+
behavior). Admins tighten per workspace via the Access editor.
|
|
363
|
+
- Removing a workspace leaves `.proof/` and `.locks/` sentinels on disk; fine
|
|
364
|
+
(directory untouched by design), but document it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-viewer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Local file viewer and wiki — view markdown, HTML, PDFs, notebooks, office docs, and more from any directory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"wiki": "node bin/wiki-viewer.js",
|
|
20
20
|
"postbuild": "node bin/postbuild.js",
|
|
21
21
|
"prepublishOnly": "npm run build",
|
|
22
|
-
"test": "tsx --test src/tests/proof/*.test.ts"
|
|
22
|
+
"test": "tsx --import ./src/tests/proof/preload.ts --test src/tests/proof/*.test.ts"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=18.0.0"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { checkAuth, enforceScope } from "@/lib/proof/auth";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveWorkspaceForAgent } from "@/lib/workspace-context";
|
|
4
4
|
import {
|
|
5
5
|
aggregateActivity,
|
|
6
6
|
ACTIVITY_DEFAULT_LIMIT,
|
|
@@ -16,14 +16,15 @@ export async function GET(req: Request): Promise<NextResponse> {
|
|
|
16
16
|
return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
21
|
-
return NextResponse.json({ error:
|
|
19
|
+
const wsx = await resolveWorkspaceForAgent(req);
|
|
20
|
+
if (!wsx.ok) {
|
|
21
|
+
return NextResponse.json({ error: wsx.code }, { status: wsx.status });
|
|
22
22
|
}
|
|
23
|
+
const { ws, rootDir } = wsx;
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
return NextResponse.json({ error:
|
|
25
|
+
const scopeCheck = enforceScope(auth.agent, { op: "read", workspaceId: ws.id });
|
|
26
|
+
if (!scopeCheck.ok) {
|
|
27
|
+
return NextResponse.json({ error: scopeCheck.code, message: scopeCheck.message }, { status: 403 });
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const url = new URL(req.url);
|
|
@@ -32,7 +33,7 @@ export async function GET(req: Request): Promise<NextResponse> {
|
|
|
32
33
|
|
|
33
34
|
// Enforce scope on explicit file query parameter
|
|
34
35
|
if (fileFilter) {
|
|
35
|
-
const fileScope = enforceScope(auth.agent, { filePath: fileFilter, op: "read" });
|
|
36
|
+
const fileScope = enforceScope(auth.agent, { filePath: fileFilter, op: "read", workspaceId: ws.id });
|
|
36
37
|
if (!fileScope.ok) {
|
|
37
38
|
return NextResponse.json({ error: fileScope.code, message: fileScope.message }, { status: 403 });
|
|
38
39
|
}
|
|
@@ -3,7 +3,8 @@ import { checkAuth, enforceScope, verifyBy } from "@/lib/proof/auth";
|
|
|
3
3
|
import { readSidecar, writeSidecar, emptySidecar } from "@/lib/proof/sidecar";
|
|
4
4
|
import { pollEvents } from "@/lib/proof/event-bus";
|
|
5
5
|
import { withFileMutex } from "@/lib/proof/mutex";
|
|
6
|
-
import {
|
|
6
|
+
import { resolveWorkspaceForAgent } from "@/lib/workspace-context";
|
|
7
|
+
import { safeWorkspacePath } from "@/lib/workspaces";
|
|
7
8
|
|
|
8
9
|
export const runtime = "nodejs";
|
|
9
10
|
|
|
@@ -28,6 +29,12 @@ export async function GET(
|
|
|
28
29
|
return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
const wsx = await resolveWorkspaceForAgent(req);
|
|
33
|
+
if (!wsx.ok) {
|
|
34
|
+
return NextResponse.json({ error: wsx.code }, { status: wsx.status });
|
|
35
|
+
}
|
|
36
|
+
const { ws, rootDir } = wsx;
|
|
37
|
+
|
|
31
38
|
const { path: segments } = await params;
|
|
32
39
|
const rel = mdPath(segments);
|
|
33
40
|
|
|
@@ -38,12 +45,12 @@ export async function GET(
|
|
|
38
45
|
return NextResponse.json({ error: "INVALID_PATH", message: "Path must be .md or .markdown" }, { status: 400 });
|
|
39
46
|
}
|
|
40
47
|
|
|
41
|
-
const absPath =
|
|
48
|
+
const absPath = safeWorkspacePath(rootDir, rel);
|
|
42
49
|
if (!absPath) {
|
|
43
50
|
return NextResponse.json({ error: "INVALID_PATH", message: "Path traversal rejected" }, { status: 400 });
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
const scopeCheck = enforceScope(auth.agent, { filePath: rel, op: "read" });
|
|
53
|
+
const scopeCheck = enforceScope(auth.agent, { filePath: rel, op: "read", workspaceId: ws.id });
|
|
47
54
|
if (!scopeCheck.ok) {
|
|
48
55
|
return NextResponse.json({ error: scopeCheck.code, message: scopeCheck.message }, { status: 403 });
|
|
49
56
|
}
|
|
@@ -65,7 +72,6 @@ export async function GET(
|
|
|
65
72
|
limit = MAX_LIMIT;
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
const rootDir = getRootDir();
|
|
69
75
|
const sidecar = (await readSidecar(rootDir, rel)) ?? emptySidecar(rel);
|
|
70
76
|
const events = pollEvents(sidecar, after, limit);
|
|
71
77
|
const lastEventId = sidecar.nextEventId - 1;
|
|
@@ -83,6 +89,12 @@ export async function POST(
|
|
|
83
89
|
return NextResponse.json({ error: "UNAUTHORIZED" }, { status: 401 });
|
|
84
90
|
}
|
|
85
91
|
|
|
92
|
+
const wsx = await resolveWorkspaceForAgent(req);
|
|
93
|
+
if (!wsx.ok) {
|
|
94
|
+
return NextResponse.json({ error: wsx.code }, { status: wsx.status });
|
|
95
|
+
}
|
|
96
|
+
const { ws, rootDir } = wsx;
|
|
97
|
+
|
|
86
98
|
const { path: segments } = await params;
|
|
87
99
|
const rel = mdPath(segments);
|
|
88
100
|
|
|
@@ -93,12 +105,12 @@ export async function POST(
|
|
|
93
105
|
return NextResponse.json({ error: "INVALID_PATH", message: "Path must be .md or .markdown" }, { status: 400 });
|
|
94
106
|
}
|
|
95
107
|
|
|
96
|
-
const absPath =
|
|
108
|
+
const absPath = safeWorkspacePath(rootDir, rel);
|
|
97
109
|
if (!absPath) {
|
|
98
110
|
return NextResponse.json({ error: "INVALID_PATH", message: "Path traversal rejected" }, { status: 400 });
|
|
99
111
|
}
|
|
100
112
|
|
|
101
|
-
const scopeCheck = enforceScope(auth.agent, { filePath: rel, op: "mutate" });
|
|
113
|
+
const scopeCheck = enforceScope(auth.agent, { filePath: rel, op: "mutate", workspaceId: ws.id });
|
|
102
114
|
if (!scopeCheck.ok) {
|
|
103
115
|
return NextResponse.json({ error: scopeCheck.code, message: scopeCheck.message }, { status: 403 });
|
|
104
116
|
}
|
|
@@ -122,11 +134,11 @@ export async function POST(
|
|
|
122
134
|
return NextResponse.json({ error: byCheck.code, message: byCheck.message }, { status: 403 });
|
|
123
135
|
}
|
|
124
136
|
|
|
125
|
-
const rootDir = getRootDir();
|
|
126
137
|
const upToId = body.upToId as number;
|
|
127
138
|
const by = body.by as string;
|
|
128
139
|
|
|
129
|
-
|
|
140
|
+
// Namespace mutex key with rootDir to prevent cross-workspace lock contention.
|
|
141
|
+
await withFileMutex(`${rootDir}\0${rel}`, async () => {
|
|
130
142
|
const sidecar = (await readSidecar(rootDir, rel)) ?? emptySidecar(rel);
|
|
131
143
|
sidecar.lastAck[by] = upToId;
|
|
132
144
|
await writeSidecar(rootDir, rel, sidecar);
|