spexcode 0.6.2 → 0.6.5

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 (479) hide show
  1. package/README.md +3 -1
  2. package/bin/spex.mjs +4 -0
  3. package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
  4. package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
  5. package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
  6. package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
  7. package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
  8. package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
  9. package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
  10. package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
  11. package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
  12. package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
  13. package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
  14. package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
  15. package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
  16. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
  17. package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
  18. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
  19. package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
  20. package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
  21. package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
  22. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
  23. package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
  24. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
  25. package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
  26. package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
  27. package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
  28. package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
  29. package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
  30. package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
  31. package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
  32. package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
  33. package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
  34. package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
  35. package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
  36. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
  37. package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
  38. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
  39. package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
  40. package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
  41. package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
  42. package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
  43. package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
  44. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
  45. package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
  46. package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
  47. package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
  48. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
  49. package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
  50. package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
  51. package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +36 -32
  52. package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
  53. package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
  54. package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
  55. package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
  56. package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
  57. package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
  58. package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
  59. package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
  60. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
  61. package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
  62. package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
  63. package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
  64. package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
  65. package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
  66. package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
  67. package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
  68. package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
  69. package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
  70. package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
  71. package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
  72. package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
  73. package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
  74. package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
  75. package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
  76. package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
  77. package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
  78. package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
  79. package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
  80. package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
  81. package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
  82. package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
  83. package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
  84. package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
  85. package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
  86. package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
  87. package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
  88. package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
  89. package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
  90. package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
  91. package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
  92. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
  93. package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
  94. package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
  95. package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
  96. package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
  97. package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
  98. package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
  99. package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
  100. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
  101. package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
  102. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
  103. package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
  104. package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
  105. package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
  106. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
  107. package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
  108. package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
  109. package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
  110. package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
  111. package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
  112. package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
  113. package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
  114. package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
  115. package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
  116. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
  117. package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
  118. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
  119. package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
  120. package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
  121. package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
  122. package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
  123. package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
  124. package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
  125. package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
  126. package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
  127. package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
  128. package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
  129. package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
  130. package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
  131. package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
  132. package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
  133. package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
  134. package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
  135. package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
  136. package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
  137. package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
  138. package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
  139. package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
  140. package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
  141. package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
  142. package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
  143. package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
  144. package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
  145. package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
  146. package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
  147. package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
  148. package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
  149. package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
  150. package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
  151. package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
  152. package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
  153. package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
  154. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
  155. package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
  156. package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
  157. package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
  158. package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
  159. package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
  160. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
  161. package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
  162. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
  163. package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +1 -1
  164. package/node_modules/@spexcode/spec-cli/package.json +47 -0
  165. package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
  166. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
  167. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
  168. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
  169. package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
  170. package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
  171. package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
  172. package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
  173. package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
  174. package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
  175. package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
  176. package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
  177. package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
  178. package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
  179. package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
  180. package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
  181. package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
  182. package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
  183. package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
  184. package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
  185. package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
  186. package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
  187. package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
  188. package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
  189. package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
  190. package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
  191. package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
  192. package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
  193. package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
  194. package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
  195. package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
  196. package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
  197. package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
  198. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
  199. package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
  200. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
  201. package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
  202. package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
  203. package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
  204. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
  205. package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
  206. package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
  207. package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
  208. package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
  209. package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
  210. package/node_modules/@spexcode/spec-core/package.json +28 -0
  211. package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
  212. package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
  213. package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
  214. package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
  215. package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
  216. package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
  217. package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
  218. package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
  219. package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
  220. package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
  221. package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
  222. package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
  223. package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
  224. package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
  225. package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
  226. package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
  227. package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
  228. package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
  229. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
  230. package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
  231. package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
  232. package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
  233. package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
  234. package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
  235. package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
  236. package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
  237. package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
  238. package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
  239. package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
  240. package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
  241. package/node_modules/@spexcode/spec-eval/package.json +36 -0
  242. package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
  243. package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
  244. package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
  245. package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
  246. package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
  247. package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
  248. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
  249. package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
  250. package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
  251. package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
  252. package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
  253. package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
  254. package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
  255. package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
  256. package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
  257. package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
  258. package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
  259. package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
  260. package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
  261. package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
  262. package/node_modules/@spexcode/spec-forge/package.json +32 -0
  263. package/package.json +33 -19
  264. package/spec-cli/bin/spex.mjs +0 -69
  265. package/spec-cli/src/anchors.ts +0 -728
  266. package/spec-cli/src/attach.ts +0 -43
  267. package/spec-cli/src/claude-headless.ts +0 -321
  268. package/spec-cli/src/cli.ts +0 -1258
  269. package/spec-cli/src/client.ts +0 -414
  270. package/spec-cli/src/cockpit.ts +0 -43
  271. package/spec-cli/src/codex-runtime-generations.ts +0 -646
  272. package/spec-cli/src/commit-surgery.ts +0 -68
  273. package/spec-cli/src/contract-filter.ts +0 -202
  274. package/spec-cli/src/delivery-queue.ts +0 -165
  275. package/spec-cli/src/doctor.ts +0 -516
  276. package/spec-cli/src/execution-trace.ts +0 -444
  277. package/spec-cli/src/file-write.ts +0 -22
  278. package/spec-cli/src/gateway-auth.ts +0 -181
  279. package/spec-cli/src/gateway-hub.ts +0 -358
  280. package/spec-cli/src/gateway.ts +0 -461
  281. package/spec-cli/src/git.ts +0 -2556
  282. package/spec-cli/src/graph.ts +0 -271
  283. package/spec-cli/src/graphCache.ts +0 -710
  284. package/spec-cli/src/graphDelta.ts +0 -77
  285. package/spec-cli/src/graphStream.ts +0 -1019
  286. package/spec-cli/src/harness-select.ts +0 -77
  287. package/spec-cli/src/harness.ts +0 -3025
  288. package/spec-cli/src/headless-controller.ts +0 -63
  289. package/spec-cli/src/help.ts +0 -492
  290. package/spec-cli/src/host-resources.ts +0 -762
  291. package/spec-cli/src/host.ts +0 -611
  292. package/spec-cli/src/identity-presets.d.ts +0 -13
  293. package/spec-cli/src/identity-presets.js +0 -138
  294. package/spec-cli/src/index.ts +0 -818
  295. package/spec-cli/src/init.ts +0 -258
  296. package/spec-cli/src/issues-cli.ts +0 -276
  297. package/spec-cli/src/issues.ts +0 -237
  298. package/spec-cli/src/layout.ts +0 -640
  299. package/spec-cli/src/lint.ts +0 -488
  300. package/spec-cli/src/localIssues.ts +0 -538
  301. package/spec-cli/src/materialize.ts +0 -417
  302. package/spec-cli/src/mentions.ts +0 -81
  303. package/spec-cli/src/opencode-headless.ts +0 -205
  304. package/spec-cli/src/pi-headless.ts +0 -153
  305. package/spec-cli/src/plugin-harness.ts +0 -157
  306. package/spec-cli/src/process-identity.ts +0 -207
  307. package/spec-cli/src/project-identity.ts +0 -73
  308. package/spec-cli/src/project-store.ts +0 -17
  309. package/spec-cli/src/pty-bridge.ts +0 -396
  310. package/spec-cli/src/pty-helper.mjs +0 -96
  311. package/spec-cli/src/pty-native-helper.mjs +0 -22
  312. package/spec-cli/src/ranker.ts +0 -191
  313. package/spec-cli/src/reaper.ts +0 -125
  314. package/spec-cli/src/resilience.ts +0 -41
  315. package/spec-cli/src/reviewSnapshot.ts +0 -28
  316. package/spec-cli/src/reviews.ts +0 -434
  317. package/spec-cli/src/runtime-guard.ts +0 -35
  318. package/spec-cli/src/runtime-ownership.ts +0 -88
  319. package/spec-cli/src/search.bench.mjs +0 -80
  320. package/spec-cli/src/search.ts +0 -63
  321. package/spec-cli/src/session-cursors.ts +0 -91
  322. package/spec-cli/src/session-execution.ts +0 -68
  323. package/spec-cli/src/session-files.ts +0 -121
  324. package/spec-cli/src/session-follow.ts +0 -182
  325. package/spec-cli/src/session-reparent.ts +0 -33
  326. package/spec-cli/src/session-timeline.ts +0 -247
  327. package/spec-cli/src/session-web.ts +0 -135
  328. package/spec-cli/src/sessions.ts +0 -3883
  329. package/spec-cli/src/sh.ts +0 -3
  330. package/spec-cli/src/slash-commands.ts +0 -318
  331. package/spec-cli/src/source-files.ts +0 -104
  332. package/spec-cli/src/specs.ts +0 -498
  333. package/spec-cli/src/supervise.ts +0 -245
  334. package/spec-cli/src/tree.ts +0 -118
  335. package/spec-cli/src/tsx-bin.ts +0 -18
  336. package/spec-cli/src/uninstall.ts +0 -171
  337. package/spec-cli/src/uploads.ts +0 -253
  338. package/spec-cli/src/worktree-sources.ts +0 -42
  339. package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
  340. package/spec-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
  341. package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
  342. package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
  343. package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
  344. package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.js +0 -1
  345. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  346. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  347. package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  348. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  349. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  350. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  351. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  352. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  353. package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  354. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  355. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  356. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  357. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  358. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  359. package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  360. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  361. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  362. package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  363. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  364. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  365. package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  366. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  367. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  368. package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  369. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  370. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  371. package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  372. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  373. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  374. package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  375. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  376. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  377. package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  378. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  379. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  380. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  381. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  382. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  383. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  384. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  385. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  386. package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  387. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  388. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  389. package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  390. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  391. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  392. package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  393. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  394. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  395. package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  396. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  397. package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  398. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  399. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  400. package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  401. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  402. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  403. package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  404. package/spec-dashboard/dist/assets/MobileApp-D9yPMR3A.js +0 -2
  405. package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
  406. package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
  407. package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
  408. package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
  409. package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
  410. package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
  411. package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
  412. package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
  413. package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
  414. package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
  415. package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
  416. package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
  417. package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
  418. package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
  419. package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.js +0 -1
  420. package/spec-dashboard/dist/index.html +0 -29
  421. package/spec-dashboard/src/reviewFilters.js +0 -322
  422. package/spec-dashboard/src/reviewQuery.js +0 -174
  423. package/spec-dashboard/src/session.js +0 -199
  424. package/spec-eval/src/cache.ts +0 -57
  425. package/spec-eval/src/cli.ts +0 -894
  426. package/spec-eval/src/evaltab.ts +0 -264
  427. package/spec-eval/src/filing.ts +0 -39
  428. package/spec-eval/src/freshness.ts +0 -836
  429. package/spec-eval/src/humanok.ts +0 -28
  430. package/spec-eval/src/scenariofresh.ts +0 -244
  431. package/spec-eval/src/scenarios.ts +0 -672
  432. package/spec-eval/src/sessioneval.ts +0 -2220
  433. package/spec-eval/src/sidecar.ts +0 -93
  434. package/spec-eval/src/timeline.ts +0 -54
  435. package/spec-forge/src/__fixtures__/github-forge.json +0 -63
  436. package/spec-forge/src/cache.ts +0 -66
  437. package/spec-forge/src/cli.ts +0 -99
  438. package/spec-forge/src/drivers/github.ts +0 -132
  439. package/spec-forge/src/drivers/gitlab.ts +0 -153
  440. package/spec-forge/src/drivers.ts +0 -73
  441. package/spec-forge/src/links.ts +0 -72
  442. package/spec-forge/src/needs-eval.ts +0 -29
  443. package/spec-forge/src/port.ts +0 -36
  444. package/spec-forge/src/resident.ts +0 -46
  445. /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
  446. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
  447. /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
  448. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
  449. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
  450. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
  451. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
  452. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
  453. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
  454. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
  455. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
  456. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
  457. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
  458. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
  459. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -0
  460. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
  461. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
  462. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
  463. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
  464. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
  465. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
  466. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
  467. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
  468. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec.md +0 -0
  469. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
  470. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
  471. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
  472. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -0
  473. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
  474. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
  475. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
  476. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
  477. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
  478. /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
  479. /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
@@ -0,0 +1,663 @@
1
+ import net from 'node:net';
2
+ import { spawn } from 'node:child_process';
3
+ import { createHash } from 'node:crypto';
4
+ import { mkdirSync, writeFileSync, readFileSync, renameSync, rmSync, readdirSync, openSync, closeSync, existsSync, realpathSync, statSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+ import { dirname, join, basename, resolve } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { spexcodeHome, encodeProject } from '@spexcode/spec-core';
9
+ import { git } from '@spexcode/spec-core';
10
+ import { serveStatic, resolveDistDir } from './gateway.js';
11
+ import { startHubGateway } from './gateway-hub.js';
12
+ import { MachinePeerGateway } from './machine-peer.js';
13
+ import { DEFAULT_PROJECT_ICON, requireIdentityChoice } from '@spexcode/spec-core/identity';
14
+ import { resolveProjectIdentity, writeGatewayIcon, } from '@spexcode/spec-core';
15
+ import { cliEntrypointArgs } from './tsx-bin.js';
16
+ const here = dirname(fileURLToPath(import.meta.url));
17
+ export const endpointRecordPath = (root) => join(spexcodeHome(), 'projects', encodeProject(root), 'backend.json');
18
+ // atomic publish: tmp + rename, so a reader never sees a torn record (the old write-in-place could be
19
+ // caught mid-write by the reconciler or a bare `spex`'s discovery probe).
20
+ export function publishEndpoint(rec) {
21
+ const file = endpointRecordPath(rec.root);
22
+ mkdirSync(dirname(file), { recursive: true });
23
+ const tmp = join(dirname(file), `.backend.json.${process.pid}.tmp`);
24
+ writeFileSync(tmp, JSON.stringify(rec, null, 2) + '\n');
25
+ renameSync(tmp, file);
26
+ }
27
+ // remove the record ONLY if it is ours (matched by instanceId): a newer serve that already overwrote it,
28
+ // or another project's record, is never deleted by a retiring process.
29
+ export function dropOwnEndpoint(instanceId, root) {
30
+ const file = endpointRecordPath(root);
31
+ try {
32
+ if (JSON.parse(readFileSync(file, 'utf8'))?.instanceId === instanceId)
33
+ rmSync(file);
34
+ }
35
+ catch { /* not ours / already gone */ }
36
+ }
37
+ // a record is HOSTABLE only in the full identity shape; legacy {url,pid} records (pre-instance-identity)
38
+ // are ignored by the host — the direct CLI ladder still reads their url, and they are rewritten in the new
39
+ // shape the next time that serve restarts.
40
+ export function readEndpointRecord(file) {
41
+ try {
42
+ const r = JSON.parse(readFileSync(file, 'utf8'));
43
+ if (r?.version === 2 && typeof r.url === 'string' && typeof r.instanceId === 'string' && typeof r.root === 'string' &&
44
+ typeof r.identity?.title === 'string' && typeof r.identity?.icon === 'string')
45
+ return r;
46
+ return null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ export const catalogPath = () => join(spexcodeHome(), 'projects.json');
53
+ let catalogWarned = false;
54
+ export function readCatalog() {
55
+ let raw;
56
+ try {
57
+ raw = readFileSync(catalogPath(), 'utf8');
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ try {
63
+ const parsed = JSON.parse(raw);
64
+ const list = Array.isArray(parsed?.projects) ? parsed.projects : [];
65
+ return list.filter((e) => e && typeof e.root === 'string');
66
+ }
67
+ catch (e) {
68
+ // reads degrade loud-but-alive (the reconciler must keep serving live records); WRITES refuse below,
69
+ // so a malformed catalog is never silently clobbered.
70
+ if (!catalogWarned) {
71
+ catalogWarned = true;
72
+ console.error(`[host] malformed ${catalogPath()} (${e.message}) — known-project catalog ignored until fixed`);
73
+ }
74
+ return [];
75
+ }
76
+ }
77
+ function writeCatalog(entries) {
78
+ if (existsSync(catalogPath())) {
79
+ try {
80
+ JSON.parse(readFileSync(catalogPath(), 'utf8'));
81
+ }
82
+ catch {
83
+ throw new Error(`refusing to overwrite malformed ${catalogPath()} — fix or remove it first`);
84
+ }
85
+ }
86
+ mkdirSync(dirname(catalogPath()), { recursive: true });
87
+ const tmp = join(dirname(catalogPath()), `.projects.json.${process.pid}.tmp`);
88
+ writeFileSync(tmp, JSON.stringify({ projects: entries }, null, 2) + '\n');
89
+ renameSync(tmp, catalogPath());
90
+ }
91
+ function existingDirectory(dir) {
92
+ let path;
93
+ try {
94
+ path = realpathSync(resolve(dir));
95
+ }
96
+ catch {
97
+ throw new Error(`${dir} is not an existing directory`);
98
+ }
99
+ try {
100
+ if (!statSync(path).isDirectory())
101
+ throw new Error('not a directory');
102
+ }
103
+ catch {
104
+ throw new Error(`${dir} is not an existing directory`);
105
+ }
106
+ return path;
107
+ }
108
+ function gitProjectRoot(dir) {
109
+ try {
110
+ return dirname(git(['-C', dir, 'rev-parse', '--path-format=absolute', '--git-common-dir']).trim());
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ export function browseProjectDirectories(dir) {
117
+ const requested = (dir ?? '').trim() || homedir();
118
+ if (!existsSync(resolve(requested))) {
119
+ const path = resolve(requested);
120
+ return {
121
+ path, exists: false, parent: dirname(path) === path ? null : dirname(path), home: homedir(),
122
+ gitRoot: null, initialized: false, cataloged: false, entries: [],
123
+ };
124
+ }
125
+ const path = existingDirectory(requested);
126
+ const gitRoot = gitProjectRoot(path);
127
+ const entries = readdirSync(path, { withFileTypes: true })
128
+ .filter((entry) => {
129
+ if (entry.isDirectory())
130
+ return true;
131
+ if (!entry.isSymbolicLink())
132
+ return false;
133
+ try {
134
+ return statSync(join(path, entry.name)).isDirectory();
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ })
140
+ .map((entry) => {
141
+ const child = join(path, entry.name);
142
+ const isGit = existsSync(join(child, '.git'));
143
+ return {
144
+ name: entry.name,
145
+ path: child,
146
+ git: isGit,
147
+ initialized: isGit && existsSync(join(child, '.spec')),
148
+ };
149
+ })
150
+ .sort((a, b) => a.name.localeCompare(b.name));
151
+ return {
152
+ path, exists: true,
153
+ parent: dirname(path) === path ? null : dirname(path),
154
+ home: homedir(),
155
+ gitRoot,
156
+ initialized: !!gitRoot && existsSync(join(gitRoot, '.spec')),
157
+ cataloged: !!gitRoot && readCatalog().some((entry) => entry.root === gitRoot),
158
+ entries,
159
+ };
160
+ }
161
+ // Register a repo by normalizing any path inside it to its MAIN checkout (the identity every record and
162
+ // store key uses), then dedupe + persist it.
163
+ export function addKnownProject(dir) {
164
+ const path = existingDirectory(dir);
165
+ const root = gitProjectRoot(path);
166
+ if (!root)
167
+ throw new Error(`${dir} is not a git repository — SpexCode projects are git-backed (run \`git init\` there first)`);
168
+ catalogAdd(root);
169
+ return root;
170
+ }
171
+ function catalogAdd(root) {
172
+ const entries = readCatalog();
173
+ if (entries.some((e) => e.root === root))
174
+ return;
175
+ writeCatalog([...entries, { root, addedAt: new Date().toISOString() }]);
176
+ }
177
+ async function fetchInstance(url) {
178
+ const ctrl = new AbortController();
179
+ const t = setTimeout(() => ctrl.abort(), 900);
180
+ try {
181
+ const r = await fetch(`${url}/api/instance`, { signal: ctrl.signal });
182
+ if (!r.ok)
183
+ return null;
184
+ return await r.json();
185
+ }
186
+ catch {
187
+ return null;
188
+ }
189
+ finally {
190
+ clearTimeout(t);
191
+ }
192
+ }
193
+ export async function reconcileProjects() {
194
+ const projectsDir = join(spexcodeHome(), 'projects');
195
+ let dirs = [];
196
+ try {
197
+ dirs = readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
198
+ }
199
+ catch (e) {
200
+ if (e.code !== 'ENOENT')
201
+ throw e;
202
+ }
203
+ const live = new Map();
204
+ const claimed = new Set(); // roots with a slot-matched record, validated or not
205
+ await Promise.all(dirs.map(async (d) => {
206
+ const rec = readEndpointRecord(join(projectsDir, d, 'backend.json'));
207
+ if (!rec)
208
+ return;
209
+ if (encodeProject(rec.root) !== d)
210
+ return;
211
+ claimed.add(rec.root); // a dead/mismatched record still NAMES a project — listed offline
212
+ const inst = await fetchInstance(rec.url);
213
+ if (inst && inst.instanceId === rec.instanceId && inst.root === rec.root &&
214
+ typeof inst.identity?.title === 'string' && typeof inst.identity?.icon === 'string') {
215
+ live.set(rec.root, { rec, identity: inst.identity });
216
+ }
217
+ }));
218
+ const byId = new Map();
219
+ const push = (root) => {
220
+ if (!existsSync(root))
221
+ return;
222
+ const projectId = encodeProject(root);
223
+ if (byId.has(projectId))
224
+ return; // encodeProject is lossy; first root wins a (pathological) collision
225
+ const active = live.get(root) ?? null;
226
+ const source = readProjectConfig(root);
227
+ let identity;
228
+ try {
229
+ identity = active?.identity ?? resolveProjectIdentity(root, root);
230
+ }
231
+ catch (e) {
232
+ console.error(`[host] cannot resolve identity for ${root}: ${e.message}`);
233
+ identity = { title: basename(root), icon: DEFAULT_PROJECT_ICON };
234
+ }
235
+ byId.set(projectId, {
236
+ projectId, root, identity, configRevision: source.revision,
237
+ online: !!active, url: active?.rec.url ?? null,
238
+ ...(active ? { pid: active.rec.pid, startedAt: active.rec.startedAt } : {}),
239
+ });
240
+ };
241
+ for (const e of readCatalog())
242
+ push(e.root);
243
+ for (const root of claimed)
244
+ push(root);
245
+ return [...byId.values()].sort((a, b) => a.identity.title.localeCompare(b.identity.title) || a.root.localeCompare(b.root));
246
+ }
247
+ // single-flight + last-snapshot: every reader (GET, the stream loop, the per-request proxy resolution)
248
+ // shares one in-flight reconcile instead of stampeding probes.
249
+ let snapshot = [];
250
+ let inflight = null;
251
+ export function reconcileNow() {
252
+ return (inflight ??= reconcileProjects()
253
+ .then((list) => { snapshot = list; return list; })
254
+ .finally(() => { inflight = null; }));
255
+ }
256
+ // ── host operations (spawned `spex`, never forked logic) ─────────────────────────────────────────────
257
+ // init / doctor / serve run the EXISTING CLI implementations as child processes with cwd = the project
258
+ // root: the same git/harness/additive guarantees `spex init` gives at a terminal, the same doctor report,
259
+ // the same supervisor `spex serve` boots — no second implementation of any domain semantics. The spawned
260
+ // env is scrubbed of routing state (SPEXCODE_API_URL/PORT/session/instance ids) so a child never inherits
261
+ // the gateway's — or another project's — backend routing.
262
+ const cliArgs = cliEntrypointArgs(join(here, '..'), here);
263
+ function scrubbedEnv() {
264
+ const env = { ...process.env };
265
+ delete env.SPEXCODE_API_URL;
266
+ delete env.PORT;
267
+ delete env.SPEXCODE_SESSION_ID;
268
+ delete env.SPEXCODE_INSTANCE_ID;
269
+ return env;
270
+ }
271
+ export function runSpex(root, args, timeoutMs = 120_000) {
272
+ return new Promise((resolve) => {
273
+ const child = spawn(process.execPath, [...cliArgs, ...args], { cwd: root, env: scrubbedEnv() });
274
+ let output = '';
275
+ child.stdout.on('data', (d) => { output += d; });
276
+ child.stderr.on('data', (d) => { output += d; });
277
+ const t = setTimeout(() => { try {
278
+ child.kill('SIGKILL');
279
+ }
280
+ catch { /* gone */ } }, timeoutMs);
281
+ child.on('close', (code) => { clearTimeout(t); resolve({ code, output }); });
282
+ child.on('error', (e) => { clearTimeout(t); resolve({ code: null, output: `${output}\nspawn failed: ${e.message}` }); });
283
+ });
284
+ }
285
+ // One ordered add transaction: select an existing directory or explicitly create one with Git, run the
286
+ // real CLI initializer when requested, and only then claim catalog success.
287
+ export async function addKnownProjectWithSetup(dir, setup = {}) {
288
+ if (setup.createDir && !setup.initGit)
289
+ throw new Error('creating a project directory requires Git initialization');
290
+ let directoryCreated = false;
291
+ let path;
292
+ try {
293
+ path = existingDirectory(dir);
294
+ }
295
+ catch (e) {
296
+ if (!setup.createDir)
297
+ throw e;
298
+ mkdirSync(resolve(dir), { recursive: true });
299
+ directoryCreated = true;
300
+ path = existingDirectory(dir);
301
+ }
302
+ let root = gitProjectRoot(path);
303
+ let gitInitialized = false;
304
+ if (!root) {
305
+ if (!setup.initGit)
306
+ throw new Error(`${dir} is not a git repository — enable Git initialization to add it`);
307
+ git(['init', '--quiet', '--', path]);
308
+ gitInitialized = true;
309
+ root = gitProjectRoot(path);
310
+ if (!root)
311
+ throw new Error(`git init completed but ${path} is still not a Git repository`);
312
+ }
313
+ let init;
314
+ if (setup.init) {
315
+ const harness = typeof setup.init.harness === 'string' ? setup.init.harness.trim() : '';
316
+ if (!harness)
317
+ throw new Error('SpexCode initialization requires at least one explicit harness target');
318
+ const preset = typeof setup.init.preset === 'string' ? setup.init.preset.trim() : '';
319
+ const result = await runSpex(root, ['init', '--harness', harness, ...(preset ? ['--preset', preset] : [])]);
320
+ init = result;
321
+ if (result.code !== 0)
322
+ return { ok: false, root, directoryCreated, gitInitialized, init };
323
+ }
324
+ catalogAdd(root);
325
+ return { ok: true, root, directoryCreated, gitInitialized, ...(init ? { init } : {}) };
326
+ }
327
+ let configWriteSeq = 0;
328
+ const configRevision = (raw) => createHash('sha256').update(raw === null ? 'missing' : `present\0${raw}`).digest('hex');
329
+ function readProjectConfig(root) {
330
+ try {
331
+ const content = readFileSync(join(root, 'spexcode.json'), 'utf8');
332
+ return { content, revision: configRevision(content) };
333
+ }
334
+ catch (e) {
335
+ if (e.code === 'ENOENT')
336
+ return { content: '{}\n', revision: configRevision(null) };
337
+ throw e;
338
+ }
339
+ }
340
+ function writeProjectConfig(root, content, revision) {
341
+ const current = readProjectConfig(root);
342
+ if (revision !== current.revision) {
343
+ const e = new Error('spexcode.json changed on disk — reload before saving');
344
+ e.status = 409;
345
+ throw e;
346
+ }
347
+ let parsed;
348
+ try {
349
+ parsed = JSON.parse(content);
350
+ }
351
+ catch (e) {
352
+ const err = new Error(`spexcode.json is not valid JSON: ${e.message}`);
353
+ err.status = 400;
354
+ throw err;
355
+ }
356
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
357
+ const e = new Error('spexcode.json must contain one top-level JSON object');
358
+ e.status = 400;
359
+ throw e;
360
+ }
361
+ const normalized = content.endsWith('\n') ? content : `${content}\n`;
362
+ const file = join(root, 'spexcode.json');
363
+ const tmp = join(root, `.spexcode.json.${process.pid}.${++configWriteSeq}.tmp`);
364
+ try {
365
+ writeFileSync(tmp, normalized);
366
+ renameSync(tmp, file);
367
+ }
368
+ finally {
369
+ try {
370
+ rmSync(tmp);
371
+ }
372
+ catch { /* rename consumed it / write never created it */ }
373
+ }
374
+ return { content: normalized, revision: configRevision(normalized) };
375
+ }
376
+ function writeProjectIcon(root, icon, revision) {
377
+ const canonical = requireIdentityChoice(icon);
378
+ const current = readProjectConfig(root);
379
+ let config;
380
+ try {
381
+ config = JSON.parse(current.content);
382
+ }
383
+ catch (e) {
384
+ const error = new Error(`spexcode.json is not valid JSON: ${e.message}`);
385
+ error.status = 400;
386
+ throw error;
387
+ }
388
+ const dashboard = config.dashboard === undefined
389
+ ? {}
390
+ : config.dashboard && typeof config.dashboard === 'object' && !Array.isArray(config.dashboard)
391
+ ? config.dashboard
392
+ : null;
393
+ if (!dashboard) {
394
+ const error = new Error('spexcode.json dashboard must be an object before its icon can be changed');
395
+ error.status = 400;
396
+ throw error;
397
+ }
398
+ const saved = writeProjectConfig(root, `${JSON.stringify({ ...config, dashboard: { ...dashboard, icon: canonical } }, null, 2)}\n`, revision);
399
+ return { ...saved, identity: resolveProjectIdentity(root, root) };
400
+ }
401
+ function freeTcpPort() {
402
+ return new Promise((res, rej) => {
403
+ const s = net.createServer();
404
+ s.once('error', rej);
405
+ s.listen(0, '127.0.0.1', () => { const p = s.address().port; s.close(() => res(p)); });
406
+ });
407
+ }
408
+ // start an OFFLINE project's backend: spawn a detached `spex serve --port <free>` whose lifetime is
409
+ // independent of the gateway (it owns its record exactly like a hand-run serve), log to the project's
410
+ // runtime tier, and wait for its instance-validated record to reconcile online.
411
+ export async function startBackend(root, waitMs = 45_000) {
412
+ const port = await freeTcpPort();
413
+ const logDir = join(spexcodeHome(), 'projects', encodeProject(root));
414
+ mkdirSync(logDir, { recursive: true });
415
+ const logFile = join(logDir, 'serve.log');
416
+ const log = openSync(logFile, 'a');
417
+ const child = spawn(process.execPath, [...cliArgs, 'serve', '--port', String(port)], { cwd: root, env: scrubbedEnv(), detached: true, stdio: ['ignore', log, log] });
418
+ child.unref();
419
+ closeSync(log); // the child holds its own copy; keep no fd open in the gateway
420
+ const deadline = Date.now() + waitMs;
421
+ while (Date.now() < deadline) {
422
+ await new Promise((r) => setTimeout(r, 500));
423
+ const entry = (await reconcileNow()).find((p) => p.root === root && p.online);
424
+ if (entry)
425
+ return entry;
426
+ }
427
+ throw new Error(`backend for ${root} did not come online within ${Math.round(waitMs / 1000)}s — see ${logFile}`);
428
+ }
429
+ const json = (res, status, body) => {
430
+ res.writeHead(status, { 'Content-Type': 'application/json' });
431
+ res.end(JSON.stringify(body));
432
+ };
433
+ function readBody(req) {
434
+ return new Promise((resolve) => {
435
+ let body = '';
436
+ req.on('data', (d) => { body += d; if (body.length > 65536)
437
+ req.destroy(); });
438
+ req.on('end', () => resolve(body));
439
+ });
440
+ }
441
+ export function startHostDashboard(opts) {
442
+ const distDir = opts.distDir ?? resolveDistDir();
443
+ const peers = new MachinePeerGateway();
444
+ peers.start();
445
+ const sseClients = new Set();
446
+ let lastBroadcast = '';
447
+ // reconcile → push the fresh list to every /projects/stream subscriber when it changed.
448
+ async function tick() {
449
+ const list = await reconcileNow();
450
+ const j = JSON.stringify(list);
451
+ if (j !== lastBroadcast) {
452
+ lastBroadcast = j;
453
+ for (const c of sseClients)
454
+ c.write(`data: ${j}\n\n`);
455
+ }
456
+ return list;
457
+ }
458
+ const extensions = {
459
+ // the hub's GET /projects rows, host-enriched: every cataloged/claimed project (not only live
460
+ // records), instance-validated online state, and the hub's own gating flag per row. `id` mirrors
461
+ // projectId — the hub's documented row key, kept so both shapes read the same list.
462
+ listProjects: async (store) => (await tick()).map((p) => ({ id: p.projectId, ...p, gated: !!store.projects[p.projectId] })),
463
+ adminRoute: async (req, res, path) => {
464
+ if (path === '/projects/stream') {
465
+ res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });
466
+ res.write(`data: ${JSON.stringify(await tick())}\n\n`);
467
+ sseClients.add(res);
468
+ req.on('close', () => sseClients.delete(res));
469
+ return true;
470
+ }
471
+ if (path === '/projects/browse' && req.method === 'GET') {
472
+ const requested = new URL(req.url ?? path, 'http://localhost').searchParams.get('path') ?? '';
473
+ try {
474
+ json(res, 200, browseProjectDirectories(requested));
475
+ }
476
+ catch (e) {
477
+ json(res, 400, { error: e.message });
478
+ }
479
+ return true;
480
+ }
481
+ if (path === '/projects' && req.method === 'POST') {
482
+ let body = {};
483
+ try {
484
+ body = JSON.parse(await readBody(req) || '{}');
485
+ }
486
+ catch { /* malformed body */ }
487
+ const root = String(body?.root ?? '').trim();
488
+ if (!root) {
489
+ json(res, 400, { error: 'body must be {"root": "/abs/path/to/repo"}' });
490
+ return true;
491
+ }
492
+ if (body.init !== undefined && (!body.init || typeof body.init !== 'object' || Array.isArray(body.init))) {
493
+ json(res, 400, { error: 'init must be {"harness":"<ids>","preset":"<optional>"}' });
494
+ return true;
495
+ }
496
+ try {
497
+ const setup = await addKnownProjectWithSetup(root, {
498
+ createDir: body.createDir === true,
499
+ initGit: body.initGit === true,
500
+ ...(body.init ? { init: { harness: body.init.harness, ...(body.init.preset !== undefined ? { preset: body.init.preset } : {}) } } : {}),
501
+ });
502
+ if (!setup.ok) {
503
+ json(res, 422, { error: 'spex init failed', ...setup });
504
+ return true;
505
+ }
506
+ const entry = (await tick()).find((p) => p.root === setup.root);
507
+ json(res, 200, { ...(entry ?? {
508
+ projectId: encodeProject(setup.root), root: setup.root,
509
+ identity: resolveProjectIdentity(setup.root, setup.root), configRevision: readProjectConfig(setup.root).revision,
510
+ online: false, url: null,
511
+ }), setup });
512
+ }
513
+ catch (e) {
514
+ json(res, 400, { error: e.message });
515
+ }
516
+ return true;
517
+ }
518
+ if (path === '/projects/icon' && req.method === 'PUT') {
519
+ let body;
520
+ try {
521
+ body = JSON.parse(await readBody(req) || '{}');
522
+ }
523
+ catch {
524
+ json(res, 400, { error: 'body must be {"icon":"<choice>","revision":"..."}' });
525
+ return true;
526
+ }
527
+ if (typeof body?.icon !== 'string' || typeof body?.revision !== 'string') {
528
+ json(res, 400, { error: 'body must be {"icon":"<choice>","revision":"..."}' });
529
+ return true;
530
+ }
531
+ try {
532
+ json(res, 200, { ok: true, gateway: writeGatewayIcon(body.icon, body.revision) });
533
+ }
534
+ catch (e) {
535
+ json(res, e.status ?? 400, { error: e.message });
536
+ }
537
+ return true;
538
+ }
539
+ const icon = path.match(/^\/projects\/([^/]+)\/icon$/);
540
+ if (icon && req.method === 'PUT') {
541
+ const projectId = decodeURIComponent(icon[1]);
542
+ const entry = (await reconcileNow()).find((p) => p.projectId === projectId);
543
+ if (!entry) {
544
+ json(res, 404, { error: `unknown project '${projectId}' — add it first (POST /projects)` });
545
+ return true;
546
+ }
547
+ let body;
548
+ try {
549
+ body = JSON.parse(await readBody(req) || '{}');
550
+ }
551
+ catch {
552
+ json(res, 400, { error: 'body must be {"icon":"<choice>","revision":"..."}' });
553
+ return true;
554
+ }
555
+ if (typeof body?.icon !== 'string' || typeof body?.revision !== 'string') {
556
+ json(res, 400, { error: 'body must be {"icon":"<choice>","revision":"..."}' });
557
+ return true;
558
+ }
559
+ try {
560
+ json(res, 200, { ok: true, ...writeProjectIcon(entry.root, body.icon, body.revision) });
561
+ }
562
+ catch (e) {
563
+ json(res, e.status ?? 400, { error: e.message });
564
+ }
565
+ return true;
566
+ }
567
+ const config = path.match(/^\/projects\/([^/]+)\/config$/);
568
+ if (config && (req.method === 'GET' || req.method === 'PUT')) {
569
+ const projectId = decodeURIComponent(config[1]);
570
+ const entry = (await reconcileNow()).find((p) => p.projectId === projectId);
571
+ if (!entry) {
572
+ json(res, 404, { error: `unknown project '${projectId}' — add it first (POST /projects)` });
573
+ return true;
574
+ }
575
+ if (req.method === 'GET') {
576
+ try {
577
+ json(res, 200, readProjectConfig(entry.root));
578
+ }
579
+ catch (e) {
580
+ json(res, 500, { error: `cannot read spexcode.json: ${e.message}` });
581
+ }
582
+ return true;
583
+ }
584
+ let body;
585
+ try {
586
+ body = JSON.parse(await readBody(req) || '{}');
587
+ }
588
+ catch {
589
+ json(res, 400, { error: 'body must be {"content":"...","revision":"..."}' });
590
+ return true;
591
+ }
592
+ if (typeof body?.content !== 'string' || typeof body?.revision !== 'string') {
593
+ json(res, 400, { error: 'body must be {"content":"...","revision":"..."}' });
594
+ return true;
595
+ }
596
+ try {
597
+ json(res, 200, { ok: true, ...writeProjectConfig(entry.root, body.content, body.revision) });
598
+ }
599
+ catch (e) {
600
+ json(res, e.status ?? 500, { error: e.message });
601
+ }
602
+ return true;
603
+ }
604
+ const op = path.match(/^\/projects\/([^/]+)\/(init|doctor|serve)$/);
605
+ if (op && req.method === 'POST') {
606
+ const projectId = decodeURIComponent(op[1]);
607
+ const entry = (await reconcileNow()).find((p) => p.projectId === projectId);
608
+ if (!entry) {
609
+ json(res, 404, { error: `unknown project '${projectId}' — add it first (POST /projects)` });
610
+ return true;
611
+ }
612
+ if (op[2] === 'serve') {
613
+ if (entry.online) {
614
+ json(res, 409, { error: `backend already online at ${entry.url}`, project: entry });
615
+ return true;
616
+ }
617
+ try {
618
+ json(res, 200, { ok: true, project: await startBackend(entry.root) });
619
+ }
620
+ catch (e) {
621
+ json(res, 502, { error: e.message });
622
+ }
623
+ return true;
624
+ }
625
+ let body = {};
626
+ try {
627
+ body = JSON.parse(await readBody(req) || '{}');
628
+ }
629
+ catch { /* malformed body → defaults */ }
630
+ const args = op[2] === 'init'
631
+ ? ['init', ...(body?.harness ? ['--harness', String(body.harness)] : []), ...(body?.preset ? ['--preset', String(body.preset)] : [])]
632
+ : ['doctor'];
633
+ const r = await runSpex(entry.root, args);
634
+ json(res, 200, { ok: r.code === 0, code: r.code, output: r.output });
635
+ return true;
636
+ }
637
+ return false;
638
+ },
639
+ fallback: (req, res, path) => serveStatic(req, res, distDir, path),
640
+ };
641
+ const server = startHubGateway({ port: opts.port, host: opts.host ?? '127.0.0.1', tls: opts.tls ?? null, extensions });
642
+ // continuous reconciliation: the stream stays live without a client-side poll and the list the hub
643
+ // serves stays fresh. Heartbeat comments keep intermediaries from timing the stream out. Both timers
644
+ // unref'd — the SERVER holds the process open, not the loops.
645
+ const loop = setInterval(() => void tick().catch((e) => console.error(`[host] reconcile failed: ${e.message}`)), 2500);
646
+ loop.unref();
647
+ const ping = setInterval(() => { for (const c of sseClients)
648
+ c.write(': ping\n\n'); }, 10_000);
649
+ ping.unref();
650
+ return {
651
+ server,
652
+ close: async () => {
653
+ clearInterval(loop);
654
+ clearInterval(ping);
655
+ for (const c of sseClients)
656
+ c.destroy();
657
+ sseClients.clear();
658
+ await peers.close();
659
+ await new Promise((resolve) => server.close(() => resolve()));
660
+ server.closeAllConnections?.();
661
+ },
662
+ };
663
+ }
@@ -0,0 +1 @@
1
+ export {};