@jlongo78/agent-spaces 0.9.6 → 0.9.8

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 (917) hide show
  1. package/.next/standalone/.claude/settings.local.json +68 -0
  2. package/.next/standalone/.claude/spaces-env.json +1 -0
  3. package/.next/standalone/.next/BUILD_ID +1 -1
  4. package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
  5. package/.next/standalone/.next/build-manifest.json +2 -2
  6. package/.next/standalone/.next/prerender-manifest.json +3 -3
  7. package/.next/standalone/.next/required-server-files.json +19 -19
  8. package/.next/standalone/.next/routes-manifest.json +6 -0
  9. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
  12. package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
  24. package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  26. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  27. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  34. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  35. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  36. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  38. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  41. package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
  42. package/.next/standalone/.next/server/app/admin/analytics.rsc +2 -2
  43. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +1 -1
  44. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  46. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  47. package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +2 -2
  48. package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
  49. package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
  50. package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/admin/users.html +1 -1
  52. package/.next/standalone/.next/server/app/admin/users.rsc +2 -2
  53. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +1 -1
  54. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
  55. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  56. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  57. package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
  59. package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
  60. package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
  61. package/.next/standalone/.next/server/app/analytics.html +1 -1
  62. package/.next/standalone/.next/server/app/analytics.rsc +2 -2
  63. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +1 -1
  64. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
  65. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  66. package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +2 -2
  67. package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
  68. package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
  69. package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  70. package/.next/standalone/.next/server/app/api/analytics/overview/route.js +1 -1
  71. package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js +1 -1
  73. package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/benchmark/run/route.js +1 -1
  75. package/.next/standalone/.next/server/app/api/benchmark/run/route.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js +1 -1
  77. package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/api/benchmark/runs/route.js +1 -1
  79. package/.next/standalone/.next/server/app/api/benchmark/runs/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/benchmark/status/route.js +1 -1
  81. package/.next/standalone/.next/server/app/api/benchmark/status/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/bulk/route.js +1 -1
  83. package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/config/route.js +1 -1
  85. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/cortex/context/route.js +1 -1
  87. package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js +1 -1
  89. package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +1 -1
  91. package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -1
  92. package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +1 -1
  93. package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +1 -1
  95. package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -1
  96. package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +1 -1
  97. package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/api/cortex/export/route.js +1 -1
  99. package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js +1 -1
  101. package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
  102. package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js +1 -1
  103. package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js +1 -1
  105. package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
  106. package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js +1 -1
  107. package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js +1 -1
  109. package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
  110. package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js +1 -1
  111. package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js +1 -1
  113. package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js +1 -1
  115. package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/api/cortex/import/route.js +1 -1
  117. package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/api/cortex/import/status/route.js +1 -1
  119. package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js +1 -1
  121. package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js +1 -1
  123. package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
  124. package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js +1 -1
  125. package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
  126. package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js +1 -1
  127. package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
  128. package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js +1 -1
  129. package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/api/cortex/lobes/route.js +1 -1
  131. package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js +1 -1
  133. package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js +1 -1
  135. package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -1
  136. package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +1 -1
  137. package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js +1 -1
  139. package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js +1 -1
  141. package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/cortex/search/route.js +1 -1
  143. package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
  144. package/.next/standalone/.next/server/app/api/cortex/settings/route.js +1 -1
  145. package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
  146. package/.next/standalone/.next/server/app/api/cortex/status/route.js +1 -1
  147. package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
  148. package/.next/standalone/.next/server/app/api/cortex/timeline/route.js +1 -1
  149. package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
  150. package/.next/standalone/.next/server/app/api/cortex/usage/route.js +1 -1
  151. package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
  152. package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js +1 -1
  153. package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
  154. package/.next/standalone/.next/server/app/api/events/route.js +1 -1
  155. package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
  156. package/.next/standalone/.next/server/app/api/files/route.js +1 -1
  157. package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  158. package/.next/standalone/.next/server/app/api/folders/route.js +1 -1
  159. package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/api/network/handshake/route.js +1 -1
  161. package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
  162. package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js +1 -1
  163. package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js.nft.json +1 -1
  164. package/.next/standalone/.next/server/app/api/network/panes/route.js +1 -1
  165. package/.next/standalone/.next/server/app/api/network/panes/route.js.nft.json +1 -1
  166. package/.next/standalone/.next/server/app/api/network/projects/route.js +1 -1
  167. package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
  168. package/.next/standalone/.next/server/app/api/network/search/route.js +1 -1
  169. package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
  170. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js +1 -1
  171. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
  172. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js +1 -1
  173. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
  174. package/.next/standalone/.next/server/app/api/network/sessions/route.js +1 -1
  175. package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
  176. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js +1 -1
  177. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
  178. package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
  179. package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
  180. package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js +1 -1
  181. package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js.nft.json +1 -1
  182. package/.next/standalone/.next/server/app/api/panes/[id]/route.js +1 -1
  183. package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
  184. package/.next/standalone/.next/server/app/api/panes/route.js +1 -1
  185. package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
  186. package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
  187. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  188. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js +1 -1
  189. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js.nft.json +1 -1
  190. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js +1 -1
  191. package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js.nft.json +1 -1
  192. package/.next/standalone/.next/server/app/api/search/route.js +2 -2
  193. package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  194. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js +1 -1
  195. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
  196. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js +1 -1
  197. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
  198. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js +1 -1
  199. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  200. package/.next/standalone/.next/server/app/api/sessions/route.js +2 -2
  201. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  202. package/.next/standalone/.next/server/app/api/sync/route.js +1 -1
  203. package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
  204. package/.next/standalone/.next/server/app/api/tags/route.js +1 -1
  205. package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
  206. package/.next/standalone/.next/server/app/api/tier/route.js +1 -1
  207. package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
  208. package/.next/standalone/.next/server/app/api/whisper/config/route.js +1 -1
  209. package/.next/standalone/.next/server/app/api/whisper/config/route.js.nft.json +1 -1
  210. package/.next/standalone/.next/server/app/api/whisper/route.js.nft.json +1 -1
  211. package/.next/standalone/.next/server/app/api/wizard/chart/route/app-paths-manifest.json +3 -0
  212. package/.next/standalone/.next/server/app/api/wizard/chart/route/build-manifest.json +11 -0
  213. package/.next/standalone/.next/server/app/api/wizard/chart/route/server-reference-manifest.json +4 -0
  214. package/.next/standalone/.next/server/app/api/wizard/chart/route.js +7 -0
  215. package/.next/standalone/.next/server/app/api/wizard/chart/route.js.map +5 -0
  216. package/.next/standalone/.next/server/app/api/wizard/chart/route.js.nft.json +1 -0
  217. package/.next/standalone/.next/server/app/api/wizard/chart/route_client-reference-manifest.js +2 -0
  218. package/.next/standalone/.next/server/app/api/wizard/chat/route.js +1 -1
  219. package/.next/standalone/.next/server/app/api/wizard/chat/route.js.nft.json +1 -1
  220. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js +1 -1
  221. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
  222. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js +1 -1
  223. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
  224. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js +1 -1
  225. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
  226. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js +1 -1
  227. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
  228. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js +1 -1
  229. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
  230. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js +1 -1
  231. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
  232. package/.next/standalone/.next/server/app/api/workspaces/route.js +2 -2
  233. package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  234. package/.next/standalone/.next/server/app/cortex.html +1 -1
  235. package/.next/standalone/.next/server/app/cortex.rsc +3 -3
  236. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
  237. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
  238. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  239. package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +3 -3
  240. package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
  241. package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
  242. package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
  243. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  244. package/.next/standalone/.next/server/app/login.html +1 -1
  245. package/.next/standalone/.next/server/app/login.rsc +2 -2
  246. package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
  247. package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
  248. package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
  249. package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  250. package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  251. package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
  252. package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
  253. package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
  254. package/.next/standalone/.next/server/app/m/projects.html +1 -1
  255. package/.next/standalone/.next/server/app/m/projects.rsc +2 -2
  256. package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
  257. package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
  258. package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
  259. package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
  260. package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +1 -1
  261. package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
  262. package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +1 -1
  263. package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
  264. package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
  265. package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
  266. package/.next/standalone/.next/server/app/m/sessions.html +1 -1
  267. package/.next/standalone/.next/server/app/m/sessions.rsc +2 -2
  268. package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
  269. package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
  270. package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
  271. package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
  272. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +1 -1
  273. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
  274. package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +1 -1
  275. package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
  276. package/.next/standalone/.next/server/app/m/settings.html +1 -1
  277. package/.next/standalone/.next/server/app/m/settings.rsc +2 -2
  278. package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
  279. package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
  280. package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
  281. package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
  282. package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +1 -1
  283. package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
  284. package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +1 -1
  285. package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
  286. package/.next/standalone/.next/server/app/m/terminal.html +1 -1
  287. package/.next/standalone/.next/server/app/m/terminal.rsc +3 -3
  288. package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +3 -3
  289. package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
  290. package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
  291. package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
  292. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
  293. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
  294. package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +1 -1
  295. package/.next/standalone/.next/server/app/m.html +1 -1
  296. package/.next/standalone/.next/server/app/m.rsc +2 -2
  297. package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
  298. package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
  299. package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
  300. package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
  301. package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +1 -1
  302. package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +1 -1
  303. package/.next/standalone/.next/server/app/network.html +1 -1
  304. package/.next/standalone/.next/server/app/network.rsc +2 -2
  305. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +1 -1
  306. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
  307. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  308. package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +2 -2
  309. package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
  310. package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
  311. package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
  312. package/.next/standalone/.next/server/app/projects.html +1 -1
  313. package/.next/standalone/.next/server/app/projects.rsc +2 -2
  314. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +1 -1
  315. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
  316. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  317. package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +2 -2
  318. package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
  319. package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
  320. package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  321. package/.next/standalone/.next/server/app/sessions.html +1 -1
  322. package/.next/standalone/.next/server/app/sessions.rsc +2 -2
  323. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +1 -1
  324. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
  325. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  326. package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +2 -2
  327. package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
  328. package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
  329. package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
  330. package/.next/standalone/.next/server/app/settings.html +1 -1
  331. package/.next/standalone/.next/server/app/settings.rsc +2 -2
  332. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +1 -1
  333. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
  334. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  335. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  336. package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  337. package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  338. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  339. package/.next/standalone/.next/server/app/terminal.html +1 -1
  340. package/.next/standalone/.next/server/app/terminal.rsc +3 -3
  341. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
  342. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
  343. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  344. package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +3 -3
  345. package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
  346. package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
  347. package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
  348. package/.next/standalone/.next/server/app/vr/page/react-loadable-manifest.json +1 -1
  349. package/.next/standalone/.next/server/app/vr/page_client-reference-manifest.js +1 -1
  350. package/.next/standalone/.next/server/app/vr.html +1 -1
  351. package/.next/standalone/.next/server/app/vr.rsc +3 -3
  352. package/.next/standalone/.next/server/app/vr.segments/_full.segment.rsc +3 -3
  353. package/.next/standalone/.next/server/app/vr.segments/_head.segment.rsc +1 -1
  354. package/.next/standalone/.next/server/app/vr.segments/_index.segment.rsc +2 -2
  355. package/.next/standalone/.next/server/app/vr.segments/_tree.segment.rsc +2 -2
  356. package/.next/standalone/.next/server/app/vr.segments/vr/__PAGE__.segment.rsc +2 -2
  357. package/.next/standalone/.next/server/app/vr.segments/vr.segment.rsc +1 -1
  358. package/.next/standalone/.next/server/app/workspaces.html +1 -1
  359. package/.next/standalone/.next/server/app/workspaces.rsc +2 -2
  360. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +1 -1
  361. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
  362. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +1 -1
  363. package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +2 -2
  364. package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
  365. package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
  366. package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
  367. package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
  368. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00e90fc6._.js +98 -0
  369. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01ab8675._.js +98 -0
  370. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03974f05._.js +98 -0
  371. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bb331da9._.js → [root-of-the-server]__046c9b91._.js} +3 -3
  372. package/.next/standalone/.next/server/chunks/[root-of-the-server]__04ae6bf0._.js +98 -0
  373. package/.next/standalone/.next/server/chunks/[root-of-the-server]__056fa416._.js +1 -1
  374. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ac4ea3f._.js +3 -0
  375. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8e64cb._.js +98 -0
  376. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d95165f0._.js → [root-of-the-server]__0facd39e._.js} +3 -3
  377. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10bc76a3._.js +3 -0
  378. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__9d68157b._.js → [root-of-the-server]__115f3934._.js} +3 -3
  379. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11f155f1._.js +3 -0
  380. package/.next/standalone/.next/server/chunks/[root-of-the-server]__160e7c73._.js +22 -33
  381. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91e23c96._.js → [root-of-the-server]__17a3b966._.js} +3 -3
  382. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__277d9445._.js → [root-of-the-server]__17d3a2b2._.js} +3 -3
  383. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a86c055._.js +98 -0
  384. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20b5e9c4._.js +3 -0
  385. package/.next/standalone/.next/server/chunks/[root-of-the-server]__28d6fbd8._.js +98 -0
  386. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04f04898._.js → [root-of-the-server]__2a3f866b._.js} +2 -2
  387. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a95bb38b._.js → [root-of-the-server]__316617e7._.js} +2 -2
  388. package/.next/standalone/.next/server/chunks/[root-of-the-server]__32ad8f71._.js +98 -0
  389. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6fe5e6c8._.js → [root-of-the-server]__35457394._.js} +2 -2
  390. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1f054c65._.js → [root-of-the-server]__35de78e6._.js} +3 -3
  391. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3685ffcb._.js +98 -0
  392. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__614458b7._.js → [root-of-the-server]__38954988._.js} +3 -3
  393. package/.next/standalone/.next/server/chunks/[root-of-the-server]__426ad936._.js +106 -0
  394. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4985c034._.js +98 -0
  395. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c6ce9ed._.js +98 -0
  396. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5cebe58a._.js +98 -0
  397. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5d5e4789._.js +98 -0
  398. package/.next/standalone/.next/server/chunks/[root-of-the-server]__65676930._.js +3 -0
  399. package/.next/standalone/.next/server/chunks/[root-of-the-server]__67cab326._.js +58 -0
  400. package/.next/standalone/.next/server/chunks/[root-of-the-server]__698c6f01._.js +98 -0
  401. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c64af29._.js +131 -0
  402. package/.next/standalone/.next/server/chunks/[root-of-the-server]__73aed9f5._.js +98 -0
  403. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac84b704._.js → [root-of-the-server]__79b6a9bb._.js} +3 -3
  404. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__e56abacf._.js → [root-of-the-server]__7db704c6._.js} +4 -4
  405. package/.next/standalone/.next/server/chunks/[root-of-the-server]__812ca02b._.js +98 -0
  406. package/.next/standalone/.next/server/chunks/[root-of-the-server]__821f50fa._.js +98 -0
  407. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8716b86e._.js +98 -0
  408. package/.next/standalone/.next/server/chunks/[root-of-the-server]__884ef754._.js +98 -0
  409. package/.next/standalone/.next/server/chunks/[root-of-the-server]__88cdbd68._.js +98 -0
  410. package/.next/standalone/.next/server/chunks/[root-of-the-server]__89d9aba9._.js +98 -0
  411. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d536cb5._.js +98 -0
  412. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8df8c5d1._.js +98 -0
  413. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2d7a454e._.js → [root-of-the-server]__8f2ccc41._.js} +3 -3
  414. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a2c5fc5._.js → [root-of-the-server]__95c9d682._.js} +4 -4
  415. package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e5d7774._.js +98 -0
  416. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cc6e2885._.js → [root-of-the-server]__9edcff87._.js} +2 -2
  417. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a049dfc2._.js +98 -0
  418. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a5b4bb9a._.js +98 -0
  419. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__929ea03a._.js → [root-of-the-server]__a83262a1._.js} +2 -2
  420. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9cd1240._.js +98 -0
  421. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d7f822._.js +98 -0
  422. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad08c221._.js +98 -0
  423. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad585f2f._.js +98 -0
  424. package/.next/standalone/.next/server/chunks/[root-of-the-server]__afcb8f7d._.js +98 -0
  425. package/.next/standalone/.next/server/chunks/[root-of-the-server]__bc250d43._.js +98 -0
  426. package/.next/standalone/.next/server/chunks/[root-of-the-server]__bce2a6e7._.js +98 -0
  427. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c011bf91._.js +98 -0
  428. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0ac2895._.js +3 -0
  429. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c130a00c._.js +1 -1
  430. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c37d6380._.js +3 -0
  431. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cae392eb._.js +98 -0
  432. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cc2616bb._.js +3 -3
  433. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d501fa9b._.js +98 -0
  434. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d59c6c15._.js +98 -0
  435. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5c1db32._.js +98 -0
  436. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5d92527._.js +1 -1
  437. package/.next/standalone/.next/server/chunks/[root-of-the-server]__dba60c86._.js +98 -0
  438. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2384f98e._.js → [root-of-the-server]__de14b9ae._.js} +3 -3
  439. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e10643d1._.js +98 -0
  440. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__00fdfbda._.js → [root-of-the-server]__e2a996e5._.js} +2 -2
  441. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d903941._.js → [root-of-the-server]__e3477417._.js} +3 -3
  442. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__32dc5513._.js → [root-of-the-server]__e4db362e._.js} +2 -2
  443. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__49e42a3a._.js → [root-of-the-server]__e4e70b86._.js} +3 -3
  444. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac39ecc7._.js → [root-of-the-server]__e54925af._.js} +3 -3
  445. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e8edc5b0._.js +98 -0
  446. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eafd040b._.js → [root-of-the-server]__eab4d83b._.js} +2 -2
  447. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ead29015._.js +1 -1
  448. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__194955d4._.js → [root-of-the-server]__f056fd83._.js} +3 -3
  449. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f0e99572._.js +98 -0
  450. package/.next/standalone/.next/server/chunks/[root-of-the-server]__fe1e16d0._.js +98 -0
  451. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff9cd277._.js +98 -0
  452. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ffaea2ce._.js +98 -0
  453. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wizard_chart_route_actions_888e2ec1.js +3 -0
  454. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2cffc362._.js +3 -0
  455. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__f1050870._.js → [root-of-the-server]__47c97637._.js} +2 -2
  456. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
  457. package/.next/standalone/.next/server/chunks/ssr/_17b946fd._.js +3 -0
  458. package/.next/standalone/.next/server/chunks/ssr/_2a1d79e7._.js +1 -1
  459. package/.next/standalone/.next/server/chunks/ssr/_5c3c4cfa._.js +7 -5
  460. package/.next/standalone/.next/server/chunks/ssr/_ba432382._.js +7 -5
  461. package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_cortex_page_tsx_0f33d8b3._.js +1 -1
  462. package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +4 -4
  463. package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__90eeddae._.js +1 -1
  464. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  465. package/.next/standalone/.next/server/pages/404.html +1 -1
  466. package/.next/standalone/.next/server/pages/500.html +2 -2
  467. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  468. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  469. package/.next/standalone/.next/static/chunks/0852575eb90c1e8d.js +85 -0
  470. package/.next/standalone/.next/static/chunks/{74d0ac0b1d0a1b79.js → 10b00b4f66102dcf.js} +1 -1
  471. package/.next/standalone/.next/static/chunks/{16eb77953dee9ea3.js → 19c71a8376c23c58.js} +1 -1
  472. package/.next/standalone/.next/static/chunks/2b769d1597e4fc1c.css +3 -0
  473. package/.next/standalone/.next/static/chunks/{3e91fc608659c524.js → 350271fe79509caf.js} +1 -1
  474. package/.next/standalone/.next/static/chunks/{70423c7afd8abf5f.js → 597847c22200c212.js} +1 -1
  475. package/.next/standalone/.next/static/chunks/{fb0abd1933b2b2e1.js → 7f6a14f1849fa94d.js} +1 -1
  476. package/.next/standalone/.next/static/chunks/{7ecd9bbb0ce4d68a.js → b7c8fe9b7275a84f.js} +1 -1
  477. package/.next/standalone/.next/static/chunks/{6245135a7afb8c7b.js → c9b10fc55516d142.js} +8 -6
  478. package/.next/standalone/.next/static/chunks/d0065f48eab94944.js +1 -0
  479. package/.next/standalone/.next/static/chunks/{180c1b9ff31b979f.js → f7b34c807badf95d.js} +8 -6
  480. package/.next/standalone/.spaces/cortex-context.md +50 -144
  481. package/.next/standalone/LICENSE +661 -661
  482. package/.next/standalone/NOTICE +5 -5
  483. package/.next/standalone/README.md +131 -131
  484. package/.next/standalone/bin/cortex-hook.js +79 -79
  485. package/.next/standalone/bin/cortex-hook.sh +62 -62
  486. package/.next/standalone/bin/cortex-learn-hook.js +138 -138
  487. package/.next/standalone/bin/cortex-mcp.js +60 -60
  488. package/.next/standalone/bin/cortex-pi-extension.ts +170 -170
  489. package/.next/standalone/bin/fix-standalone-externals.js +79 -79
  490. package/.next/standalone/bin/lib/auto-setup.js +110 -110
  491. package/.next/standalone/bin/mdns-service.js +171 -171
  492. package/.next/standalone/bin/postinstall.js +35 -35
  493. package/.next/standalone/bin/setup-admin.js +195 -195
  494. package/.next/standalone/bin/spaces-dev.js +247 -247
  495. package/.next/standalone/bin/spaces-install.js +660 -660
  496. package/.next/standalone/bin/spaces-postinstall.js +50 -50
  497. package/.next/standalone/bin/spaces-reset-totp.js +50 -50
  498. package/.next/standalone/bin/spaces-service.js +1046 -1046
  499. package/.next/standalone/bin/spaces-setup.js +253 -253
  500. package/.next/standalone/bin/spaces.js +808 -805
  501. package/.next/standalone/bin/ssh-auth-keys.sh +68 -68
  502. package/.next/standalone/bin/terminal-server.js +2819 -2781
  503. package/.next/standalone/cortex-hook-debug.log +57 -23
  504. package/.next/standalone/docker-compose.yml +28 -28
  505. package/.next/standalone/docs/architecture.md +387 -387
  506. package/.next/standalone/docs/cortex-integration-reference.md +268 -268
  507. package/.next/standalone/docs/cortex.md +293 -293
  508. package/.next/standalone/docs/getting-started.md +96 -96
  509. package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-design.md +133 -133
  510. package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-plan.md +959 -959
  511. package/.next/standalone/docs/plans/2026-03-02-security-audit.md +229 -229
  512. package/.next/standalone/docs/plans/2026-03-07-service-command-design.md +146 -146
  513. package/.next/standalone/docs/plans/2026-03-07-service-command-plan.md +254 -254
  514. package/.next/standalone/docs/server-install.md +564 -564
  515. package/.next/standalone/docs/social-card.html +150 -150
  516. package/.next/standalone/docs/superpowers/plans/2026-03-12-spaces-cortex.md +5270 -5270
  517. package/.next/standalone/docs/superpowers/plans/2026-03-13-cortex-wiring.md +1387 -1387
  518. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-entity-graph.md +1923 -1923
  519. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-knowledge-evolution.md +1113 -1113
  520. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-boundary-engine.md +853 -853
  521. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-context-engine.md +1274 -1274
  522. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-signal-ingestion.md +933 -933
  523. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-lobes.md +1080 -1080
  524. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-gravity-system.md +768 -768
  525. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-ui.md +1108 -1108
  526. package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -1846
  527. package/.next/standalone/docs/superpowers/plans/2026-03-19-vr-phase1-shell.md +1639 -1639
  528. package/.next/standalone/docs/superpowers/plans/2026-03-27-dockview-pane-layout.md +98 -98
  529. package/.next/standalone/docs/superpowers/specs/2026-03-11-universe-view-design.md +320 -320
  530. package/.next/standalone/docs/superpowers/specs/2026-03-12-spaces-brain-design.md +720 -720
  531. package/.next/standalone/docs/superpowers/specs/2026-03-13-cortex-wiring-design.md +268 -268
  532. package/.next/standalone/docs/superpowers/specs/2026-03-14-cortex-v2-design.md +623 -623
  533. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-lobes-design.md +263 -263
  534. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-v2-ui-design.md +240 -240
  535. package/.next/standalone/docs/superpowers/specs/2026-03-16-pane-ux-design.md +77 -77
  536. package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -341
  537. package/.next/standalone/docs/superpowers/specs/2026-03-19-vr-phase1-shell-design.md +288 -288
  538. package/.next/standalone/docs/superpowers/specs/2026-03-27-pane-diff-review-and-project-wizard-design.md +322 -322
  539. package/.next/standalone/docs/tiers.md +104 -104
  540. package/.next/standalone/eslint.config.mjs +18 -18
  541. package/.next/standalone/next.config.ts +20 -20
  542. package/.next/standalone/nginx.conf +53 -53
  543. package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
  544. package/.next/standalone/node_modules/@img/{sharp-linux-x64 → sharp-win32-x64}/package.json +39 -46
  545. package/.next/standalone/package-lock.json +14985 -14986
  546. package/.next/standalone/package.json +111 -111
  547. package/.next/standalone/postcss.config.mjs +7 -7
  548. package/.next/standalone/scripts/rebuild.cmd +65 -65
  549. package/.next/standalone/scripts/rebuild.sh +59 -59
  550. package/.next/standalone/server.js +1 -1
  551. package/.next/standalone/src/app/(desktop)/admin/analytics/page.tsx +266 -266
  552. package/.next/standalone/src/app/(desktop)/admin/users/page.tsx +399 -399
  553. package/.next/standalone/src/app/(desktop)/analytics/page.tsx +166 -166
  554. package/.next/standalone/src/app/(desktop)/cortex/page.tsx +81 -81
  555. package/.next/standalone/src/app/(desktop)/dashboard-client.tsx +56 -56
  556. package/.next/standalone/src/app/(desktop)/layout.tsx +18 -18
  557. package/.next/standalone/src/app/(desktop)/network/page.tsx +137 -137
  558. package/.next/standalone/src/app/(desktop)/page.tsx +17 -17
  559. package/.next/standalone/src/app/(desktop)/projects/page.tsx +68 -68
  560. package/.next/standalone/src/app/(desktop)/sessions/[id]/page.tsx +519 -519
  561. package/.next/standalone/src/app/(desktop)/sessions/page.tsx +145 -145
  562. package/.next/standalone/src/app/(desktop)/settings/page.tsx +446 -446
  563. package/.next/standalone/src/app/(desktop)/terminal/layout.tsx +7 -7
  564. package/.next/standalone/src/app/(desktop)/terminal/page.tsx +1330 -1291
  565. package/.next/standalone/src/app/(desktop)/terminal/pane/[id]/page.tsx +211 -211
  566. package/.next/standalone/src/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.tsx +252 -252
  567. package/.next/standalone/src/app/(desktop)/workspaces/page.tsx +12 -12
  568. package/.next/standalone/src/app/api/admin/analytics/route.ts +10 -10
  569. package/.next/standalone/src/app/api/admin/users/[id]/route.ts +20 -20
  570. package/.next/standalone/src/app/api/admin/users/route.ts +15 -15
  571. package/.next/standalone/src/app/api/analytics/overview/route.ts +80 -80
  572. package/.next/standalone/src/app/api/auth/login/route.ts +10 -10
  573. package/.next/standalone/src/app/api/auth/logout/route.ts +9 -9
  574. package/.next/standalone/src/app/api/auth/me/route.ts +22 -22
  575. package/.next/standalone/src/app/api/auth/totp/setup/route.ts +10 -10
  576. package/.next/standalone/src/app/api/auth/totp/status/route.ts +10 -10
  577. package/.next/standalone/src/app/api/auth/totp/verify/route.ts +10 -10
  578. package/.next/standalone/src/app/api/benchmark/lobes/route.ts +16 -16
  579. package/.next/standalone/src/app/api/benchmark/run/route.ts +113 -92
  580. package/.next/standalone/src/app/api/benchmark/runs/[id]/route.ts +26 -26
  581. package/.next/standalone/src/app/api/benchmark/runs/route.ts +16 -16
  582. package/.next/standalone/src/app/api/benchmark/status/route.ts +35 -35
  583. package/.next/standalone/src/app/api/bulk/route.ts +34 -34
  584. package/.next/standalone/src/app/api/chat/route.ts +85 -85
  585. package/.next/standalone/src/app/api/config/route.ts +30 -30
  586. package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
  587. package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -27
  588. package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -23
  589. package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -23
  590. package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -29
  591. package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -23
  592. package/.next/standalone/src/app/api/cortex/export/route.ts +40 -40
  593. package/.next/standalone/src/app/api/cortex/federation/pending/route.ts +20 -20
  594. package/.next/standalone/src/app/api/cortex/federation/resolve/route.ts +43 -43
  595. package/.next/standalone/src/app/api/cortex/federation/search/route.ts +35 -35
  596. package/.next/standalone/src/app/api/cortex/federation/teach/route.ts +76 -76
  597. package/.next/standalone/src/app/api/cortex/graph/edges/route.ts +112 -112
  598. package/.next/standalone/src/app/api/cortex/graph/entities/[id]/route.ts +73 -73
  599. package/.next/standalone/src/app/api/cortex/graph/entities/route.ts +75 -75
  600. package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +203 -203
  601. package/.next/standalone/src/app/api/cortex/import/route.ts +75 -75
  602. package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
  603. package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +39 -39
  604. package/.next/standalone/src/app/api/cortex/ingest/status/route.ts +15 -15
  605. package/.next/standalone/src/app/api/cortex/knowledge/[id]/route.ts +91 -91
  606. package/.next/standalone/src/app/api/cortex/knowledge/route.ts +97 -97
  607. package/.next/standalone/src/app/api/cortex/lobes/[id]/route.ts +67 -67
  608. package/.next/standalone/src/app/api/cortex/lobes/route.ts +22 -22
  609. package/.next/standalone/src/app/api/cortex/lobes/share/route.ts +80 -80
  610. package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -43
  611. package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -46
  612. package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
  613. package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +8 -8
  614. package/.next/standalone/src/app/api/cortex/search/route.ts +57 -45
  615. package/.next/standalone/src/app/api/cortex/settings/route.ts +35 -35
  616. package/.next/standalone/src/app/api/cortex/status/route.ts +169 -169
  617. package/.next/standalone/src/app/api/cortex/timeline/route.ts +42 -42
  618. package/.next/standalone/src/app/api/cortex/usage/route.ts +31 -31
  619. package/.next/standalone/src/app/api/cortex/workspace/[id]/context/route.ts +41 -41
  620. package/.next/standalone/src/app/api/events/route.ts +40 -40
  621. package/.next/standalone/src/app/api/files/route.ts +187 -187
  622. package/.next/standalone/src/app/api/folders/route.ts +107 -97
  623. package/.next/standalone/src/app/api/network/connect-callback/route.ts +11 -11
  624. package/.next/standalone/src/app/api/network/connect-request/[id]/route.ts +11 -11
  625. package/.next/standalone/src/app/api/network/connect-request/route.ts +17 -17
  626. package/.next/standalone/src/app/api/network/discovered/route.ts +9 -9
  627. package/.next/standalone/src/app/api/network/handshake/route.ts +25 -25
  628. package/.next/standalone/src/app/api/network/health/route.ts +10 -10
  629. package/.next/standalone/src/app/api/network/identity/route.ts +15 -15
  630. package/.next/standalone/src/app/api/network/keys/[id]/route.ts +10 -10
  631. package/.next/standalone/src/app/api/network/keys/route.ts +15 -15
  632. package/.next/standalone/src/app/api/network/nodes/[id]/route.ts +15 -15
  633. package/.next/standalone/src/app/api/network/nodes/check/route.ts +9 -9
  634. package/.next/standalone/src/app/api/network/nodes/route.ts +15 -15
  635. package/.next/standalone/src/app/api/network/panes/[id]/route.ts +78 -62
  636. package/.next/standalone/src/app/api/network/panes/route.ts +61 -50
  637. package/.next/standalone/src/app/api/network/projects/route.ts +32 -25
  638. package/.next/standalone/src/app/api/network/proxy/[nodeId]/[...path]/route.ts +25 -25
  639. package/.next/standalone/src/app/api/network/search/route.ts +45 -38
  640. package/.next/standalone/src/app/api/network/sessions/[id]/messages/route.ts +43 -36
  641. package/.next/standalone/src/app/api/network/sessions/[id]/route.ts +41 -34
  642. package/.next/standalone/src/app/api/network/sessions/route.ts +50 -43
  643. package/.next/standalone/src/app/api/network/terminal/token/route.ts +10 -10
  644. package/.next/standalone/src/app/api/network/workspaces/[id]/route.ts +80 -71
  645. package/.next/standalone/src/app/api/network/workspaces/route.ts +87 -85
  646. package/.next/standalone/src/app/api/panes/[id]/diff/route.ts +121 -121
  647. package/.next/standalone/src/app/api/panes/[id]/route.ts +60 -60
  648. package/.next/standalone/src/app/api/panes/route.ts +39 -39
  649. package/.next/standalone/src/app/api/projects/route.ts +13 -13
  650. package/.next/standalone/src/app/api/proxy/models/[modelId]/[...path]/route.ts +80 -80
  651. package/.next/standalone/src/app/api/proxy/models/[modelId]/status/route.ts +33 -33
  652. package/.next/standalone/src/app/api/search/route.ts +47 -47
  653. package/.next/standalone/src/app/api/sessions/[id]/chat/route.ts +120 -120
  654. package/.next/standalone/src/app/api/sessions/[id]/messages/route.ts +34 -34
  655. package/.next/standalone/src/app/api/sessions/[id]/route.ts +73 -73
  656. package/.next/standalone/src/app/api/sessions/route.ts +64 -64
  657. package/.next/standalone/src/app/api/sync/route.ts +24 -24
  658. package/.next/standalone/src/app/api/tags/route.ts +35 -35
  659. package/.next/standalone/src/app/api/tier/route.ts +16 -16
  660. package/.next/standalone/src/app/api/updates/route.ts +65 -65
  661. package/.next/standalone/src/app/api/whisper/config/route.ts +50 -42
  662. package/.next/standalone/src/app/api/whisper/route.ts +91 -91
  663. package/.next/standalone/src/app/api/wizard/chart/route.ts +129 -0
  664. package/.next/standalone/src/app/api/wizard/chat/route.ts +113 -113
  665. package/.next/standalone/src/app/api/workspaces/[id]/context/[key]/route.ts +39 -39
  666. package/.next/standalone/src/app/api/workspaces/[id]/context/route.ts +28 -28
  667. package/.next/standalone/src/app/api/workspaces/[id]/messages/[msgId]/route.ts +17 -17
  668. package/.next/standalone/src/app/api/workspaces/[id]/messages/route.ts +39 -39
  669. package/.next/standalone/src/app/api/workspaces/[id]/route.ts +47 -47
  670. package/.next/standalone/src/app/api/workspaces/[id]/sessions/route.ts +62 -62
  671. package/.next/standalone/src/app/api/workspaces/route.ts +79 -79
  672. package/.next/standalone/src/app/globals.css +88 -88
  673. package/.next/standalone/src/app/layout.tsx +33 -33
  674. package/.next/standalone/src/app/login/layout.tsx +7 -7
  675. package/.next/standalone/src/app/login/page.tsx +315 -315
  676. package/.next/standalone/src/app/m/layout.tsx +16 -16
  677. package/.next/standalone/src/app/m/page.tsx +118 -118
  678. package/.next/standalone/src/app/m/projects/page.tsx +64 -64
  679. package/.next/standalone/src/app/m/sessions/[id]/page.tsx +168 -168
  680. package/.next/standalone/src/app/m/sessions/page.tsx +177 -177
  681. package/.next/standalone/src/app/m/settings/page.tsx +230 -230
  682. package/.next/standalone/src/app/m/terminal/page.tsx +413 -413
  683. package/.next/standalone/src/app/vr/page.tsx +21 -21
  684. package/.next/standalone/src/app/vr/vr-app.tsx +163 -163
  685. package/.next/standalone/src/app/vr/vr-controls.tsx +139 -139
  686. package/.next/standalone/src/app/vr/vr-door.tsx +82 -82
  687. package/.next/standalone/src/app/vr/vr-environment.tsx +71 -71
  688. package/.next/standalone/src/app/vr/vr-gaze.tsx +89 -89
  689. package/.next/standalone/src/app/vr/vr-layout.ts +49 -49
  690. package/.next/standalone/src/app/vr/vr-lobby.tsx +97 -97
  691. package/.next/standalone/src/app/vr/vr-pane.tsx +195 -195
  692. package/.next/standalone/src/app/vr/vr-room.tsx +79 -79
  693. package/.next/standalone/src/app/vr/vr-terminal.tsx +303 -303
  694. package/.next/standalone/src/components/auth/totp-gate.tsx +183 -183
  695. package/.next/standalone/src/components/bus/activity-panel.tsx +261 -261
  696. package/.next/standalone/src/components/common/color-picker.tsx +35 -35
  697. package/.next/standalone/src/components/common/dev-directory-picker.tsx +339 -339
  698. package/.next/standalone/src/components/common/folder-picker.tsx +200 -200
  699. package/.next/standalone/src/components/common/tag-picker.tsx +190 -190
  700. package/.next/standalone/src/components/common/workspace-picker.tsx +113 -113
  701. package/.next/standalone/src/components/cortex/benchmark-tab.tsx +894 -880
  702. package/.next/standalone/src/components/cortex/constants.ts +29 -29
  703. package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -304
  704. package/.next/standalone/src/components/cortex/cortex-indicator.tsx +44 -44
  705. package/.next/standalone/src/components/cortex/cortex-panel.tsx +140 -140
  706. package/.next/standalone/src/components/cortex/cortex-settings.tsx +280 -280
  707. package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -810
  708. package/.next/standalone/src/components/cortex/entity-detail.tsx +101 -101
  709. package/.next/standalone/src/components/cortex/entity-graph.tsx +382 -382
  710. package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -212
  711. package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
  712. package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -109
  713. package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -158
  714. package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -215
  715. package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -126
  716. package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -113
  717. package/.next/standalone/src/components/dashboard/activity-chart.tsx +41 -41
  718. package/.next/standalone/src/components/dashboard/model-usage-chart.tsx +61 -61
  719. package/.next/standalone/src/components/dashboard/recent-sessions.tsx +68 -68
  720. package/.next/standalone/src/components/dashboard/stats-cards.tsx +36 -36
  721. package/.next/standalone/src/components/files/file-explorer.tsx +703 -703
  722. package/.next/standalone/src/components/layout/providers.tsx +38 -38
  723. package/.next/standalone/src/components/layout/sidebar.tsx +170 -170
  724. package/.next/standalone/src/components/layout/tier-provider.tsx +53 -53
  725. package/.next/standalone/src/components/layout/update-banner.tsx +92 -92
  726. package/.next/standalone/src/components/mobile/bottom-nav.tsx +46 -46
  727. package/.next/standalone/src/components/mobile/immersive-voice-button.tsx +123 -123
  728. package/.next/standalone/src/components/mobile/mobile-chat-input.tsx +244 -244
  729. package/.next/standalone/src/components/mobile/mobile-header.tsx +44 -44
  730. package/.next/standalone/src/components/mobile/mobile-session-card.tsx +56 -56
  731. package/.next/standalone/src/components/mobile/mobile-terminal-input.tsx +74 -74
  732. package/.next/standalone/src/components/mobile/mobile-terminal-pane.tsx +302 -302
  733. package/.next/standalone/src/components/mobile/mobile-terminal-toolbar.tsx +76 -76
  734. package/.next/standalone/src/components/mobile/pull-to-refresh.tsx +82 -82
  735. package/.next/standalone/src/components/mobile/voice-input.tsx +53 -53
  736. package/.next/standalone/src/components/network/api-key-list.tsx +190 -190
  737. package/.next/standalone/src/components/network/connection-requests.tsx +94 -94
  738. package/.next/standalone/src/components/network/node-add-dialog.tsx +131 -131
  739. package/.next/standalone/src/components/network/node-badge.tsx +26 -26
  740. package/.next/standalone/src/components/network/node-list.tsx +207 -207
  741. package/.next/standalone/src/components/network/node-selector.tsx +49 -49
  742. package/.next/standalone/src/components/sessions/session-filters.tsx +116 -116
  743. package/.next/standalone/src/components/sessions/session-list.tsx +485 -485
  744. package/.next/standalone/src/components/terminal/pane-diff-panel.tsx +179 -179
  745. package/.next/standalone/src/components/terminal/terminal-pane.tsx +1530 -1464
  746. package/.next/standalone/src/components/viewer/chat-input.tsx +275 -275
  747. package/.next/standalone/src/components/viewer/message-renderer.tsx +551 -551
  748. package/.next/standalone/src/components/wizard/chart-wizard.tsx +405 -0
  749. package/.next/standalone/src/components/wizard/project-wizard.tsx +153 -153
  750. package/.next/standalone/src/components/wizard/wizard-chat.tsx +99 -99
  751. package/.next/standalone/src/components/wizard/wizard-plan-summary.tsx +103 -103
  752. package/.next/standalone/src/components/wizard/wizard-review.tsx +225 -225
  753. package/.next/standalone/src/components/workspace/universe-cluster.tsx +131 -131
  754. package/.next/standalone/src/components/workspace/universe-orb.tsx +128 -128
  755. package/.next/standalone/src/components/workspace/universe-types.ts +22 -22
  756. package/.next/standalone/src/components/workspace/universe-utils.ts +11 -11
  757. package/.next/standalone/src/components/workspace/universe-view.tsx +397 -397
  758. package/.next/standalone/src/components/workspace/workspace-chooser.tsx +634 -634
  759. package/.next/standalone/src/hooks/use-benchmark.ts +72 -71
  760. package/.next/standalone/src/hooks/use-bus.ts +147 -147
  761. package/.next/standalone/src/hooks/use-idle-detection.ts +79 -79
  762. package/.next/standalone/src/hooks/use-network.ts +229 -229
  763. package/.next/standalone/src/hooks/use-sessions.ts +437 -437
  764. package/.next/standalone/src/hooks/use-speech-recognition.ts +114 -113
  765. package/.next/standalone/src/hooks/use-sse.ts +35 -35
  766. package/.next/standalone/src/hooks/use-tier.ts +39 -39
  767. package/.next/standalone/src/lib/agents.ts +97 -97
  768. package/.next/standalone/src/lib/aider/parser.ts +111 -111
  769. package/.next/standalone/src/lib/api.ts +19 -19
  770. package/.next/standalone/src/lib/auth.ts +47 -47
  771. package/.next/standalone/src/lib/claude/parser.ts +212 -212
  772. package/.next/standalone/src/lib/claude/stats.ts +204 -204
  773. package/.next/standalone/src/lib/codex/parser.test.ts +111 -111
  774. package/.next/standalone/src/lib/codex/parser.ts +287 -287
  775. package/.next/standalone/src/lib/config.ts +132 -132
  776. package/.next/standalone/src/lib/cortex/benchmark.ts +83 -67
  777. package/.next/standalone/src/lib/cortex/config.ts +42 -42
  778. package/.next/standalone/src/lib/cortex/debug.ts +10 -10
  779. package/.next/standalone/src/lib/cortex/distillation/usage-store.ts +18 -18
  780. package/.next/standalone/src/lib/cortex/graph/resolver.ts +10 -10
  781. package/.next/standalone/src/lib/cortex/graph/types.ts +22 -22
  782. package/.next/standalone/src/lib/cortex/index.ts +109 -56
  783. package/.next/standalone/src/lib/cortex/ingestion/bootstrap.ts +14 -14
  784. package/.next/standalone/src/lib/cortex/knowledge/compat.ts +14 -14
  785. package/.next/standalone/src/lib/cortex/knowledge/contradiction.ts +10 -10
  786. package/.next/standalone/src/lib/cortex/knowledge/types.ts +67 -67
  787. package/.next/standalone/src/lib/cortex/lobes/config.ts +16 -16
  788. package/.next/standalone/src/lib/cortex/lobes/resolver.ts +8 -8
  789. package/.next/standalone/src/lib/cortex/lobes/shares.ts +14 -14
  790. package/.next/standalone/src/lib/cortex/mcp/server.ts +12 -12
  791. package/.next/standalone/src/lib/cortex/portability/exporter.ts +6 -6
  792. package/.next/standalone/src/lib/cortex/portability/importer.ts +10 -10
  793. package/.next/standalone/src/lib/cortex/retrieval/context-engine.ts +10 -10
  794. package/.next/standalone/src/lib/cortex/types.ts +39 -39
  795. package/.next/standalone/src/lib/cost-calculator.ts +48 -48
  796. package/.next/standalone/src/lib/db/init.ts +71 -71
  797. package/.next/standalone/src/lib/db/queries.ts +740 -827
  798. package/.next/standalone/src/lib/db/schema.ts +206 -206
  799. package/.next/standalone/src/lib/events/sse.ts +36 -36
  800. package/.next/standalone/src/lib/forge/parser.ts +52 -52
  801. package/.next/standalone/src/lib/gemini/parser.ts +258 -258
  802. package/.next/standalone/src/lib/license.ts +56 -56
  803. package/.next/standalone/src/lib/pro.ts +31 -31
  804. package/.next/standalone/src/lib/shell-user.ts +101 -0
  805. package/.next/standalone/src/lib/sync/indexer.ts +504 -504
  806. package/.next/standalone/src/lib/sync/watcher.ts +64 -64
  807. package/.next/standalone/src/lib/teams.ts +31 -31
  808. package/.next/standalone/src/lib/telemetry.ts +75 -75
  809. package/.next/standalone/src/lib/terminal/server.ts +188 -188
  810. package/.next/standalone/src/lib/tier.ts +38 -38
  811. package/.next/standalone/src/lib/utils.ts +72 -72
  812. package/.next/standalone/src/lib/vms/manager.ts +121 -121
  813. package/.next/standalone/src/middleware.ts +133 -133
  814. package/.next/standalone/src/types/claude.ts +208 -208
  815. package/.next/standalone/src/types/network.ts +61 -61
  816. package/.next/standalone/tests/setup.ts +8 -8
  817. package/.next/standalone/tsconfig.json +34 -34
  818. package/.next/standalone/vitest.config.ts +24 -24
  819. package/LICENSE +661 -661
  820. package/README.md +131 -131
  821. package/bin/cortex-hook.js +79 -79
  822. package/bin/cortex-hook.sh +62 -62
  823. package/bin/cortex-learn-hook.js +138 -138
  824. package/bin/cortex-mcp.js +60 -60
  825. package/bin/cortex-pi-extension.ts +170 -170
  826. package/bin/fix-standalone-externals.js +79 -79
  827. package/bin/lib/auto-setup.js +110 -110
  828. package/bin/mdns-service.js +171 -171
  829. package/bin/postinstall.js +35 -35
  830. package/bin/setup-admin.js +195 -195
  831. package/bin/spaces-dev.js +247 -247
  832. package/bin/spaces-install.js +660 -660
  833. package/bin/spaces-postinstall.js +50 -50
  834. package/bin/spaces-reset-totp.js +50 -50
  835. package/bin/spaces-service.js +1046 -1046
  836. package/bin/spaces-setup.js +253 -253
  837. package/bin/spaces.js +808 -805
  838. package/bin/ssh-auth-keys.sh +68 -68
  839. package/bin/terminal-server.js +2819 -2781
  840. package/package.json +111 -111
  841. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0903a426._.js +0 -98
  842. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09e8ccc9._.js +0 -98
  843. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11c684b1._.js +0 -98
  844. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1572d4ef._.js +0 -98
  845. package/.next/standalone/.next/server/chunks/[root-of-the-server]__186cd0bb._.js +0 -3
  846. package/.next/standalone/.next/server/chunks/[root-of-the-server]__212760e6._.js +0 -98
  847. package/.next/standalone/.next/server/chunks/[root-of-the-server]__228595ec._.js +0 -98
  848. package/.next/standalone/.next/server/chunks/[root-of-the-server]__283c890f._.js +0 -3
  849. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f300a68._.js +0 -98
  850. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f452778._.js +0 -98
  851. package/.next/standalone/.next/server/chunks/[root-of-the-server]__35f8e77e._.js +0 -98
  852. package/.next/standalone/.next/server/chunks/[root-of-the-server]__379fc2e9._.js +0 -98
  853. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3b40d79f._.js +0 -98
  854. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3d3dca2b._.js +0 -98
  855. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4d5b78d2._.js +0 -98
  856. package/.next/standalone/.next/server/chunks/[root-of-the-server]__54163e52._.js +0 -98
  857. package/.next/standalone/.next/server/chunks/[root-of-the-server]__563c0817._.js +0 -3
  858. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5812f90a._.js +0 -98
  859. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c5e87f5._.js +0 -98
  860. package/.next/standalone/.next/server/chunks/[root-of-the-server]__60d15b16._.js +0 -98
  861. package/.next/standalone/.next/server/chunks/[root-of-the-server]__69d315e5._.js +0 -3
  862. package/.next/standalone/.next/server/chunks/[root-of-the-server]__71f29038._.js +0 -98
  863. package/.next/standalone/.next/server/chunks/[root-of-the-server]__74084e07._.js +0 -3
  864. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7921aa80._.js +0 -98
  865. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e077dd8._.js +0 -98
  866. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7ebc4280._.js +0 -131
  867. package/.next/standalone/.next/server/chunks/[root-of-the-server]__857c60bb._.js +0 -98
  868. package/.next/standalone/.next/server/chunks/[root-of-the-server]__874fe565._.js +0 -98
  869. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8e2171f7._.js +0 -98
  870. package/.next/standalone/.next/server/chunks/[root-of-the-server]__95659b2d._.js +0 -98
  871. package/.next/standalone/.next/server/chunks/[root-of-the-server]__9679b91e._.js +0 -98
  872. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a90729a1._.js +0 -98
  873. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad4346fa._.js +0 -98
  874. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b0862d69._.js +0 -98
  875. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b43306ee._.js +0 -98
  876. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b689ff5e._.js +0 -106
  877. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ba87daaa._.js +0 -98
  878. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0461005._.js +0 -98
  879. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c1deb5f3._.js +0 -98
  880. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c8a62f42._.js +0 -98
  881. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cabaac2b._.js +0 -98
  882. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cb027619._.js +0 -98
  883. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf608218._.js +0 -98
  884. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cfc1290d._.js +0 -98
  885. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0109b9b._.js +0 -98
  886. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0125483._.js +0 -3
  887. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d048ee6b._.js +0 -98
  888. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d12644e7._.js +0 -98
  889. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e3cc946c._.js +0 -98
  890. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e6fd27f8._.js +0 -98
  891. package/.next/standalone/.next/server/chunks/[root-of-the-server]__efb8251e._.js +0 -98
  892. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f44c6882._.js +0 -98
  893. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f85283de._.js +0 -98
  894. package/.next/standalone/.next/server/chunks/[root-of-the-server]__feceb3e4._.js +0 -98
  895. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__843070a6._.js +0 -3
  896. package/.next/standalone/.next/server/chunks/ssr/_e84a0c06._.js +0 -3
  897. package/.next/standalone/.next/static/chunks/470cade58d4eceeb.css +0 -3
  898. package/.next/standalone/.next/static/chunks/9d4164833c2c1fd6.js +0 -85
  899. package/.next/standalone/.next/static/chunks/f091f4bf8d80fd07.js +0 -1
  900. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +0 -46
  901. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  902. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
  903. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  904. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +0 -42
  905. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
  906. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  907. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
  908. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  909. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  910. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
  911. package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  912. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
  913. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  914. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_buildManifest.js +0 -0
  915. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_clientMiddlewareManifest.json +0 -0
  916. /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_ssgManifest.js +0 -0
  917. /package/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-win32-x64}/versions.json +0 -0
@@ -1,1046 +1,1046 @@
1
- #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- const { execFileSync, spawnSync } = require('child_process');
6
- const path = require('path');
7
- const os = require('os');
8
- const fs = require('fs');
9
- const readline = require('readline');
10
-
11
- let SPACES_DIR = path.join(os.homedir(), '.spaces');
12
- let CONFIG_PATH = path.join(SPACES_DIR, 'server.json');
13
- let LOGS_DIR = path.join(SPACES_DIR, 'logs');
14
- const SERVICE_NAME = 'spaces';
15
- const LABEL = 'com.agentspaces.spaces';
16
- const TASK_NAME = 'Spaces';
17
-
18
- // Override SPACES_DIR to point to a different user's home
19
- function setTargetHome(homedir) {
20
- SPACES_DIR = path.join(homedir, '.spaces');
21
- CONFIG_PATH = path.join(SPACES_DIR, 'server.json');
22
- LOGS_DIR = path.join(SPACES_DIR, 'logs');
23
- }
24
-
25
- // ─── Helpers ──────────────────────────────────────────────────
26
- function log(msg) { console.log(` ${msg}`); }
27
- function logOk(msg) { console.log(` ✓ ${msg}`); }
28
- function logErr(msg) { console.error(` ✗ ${msg}`); }
29
-
30
- function resolveConfig() {
31
- let config = {};
32
- if (fs.existsSync(CONFIG_PATH)) {
33
- try { config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); } catch {}
34
- }
35
- return {
36
- port: config.port || 3457,
37
- tier: config.tier || 'community',
38
- basePath: config.basePath || '',
39
- allowedOrigins: config.allowedOrigins || '',
40
- };
41
- }
42
-
43
- function resolveSpacesPath() {
44
- // Prefer the globally-installed package so upgrades via `npm i -g` take
45
- // effect without needing to re-run `spaces service install`.
46
- try {
47
- const globalRoot = execFileSync('npm', ['root', '-g'], { encoding: 'utf-8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
48
- const globalSpaces = path.join(globalRoot, '@jlongo78', 'agent-spaces', 'bin', 'spaces.js');
49
- if (fs.existsSync(globalSpaces)) return globalSpaces;
50
- } catch { /* fall through */ }
51
- return path.join(__dirname, 'spaces.js');
52
- }
53
-
54
- function resolveProjectDir() {
55
- return path.join(__dirname, '..');
56
- }
57
-
58
- function resolveNodePath() {
59
- return process.execPath;
60
- }
61
-
62
- function promptLevel() {
63
- return new Promise((resolve) => {
64
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
65
- console.log('');
66
- log('Install as:');
67
- log(' 1. System service (starts on boot, all users)');
68
- log(' 2. User service (starts on login, current user only)');
69
- console.log('');
70
- rl.question(' Choice [2]: ', (answer) => {
71
- rl.close();
72
- const choice = answer.trim() || '2';
73
- resolve(choice === '1' ? 'system' : 'user');
74
- });
75
- });
76
- }
77
-
78
- function ensureLogsDir() {
79
- fs.mkdirSync(LOGS_DIR, { recursive: true });
80
- }
81
-
82
- // Detect Windows user profiles that have .claude/ (Claude Code data)
83
- function findClaudeUsers() {
84
- if (process.platform !== 'win32') return [];
85
- const usersDir = path.dirname(os.homedir());
86
- const skip = new Set(['Public', 'Default', 'Default User', 'All Users']);
87
- try {
88
- return fs.readdirSync(usersDir)
89
- .filter(name => !skip.has(name) && !name.startsWith('.'))
90
- .filter(name => {
91
- const claudeDir = path.join(usersDir, name, '.claude');
92
- return fs.existsSync(claudeDir);
93
- })
94
- .map(name => ({ name, homedir: path.join(usersDir, name) }));
95
- } catch { return []; }
96
- }
97
-
98
- // Prompt for which user's home directory to target (system service only).
99
- // Looks for users with .claude/ (Claude Code data) since that's what gets synced.
100
- function promptTargetUser() {
101
- return new Promise((resolve) => {
102
- const currentHome = os.homedir();
103
- const users = findClaudeUsers();
104
-
105
- // No Claude users found — fall back to current user
106
- if (users.length === 0) {
107
- resolve(currentHome);
108
- return;
109
- }
110
- // Single Claude user — auto-select
111
- if (users.length === 1) {
112
- log(`Using home directory: ${users[0].homedir}`);
113
- resolve(users[0].homedir);
114
- return;
115
- }
116
-
117
- // Multiple Claude users — ask which one
118
- console.log('');
119
- log('Multiple users have Claude Code data:');
120
- users.forEach((u, i) => log(` ${i + 1}. ${u.name} (${u.homedir})`));
121
- console.log('');
122
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
123
- rl.question(` Run as user [1]: `, (answer) => {
124
- rl.close();
125
- const idx = parseInt(answer.trim() || '1', 10) - 1;
126
- const chosen = users[idx] || users[0];
127
- resolve(chosen.homedir);
128
- });
129
- });
130
- }
131
-
132
-
133
- // --- SSH AuthorizedKeysCommand setup (Linux/macOS) ---
134
- // Configures sshd to dynamically authorize Spaces users via a lookup script,
135
- // eliminating the need to manually manage ~/.ssh/authorized_keys per user.
136
- function configureAuthorizedKeysCommand() {
137
- if (process.platform === 'win32') return; // Windows uses a different approach
138
-
139
- const scriptSrc = path.join(__dirname, 'ssh-auth-keys.sh');
140
- const scriptDst = '/opt/spaces/bin/ssh-auth-keys.sh';
141
- const sshdConfig = '/etc/ssh/sshd_config';
142
- const spacesConfDrop = '/etc/ssh/sshd_config.d/spaces.conf';
143
-
144
- // Install the script to a stable system path
145
- try {
146
- spawnSync('sudo', ['mkdir', '-p', '/opt/spaces/bin'], { stdio: 'pipe', timeout: 5000 });
147
- spawnSync('sudo', ['cp', scriptSrc, scriptDst], { stdio: 'pipe', timeout: 5000 });
148
- spawnSync('sudo', ['chmod', '755', scriptDst], { stdio: 'pipe', timeout: 5000 });
149
- spawnSync('sudo', ['chown', 'root:root', scriptDst], { stdio: 'pipe', timeout: 5000 });
150
- logOk('Installed ssh-auth-keys.sh to ' + scriptDst);
151
- } catch (e) {
152
- logErr('Failed to install ssh-auth-keys.sh: ' + e.message);
153
- return;
154
- }
155
-
156
- // Prefer sshd_config.d drop-in if the Include directive exists
157
- const sshdContent = fs.existsSync(sshdConfig)
158
- ? fs.readFileSync(sshdConfig, 'utf-8') : '';
159
-
160
- const configLines = [
161
- '# Spaces: dynamically authorize shell users via admin DB lookup',
162
- 'AuthorizedKeysCommand ' + scriptDst + ' %u',
163
- 'AuthorizedKeysCommandUser nobody',
164
- ].join('\n') + '\n';
165
-
166
- // Already configured?
167
- if (sshdContent.includes('ssh-auth-keys.sh')) {
168
- logOk('AuthorizedKeysCommand already configured in sshd_config');
169
- return;
170
- }
171
-
172
- // Try drop-in directory first (cleaner, doesn't touch main config)
173
- const hasIncludeDir = sshdContent.includes('Include /etc/ssh/sshd_config.d/');
174
- if (hasIncludeDir) {
175
- try {
176
- spawnSync('sudo', ['mkdir', '-p', '/etc/ssh/sshd_config.d'], { stdio: 'pipe', timeout: 5000 });
177
- const result = spawnSync('sudo', ['tee', spacesConfDrop], {
178
- input: configLines, stdio: ['pipe', 'pipe', 'inherit'], timeout: 5000,
179
- });
180
- if (result.status === 0) {
181
- logOk('Created ' + spacesConfDrop);
182
- }
183
- } catch (e) {
184
- logErr('Failed to write drop-in config: ' + e.message);
185
- }
186
- } else {
187
- // Append directly to sshd_config
188
- try {
189
- const appendContent = '\n# Spaces SSH authorization\n' + configLines;
190
- const result = spawnSync('sudo', ['tee', '-a', sshdConfig], {
191
- input: appendContent, stdio: ['pipe', 'pipe', 'inherit'], timeout: 5000,
192
- });
193
- if (result.status === 0) {
194
- logOk('Added AuthorizedKeysCommand to ' + sshdConfig);
195
- }
196
- } catch (e) {
197
- logErr('Failed to update sshd_config: ' + e.message);
198
- }
199
- }
200
-
201
- // Restart sshd to pick up the change
202
- try {
203
- spawnSync('sudo', ['systemctl', 'restart', 'sshd'], { stdio: 'pipe', timeout: 10000 });
204
- logOk('Restarted sshd');
205
- } catch {
206
- try {
207
- spawnSync('sudo', ['systemctl', 'restart', 'ssh'], { stdio: 'pipe', timeout: 10000 });
208
- logOk('Restarted ssh');
209
- } catch {
210
- log('Warning: could not restart sshd — restart it manually');
211
- }
212
- }
213
- }
214
-
215
- // --- SSH key provisioning (for multi-user system service) ---
216
- function checkOpenSSHServer() {
217
- if (process.platform !== 'win32') return true;
218
- try {
219
- const result = spawnSync('powershell', ['-NoProfile', '-Command',
220
- 'Get-Service sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status'
221
- ], { encoding: 'utf-8', timeout: 10000 });
222
- const status = (result.stdout || '').trim();
223
- return status === 'Running' || status === 'Stopped';
224
- } catch { return false; }
225
- }
226
-
227
- function ensureOpenSSHServer() {
228
- if (process.platform !== 'win32') return;
229
- if (checkOpenSSHServer()) {
230
- try {
231
- spawnSync('powershell', ['-NoProfile', '-Command',
232
- 'Start-Service sshd; Set-Service -Name sshd -StartupType Automatic'
233
- ], { stdio: 'pipe', timeout: 15000 });
234
- logOk('OpenSSH Server started and set to automatic');
235
- } catch {
236
- log('Warning: could not start OpenSSH Server');
237
- }
238
- return;
239
- }
240
- log('Installing OpenSSH Server (required for multi-user terminals)...');
241
- try {
242
- const result = spawnSync('powershell', ['-NoProfile', '-Command',
243
- 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0'
244
- ], { encoding: 'utf-8', stdio: 'pipe', timeout: 120000 });
245
- if (result.status === 0) {
246
- logOk('OpenSSH Server installed');
247
- spawnSync('powershell', ['-NoProfile', '-Command',
248
- 'Start-Service sshd; Set-Service -Name sshd -StartupType Automatic'
249
- ], { stdio: 'pipe', timeout: 15000 });
250
- logOk('OpenSSH Server started');
251
- } else {
252
- logErr('Failed to install OpenSSH Server: ' + (result.stderr || '').trim());
253
- log('Multi-user terminals will not work. Install manually:');
254
- log(' Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0');
255
- }
256
- } catch (e) {
257
- logErr('Failed to install OpenSSH Server: ' + e.message);
258
- }
259
- }
260
-
261
- function ensureServiceKey() {
262
- const keyPath = path.join(SPACES_DIR, 'service_key');
263
- if (fs.existsSync(keyPath)) return keyPath;
264
- log('Generating SSH service key...');
265
- fs.mkdirSync(SPACES_DIR, { recursive: true });
266
- const result = spawnSync('ssh-keygen', [
267
- '-t', 'ed25519',
268
- '-f', keyPath,
269
- '-N', '',
270
- '-C', 'spaces-service-key',
271
- ], { stdio: 'pipe', timeout: 10000 });
272
- if (result.status !== 0) {
273
- logErr('Failed to generate SSH key');
274
- return null;
275
- }
276
-
277
- // On Windows, restrict private key permissions so OpenSSH accepts it.
278
- // OpenSSH requires: no inherited ACLs, only the file owner + SYSTEM may have access.
279
- if (process.platform === 'win32') {
280
- try {
281
- const currentUser = os.userInfo().username;
282
- // Remove inheritance and all default ACLs, then grant only owner + SYSTEM
283
- spawnSync('icacls', [keyPath, '/inheritance:r',
284
- '/remove', 'BUILTIN\\Administrators',
285
- '/remove', 'BUILTIN\\Users',
286
- '/remove', 'Everyone',
287
- '/grant:r', currentUser + ':(F)',
288
- '/grant', 'NT AUTHORITY\\SYSTEM:(F)'], { stdio: 'pipe', timeout: 5000 });
289
- } catch {}
290
- }
291
-
292
- logOk('SSH service key generated');
293
- return keyPath;
294
- }
295
-
296
- function authorizeServiceKey(keyPath, targetUser) {
297
- const pubKey = fs.readFileSync(keyPath + '.pub', 'utf-8').trim();
298
-
299
- if (process.platform === 'win32') {
300
- // Windows OpenSSH ignores ~/.ssh/authorized_keys for admin users.
301
- // Must use C:\ProgramData\ssh\administrators_authorized_keys instead.
302
- const adminAuthKeys = path.join(process.env.ProgramData || 'C:\\ProgramData', 'ssh', 'administrators_authorized_keys');
303
- const userAuthKeys = path.join(path.dirname(os.homedir()), targetUser, '.ssh', 'authorized_keys');
304
-
305
- // Check if user is an administrator (exact match, not substring)
306
- let isAdmin = false;
307
- try {
308
- const result = spawnSync('net', ['localgroup', 'Administrators'], { encoding: 'utf-8', timeout: 5000 });
309
- const lines = (result.stdout || '').split(/\r?\n/);
310
- const sep = lines.findIndex(l => l.trim().startsWith('---'));
311
- if (sep >= 0) {
312
- const members = lines.slice(sep + 1).map(l => l.trim()).filter(Boolean);
313
- isAdmin = members.some(m => m.toLowerCase() === targetUser.toLowerCase());
314
- }
315
- } catch {}
316
-
317
- const authKeysPath = isAdmin ? adminAuthKeys : userAuthKeys;
318
- const authDir = path.dirname(authKeysPath);
319
- if (!fs.existsSync(authDir)) fs.mkdirSync(authDir, { recursive: true });
320
-
321
- // Check if key already authorized
322
- if (fs.existsSync(authKeysPath)) {
323
- const existing = fs.readFileSync(authKeysPath, 'utf-8');
324
- if (existing.includes(pubKey)) return;
325
- }
326
- fs.appendFileSync(authKeysPath, pubKey + String.fromCharCode(10));
327
-
328
- // Fix permissions for administrators_authorized_keys
329
- if (isAdmin) {
330
- try {
331
- spawnSync('icacls', [authKeysPath, '/inheritance:r', '/grant', 'SYSTEM:(R)', '/grant', 'Administrators:(R)'], { stdio: 'pipe', timeout: 5000 });
332
- } catch {}
333
- }
334
- } else {
335
- // Linux/macOS: use ~/.ssh/authorized_keys
336
- let userHome;
337
- try {
338
- // Use getent to resolve the actual home directory (works with LDAP, NIS, etc.)
339
- const result = spawnSync('getent', ['passwd', targetUser], { encoding: 'utf-8', timeout: 5000 });
340
- const fields = (result.stdout || '').split(':');
341
- userHome = fields[5] || (process.platform === 'darwin' ? `/Users/${targetUser}` : `/home/${targetUser}`);
342
- } catch {
343
- userHome = process.platform === 'darwin' ? `/Users/${targetUser}` : `/home/${targetUser}`;
344
- }
345
- const sshDir = path.join(userHome, '.ssh');
346
- const authKeysPath = path.join(sshDir, 'authorized_keys');
347
- if (!fs.existsSync(sshDir)) fs.mkdirSync(sshDir, { recursive: true, mode: 0o700 });
348
- if (fs.existsSync(authKeysPath)) {
349
- const existing = fs.readFileSync(authKeysPath, 'utf-8');
350
- if (existing.includes(pubKey)) return;
351
- }
352
- fs.appendFileSync(authKeysPath, pubKey + String.fromCharCode(10));
353
- }
354
- logOk('SSH key authorized for ' + targetUser);
355
- }
356
-
357
- const LEVEL_PATH = path.join(SPACES_DIR, 'service-level');
358
-
359
- function saveLevel(level) {
360
- fs.mkdirSync(SPACES_DIR, { recursive: true });
361
- fs.writeFileSync(LEVEL_PATH, level);
362
- }
363
-
364
- function loadLevel() {
365
- try { return fs.readFileSync(LEVEL_PATH, 'utf-8').trim(); } catch { return null; }
366
- }
367
-
368
- // ─── Platform detection ───────────────────────────────────────
369
- function getPlatform() {
370
- switch (process.platform) {
371
- case 'linux': return 'linux';
372
- case 'darwin': return 'darwin';
373
- case 'win32': return 'win32';
374
- default:
375
- logErr(`Unsupported platform: ${process.platform}`);
376
- process.exit(1);
377
- }
378
- }
379
-
380
- // ─── Linux (systemd) ─────────────────────────────────────────
381
- function linuxServicePath(level) {
382
- if (level === 'system') {
383
- return `/etc/systemd/system/${SERVICE_NAME}.service`;
384
- }
385
- const userDir = path.join(os.homedir(), '.config', 'systemd', 'user');
386
- fs.mkdirSync(userDir, { recursive: true });
387
- return path.join(userDir, `${SERVICE_NAME}.service`);
388
- }
389
-
390
- function linuxUnitFile(level) {
391
- const config = resolveConfig();
392
- const nodePath = resolveNodePath();
393
- const spacesPath = resolveSpacesPath();
394
- const projectDir = resolveProjectDir();
395
-
396
- let envLines = [
397
- `Environment=SPACES_SERVICE=1`,
398
- `Environment=SPACES_PORT=${config.port}`,
399
- ];
400
- // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
401
- if (config.tier && config.tier !== 'community') {
402
- envLines.push(`Environment=SPACES_TIER=${config.tier}`);
403
- }
404
- if (config.basePath) {
405
- envLines.push(`Environment=SPACES_BASE_PATH=${config.basePath}`);
406
- }
407
- if (config.allowedOrigins) {
408
- envLines.push(`Environment=SPACES_ALLOWED_ORIGINS=${config.allowedOrigins}`);
409
- }
410
-
411
- // Allow users to set API keys (OPENAI_API_KEY, etc.) in ~/.spaces/env
412
- const envFilePath = path.join(SPACES_DIR, 'env');
413
-
414
- let serviceSection = [
415
- 'Type=simple',
416
- `ExecStart=${nodePath} ${spacesPath}`,
417
- `WorkingDirectory=${projectDir}`,
418
- `EnvironmentFile=-${envFilePath}`,
419
- ...envLines,
420
- 'Restart=on-failure',
421
- 'RestartSec=5',
422
- ];
423
-
424
- if (level === 'system') {
425
- const username = os.userInfo().username;
426
- serviceSection.push(`User=${username}`);
427
- serviceSection.push(`Group=${username}`);
428
- }
429
-
430
- const wantedBy = level === 'system' ? 'multi-user.target' : 'default.target';
431
-
432
- return [
433
- '[Unit]',
434
- 'Description=Spaces - Agent Workspace Manager',
435
- 'After=network.target',
436
- '',
437
- '[Service]',
438
- ...serviceSection,
439
- '',
440
- '[Install]',
441
- `WantedBy=${wantedBy}`,
442
- '',
443
- ].join('\n');
444
- }
445
-
446
- function linuxSystemctl(level, ...args) {
447
- if (level === 'system') {
448
- execFileSync('sudo', ['systemctl', ...args], { stdio: 'inherit' });
449
- } else {
450
- // User-level systemctl needs XDG_RUNTIME_DIR to connect to the user bus.
451
- // Always set it from the real UID — inherited env may have a stale value
452
- // (e.g. from a prior session with a different UID mapping).
453
- const uid = process.getuid();
454
- const env = {
455
- ...process.env,
456
- XDG_RUNTIME_DIR: `/run/user/${uid}`,
457
- DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
458
- };
459
- execFileSync('systemctl', ['--user', ...args], { stdio: 'inherit', env });
460
- }
461
- }
462
-
463
- async function linuxInstall() {
464
- const level = await promptLevel();
465
- const unitContent = linuxUnitFile(level);
466
- const servicePath = linuxServicePath(level);
467
-
468
- log(`Writing unit file to ${servicePath}`);
469
- if (level === 'system') {
470
- const result = spawnSync('sudo', ['tee', servicePath], {
471
- input: unitContent,
472
- stdio: ['pipe', 'pipe', 'inherit'],
473
- });
474
- if (result.status !== 0) {
475
- logErr('Failed to write unit file');
476
- process.exit(1);
477
- }
478
- } else {
479
- fs.writeFileSync(servicePath, unitContent);
480
- }
481
- logOk('Unit file written');
482
-
483
- saveLevel(level);
484
-
485
- linuxSystemctl(level, 'daemon-reload');
486
- logOk('Reloaded systemd daemon');
487
-
488
- linuxSystemctl(level, 'enable', `${SERVICE_NAME}.service`);
489
- logOk('Service enabled');
490
-
491
- linuxSystemctl(level, 'start', `${SERVICE_NAME}.service`);
492
- logOk('Service started');
493
-
494
- if (level === 'user') {
495
- try {
496
- const uid = process.getuid();
497
- const env = {
498
- ...process.env,
499
- XDG_RUNTIME_DIR: `/run/user/${uid}`,
500
- DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
501
- };
502
- execFileSync('loginctl', ['enable-linger', os.userInfo().username], { stdio: 'inherit', env });
503
- logOk('Enabled login lingering for user service');
504
- } catch {
505
- log('Warning: could not enable-linger (user service may not start on boot)');
506
- }
507
- }
508
-
509
- // Set up SSH AuthorizedKeysCommand for seamless multi-user terminal access
510
- if (level === 'system') {
511
- configureAuthorizedKeysCommand();
512
- const keyPath = ensureServiceKey();
513
- if (keyPath) {
514
- authorizeServiceKey(keyPath, os.userInfo().username);
515
- }
516
- }
517
-
518
- logOk(`Spaces installed as ${level} service`);
519
- }
520
-
521
- async function linuxUninstall() {
522
- const level = loadLevel() || 'user';
523
- const servicePath = linuxServicePath(level);
524
-
525
- try {
526
- linuxSystemctl(level, 'stop', `${SERVICE_NAME}.service`);
527
- logOk('Service stopped');
528
- } catch {
529
- log('Service was not running');
530
- }
531
-
532
- try {
533
- linuxSystemctl(level, 'disable', `${SERVICE_NAME}.service`);
534
- logOk('Service disabled');
535
- } catch {
536
- log('Service was not enabled');
537
- }
538
-
539
- try {
540
- if (level === 'system') {
541
- execFileSync('sudo', ['rm', '-f', servicePath], { stdio: 'inherit' });
542
- } else {
543
- fs.unlinkSync(servicePath);
544
- }
545
- logOk('Unit file removed');
546
- } catch {
547
- log('Unit file was already removed');
548
- }
549
-
550
- try {
551
- linuxSystemctl(level, 'daemon-reload');
552
- logOk('Reloaded systemd daemon');
553
- } catch {
554
- log('Warning: daemon-reload failed');
555
- }
556
-
557
- try {
558
- fs.unlinkSync(LEVEL_PATH);
559
- } catch {}
560
-
561
- logOk('Spaces service uninstalled');
562
- }
563
-
564
- async function linuxStart() {
565
- const level = loadLevel() || 'user';
566
- // Regenerate unit file so it picks up the latest installed version
567
- const unitContent = linuxUnitFile(level);
568
- const unitPath = linuxServicePath(level);
569
- fs.writeFileSync(unitPath, unitContent);
570
- linuxSystemctl(level, 'daemon-reload');
571
- // Clear stale update-check cache
572
- try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
573
- linuxSystemctl(level, 'start', `${SERVICE_NAME}.service`);
574
- logOk('Service started');
575
- }
576
-
577
- async function linuxStop() {
578
- const level = loadLevel() || 'user';
579
- linuxSystemctl(level, 'stop', `${SERVICE_NAME}.service`);
580
- logOk('Service stopped');
581
- }
582
-
583
- async function linuxStatus() {
584
- const level = loadLevel() || 'user';
585
- try {
586
- linuxSystemctl(level, 'status', `${SERVICE_NAME}.service`);
587
- } catch {
588
- // systemctl status returns non-zero for stopped/failed services
589
- }
590
- }
591
-
592
- async function linuxLogs() {
593
- const level = loadLevel() || 'user';
594
- if (level === 'user') {
595
- const uid = process.getuid();
596
- const env = {
597
- ...process.env,
598
- XDG_RUNTIME_DIR: `/run/user/${uid}`,
599
- DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
600
- };
601
- spawnSync('journalctl', ['--user', '-u', SERVICE_NAME, '-f', '--no-pager'], { stdio: 'inherit', env });
602
- } else {
603
- spawnSync('sudo', ['journalctl', '-u', SERVICE_NAME, '-f', '--no-pager'], { stdio: 'inherit' });
604
- }
605
- }
606
-
607
- // ─── macOS (launchd) ─────────────────────────────────────────
608
- function darwinPlistPath(level) {
609
- if (level === 'system') {
610
- return `/Library/LaunchDaemons/${LABEL}.plist`;
611
- }
612
- return path.join(os.homedir(), 'Library', 'LaunchAgents', `${LABEL}.plist`);
613
- }
614
-
615
- function darwinPlistContent(level) {
616
- ensureLogsDir();
617
- const config = resolveConfig();
618
- const nodePath = resolveNodePath();
619
- const spacesPath = resolveSpacesPath();
620
- const projectDir = resolveProjectDir();
621
- const outLog = path.join(LOGS_DIR, 'spaces.out.log');
622
- const errLog = path.join(LOGS_DIR, 'spaces.err.log');
623
-
624
- let envEntries = [
625
- ` <key>SPACES_SERVICE</key>`,
626
- ` <string>1</string>`,
627
- ` <key>SPACES_PORT</key>`,
628
- ` <string>${config.port}</string>`,
629
- ];
630
- // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
631
- if (config.tier && config.tier !== 'community') {
632
- envEntries.push(` <key>SPACES_TIER</key>`);
633
- envEntries.push(` <string>${config.tier}</string>`);
634
- }
635
- if (config.basePath) {
636
- envEntries.push(` <key>SPACES_BASE_PATH</key>`);
637
- envEntries.push(` <string>${config.basePath}</string>`);
638
- }
639
- if (config.allowedOrigins) {
640
- envEntries.push(` <key>SPACES_ALLOWED_ORIGINS</key>`);
641
- envEntries.push(` <string>${config.allowedOrigins}</string>`);
642
- }
643
-
644
- let extraKeys = '';
645
- if (level === 'system') {
646
- extraKeys = ` <key>UserName</key>\n <string>${os.userInfo().username}</string>\n`;
647
- }
648
-
649
- return [
650
- '<?xml version="1.0" encoding="UTF-8"?>',
651
- '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
652
- '<plist version="1.0">',
653
- '<dict>',
654
- ` <key>Label</key>`,
655
- ` <string>${LABEL}</string>`,
656
- ` <key>ProgramArguments</key>`,
657
- ` <array>`,
658
- ` <string>${nodePath}</string>`,
659
- ` <string>${spacesPath}</string>`,
660
- ` </array>`,
661
- ` <key>WorkingDirectory</key>`,
662
- ` <string>${projectDir}</string>`,
663
- ` <key>EnvironmentVariables</key>`,
664
- ` <dict>`,
665
- ...envEntries,
666
- ` </dict>`,
667
- ` <key>RunAtLoad</key>`,
668
- ` <true/>`,
669
- ` <key>KeepAlive</key>`,
670
- ` <true/>`,
671
- ` <key>StandardOutPath</key>`,
672
- ` <string>${outLog}</string>`,
673
- ` <key>StandardErrorPath</key>`,
674
- ` <string>${errLog}</string>`,
675
- extraKeys ? extraKeys.trimEnd() : null,
676
- '</dict>',
677
- '</plist>',
678
- '',
679
- ].filter((line) => line !== null).join('\n');
680
- }
681
-
682
- async function darwinInstall() {
683
- const level = await promptLevel();
684
- const plistPath = darwinPlistPath(level);
685
- const plistContent = darwinPlistContent(level);
686
-
687
- // Unload existing (ignore errors)
688
- try {
689
- execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'pipe' });
690
- } catch {}
691
-
692
- log(`Writing plist to ${plistPath}`);
693
- if (level === 'system') {
694
- const result = spawnSync('sudo', ['tee', plistPath], {
695
- input: plistContent,
696
- stdio: ['pipe', 'pipe', 'inherit'],
697
- });
698
- if (result.status !== 0) {
699
- logErr('Failed to write plist file');
700
- process.exit(1);
701
- }
702
- execFileSync('sudo', ['chown', 'root:wheel', plistPath], { stdio: 'inherit' });
703
- execFileSync('sudo', ['chmod', '644', plistPath], { stdio: 'inherit' });
704
- } else {
705
- const agentsDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
706
- fs.mkdirSync(agentsDir, { recursive: true });
707
- fs.writeFileSync(plistPath, plistContent);
708
- }
709
- logOk('Plist file written');
710
-
711
- saveLevel(level);
712
-
713
- if (level === 'system') {
714
- execFileSync('sudo', ['launchctl', 'load', '-w', plistPath], { stdio: 'inherit' });
715
- } else {
716
- execFileSync('launchctl', ['load', '-w', plistPath], { stdio: 'inherit' });
717
- }
718
- logOk('Service loaded');
719
-
720
- // Set up SSH AuthorizedKeysCommand for seamless multi-user terminal access
721
- if (level === 'system') {
722
- configureAuthorizedKeysCommand();
723
- const keyPath = ensureServiceKey();
724
- if (keyPath) {
725
- authorizeServiceKey(keyPath, os.userInfo().username);
726
- }
727
- }
728
-
729
- logOk(`Spaces installed as ${level} service`);
730
- }
731
-
732
- async function darwinUninstall() {
733
- const level = loadLevel() || 'user';
734
- const plistPath = darwinPlistPath(level);
735
-
736
- try {
737
- if (level === 'system') {
738
- execFileSync('sudo', ['launchctl', 'unload', '-w', plistPath], { stdio: 'inherit' });
739
- } else {
740
- execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'inherit' });
741
- }
742
- logOk('Service unloaded');
743
- } catch {
744
- log('Service was not loaded');
745
- }
746
-
747
- try {
748
- if (level === 'system') {
749
- execFileSync('sudo', ['rm', '-f', plistPath], { stdio: 'inherit' });
750
- } else {
751
- fs.unlinkSync(plistPath);
752
- }
753
- logOk('Plist file removed');
754
- } catch {
755
- log('Plist file was already removed');
756
- }
757
-
758
- try {
759
- fs.unlinkSync(LEVEL_PATH);
760
- } catch {}
761
-
762
- logOk('Spaces service uninstalled');
763
- }
764
-
765
- async function darwinStart() {
766
- const level = loadLevel() || 'user';
767
- const plistPath = darwinPlistPath(level);
768
- // Regenerate plist so it picks up the latest installed version
769
- const plistContent = darwinPlistContent(level);
770
- fs.writeFileSync(plistPath, plistContent);
771
- // Clear stale update-check cache
772
- try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
773
- if (level === 'system') {
774
- execFileSync('sudo', ['launchctl', 'load', '-w', plistPath], { stdio: 'inherit' });
775
- } else {
776
- execFileSync('launchctl', ['load', '-w', plistPath], { stdio: 'inherit' });
777
- }
778
- logOk('Service started');
779
- }
780
-
781
- async function darwinStop() {
782
- const level = loadLevel() || 'user';
783
- const plistPath = darwinPlistPath(level);
784
- if (level === 'system') {
785
- execFileSync('sudo', ['launchctl', 'unload', '-w', plistPath], { stdio: 'inherit' });
786
- } else {
787
- execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'inherit' });
788
- }
789
- logOk('Service stopped');
790
- }
791
-
792
- async function darwinStatus() {
793
- try {
794
- const result = execFileSync('launchctl', ['list'], { encoding: 'utf-8' });
795
- const lines = result.split('\n').filter((line) => line.includes(LABEL));
796
- if (lines.length > 0) {
797
- log('Spaces service status:');
798
- lines.forEach((line) => log(line));
799
- } else {
800
- log('Spaces service is not loaded');
801
- }
802
- } catch {
803
- log('Spaces service is not loaded');
804
- }
805
- }
806
-
807
- async function darwinLogs() {
808
- ensureLogsDir();
809
- const outLogPath = path.join(LOGS_DIR, 'spaces.out.log');
810
- if (!fs.existsSync(outLogPath)) {
811
- logErr(`Log file not found: ${outLogPath}`);
812
- log('Service may not have started yet');
813
- process.exit(1);
814
- }
815
- spawnSync('tail', ['-f', outLogPath], { stdio: 'inherit' });
816
- }
817
-
818
- // ─── Windows (Task Scheduler) ────────────────────────────────
819
- function win32WrapperScript(level) {
820
- ensureLogsDir();
821
- const config = resolveConfig();
822
- const nodePath = resolveNodePath();
823
- const spacesPath = resolveSpacesPath();
824
- const outLog = path.join(LOGS_DIR, 'spaces.out.log');
825
- const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
826
-
827
- const lines = [
828
- '@echo off',
829
- ];
830
- // When running as SYSTEM, override USERPROFILE so os.homedir() resolves
831
- // to the target user's home directory (where .spaces/ config lives).
832
- if (level === 'system') {
833
- const homedir = path.dirname(SPACES_DIR); // use resolved target, not os.homedir()
834
- const drive = path.parse(homedir).root.slice(0, -1);
835
- const rest = homedir.slice(drive.length);
836
- lines.push(`set USERPROFILE=${homedir}`);
837
- lines.push(`set HOMEDRIVE=${drive}`);
838
- lines.push(`set HOMEPATH=${rest}`);
839
- }
840
- lines.push('set SPACES_SERVICE=1');
841
- lines.push(`set SPACES_PORT=${config.port}`);
842
- // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
843
- if (config.tier && config.tier !== 'community') {
844
- lines.push(`set SPACES_TIER=${config.tier}`);
845
- }
846
- if (config.basePath) {
847
- lines.push(`set SPACES_BASE_PATH=${config.basePath}`);
848
- }
849
- if (config.allowedOrigins) {
850
- lines.push(`set SPACES_ALLOWED_ORIGINS=${config.allowedOrigins}`);
851
- }
852
- lines.push(`"${nodePath}" "${spacesPath}" >> "${outLog}" 2>&1`);
853
- lines.push('');
854
-
855
- fs.writeFileSync(wrapperPath, lines.join('\r\n'));
856
- return wrapperPath;
857
- }
858
-
859
- async function win32Install() {
860
- const level = await promptLevel();
861
-
862
- // For system service, determine the target user's home directory
863
- // (may differ from the admin account running the installer)
864
- if (level === 'system' && process.platform === 'win32') {
865
- const targetHome = await promptTargetUser();
866
- setTargetHome(targetHome);
867
- }
868
-
869
- const wrapperPath = win32WrapperScript(level);
870
-
871
- log(`Wrapper script written to ${wrapperPath}`);
872
-
873
- // Delete existing task (ignore errors)
874
- try {
875
- execFileSync('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'pipe' });
876
- } catch {}
877
-
878
- if (level === 'system') {
879
- execFileSync('schtasks', ['/Create', '/TN', TASK_NAME, '/TR', `"${wrapperPath}"`, '/SC', 'ONSTART', '/RU', 'SYSTEM', '/F'], { stdio: 'inherit' });
880
- } else {
881
- execFileSync('schtasks', ['/Create', '/TN', TASK_NAME, '/TR', `"${wrapperPath}"`, '/SC', 'ONLOGON', '/RL', 'HIGHEST', '/F'], { stdio: 'inherit' });
882
- }
883
- logOk('Scheduled task created');
884
-
885
- saveLevel(level);
886
-
887
- // Set up SSH for multi-user support (system service only)
888
- // Only ensure OpenSSH is available — key generation is handled at runtime
889
- // by ensureServiceKeyAtRuntime() in terminal-server.js, which runs as SYSTEM
890
- // so the key is owned by SYSTEM and OpenSSH accepts it.
891
- if (level === 'system') {
892
- ensureOpenSSHServer();
893
- log('SSH service key will be generated on first run (as SYSTEM)');
894
- }
895
-
896
- execFileSync('schtasks', ['/Run', '/TN', TASK_NAME], { stdio: 'inherit' });
897
- logOk('Task started');
898
-
899
- logOk(`Spaces installed as ${level} service`);
900
- }
901
-
902
- async function win32Uninstall() {
903
- try {
904
- execFileSync('schtasks', ['/End', '/TN', TASK_NAME], { stdio: 'pipe' });
905
- logOk('Task ended');
906
- } catch {
907
- log('Task was not running');
908
- }
909
-
910
- try {
911
- execFileSync('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'pipe' });
912
- logOk('Scheduled task removed');
913
- } catch {
914
- log('Scheduled task was already removed');
915
- }
916
-
917
- try {
918
- const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
919
- fs.unlinkSync(wrapperPath);
920
- logOk('Wrapper script removed');
921
- } catch {
922
- log('Wrapper script was already removed');
923
- }
924
-
925
- try {
926
- fs.unlinkSync(LEVEL_PATH);
927
- } catch {}
928
-
929
- logOk('Spaces service uninstalled');
930
- }
931
-
932
- async function win32Start() {
933
- // Regenerate wrapper script so it always points to the latest installed version
934
- const level = loadLevel() || 'user';
935
- win32WrapperScript(level);
936
-
937
- // Clear stale update-check cache so the UI refreshes after an upgrade
938
- try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
939
-
940
- // Try schtasks /Run first (works when running as admin or for user-level tasks)
941
- try {
942
- execFileSync('schtasks', ['/Run', '/TN', TASK_NAME], { stdio: 'pipe' });
943
- logOk('Task started');
944
- return;
945
- } catch {}
946
-
947
- // Fallback: launch the wrapper script directly as a detached process.
948
- // This works for non-admin shells when the task is registered as SYSTEM.
949
- // The service will run as the current user, not SYSTEM, but functionally
950
- // equivalent for local development.
951
- const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
952
- if (!fs.existsSync(wrapperPath)) {
953
- logErr('Wrapper script not found — run "spaces service install" first');
954
- process.exit(1);
955
- }
956
-
957
- const { spawn } = require('child_process');
958
- const child = spawn('cmd.exe', ['/c', wrapperPath], {
959
- detached: true,
960
- stdio: 'ignore',
961
- windowsHide: true,
962
- });
963
- child.unref();
964
- logOk('Service started (direct launch)');
965
- }
966
-
967
- async function win32Stop() {
968
- try {
969
- execFileSync('schtasks', ['/End', '/TN', TASK_NAME], { stdio: 'pipe' });
970
- } catch {}
971
-
972
- // Kill the actual node processes on our ports (verify it's node before killing)
973
- const config = resolveConfig();
974
- const ports = [config.port || 3457, 3400];
975
- let killed = 0;
976
- for (const port of ports) {
977
- try {
978
- const output = execFileSync('netstat', ['-ano'], { encoding: 'utf-8' });
979
- for (const line of output.split(String.fromCharCode(10))) {
980
- if (line.includes(':' + port + ' ') && line.includes('LISTENING')) {
981
- const parts = line.trim().split(/\s+/);
982
- const pid = parseInt(parts[parts.length - 1], 10);
983
- if (pid > 0) {
984
- // Verify the process is node.exe before killing to avoid terminating unrelated processes
985
- try {
986
- const taskInfo = execFileSync('tasklist', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], { encoding: 'utf-8' });
987
- if (!taskInfo.toLowerCase().includes('node.exe')) continue;
988
- process.kill(pid, 'SIGTERM'); killed++;
989
- } catch {}
990
- }
991
- }
992
- }
993
- } catch {}
994
- }
995
-
996
- if (killed > 0) {
997
- logOk('Stopped ' + killed + ' process(es)');
998
- } else {
999
- logOk('Task stopped');
1000
- }
1001
- }
1002
-
1003
- async function win32Status() {
1004
- try {
1005
- execFileSync('schtasks', ['/Query', '/TN', TASK_NAME, '/V', '/FO', 'LIST'], { stdio: 'inherit' });
1006
- } catch {
1007
- log('Spaces service is not installed');
1008
- }
1009
- }
1010
-
1011
- async function win32Logs() {
1012
- ensureLogsDir();
1013
- const outLogPath = path.join(LOGS_DIR, 'spaces.out.log');
1014
- if (!fs.existsSync(outLogPath)) {
1015
- logErr(`Log file not found: ${outLogPath}`);
1016
- log('Service may not have started yet');
1017
- process.exit(1);
1018
- }
1019
- spawnSync('powershell', ['-Command', `Get-Content "${outLogPath}" -Wait -Tail 50`], { stdio: 'inherit' });
1020
- }
1021
-
1022
- // ─── Dispatch table ──────────────────────────────────────────
1023
- const platforms = {
1024
- linux: { install: linuxInstall, uninstall: linuxUninstall, start: linuxStart, stop: linuxStop, status: linuxStatus, logs: linuxLogs },
1025
- darwin: { install: darwinInstall, uninstall: darwinUninstall, start: darwinStart, stop: darwinStop, status: darwinStatus, logs: darwinLogs },
1026
- win32: { install: win32Install, uninstall: win32Uninstall, start: win32Start, stop: win32Stop, status: win32Status, logs: win32Logs },
1027
- };
1028
-
1029
- // ─── CLI ──────────────────────────────────────────────────────
1030
- async function main() {
1031
- const action = process.argv[2];
1032
- const platform = getPlatform();
1033
- const dispatch = platforms[platform];
1034
-
1035
- if (!action || !dispatch[action]) {
1036
- log('Usage: spaces service <install|uninstall|start|stop|status|logs>');
1037
- process.exit(action ? 1 : 0);
1038
- }
1039
-
1040
- await dispatch[action]();
1041
- }
1042
-
1043
- main().catch((err) => {
1044
- logErr(err.message);
1045
- process.exit(1);
1046
- });
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { execFileSync, spawnSync } = require('child_process');
6
+ const path = require('path');
7
+ const os = require('os');
8
+ const fs = require('fs');
9
+ const readline = require('readline');
10
+
11
+ let SPACES_DIR = path.join(os.homedir(), '.spaces');
12
+ let CONFIG_PATH = path.join(SPACES_DIR, 'server.json');
13
+ let LOGS_DIR = path.join(SPACES_DIR, 'logs');
14
+ const SERVICE_NAME = 'spaces';
15
+ const LABEL = 'com.agentspaces.spaces';
16
+ const TASK_NAME = 'Spaces';
17
+
18
+ // Override SPACES_DIR to point to a different user's home
19
+ function setTargetHome(homedir) {
20
+ SPACES_DIR = path.join(homedir, '.spaces');
21
+ CONFIG_PATH = path.join(SPACES_DIR, 'server.json');
22
+ LOGS_DIR = path.join(SPACES_DIR, 'logs');
23
+ }
24
+
25
+ // ─── Helpers ──────────────────────────────────────────────────
26
+ function log(msg) { console.log(` ${msg}`); }
27
+ function logOk(msg) { console.log(` ✓ ${msg}`); }
28
+ function logErr(msg) { console.error(` ✗ ${msg}`); }
29
+
30
+ function resolveConfig() {
31
+ let config = {};
32
+ if (fs.existsSync(CONFIG_PATH)) {
33
+ try { config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); } catch {}
34
+ }
35
+ return {
36
+ port: config.port || 3457,
37
+ tier: config.tier || 'community',
38
+ basePath: config.basePath || '',
39
+ allowedOrigins: config.allowedOrigins || '',
40
+ };
41
+ }
42
+
43
+ function resolveSpacesPath() {
44
+ // Prefer the globally-installed package so upgrades via `npm i -g` take
45
+ // effect without needing to re-run `spaces service install`.
46
+ try {
47
+ const globalRoot = execFileSync('npm', ['root', '-g'], { encoding: 'utf-8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'] }).trim();
48
+ const globalSpaces = path.join(globalRoot, '@jlongo78', 'agent-spaces', 'bin', 'spaces.js');
49
+ if (fs.existsSync(globalSpaces)) return globalSpaces;
50
+ } catch { /* fall through */ }
51
+ return path.join(__dirname, 'spaces.js');
52
+ }
53
+
54
+ function resolveProjectDir() {
55
+ return path.join(__dirname, '..');
56
+ }
57
+
58
+ function resolveNodePath() {
59
+ return process.execPath;
60
+ }
61
+
62
+ function promptLevel() {
63
+ return new Promise((resolve) => {
64
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
65
+ console.log('');
66
+ log('Install as:');
67
+ log(' 1. System service (starts on boot, all users)');
68
+ log(' 2. User service (starts on login, current user only)');
69
+ console.log('');
70
+ rl.question(' Choice [2]: ', (answer) => {
71
+ rl.close();
72
+ const choice = answer.trim() || '2';
73
+ resolve(choice === '1' ? 'system' : 'user');
74
+ });
75
+ });
76
+ }
77
+
78
+ function ensureLogsDir() {
79
+ fs.mkdirSync(LOGS_DIR, { recursive: true });
80
+ }
81
+
82
+ // Detect Windows user profiles that have .claude/ (Claude Code data)
83
+ function findClaudeUsers() {
84
+ if (process.platform !== 'win32') return [];
85
+ const usersDir = path.dirname(os.homedir());
86
+ const skip = new Set(['Public', 'Default', 'Default User', 'All Users']);
87
+ try {
88
+ return fs.readdirSync(usersDir)
89
+ .filter(name => !skip.has(name) && !name.startsWith('.'))
90
+ .filter(name => {
91
+ const claudeDir = path.join(usersDir, name, '.claude');
92
+ return fs.existsSync(claudeDir);
93
+ })
94
+ .map(name => ({ name, homedir: path.join(usersDir, name) }));
95
+ } catch { return []; }
96
+ }
97
+
98
+ // Prompt for which user's home directory to target (system service only).
99
+ // Looks for users with .claude/ (Claude Code data) since that's what gets synced.
100
+ function promptTargetUser() {
101
+ return new Promise((resolve) => {
102
+ const currentHome = os.homedir();
103
+ const users = findClaudeUsers();
104
+
105
+ // No Claude users found — fall back to current user
106
+ if (users.length === 0) {
107
+ resolve(currentHome);
108
+ return;
109
+ }
110
+ // Single Claude user — auto-select
111
+ if (users.length === 1) {
112
+ log(`Using home directory: ${users[0].homedir}`);
113
+ resolve(users[0].homedir);
114
+ return;
115
+ }
116
+
117
+ // Multiple Claude users — ask which one
118
+ console.log('');
119
+ log('Multiple users have Claude Code data:');
120
+ users.forEach((u, i) => log(` ${i + 1}. ${u.name} (${u.homedir})`));
121
+ console.log('');
122
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
123
+ rl.question(` Run as user [1]: `, (answer) => {
124
+ rl.close();
125
+ const idx = parseInt(answer.trim() || '1', 10) - 1;
126
+ const chosen = users[idx] || users[0];
127
+ resolve(chosen.homedir);
128
+ });
129
+ });
130
+ }
131
+
132
+
133
+ // --- SSH AuthorizedKeysCommand setup (Linux/macOS) ---
134
+ // Configures sshd to dynamically authorize Spaces users via a lookup script,
135
+ // eliminating the need to manually manage ~/.ssh/authorized_keys per user.
136
+ function configureAuthorizedKeysCommand() {
137
+ if (process.platform === 'win32') return; // Windows uses a different approach
138
+
139
+ const scriptSrc = path.join(__dirname, 'ssh-auth-keys.sh');
140
+ const scriptDst = '/opt/spaces/bin/ssh-auth-keys.sh';
141
+ const sshdConfig = '/etc/ssh/sshd_config';
142
+ const spacesConfDrop = '/etc/ssh/sshd_config.d/spaces.conf';
143
+
144
+ // Install the script to a stable system path
145
+ try {
146
+ spawnSync('sudo', ['mkdir', '-p', '/opt/spaces/bin'], { stdio: 'pipe', timeout: 5000 });
147
+ spawnSync('sudo', ['cp', scriptSrc, scriptDst], { stdio: 'pipe', timeout: 5000 });
148
+ spawnSync('sudo', ['chmod', '755', scriptDst], { stdio: 'pipe', timeout: 5000 });
149
+ spawnSync('sudo', ['chown', 'root:root', scriptDst], { stdio: 'pipe', timeout: 5000 });
150
+ logOk('Installed ssh-auth-keys.sh to ' + scriptDst);
151
+ } catch (e) {
152
+ logErr('Failed to install ssh-auth-keys.sh: ' + e.message);
153
+ return;
154
+ }
155
+
156
+ // Prefer sshd_config.d drop-in if the Include directive exists
157
+ const sshdContent = fs.existsSync(sshdConfig)
158
+ ? fs.readFileSync(sshdConfig, 'utf-8') : '';
159
+
160
+ const configLines = [
161
+ '# Spaces: dynamically authorize shell users via admin DB lookup',
162
+ 'AuthorizedKeysCommand ' + scriptDst + ' %u',
163
+ 'AuthorizedKeysCommandUser nobody',
164
+ ].join('\n') + '\n';
165
+
166
+ // Already configured?
167
+ if (sshdContent.includes('ssh-auth-keys.sh')) {
168
+ logOk('AuthorizedKeysCommand already configured in sshd_config');
169
+ return;
170
+ }
171
+
172
+ // Try drop-in directory first (cleaner, doesn't touch main config)
173
+ const hasIncludeDir = sshdContent.includes('Include /etc/ssh/sshd_config.d/');
174
+ if (hasIncludeDir) {
175
+ try {
176
+ spawnSync('sudo', ['mkdir', '-p', '/etc/ssh/sshd_config.d'], { stdio: 'pipe', timeout: 5000 });
177
+ const result = spawnSync('sudo', ['tee', spacesConfDrop], {
178
+ input: configLines, stdio: ['pipe', 'pipe', 'inherit'], timeout: 5000,
179
+ });
180
+ if (result.status === 0) {
181
+ logOk('Created ' + spacesConfDrop);
182
+ }
183
+ } catch (e) {
184
+ logErr('Failed to write drop-in config: ' + e.message);
185
+ }
186
+ } else {
187
+ // Append directly to sshd_config
188
+ try {
189
+ const appendContent = '\n# Spaces SSH authorization\n' + configLines;
190
+ const result = spawnSync('sudo', ['tee', '-a', sshdConfig], {
191
+ input: appendContent, stdio: ['pipe', 'pipe', 'inherit'], timeout: 5000,
192
+ });
193
+ if (result.status === 0) {
194
+ logOk('Added AuthorizedKeysCommand to ' + sshdConfig);
195
+ }
196
+ } catch (e) {
197
+ logErr('Failed to update sshd_config: ' + e.message);
198
+ }
199
+ }
200
+
201
+ // Restart sshd to pick up the change
202
+ try {
203
+ spawnSync('sudo', ['systemctl', 'restart', 'sshd'], { stdio: 'pipe', timeout: 10000 });
204
+ logOk('Restarted sshd');
205
+ } catch {
206
+ try {
207
+ spawnSync('sudo', ['systemctl', 'restart', 'ssh'], { stdio: 'pipe', timeout: 10000 });
208
+ logOk('Restarted ssh');
209
+ } catch {
210
+ log('Warning: could not restart sshd — restart it manually');
211
+ }
212
+ }
213
+ }
214
+
215
+ // --- SSH key provisioning (for multi-user system service) ---
216
+ function checkOpenSSHServer() {
217
+ if (process.platform !== 'win32') return true;
218
+ try {
219
+ const result = spawnSync('powershell', ['-NoProfile', '-Command',
220
+ 'Get-Service sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status'
221
+ ], { encoding: 'utf-8', timeout: 10000 });
222
+ const status = (result.stdout || '').trim();
223
+ return status === 'Running' || status === 'Stopped';
224
+ } catch { return false; }
225
+ }
226
+
227
+ function ensureOpenSSHServer() {
228
+ if (process.platform !== 'win32') return;
229
+ if (checkOpenSSHServer()) {
230
+ try {
231
+ spawnSync('powershell', ['-NoProfile', '-Command',
232
+ 'Start-Service sshd; Set-Service -Name sshd -StartupType Automatic'
233
+ ], { stdio: 'pipe', timeout: 15000 });
234
+ logOk('OpenSSH Server started and set to automatic');
235
+ } catch {
236
+ log('Warning: could not start OpenSSH Server');
237
+ }
238
+ return;
239
+ }
240
+ log('Installing OpenSSH Server (required for multi-user terminals)...');
241
+ try {
242
+ const result = spawnSync('powershell', ['-NoProfile', '-Command',
243
+ 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0'
244
+ ], { encoding: 'utf-8', stdio: 'pipe', timeout: 120000 });
245
+ if (result.status === 0) {
246
+ logOk('OpenSSH Server installed');
247
+ spawnSync('powershell', ['-NoProfile', '-Command',
248
+ 'Start-Service sshd; Set-Service -Name sshd -StartupType Automatic'
249
+ ], { stdio: 'pipe', timeout: 15000 });
250
+ logOk('OpenSSH Server started');
251
+ } else {
252
+ logErr('Failed to install OpenSSH Server: ' + (result.stderr || '').trim());
253
+ log('Multi-user terminals will not work. Install manually:');
254
+ log(' Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0');
255
+ }
256
+ } catch (e) {
257
+ logErr('Failed to install OpenSSH Server: ' + e.message);
258
+ }
259
+ }
260
+
261
+ function ensureServiceKey() {
262
+ const keyPath = path.join(SPACES_DIR, 'service_key');
263
+ if (fs.existsSync(keyPath)) return keyPath;
264
+ log('Generating SSH service key...');
265
+ fs.mkdirSync(SPACES_DIR, { recursive: true });
266
+ const result = spawnSync('ssh-keygen', [
267
+ '-t', 'ed25519',
268
+ '-f', keyPath,
269
+ '-N', '',
270
+ '-C', 'spaces-service-key',
271
+ ], { stdio: 'pipe', timeout: 10000 });
272
+ if (result.status !== 0) {
273
+ logErr('Failed to generate SSH key');
274
+ return null;
275
+ }
276
+
277
+ // On Windows, restrict private key permissions so OpenSSH accepts it.
278
+ // OpenSSH requires: no inherited ACLs, only the file owner + SYSTEM may have access.
279
+ if (process.platform === 'win32') {
280
+ try {
281
+ const currentUser = os.userInfo().username;
282
+ // Remove inheritance and all default ACLs, then grant only owner + SYSTEM
283
+ spawnSync('icacls', [keyPath, '/inheritance:r',
284
+ '/remove', 'BUILTIN\\Administrators',
285
+ '/remove', 'BUILTIN\\Users',
286
+ '/remove', 'Everyone',
287
+ '/grant:r', currentUser + ':(F)',
288
+ '/grant', 'NT AUTHORITY\\SYSTEM:(F)'], { stdio: 'pipe', timeout: 5000 });
289
+ } catch {}
290
+ }
291
+
292
+ logOk('SSH service key generated');
293
+ return keyPath;
294
+ }
295
+
296
+ function authorizeServiceKey(keyPath, targetUser) {
297
+ const pubKey = fs.readFileSync(keyPath + '.pub', 'utf-8').trim();
298
+
299
+ if (process.platform === 'win32') {
300
+ // Windows OpenSSH ignores ~/.ssh/authorized_keys for admin users.
301
+ // Must use C:\ProgramData\ssh\administrators_authorized_keys instead.
302
+ const adminAuthKeys = path.join(process.env.ProgramData || 'C:\\ProgramData', 'ssh', 'administrators_authorized_keys');
303
+ const userAuthKeys = path.join(path.dirname(os.homedir()), targetUser, '.ssh', 'authorized_keys');
304
+
305
+ // Check if user is an administrator (exact match, not substring)
306
+ let isAdmin = false;
307
+ try {
308
+ const result = spawnSync('net', ['localgroup', 'Administrators'], { encoding: 'utf-8', timeout: 5000 });
309
+ const lines = (result.stdout || '').split(/\r?\n/);
310
+ const sep = lines.findIndex(l => l.trim().startsWith('---'));
311
+ if (sep >= 0) {
312
+ const members = lines.slice(sep + 1).map(l => l.trim()).filter(Boolean);
313
+ isAdmin = members.some(m => m.toLowerCase() === targetUser.toLowerCase());
314
+ }
315
+ } catch {}
316
+
317
+ const authKeysPath = isAdmin ? adminAuthKeys : userAuthKeys;
318
+ const authDir = path.dirname(authKeysPath);
319
+ if (!fs.existsSync(authDir)) fs.mkdirSync(authDir, { recursive: true });
320
+
321
+ // Check if key already authorized
322
+ if (fs.existsSync(authKeysPath)) {
323
+ const existing = fs.readFileSync(authKeysPath, 'utf-8');
324
+ if (existing.includes(pubKey)) return;
325
+ }
326
+ fs.appendFileSync(authKeysPath, pubKey + String.fromCharCode(10));
327
+
328
+ // Fix permissions for administrators_authorized_keys
329
+ if (isAdmin) {
330
+ try {
331
+ spawnSync('icacls', [authKeysPath, '/inheritance:r', '/grant', 'SYSTEM:(R)', '/grant', 'Administrators:(R)'], { stdio: 'pipe', timeout: 5000 });
332
+ } catch {}
333
+ }
334
+ } else {
335
+ // Linux/macOS: use ~/.ssh/authorized_keys
336
+ let userHome;
337
+ try {
338
+ // Use getent to resolve the actual home directory (works with LDAP, NIS, etc.)
339
+ const result = spawnSync('getent', ['passwd', targetUser], { encoding: 'utf-8', timeout: 5000 });
340
+ const fields = (result.stdout || '').split(':');
341
+ userHome = fields[5] || (process.platform === 'darwin' ? `/Users/${targetUser}` : `/home/${targetUser}`);
342
+ } catch {
343
+ userHome = process.platform === 'darwin' ? `/Users/${targetUser}` : `/home/${targetUser}`;
344
+ }
345
+ const sshDir = path.join(userHome, '.ssh');
346
+ const authKeysPath = path.join(sshDir, 'authorized_keys');
347
+ if (!fs.existsSync(sshDir)) fs.mkdirSync(sshDir, { recursive: true, mode: 0o700 });
348
+ if (fs.existsSync(authKeysPath)) {
349
+ const existing = fs.readFileSync(authKeysPath, 'utf-8');
350
+ if (existing.includes(pubKey)) return;
351
+ }
352
+ fs.appendFileSync(authKeysPath, pubKey + String.fromCharCode(10));
353
+ }
354
+ logOk('SSH key authorized for ' + targetUser);
355
+ }
356
+
357
+ const LEVEL_PATH = path.join(SPACES_DIR, 'service-level');
358
+
359
+ function saveLevel(level) {
360
+ fs.mkdirSync(SPACES_DIR, { recursive: true });
361
+ fs.writeFileSync(LEVEL_PATH, level);
362
+ }
363
+
364
+ function loadLevel() {
365
+ try { return fs.readFileSync(LEVEL_PATH, 'utf-8').trim(); } catch { return null; }
366
+ }
367
+
368
+ // ─── Platform detection ───────────────────────────────────────
369
+ function getPlatform() {
370
+ switch (process.platform) {
371
+ case 'linux': return 'linux';
372
+ case 'darwin': return 'darwin';
373
+ case 'win32': return 'win32';
374
+ default:
375
+ logErr(`Unsupported platform: ${process.platform}`);
376
+ process.exit(1);
377
+ }
378
+ }
379
+
380
+ // ─── Linux (systemd) ─────────────────────────────────────────
381
+ function linuxServicePath(level) {
382
+ if (level === 'system') {
383
+ return `/etc/systemd/system/${SERVICE_NAME}.service`;
384
+ }
385
+ const userDir = path.join(os.homedir(), '.config', 'systemd', 'user');
386
+ fs.mkdirSync(userDir, { recursive: true });
387
+ return path.join(userDir, `${SERVICE_NAME}.service`);
388
+ }
389
+
390
+ function linuxUnitFile(level) {
391
+ const config = resolveConfig();
392
+ const nodePath = resolveNodePath();
393
+ const spacesPath = resolveSpacesPath();
394
+ const projectDir = resolveProjectDir();
395
+
396
+ let envLines = [
397
+ `Environment=SPACES_SERVICE=1`,
398
+ `Environment=SPACES_PORT=${config.port}`,
399
+ ];
400
+ // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
401
+ if (config.tier && config.tier !== 'community') {
402
+ envLines.push(`Environment=SPACES_TIER=${config.tier}`);
403
+ }
404
+ if (config.basePath) {
405
+ envLines.push(`Environment=SPACES_BASE_PATH=${config.basePath}`);
406
+ }
407
+ if (config.allowedOrigins) {
408
+ envLines.push(`Environment=SPACES_ALLOWED_ORIGINS=${config.allowedOrigins}`);
409
+ }
410
+
411
+ // Allow users to set API keys (OPENAI_API_KEY, etc.) in ~/.spaces/env
412
+ const envFilePath = path.join(SPACES_DIR, 'env');
413
+
414
+ let serviceSection = [
415
+ 'Type=simple',
416
+ `ExecStart=${nodePath} ${spacesPath}`,
417
+ `WorkingDirectory=${projectDir}`,
418
+ `EnvironmentFile=-${envFilePath}`,
419
+ ...envLines,
420
+ 'Restart=on-failure',
421
+ 'RestartSec=5',
422
+ ];
423
+
424
+ if (level === 'system') {
425
+ const username = os.userInfo().username;
426
+ serviceSection.push(`User=${username}`);
427
+ serviceSection.push(`Group=${username}`);
428
+ }
429
+
430
+ const wantedBy = level === 'system' ? 'multi-user.target' : 'default.target';
431
+
432
+ return [
433
+ '[Unit]',
434
+ 'Description=Spaces - Agent Workspace Manager',
435
+ 'After=network.target',
436
+ '',
437
+ '[Service]',
438
+ ...serviceSection,
439
+ '',
440
+ '[Install]',
441
+ `WantedBy=${wantedBy}`,
442
+ '',
443
+ ].join('\n');
444
+ }
445
+
446
+ function linuxSystemctl(level, ...args) {
447
+ if (level === 'system') {
448
+ execFileSync('sudo', ['systemctl', ...args], { stdio: 'inherit' });
449
+ } else {
450
+ // User-level systemctl needs XDG_RUNTIME_DIR to connect to the user bus.
451
+ // Always set it from the real UID — inherited env may have a stale value
452
+ // (e.g. from a prior session with a different UID mapping).
453
+ const uid = process.getuid();
454
+ const env = {
455
+ ...process.env,
456
+ XDG_RUNTIME_DIR: `/run/user/${uid}`,
457
+ DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
458
+ };
459
+ execFileSync('systemctl', ['--user', ...args], { stdio: 'inherit', env });
460
+ }
461
+ }
462
+
463
+ async function linuxInstall() {
464
+ const level = await promptLevel();
465
+ const unitContent = linuxUnitFile(level);
466
+ const servicePath = linuxServicePath(level);
467
+
468
+ log(`Writing unit file to ${servicePath}`);
469
+ if (level === 'system') {
470
+ const result = spawnSync('sudo', ['tee', servicePath], {
471
+ input: unitContent,
472
+ stdio: ['pipe', 'pipe', 'inherit'],
473
+ });
474
+ if (result.status !== 0) {
475
+ logErr('Failed to write unit file');
476
+ process.exit(1);
477
+ }
478
+ } else {
479
+ fs.writeFileSync(servicePath, unitContent);
480
+ }
481
+ logOk('Unit file written');
482
+
483
+ saveLevel(level);
484
+
485
+ linuxSystemctl(level, 'daemon-reload');
486
+ logOk('Reloaded systemd daemon');
487
+
488
+ linuxSystemctl(level, 'enable', `${SERVICE_NAME}.service`);
489
+ logOk('Service enabled');
490
+
491
+ linuxSystemctl(level, 'start', `${SERVICE_NAME}.service`);
492
+ logOk('Service started');
493
+
494
+ if (level === 'user') {
495
+ try {
496
+ const uid = process.getuid();
497
+ const env = {
498
+ ...process.env,
499
+ XDG_RUNTIME_DIR: `/run/user/${uid}`,
500
+ DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
501
+ };
502
+ execFileSync('loginctl', ['enable-linger', os.userInfo().username], { stdio: 'inherit', env });
503
+ logOk('Enabled login lingering for user service');
504
+ } catch {
505
+ log('Warning: could not enable-linger (user service may not start on boot)');
506
+ }
507
+ }
508
+
509
+ // Set up SSH AuthorizedKeysCommand for seamless multi-user terminal access
510
+ if (level === 'system') {
511
+ configureAuthorizedKeysCommand();
512
+ const keyPath = ensureServiceKey();
513
+ if (keyPath) {
514
+ authorizeServiceKey(keyPath, os.userInfo().username);
515
+ }
516
+ }
517
+
518
+ logOk(`Spaces installed as ${level} service`);
519
+ }
520
+
521
+ async function linuxUninstall() {
522
+ const level = loadLevel() || 'user';
523
+ const servicePath = linuxServicePath(level);
524
+
525
+ try {
526
+ linuxSystemctl(level, 'stop', `${SERVICE_NAME}.service`);
527
+ logOk('Service stopped');
528
+ } catch {
529
+ log('Service was not running');
530
+ }
531
+
532
+ try {
533
+ linuxSystemctl(level, 'disable', `${SERVICE_NAME}.service`);
534
+ logOk('Service disabled');
535
+ } catch {
536
+ log('Service was not enabled');
537
+ }
538
+
539
+ try {
540
+ if (level === 'system') {
541
+ execFileSync('sudo', ['rm', '-f', servicePath], { stdio: 'inherit' });
542
+ } else {
543
+ fs.unlinkSync(servicePath);
544
+ }
545
+ logOk('Unit file removed');
546
+ } catch {
547
+ log('Unit file was already removed');
548
+ }
549
+
550
+ try {
551
+ linuxSystemctl(level, 'daemon-reload');
552
+ logOk('Reloaded systemd daemon');
553
+ } catch {
554
+ log('Warning: daemon-reload failed');
555
+ }
556
+
557
+ try {
558
+ fs.unlinkSync(LEVEL_PATH);
559
+ } catch {}
560
+
561
+ logOk('Spaces service uninstalled');
562
+ }
563
+
564
+ async function linuxStart() {
565
+ const level = loadLevel() || 'user';
566
+ // Regenerate unit file so it picks up the latest installed version
567
+ const unitContent = linuxUnitFile(level);
568
+ const unitPath = linuxServicePath(level);
569
+ fs.writeFileSync(unitPath, unitContent);
570
+ linuxSystemctl(level, 'daemon-reload');
571
+ // Clear stale update-check cache
572
+ try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
573
+ linuxSystemctl(level, 'start', `${SERVICE_NAME}.service`);
574
+ logOk('Service started');
575
+ }
576
+
577
+ async function linuxStop() {
578
+ const level = loadLevel() || 'user';
579
+ linuxSystemctl(level, 'stop', `${SERVICE_NAME}.service`);
580
+ logOk('Service stopped');
581
+ }
582
+
583
+ async function linuxStatus() {
584
+ const level = loadLevel() || 'user';
585
+ try {
586
+ linuxSystemctl(level, 'status', `${SERVICE_NAME}.service`);
587
+ } catch {
588
+ // systemctl status returns non-zero for stopped/failed services
589
+ }
590
+ }
591
+
592
+ async function linuxLogs() {
593
+ const level = loadLevel() || 'user';
594
+ if (level === 'user') {
595
+ const uid = process.getuid();
596
+ const env = {
597
+ ...process.env,
598
+ XDG_RUNTIME_DIR: `/run/user/${uid}`,
599
+ DBUS_SESSION_BUS_ADDRESS: `unix:path=/run/user/${uid}/bus`,
600
+ };
601
+ spawnSync('journalctl', ['--user', '-u', SERVICE_NAME, '-f', '--no-pager'], { stdio: 'inherit', env });
602
+ } else {
603
+ spawnSync('sudo', ['journalctl', '-u', SERVICE_NAME, '-f', '--no-pager'], { stdio: 'inherit' });
604
+ }
605
+ }
606
+
607
+ // ─── macOS (launchd) ─────────────────────────────────────────
608
+ function darwinPlistPath(level) {
609
+ if (level === 'system') {
610
+ return `/Library/LaunchDaemons/${LABEL}.plist`;
611
+ }
612
+ return path.join(os.homedir(), 'Library', 'LaunchAgents', `${LABEL}.plist`);
613
+ }
614
+
615
+ function darwinPlistContent(level) {
616
+ ensureLogsDir();
617
+ const config = resolveConfig();
618
+ const nodePath = resolveNodePath();
619
+ const spacesPath = resolveSpacesPath();
620
+ const projectDir = resolveProjectDir();
621
+ const outLog = path.join(LOGS_DIR, 'spaces.out.log');
622
+ const errLog = path.join(LOGS_DIR, 'spaces.err.log');
623
+
624
+ let envEntries = [
625
+ ` <key>SPACES_SERVICE</key>`,
626
+ ` <string>1</string>`,
627
+ ` <key>SPACES_PORT</key>`,
628
+ ` <string>${config.port}</string>`,
629
+ ];
630
+ // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
631
+ if (config.tier && config.tier !== 'community') {
632
+ envEntries.push(` <key>SPACES_TIER</key>`);
633
+ envEntries.push(` <string>${config.tier}</string>`);
634
+ }
635
+ if (config.basePath) {
636
+ envEntries.push(` <key>SPACES_BASE_PATH</key>`);
637
+ envEntries.push(` <string>${config.basePath}</string>`);
638
+ }
639
+ if (config.allowedOrigins) {
640
+ envEntries.push(` <key>SPACES_ALLOWED_ORIGINS</key>`);
641
+ envEntries.push(` <string>${config.allowedOrigins}</string>`);
642
+ }
643
+
644
+ let extraKeys = '';
645
+ if (level === 'system') {
646
+ extraKeys = ` <key>UserName</key>\n <string>${os.userInfo().username}</string>\n`;
647
+ }
648
+
649
+ return [
650
+ '<?xml version="1.0" encoding="UTF-8"?>',
651
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
652
+ '<plist version="1.0">',
653
+ '<dict>',
654
+ ` <key>Label</key>`,
655
+ ` <string>${LABEL}</string>`,
656
+ ` <key>ProgramArguments</key>`,
657
+ ` <array>`,
658
+ ` <string>${nodePath}</string>`,
659
+ ` <string>${spacesPath}</string>`,
660
+ ` </array>`,
661
+ ` <key>WorkingDirectory</key>`,
662
+ ` <string>${projectDir}</string>`,
663
+ ` <key>EnvironmentVariables</key>`,
664
+ ` <dict>`,
665
+ ...envEntries,
666
+ ` </dict>`,
667
+ ` <key>RunAtLoad</key>`,
668
+ ` <true/>`,
669
+ ` <key>KeepAlive</key>`,
670
+ ` <true/>`,
671
+ ` <key>StandardOutPath</key>`,
672
+ ` <string>${outLog}</string>`,
673
+ ` <key>StandardErrorPath</key>`,
674
+ ` <string>${errLog}</string>`,
675
+ extraKeys ? extraKeys.trimEnd() : null,
676
+ '</dict>',
677
+ '</plist>',
678
+ '',
679
+ ].filter((line) => line !== null).join('\n');
680
+ }
681
+
682
+ async function darwinInstall() {
683
+ const level = await promptLevel();
684
+ const plistPath = darwinPlistPath(level);
685
+ const plistContent = darwinPlistContent(level);
686
+
687
+ // Unload existing (ignore errors)
688
+ try {
689
+ execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'pipe' });
690
+ } catch {}
691
+
692
+ log(`Writing plist to ${plistPath}`);
693
+ if (level === 'system') {
694
+ const result = spawnSync('sudo', ['tee', plistPath], {
695
+ input: plistContent,
696
+ stdio: ['pipe', 'pipe', 'inherit'],
697
+ });
698
+ if (result.status !== 0) {
699
+ logErr('Failed to write plist file');
700
+ process.exit(1);
701
+ }
702
+ execFileSync('sudo', ['chown', 'root:wheel', plistPath], { stdio: 'inherit' });
703
+ execFileSync('sudo', ['chmod', '644', plistPath], { stdio: 'inherit' });
704
+ } else {
705
+ const agentsDir = path.join(os.homedir(), 'Library', 'LaunchAgents');
706
+ fs.mkdirSync(agentsDir, { recursive: true });
707
+ fs.writeFileSync(plistPath, plistContent);
708
+ }
709
+ logOk('Plist file written');
710
+
711
+ saveLevel(level);
712
+
713
+ if (level === 'system') {
714
+ execFileSync('sudo', ['launchctl', 'load', '-w', plistPath], { stdio: 'inherit' });
715
+ } else {
716
+ execFileSync('launchctl', ['load', '-w', plistPath], { stdio: 'inherit' });
717
+ }
718
+ logOk('Service loaded');
719
+
720
+ // Set up SSH AuthorizedKeysCommand for seamless multi-user terminal access
721
+ if (level === 'system') {
722
+ configureAuthorizedKeysCommand();
723
+ const keyPath = ensureServiceKey();
724
+ if (keyPath) {
725
+ authorizeServiceKey(keyPath, os.userInfo().username);
726
+ }
727
+ }
728
+
729
+ logOk(`Spaces installed as ${level} service`);
730
+ }
731
+
732
+ async function darwinUninstall() {
733
+ const level = loadLevel() || 'user';
734
+ const plistPath = darwinPlistPath(level);
735
+
736
+ try {
737
+ if (level === 'system') {
738
+ execFileSync('sudo', ['launchctl', 'unload', '-w', plistPath], { stdio: 'inherit' });
739
+ } else {
740
+ execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'inherit' });
741
+ }
742
+ logOk('Service unloaded');
743
+ } catch {
744
+ log('Service was not loaded');
745
+ }
746
+
747
+ try {
748
+ if (level === 'system') {
749
+ execFileSync('sudo', ['rm', '-f', plistPath], { stdio: 'inherit' });
750
+ } else {
751
+ fs.unlinkSync(plistPath);
752
+ }
753
+ logOk('Plist file removed');
754
+ } catch {
755
+ log('Plist file was already removed');
756
+ }
757
+
758
+ try {
759
+ fs.unlinkSync(LEVEL_PATH);
760
+ } catch {}
761
+
762
+ logOk('Spaces service uninstalled');
763
+ }
764
+
765
+ async function darwinStart() {
766
+ const level = loadLevel() || 'user';
767
+ const plistPath = darwinPlistPath(level);
768
+ // Regenerate plist so it picks up the latest installed version
769
+ const plistContent = darwinPlistContent(level);
770
+ fs.writeFileSync(plistPath, plistContent);
771
+ // Clear stale update-check cache
772
+ try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
773
+ if (level === 'system') {
774
+ execFileSync('sudo', ['launchctl', 'load', '-w', plistPath], { stdio: 'inherit' });
775
+ } else {
776
+ execFileSync('launchctl', ['load', '-w', plistPath], { stdio: 'inherit' });
777
+ }
778
+ logOk('Service started');
779
+ }
780
+
781
+ async function darwinStop() {
782
+ const level = loadLevel() || 'user';
783
+ const plistPath = darwinPlistPath(level);
784
+ if (level === 'system') {
785
+ execFileSync('sudo', ['launchctl', 'unload', '-w', plistPath], { stdio: 'inherit' });
786
+ } else {
787
+ execFileSync('launchctl', ['unload', '-w', plistPath], { stdio: 'inherit' });
788
+ }
789
+ logOk('Service stopped');
790
+ }
791
+
792
+ async function darwinStatus() {
793
+ try {
794
+ const result = execFileSync('launchctl', ['list'], { encoding: 'utf-8' });
795
+ const lines = result.split('\n').filter((line) => line.includes(LABEL));
796
+ if (lines.length > 0) {
797
+ log('Spaces service status:');
798
+ lines.forEach((line) => log(line));
799
+ } else {
800
+ log('Spaces service is not loaded');
801
+ }
802
+ } catch {
803
+ log('Spaces service is not loaded');
804
+ }
805
+ }
806
+
807
+ async function darwinLogs() {
808
+ ensureLogsDir();
809
+ const outLogPath = path.join(LOGS_DIR, 'spaces.out.log');
810
+ if (!fs.existsSync(outLogPath)) {
811
+ logErr(`Log file not found: ${outLogPath}`);
812
+ log('Service may not have started yet');
813
+ process.exit(1);
814
+ }
815
+ spawnSync('tail', ['-f', outLogPath], { stdio: 'inherit' });
816
+ }
817
+
818
+ // ─── Windows (Task Scheduler) ────────────────────────────────
819
+ function win32WrapperScript(level) {
820
+ ensureLogsDir();
821
+ const config = resolveConfig();
822
+ const nodePath = resolveNodePath();
823
+ const spacesPath = resolveSpacesPath();
824
+ const outLog = path.join(LOGS_DIR, 'spaces.out.log');
825
+ const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
826
+
827
+ const lines = [
828
+ '@echo off',
829
+ ];
830
+ // When running as SYSTEM, override USERPROFILE so os.homedir() resolves
831
+ // to the target user's home directory (where .spaces/ config lives).
832
+ if (level === 'system') {
833
+ const homedir = path.dirname(SPACES_DIR); // use resolved target, not os.homedir()
834
+ const drive = path.parse(homedir).root.slice(0, -1);
835
+ const rest = homedir.slice(drive.length);
836
+ lines.push(`set USERPROFILE=${homedir}`);
837
+ lines.push(`set HOMEDRIVE=${drive}`);
838
+ lines.push(`set HOMEPATH=${rest}`);
839
+ }
840
+ lines.push('set SPACES_SERVICE=1');
841
+ lines.push(`set SPACES_PORT=${config.port}`);
842
+ // Don't hardcode SPACES_TIER — let spaces.js auto-detect from installed packages
843
+ if (config.tier && config.tier !== 'community') {
844
+ lines.push(`set SPACES_TIER=${config.tier}`);
845
+ }
846
+ if (config.basePath) {
847
+ lines.push(`set SPACES_BASE_PATH=${config.basePath}`);
848
+ }
849
+ if (config.allowedOrigins) {
850
+ lines.push(`set SPACES_ALLOWED_ORIGINS=${config.allowedOrigins}`);
851
+ }
852
+ lines.push(`"${nodePath}" "${spacesPath}" >> "${outLog}" 2>&1`);
853
+ lines.push('');
854
+
855
+ fs.writeFileSync(wrapperPath, lines.join('\r\n'));
856
+ return wrapperPath;
857
+ }
858
+
859
+ async function win32Install() {
860
+ const level = await promptLevel();
861
+
862
+ // For system service, determine the target user's home directory
863
+ // (may differ from the admin account running the installer)
864
+ if (level === 'system' && process.platform === 'win32') {
865
+ const targetHome = await promptTargetUser();
866
+ setTargetHome(targetHome);
867
+ }
868
+
869
+ const wrapperPath = win32WrapperScript(level);
870
+
871
+ log(`Wrapper script written to ${wrapperPath}`);
872
+
873
+ // Delete existing task (ignore errors)
874
+ try {
875
+ execFileSync('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'pipe' });
876
+ } catch {}
877
+
878
+ if (level === 'system') {
879
+ execFileSync('schtasks', ['/Create', '/TN', TASK_NAME, '/TR', `"${wrapperPath}"`, '/SC', 'ONSTART', '/RU', 'SYSTEM', '/F'], { stdio: 'inherit' });
880
+ } else {
881
+ execFileSync('schtasks', ['/Create', '/TN', TASK_NAME, '/TR', `"${wrapperPath}"`, '/SC', 'ONLOGON', '/RL', 'HIGHEST', '/F'], { stdio: 'inherit' });
882
+ }
883
+ logOk('Scheduled task created');
884
+
885
+ saveLevel(level);
886
+
887
+ // Set up SSH for multi-user support (system service only)
888
+ // Only ensure OpenSSH is available — key generation is handled at runtime
889
+ // by ensureServiceKeyAtRuntime() in terminal-server.js, which runs as SYSTEM
890
+ // so the key is owned by SYSTEM and OpenSSH accepts it.
891
+ if (level === 'system') {
892
+ ensureOpenSSHServer();
893
+ log('SSH service key will be generated on first run (as SYSTEM)');
894
+ }
895
+
896
+ execFileSync('schtasks', ['/Run', '/TN', TASK_NAME], { stdio: 'inherit' });
897
+ logOk('Task started');
898
+
899
+ logOk(`Spaces installed as ${level} service`);
900
+ }
901
+
902
+ async function win32Uninstall() {
903
+ try {
904
+ execFileSync('schtasks', ['/End', '/TN', TASK_NAME], { stdio: 'pipe' });
905
+ logOk('Task ended');
906
+ } catch {
907
+ log('Task was not running');
908
+ }
909
+
910
+ try {
911
+ execFileSync('schtasks', ['/Delete', '/TN', TASK_NAME, '/F'], { stdio: 'pipe' });
912
+ logOk('Scheduled task removed');
913
+ } catch {
914
+ log('Scheduled task was already removed');
915
+ }
916
+
917
+ try {
918
+ const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
919
+ fs.unlinkSync(wrapperPath);
920
+ logOk('Wrapper script removed');
921
+ } catch {
922
+ log('Wrapper script was already removed');
923
+ }
924
+
925
+ try {
926
+ fs.unlinkSync(LEVEL_PATH);
927
+ } catch {}
928
+
929
+ logOk('Spaces service uninstalled');
930
+ }
931
+
932
+ async function win32Start() {
933
+ // Regenerate wrapper script so it always points to the latest installed version
934
+ const level = loadLevel() || 'user';
935
+ win32WrapperScript(level);
936
+
937
+ // Clear stale update-check cache so the UI refreshes after an upgrade
938
+ try { fs.unlinkSync(path.join(SPACES_DIR, 'update-check.json')); } catch {}
939
+
940
+ // Try schtasks /Run first (works when running as admin or for user-level tasks)
941
+ try {
942
+ execFileSync('schtasks', ['/Run', '/TN', TASK_NAME], { stdio: 'pipe' });
943
+ logOk('Task started');
944
+ return;
945
+ } catch {}
946
+
947
+ // Fallback: launch the wrapper script directly as a detached process.
948
+ // This works for non-admin shells when the task is registered as SYSTEM.
949
+ // The service will run as the current user, not SYSTEM, but functionally
950
+ // equivalent for local development.
951
+ const wrapperPath = path.join(SPACES_DIR, 'spaces-service.cmd');
952
+ if (!fs.existsSync(wrapperPath)) {
953
+ logErr('Wrapper script not found — run "spaces service install" first');
954
+ process.exit(1);
955
+ }
956
+
957
+ const { spawn } = require('child_process');
958
+ const child = spawn('cmd.exe', ['/c', wrapperPath], {
959
+ detached: true,
960
+ stdio: 'ignore',
961
+ windowsHide: true,
962
+ });
963
+ child.unref();
964
+ logOk('Service started (direct launch)');
965
+ }
966
+
967
+ async function win32Stop() {
968
+ try {
969
+ execFileSync('schtasks', ['/End', '/TN', TASK_NAME], { stdio: 'pipe' });
970
+ } catch {}
971
+
972
+ // Kill the actual node processes on our ports (verify it's node before killing)
973
+ const config = resolveConfig();
974
+ const ports = [config.port || 3457, 3400];
975
+ let killed = 0;
976
+ for (const port of ports) {
977
+ try {
978
+ const output = execFileSync('netstat', ['-ano'], { encoding: 'utf-8' });
979
+ for (const line of output.split(String.fromCharCode(10))) {
980
+ if (line.includes(':' + port + ' ') && line.includes('LISTENING')) {
981
+ const parts = line.trim().split(/\s+/);
982
+ const pid = parseInt(parts[parts.length - 1], 10);
983
+ if (pid > 0) {
984
+ // Verify the process is node.exe before killing to avoid terminating unrelated processes
985
+ try {
986
+ const taskInfo = execFileSync('tasklist', ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], { encoding: 'utf-8' });
987
+ if (!taskInfo.toLowerCase().includes('node.exe')) continue;
988
+ process.kill(pid, 'SIGTERM'); killed++;
989
+ } catch {}
990
+ }
991
+ }
992
+ }
993
+ } catch {}
994
+ }
995
+
996
+ if (killed > 0) {
997
+ logOk('Stopped ' + killed + ' process(es)');
998
+ } else {
999
+ logOk('Task stopped');
1000
+ }
1001
+ }
1002
+
1003
+ async function win32Status() {
1004
+ try {
1005
+ execFileSync('schtasks', ['/Query', '/TN', TASK_NAME, '/V', '/FO', 'LIST'], { stdio: 'inherit' });
1006
+ } catch {
1007
+ log('Spaces service is not installed');
1008
+ }
1009
+ }
1010
+
1011
+ async function win32Logs() {
1012
+ ensureLogsDir();
1013
+ const outLogPath = path.join(LOGS_DIR, 'spaces.out.log');
1014
+ if (!fs.existsSync(outLogPath)) {
1015
+ logErr(`Log file not found: ${outLogPath}`);
1016
+ log('Service may not have started yet');
1017
+ process.exit(1);
1018
+ }
1019
+ spawnSync('powershell', ['-Command', `Get-Content "${outLogPath}" -Wait -Tail 50`], { stdio: 'inherit' });
1020
+ }
1021
+
1022
+ // ─── Dispatch table ──────────────────────────────────────────
1023
+ const platforms = {
1024
+ linux: { install: linuxInstall, uninstall: linuxUninstall, start: linuxStart, stop: linuxStop, status: linuxStatus, logs: linuxLogs },
1025
+ darwin: { install: darwinInstall, uninstall: darwinUninstall, start: darwinStart, stop: darwinStop, status: darwinStatus, logs: darwinLogs },
1026
+ win32: { install: win32Install, uninstall: win32Uninstall, start: win32Start, stop: win32Stop, status: win32Status, logs: win32Logs },
1027
+ };
1028
+
1029
+ // ─── CLI ──────────────────────────────────────────────────────
1030
+ async function main() {
1031
+ const action = process.argv[2];
1032
+ const platform = getPlatform();
1033
+ const dispatch = platforms[platform];
1034
+
1035
+ if (!action || !dispatch[action]) {
1036
+ log('Usage: spaces service <install|uninstall|start|stop|status|logs>');
1037
+ process.exit(action ? 1 : 0);
1038
+ }
1039
+
1040
+ await dispatch[action]();
1041
+ }
1042
+
1043
+ main().catch((err) => {
1044
+ logErr(err.message);
1045
+ process.exit(1);
1046
+ });