@crouton-kit/crouter 0.3.32 → 0.3.33

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 (440) hide show
  1. package/README.md +8 -0
  2. package/dist/build-root.js +1 -0
  3. package/dist/builtin-memory/crouter-development/marketplaces.md +2 -2
  4. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +2 -2
  5. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +5 -5
  6. package/dist/builtin-memory/crouter-development/personas.md +3 -4
  7. package/dist/builtin-memory/crouter-development/plugins.md +18 -18
  8. package/dist/builtin-memory/design.md +4 -1
  9. package/dist/builtin-memory/development.md +4 -1
  10. package/dist/builtin-memory/internal/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +6 -6
  12. package/dist/builtin-memory/internal/storage-tiers.md +5 -5
  13. package/dist/builtin-memory/planning.md +4 -1
  14. package/dist/builtin-memory/product.md +80 -0
  15. package/dist/builtin-memory/spec.md +4 -1
  16. package/dist/builtin-personas/advisor/PERSONA.md +10 -0
  17. package/dist/builtin-personas/design/PERSONA.md +2 -2
  18. package/dist/builtin-personas/design/orchestrator.md +3 -3
  19. package/dist/builtin-personas/developer/PERSONA.md +1 -1
  20. package/dist/builtin-personas/developer/orchestrator.md +2 -2
  21. package/dist/builtin-personas/explore/PERSONA.md +3 -3
  22. package/dist/builtin-personas/explore/orchestrator.md +4 -2
  23. package/dist/builtin-personas/general/PERSONA.md +1 -1
  24. package/dist/builtin-personas/general/orchestrator.md +1 -1
  25. package/dist/builtin-personas/orchestration-kernel.md +7 -14
  26. package/dist/builtin-personas/plan/PERSONA.md +1 -1
  27. package/dist/builtin-personas/plan/orchestrator.md +2 -2
  28. package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -1
  29. package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -1
  30. package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -1
  31. package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -1
  32. package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -1
  33. package/dist/builtin-personas/product/PERSONA.md +18 -0
  34. package/dist/builtin-personas/product/orchestrator.md +14 -0
  35. package/dist/builtin-personas/product/teardown/PERSONA.md +13 -0
  36. package/dist/builtin-personas/review/PERSONA.md +1 -1
  37. package/dist/builtin-personas/review/orchestrator.md +1 -1
  38. package/dist/builtin-personas/runtime-base.md +5 -0
  39. package/dist/builtin-personas/spec/PERSONA.md +1 -1
  40. package/dist/builtin-personas/spec/orchestrator.md +2 -2
  41. package/dist/builtin-personas/spec/requirements/PERSONA.md +1 -1
  42. package/dist/builtin-views/canvas/core.mjs +82 -1
  43. package/dist/builtin-views/canvas/tui.mjs +9 -5
  44. package/dist/builtin-views/canvas/web.jsx +3 -2
  45. package/dist/builtin-views/chat/core.mjs +725 -0
  46. package/dist/builtin-views/chat/text.mjs +101 -0
  47. package/dist/builtin-views/chat/tui.mjs +368 -0
  48. package/dist/builtin-views/chat/web.jsx +367 -0
  49. package/dist/builtin-views/prompt-review/core.mjs +863 -0
  50. package/dist/builtin-views/prompt-review/text.mjs +15 -0
  51. package/dist/builtin-views/prompt-review/tui.mjs +196 -0
  52. package/dist/builtin-views/prompt-review/web.jsx +484 -0
  53. package/dist/builtin-views/settings/core.mjs +397 -0
  54. package/dist/builtin-views/settings/text.mjs +40 -0
  55. package/dist/builtin-views/settings/tui.mjs +95 -0
  56. package/dist/builtin-views/settings/web.jsx +167 -0
  57. package/dist/cli.js +16 -3
  58. package/dist/clients/attach/__tests__/autocomplete-and-bash-mode.test.js +96 -0
  59. package/dist/clients/attach/__tests__/bash-bang-routing.test.js +58 -0
  60. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.d.ts +1 -0
  61. package/dist/clients/attach/__tests__/bundle-pi-tui-dedup.test.js +56 -0
  62. package/dist/clients/attach/__tests__/titled-editor.test.js +22 -1
  63. package/dist/clients/attach/attach-cmd.d.ts +18 -0
  64. package/dist/clients/attach/attach-cmd.js +1723 -795
  65. package/dist/clients/attach/canvas-panels.js +2 -2
  66. package/dist/clients/attach/chat-view.d.ts +10 -0
  67. package/dist/clients/attach/chat-view.js +23 -0
  68. package/dist/clients/attach/config-load.d.ts +8 -4
  69. package/dist/clients/attach/config-load.js +2 -0
  70. package/dist/clients/attach/extension-dialogs.d.ts +2 -3
  71. package/dist/clients/attach/extension-dialogs.js +7 -9
  72. package/dist/clients/attach/graph-overlay.js +3 -2
  73. package/dist/clients/attach/input-controller.d.ts +4 -3
  74. package/dist/clients/attach/input-controller.js +24 -3
  75. package/dist/clients/attach/slash-commands.d.ts +7 -7
  76. package/dist/clients/attach/slash-commands.js +38 -14
  77. package/dist/clients/attach/titled-editor.js +18 -14
  78. package/dist/clients/attach/view-socket.d.ts +2 -1
  79. package/dist/clients/attach/view-socket.js +27 -8
  80. package/dist/clients/web/__tests__/source-cache.test.d.ts +1 -0
  81. package/dist/clients/web/__tests__/source-cache.test.js +32 -0
  82. package/dist/clients/web/dev-server.js +1 -0
  83. package/dist/clients/web/events.js +9 -11
  84. package/dist/clients/web/server.d.ts +4 -3
  85. package/dist/clients/web/server.js +138 -31
  86. package/dist/clients/web/source-cache.d.ts +10 -0
  87. package/dist/clients/web/source-cache.js +57 -0
  88. package/dist/clients/web/web-cmd.js +28 -9
  89. package/dist/commands/__tests__/human.test.js +30 -28
  90. package/dist/commands/canvas-history/search.js +1 -1
  91. package/dist/commands/canvas-history.js +5 -8
  92. package/dist/commands/canvas-issue.d.ts +2 -0
  93. package/dist/commands/canvas-issue.js +148 -0
  94. package/dist/commands/canvas-prune.js +19 -0
  95. package/dist/commands/canvas-rebuild-index.d.ts +2 -0
  96. package/dist/commands/canvas-rebuild-index.js +57 -0
  97. package/dist/commands/canvas-snapshot.d.ts +2 -0
  98. package/dist/commands/canvas-snapshot.js +48 -0
  99. package/dist/commands/canvas-tmux-spread.d.ts +2 -0
  100. package/dist/commands/canvas-tmux-spread.js +188 -0
  101. package/dist/commands/canvas.d.ts +1 -0
  102. package/dist/commands/canvas.js +16 -1
  103. package/dist/commands/chord.js +143 -48
  104. package/dist/commands/daemon.js +3 -3
  105. package/dist/commands/human/prompts.d.ts +0 -1
  106. package/dist/commands/human/prompts.js +39 -54
  107. package/dist/commands/human/queue.d.ts +12 -1
  108. package/dist/commands/human/queue.js +468 -73
  109. package/dist/commands/human/shared.d.ts +3 -4
  110. package/dist/commands/human/shared.js +3 -3
  111. package/dist/commands/human.js +11 -10
  112. package/dist/commands/memory/find.js +3 -2
  113. package/dist/commands/memory/list.js +4 -3
  114. package/dist/commands/memory/read.js +3 -3
  115. package/dist/commands/memory/shared.d.ts +1 -4
  116. package/dist/commands/memory/shared.js +6 -9
  117. package/dist/commands/memory/write.js +3 -3
  118. package/dist/commands/memory.js +4 -7
  119. package/dist/commands/node-context.d.ts +2 -0
  120. package/dist/commands/node-context.js +172 -0
  121. package/dist/commands/node-snapshot.d.ts +2 -0
  122. package/dist/commands/node-snapshot.js +123 -0
  123. package/dist/commands/node.js +228 -52
  124. package/dist/commands/pkg/plugin-inspect.js +6 -6
  125. package/dist/commands/pkg/plugin-manage.js +1 -1
  126. package/dist/commands/pkg/plugin.js +2 -2
  127. package/dist/commands/pkg.js +2 -2
  128. package/dist/commands/push.d.ts +2 -0
  129. package/dist/commands/push.js +79 -11
  130. package/dist/commands/revive.js +74 -1
  131. package/dist/commands/search/answer.d.ts +1 -0
  132. package/dist/commands/search/answer.js +50 -0
  133. package/dist/commands/search/contents.d.ts +1 -0
  134. package/dist/commands/search/contents.js +96 -0
  135. package/dist/commands/search/exa.d.ts +53 -0
  136. package/dist/commands/search/exa.js +155 -0
  137. package/dist/commands/search/puremd.d.ts +11 -0
  138. package/dist/commands/search/puremd.js +53 -0
  139. package/dist/commands/search/web.d.ts +1 -0
  140. package/dist/commands/search/web.js +65 -0
  141. package/dist/commands/search.d.ts +2 -0
  142. package/dist/commands/search.js +24 -0
  143. package/dist/commands/sys/__tests__/sync-import.test.d.ts +1 -0
  144. package/dist/commands/sys/__tests__/sync-import.test.js +72 -0
  145. package/dist/commands/sys/config.js +59 -10
  146. package/dist/commands/sys/doctor.js +56 -4
  147. package/dist/commands/sys/prompt-review.d.ts +1 -0
  148. package/dist/commands/sys/prompt-review.js +178 -0
  149. package/dist/commands/sys/promptstudio.d.ts +2 -0
  150. package/dist/commands/sys/promptstudio.js +65 -0
  151. package/dist/commands/sys/settings.d.ts +2 -0
  152. package/dist/commands/sys/settings.js +16 -0
  153. package/dist/commands/sys/sync.js +311 -159
  154. package/dist/commands/sys/sysprompt.d.ts +1 -0
  155. package/dist/commands/sys/sysprompt.js +86 -0
  156. package/dist/commands/sys.js +9 -5
  157. package/dist/commands/view-new.js +2 -2
  158. package/dist/core/__tests__/artifact-paths.test.d.ts +1 -0
  159. package/dist/core/__tests__/artifact-paths.test.js +44 -0
  160. package/dist/core/__tests__/broker-preflight.test.d.ts +1 -0
  161. package/dist/core/__tests__/broker-preflight.test.js +85 -0
  162. package/dist/core/__tests__/broker-sdk-wiring.test.js +15 -0
  163. package/dist/core/__tests__/canvas.test.js +37 -1
  164. package/dist/core/__tests__/chat-view-reconnect.test.d.ts +1 -0
  165. package/dist/core/__tests__/chat-view-reconnect.test.js +70 -0
  166. package/dist/core/__tests__/child-death-wake.test.js +11 -2
  167. package/dist/core/__tests__/close.test.js +39 -1
  168. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +1 -0
  169. package/dist/core/__tests__/connection-reconnect-resume.test.js +37 -0
  170. package/dist/core/__tests__/context-intro.test.js +48 -14
  171. package/dist/core/__tests__/daemon-boot.test.js +182 -9
  172. package/dist/core/__tests__/editor-label.test.d.ts +1 -0
  173. package/dist/core/__tests__/editor-label.test.js +26 -0
  174. package/dist/core/__tests__/fault-marker.test.d.ts +1 -0
  175. package/dist/core/__tests__/fault-marker.test.js +112 -0
  176. package/dist/core/__tests__/fault-retry-rewind.test.d.ts +1 -0
  177. package/dist/core/__tests__/fault-retry-rewind.test.js +123 -0
  178. package/dist/core/__tests__/fixtures/fake-engine.d.ts +30 -4
  179. package/dist/core/__tests__/fixtures/fake-engine.js +156 -17
  180. package/dist/core/__tests__/fixtures/fake-pi-host.js +10 -1
  181. package/dist/core/__tests__/full/human-new-window-regression.test.js +1 -1
  182. package/dist/core/__tests__/helpers/harness.d.ts +1 -1
  183. package/dist/core/__tests__/helpers/harness.js +2 -2
  184. package/dist/core/__tests__/human-stranded-deliver.test.js +38 -1
  185. package/dist/core/__tests__/human-surface-target.test.js +1 -1
  186. package/dist/core/__tests__/kickoff.test.js +18 -19
  187. package/dist/core/__tests__/memory-resolver.test.js +12 -1
  188. package/dist/core/__tests__/migration.test.js +49 -14
  189. package/dist/core/__tests__/model-ladders.test.d.ts +1 -0
  190. package/dist/core/__tests__/model-ladders.test.js +160 -0
  191. package/dist/core/__tests__/node-env.test.d.ts +1 -0
  192. package/dist/core/__tests__/node-env.test.js +26 -0
  193. package/dist/core/__tests__/push-final-guard.test.js +7 -1
  194. package/dist/core/__tests__/relaunch-root.test.js +9 -9
  195. package/dist/core/__tests__/revive.test.js +36 -6
  196. package/dist/core/__tests__/spawn-root.test.js +24 -1
  197. package/dist/core/__tests__/stop-guard.test.js +10 -0
  198. package/dist/core/__tests__/stranded-relaunch.test.d.ts +1 -0
  199. package/dist/core/__tests__/stranded-relaunch.test.js +72 -0
  200. package/dist/core/__tests__/tmux-surface.test.js +5 -1
  201. package/dist/core/__tests__/unknown-path.test.js +19 -19
  202. package/dist/core/__tests__/yield-ensures-daemon.test.d.ts +1 -0
  203. package/dist/core/__tests__/yield-ensures-daemon.test.js +54 -0
  204. package/dist/core/artifact.d.ts +2 -33
  205. package/dist/core/artifact.js +27 -87
  206. package/dist/core/bootstrap.js +2 -0
  207. package/dist/core/canvas/browse/__tests__/model.test.js +35 -1
  208. package/dist/core/canvas/browse/app.js +87 -17
  209. package/dist/core/canvas/browse/model.d.ts +10 -1
  210. package/dist/core/canvas/browse/model.js +37 -1
  211. package/dist/core/canvas/browse/render.js +35 -33
  212. package/dist/core/canvas/canvas.d.ts +7 -0
  213. package/dist/core/canvas/canvas.js +54 -5
  214. package/dist/core/canvas/db.js +35 -8
  215. package/dist/core/canvas/labels.d.ts +6 -8
  216. package/dist/core/canvas/labels.js +8 -11
  217. package/dist/core/canvas/nav-model.d.ts +5 -4
  218. package/dist/core/canvas/nav-model.js +31 -17
  219. package/dist/core/canvas/paths.d.ts +18 -1
  220. package/dist/core/canvas/paths.js +31 -2
  221. package/dist/core/canvas/render.d.ts +25 -0
  222. package/dist/core/canvas/render.js +96 -13
  223. package/dist/core/canvas/status-glyph.d.ts +35 -0
  224. package/dist/core/canvas/status-glyph.js +104 -0
  225. package/dist/core/canvas/types.d.ts +2 -2
  226. package/dist/core/config.js +114 -16
  227. package/dist/core/fault-classifier.d.ts +41 -0
  228. package/dist/core/fault-classifier.js +110 -0
  229. package/dist/core/feed/inbox.d.ts +15 -0
  230. package/dist/core/feed/inbox.js +40 -14
  231. package/dist/core/frontmatter.d.ts +1 -11
  232. package/dist/core/frontmatter.js +1 -55
  233. package/dist/core/hearth/config.d.ts +2 -0
  234. package/dist/core/hearth/config.js +75 -0
  235. package/dist/core/hearth/guest-env.d.ts +2 -0
  236. package/dist/core/hearth/guest-env.js +11 -0
  237. package/dist/core/hearth/index.d.ts +5 -0
  238. package/dist/core/hearth/index.js +5 -0
  239. package/dist/core/hearth/provider.d.ts +21 -0
  240. package/dist/core/hearth/provider.js +10 -0
  241. package/dist/core/hearth/providers/blaxel-bootstrap.d.ts +10 -0
  242. package/dist/core/hearth/providers/blaxel-bootstrap.js +39 -0
  243. package/dist/core/hearth/providers/blaxel-home.d.ts +11 -0
  244. package/dist/core/hearth/providers/blaxel-home.js +126 -0
  245. package/dist/core/hearth/providers/blaxel.d.ts +26 -0
  246. package/dist/core/hearth/providers/blaxel.js +208 -0
  247. package/dist/core/hearth/providers/types.d.ts +72 -0
  248. package/dist/core/hearth/providers/types.js +1 -0
  249. package/dist/core/hearth/registry.d.ts +15 -0
  250. package/dist/core/hearth/registry.js +179 -0
  251. package/dist/core/hearth/types.d.ts +121 -0
  252. package/dist/core/hearth/types.js +1 -0
  253. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +1 -0
  254. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +79 -0
  255. package/dist/core/{skill-sync → host-exports}/builtins.d.ts +21 -13
  256. package/dist/core/host-exports/builtins.js +68 -0
  257. package/dist/core/{skill-sync → host-exports}/export.d.ts +9 -6
  258. package/dist/core/{skill-sync → host-exports}/export.js +37 -14
  259. package/dist/core/io.js +1 -1
  260. package/dist/core/log.d.ts +9 -0
  261. package/dist/core/log.js +113 -0
  262. package/dist/core/memory-resolver.d.ts +8 -8
  263. package/dist/core/memory-resolver.js +15 -28
  264. package/dist/core/personas/index.d.ts +4 -4
  265. package/dist/core/personas/index.js +2 -2
  266. package/dist/core/personas/loader.d.ts +51 -0
  267. package/dist/core/personas/loader.js +54 -50
  268. package/dist/core/personas/resolve.d.ts +43 -27
  269. package/dist/core/personas/resolve.js +336 -94
  270. package/dist/core/runtime/auth-reload.d.ts +7 -0
  271. package/dist/core/runtime/auth-reload.js +69 -0
  272. package/dist/core/runtime/bearings.d.ts +35 -15
  273. package/dist/core/runtime/bearings.js +305 -44
  274. package/dist/core/runtime/branded-host.d.ts +4 -2
  275. package/dist/core/runtime/branded-host.js +66 -4
  276. package/dist/core/runtime/broker-protocol.d.ts +38 -3
  277. package/dist/core/runtime/broker.js +343 -18
  278. package/dist/core/runtime/close.js +37 -29
  279. package/dist/core/runtime/connectivity.d.ts +12 -0
  280. package/dist/core/runtime/connectivity.js +73 -0
  281. package/dist/core/runtime/fault-recovery-nudge.d.ts +2 -0
  282. package/dist/core/runtime/fault-recovery-nudge.js +2 -0
  283. package/dist/core/runtime/fault-recovery.d.ts +23 -0
  284. package/dist/core/runtime/fault-recovery.js +74 -0
  285. package/dist/core/runtime/fault.d.ts +25 -0
  286. package/dist/core/runtime/fault.js +176 -0
  287. package/dist/core/runtime/front-door.d.ts +1 -1
  288. package/dist/core/runtime/front-door.js +2 -2
  289. package/dist/core/runtime/host.d.ts +10 -0
  290. package/dist/core/runtime/host.js +71 -9
  291. package/dist/core/runtime/kickoff.js +31 -45
  292. package/dist/core/runtime/launch.d.ts +32 -18
  293. package/dist/core/runtime/launch.js +148 -43
  294. package/dist/core/runtime/model-swap.d.ts +18 -0
  295. package/dist/core/runtime/model-swap.js +95 -0
  296. package/dist/core/runtime/naming.d.ts +7 -0
  297. package/dist/core/runtime/naming.js +61 -9
  298. package/dist/core/runtime/nodes.js +6 -1
  299. package/dist/core/runtime/persona.js +16 -17
  300. package/dist/core/runtime/placement.d.ts +36 -31
  301. package/dist/core/runtime/placement.js +149 -64
  302. package/dist/core/runtime/promote.d.ts +2 -0
  303. package/dist/core/runtime/promote.js +23 -3
  304. package/dist/core/runtime/recycle.js +6 -4
  305. package/dist/core/runtime/reset.d.ts +5 -2
  306. package/dist/core/runtime/reset.js +2 -2
  307. package/dist/core/runtime/revive.d.ts +4 -0
  308. package/dist/core/runtime/revive.js +27 -9
  309. package/dist/core/runtime/roadmap.d.ts +1 -1
  310. package/dist/core/runtime/roadmap.js +1 -1
  311. package/dist/core/runtime/spawn.d.ts +2 -2
  312. package/dist/core/runtime/spawn.js +30 -6
  313. package/dist/core/runtime/stop-guard.js +6 -1
  314. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  315. package/dist/core/runtime/tmux-chrome.js +1 -1
  316. package/dist/core/runtime/tmux.d.ts +29 -6
  317. package/dist/core/runtime/tmux.js +134 -80
  318. package/dist/core/runtime/view-socket-client.d.ts +46 -0
  319. package/dist/core/runtime/view-socket-client.js +203 -0
  320. package/dist/core/substrate/index.d.ts +1 -1
  321. package/dist/core/substrate/index.js +1 -1
  322. package/dist/core/substrate/on-read.js +16 -4
  323. package/dist/core/substrate/render.d.ts +10 -13
  324. package/dist/core/substrate/render.js +56 -51
  325. package/dist/core/tui/host.js +83 -18
  326. package/dist/core/view/__tests__/transport-cache.test.d.ts +1 -0
  327. package/dist/core/view/__tests__/transport-cache.test.js +62 -0
  328. package/dist/core/view/contract.d.ts +20 -0
  329. package/dist/core/view/stream-local.d.ts +3 -0
  330. package/dist/core/view/stream-local.js +184 -0
  331. package/dist/core/view/transport-cache.d.ts +8 -0
  332. package/dist/core/view/transport-cache.js +38 -0
  333. package/dist/core/view/transport-local.js +4 -0
  334. package/dist/core/view/transport.d.ts +7 -1
  335. package/dist/daemon/crtrd.d.ts +3 -19
  336. package/dist/daemon/crtrd.js +139 -178
  337. package/dist/daemon/manage.d.ts +18 -1
  338. package/dist/daemon/manage.js +54 -9
  339. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.d.ts +1 -0
  340. package/dist/hearth/wake-proxy/__tests__/anthropic-oauth.test.js +289 -0
  341. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.d.ts +1 -0
  342. package/dist/hearth/wake-proxy/__tests__/config-timeout.test.js +34 -0
  343. package/dist/hearth/wake-proxy/__tests__/guest-source.test.d.ts +1 -0
  344. package/dist/hearth/wake-proxy/__tests__/guest-source.test.js +203 -0
  345. package/dist/hearth/wake-proxy/__tests__/hardening.test.d.ts +1 -0
  346. package/dist/hearth/wake-proxy/__tests__/hardening.test.js +59 -0
  347. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.d.ts +1 -0
  348. package/dist/hearth/wake-proxy/__tests__/hearth-status-route.test.js +372 -0
  349. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.d.ts +1 -0
  350. package/dist/hearth/wake-proxy/__tests__/http-running-route-fast-path.test.js +258 -0
  351. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.d.ts +1 -0
  352. package/dist/hearth/wake-proxy/__tests__/model-auth-routes.test.js +437 -0
  353. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.d.ts +1 -0
  354. package/dist/hearth/wake-proxy/__tests__/source-bridge-gate.test.js +455 -0
  355. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.d.ts +1 -0
  356. package/dist/hearth/wake-proxy/__tests__/static-asset-allowlist.test.js +15 -0
  357. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.d.ts +1 -0
  358. package/dist/hearth/wake-proxy/__tests__/warm-path-concurrency.test.js +141 -0
  359. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.d.ts +1 -0
  360. package/dist/hearth/wake-proxy/__tests__/ws-teardown-accounting.test.js +143 -0
  361. package/dist/hearth/wake-proxy/anthropic-oauth.d.ts +58 -0
  362. package/dist/hearth/wake-proxy/anthropic-oauth.js +189 -0
  363. package/dist/hearth/wake-proxy/auth.d.ts +22 -0
  364. package/dist/hearth/wake-proxy/auth.js +128 -0
  365. package/dist/hearth/wake-proxy/config.d.ts +2 -0
  366. package/dist/hearth/wake-proxy/config.js +151 -0
  367. package/dist/hearth/wake-proxy/guest-source.d.ts +14 -0
  368. package/dist/hearth/wake-proxy/guest-source.js +130 -0
  369. package/dist/hearth/wake-proxy/hearth-status.d.ts +44 -0
  370. package/dist/hearth/wake-proxy/hearth-status.js +267 -0
  371. package/dist/hearth/wake-proxy/home.d.ts +67 -0
  372. package/dist/hearth/wake-proxy/home.js +297 -0
  373. package/dist/hearth/wake-proxy/main.d.ts +1 -0
  374. package/dist/hearth/wake-proxy/main.js +134 -0
  375. package/dist/hearth/wake-proxy/model-auth.d.ts +13 -0
  376. package/dist/hearth/wake-proxy/model-auth.js +345 -0
  377. package/dist/hearth/wake-proxy/proxy.d.ts +35 -0
  378. package/dist/hearth/wake-proxy/proxy.js +716 -0
  379. package/dist/hearth/wake-proxy/public-source-gate.d.ts +9 -0
  380. package/dist/hearth/wake-proxy/public-source-gate.js +409 -0
  381. package/dist/hearth/wake-proxy/redact.d.ts +1 -0
  382. package/dist/hearth/wake-proxy/redact.js +17 -0
  383. package/dist/hearth/wake-proxy/server.d.ts +11 -0
  384. package/dist/hearth/wake-proxy/server.js +142 -0
  385. package/dist/hearth/wake-proxy/state.d.ts +18 -0
  386. package/dist/hearth/wake-proxy/state.js +342 -0
  387. package/dist/hearth/wake-proxy/types.d.ts +76 -0
  388. package/dist/hearth/wake-proxy/types.js +1 -0
  389. package/dist/index.js +3 -2
  390. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +6 -5
  391. package/dist/pi-extensions/canvas-context-intro.js +7 -7
  392. package/dist/pi-extensions/canvas-doc-substrate.js +19 -22
  393. package/dist/pi-extensions/canvas-nav.js +15 -8
  394. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  395. package/dist/pi-extensions/canvas-stophook.js +78 -11
  396. package/dist/types.d.ts +39 -20
  397. package/dist/types.js +38 -13
  398. package/dist/web/runtime.js +141 -42
  399. package/dist/web/transport-http.js +7 -5
  400. package/dist/web/transport-stream.d.ts +3 -0
  401. package/dist/web/transport-stream.js +204 -0
  402. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  403. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  404. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  405. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  406. package/dist/web-client/assets/fraunces-latin-ext-wght-normal-Ca2vKHc0.woff2 +0 -0
  407. package/dist/web-client/assets/fraunces-latin-wght-normal-ukD16Tqj.woff2 +0 -0
  408. package/dist/web-client/assets/fraunces-vietnamese-wght-normal-CnvboYUG.woff2 +0 -0
  409. package/dist/web-client/assets/index-BZUxTkv5.css +2 -0
  410. package/dist/web-client/assets/index-D36PNBj4.js +80 -0
  411. package/dist/web-client/assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2 +0 -0
  412. package/dist/web-client/assets/instrument-sans-latin-wght-normal-BbzFLZTg.woff2 +0 -0
  413. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  414. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  415. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  416. package/dist/web-client/index.html +3 -2
  417. package/package.json +32 -10
  418. package/dist/core/__tests__/error-stall-recycle.test.js +0 -141
  419. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +0 -57
  420. package/dist/core/skill-sync/builtins.js +0 -112
  421. package/dist/core/skill-sync/claude-plugins.d.ts +0 -23
  422. package/dist/core/skill-sync/claude-plugins.js +0 -71
  423. package/dist/core/skill-sync/engine.d.ts +0 -42
  424. package/dist/core/skill-sync/engine.js +0 -633
  425. package/dist/core/skill-sync/manifest.d.ts +0 -64
  426. package/dist/core/skill-sync/manifest.js +0 -181
  427. package/dist/core/skill-sync/profile.d.ts +0 -76
  428. package/dist/core/skill-sync/profile.js +0 -173
  429. package/dist/core/skill-sync/snapshot.d.ts +0 -57
  430. package/dist/core/skill-sync/snapshot.js +0 -120
  431. package/dist/pi-extensions/canvas-commands.d.ts +0 -37
  432. package/dist/pi-extensions/canvas-commands.js +0 -113
  433. package/dist/pi-extensions/canvas-resume.d.ts +0 -21
  434. package/dist/pi-extensions/canvas-resume.js +0 -83
  435. package/dist/pi-extensions/canvas-view.d.ts +0 -21
  436. package/dist/pi-extensions/canvas-view.js +0 -76
  437. package/dist/web-client/assets/index-BUvQb4hR.css +0 -2
  438. package/dist/web-client/assets/index-ClLQXYAE.js +0 -10
  439. /package/dist/{core/__tests__/error-stall-recycle.test.d.ts → clients/attach/__tests__/autocomplete-and-bash-mode.test.d.ts} +0 -0
  440. /package/dist/{core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts → clients/attach/__tests__/bash-bang-routing.test.d.ts} +0 -0
@@ -1,18 +1,6 @@
1
1
  import { parse as parseYaml } from 'yaml';
2
- import { isSkillType } from '../types.js';
3
2
  const FRONTMATTER_RE = /^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n?/;
4
- export function parseFrontmatter(source) {
5
- const match = source.match(FRONTMATTER_RE);
6
- if (!match) {
7
- return { data: null, body: source, raw: '' };
8
- }
9
- const raw = match[1];
10
- const body = source.slice(match[0].length);
11
- return { data: toSkillFrontmatter(parseYamlBlock(raw)), body, raw };
12
- }
13
- /** Like parseFrontmatter but returns the raw key/value record instead of
14
- * coercing to SkillFrontmatter. Used by consumers (e.g. subagents) that read
15
- * fields skills don't declare, such as `tools` and `model`. */
3
+ /** Parse a frontmatter block as a raw key/value record. */
16
4
  export function parseFrontmatterGeneric(source) {
17
5
  const match = source.match(FRONTMATTER_RE);
18
6
  if (!match) {
@@ -22,26 +10,6 @@ export function parseFrontmatterGeneric(source) {
22
10
  const body = source.slice(match[0].length);
23
11
  return { data: parseYamlBlock(raw), body, raw };
24
12
  }
25
- function toSkillFrontmatter(out) {
26
- const fm = {
27
- name: scalarToString(out.name) ?? '',
28
- description: scalarToString(out.description),
29
- keywords: Array.isArray(out.keywords) ? out.keywords.map((k) => String(k)) : undefined,
30
- type: isSkillType(out.type) ? out.type : undefined,
31
- };
32
- return fm;
33
- }
34
- /** Coerce a YAML scalar (string/number/boolean) to its string form. A skill's
35
- * `name`/`description` are always strings to consumers; native non-string
36
- * scalars (a numeric/boolean value) render as their string form, and
37
- * non-scalars (null/undefined/array/object) yield undefined. */
38
- function scalarToString(v) {
39
- if (typeof v === 'string')
40
- return v;
41
- if (typeof v === 'number' || typeof v === 'boolean')
42
- return String(v);
43
- return undefined;
44
- }
45
13
  /** Parse the inner YAML of a frontmatter block into a key/value record.
46
14
  *
47
15
  * ONE strict parser, no fallback. The frontmatter contract IS "valid YAML":
@@ -65,25 +33,3 @@ function normalizeToRecord(parsed) {
65
33
  }
66
34
  return {};
67
35
  }
68
- export function serializeFrontmatter(data) {
69
- const lines = ['---'];
70
- lines.push(`name: ${quoteIfNeeded(data.name)}`);
71
- if (data.description !== undefined) {
72
- lines.push(`description: ${quoteIfNeeded(data.description)}`);
73
- }
74
- if (data.type !== undefined) {
75
- lines.push(`type: ${data.type}`);
76
- }
77
- if (data.keywords && data.keywords.length) {
78
- const inline = `[${data.keywords.map(quoteIfNeeded).join(', ')}]`;
79
- lines.push(`keywords: ${inline}`);
80
- }
81
- lines.push('---');
82
- return lines.join('\n') + '\n';
83
- }
84
- function quoteIfNeeded(s) {
85
- if (/[:#\-\[\]{},&*?|<>=!%@`]/.test(s) || /^\s/.test(s) || /\s$/.test(s)) {
86
- return `"${s.replace(/"/g, '\\"')}"`;
87
- }
88
- return s;
89
- }
@@ -0,0 +1,2 @@
1
+ import type { M0Config } from './types.js';
2
+ export declare function loadM0Config(env?: NodeJS.ProcessEnv): M0Config;
@@ -0,0 +1,75 @@
1
+ import { homedir } from 'node:os';
2
+ import { join, resolve } from 'node:path';
3
+ import { general } from '../errors.js';
4
+ const ENV = {
5
+ provider: 'CRTR_HEARTH_PROVIDER',
6
+ registry: 'CRTR_HEARTH_M0_REGISTRY',
7
+ guestUser: 'CRTR_HEARTH_M0_GUEST_USER',
8
+ guestHome: 'CRTR_HEARTH_M0_GUEST_HOME',
9
+ guestCode: 'CRTR_HEARTH_M0_GUEST_CODE',
10
+ webPort: 'CRTR_HEARTH_M0_WEB_PORT',
11
+ nodeKind: 'CRTR_HEARTH_M0_NODE_KIND',
12
+ piPackages: 'CRTR_HEARTH_M0_PI_PACKAGES',
13
+ piAuthJson: 'CRTR_HEARTH_M0_PI_AUTH_JSON',
14
+ relayToken: 'CRTR_HEARTH_M0_RELAY_TOKEN',
15
+ };
16
+ function expandHomePath(input) {
17
+ if (input.startsWith('~'))
18
+ return join(homedir(), input.slice(1));
19
+ return resolve(input);
20
+ }
21
+ function parsePort(value, fallback, name) {
22
+ if (value === undefined || value === '')
23
+ return fallback;
24
+ const parsed = Number(value);
25
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
26
+ throw general(`invalid ${name}: ${value}`, { env: name });
27
+ }
28
+ return parsed;
29
+ }
30
+ const SAFE_PI_PACKAGE_SPEC = /^[A-Za-z0-9@/_:.-]+$/;
31
+ const DEFAULT_PI_PACKAGES = ['npm:pi-claude-oauth-adapter@0.1.2'];
32
+ function parsePiPackages(env) {
33
+ const raw = env[ENV.piPackages]?.trim();
34
+ if (raw === undefined || raw === '')
35
+ return [...DEFAULT_PI_PACKAGES];
36
+ const tokens = raw.split(/[,\s]+/).map((token) => token.trim()).filter((token) => token !== '');
37
+ if (tokens.length === 0) {
38
+ throw general(`invalid ${ENV.piPackages}: ${raw}`, { env: ENV.piPackages });
39
+ }
40
+ if (tokens.length === 1 && tokens[0]?.toLowerCase() === 'none')
41
+ return [];
42
+ if (tokens.some((token) => token.toLowerCase() === 'none')) {
43
+ throw general(`invalid ${ENV.piPackages}: none must be the only value`, { env: ENV.piPackages });
44
+ }
45
+ for (const spec of tokens) {
46
+ if (!SAFE_PI_PACKAGE_SPEC.test(spec)) {
47
+ throw general(`invalid ${ENV.piPackages}: ${spec}`, { env: ENV.piPackages, packageSpec: spec });
48
+ }
49
+ }
50
+ return tokens;
51
+ }
52
+ export function loadM0Config(env = process.env) {
53
+ const providerName = (env[ENV.provider] ?? '').trim().toLowerCase();
54
+ const registryPath = expandHomePath(env[ENV.registry] ?? '~/.crouter/hearth/m0/homes.json');
55
+ const guestUser = (env[ENV.guestUser] ?? 'agent').trim();
56
+ const guestHome = expandHomePath(env[ENV.guestHome] ?? '/home/agent');
57
+ const guestCodePath = expandHomePath(env[ENV.guestCode] ?? '/opt/crouter');
58
+ const piPackages = parsePiPackages(env);
59
+ const webPort = parsePort(env[ENV.webPort], 7878, ENV.webPort);
60
+ const nodeKind = (env[ENV.nodeKind] ?? 'general').trim();
61
+ const piAuthJson = env[ENV.piAuthJson]?.trim() || null;
62
+ const relayToken = env[ENV.relayToken]?.trim() || null;
63
+ return {
64
+ providerName,
65
+ registryPath,
66
+ guestUser,
67
+ guestHome,
68
+ guestCodePath,
69
+ piPackages,
70
+ webPort,
71
+ nodeKind,
72
+ piAuthJson,
73
+ relayToken,
74
+ };
75
+ }
@@ -0,0 +1,2 @@
1
+ import type { M0Config } from './types.js';
2
+ export declare function guestEnv(config: M0Config): Record<string, string>;
@@ -0,0 +1,11 @@
1
+ import { join } from 'node:path';
2
+ export function guestEnv(config) {
3
+ const home = config.guestHome;
4
+ return {
5
+ HOME: home,
6
+ CRTR_HOME: join(home, '.crouter'),
7
+ PI_CODING_AGENT_DIR: join(home, '.pi', 'agent'),
8
+ PATH: `${join(home, '.crtr-runtime', 'bin')}:/usr/local/bin:/usr/bin:/bin`,
9
+ SANDBOX_DISABLE_PROCESS_LOGGING: 'true',
10
+ };
11
+ }
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { loadM0Config } from './config.js';
3
+ export { getHomeBackend, type HomeBackend } from './provider.js';
4
+ export { readRegistry, writeRegistry, getHome, putHome, updateHome, listHomes, acquireRegistryLock } from './registry.js';
5
+ export { guestEnv } from './guest-env.js';
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export { loadM0Config } from './config.js';
3
+ export { getHomeBackend } from './provider.js';
4
+ export { readRegistry, writeRegistry, getHome, putHome, updateHome, listHomes, acquireRegistryLock } from './registry.js';
5
+ export { guestEnv } from './guest-env.js';
@@ -0,0 +1,21 @@
1
+ import type { MachineId, MachineRef, M0Config, TenantId } from './types.js';
2
+ /**
3
+ * The Hearth home backend seam. The wake-proxy front door (token auth, origin checks,
4
+ * SSE/WS forwarding, the source gate) is vendor-neutral and drives a home purely through
5
+ * this interface — never a concrete sandbox SDK.
6
+ */
7
+ export interface HomeBackend {
8
+ /**
9
+ * Bring the tenant's home up and serving — warm-resume or recreate as the backend decides —
10
+ * refresh its public route, and persist `webRoute` + `relayToken` to the registry.
11
+ */
12
+ wake(tenantId: TenantId): Promise<void>;
13
+ /** Pause the tenant's home. */
14
+ suspend(tenantId: TenantId): Promise<void>;
15
+ /** Machine resources (cpu/mem) for status, or null when unavailable. */
16
+ getMachine(machineId: MachineId): Promise<MachineRef | null>;
17
+ }
18
+ /**
19
+ * Resolve the configured home backend.
20
+ */
21
+ export declare function getHomeBackend(config: M0Config): HomeBackend;
@@ -0,0 +1,10 @@
1
+ import { general } from '../errors.js';
2
+ import { BlaxelHomeBackend } from './providers/blaxel-home.js';
3
+ /**
4
+ * Resolve the configured home backend.
5
+ */
6
+ export function getHomeBackend(config) {
7
+ if (config.providerName === 'blaxel')
8
+ return new BlaxelHomeBackend(config);
9
+ throw general('no Hearth home provider configured — Blaxel backend pending', { status: 503 });
10
+ }
@@ -0,0 +1,10 @@
1
+ export interface BuildBootstrapScriptOpts {
2
+ guestHome: string;
3
+ crtrVersion: string;
4
+ relayToken: string;
5
+ webPort: number;
6
+ nodeId?: string | null;
7
+ piAuthJson?: string | null;
8
+ isFirstProvision: boolean;
9
+ }
10
+ export declare function buildBootstrapScript(opts: BuildBootstrapScriptOpts): string;
@@ -0,0 +1,39 @@
1
+ import { join } from 'node:path';
2
+ function shellQuote(value) {
3
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
4
+ }
5
+ export function buildBootstrapScript(opts) {
6
+ const runtimeDir = join(opts.guestHome, '.crtr-runtime');
7
+ const versionMarker = join(runtimeDir, '.crtr-version');
8
+ const lines = [
9
+ 'set -e',
10
+ `export HOME=${shellQuote(opts.guestHome)}`,
11
+ 'export CRTR_HOME="$HOME/.crouter"',
12
+ 'export PI_CODING_AGENT_DIR="$HOME/.pi/agent"',
13
+ 'export PATH="$HOME/.crtr-runtime/bin:/usr/local/bin:/usr/bin:/bin"',
14
+ 'export SANDBOX_DISABLE_PROCESS_LOGGING=true',
15
+ 'mkdir -p "$HOME/.crouter" "$HOME/.pi/agent" "$HOME/.crtr-runtime/bin"',
16
+ // The provisioner launches this script as a detached (background) exec, so its stdout is not
17
+ // captured. Redirect everything to a log on the durable volume so install/seed/node-new
18
+ // failures are inspectable, and so the first-provision node id can be read from a file (below).
19
+ 'exec >>"$HOME/.crtr-runtime/bootstrap.log" 2>&1',
20
+ `if [ ! -x "$HOME/.crtr-runtime/bin/crtr" ] || [ "$(cat ${shellQuote(versionMarker)} 2>/dev/null || true)" != ${shellQuote(opts.crtrVersion)} ]; then`,
21
+ ` npm install -g --prefix ${shellQuote(runtimeDir)} @crouton-kit/crouter@${shellQuote(opts.crtrVersion)}`,
22
+ ` printf '%s\n' ${shellQuote(opts.crtrVersion)} > ${shellQuote(versionMarker)}`,
23
+ 'fi',
24
+ ];
25
+ if (opts.isFirstProvision && (opts.piAuthJson?.trim() ?? '') !== '') {
26
+ lines.push('if [ ! -f "$PI_CODING_AGENT_DIR/auth.json" ]; then', ' cat > "$PI_CODING_AGENT_DIR/auth.json" <<\'PI_AUTH_JSON\'', opts.piAuthJson.trim(), 'PI_AUTH_JSON', 'fi');
27
+ }
28
+ lines.push('setsid --fork crtr canvas daemon start </dev/null >>"$HOME/.crtr-runtime/daemon.log" 2>&1');
29
+ if (opts.isFirstProvision) {
30
+ lines.push('home_node_output="$(', "crtr node new --root --kind general --name home <<'SEED'", 'Hearth home seed for the Blaxel home backend.', 'SEED', ')"', 'home_node_id="$(printf %s\\n "$home_node_output" | grep -Eo \'__HEARTH_NODE_ID__=[^[:space:]]+|node_id[:=][[:space:]]*[^[:space:]]+|node_id[[:space:]]+[A-Za-z0-9-]+\' | tail -n 1 | sed -E \'s/^(__HEARTH_NODE_ID__=|node_id[:=][[:space:]]*|node_id[[:space:]]+)//\')"', 'if [ -z "$home_node_id" ]; then', ' printf "failed to parse home node id from crtr node new output\n%s\n" "$home_node_output" >&2', ' exit 1', 'fi', 'printf "__HEARTH_NODE_ID__=%s\n" "$home_node_id"',
31
+ // The provisioner polls this file (background exec stdout is not captured).
32
+ 'printf "%s\n" "$home_node_id" > "$HOME/.crtr-runtime/home-node-id"');
33
+ }
34
+ else {
35
+ lines.push('if [ -z ' + shellQuote((opts.nodeId ?? '').trim()) + ' ]; then', ' printf "missing nodeId for bootstrap revive\n" >&2', ' exit 1', 'fi', `crtr canvas revive ${shellQuote((opts.nodeId ?? '').trim())}`);
36
+ }
37
+ lines.push(`setsid --fork crtr web serve --host 0.0.0.0 --port ${opts.webPort} --token ${shellQuote(opts.relayToken)} </dev/null >>"$HOME/.crtr-runtime/web.log" 2>&1`);
38
+ return lines.join('\n');
39
+ }
@@ -0,0 +1,11 @@
1
+ import type { M0Config, MachineRef, TenantId } from '../types.js';
2
+ import { BlaxelMachineProvider } from './blaxel.js';
3
+ export declare class BlaxelHomeBackend {
4
+ private readonly config;
5
+ private readonly provider;
6
+ private readonly crtrVersion;
7
+ constructor(config: M0Config, provider?: BlaxelMachineProvider);
8
+ wake(tenantId: TenantId): Promise<void>;
9
+ suspend(tenantId: TenantId): Promise<void>;
10
+ getMachine(machineId: string): Promise<MachineRef | null>;
11
+ }
@@ -0,0 +1,126 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { general } from '../../errors.js';
3
+ import { nowIso } from '../../fs-utils.js';
4
+ import { getHome, updateHome } from '../registry.js';
5
+ import { guestEnv } from '../guest-env.js';
6
+ import { buildBootstrapScript } from './blaxel-bootstrap.js';
7
+ import { BlaxelMachineProvider } from './blaxel.js';
8
+ function readCrtrVersion() {
9
+ const file = new URL('../../../../package.json', import.meta.url);
10
+ const pkg = JSON.parse(readFileSync(file, 'utf8'));
11
+ if (typeof pkg.version !== 'string' || pkg.version.trim() === '') {
12
+ throw general('missing crtr package version', { packageJson: file.pathname });
13
+ }
14
+ return pkg.version;
15
+ }
16
+ function stableRouteName(home) {
17
+ return home.webRoute?.routeId ?? `${home.machineId}-web`;
18
+ }
19
+ function homeStorage(home, config) {
20
+ return {
21
+ kind: 'volume',
22
+ durability: 'durable-volume',
23
+ longTermContract: true,
24
+ ownerMachineId: home.machineId,
25
+ guestHome: config.guestHome,
26
+ volumeId: home.homeStorage.volumeId,
27
+ mountPath: config.guestHome,
28
+ };
29
+ }
30
+ function buildHomeRecord(home, config, route, timestamps) {
31
+ return {
32
+ ...home,
33
+ providerName: 'blaxel',
34
+ homeStorage: homeStorage(home, config),
35
+ webRoute: route,
36
+ state: 'running',
37
+ createdAt: timestamps.createdAt ?? home.createdAt,
38
+ updatedAt: timestamps.updatedAt,
39
+ };
40
+ }
41
+ function ensureReadyHome(home, tenantId) {
42
+ if (home === null) {
43
+ throw general(`no home for tenant ${tenantId} — run the existing provision/bootstrap flow`, { tenantId });
44
+ }
45
+ if (typeof home.machineId !== 'string' || home.machineId === '') {
46
+ throw general(`home ${tenantId} is missing machineId — run the existing provision/bootstrap flow`, { tenantId });
47
+ }
48
+ if (typeof home.homeStorage?.volumeId !== 'string' || home.homeStorage.volumeId === '') {
49
+ throw general(`home ${tenantId} is missing homeStorage.volumeId — run the existing provision/bootstrap flow`, { tenantId });
50
+ }
51
+ if (typeof home.nodeId !== 'string' || home.nodeId === '') {
52
+ throw general(`home ${tenantId} is missing nodeId — run the existing provision/bootstrap flow`, { tenantId });
53
+ }
54
+ if (typeof home.relayToken !== 'string' || home.relayToken === '') {
55
+ throw general(`home ${tenantId} is missing relayToken — run the existing provision/bootstrap flow`, { tenantId });
56
+ }
57
+ return home;
58
+ }
59
+ function isRunningMachine(machine) {
60
+ return machine !== null && machine.state !== 'terminated';
61
+ }
62
+ export class BlaxelHomeBackend {
63
+ config;
64
+ provider;
65
+ crtrVersion;
66
+ constructor(config, provider) {
67
+ this.config = config;
68
+ this.provider = provider ?? new BlaxelMachineProvider();
69
+ this.crtrVersion = readCrtrVersion();
70
+ }
71
+ async wake(tenantId) {
72
+ const current = ensureReadyHome(getHome(this.config.registryPath, tenantId), tenantId);
73
+ const machine = await this.getMachine(current.machineId);
74
+ const relayToken = current.relayToken ?? '';
75
+ if (relayToken === '') {
76
+ throw general(`home ${tenantId} is missing relayToken — run the existing provision/bootstrap flow`, { tenantId });
77
+ }
78
+ const bootstrapScript = buildBootstrapScript({
79
+ guestHome: this.config.guestHome,
80
+ crtrVersion: this.crtrVersion,
81
+ relayToken,
82
+ webPort: this.config.webPort,
83
+ nodeId: current.nodeId ?? undefined,
84
+ piAuthJson: this.config.piAuthJson,
85
+ isFirstProvision: false,
86
+ });
87
+ if (isRunningMachine(machine)) {
88
+ try {
89
+ await this.provider.waitForPort(current.machineId, this.config.webPort, { maxWaitMs: 30_000 });
90
+ }
91
+ catch {
92
+ await this.provider.exec(current.machineId, { command: bootstrapScript, background: true, cwd: this.config.guestHome });
93
+ await this.provider.waitForPort(current.machineId, this.config.webPort, { maxWaitMs: 30_000 });
94
+ }
95
+ }
96
+ else {
97
+ await this.provider.createMachine({
98
+ tenantId,
99
+ name: current.machineId,
100
+ envs: guestEnv(this.config),
101
+ volumeMount: { volumeName: current.homeStorage.volumeId, path: this.config.guestHome },
102
+ ports: [{ target: this.config.webPort, protocol: 'HTTP' }],
103
+ });
104
+ await this.provider.exec(current.machineId, { command: bootstrapScript, background: true, cwd: this.config.guestHome });
105
+ await this.provider.waitForPort(current.machineId, this.config.webPort, { maxWaitMs: 60_000 });
106
+ }
107
+ const route = await this.provider.publishHttpPort(current.machineId, {
108
+ machineId: current.machineId,
109
+ targetPort: this.config.webPort,
110
+ routeName: stableRouteName(current),
111
+ routeId: stableRouteName(current),
112
+ });
113
+ const updatedAt = nowIso();
114
+ updateHome(this.config.registryPath, tenantId, () => buildHomeRecord({ ...current, webRoute: route, relayToken }, this.config, route, { createdAt: current.createdAt, updatedAt }));
115
+ }
116
+ async suspend(tenantId) {
117
+ updateHome(this.config.registryPath, tenantId, (draft) => {
118
+ draft.updatedAt = nowIso();
119
+ return draft;
120
+ });
121
+ }
122
+ async getMachine(machineId) {
123
+ const machine = await this.provider.getMachine(machineId);
124
+ return machine.state === 'terminated' ? null : machine;
125
+ }
126
+ }
@@ -0,0 +1,26 @@
1
+ import type { MachineRef, RouteRef } from '../types.js';
2
+ import type { CreateMachineInput, DestroyOutcome, ExecInput, ExecResult, HealthResult, MachineProvider, PublishHttpPortInput, VolumeRef } from './types.js';
3
+ interface BlaxelMachineProviderOptions {
4
+ region?: string;
5
+ memory?: number;
6
+ image?: string;
7
+ }
8
+ export declare class BlaxelMachineProvider implements MachineProvider {
9
+ readonly providerName: "blaxel";
10
+ private readonly config;
11
+ constructor(options?: BlaxelMachineProviderOptions);
12
+ createVolume(input: {
13
+ name: string;
14
+ }): Promise<VolumeRef>;
15
+ createMachine(input: CreateMachineInput): Promise<MachineRef>;
16
+ getMachine(machineId: string): Promise<MachineRef>;
17
+ exec(machineId: string, input: ExecInput): Promise<ExecResult>;
18
+ waitForPort(machineId: string, port: number, opts?: {
19
+ maxWaitMs?: number;
20
+ }): Promise<void>;
21
+ publishHttpPort(machineId: string, input: PublishHttpPortInput): Promise<RouteRef>;
22
+ health(): Promise<HealthResult>;
23
+ destroy(machineId: string): Promise<DestroyOutcome>;
24
+ destroyVolume(volumeId: string): Promise<DestroyOutcome>;
25
+ }
26
+ export {};
@@ -0,0 +1,208 @@
1
+ import { SandboxInstance, VolumeInstance } from '@blaxel/core';
2
+ import { general } from '../../errors.js';
3
+ const TERMINATED_STATES = new Set(['TERMINATED', 'DELETING', 'DEACTIVATED', 'DEACTIVATING', 'FAILED']);
4
+ const PROVISIONING_STATES = new Set(['UPLOADING', 'BUILDING', 'DEPLOYING', 'BUILT']);
5
+ function shStatus(status) {
6
+ if (status === undefined || status === null || status === '')
7
+ return 'unknown';
8
+ const normalized = status.toUpperCase();
9
+ if (normalized === 'DEPLOYED')
10
+ return 'running';
11
+ if (normalized === 'RUNNING' || normalized === 'STANDBY')
12
+ return 'running';
13
+ if (TERMINATED_STATES.has(normalized))
14
+ return 'terminated';
15
+ if (PROVISIONING_STATES.has(normalized))
16
+ return 'provisioning';
17
+ if (normalized === 'PAUSED' || normalized === 'SUSPENDED')
18
+ return 'paused';
19
+ return status;
20
+ }
21
+ function record(value) {
22
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
23
+ return null;
24
+ return value;
25
+ }
26
+ function rawFromSandbox(sandbox) {
27
+ return {
28
+ metadata: sandbox.metadata,
29
+ status: sandbox.status ?? null,
30
+ spec: sandbox.spec,
31
+ };
32
+ }
33
+ function rawFromVolume(volume) {
34
+ return {
35
+ metadata: volume.metadata,
36
+ status: volume.status ?? null,
37
+ spec: volume.spec,
38
+ };
39
+ }
40
+ function toEnvVars(envs) {
41
+ if (envs === undefined)
42
+ return undefined;
43
+ return Object.entries(envs)
44
+ .sort(([a], [b]) => a.localeCompare(b))
45
+ .map(([name, value]) => ({ name, value }));
46
+ }
47
+ function isMissingResourceError(error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ return /\b(not found|404|does not exist|missing)\b/i.test(message);
50
+ }
51
+ function toMachineRef(sandbox) {
52
+ const metadata = sandbox.metadata;
53
+ const status = sandbox.status ?? undefined;
54
+ return {
55
+ providerName: 'blaxel',
56
+ machineId: metadata.name,
57
+ state: shStatus(status),
58
+ updatedAt: metadata.updatedAt ?? null,
59
+ raw: rawFromSandbox(sandbox),
60
+ };
61
+ }
62
+ function toRouteRef(machineId, targetPort, preview, raw) {
63
+ const url = preview.spec.url ?? null;
64
+ return {
65
+ routeId: preview.name,
66
+ machineId,
67
+ targetPort,
68
+ url,
69
+ concreteUrl: url,
70
+ concreteUrlSource: 'provider',
71
+ raw: raw ?? null,
72
+ };
73
+ }
74
+ export class BlaxelMachineProvider {
75
+ providerName = 'blaxel';
76
+ config;
77
+ constructor(options = {}) {
78
+ this.config = {
79
+ region: options.region ?? 'us-west-2',
80
+ memory: options.memory ?? 2048,
81
+ // blaxel/prod-base:latest is NOT a supported Blaxel sandbox image (400 INVALID_IMAGE,
82
+ // confirmed live 2026-06-29). blaxel/node:latest ships node+npm on Alpine/musl, which the
83
+ // in-guest `npm i -g @crouton-kit/crouter` needs.
84
+ image: options.image ?? 'blaxel/node:latest',
85
+ };
86
+ }
87
+ async createVolume(input) {
88
+ const volume = await VolumeInstance.createIfNotExists({ name: input.name, size: 1024, region: this.config.region });
89
+ return {
90
+ volumeId: volume.metadata.name ?? input.name,
91
+ name: input.name,
92
+ raw: rawFromVolume(volume),
93
+ };
94
+ }
95
+ async createMachine(input) {
96
+ const sandbox = await SandboxInstance.createIfNotExists({
97
+ name: input.name ?? input.tenantId,
98
+ image: this.config.image,
99
+ memory: this.config.memory,
100
+ region: this.config.region,
101
+ volumes: input.volumeMount === undefined || input.volumeMount === null ? undefined : [{ name: input.volumeMount.volumeName, mountPath: input.volumeMount.path, readOnly: false }],
102
+ envs: toEnvVars(input.envs),
103
+ ports: input.ports?.map((port) => ({ target: port.target, protocol: port.protocol })),
104
+ });
105
+ await sandbox.wait({ maxWait: 90_000, interval: 500 }).catch(() => sandbox);
106
+ return toMachineRef(sandbox);
107
+ }
108
+ async getMachine(machineId) {
109
+ try {
110
+ const sandbox = await SandboxInstance.get(machineId);
111
+ return toMachineRef(sandbox);
112
+ }
113
+ catch (error) {
114
+ if (!isMissingResourceError(error))
115
+ throw error;
116
+ return {
117
+ providerName: 'blaxel',
118
+ machineId,
119
+ state: 'terminated',
120
+ raw: { error: error instanceof Error ? error.message : String(error) },
121
+ };
122
+ }
123
+ }
124
+ async exec(machineId, input) {
125
+ const sandbox = await SandboxInstance.get(machineId);
126
+ const response = await sandbox.process.exec({
127
+ command: input.command,
128
+ env: input.env,
129
+ timeout: Math.ceil((input.timeoutMs ?? 55_000) / 1000),
130
+ workingDir: input.cwd,
131
+ ...(input.background === true ? { waitForCompletion: false } : { waitForCompletion: true }),
132
+ });
133
+ const result = record(response) ?? {};
134
+ return {
135
+ exitCode: typeof result.exitCode === 'number' ? result.exitCode : null,
136
+ timedOut: false,
137
+ stdout: typeof result.stdout === 'string' ? result.stdout : typeof result.logs === 'string' ? result.logs : '',
138
+ stderr: typeof result.stderr === 'string' ? result.stderr : '',
139
+ raw: result,
140
+ };
141
+ }
142
+ async waitForPort(machineId, port, opts) {
143
+ const sandbox = await SandboxInstance.get(machineId);
144
+ try {
145
+ await sandbox.process.exec({
146
+ command: 'true',
147
+ waitForPorts: [port],
148
+ waitForCompletion: true,
149
+ timeout: Math.ceil((opts?.maxWaitMs ?? 60_000) / 1000),
150
+ });
151
+ }
152
+ catch (error) {
153
+ throw general(`blaxel sandbox ${machineId} never opened port ${port}`, {
154
+ machineId,
155
+ port,
156
+ cause: error instanceof Error ? error.message : String(error),
157
+ });
158
+ }
159
+ }
160
+ async publishHttpPort(machineId, input) {
161
+ const sandbox = await SandboxInstance.get(machineId);
162
+ const previewName = input.routeName ?? input.routeId ?? `${machineId}-http`;
163
+ const preview = await sandbox.previews.createIfNotExists({
164
+ metadata: { name: previewName },
165
+ spec: { port: input.targetPort, public: true },
166
+ });
167
+ return toRouteRef(machineId, input.targetPort, { name: preview.name, spec: preview.spec }, {
168
+ preview: { metadata: preview.metadata, spec: preview.spec },
169
+ input,
170
+ });
171
+ }
172
+ async health() {
173
+ try {
174
+ await VolumeInstance.list();
175
+ return { ok: true, providerName: 'blaxel' };
176
+ }
177
+ catch (error) {
178
+ return {
179
+ ok: false,
180
+ providerName: 'blaxel',
181
+ status: error instanceof Error ? error.message : String(error),
182
+ raw: { error: error instanceof Error ? error.message : String(error) },
183
+ };
184
+ }
185
+ }
186
+ async destroy(machineId) {
187
+ try {
188
+ await SandboxInstance.delete(machineId);
189
+ return { kind: 'destroyed' };
190
+ }
191
+ catch (error) {
192
+ if (isMissingResourceError(error))
193
+ return { kind: 'not_found' };
194
+ throw error;
195
+ }
196
+ }
197
+ async destroyVolume(volumeId) {
198
+ try {
199
+ await VolumeInstance.delete(volumeId);
200
+ return { kind: 'destroyed' };
201
+ }
202
+ catch (error) {
203
+ if (isMissingResourceError(error))
204
+ return { kind: 'not_found' };
205
+ throw error;
206
+ }
207
+ }
208
+ }