wiki-viewer 1.7.2 → 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.
Files changed (414) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +6 -0
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/routes-manifest.json +40 -0
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/.next/standalone/.next/server/app/api/agent/activity/route.js +6 -6
  23. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js +6 -6
  25. package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js +6 -6
  27. package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js +7 -7
  29. package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js +5 -5
  31. package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js +5 -5
  33. package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +8 -7
  35. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +9 -8
  37. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +8 -7
  39. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +7 -7
  41. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +6 -5
  43. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +7 -6
  45. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +6 -5
  47. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/agent/settings/route.js +7 -7
  49. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +8 -7
  51. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js +2 -2
  53. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js +8 -2
  55. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/api/auth/[...all]/route.js +5 -5
  57. package/.next/standalone/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/system/admins/route/app-paths-manifest.json +3 -0
  59. package/.next/standalone/.next/server/app/api/system/admins/route/build-manifest.json +9 -0
  60. package/.next/standalone/.next/server/app/api/system/admins/route/server-reference-manifest.json +4 -0
  61. package/.next/standalone/.next/server/app/api/system/admins/route.js +14 -0
  62. package/.next/standalone/.next/server/app/api/system/admins/route.js.map +5 -0
  63. package/.next/standalone/.next/server/app/api/system/admins/route.js.nft.json +1 -0
  64. package/.next/standalone/.next/server/app/api/system/admins/route_client-reference-manifest.js +3 -0
  65. package/.next/standalone/.next/server/app/api/system/auth-config/route.js +6 -6
  66. package/.next/standalone/.next/server/app/api/system/auth-config/route.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/api/system/auth-settings/route.js +4 -4
  68. package/.next/standalone/.next/server/app/api/system/auth-settings/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/api/system/browse/route.js +4 -4
  70. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/system/clear-root/route.js +5 -5
  72. package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/system/config/route.js +6 -6
  74. package/.next/standalone/.next/server/app/api/system/config/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/system/pins/route.js +4 -4
  76. package/.next/standalone/.next/server/app/api/system/pins/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/system/reveal/route.js +6 -6
  78. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/system/root-status/route.js +5 -5
  80. package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/system/set-root/route.js +6 -6
  82. package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/system/users/route/app-paths-manifest.json +3 -0
  84. package/.next/standalone/.next/server/app/api/system/users/route/build-manifest.json +9 -0
  85. package/.next/standalone/.next/server/app/api/system/users/route/server-reference-manifest.json +4 -0
  86. package/.next/standalone/.next/server/app/api/system/users/route.js +14 -0
  87. package/.next/standalone/.next/server/app/api/system/users/route.js.map +5 -0
  88. package/.next/standalone/.next/server/app/api/system/users/route.js.nft.json +1 -0
  89. package/.next/standalone/.next/server/app/api/system/users/route_client-reference-manifest.js +3 -0
  90. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/app-paths-manifest.json +3 -0
  91. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/build-manifest.json +9 -0
  92. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route/server-reference-manifest.json +4 -0
  93. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js +14 -0
  94. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.map +5 -0
  95. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -0
  96. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route_client-reference-manifest.js +3 -0
  97. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/app-paths-manifest.json +3 -0
  98. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/build-manifest.json +9 -0
  99. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route/server-reference-manifest.json +4 -0
  100. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js +14 -0
  101. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.map +5 -0
  102. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -0
  103. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route_client-reference-manifest.js +3 -0
  104. package/.next/standalone/.next/server/app/api/system/workspaces/route/app-paths-manifest.json +3 -0
  105. package/.next/standalone/.next/server/app/api/system/workspaces/route/build-manifest.json +9 -0
  106. package/.next/standalone/.next/server/app/api/system/workspaces/route/server-reference-manifest.json +4 -0
  107. package/.next/standalone/.next/server/app/api/system/workspaces/route.js +14 -0
  108. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.map +5 -0
  109. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -0
  110. package/.next/standalone/.next/server/app/api/system/workspaces/route_client-reference-manifest.js +3 -0
  111. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js +8 -2
  112. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  113. package/.next/standalone/.next/server/app/api/wiki/app/route.js +5 -5
  114. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/api/wiki/content/route.js +6 -6
  116. package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
  117. package/.next/standalone/.next/server/app/api/wiki/download/route.js +7 -7
  118. package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
  119. package/.next/standalone/.next/server/app/api/wiki/file/route.js +8 -2
  120. package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/api/wiki/folder/route.js +6 -6
  122. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  123. package/.next/standalone/.next/server/app/api/wiki/move/route.js +6 -6
  124. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  125. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js +7 -7
  126. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  127. package/.next/standalone/.next/server/app/api/wiki/page/route.js +7 -7
  128. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  129. package/.next/standalone/.next/server/app/api/wiki/presence/route.js +7 -7
  130. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
  131. package/.next/standalone/.next/server/app/api/wiki/route.js +6 -6
  132. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/api/wiki/search/route/app-paths-manifest.json +3 -0
  134. package/.next/standalone/.next/server/app/api/wiki/search/route/build-manifest.json +9 -0
  135. package/.next/standalone/.next/server/app/api/wiki/search/route/server-reference-manifest.json +4 -0
  136. package/.next/standalone/.next/server/app/api/wiki/search/route.js +15 -0
  137. package/.next/standalone/.next/server/app/api/wiki/search/route.js.map +5 -0
  138. package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -0
  139. package/.next/standalone/.next/server/app/api/wiki/search/route_client-reference-manifest.js +3 -0
  140. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js +6 -6
  141. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/wiki/upload/route.js +7 -7
  143. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  144. package/.next/standalone/.next/server/app/api/wiki/watch/route.js +7 -9
  145. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  146. package/.next/standalone/.next/server/app/index.html +1 -1
  147. package/.next/standalone/.next/server/app/index.rsc +3 -3
  148. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  149. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  150. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  151. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  152. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  153. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  154. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  155. package/.next/standalone/.next/server/app/signin/page.js +1 -1
  156. package/.next/standalone/.next/server/app/signin/page.js.nft.json +1 -1
  157. package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  158. package/.next/standalone/.next/server/app-paths-manifest.json +6 -0
  159. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0d2jbo5.js +1 -1
  160. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0ytral2.js +1 -1
  161. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-244bv._.js +16 -0
  162. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0xz-_0v._.js → [root-of-the-server]__0-inabx._.js} +2 -2
  163. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-ao6a_._.js → [root-of-the-server]__0.-9jt0._.js} +1 -1
  164. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0~-r-8f._.js → [root-of-the-server]__00c1-dr._.js} +1 -1
  165. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00lep-u._.js +3 -0
  166. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00mtevo._.js +3 -0
  167. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00qu86p._.js +3 -0
  168. package/.next/standalone/.next/server/chunks/[root-of-the-server]__012753j._.js +3 -0
  169. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01hp8ax._.js +3 -0
  170. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0jhr.ex._.js → [root-of-the-server]__02n6z97._.js} +1 -1
  171. package/.next/standalone/.next/server/chunks/[root-of-the-server]__036mhjp._.js +5 -0
  172. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03p0ob1._.js +16 -0
  173. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05t~v6l._.js +3 -0
  174. package/.next/standalone/.next/server/chunks/[root-of-the-server]__068ni_k._.js +3 -0
  175. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__082nhm6._.js → [root-of-the-server]__07vphbd._.js} +1 -1
  176. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0pg6dn-._.js → [root-of-the-server]__08lkuwa._.js} +1 -1
  177. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09cqzem._.js +3 -0
  178. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0p_ox-k._.js → [root-of-the-server]__09k4q9e._.js} +1 -1
  179. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0_601._.js +3 -0
  180. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ae3dk6._.js +3 -0
  181. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0s28.4-._.js → [root-of-the-server]__0af-x45._.js} +1 -1
  182. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cqu-wm._.js +3 -0
  183. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d75a5q._.js +1 -1
  184. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d7x3e1._.js +3 -0
  185. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d7zxm_._.js +3 -0
  186. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e7vs98._.js +3 -0
  187. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fxzg4v._.js +3 -0
  188. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gda_iu._.js +3 -0
  189. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hu~q20._.js +3 -0
  190. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jk1uf4._.js +3 -0
  191. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-8clq._.js +3 -0
  192. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lhka0x._.js +3 -0
  193. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0kdn1le._.js → [root-of-the-server]__0na11h0._.js} +2 -2
  194. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nd.b_c._.js +3 -0
  195. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0otb8ra._.js +3 -0
  196. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0p0-p__._.js +3 -0
  197. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0piofm3._.js +3 -0
  198. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rrf-jr._.js +3 -0
  199. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s6_lzw._.js +3 -0
  200. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sh13v5._.js +3 -0
  201. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ta~flu._.js +3 -0
  202. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0v4im4p._.js +3 -0
  203. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0qn8e~s._.js → [root-of-the-server]__0v_kyp-._.js} +1 -1
  204. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vatqe-._.js +3 -0
  205. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vdb2nr._.js +3 -0
  206. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wdh63w._.js +3 -0
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z3wg9x._.js +12 -0
  208. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0mzo03s._.js → [root-of-the-server]__0~.-3yk._.js} +1 -1
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__123._fe._.js +3 -0
  210. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__12800b1._.js → [root-of-the-server]__12ee_s6._.js} +1 -1
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__12onytt._.js +3 -0
  212. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04udrya._.js → [root-of-the-server]__12q8ch8._.js} +2 -2
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1377.79._.js +3 -0
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1378-ox._.js +3 -0
  215. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13aid91._.js +3 -0
  216. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13hp4tw._.js +12 -0
  217. package/.next/standalone/.next/server/chunks/_079xuln._.js +1 -1
  218. package/.next/standalone/.next/server/chunks/_0dr1r1g._.js +452 -0
  219. package/.next/standalone/.next/server/chunks/_101exke._.js +452 -0
  220. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_admins_route_actions_0dsm7cx.js +3 -0
  221. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_users_route_actions_0whj5cy.js +3 -0
  222. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_[id]_open_route_actions_1087xu7.js +3 -0
  223. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_[id]_route_actions_0~h7-8s.js +3 -0
  224. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_route_actions_0mkalc3.js +3 -0
  225. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wiki_search_route_actions_0inxu93.js +3 -0
  226. package/.next/standalone/.next/server/chunks/node_modules__pnpm_0aj5g-w._.js +1 -1
  227. package/.next/standalone/.next/server/chunks/src_lib_search_0d-_j3u._.js +46 -0
  228. package/.next/standalone/.next/server/chunks/src_lib_search_0ymmp0c._.js +46 -0
  229. 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
  230. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_erDiagram-TEJ5UH35_mjs_0v6rnqy._.js +1 -1
  231. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_mermaid_core_mjs_0hj_n-~._.js +1 -1
  232. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0btcd4o._.js → [root-of-the-server]__02twzph._.js} +2 -2
  233. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ytvlzs._.js → [root-of-the-server]__09~x2v~._.js} +2 -2
  234. package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +18 -18
  235. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0c71i8a._.js → node_modules__pnpm_0.96tmo._.js} +1 -1
  236. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0lm8e2c._.js → node_modules__pnpm_011vy_q._.js} +2 -2
  237. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ezm2us._.js → node_modules__pnpm_01o_5qt._.js} +1 -1
  238. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j3mvgg._.js → node_modules__pnpm_04n0h6.._.js} +1 -1
  239. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0~ln0by._.js → node_modules__pnpm_093dp2q._.js} +1 -1
  240. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_11e~j49._.js → node_modules__pnpm_09ej62_._.js} +1 -1
  241. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0de8xt-._.js → node_modules__pnpm_09ljbdz._.js} +2 -2
  242. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-x6zex._.js → node_modules__pnpm_09~_ob2._.js} +1 -1
  243. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0yq5z.h._.js → node_modules__pnpm_0a01ng7._.js} +2 -2
  244. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0kwc-vs._.js → node_modules__pnpm_0b8qx0g._.js} +1 -1
  245. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hrbz84._.js → node_modules__pnpm_0bbc---._.js} +1 -1
  246. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hobcbs._.js → node_modules__pnpm_0c.309.._.js} +1 -1
  247. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0rw91-f._.js → node_modules__pnpm_0c8aunj._.js} +1 -1
  248. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0o.c1z8._.js → node_modules__pnpm_0hk6xys._.js} +2 -2
  249. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0dboa_e._.js → node_modules__pnpm_0hlzy8f._.js} +1 -1
  250. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0j53hti._.js → node_modules__pnpm_0nw5ou2._.js} +1 -1
  251. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xrmzh1._.js → node_modules__pnpm_0o.dn8o._.js} +1 -1
  252. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0cxwhfd._.js → node_modules__pnpm_0s0ur~d._.js} +1 -1
  253. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_07rxv2x._.js → node_modules__pnpm_0shlang._.js} +1 -1
  254. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i.pw_m._.js → node_modules__pnpm_0sq3c2r._.js} +1 -1
  255. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ajzar9._.js → node_modules__pnpm_0t9jb7d._.js} +1 -1
  256. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_012nt.s._.js → node_modules__pnpm_0wmx9kq._.js} +2 -2
  257. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0z~h2ow._.js → node_modules__pnpm_0xfx0sf._.js} +1 -1
  258. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0cbsnwf._.js → node_modules__pnpm_0yiz.-t._.js} +1 -1
  259. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0bum3-3._.js → node_modules__pnpm_11sut1u._.js} +1 -1
  260. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0mdo4i3._.js → node_modules__pnpm_124u4o2._.js} +2 -2
  261. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0il8j.j._.js → node_modules__pnpm_13tajgx._.js} +2 -2
  262. package/.next/standalone/.next/server/chunks/ssr/src_app_signin_00yk0vm._.js +2 -2
  263. 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
  264. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0.pmgt2._.js +3 -0
  265. package/.next/standalone/.next/server/functions-config-manifest.json +6 -0
  266. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  267. package/.next/standalone/.next/server/middleware-manifest.json +8 -8
  268. package/.next/standalone/.next/server/pages/404.html +1 -1
  269. package/.next/standalone/.next/server/pages/500.html +1 -1
  270. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  271. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  272. package/.next/standalone/.next/static/chunks/0c_~zevf2n0r4.css +2 -0
  273. package/.next/standalone/.next/static/chunks/0l4842ez-0_66.js +129 -0
  274. package/.next/standalone/README.md +46 -2
  275. package/.next/standalone/docs/workspacing-plan.md +364 -0
  276. package/.next/standalone/package.json +2 -2
  277. package/.next/standalone/src/app/api/agent/activity/route.ts +9 -8
  278. package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +20 -8
  279. package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +16 -9
  280. package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +28 -18
  281. package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +12 -7
  282. package/.next/standalone/src/app/api/agent/fs/move/route.ts +16 -11
  283. package/.next/standalone/src/app/api/agent/fs/search/route.ts +49 -13
  284. package/.next/standalone/src/app/api/agent/internal/span/route.ts +9 -5
  285. package/.next/standalone/src/app/api/agent/register/route.ts +9 -1
  286. package/.next/standalone/src/app/api/agent/settings/route.ts +7 -3
  287. package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +8 -4
  288. package/.next/standalone/src/app/api/assets/[...path]/route.ts +9 -4
  289. package/.next/standalone/src/app/api/system/admins/route.ts +103 -0
  290. package/.next/standalone/src/app/api/system/reveal/route.ts +7 -6
  291. package/.next/standalone/src/app/api/system/set-root/route.ts +24 -1
  292. package/.next/standalone/src/app/api/system/users/route.ts +120 -0
  293. package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +34 -0
  294. package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +74 -0
  295. package/.next/standalone/src/app/api/system/workspaces/route.ts +67 -0
  296. package/.next/standalone/src/app/api/upload/[...path]/route.ts +10 -5
  297. package/.next/standalone/src/app/api/wiki/app/route.ts +10 -9
  298. package/.next/standalone/src/app/api/wiki/content/route.ts +14 -14
  299. package/.next/standalone/src/app/api/wiki/download/route.ts +7 -6
  300. package/.next/standalone/src/app/api/wiki/file/route.ts +7 -2
  301. package/.next/standalone/src/app/api/wiki/folder/route.ts +6 -5
  302. package/.next/standalone/src/app/api/wiki/move/route.ts +7 -7
  303. package/.next/standalone/src/app/api/wiki/new-file/route.ts +7 -6
  304. package/.next/standalone/src/app/api/wiki/page/route.ts +6 -5
  305. package/.next/standalone/src/app/api/wiki/presence/route.ts +8 -7
  306. package/.next/standalone/src/app/api/wiki/route.ts +13 -11
  307. package/.next/standalone/src/app/api/wiki/search/route.ts +44 -0
  308. package/.next/standalone/src/app/api/wiki/slugs/route.ts +8 -8
  309. package/.next/standalone/src/app/api/wiki/upload/route.ts +6 -5
  310. package/.next/standalone/src/app/api/wiki/watch/route.ts +16 -40
  311. package/.next/standalone/src/app/globals.css +484 -484
  312. package/.next/standalone/src/app/page.tsx +163 -49
  313. package/.next/standalone/src/components/auth-settings-sheet.tsx +171 -2
  314. package/.next/standalone/src/components/dir-picker.tsx +8 -6
  315. package/.next/standalone/src/components/editor/comment-thread.tsx +2 -1
  316. package/.next/standalone/src/components/editor/csv-viewer.tsx +3 -2
  317. package/.next/standalone/src/components/editor/editor-toolbar.tsx +2 -2
  318. package/.next/standalone/src/components/editor/editor.tsx +6 -5
  319. package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +3 -2
  320. package/.next/standalone/src/components/editor/folder-index.tsx +2 -1
  321. package/.next/standalone/src/components/editor/image-viewer.tsx +2 -1
  322. package/.next/standalone/src/components/editor/media-popover.tsx +2 -1
  323. package/.next/standalone/src/components/editor/media-viewer.tsx +2 -1
  324. package/.next/standalone/src/components/editor/mermaid-viewer.tsx +3 -2
  325. package/.next/standalone/src/components/editor/node-app-viewer.tsx +4 -3
  326. package/.next/standalone/src/components/editor/notebook-viewer.tsx +3 -2
  327. package/.next/standalone/src/components/editor/office/docx-viewer.tsx +2 -1
  328. package/.next/standalone/src/components/editor/office/office-chrome.tsx +3 -2
  329. package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +2 -1
  330. package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +2 -1
  331. package/.next/standalone/src/components/editor/pdf-viewer.tsx +2 -1
  332. package/.next/standalone/src/components/editor/proof-span-popover.tsx +2 -1
  333. package/.next/standalone/src/components/editor/source-viewer.tsx +3 -2
  334. package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +2 -1
  335. package/.next/standalone/src/components/editor/suggestion-card.tsx +2 -1
  336. package/.next/standalone/src/components/editor/website-viewer.tsx +2 -1
  337. package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +2 -1
  338. package/.next/standalone/src/components/search/search-command-dialog.tsx +109 -0
  339. package/.next/standalone/src/components/search/sidebar-search-box.tsx +121 -0
  340. package/.next/standalone/src/components/search/snippet-text.tsx +48 -0
  341. package/.next/standalone/src/components/ui/command.tsx +3 -2
  342. package/.next/standalone/src/lib/auth/admin.ts +112 -0
  343. package/.next/standalone/src/lib/auth/server.ts +13 -2
  344. package/.next/standalone/src/lib/config.ts +56 -4
  345. package/.next/standalone/src/lib/proof/audit.ts +19 -10
  346. package/.next/standalone/src/lib/proof/auth.ts +11 -0
  347. package/.next/standalone/src/lib/proof/collab-state.ts +4 -2
  348. package/.next/standalone/src/lib/proof/file-lock.ts +5 -0
  349. package/.next/standalone/src/lib/proof/idempotency.ts +5 -0
  350. package/.next/standalone/src/lib/proof/lease.ts +40 -27
  351. package/.next/standalone/src/lib/proof/mutex.ts +4 -0
  352. package/.next/standalone/src/lib/proof/raw-fs.ts +2 -3
  353. package/.next/standalone/src/lib/proof/registry.ts +5 -0
  354. package/.next/standalone/src/lib/search/indexable-exts.ts +25 -0
  355. package/.next/standalone/src/lib/search/indexer.ts +567 -0
  356. package/.next/standalone/src/lib/search/sanitize.ts +35 -0
  357. package/.next/standalone/src/lib/search/search-db.ts +57 -0
  358. package/.next/standalone/src/lib/search/watcher-pool.ts +79 -0
  359. package/.next/standalone/src/lib/workspace-client.ts +82 -0
  360. package/.next/standalone/src/lib/workspace-context.ts +142 -0
  361. package/.next/standalone/src/lib/workspaces.ts +196 -0
  362. package/.next/standalone/src/stores/ai-panel-store.ts +3 -2
  363. package/.next/standalone/src/stores/editor-store.ts +4 -3
  364. package/.next/standalone/src/stores/proof-store.ts +4 -3
  365. package/.next/standalone/src/stores/search-store.ts +86 -0
  366. package/.next/standalone/src/stores/wiki-slugs-store.ts +2 -1
  367. package/.next/standalone/src/tests/proof/admin-create-user.test.ts +43 -0
  368. package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +134 -0
  369. package/.next/standalone/src/tests/proof/collab-state.test.ts +43 -41
  370. package/.next/standalone/src/tests/proof/preload.ts +21 -0
  371. package/.next/standalone/src/tests/proof/search-index.test.ts +339 -0
  372. package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +87 -0
  373. package/.next/standalone/src/tests/proof/workspaces-api.test.ts +227 -0
  374. package/.next/standalone/tsconfig.tsbuildinfo +1 -1
  375. package/README.md +46 -2
  376. package/package.json +2 -2
  377. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0o~h4zq.js +0 -5
  378. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.hb9q2._.js +0 -3
  379. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.uux34._.js +0 -3
  380. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0092hkp._.js +0 -3
  381. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05f6x0i._.js +0 -3
  382. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05idwm9._.js +0 -3
  383. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06-dtd0._.js +0 -3
  384. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06sek6_._.js +0 -3
  385. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07fpqks._.js +0 -3
  386. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094q5bj._.js +0 -3
  387. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0awzyon._.js +0 -3
  388. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d_da6q._.js +0 -3
  389. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0hhhvm7._.js +0 -3
  390. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kk26wc._.js +0 -3
  391. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0km8suj._.js +0 -3
  392. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kur4ro._.js +0 -15
  393. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0mkwoul._.js +0 -12
  394. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qc2~6l._.js +0 -15
  395. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qyd-o8._.js +0 -3
  396. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rkve~v._.js +0 -3
  397. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rzo40z._.js +0 -3
  398. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sjocwy._.js +0 -3
  399. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ssj9~d._.js +0 -3
  400. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s~i040._.js +0 -3
  401. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t220d-._.js +0 -3
  402. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t5mg.n._.js +0 -3
  403. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0trsqf4._.js +0 -3
  404. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xq124z._.js +0 -3
  405. package/.next/standalone/.next/server/chunks/[root-of-the-server]__106uw_c._.js +0 -3
  406. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10w2q~o._.js +0 -3
  407. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13w-0iy._.js +0 -3
  408. package/.next/standalone/.next/server/chunks/src_lib_auth_server_ts_00f4e0h._.js +0 -452
  409. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__0ug2rvl._.js +0 -3
  410. package/.next/standalone/.next/static/chunks/0vwkqw_rofm-c.js +0 -129
  411. package/.next/standalone/.next/static/chunks/0zay6349~v_wc.css +0 -2
  412. /package/.next/standalone/.next/static/{0XvNCOSEq7cbARTLtAolL → t7B6nXaoD1jo9f61t3aeK}/_buildManifest.js +0 -0
  413. /package/.next/standalone/.next/static/{0XvNCOSEq7cbARTLtAolL → t7B6nXaoD1jo9f61t3aeK}/_clientMiddlewareManifest.js +0 -0
  414. /package/.next/standalone/.next/static/{0XvNCOSEq7cbARTLtAolL → t7B6nXaoD1jo9f61t3aeK}/_ssgManifest.js +0 -0
@@ -0,0 +1,121 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Persistent sidebar search box. Renders an inline results dropdown under the
5
+ * input. Shares state with the cmd+k palette via the search store.
6
+ * Supports keyboard navigation: ArrowUp / ArrowDown to move, Enter to open,
7
+ * Escape to clear.
8
+ */
9
+ import { useEffect, useRef, useState } from "react";
10
+ import { Search } from "lucide-react";
11
+ import { useSearchStore } from "@/stores/search-store";
12
+ import { SnippetText } from "./snippet-text";
13
+
14
+ export function SidebarSearchBox({
15
+ onOpenFile,
16
+ }: {
17
+ onOpenFile: (relPath: string) => void;
18
+ }) {
19
+ const query = useSearchStore((s) => s.query);
20
+ const setQuery = useSearchStore((s) => s.setQuery);
21
+ const results = useSearchStore((s) => s.results);
22
+ const loading = useSearchStore((s) => s.loading);
23
+ const search = useSearchStore((s) => s.search);
24
+ const clear = useSearchStore((s) => s.clear);
25
+
26
+ const [active, setActive] = useState(0);
27
+ const listRef = useRef<HTMLDivElement>(null);
28
+
29
+ // Reset / clamp the highlighted row whenever the result set changes.
30
+ useEffect(() => {
31
+ setActive(0);
32
+ }, [results]);
33
+
34
+ // Keep the active row scrolled into view.
35
+ useEffect(() => {
36
+ const el = listRef.current?.querySelector<HTMLElement>(
37
+ `[data-idx="${active}"]`,
38
+ );
39
+ el?.scrollIntoView({ block: "nearest" });
40
+ }, [active]);
41
+
42
+ function openAt(idx: number) {
43
+ const r = results[idx];
44
+ if (!r) return;
45
+ onOpenFile(r.path);
46
+ clear();
47
+ }
48
+
49
+ return (
50
+ <div className="flex flex-col gap-1 px-2 py-1.5">
51
+ <div className="relative">
52
+ <Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 opacity-50" />
53
+ <input
54
+ type="text"
55
+ className="w-full rounded-sm border bg-transparent py-1 pl-7 pr-2 text-xs outline-none focus:ring-1 focus:ring-ring"
56
+ placeholder="Search… (⌘K)"
57
+ value={query}
58
+ onChange={(e) => {
59
+ setQuery(e.target.value);
60
+ void search(e.target.value);
61
+ }}
62
+ onKeyDown={(e) => {
63
+ if (e.key === "Escape") {
64
+ clear();
65
+ return;
66
+ }
67
+ if (results.length === 0) return;
68
+ if (e.key === "ArrowDown") {
69
+ e.preventDefault();
70
+ setActive((i) => (i + 1) % results.length);
71
+ } else if (e.key === "ArrowUp") {
72
+ e.preventDefault();
73
+ setActive((i) => (i - 1 + results.length) % results.length);
74
+ } else if (e.key === "Enter") {
75
+ e.preventDefault();
76
+ openAt(active);
77
+ }
78
+ }}
79
+ />
80
+ </div>
81
+ {query.trim() !== "" && (
82
+ <div
83
+ ref={listRef}
84
+ className="max-h-64 overflow-y-auto rounded-sm border bg-popover"
85
+ >
86
+ {loading && (
87
+ <div className="px-2 py-1 text-xs text-muted-foreground">
88
+ Searching…
89
+ </div>
90
+ )}
91
+ {!loading && results.length === 0 && (
92
+ <div className="px-2 py-1 text-xs text-muted-foreground">
93
+ No matches
94
+ </div>
95
+ )}
96
+ {results.map((r, idx) => (
97
+ <button
98
+ key={r.path}
99
+ type="button"
100
+ data-idx={idx}
101
+ onClick={() => openAt(idx)}
102
+ onMouseEnter={() => setActive(idx)}
103
+ className={`block w-full px-2 py-1 text-left ${
104
+ idx === active ? "bg-accent" : "hover:bg-accent"
105
+ }`}
106
+ >
107
+ <div className="truncate font-mono text-xs">
108
+ {r.path}
109
+ </div>
110
+ {r.snippet && (
111
+ <div className="truncate text-[10px] text-muted-foreground">
112
+ <SnippetText html={r.snippet} />
113
+ </div>
114
+ )}
115
+ </button>
116
+ ))}
117
+ </div>
118
+ )}
119
+ </div>
120
+ );
121
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Safe renderer for FTS5 snippet() output.
3
+ * Parses <mark>...</mark> tags from the snippet string without using
4
+ * dangerouslySetInnerHTML. Any other HTML in source files is rendered as text.
5
+ */
6
+
7
+ interface Part {
8
+ text: string;
9
+ mark: boolean;
10
+ }
11
+
12
+ function parseSnippet(html: string): Part[] {
13
+ const parts: Part[] = [];
14
+ const re = /<mark>([\s\S]*?)<\/mark>/g;
15
+ let last = 0;
16
+ let m: RegExpExecArray | null;
17
+ while ((m = re.exec(html)) !== null) {
18
+ if (m.index > last) {
19
+ parts.push({ text: html.slice(last, m.index), mark: false });
20
+ }
21
+ parts.push({ text: m[1] ?? "", mark: true });
22
+ last = m.index + m[0].length;
23
+ }
24
+ if (last < html.length) {
25
+ parts.push({ text: html.slice(last), mark: false });
26
+ }
27
+ return parts;
28
+ }
29
+
30
+ export function SnippetText({ html }: { html: string }) {
31
+ const parts = parseSnippet(html);
32
+ return (
33
+ <>
34
+ {parts.map((p, i) =>
35
+ p.mark ? (
36
+ <mark
37
+ key={i}
38
+ className="bg-yellow-200 dark:bg-yellow-700 rounded-[2px] px-[1px]"
39
+ >
40
+ {p.text}
41
+ </mark>
42
+ ) : (
43
+ <span key={i}>{p.text}</span>
44
+ ),
45
+ )}
46
+ </>
47
+ );
48
+ }
@@ -23,13 +23,14 @@ Command.displayName = CommandPrimitive.displayName;
23
23
 
24
24
  function CommandDialog({
25
25
  children,
26
+ shouldFilter,
26
27
  ...props
27
- }: React.ComponentProps<typeof Dialog>) {
28
+ }: React.ComponentProps<typeof Dialog> & { shouldFilter?: boolean }) {
28
29
  return (
29
30
  <Dialog {...props}>
30
31
  <DialogContent className="overflow-hidden p-0 shadow-golden max-w-lg">
31
32
  <DialogTitle className="sr-only">Search</DialogTitle>
32
- <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-normal [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
33
+ <Command shouldFilter={shouldFilter} className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-normal [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
33
34
  {children}
34
35
  </Command>
35
36
  </DialogContent>
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Global admin role.
3
+ *
4
+ * Resolution order:
5
+ * 1. WIKI_ADMIN_EMAILS env (csv, case-insensitive) — seed/override, headless-friendly.
6
+ * 2. config.adminUserIds — persisted list managed via the admin API.
7
+ *
8
+ * Bootstrap: if adminUserIds is empty AND no WIKI_ADMIN_EMAILS is set,
9
+ * the first call to ensureBootstrapAdmin(userId) writes that user as admin.
10
+ * Called lazily on the first authenticated request (race-safe: re-read inside).
11
+ */
12
+
13
+ import { readConfig, updateConfig } from "@/lib/config";
14
+ import { requireUser } from "@/lib/auth/server";
15
+
16
+ // ── Internal helpers ───────────────────────────────────────────────────────────
17
+
18
+ function adminEmailsFromEnv(): string[] {
19
+ return (process.env.WIKI_ADMIN_EMAILS ?? "")
20
+ .split(",")
21
+ .map((e) => e.trim().toLowerCase())
22
+ .filter(Boolean);
23
+ }
24
+
25
+ // ── Public API ─────────────────────────────────────────────────────────────────
26
+
27
+ export async function isAdmin(userId: string, email?: string): Promise<boolean> {
28
+ // Env override: any matching email is admin
29
+ if (email) {
30
+ const envAdmins = adminEmailsFromEnv();
31
+ if (envAdmins.length > 0 && envAdmins.includes(email.toLowerCase())) {
32
+ return true;
33
+ }
34
+ }
35
+ const cfg = await readConfig();
36
+ return (cfg.adminUserIds ?? []).includes(userId);
37
+ }
38
+
39
+ export async function listAdmins(): Promise<string[]> {
40
+ const cfg = await readConfig();
41
+ return cfg.adminUserIds ?? [];
42
+ }
43
+
44
+ export async function addAdmin(userId: string): Promise<void> {
45
+ await updateConfig((cfg) => {
46
+ const current = cfg.adminUserIds ?? [];
47
+ if (current.includes(userId)) return cfg;
48
+ return { ...cfg, adminUserIds: [...current, userId] };
49
+ });
50
+ }
51
+
52
+ export async function removeAdmin(userId: string): Promise<void> {
53
+ const hasEnvFallback = adminEmailsFromEnv().length > 0;
54
+ let rejected = false;
55
+ await updateConfig((cfg) => {
56
+ const current = cfg.adminUserIds ?? [];
57
+ const next = current.filter((id) => id !== userId);
58
+ // Refuse to remove last admin if no env fallback exists
59
+ if (next.length === 0 && !hasEnvFallback) {
60
+ rejected = true;
61
+ return cfg;
62
+ }
63
+ return { ...cfg, adminUserIds: next };
64
+ });
65
+ if (rejected) {
66
+ throw new Error(
67
+ "LAST_ADMIN: cannot remove the last admin with no WIKI_ADMIN_EMAILS configured",
68
+ );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * If adminUserIds is empty AND no WIKI_ADMIN_EMAILS, write userId as the first
74
+ * admin. Safe to call concurrently: re-reads inside before writing.
75
+ */
76
+ export async function ensureBootstrapAdmin(userId: string): Promise<void> {
77
+ if (adminEmailsFromEnv().length > 0) return; // env handles it
78
+
79
+ // Fast-path read to avoid the serialized write when an admin already exists.
80
+ const cfg = await readConfig();
81
+ if ((cfg.adminUserIds ?? []).length > 0) return;
82
+
83
+ let promoted = false;
84
+ // Atomic: re-check inside the lock so only one concurrent caller writes.
85
+ await updateConfig((fresh) => {
86
+ if ((fresh.adminUserIds ?? []).length > 0) return fresh;
87
+ promoted = true;
88
+ return { ...fresh, adminUserIds: [userId] };
89
+ });
90
+ if (promoted) {
91
+ console.log(`[wiki-viewer] Bootstrap: first user ${userId} promoted to admin`);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Use in route handlers.
97
+ * Returns { ok: true, user } or { ok: false, status, code } ready for NextResponse.
98
+ */
99
+ export async function requireAdmin(
100
+ req: Request,
101
+ ): Promise<
102
+ | { ok: true; user: { id: string; email: string; name: string } }
103
+ | { ok: false; status: number; code: string }
104
+ > {
105
+ const auth = await requireUser(req);
106
+ if (!auth.ok) return { ok: false, status: 401, code: "UNAUTHORIZED" };
107
+
108
+ const admin = await isAdmin(auth.user.id, auth.user.email);
109
+ if (!admin) return { ok: false, status: 403, code: "ADMIN_REQUIRED" };
110
+
111
+ return { ok: true, user: auth.user };
112
+ }
@@ -10,8 +10,19 @@ import path from "node:path";
10
10
  import os from "node:os";
11
11
  import { mkdirSync, readFileSync, writeFileSync, existsSync, chmodSync } from "node:fs";
12
12
  import { randomBytes } from "node:crypto";
13
+ import { AsyncLocalStorage } from "node:async_hooks";
13
14
  import { isEmailAllowed } from "./allowlist";
14
15
 
16
+ // When an admin creates a user, the signup allowlist must not apply (the admin
17
+ // is the gate). We mark that context with an AsyncLocalStorage flag so the
18
+ // user-create hook can skip the allowlist check for that one call only.
19
+ const adminCreateContext = new AsyncLocalStorage<boolean>();
20
+
21
+ /** Run `fn` with the signup allowlist bypassed (admin-initiated user creation). */
22
+ export function withAdminUserCreate<T>(fn: () => Promise<T>): Promise<T> {
23
+ return adminCreateContext.run(true, fn);
24
+ }
25
+
15
26
  // Guard runs at server startup, not during next build (NEXT_PHASE=phase-production-build).
16
27
  // Set WIKI_ALLOW_INSECURE=1 to bypass the https requirement (development / CI / smoke tests).
17
28
  if (
@@ -52,7 +63,7 @@ function resolveSecret(): string {
52
63
  return fresh;
53
64
  }
54
65
 
55
- const db = new Database(DB_PATH);
66
+ export const db = new Database(DB_PATH);
56
67
  db.pragma("journal_mode = WAL");
57
68
 
58
69
  function getTrustedOrigins(): string[] {
@@ -142,7 +153,7 @@ export const auth = betterAuth({
142
153
  user: {
143
154
  create: {
144
155
  before: async (user: { email: string }) => {
145
- if (!(await isEmailAllowed(user.email))) {
156
+ if (!adminCreateContext.getStore() && !(await isEmailAllowed(user.email))) {
146
157
  throw new Error("SIGNUP_NOT_ALLOWED");
147
158
  }
148
159
  return { data: user };
@@ -2,13 +2,30 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
 
5
- interface WikiViewerConfig {
5
+ // Workspace type is defined in workspaces.ts; we use a forward-compatible
6
+ // inline shape here to avoid a circular import between config ↔ workspaces.
7
+ export interface WorkspaceEntry {
8
+ id: string;
9
+ name: string;
10
+ rootDir: string;
11
+ createdAt: string;
12
+ lastOpenedAt?: string;
13
+ pinnedPaths?: string[];
14
+ createdBy?: string;
15
+ allowedUserIds?: string[];
16
+ }
17
+
18
+ export interface WikiViewerConfig {
6
19
  pinnedPaths?: string[];
7
20
  lastOpenedPath?: string;
8
21
  /** Email allowlist for signup. Empty/undefined = no email restriction. */
9
22
  allowedEmails?: string[];
10
23
  /** Domain allowlist for signup. Empty/undefined = no domain restriction. */
11
24
  allowedDomains?: string[];
25
+ /** Registered workspaces (replaces flat lastOpenedPath/pinnedPaths over time). */
26
+ workspaces?: WorkspaceEntry[];
27
+ /** User IDs with admin privileges. Empty = no admins yet (bootstrap on first request). */
28
+ adminUserIds?: string[];
12
29
  }
13
30
 
14
31
  function configPath() {
@@ -28,9 +45,44 @@ export async function readConfig(): Promise<WikiViewerConfig> {
28
45
  }
29
46
  }
30
47
 
31
- export async function writeConfig(patch: Partial<WikiViewerConfig>): Promise<void> {
48
+ // Serialize all writes so concurrent read-modify-write callers can't lose
49
+ // updates (workspace mutations, admin bootstrap all race on one file).
50
+ let _writeChain: Promise<unknown> = Promise.resolve();
51
+
52
+ function serialize<T>(fn: () => Promise<T>): Promise<T> {
53
+ const run = _writeChain.then(fn, fn);
54
+ // Keep the chain alive but swallow errors so one failure doesn't poison it.
55
+ _writeChain = run.then(
56
+ () => undefined,
57
+ () => undefined,
58
+ );
59
+ return run;
60
+ }
61
+
62
+ async function writeConfigUnsafe(next: WikiViewerConfig): Promise<void> {
32
63
  await ensureDir();
33
- const existing = await readConfig();
34
- const next = { ...existing, ...patch };
35
64
  await writeFile(configPath(), JSON.stringify(next, null, 2), "utf8");
36
65
  }
66
+
67
+ export async function writeConfig(patch: Partial<WikiViewerConfig>): Promise<void> {
68
+ return serialize(async () => {
69
+ const existing = await readConfig();
70
+ await writeConfigUnsafe({ ...existing, ...patch });
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Atomic read-modify-write. The mutator receives a fresh copy of the config
76
+ * (read inside the lock) and returns the next config. Serialized against all
77
+ * other writeConfig/updateConfig callers so updates are never lost.
78
+ */
79
+ export async function updateConfig(
80
+ mutator: (cfg: WikiViewerConfig) => WikiViewerConfig,
81
+ ): Promise<WikiViewerConfig> {
82
+ return serialize(async () => {
83
+ const existing = await readConfig();
84
+ const next = mutator({ ...existing });
85
+ await writeConfigUnsafe(next);
86
+ return next;
87
+ });
88
+ }
@@ -27,16 +27,23 @@ function getDb(): InstanceType<typeof Database> {
27
27
  _db.pragma("journal_mode = WAL");
28
28
  _db.exec(`
29
29
  CREATE TABLE IF NOT EXISTS agent_fs_audit (
30
- id INTEGER PRIMARY KEY AUTOINCREMENT,
31
- agent_id TEXT NOT NULL,
32
- op TEXT NOT NULL,
33
- path TEXT NOT NULL,
34
- old_sha TEXT,
35
- new_sha TEXT,
36
- forced INTEGER NOT NULL DEFAULT 0,
37
- at TEXT NOT NULL
30
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
31
+ agent_id TEXT NOT NULL,
32
+ op TEXT NOT NULL,
33
+ path TEXT NOT NULL,
34
+ old_sha TEXT,
35
+ new_sha TEXT,
36
+ forced INTEGER NOT NULL DEFAULT 0,
37
+ at TEXT NOT NULL,
38
+ workspace_id TEXT
38
39
  )
39
40
  `);
41
+ // Tolerant migration for existing DBs that lack the workspace_id column.
42
+ try {
43
+ _db.exec(`ALTER TABLE agent_fs_audit ADD COLUMN workspace_id TEXT`);
44
+ } catch {
45
+ // Column already exists — ignore.
46
+ }
40
47
  return _db;
41
48
  }
42
49
 
@@ -47,6 +54,7 @@ export interface AuditRow {
47
54
  oldSha?: string;
48
55
  newSha?: string;
49
56
  forced?: boolean;
57
+ workspaceId?: string;
50
58
  }
51
59
 
52
60
  /**
@@ -57,8 +65,8 @@ export function writeAuditRow(row: AuditRow): void {
57
65
  try {
58
66
  const db = getDb();
59
67
  db.prepare(
60
- `INSERT INTO agent_fs_audit (agent_id, op, path, old_sha, new_sha, forced, at)
61
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
68
+ `INSERT INTO agent_fs_audit (agent_id, op, path, old_sha, new_sha, forced, at, workspace_id)
69
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
62
70
  ).run(
63
71
  row.agentId,
64
72
  row.op,
@@ -67,6 +75,7 @@ export function writeAuditRow(row: AuditRow): void {
67
75
  row.newSha ?? null,
68
76
  row.forced ? 1 : 0,
69
77
  new Date().toISOString(),
78
+ row.workspaceId ?? null,
70
79
  );
71
80
  } catch (e) {
72
81
  console.error("[agent-fs audit] write failed:", e);
@@ -72,6 +72,8 @@ export async function checkAuth(req: Request): Promise<CheckAuthResult> {
72
72
  export interface ScopeParams {
73
73
  filePath?: string;
74
74
  op: "read" | "mutate" | "delete";
75
+ /** Id of the workspace this request resolved to. If the agent has a workspaceId grant and it differs, 403 is returned. */
76
+ workspaceId?: string;
75
77
  }
76
78
 
77
79
  export function enforceScope(
@@ -80,6 +82,15 @@ export function enforceScope(
80
82
  ): { ok: true } | { ok: false; code: "FORBIDDEN"; message: string } {
81
83
  const { scope } = agent;
82
84
 
85
+ // Workspace grant check: if agent is restricted to a workspace, enforce it.
86
+ if (scope.workspaceId !== undefined && params.workspaceId !== undefined && scope.workspaceId !== params.workspaceId) {
87
+ return {
88
+ ok: false,
89
+ code: "FORBIDDEN",
90
+ message: `Agent scope does not cover workspace "${params.workspaceId}" (granted: "${scope.workspaceId}")`,
91
+ };
92
+ }
93
+
83
94
  if (!scope.ops.includes(params.op)) {
84
95
  return { ok: false, code: "FORBIDDEN", message: `Agent scope does not allow "${params.op}"` };
85
96
  }
@@ -48,13 +48,15 @@ export async function computeCollabState(
48
48
  const snapshotUrl = `/api/agent/files/${relPath}`;
49
49
 
50
50
  // Compute revision components
51
+ // ns = rootDir: workspace-namespaces lease keys so two workspaces sharing
52
+ // the same relPath never collide in the lease store.
51
53
  const sidecar = await readSidecar(rootDir, relPath);
52
- const gen = leaseGeneration(relPath);
54
+ const gen = leaseGeneration(rootDir, relPath);
53
55
  const sidecarRevision = sidecar?.revision ?? 0;
54
56
  const revision = sidecarRevision + gen;
55
57
 
56
58
  // Lease check — do after reading sidecar to keep gen stable within this call
57
- const leaseActive = hasActiveLease(relPath);
59
+ const leaseActive = hasActiveLease(rootDir, relPath);
58
60
 
59
61
  if (!sidecar && !leaseActive) {
60
62
  return { state: "untracked", revision, snapshotUrl };
@@ -1,3 +1,8 @@
1
+ // Workspacing note (Phase B): the lockKey must be workspace-unique. Callers
2
+ // should prefix lockKey with rootDir (or wsId): `${rootDir}\0${relPath}`.
3
+ // The cross-process sentinel hash then differs per workspace automatically.
4
+ // Phase B call sites will make this change when route threading is done.
5
+
1
6
  /**
2
7
  * Cross-process file locking via proper-lockfile.
3
8
  *
@@ -1,3 +1,8 @@
1
+ // Workspacing note (Phase B): the idempotency key must be prefixed with wsId
2
+ // (or rootDir) at the call site to prevent cross-workspace collisions:
3
+ // e.g. key = `${rootDir}\0${idempotencyHeader}`. Phase B call sites will
4
+ // make this change when route threading is done.
5
+
1
6
  // Tiny in-memory LRU. Lives for process lifetime. Acceptable: idempotency
2
7
  // guards retries within seconds, not days.
3
8
  const MAX = 1000;
@@ -1,20 +1,25 @@
1
1
  /**
2
2
  * Human edit-lease store (in-memory, globalThis for hot-reload survival).
3
3
  *
4
+ * Namespace choice (workspacing): the first argument to every public function
5
+ * is `ns` — a namespace string that MUST be the workspace rootDir (or wsId in
6
+ * future phases). The internal map key is `${ns}\u0000${relPath}`. Two
7
+ * workspaces that share the same relative path (e.g. "notes.md") therefore
8
+ * never collide. Phase B routes pass rootDir from resolveWorkspaceForUser;
9
+ * Phase A callers pass getRootDir() or tmpRoot in tests.
10
+ *
4
11
  * A lease is set when a human opens a .md doc in the editor and refreshed
5
12
  * via heartbeat while the doc stays open. Its presence makes collab-state
6
- * "active" even before the human has typed a single suggestion (closes the
7
- * false-negative GPT-5.5 caught in the design review).
13
+ * "active" even before the human has typed a single suggestion.
8
14
  *
9
15
  * TTL: LEASE_TTL_MS (default 90 s). Editor should heartbeat every ~30 s.
10
16
  *
11
17
  * Collab-generation counter:
12
- * - Starts at 0 per path.
18
+ * - Starts at 0 per (ns, path).
13
19
  * - Increments on EVERY 0→1 transition (first setLease, or setLease after
14
20
  * all prior leases expired) AND on every 1→0 transition (clearLease or
15
21
  * expiry-on-read).
16
- * - X-Collab-Revision = sidecar.revision + leaseGeneration(path).
17
- * Any lease or sidecar state change therefore bumps the combined revision.
22
+ * - X-Collab-Revision = sidecar.revision + leaseGeneration(ns, path).
18
23
  */
19
24
 
20
25
  export const LEASE_TTL_MS = 90_000;
@@ -38,62 +43,70 @@ function store(): LeaseStore {
38
43
  return g.__wikiLeaseStore!;
39
44
  }
40
45
 
41
- /** True if relPath has a non-expired lease RIGHT NOW (lazy-sweeps on check). */
42
- function isLeaseActiveInternal(relPath: string): boolean {
46
+ function storeKey(ns: string, relPath: string): string {
47
+ return `${ns}\u0000${relPath}`;
48
+ }
49
+
50
+ /** True if (ns, relPath) has a non-expired lease RIGHT NOW (lazy-sweeps on check). */
51
+ function isLeaseActiveInternal(ns: string, relPath: string): boolean {
43
52
  const s = store();
44
- const entry = s.leases.get(relPath);
53
+ const key = storeKey(ns, relPath);
54
+ const entry = s.leases.get(key);
45
55
  if (!entry) return false;
46
56
  if (Date.now() >= entry.expiresAt) {
47
- s.leases.delete(relPath);
57
+ s.leases.delete(key);
48
58
  // 1→0 transition: bump generation
49
- s.generations.set(relPath, (s.generations.get(relPath) ?? 0) + 1);
59
+ s.generations.set(key, (s.generations.get(key) ?? 0) + 1);
50
60
  return false;
51
61
  }
52
62
  return true;
53
63
  }
54
64
 
55
65
  /**
56
- * Set (or refresh) a lease for relPath by userId.
66
+ * Set (or refresh) a lease for (ns, relPath) by userId.
57
67
  * Bumps the generation counter on a 0→1 transition.
68
+ * `ns` must be the workspace rootDir (or a stable workspace identifier).
58
69
  */
59
- export function setLease(relPath: string, userId: string, ttlMs = LEASE_TTL_MS): void {
70
+ export function setLease(ns: string, relPath: string, userId: string, ttlMs = LEASE_TTL_MS): void {
60
71
  const s = store();
61
- const wasActive = isLeaseActiveInternal(relPath);
62
- s.leases.set(relPath, { userId, expiresAt: Date.now() + ttlMs });
72
+ const key = storeKey(ns, relPath);
73
+ const wasActive = isLeaseActiveInternal(ns, relPath);
74
+ s.leases.set(key, { userId, expiresAt: Date.now() + ttlMs });
63
75
  if (!wasActive) {
64
76
  // 0→1 transition: bump generation
65
- s.generations.set(relPath, (s.generations.get(relPath) ?? 0) + 1);
77
+ s.generations.set(key, (s.generations.get(key) ?? 0) + 1);
66
78
  }
67
79
  }
68
80
 
69
- /** True if relPath has a current non-expired lease. */
70
- export function hasActiveLease(relPath: string): boolean {
71
- return isLeaseActiveInternal(relPath);
81
+ /** True if (ns, relPath) has a current non-expired lease. */
82
+ export function hasActiveLease(ns: string, relPath: string): boolean {
83
+ return isLeaseActiveInternal(ns, relPath);
72
84
  }
73
85
 
74
86
  /**
75
- * Clear the lease for relPath (on doc close).
87
+ * Clear the lease for (ns, relPath) (on doc close).
76
88
  * Only clears if the existing lease belongs to userId.
77
89
  * Bumps generation on removal.
78
90
  */
79
- export function clearLease(relPath: string, userId: string): void {
91
+ export function clearLease(ns: string, relPath: string, userId: string): void {
80
92
  const s = store();
81
- const entry = s.leases.get(relPath);
93
+ const key = storeKey(ns, relPath);
94
+ const entry = s.leases.get(key);
82
95
  if (!entry) return;
83
- // Still clear expired leases owned by the same user
84
96
  if (entry.userId === userId) {
85
- s.leases.delete(relPath);
97
+ s.leases.delete(key);
86
98
  // 1→0 transition: bump generation
87
- s.generations.set(relPath, (s.generations.get(relPath) ?? 0) + 1);
99
+ s.generations.set(key, (s.generations.get(key) ?? 0) + 1);
88
100
  }
89
101
  }
90
102
 
91
103
  /**
92
- * Returns the monotonic-ish collab generation for relPath.
104
+ * Returns the monotonic-ish collab generation for (ns, relPath).
93
105
  * Increments on every 0→1 and 1→0 lease transition.
94
106
  */
95
- export function leaseGeneration(relPath: string): number {
96
- return store().generations.get(relPath) ?? 0;
107
+ export function leaseGeneration(ns: string, relPath: string): number {
108
+ const key = storeKey(ns, relPath);
109
+ return store().generations.get(key) ?? 0;
97
110
  }
98
111
 
99
112
  /** Reset store — for tests only. */
@@ -1,3 +1,7 @@
1
+ // Workspacing note (Phase B): callers must prefix the lock key with rootDir
2
+ // (or wsId) to ensure two workspaces sharing the same relPath do not serialize
3
+ // against each other. e.g. key = `${rootDir}\0${relPath}`. Phase B call sites
4
+ // will make this change when route threading is done.
1
5
  import { withFileLock } from "./file-lock";
2
6
 
3
7
  const locks = new Map<string, Promise<void>>();