smol-symphony 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (716) hide show
  1. package/AGENTS.md +41 -22
  2. package/DESIGN.md +494 -273
  3. package/README.md +109 -57
  4. package/SPEC.md +33 -24
  5. package/WORKFLOW.minimal.yaml +34 -0
  6. package/{WORKFLOW.template.md → WORKFLOW.template.yaml} +409 -256
  7. package/WORKFLOW.yaml +487 -0
  8. package/assets/skills/symphony-issues/SKILL.md +136 -0
  9. package/assets/symphony-mise.system.toml +68 -0
  10. package/dist/src/bin/symphony.js +30 -0
  11. package/dist/src/bin/symphony.js.map +1 -0
  12. package/dist/src/core/actions/context.js +109 -0
  13. package/dist/src/core/actions/context.js.map +1 -0
  14. package/dist/{actions/parsing.js → src/core/actions/parse.js} +33 -114
  15. package/dist/src/core/actions/parse.js.map +1 -0
  16. package/dist/src/core/actions/plan.js +197 -0
  17. package/dist/src/core/actions/plan.js.map +1 -0
  18. package/dist/src/core/actions/predicates.js +111 -0
  19. package/dist/src/core/actions/predicates.js.map +1 -0
  20. package/dist/src/core/actions/run-fold.js +248 -0
  21. package/dist/src/core/actions/run-fold.js.map +1 -0
  22. package/dist/src/core/actions/template.js +118 -0
  23. package/dist/src/core/actions/template.js.map +1 -0
  24. package/dist/src/core/cli/args.js +116 -0
  25. package/dist/src/core/cli/args.js.map +1 -0
  26. package/dist/src/core/coerce.js +75 -0
  27. package/dist/src/core/coerce.js.map +1 -0
  28. package/dist/src/core/credential/account-id.js +20 -0
  29. package/dist/src/core/credential/account-id.js.map +1 -0
  30. package/dist/src/core/credential/adapter-config.js +136 -0
  31. package/dist/src/core/credential/adapter-config.js.map +1 -0
  32. package/dist/src/core/credential/availability.js +98 -0
  33. package/dist/src/core/credential/availability.js.map +1 -0
  34. package/dist/src/core/credential/extract.js +228 -0
  35. package/dist/src/core/credential/extract.js.map +1 -0
  36. package/dist/src/core/credential/fake-creds.js +171 -0
  37. package/dist/src/core/credential/fake-creds.js.map +1 -0
  38. package/dist/src/core/credential/identity.js +125 -0
  39. package/dist/src/core/credential/identity.js.map +1 -0
  40. package/dist/src/core/credential/shape.js +230 -0
  41. package/dist/src/core/credential/shape.js.map +1 -0
  42. package/dist/src/core/credential/strings.js +15 -0
  43. package/dist/src/core/credential/strings.js.map +1 -0
  44. package/dist/src/core/doctor/checks.js +303 -0
  45. package/dist/src/core/doctor/checks.js.map +1 -0
  46. package/dist/src/core/git/result.js +107 -0
  47. package/dist/src/core/git/result.js.map +1 -0
  48. package/dist/src/core/http/decisions.js +225 -0
  49. package/dist/src/core/http/decisions.js.map +1 -0
  50. package/dist/{http.js → src/core/http/render.js} +472 -738
  51. package/dist/src/core/http/render.js.map +1 -0
  52. package/dist/{http-handlers.js → src/core/http/routes.js} +52 -87
  53. package/dist/src/core/http/routes.js.map +1 -0
  54. package/dist/src/core/http/views.js +181 -0
  55. package/dist/src/core/http/views.js.map +1 -0
  56. package/dist/src/core/image/managed-image.js +95 -0
  57. package/dist/src/core/image/managed-image.js.map +1 -0
  58. package/dist/src/core/issue/file.js +149 -0
  59. package/dist/src/core/issue/file.js.map +1 -0
  60. package/dist/src/core/issue/parse.js +210 -0
  61. package/dist/src/core/issue/parse.js.map +1 -0
  62. package/dist/src/core/mcp/dispatch.js +239 -0
  63. package/dist/src/core/mcp/dispatch.js.map +1 -0
  64. package/dist/src/core/mcp/post-move.js +92 -0
  65. package/dist/src/core/mcp/post-move.js.map +1 -0
  66. package/dist/src/core/mcp/protocol.js +293 -0
  67. package/dist/src/core/mcp/protocol.js.map +1 -0
  68. package/dist/src/core/mcp/url.js +162 -0
  69. package/dist/src/core/mcp/url.js.map +1 -0
  70. package/dist/src/core/path.js +63 -0
  71. package/dist/src/core/path.js.map +1 -0
  72. package/dist/src/core/reconcile/image-decide.js +48 -0
  73. package/dist/src/core/reconcile/image-decide.js.map +1 -0
  74. package/dist/src/core/reconcile/ledger.js +142 -0
  75. package/dist/src/core/reconcile/ledger.js.map +1 -0
  76. package/dist/src/core/reconcile/pr-classify.js +62 -0
  77. package/dist/src/core/reconcile/pr-classify.js.map +1 -0
  78. package/dist/{reconciler → src/core/reconcile}/pr-decide.js +25 -12
  79. package/dist/src/core/reconcile/pr-decide.js.map +1 -0
  80. package/dist/src/core/reconcile/pr-loop.js +161 -0
  81. package/dist/src/core/reconcile/pr-loop.js.map +1 -0
  82. package/dist/src/core/reconcile/pr-notes.js +35 -0
  83. package/dist/src/core/reconcile/pr-notes.js.map +1 -0
  84. package/dist/src/core/reconcile/vm-decide.js +70 -0
  85. package/dist/src/core/reconcile/vm-decide.js.map +1 -0
  86. package/dist/src/core/reconcile/vm-reap.js +207 -0
  87. package/dist/src/core/reconcile/vm-reap.js.map +1 -0
  88. package/dist/src/core/reconcile/workspace-decide.js +162 -0
  89. package/dist/src/core/reconcile/workspace-decide.js.map +1 -0
  90. package/dist/src/core/runlog/summary.js +231 -0
  91. package/dist/src/core/runlog/summary.js.map +1 -0
  92. package/dist/src/core/runner/dispatch-config.js +95 -0
  93. package/dist/src/core/runner/dispatch-config.js.map +1 -0
  94. package/dist/src/core/runner/injection.js +61 -0
  95. package/dist/src/core/runner/injection.js.map +1 -0
  96. package/dist/src/core/runner/mise.js +210 -0
  97. package/dist/src/core/runner/mise.js.map +1 -0
  98. package/dist/src/core/runner/prompt.js +720 -0
  99. package/dist/src/core/runner/prompt.js.map +1 -0
  100. package/dist/src/core/runner/turn.js +242 -0
  101. package/dist/src/core/runner/turn.js.map +1 -0
  102. package/dist/src/core/runner/vm-plan.js +390 -0
  103. package/dist/src/core/runner/vm-plan.js.map +1 -0
  104. package/dist/src/core/schedule/admission.js +123 -0
  105. package/dist/src/core/schedule/admission.js.map +1 -0
  106. package/dist/src/core/schedule/circuit-breaker.js +111 -0
  107. package/dist/src/core/schedule/circuit-breaker.js.map +1 -0
  108. package/dist/src/core/schedule/eligibility.js +83 -0
  109. package/dist/src/core/schedule/eligibility.js.map +1 -0
  110. package/dist/src/core/schedule/reconcile-issue.js +82 -0
  111. package/dist/src/core/schedule/reconcile-issue.js.map +1 -0
  112. package/dist/src/core/schedule/retry.js +96 -0
  113. package/dist/src/core/schedule/retry.js.map +1 -0
  114. package/dist/src/core/schedule/sleep-cycle.js +133 -0
  115. package/dist/src/core/schedule/sleep-cycle.js.map +1 -0
  116. package/dist/src/core/schedule/slots.js +124 -0
  117. package/dist/src/core/schedule/slots.js.map +1 -0
  118. package/dist/src/core/schedule/tick.js +553 -0
  119. package/dist/src/core/schedule/tick.js.map +1 -0
  120. package/dist/src/core/schedule/token-fold.js +181 -0
  121. package/dist/src/core/schedule/token-fold.js.map +1 -0
  122. package/dist/src/core/state-resolve.js +86 -0
  123. package/dist/src/core/state-resolve.js.map +1 -0
  124. package/dist/src/core/vm-guards.js +278 -0
  125. package/dist/src/core/vm-guards.js.map +1 -0
  126. package/dist/src/core/workflow/derive.js +107 -0
  127. package/dist/src/core/workflow/derive.js.map +1 -0
  128. package/dist/src/core/workflow/parse.js +687 -0
  129. package/dist/src/core/workflow/parse.js.map +1 -0
  130. package/dist/src/core/workflow/prompt-probe.js +78 -0
  131. package/dist/src/core/workflow/prompt-probe.js.map +1 -0
  132. package/dist/src/core/workflow/validate.js +189 -0
  133. package/dist/src/core/workflow/validate.js.map +1 -0
  134. package/dist/src/core/workspace-key.js +19 -0
  135. package/dist/src/core/workspace-key.js.map +1 -0
  136. package/dist/src/shell/actions-runner.js +356 -0
  137. package/dist/src/shell/actions-runner.js.map +1 -0
  138. package/dist/src/shell/adapter/adapter-registry.js +45 -0
  139. package/dist/src/shell/adapter/adapter-registry.js.map +1 -0
  140. package/dist/src/shell/adapter/clock-random.js +96 -0
  141. package/dist/src/shell/adapter/clock-random.js.map +1 -0
  142. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js +158 -0
  143. package/dist/src/shell/adapter/gondolin-dispatch-helpers.js.map +1 -0
  144. package/dist/src/shell/adapter/gondolin-dispatch.js +385 -0
  145. package/dist/src/shell/adapter/gondolin-dispatch.js.map +1 -0
  146. package/dist/src/shell/adapter/gondolin-image-converter.js +233 -0
  147. package/dist/src/shell/adapter/gondolin-image-converter.js.map +1 -0
  148. package/dist/src/shell/adapter/gondolin-image-fetch.js +180 -0
  149. package/dist/src/shell/adapter/gondolin-image-fetch.js.map +1 -0
  150. package/dist/src/shell/adapter/launcher-asset.js +57 -0
  151. package/dist/src/shell/adapter/launcher-asset.js.map +1 -0
  152. package/dist/src/shell/adapter/mise-config-asset.js +65 -0
  153. package/dist/src/shell/adapter/mise-config-asset.js.map +1 -0
  154. package/dist/src/shell/adapter/workflow-loader.js +304 -0
  155. package/dist/src/shell/adapter/workflow-loader.js.map +1 -0
  156. package/dist/src/shell/cli/doctor.js +268 -0
  157. package/dist/src/shell/cli/doctor.js.map +1 -0
  158. package/dist/src/shell/effect-interpreter-families.js +314 -0
  159. package/dist/src/shell/effect-interpreter-families.js.map +1 -0
  160. package/dist/src/shell/effect-interpreter.js +29 -0
  161. package/dist/src/shell/effect-interpreter.js.map +1 -0
  162. package/dist/src/shell/interp/acp-frame.js +137 -0
  163. package/dist/src/shell/interp/acp-frame.js.map +1 -0
  164. package/dist/src/shell/interp/acp-ws-conn.js +320 -0
  165. package/dist/src/shell/interp/acp-ws-conn.js.map +1 -0
  166. package/dist/src/shell/interp/acp-ws-frames.js +159 -0
  167. package/dist/src/shell/interp/acp-ws-frames.js.map +1 -0
  168. package/dist/src/shell/interp/acp-ws.js +197 -0
  169. package/dist/src/shell/interp/acp-ws.js.map +1 -0
  170. package/dist/src/shell/interp/acp.js +319 -0
  171. package/dist/src/shell/interp/acp.js.map +1 -0
  172. package/dist/src/shell/interp/credential-defaults.js +128 -0
  173. package/dist/src/shell/interp/credential-defaults.js.map +1 -0
  174. package/dist/src/shell/interp/credential-hooks.js +149 -0
  175. package/dist/src/shell/interp/credential-hooks.js.map +1 -0
  176. package/dist/src/shell/interp/credential-registry.js +226 -0
  177. package/dist/src/shell/interp/credential-registry.js.map +1 -0
  178. package/dist/src/shell/interp/credential.js +103 -0
  179. package/dist/src/shell/interp/credential.js.map +1 -0
  180. package/dist/src/shell/interp/gh.js +163 -0
  181. package/dist/src/shell/interp/gh.js.map +1 -0
  182. package/dist/src/shell/interp/git.js +28 -0
  183. package/dist/src/shell/interp/git.js.map +1 -0
  184. package/dist/src/shell/interp/log.js +213 -0
  185. package/dist/src/shell/interp/log.js.map +1 -0
  186. package/dist/src/shell/interp/process.js +178 -0
  187. package/dist/src/shell/interp/process.js.map +1 -0
  188. package/dist/src/shell/interp/runlog.js +193 -0
  189. package/dist/src/shell/interp/runlog.js.map +1 -0
  190. package/dist/src/shell/interp/timer.js +64 -0
  191. package/dist/src/shell/interp/timer.js.map +1 -0
  192. package/dist/src/shell/interp/tracker-disk.js +99 -0
  193. package/dist/src/shell/interp/tracker-disk.js.map +1 -0
  194. package/dist/src/shell/interp/tracker-parse.js +71 -0
  195. package/dist/src/shell/interp/tracker-parse.js.map +1 -0
  196. package/dist/src/shell/interp/tracker-scan.js +238 -0
  197. package/dist/src/shell/interp/tracker-scan.js.map +1 -0
  198. package/dist/src/shell/interp/tracker-write.js +91 -0
  199. package/dist/src/shell/interp/tracker-write.js.map +1 -0
  200. package/dist/src/shell/interp/tracker.js +41 -0
  201. package/dist/src/shell/interp/tracker.js.map +1 -0
  202. package/dist/src/shell/interp/tty.js +48 -0
  203. package/dist/src/shell/interp/tty.js.map +1 -0
  204. package/dist/src/shell/interp/vm.js +199 -0
  205. package/dist/src/shell/interp/vm.js.map +1 -0
  206. package/dist/src/shell/interp/workspace.js +310 -0
  207. package/dist/src/shell/interp/workspace.js.map +1 -0
  208. package/dist/src/shell/main-acp.js +78 -0
  209. package/dist/src/shell/main-acp.js.map +1 -0
  210. package/dist/src/shell/main-adapters.js +222 -0
  211. package/dist/src/shell/main-adapters.js.map +1 -0
  212. package/dist/src/shell/main-credential.js +122 -0
  213. package/dist/src/shell/main-credential.js.map +1 -0
  214. package/dist/src/shell/main-doctor.js +22 -0
  215. package/dist/src/shell/main-doctor.js.map +1 -0
  216. package/dist/src/shell/main-entry.js +46 -0
  217. package/dist/src/shell/main-entry.js.map +1 -0
  218. package/dist/src/shell/main-http-csrf.js +45 -0
  219. package/dist/src/shell/main-http-csrf.js.map +1 -0
  220. package/dist/src/shell/main-http-handler.js +389 -0
  221. package/dist/src/shell/main-http-handler.js.map +1 -0
  222. package/dist/src/shell/main-http-mcp.js +122 -0
  223. package/dist/src/shell/main-http-mcp.js.map +1 -0
  224. package/dist/src/shell/main-http-views.js +253 -0
  225. package/dist/src/shell/main-http-views.js.map +1 -0
  226. package/dist/src/shell/main-http.js +76 -0
  227. package/dist/src/shell/main-http.js.map +1 -0
  228. package/dist/src/shell/main-loops.js +130 -0
  229. package/dist/src/shell/main-loops.js.map +1 -0
  230. package/dist/src/shell/main-mcp.js +129 -0
  231. package/dist/src/shell/main-mcp.js.map +1 -0
  232. package/dist/src/shell/main-orchestrator.js +120 -0
  233. package/dist/src/shell/main-orchestrator.js.map +1 -0
  234. package/dist/src/shell/main-preflight.js +43 -0
  235. package/dist/src/shell/main-preflight.js.map +1 -0
  236. package/dist/src/shell/main-reconcilers-helpers.js +244 -0
  237. package/dist/src/shell/main-reconcilers-helpers.js.map +1 -0
  238. package/dist/src/shell/main-reconcilers-pr.js +148 -0
  239. package/dist/src/shell/main-reconcilers-pr.js.map +1 -0
  240. package/dist/src/shell/main-reconcilers.js +225 -0
  241. package/dist/src/shell/main-reconcilers.js.map +1 -0
  242. package/dist/src/shell/main-runner.js +355 -0
  243. package/dist/src/shell/main-runner.js.map +1 -0
  244. package/dist/src/shell/main-scaffold.js +116 -0
  245. package/dist/src/shell/main-scaffold.js.map +1 -0
  246. package/dist/src/shell/main-shutdown.js +115 -0
  247. package/dist/src/shell/main-shutdown.js.map +1 -0
  248. package/dist/src/shell/main-startup.js +48 -0
  249. package/dist/src/shell/main-startup.js.map +1 -0
  250. package/dist/src/shell/main-substrates.js +43 -0
  251. package/dist/src/shell/main-substrates.js.map +1 -0
  252. package/dist/src/shell/main.js +385 -0
  253. package/dist/src/shell/main.js.map +1 -0
  254. package/dist/src/shell/orchestrator-feedback.js +69 -0
  255. package/dist/src/shell/orchestrator-feedback.js.map +1 -0
  256. package/dist/src/shell/orchestrator-image.js +167 -0
  257. package/dist/src/shell/orchestrator-image.js.map +1 -0
  258. package/dist/src/shell/orchestrator-loop.js +468 -0
  259. package/dist/src/shell/orchestrator-loop.js.map +1 -0
  260. package/dist/src/shell/orchestrator-reconcile.js +36 -0
  261. package/dist/src/shell/orchestrator-reconcile.js.map +1 -0
  262. package/dist/src/shell/reconciler-loop.js +228 -0
  263. package/dist/src/shell/reconciler-loop.js.map +1 -0
  264. package/dist/src/shell/runner-loop-turn.js +301 -0
  265. package/dist/src/shell/runner-loop-turn.js.map +1 -0
  266. package/dist/src/shell/runner-loop.js +338 -0
  267. package/dist/src/shell/runner-loop.js.map +1 -0
  268. package/dist/src/shell/server/http.js +208 -0
  269. package/dist/src/shell/server/http.js.map +1 -0
  270. package/dist/src/shell/server/mcp-runtime-effects.js +237 -0
  271. package/dist/src/shell/server/mcp-runtime-effects.js.map +1 -0
  272. package/dist/src/shell/server/mcp-runtime.js +99 -0
  273. package/dist/src/shell/server/mcp-runtime.js.map +1 -0
  274. package/dist/src/shell/workspace-key.js +14 -0
  275. package/dist/src/shell/workspace-key.js.map +1 -0
  276. package/dist/src/types/acp.js +8 -0
  277. package/dist/src/types/acp.js.map +1 -0
  278. package/dist/src/types/actions/plan.js +6 -0
  279. package/dist/src/types/actions/plan.js.map +1 -0
  280. package/dist/src/types/actions/predicates.js +6 -0
  281. package/dist/src/types/actions/predicates.js.map +1 -0
  282. package/dist/src/types/actions/run-fold.js +8 -0
  283. package/dist/src/types/actions/run-fold.js.map +1 -0
  284. package/dist/src/types/actions.js +7 -0
  285. package/dist/src/types/actions.js.map +1 -0
  286. package/dist/src/types/adapter/clock-random.js +4 -0
  287. package/dist/src/types/adapter/clock-random.js.map +1 -0
  288. package/dist/src/types/adapter/gondolin-image-converter.js +5 -0
  289. package/dist/src/types/adapter/gondolin-image-converter.js.map +1 -0
  290. package/dist/src/types/adapter/gondolin-image-fetch.js +5 -0
  291. package/dist/src/types/adapter/gondolin-image-fetch.js.map +1 -0
  292. package/dist/src/types/adapter/workflow-loader.js +4 -0
  293. package/dist/src/types/adapter/workflow-loader.js.map +1 -0
  294. package/dist/src/types/cli/args.js +8 -0
  295. package/dist/src/types/cli/args.js.map +1 -0
  296. package/dist/src/types/config.js +8 -0
  297. package/dist/src/types/config.js.map +1 -0
  298. package/dist/src/types/credential-interp.js +6 -0
  299. package/dist/src/types/credential-interp.js.map +1 -0
  300. package/dist/src/types/credentials.js +10 -0
  301. package/dist/src/types/credentials.js.map +1 -0
  302. package/dist/src/types/doctor.js +7 -0
  303. package/dist/src/types/doctor.js.map +1 -0
  304. package/dist/src/types/domain.js +7 -0
  305. package/dist/src/types/domain.js.map +1 -0
  306. package/dist/src/types/effect.js +15 -0
  307. package/dist/src/types/effect.js.map +1 -0
  308. package/dist/src/types/errors.js +39 -0
  309. package/dist/src/types/errors.js.map +1 -0
  310. package/dist/src/types/http/decisions.js +6 -0
  311. package/dist/src/types/http/decisions.js.map +1 -0
  312. package/dist/src/types/http/render.js +10 -0
  313. package/dist/src/types/http/render.js.map +1 -0
  314. package/dist/src/types/http/views.js +6 -0
  315. package/dist/src/types/http/views.js.map +1 -0
  316. package/dist/src/types/http.js +9 -0
  317. package/dist/src/types/http.js.map +1 -0
  318. package/dist/src/types/image/managed-image.js +7 -0
  319. package/dist/src/types/image/managed-image.js.map +1 -0
  320. package/dist/src/types/interp/effect-interpreter.js +8 -0
  321. package/dist/src/types/interp/effect-interpreter.js.map +1 -0
  322. package/dist/src/types/interp/tracker.js +7 -0
  323. package/dist/src/types/interp/tracker.js.map +1 -0
  324. package/dist/src/types/issue/file.js +6 -0
  325. package/dist/src/types/issue/file.js.map +1 -0
  326. package/dist/src/types/issue/parse.js +8 -0
  327. package/dist/src/types/issue/parse.js.map +1 -0
  328. package/dist/src/types/main-acp.js +13 -0
  329. package/dist/src/types/main-acp.js.map +1 -0
  330. package/dist/src/types/main-adapters.js +5 -0
  331. package/dist/src/types/main-adapters.js.map +1 -0
  332. package/dist/src/types/main-credential.js +21 -0
  333. package/dist/src/types/main-credential.js.map +1 -0
  334. package/dist/src/types/main-doctor.js +6 -0
  335. package/dist/src/types/main-doctor.js.map +1 -0
  336. package/dist/src/types/main-http-handler.js +12 -0
  337. package/dist/src/types/main-http-handler.js.map +1 -0
  338. package/dist/src/types/main-http.js +5 -0
  339. package/dist/src/types/main-http.js.map +1 -0
  340. package/dist/src/types/main-loops.js +5 -0
  341. package/dist/src/types/main-loops.js.map +1 -0
  342. package/dist/src/types/main-mcp.js +12 -0
  343. package/dist/src/types/main-mcp.js.map +1 -0
  344. package/dist/src/types/main-orchestrator.js +5 -0
  345. package/dist/src/types/main-orchestrator.js.map +1 -0
  346. package/dist/src/types/main-reconcilers.js +11 -0
  347. package/dist/src/types/main-reconcilers.js.map +1 -0
  348. package/dist/src/types/main-runner.js +13 -0
  349. package/dist/src/types/main-runner.js.map +1 -0
  350. package/dist/src/types/main-startup.js +5 -0
  351. package/dist/src/types/main-startup.js.map +1 -0
  352. package/dist/src/types/main-substrates.js +5 -0
  353. package/dist/src/types/main-substrates.js.map +1 -0
  354. package/dist/src/types/mcp/dispatch.js +4 -0
  355. package/dist/src/types/mcp/dispatch.js.map +1 -0
  356. package/dist/src/types/mcp/post-move.js +7 -0
  357. package/dist/src/types/mcp/post-move.js.map +1 -0
  358. package/dist/src/types/mcp.js +9 -0
  359. package/dist/src/types/mcp.js.map +1 -0
  360. package/dist/src/types/ports.js +12 -0
  361. package/dist/src/types/ports.js.map +1 -0
  362. package/dist/src/types/reconcile/image-decide.js +5 -0
  363. package/dist/src/types/reconcile/image-decide.js.map +1 -0
  364. package/dist/src/types/reconcile/ledger.js +7 -0
  365. package/dist/src/types/reconcile/ledger.js.map +1 -0
  366. package/dist/src/types/reconcile/pr-loop.js +8 -0
  367. package/dist/src/types/reconcile/pr-loop.js.map +1 -0
  368. package/dist/src/types/reconcile/vm-reap.js +8 -0
  369. package/dist/src/types/reconcile/vm-reap.js.map +1 -0
  370. package/dist/src/types/reconcile/workspace-decide.js +7 -0
  371. package/dist/src/types/reconcile/workspace-decide.js.map +1 -0
  372. package/dist/src/types/reconcile.js +9 -0
  373. package/dist/src/types/reconcile.js.map +1 -0
  374. package/dist/src/types/runlog.js +7 -0
  375. package/dist/src/types/runlog.js.map +1 -0
  376. package/dist/src/types/runner/actions-runner.js +12 -0
  377. package/dist/src/types/runner/actions-runner.js.map +1 -0
  378. package/dist/src/types/runner/gondolin-dispatch.js +5 -0
  379. package/dist/src/types/runner/gondolin-dispatch.js.map +1 -0
  380. package/dist/src/types/runner/injection.js +6 -0
  381. package/dist/src/types/runner/injection.js.map +1 -0
  382. package/dist/src/types/runner/runner-loop.js +5 -0
  383. package/dist/src/types/runner/runner-loop.js.map +1 -0
  384. package/dist/src/types/runner/turn.js +4 -0
  385. package/dist/src/types/runner/turn.js.map +1 -0
  386. package/dist/src/types/runner/vm-plan.js +4 -0
  387. package/dist/src/types/runner/vm-plan.js.map +1 -0
  388. package/dist/src/types/runtime.js +9 -0
  389. package/dist/src/types/runtime.js.map +1 -0
  390. package/dist/src/types/schedule/admission.js +7 -0
  391. package/dist/src/types/schedule/admission.js.map +1 -0
  392. package/dist/src/types/schedule/circuit-breaker.js +2 -0
  393. package/dist/src/types/schedule/circuit-breaker.js.map +1 -0
  394. package/dist/src/types/schedule/eligibility.js +9 -0
  395. package/dist/src/types/schedule/eligibility.js.map +1 -0
  396. package/dist/src/types/schedule/orchestrator-loop.js +10 -0
  397. package/dist/src/types/schedule/orchestrator-loop.js.map +1 -0
  398. package/dist/src/types/schedule/sleep-cycle.js +4 -0
  399. package/dist/src/types/schedule/sleep-cycle.js.map +1 -0
  400. package/dist/src/types/schedule/slots.js +8 -0
  401. package/dist/src/types/schedule/slots.js.map +1 -0
  402. package/dist/src/types/schedule/tick.js +9 -0
  403. package/dist/src/types/schedule/tick.js.map +1 -0
  404. package/dist/src/types/server/mcp-runtime.js +8 -0
  405. package/dist/src/types/server/mcp-runtime.js.map +1 -0
  406. package/dist/src/types/workflow/parse.js +4 -0
  407. package/dist/src/types/workflow/parse.js.map +1 -0
  408. package/dist/tests/core/account-id.test.js +35 -0
  409. package/dist/tests/core/account-id.test.js.map +1 -0
  410. package/dist/tests/core/actions-parse.test.js +176 -0
  411. package/dist/tests/core/actions-parse.test.js.map +1 -0
  412. package/dist/tests/core/adapter-config.test.js +133 -0
  413. package/dist/tests/core/adapter-config.test.js.map +1 -0
  414. package/dist/tests/core/admission.test.js +215 -0
  415. package/dist/tests/core/admission.test.js.map +1 -0
  416. package/dist/tests/core/args.test.js +132 -0
  417. package/dist/tests/core/args.test.js.map +1 -0
  418. package/dist/tests/core/availability.test.js +62 -0
  419. package/dist/tests/core/availability.test.js.map +1 -0
  420. package/dist/tests/core/checks.test.js +395 -0
  421. package/dist/tests/core/checks.test.js.map +1 -0
  422. package/dist/tests/core/circuit-breaker.test.js +172 -0
  423. package/dist/tests/core/circuit-breaker.test.js.map +1 -0
  424. package/dist/tests/core/coerce.test.js +87 -0
  425. package/dist/tests/core/coerce.test.js.map +1 -0
  426. package/dist/tests/core/context.test.js +228 -0
  427. package/dist/tests/core/context.test.js.map +1 -0
  428. package/dist/tests/core/decisions.test.js +310 -0
  429. package/dist/tests/core/decisions.test.js.map +1 -0
  430. package/dist/tests/core/derive.test.js +205 -0
  431. package/dist/tests/core/derive.test.js.map +1 -0
  432. package/dist/tests/core/dispatch-config.test.js +164 -0
  433. package/dist/tests/core/dispatch-config.test.js.map +1 -0
  434. package/dist/tests/core/dispatch.test.js +302 -0
  435. package/dist/tests/core/dispatch.test.js.map +1 -0
  436. package/dist/tests/core/eligibility.test.js +163 -0
  437. package/dist/tests/core/eligibility.test.js.map +1 -0
  438. package/dist/tests/core/extract.test.js +139 -0
  439. package/dist/tests/core/extract.test.js.map +1 -0
  440. package/dist/tests/core/fake-creds.test.js +134 -0
  441. package/dist/tests/core/fake-creds.test.js.map +1 -0
  442. package/dist/tests/core/file.test.js +197 -0
  443. package/dist/tests/core/file.test.js.map +1 -0
  444. package/dist/tests/core/git-result.test.js +113 -0
  445. package/dist/tests/core/git-result.test.js.map +1 -0
  446. package/dist/tests/core/identity.test.js +180 -0
  447. package/dist/tests/core/identity.test.js.map +1 -0
  448. package/dist/tests/core/image-decide.test.js +59 -0
  449. package/dist/tests/core/image-decide.test.js.map +1 -0
  450. package/dist/tests/core/injection.test.js +163 -0
  451. package/dist/tests/core/injection.test.js.map +1 -0
  452. package/dist/tests/core/ledger.test.js +218 -0
  453. package/dist/tests/core/ledger.test.js.map +1 -0
  454. package/dist/tests/core/managed-image.test.js +68 -0
  455. package/dist/tests/core/managed-image.test.js.map +1 -0
  456. package/dist/tests/core/mise.test.js +138 -0
  457. package/dist/tests/core/mise.test.js.map +1 -0
  458. package/dist/tests/core/parse.test.js +174 -0
  459. package/dist/tests/core/parse.test.js.map +1 -0
  460. package/dist/tests/core/path.test.js +50 -0
  461. package/dist/tests/core/path.test.js.map +1 -0
  462. package/dist/tests/core/plan.test.js +218 -0
  463. package/dist/tests/core/plan.test.js.map +1 -0
  464. package/dist/tests/core/post-move.test.js +162 -0
  465. package/dist/tests/core/post-move.test.js.map +1 -0
  466. package/dist/tests/core/pr-classify.test.js +117 -0
  467. package/dist/tests/core/pr-classify.test.js.map +1 -0
  468. package/dist/tests/core/pr-decide.test.js +298 -0
  469. package/dist/tests/core/pr-decide.test.js.map +1 -0
  470. package/dist/tests/core/pr-loop.test.js +301 -0
  471. package/dist/tests/core/pr-loop.test.js.map +1 -0
  472. package/dist/tests/core/pr-notes.test.js +165 -0
  473. package/dist/tests/core/pr-notes.test.js.map +1 -0
  474. package/dist/tests/core/predicates.test.js +154 -0
  475. package/dist/tests/core/predicates.test.js.map +1 -0
  476. package/dist/tests/core/prompt.test.js +189 -0
  477. package/dist/tests/core/prompt.test.js.map +1 -0
  478. package/dist/tests/core/protocol.test.js +195 -0
  479. package/dist/tests/core/protocol.test.js.map +1 -0
  480. package/dist/tests/core/reconcile-issue.test.js +116 -0
  481. package/dist/tests/core/reconcile-issue.test.js.map +1 -0
  482. package/dist/tests/core/render.test.js +549 -0
  483. package/dist/tests/core/render.test.js.map +1 -0
  484. package/dist/tests/core/retry.test.js +186 -0
  485. package/dist/tests/core/retry.test.js.map +1 -0
  486. package/dist/tests/core/routes.test.js +247 -0
  487. package/dist/tests/core/routes.test.js.map +1 -0
  488. package/dist/tests/core/run-fold.test.js +299 -0
  489. package/dist/tests/core/run-fold.test.js.map +1 -0
  490. package/dist/tests/core/shape.test.js +185 -0
  491. package/dist/tests/core/shape.test.js.map +1 -0
  492. package/dist/tests/core/sleep-cycle.test.js +150 -0
  493. package/dist/tests/core/sleep-cycle.test.js.map +1 -0
  494. package/dist/tests/core/slots.test.js +201 -0
  495. package/dist/tests/core/slots.test.js.map +1 -0
  496. package/dist/tests/core/state-resolve.test.js +80 -0
  497. package/dist/tests/core/state-resolve.test.js.map +1 -0
  498. package/dist/tests/core/summary.test.js +200 -0
  499. package/dist/tests/core/summary.test.js.map +1 -0
  500. package/dist/tests/core/template.test.js +116 -0
  501. package/dist/tests/core/template.test.js.map +1 -0
  502. package/dist/tests/core/tick.test.js +558 -0
  503. package/dist/tests/core/tick.test.js.map +1 -0
  504. package/dist/tests/core/token-fold.test.js +176 -0
  505. package/dist/tests/core/token-fold.test.js.map +1 -0
  506. package/dist/tests/core/turn.test.js +388 -0
  507. package/dist/tests/core/turn.test.js.map +1 -0
  508. package/dist/tests/core/url.test.js +118 -0
  509. package/dist/tests/core/url.test.js.map +1 -0
  510. package/dist/tests/core/validate.test.js +247 -0
  511. package/dist/tests/core/validate.test.js.map +1 -0
  512. package/dist/tests/core/views.test.js +252 -0
  513. package/dist/tests/core/views.test.js.map +1 -0
  514. package/dist/tests/core/vm-decide.test.js +110 -0
  515. package/dist/tests/core/vm-decide.test.js.map +1 -0
  516. package/dist/tests/core/vm-guards.test.js +153 -0
  517. package/dist/tests/core/vm-guards.test.js.map +1 -0
  518. package/dist/tests/core/vm-plan.test.js +332 -0
  519. package/dist/tests/core/vm-plan.test.js.map +1 -0
  520. package/dist/tests/core/vm-reap.test.js +196 -0
  521. package/dist/tests/core/vm-reap.test.js.map +1 -0
  522. package/dist/tests/core/workflow-parse.test.js +493 -0
  523. package/dist/tests/core/workflow-parse.test.js.map +1 -0
  524. package/dist/tests/core/workspace-decide.test.js +236 -0
  525. package/dist/tests/core/workspace-decide.test.js.map +1 -0
  526. package/dist/tests/helpers/fixtures.js +167 -0
  527. package/dist/tests/helpers/fixtures.js.map +1 -0
  528. package/dist/tests/shell/acp-substrate.test.js +101 -0
  529. package/dist/tests/shell/acp-substrate.test.js.map +1 -0
  530. package/dist/tests/shell/actions-runner-push.test.js +203 -0
  531. package/dist/tests/shell/actions-runner-push.test.js.map +1 -0
  532. package/dist/tests/shell/credential-hooks.test.js +36 -0
  533. package/dist/tests/shell/credential-hooks.test.js.map +1 -0
  534. package/dist/tests/shell/credential-registry.test.js +165 -0
  535. package/dist/tests/shell/credential-registry.test.js.map +1 -0
  536. package/dist/tests/shell/credential-substrate.test.js +179 -0
  537. package/dist/tests/shell/credential-substrate.test.js.map +1 -0
  538. package/dist/tests/shell/dockerfile-mise-pin.test.js +51 -0
  539. package/dist/tests/shell/dockerfile-mise-pin.test.js.map +1 -0
  540. package/dist/tests/shell/doctor.test.js +101 -0
  541. package/dist/tests/shell/doctor.test.js.map +1 -0
  542. package/dist/tests/shell/effect-vm-create.test.js +52 -0
  543. package/dist/tests/shell/effect-vm-create.test.js.map +1 -0
  544. package/dist/tests/shell/gh-port.test.js +63 -0
  545. package/dist/tests/shell/gh-port.test.js.map +1 -0
  546. package/dist/tests/shell/gondolin-dispatch-guard.test.js +144 -0
  547. package/dist/tests/shell/gondolin-dispatch-guard.test.js.map +1 -0
  548. package/dist/tests/shell/gondolin-dispatch-shquote.test.js +168 -0
  549. package/dist/tests/shell/gondolin-dispatch-shquote.test.js.map +1 -0
  550. package/dist/tests/shell/gondolin-image-converter.test.js +208 -0
  551. package/dist/tests/shell/gondolin-image-converter.test.js.map +1 -0
  552. package/dist/tests/shell/gondolin-image-fetch.test.js +93 -0
  553. package/dist/tests/shell/gondolin-image-fetch.test.js.map +1 -0
  554. package/dist/tests/shell/http-handler.test.js +608 -0
  555. package/dist/tests/shell/http-handler.test.js.map +1 -0
  556. package/dist/tests/shell/http-server.test.js +53 -0
  557. package/dist/tests/shell/http-server.test.js.map +1 -0
  558. package/dist/tests/shell/mcp-runtime.test.js +366 -0
  559. package/dist/tests/shell/mcp-runtime.test.js.map +1 -0
  560. package/dist/tests/shell/mise-config-asset.test.js +87 -0
  561. package/dist/tests/shell/mise-config-asset.test.js.map +1 -0
  562. package/dist/tests/shell/orchestrator-loop.test.js +583 -0
  563. package/dist/tests/shell/orchestrator-loop.test.js.map +1 -0
  564. package/dist/tests/shell/reconciler-passes.test.js +314 -0
  565. package/dist/tests/shell/reconciler-passes.test.js.map +1 -0
  566. package/dist/tests/shell/runner-loop-turn.test.js +97 -0
  567. package/dist/tests/shell/runner-loop-turn.test.js.map +1 -0
  568. package/dist/tests/shell/runner-slice.test.js +536 -0
  569. package/dist/tests/shell/runner-slice.test.js.map +1 -0
  570. package/dist/tests/shell/scaffold.test.js +65 -0
  571. package/dist/tests/shell/scaffold.test.js.map +1 -0
  572. package/dist/tests/shell/tick-config.test.js +83 -0
  573. package/dist/tests/shell/tick-config.test.js.map +1 -0
  574. package/dist/tests/shell/tracker-parse-dates.test.js +44 -0
  575. package/dist/tests/shell/tracker-parse-dates.test.js.map +1 -0
  576. package/dist/tests/shell/tracker-write-issue.test.js +154 -0
  577. package/dist/tests/shell/tracker-write-issue.test.js.map +1 -0
  578. package/dist/tests/shell/workflow-prompt-split.test.js +208 -0
  579. package/dist/tests/shell/workflow-prompt-split.test.js.map +1 -0
  580. package/dist/tests/shell/workspace-live-config.test.js +140 -0
  581. package/dist/tests/shell/workspace-live-config.test.js.map +1 -0
  582. package/package.json +21 -9
  583. package/patches/@earendil-works+gondolin+0.12.0.patch +173 -0
  584. package/prompts/Reflect.md +91 -0
  585. package/prompts/Review.md +97 -0
  586. package/prompts/Todo.md +96 -0
  587. package/prompts/_footer.md +41 -0
  588. package/prompts/_preamble.md +42 -0
  589. package/prompts-minimal/Todo.md +26 -0
  590. package/scripts/postinstall.mjs +63 -0
  591. package/scripts/vm-agent.mjs +312 -90
  592. package/WORKFLOW.md +0 -744
  593. package/dist/acp-bridge.js +0 -324
  594. package/dist/acp-bridge.js.map +0 -1
  595. package/dist/actions/cache.js +0 -191
  596. package/dist/actions/cache.js.map +0 -1
  597. package/dist/actions/effects.js +0 -41
  598. package/dist/actions/effects.js.map +0 -1
  599. package/dist/actions/executor.js +0 -570
  600. package/dist/actions/executor.js.map +0 -1
  601. package/dist/actions/index.js +0 -13
  602. package/dist/actions/index.js.map +0 -1
  603. package/dist/actions/parsing.js.map +0 -1
  604. package/dist/actions/predicate-env.js +0 -27
  605. package/dist/actions/predicate-env.js.map +0 -1
  606. package/dist/actions/predicates.js +0 -49
  607. package/dist/actions/predicates.js.map +0 -1
  608. package/dist/actions/templating.js +0 -66
  609. package/dist/actions/templating.js.map +0 -1
  610. package/dist/actions/types.js +0 -15
  611. package/dist/actions/types.js.map +0 -1
  612. package/dist/agent/acp.js +0 -473
  613. package/dist/agent/acp.js.map +0 -1
  614. package/dist/agent/adapter-names.js +0 -159
  615. package/dist/agent/adapter-names.js.map +0 -1
  616. package/dist/agent/adapters.js +0 -511
  617. package/dist/agent/adapters.js.map +0 -1
  618. package/dist/agent/credential-extractors.js +0 -342
  619. package/dist/agent/credential-extractors.js.map +0 -1
  620. package/dist/agent/credential-secrets.js +0 -628
  621. package/dist/agent/credential-secrets.js.map +0 -1
  622. package/dist/agent/credential-ticker.js +0 -57
  623. package/dist/agent/credential-ticker.js.map +0 -1
  624. package/dist/agent/gondolin-creds-staging.js +0 -356
  625. package/dist/agent/gondolin-creds-staging.js.map +0 -1
  626. package/dist/agent/gondolin-dispatch.js +0 -375
  627. package/dist/agent/gondolin-dispatch.js.map +0 -1
  628. package/dist/agent/gondolin.js +0 -124
  629. package/dist/agent/gondolin.js.map +0 -1
  630. package/dist/agent/runner-decisions.js +0 -134
  631. package/dist/agent/runner-decisions.js.map +0 -1
  632. package/dist/agent/runner.js +0 -1456
  633. package/dist/agent/runner.js.map +0 -1
  634. package/dist/agent/tool-call-summary.js +0 -102
  635. package/dist/agent/tool-call-summary.js.map +0 -1
  636. package/dist/agent/vm-acp-mapping.js +0 -73
  637. package/dist/agent/vm-acp-mapping.js.map +0 -1
  638. package/dist/agent/vm-guards.js +0 -262
  639. package/dist/agent/vm-guards.js.map +0 -1
  640. package/dist/agent/vm-port.js +0 -22
  641. package/dist/agent/vm-port.js.map +0 -1
  642. package/dist/agent/vm-process-registry.js +0 -79
  643. package/dist/agent/vm-process-registry.js.map +0 -1
  644. package/dist/bin/cli-args.js +0 -105
  645. package/dist/bin/cli-args.js.map +0 -1
  646. package/dist/bin/symphony.js +0 -794
  647. package/dist/bin/symphony.js.map +0 -1
  648. package/dist/errors.js +0 -15
  649. package/dist/errors.js.map +0 -1
  650. package/dist/http-disk.js +0 -135
  651. package/dist/http-disk.js.map +0 -1
  652. package/dist/http-handlers.js.map +0 -1
  653. package/dist/http.js.map +0 -1
  654. package/dist/issues.js +0 -178
  655. package/dist/issues.js.map +0 -1
  656. package/dist/logging.js +0 -203
  657. package/dist/logging.js.map +0 -1
  658. package/dist/mcp.js +0 -706
  659. package/dist/mcp.js.map +0 -1
  660. package/dist/memory.js +0 -85
  661. package/dist/memory.js.map +0 -1
  662. package/dist/orchestrator-decisions.js +0 -331
  663. package/dist/orchestrator-decisions.js.map +0 -1
  664. package/dist/orchestrator.js +0 -1569
  665. package/dist/orchestrator.js.map +0 -1
  666. package/dist/prompt.js +0 -65
  667. package/dist/prompt.js.map +0 -1
  668. package/dist/reconciler/cache.js +0 -65
  669. package/dist/reconciler/cache.js.map +0 -1
  670. package/dist/reconciler/index.js +0 -448
  671. package/dist/reconciler/index.js.map +0 -1
  672. package/dist/reconciler/ledger.js +0 -131
  673. package/dist/reconciler/ledger.js.map +0 -1
  674. package/dist/reconciler/pr-adapters.js +0 -174
  675. package/dist/reconciler/pr-adapters.js.map +0 -1
  676. package/dist/reconciler/pr-decide.js.map +0 -1
  677. package/dist/reconciler/pr.js +0 -422
  678. package/dist/reconciler/pr.js.map +0 -1
  679. package/dist/reconciler/types.js +0 -12
  680. package/dist/reconciler/types.js.map +0 -1
  681. package/dist/reconciler/vm.js +0 -243
  682. package/dist/reconciler/vm.js.map +0 -1
  683. package/dist/reconciler/workspace-defaults.js +0 -83
  684. package/dist/reconciler/workspace-defaults.js.map +0 -1
  685. package/dist/reconciler/workspace.js +0 -272
  686. package/dist/reconciler/workspace.js.map +0 -1
  687. package/dist/runlog.js +0 -403
  688. package/dist/runlog.js.map +0 -1
  689. package/dist/scaffold.js +0 -165
  690. package/dist/scaffold.js.map +0 -1
  691. package/dist/trackers/local.js +0 -445
  692. package/dist/trackers/local.js.map +0 -1
  693. package/dist/trackers/types.js +0 -10
  694. package/dist/trackers/types.js.map +0 -1
  695. package/dist/types.js +0 -3
  696. package/dist/types.js.map +0 -1
  697. package/dist/util/clock.js +0 -12
  698. package/dist/util/clock.js.map +0 -1
  699. package/dist/util/crypto.js +0 -25
  700. package/dist/util/crypto.js.map +0 -1
  701. package/dist/util/frontmatter.js +0 -70
  702. package/dist/util/frontmatter.js.map +0 -1
  703. package/dist/util/fs-issues.js +0 -22
  704. package/dist/util/fs-issues.js.map +0 -1
  705. package/dist/util/process.js +0 -152
  706. package/dist/util/process.js.map +0 -1
  707. package/dist/util/workspace-key.js +0 -10
  708. package/dist/util/workspace-key.js.map +0 -1
  709. package/dist/workflow-loader.js +0 -147
  710. package/dist/workflow-loader.js.map +0 -1
  711. package/dist/workflow.js +0 -822
  712. package/dist/workflow.js.map +0 -1
  713. package/dist/workspace-types.js +0 -8
  714. package/dist/workspace-types.js.map +0 -1
  715. package/dist/workspace.js +0 -443
  716. package/dist/workspace.js.map +0 -1
@@ -0,0 +1,233 @@
1
+ // FCIS rewrite — programmatic OCI→gondolin image converter (kind: adapter, issue 206).
2
+ //
3
+ // The programmatic equivalent of `images/agents/build-image-oci.sh` Stage 2: take a
4
+ // normal OCI image reference (a docker/podman repo string), convert it into a
5
+ // content-addressed Gondolin asset bundle (rootfs.ext4 / vmlinuz-virt /
6
+ // initramfs.cpio.lz4 + manifest), and cache it in the local Gondolin image store so
7
+ // a second call for the same image is a no-op. Symphony's reconcile loop calls this
8
+ // on demand (`OrchestratorLoop.ensureImage`) so the operator no longer has to run
9
+ // `npm run build:image` by hand before dispatch can start. The shell script stays as
10
+ // a manual fallback.
11
+ //
12
+ // Caching: the cache key is the resolved OCI digest PLUS the guest-contract overlay
13
+ // version, tagged under a `symphony-oci:<digest>-<overlay>` ref in the Gondolin
14
+ // store. A digest-pinned source (`repo@sha256:…`) caches exactly; a moving tag
15
+ // (`repo:latest`) reconverts when the underlying image's digest changes, because the
16
+ // digest — not the tag string — is the key.
17
+ //
18
+ // Scope (MVP): the golden-image path — a published OCI ref that already satisfies the
19
+ // guest contract (Node ≥ 21 + the ACP CLIs on PATH; the in-VM launcher is staged at
20
+ // dispatch, not baked). The `postBuild.copy`/`.commands` overlay that would project
21
+ // the guest contract onto an ARBITRARY base is a clearly-marked extension point below.
22
+ //
23
+ // Shell rule: this lives in src/shell/adapter (an IO home) so its docker/podman +
24
+ // filesystem + Gondolin-SDK IO is sanctioned. All Gondolin/runtime operations are
25
+ // injectable (`ConverterDeps`) so the cache-hit path is unit-testable without docker.
26
+ import { mkdtempSync } from 'node:fs';
27
+ import { spawnSync } from 'node:child_process';
28
+ import os from 'node:os';
29
+ import path from 'node:path';
30
+ import { buildAssets, getDefaultBuildConfig, importImageFromDirectory, resolveImageSelector, setImageRef, } from '@earendil-works/gondolin';
31
+ /**
32
+ * Guest-contract overlay version — part of the cache key. The MVP overlays nothing
33
+ * (the golden base already satisfies the contract), so this is a constant; bump it
34
+ * when the conversion-time overlay (postBuild) changes so stale assets re-convert.
35
+ */
36
+ export const GUEST_CONTRACT_OVERLAY_VERSION = 'v1';
37
+ /** The cache ref a converted asset is tagged under (digest + overlay version). */
38
+ export function cacheRefFor(digest, overlayVersion = GUEST_CONTRACT_OVERLAY_VERSION) {
39
+ const tag = `${digest.replace(/[^a-z0-9]/gi, '-')}-${overlayVersion}`;
40
+ return `symphony-oci:${tag}`;
41
+ }
42
+ /**
43
+ * The runtime image selector for a converted asset (issue 206): the content-derived
44
+ * build id when known (what `npm run build:image` prints + the operator would pin),
45
+ * else the resolved asset directory (a `path` selector, always present). The
46
+ * orchestrator boots the cached asset with this when `gondolin.image` is unset.
47
+ */
48
+ function runtimeSelector(buildId, assetDir) {
49
+ return buildId.length > 0 ? buildId : assetDir;
50
+ }
51
+ /**
52
+ * Convert + cache `ociRef` into a Gondolin asset, returning the resolved asset (or a
53
+ * cache hit). Resolves the OCI digest (pulling per `pullPolicy`), checks the local
54
+ * store for an already-converted asset at the digest+overlay cache ref, and only
55
+ * runs `buildAssets` on a miss.
56
+ */
57
+ export async function ensureConvertedImage(ociRef, opts = {}) {
58
+ const pullPolicy = opts.pullPolicy ?? 'if-not-present';
59
+ const runtime = opts.runtime ?? null;
60
+ const deps = opts.deps ?? defaultConverterDeps();
61
+ const log = opts.log;
62
+ // Resolve the immutable OCI digest honoring the FULL pull policy → cache ref. The
63
+ // policy is preserved (not reduced to a pull/skip boolean) so `always` re-pulls
64
+ // even when the ref is present locally and the POST-pull digest keys the cache;
65
+ // otherwise a moving tag (`repo:latest`) under `always` would cache against a stale
66
+ // local digest.
67
+ const digest = deps.resolveOciDigest(ociRef, runtime, pullPolicy);
68
+ const cacheRef = cacheRefFor(digest);
69
+ // Cache lookup — an already-converted asset tagged at this digest+overlay?
70
+ const hit = tryResolve(deps, cacheRef);
71
+ if (hit) {
72
+ const buildId = hit.buildId ?? '';
73
+ log?.emit('info', 'gondolin-image-converter: cache hit', { oci_image: ociRef, digest, cacheRef });
74
+ return {
75
+ buildId,
76
+ assetDir: hit.assetDir,
77
+ selector: runtimeSelector(buildId, hit.assetDir),
78
+ cached: true,
79
+ };
80
+ }
81
+ return convertAndTag(deps, { ociRef, runtime, digest, cacheRef, log });
82
+ }
83
+ /**
84
+ * Cache miss: convert the OCI base into a fresh asset bundle, import it, and tag it
85
+ * under the cache ref so the next call is a no-op. EXTENSION: to support an arbitrary
86
+ * base that does NOT bake the guest contract, set `postBuild.copy` / `.commands` on
87
+ * `config` here to project the guest contract at conversion time.
88
+ */
89
+ async function convertAndTag(deps, ctx) {
90
+ const { ociRef, runtime, digest, cacheRef, log } = ctx;
91
+ log?.emit('info', 'gondolin-image-converter: building (one-time)', { oci_image: ociRef, digest, cacheRef });
92
+ // We already enforced the operator's pull policy when resolving `digest` (above),
93
+ // so the LOCAL image at `ociRef` now IS that digest. Hand `buildAssets` an
94
+ // `if-not-present` pull so it converts that exact local image rather than re-pulling
95
+ // — guaranteeing the digest we keyed the cache on (`cacheRef`) is the digest the
96
+ // asset is built from, never tagging a freshly re-pulled image under a stale digest.
97
+ const config = {
98
+ ...deps.defaultBuildConfig(),
99
+ oci: { image: ociRef, pullPolicy: 'if-not-present', ...(runtime ? { runtime } : {}) },
100
+ };
101
+ const result = await deps.buildAssets(config, { outputDir: deps.mkOutputDir(), verbose: false });
102
+ const imported = deps.importImageFromDirectory(result.outputDir);
103
+ deps.setImageRef(cacheRef, imported.buildId, imported.arch);
104
+ log?.emit('info', 'gondolin-image-converter: converted + cached', {
105
+ oci_image: ociRef,
106
+ digest,
107
+ cacheRef,
108
+ build_id: imported.buildId,
109
+ });
110
+ return {
111
+ buildId: imported.buildId,
112
+ assetDir: imported.assetDir,
113
+ selector: runtimeSelector(imported.buildId, imported.assetDir),
114
+ cached: false,
115
+ };
116
+ }
117
+ /**
118
+ * Probe whether `ociRef` is already converted + locally resolvable, WITHOUT pulling
119
+ * or building. Used by BOTH the doctor's read-only preflight AND the reconcile loop's
120
+ * per-tick readiness check (issue 206 rework). It resolves the ref's CURRENT local
121
+ * digest and looks up the cached asset at that digest, so readiness tracks the live
122
+ * digest rather than the ref string: a moving tag whose underlying image changed
123
+ * reports `converted: false` (a stale selector is dropped) until the new digest is
124
+ * reconverted.
125
+ *
126
+ * - `digest` — the ref's resolved local OCI digest, or null when it
127
+ * can't be inspected (not pulled yet / pull disabled).
128
+ * - `selector` — the cached asset's runtime image selector for that digest
129
+ * (what dispatch boots), or null when not yet converted.
130
+ * - `converted` — a cached asset for the current digest resolves.
131
+ * - `locallyResolvable` — the OCI image's digest is readable from the local store.
132
+ */
133
+ export function probeConvertedImage(ociRef, opts = {}) {
134
+ const deps = opts.deps ?? defaultConverterDeps();
135
+ const runtime = opts.runtime ?? null;
136
+ let digest = null;
137
+ try {
138
+ digest = deps.resolveOciDigest(ociRef, runtime, 'never'); // read-only: never pulls
139
+ }
140
+ catch {
141
+ return { converted: false, locallyResolvable: false, digest: null, selector: null };
142
+ }
143
+ const hit = tryResolve(deps, cacheRefFor(digest));
144
+ if (hit === null)
145
+ return { converted: false, locallyResolvable: true, digest, selector: null };
146
+ const buildId = hit.buildId ?? '';
147
+ return { converted: true, locallyResolvable: true, digest, selector: runtimeSelector(buildId, hit.assetDir) };
148
+ }
149
+ function tryResolve(deps, selector) {
150
+ try {
151
+ return deps.resolveImageSelector(selector);
152
+ }
153
+ catch {
154
+ return null;
155
+ }
156
+ }
157
+ // ─── production wiring (real docker/podman + Gondolin SDK) ─────────────────────
158
+ /** The real Gondolin/runtime ops. Tests inject a stub bundle instead. */
159
+ export function defaultConverterDeps() {
160
+ return {
161
+ resolveOciDigest: resolveOciDigestViaRuntime,
162
+ resolveImageSelector,
163
+ buildAssets,
164
+ importImageFromDirectory,
165
+ setImageRef,
166
+ defaultBuildConfig: getDefaultBuildConfig,
167
+ mkOutputDir: () => mkdtempSync(path.join(os.tmpdir(), 'symphony-oci-')),
168
+ };
169
+ }
170
+ /**
171
+ * Drive the OCI digest resolution honoring the FULL pull policy, given injected
172
+ * runtime effects. Pure ordering logic (no spawn), so the policy semantics are
173
+ * unit-testable without docker:
174
+ *
175
+ * - `always` — pull FIRST, unconditionally (even when the ref is already
176
+ * present locally), then inspect, so the POST-pull digest wins.
177
+ * - `if-not-present` — inspect first; pull only on a local miss, then re-inspect.
178
+ * - `never` — never pull; a missing ref resolves to null (caller errors).
179
+ *
180
+ * Returns the resolved local image id, or null when unresolvable.
181
+ */
182
+ export function resolveDigestForPolicy(pullPolicy, io) {
183
+ if (pullPolicy === 'always')
184
+ io.pull();
185
+ let id = io.inspect();
186
+ if (id === null && pullPolicy === 'if-not-present') {
187
+ io.pull();
188
+ id = io.inspect();
189
+ }
190
+ return id;
191
+ }
192
+ /**
193
+ * Resolve an OCI ref to its immutable local image id (`sha256:…`) via the container
194
+ * runtime, pulling per `pullPolicy`. Mirrors what Gondolin's own OCI export path does
195
+ * (the package does not re-export `resolveLocalOciImageDigest`, so we shell out to the
196
+ * same runtime). Throws a precise, operator-facing error on failure.
197
+ */
198
+ function resolveOciDigestViaRuntime(ref, runtime, pullPolicy) {
199
+ const rt = runtime ?? detectRuntime();
200
+ const id = resolveDigestForPolicy(pullPolicy, {
201
+ inspect: () => inspectImageId(rt, ref),
202
+ pull: () => pullImage(rt, ref),
203
+ });
204
+ if (id === null) {
205
+ throw new Error(`cannot resolve OCI image "${ref}" via ${rt}${pullPolicy === 'never' ? ' (not present locally; pull disabled)' : ' (after pull)'}`);
206
+ }
207
+ return id;
208
+ }
209
+ /** `<runtime> pull <ref>`; throws a precise, operator-facing error on failure. */
210
+ function pullImage(runtime, ref) {
211
+ const pulled = spawnSync(runtime, ['pull', ref], { encoding: 'utf8' });
212
+ if (pulled.status !== 0) {
213
+ throw new Error(`failed to pull OCI image "${ref}" via ${runtime}: ${(pulled.stderr || pulled.error?.message || '').trim()}`);
214
+ }
215
+ }
216
+ /** `<runtime> image inspect --format {{.Id}} <ref>` → the local config digest, or null. */
217
+ function inspectImageId(runtime, ref) {
218
+ const r = spawnSync(runtime, ['image', 'inspect', '--format', '{{.Id}}', ref], { encoding: 'utf8' });
219
+ if (r.status !== 0)
220
+ return null;
221
+ const id = (r.stdout || '').trim();
222
+ return id.length > 0 ? id : null;
223
+ }
224
+ /** Pick docker, else podman, by which `--version` answers; defaults to docker. */
225
+ function detectRuntime() {
226
+ for (const rt of ['docker', 'podman']) {
227
+ const r = spawnSync(rt, ['--version'], { encoding: 'utf8' });
228
+ if (r.status === 0)
229
+ return rt;
230
+ }
231
+ return 'docker';
232
+ }
233
+ //# sourceMappingURL=gondolin-image-converter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-image-converter.js","sourceRoot":"","sources":["../../../../src/shell/adapter/gondolin-image-converter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ // FCIS rewrite — managed prebuilt-image fetcher (kind: adapter, issue 224 "Rung 2a").
2
+ //
3
+ // The runtime counterpart of `gondolin-image-converter.ts`, but instead of building an
4
+ // asset from a local OCI base it FETCHES a prebuilt Gondolin asset bundle published by
5
+ // CI to smol-symphony's GitHub releases, verifies it against a published `.sha256`,
6
+ // imports it into the local Gondolin store, and tags it under the managed `name:tag`
7
+ // ref. Onboarding becomes `scaffold → run`: no docker, no `npm run build:image`, no
8
+ // local convert. The orchestrator drives this through the SAME `image:ensure` effect +
9
+ // `ImageBuildTracker` readiness path the convert path uses (main-loops routes the
10
+ // managed ref here, every other ref to the converter).
11
+ //
12
+ // The pure half — asset name / store ref / download-URL / arch map / sha256-sidecar
13
+ // parse — lives in `src/core/image/managed-image.ts` and is reused here
14
+ // (`parseSha256Sidecar`). This module is the IO home: network download, sha256, tar/zstd
15
+ // extract, and the Gondolin SDK import/tag. All of it is injectable (`FetchDeps`) so the
16
+ // cache-hit + verify paths are unit-testable without network or a real store.
17
+ import { createHash } from 'node:crypto';
18
+ import { createReadStream, createWriteStream, existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, statSync } from 'node:fs';
19
+ import { spawnSync } from 'node:child_process';
20
+ import { Readable } from 'node:stream';
21
+ import { finished } from 'node:stream/promises';
22
+ import os from 'node:os';
23
+ import path from 'node:path';
24
+ import { importImageFromDirectory, resolveImageSelector, setImageRef, } from '@earendil-works/gondolin';
25
+ import { parseSha256Sidecar } from '../../core/image/managed-image.js';
26
+ /** Mirrors the converter's `runtimeSelector`: prefer the content-derived build id, else
27
+ * the asset dir (a `path` selector). Always non-empty so dispatch never boots empty. */
28
+ function runtimeSelector(buildId, assetDir) {
29
+ return buildId.length > 0 ? buildId : assetDir;
30
+ }
31
+ /** Resolve a managed ref WITHOUT throwing — null is the cache-miss signal. */
32
+ function tryResolve(deps, ref) {
33
+ try {
34
+ return deps.resolveImageSelector(ref);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Probe whether the managed asset is already imported + resolvable, read-only (no
42
+ * download). The reconcile loop's per-tick readiness check calls this; mirrors the
43
+ * converter's `probeConvertedImage` shape. `digest` is the resolved build id (a stable
44
+ * marker the tracker keys failure-suppression on); both null on a miss.
45
+ */
46
+ export function probeFetchedImage(wiring, opts = {}) {
47
+ const deps = opts.deps ?? defaultFetchDeps();
48
+ const hit = tryResolve(deps, wiring.ref);
49
+ if (hit === null)
50
+ return { digest: null, selector: null };
51
+ const buildId = hit.buildId ?? '';
52
+ return {
53
+ digest: buildId.length > 0 ? buildId : wiring.ref,
54
+ selector: runtimeSelector(buildId, hit.assetDir),
55
+ };
56
+ }
57
+ /**
58
+ * Fetch + import the managed asset, returning the resolved asset (or a cache hit). On a
59
+ * cache hit (`resolveImageSelector(ref)` succeeds) returns the cached selector with NO
60
+ * download. On a miss: download the asset + its `.sha256` sidecar (trying each release-tag
61
+ * candidate), VERIFY the sha256 (a mismatch or a digest-less sidecar THROWS before import),
62
+ * extract, `importImageFromDirectory`, and `setImageRef` under the managed ref.
63
+ */
64
+ export async function ensureFetchedImage(wiring, opts = {}) {
65
+ const deps = opts.deps ?? defaultFetchDeps();
66
+ const log = opts.log;
67
+ const hit = tryResolve(deps, wiring.ref);
68
+ if (hit !== null) {
69
+ const buildId = hit.buildId ?? '';
70
+ log?.emit('info', 'gondolin-image-fetch: cache hit', { ref: wiring.ref });
71
+ return { buildId, assetDir: hit.assetDir, selector: runtimeSelector(buildId, hit.assetDir), cached: true };
72
+ }
73
+ const tmp = deps.mkTempDir();
74
+ const archivePath = path.join(tmp, wiring.assetName);
75
+ const sidecarPath = `${archivePath}.sha256`;
76
+ log?.emit('info', 'gondolin-image-fetch: downloading (one-time)', { ref: wiring.ref, asset: wiring.assetName });
77
+ const chosen = await downloadFirstCandidate(deps, wiring, archivePath, sidecarPath);
78
+ return verifyExtractImport(deps, wiring, { archivePath, sidecarPath, extractDir: path.join(tmp, 'extracted'), chosen, log });
79
+ }
80
+ /** Verify the downloaded asset against its sidecar, extract, import, and tag it. A
81
+ * digest-less sidecar or a sha256 mismatch THROWS before any import. */
82
+ async function verifyExtractImport(deps, wiring, ctx) {
83
+ const { archivePath, sidecarPath, extractDir, chosen, log } = ctx;
84
+ // VERIFY before import — a published asset is only trusted against its published digest.
85
+ const expected = parseSha256Sidecar(deps.readTextFile(sidecarPath));
86
+ if (expected === null) {
87
+ throw new Error(`managed image ${wiring.ref}: sha256 sidecar at ${chosen.sha256Url} is missing or malformed; refusing to import unverified asset`);
88
+ }
89
+ const actual = (await deps.sha256OfFile(archivePath)).toLowerCase();
90
+ if (actual !== expected) {
91
+ throw new Error(`managed image ${wiring.ref}: sha256 mismatch (expected ${expected}, got ${actual}) for ${chosen.assetUrl}; refusing to import`);
92
+ }
93
+ const assetDir = await deps.extractAsset(archivePath, extractDir);
94
+ const imported = deps.importImageFromDirectory(assetDir);
95
+ deps.setImageRef(wiring.ref, imported.buildId, imported.arch);
96
+ log?.emit('info', 'gondolin-image-fetch: imported + tagged', { ref: wiring.ref, build_id: imported.buildId, tag: chosen.tag });
97
+ return {
98
+ buildId: imported.buildId,
99
+ assetDir: imported.assetDir,
100
+ selector: runtimeSelector(imported.buildId, imported.assetDir),
101
+ cached: false,
102
+ };
103
+ }
104
+ /**
105
+ * Download the asset + sidecar, trying each release-tag candidate (`v<version>`, then a
106
+ * bare `<version>`) until one yields BOTH. Throws an aggregate error when none resolve.
107
+ */
108
+ async function downloadFirstCandidate(deps, wiring, archivePath, sidecarPath) {
109
+ const errors = [];
110
+ for (const candidate of wiring.candidates) {
111
+ try {
112
+ await deps.downloadToFile(candidate.assetUrl, archivePath);
113
+ await deps.downloadToFile(candidate.sha256Url, sidecarPath);
114
+ return candidate;
115
+ }
116
+ catch (err) {
117
+ errors.push(`${candidate.tag}: ${err.message}`);
118
+ }
119
+ }
120
+ throw new Error(`managed image ${wiring.ref}: no published asset found for ${wiring.assetName} (tried ${wiring.candidates
121
+ .map((c) => c.tag)
122
+ .join(', ')}). Publish the release asset, or set gondolin.image / gondolin.oci_image. [${errors.join('; ')}]`);
123
+ }
124
+ // ─── production wiring (real fetch + tar/zstd + Gondolin SDK) ──────────────────
125
+ /** The real network + filesystem + Gondolin SDK ops. Tests inject a stub bundle. */
126
+ export function defaultFetchDeps() {
127
+ return {
128
+ resolveImageSelector,
129
+ importImageFromDirectory,
130
+ setImageRef,
131
+ downloadToFile: downloadToFileViaFetch,
132
+ readTextFile: (p) => readFileSync(p, 'utf8'),
133
+ sha256OfFile: sha256OfFileStreaming,
134
+ extractAsset: extractTarZst,
135
+ mkTempDir: () => mkdtempSync(path.join(os.tmpdir(), 'symphony-managed-')),
136
+ };
137
+ }
138
+ /** Stream a URL to `destPath`; throws on a non-OK response (so the candidate loop falls
139
+ * through to the next release tag). */
140
+ async function downloadToFileViaFetch(url, destPath) {
141
+ const res = await fetch(url);
142
+ if (!res.ok || res.body === null) {
143
+ throw new Error(`GET ${url} → ${res.status} ${res.statusText}`);
144
+ }
145
+ const out = createWriteStream(destPath);
146
+ await finished(Readable.fromWeb(res.body).pipe(out));
147
+ }
148
+ /** Lowercased hex sha256 of a file, streamed (the asset is hundreds of MB). */
149
+ async function sha256OfFileStreaming(filePath) {
150
+ const hash = createHash('sha256');
151
+ await finished(createReadStream(filePath).on('data', (chunk) => hash.update(chunk)));
152
+ return hash.digest('hex');
153
+ }
154
+ /** Extract a `.tar.zst` into `destDir`; return the asset dir (the one holding
155
+ * `manifest.json` — `destDir` itself, else a single nested subdir). */
156
+ async function extractTarZst(archivePath, destDir) {
157
+ mkdirSync(destDir, { recursive: true });
158
+ const r = spawnSync('tar', ['--use-compress-program=unzstd', '-xf', archivePath, '-C', destDir], {
159
+ encoding: 'utf8',
160
+ });
161
+ if (r.status !== 0) {
162
+ throw new Error(`tar extract of ${archivePath} failed: ${(r.stderr || r.error?.message || '').trim()}`);
163
+ }
164
+ return locateAssetDir(destDir);
165
+ }
166
+ /** The dir holding `manifest.json`: `destDir` itself, else its single subdir. */
167
+ function locateAssetDir(destDir) {
168
+ if (existsSync(path.join(destDir, 'manifest.json')))
169
+ return destDir;
170
+ const subdirs = readdirSync(destDir).filter((name) => statSync(path.join(destDir, name)).isDirectory());
171
+ for (const name of subdirs) {
172
+ const candidate = path.join(destDir, name);
173
+ if (existsSync(path.join(candidate, 'manifest.json')))
174
+ return candidate;
175
+ }
176
+ // No manifest located — return destDir and let importImageFromDirectory surface the
177
+ // precise error against the actual contents.
178
+ return destDir;
179
+ }
180
+ //# sourceMappingURL=gondolin-image-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-image-fetch.js","sourceRoot":"","sources":["../../../../src/shell/adapter/gondolin-image-fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ // FCIS rewrite — in-VM launcher asset loader (shell adapter).
2
+ //
3
+ // Ports the reference `src/agent/launcher-asset.ts`: reads `scripts/vm-agent.mjs`
4
+ // off the orchestrator's OWN disk once at load, returning the UTF-8 source for
5
+ // per-dispatch staging into the guest. Reading the launcher from host disk (rather
6
+ // than baking it into the image) guarantees the staged launcher is byte-identical
7
+ // to the version the running `acp-ws.ts` speaks — eliminating the image⇄host
8
+ // version lock (see project memory: agent image ⇄ host coupling).
9
+ //
10
+ // Resolution is anchored to THIS module via import.meta.url (never cwd) so it works
11
+ // from both `src/shell/adapter` (tsx) and a built `dist/shell/adapter` layout: both
12
+ // sit three levels under a root that holds `scripts/vm-agent.mjs`. A sibling
13
+ // fallback covers a packaging layout that co-locates the launcher next to dist.
14
+ //
15
+ // Fail-fast: a missing/empty asset throws at load (surfacing the packaging miss at
16
+ // boot, not mid-dispatch). Shell rule: node builtins only; no core, no src/types.
17
+ import { readFileSync } from 'node:fs';
18
+ import { createHash } from 'node:crypto';
19
+ import { fileURLToPath } from 'node:url';
20
+ import path from 'node:path';
21
+ /** Guest path the launcher is staged to (mirrors the historical bake path). */
22
+ export const VM_AGENT_GUEST_PATH = '/opt/symphony/vm-agent.mjs';
23
+ /** Candidate on-disk locations of `scripts/vm-agent.mjs`, resolved relative to here. */
24
+ function candidatePaths() {
25
+ const here = path.dirname(fileURLToPath(import.meta.url));
26
+ return [
27
+ path.resolve(here, '../../../scripts/vm-agent.mjs'),
28
+ path.resolve(here, './vm-agent.mjs'),
29
+ ];
30
+ }
31
+ /** Read the launcher source from the first candidate that exists; throw if none. */
32
+ function loadLauncherSource() {
33
+ const candidates = candidatePaths();
34
+ for (const candidate of candidates) {
35
+ try {
36
+ const source = readFileSync(candidate, 'utf8');
37
+ if (source.length === 0)
38
+ throw new Error(`in-VM launcher asset is empty at ${candidate}`);
39
+ return { source, resolvedPath: candidate };
40
+ }
41
+ catch (err) {
42
+ if (err.code !== 'ENOENT')
43
+ throw err;
44
+ }
45
+ }
46
+ throw new Error(`in-VM launcher asset (scripts/vm-agent.mjs) not found; looked in: ${candidates.join(', ')}.`);
47
+ }
48
+ let cached = null;
49
+ /** Load (once) + return the launcher source + path + sha256. Throws if absent. */
50
+ export function loadLauncherAsset() {
51
+ if (cached)
52
+ return cached;
53
+ const { source, resolvedPath } = loadLauncherSource();
54
+ cached = { source, resolvedPath, sha256: createHash('sha256').update(source, 'utf8').digest('hex') };
55
+ return cached;
56
+ }
57
+ //# sourceMappingURL=launcher-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launcher-asset.js","sourceRoot":"","sources":["../../../../src/shell/adapter/launcher-asset.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,kFAAkF;AAClF,+EAA+E;AAC/E,mFAAmF;AACnF,kFAAkF;AAClF,6EAA6E;AAC7E,kEAAkE;AAClE,EAAE;AACF,oFAAoF;AACpF,oFAAoF;AACpF,6EAA6E;AAC7E,gFAAgF;AAChF,EAAE;AACF,mFAAmF;AACnF,kFAAkF;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,4BAA4B,CAAC;AAEhE,wFAAwF;AACxF,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,+BAA+B,CAAC;QACnD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,SAAS,kBAAkB;IACzB,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;YAC1F,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,qEAAqE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC9F,CAAC;AACJ,CAAC;AAED,IAAI,MAAM,GAAoE,IAAI,CAAC;AAEnF,kFAAkF;AAClF,MAAM,UAAU,iBAAiB;IAC/B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,kBAAkB,EAAE,CAAC;IACtD,MAAM,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACrG,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,65 @@
1
+ // FCIS rewrite — mise SYSTEM config asset loader (shell adapter; issue 209/224).
2
+ //
3
+ // Reads the symphony-bundled mise SYSTEM config (`assets/symphony-mise.system.toml`)
4
+ // off the orchestrator's OWN disk once at load, returning the UTF-8 source for
5
+ // per-dispatch staging into the guest at /etc/mise/config.toml. Shipping the config
6
+ // from host disk (rather than baking node + the agent CLIs into the image) is the
7
+ // whole point of issue 209: bump a pin in this file → no image rebuild + digest repin.
8
+ // It mirrors launcher-asset.ts exactly (the launcher gets the same treatment).
9
+ //
10
+ // The asset lives under `assets/` (issue 224 Part 5): it is read at RUNTIME and staged
11
+ // per dispatch, so it must NOT live in the CI-build-only `images/agents/` (the image
12
+ // build does not consume it). A back-compat `images/agents/` candidate keeps an older
13
+ // checkout/package working through the transition.
14
+ //
15
+ // Resolution is anchored to THIS module via import.meta.url (never cwd) so it works
16
+ // from both `src/shell/adapter` (tsx) and a built `dist/shell/adapter` layout: both
17
+ // sit three levels under a root that holds `assets/symphony-mise.system.toml`. A
18
+ // sibling fallback covers a packaging layout that co-locates the asset next to dist.
19
+ //
20
+ // Fail-fast: a missing/empty asset throws at load (surfacing the packaging miss at
21
+ // boot, not mid-dispatch). Shell rule: node builtins only; no core, no src/types.
22
+ import { readFileSync } from 'node:fs';
23
+ import { createHash } from 'node:crypto';
24
+ import { fileURLToPath } from 'node:url';
25
+ import path from 'node:path';
26
+ /** Asset filename (kept in sync with the staged guest path in core `runner/mise.ts`). */
27
+ const ASSET_NAME = 'symphony-mise.system.toml';
28
+ /** Candidate on-disk locations of the mise SYSTEM config, resolved relative to here.
29
+ * `assets/` is the runtime home (issue 224 Part 5); `images/agents/` is a back-compat
30
+ * candidate for an older checkout/package; the sibling covers a co-located layout. */
31
+ function candidatePaths() {
32
+ const here = path.dirname(fileURLToPath(import.meta.url));
33
+ return [
34
+ path.resolve(here, '../../../assets', ASSET_NAME),
35
+ path.resolve(here, '../../../images/agents', ASSET_NAME),
36
+ path.resolve(here, ASSET_NAME),
37
+ ];
38
+ }
39
+ /** Read the config source from the first candidate that exists; throw if none. */
40
+ function loadMiseConfigSource() {
41
+ const candidates = candidatePaths();
42
+ for (const candidate of candidates) {
43
+ try {
44
+ const source = readFileSync(candidate, 'utf8');
45
+ if (source.length === 0)
46
+ throw new Error(`mise SYSTEM config asset is empty at ${candidate}`);
47
+ return { source, resolvedPath: candidate };
48
+ }
49
+ catch (err) {
50
+ if (err.code !== 'ENOENT')
51
+ throw err;
52
+ }
53
+ }
54
+ throw new Error(`mise SYSTEM config asset (assets/${ASSET_NAME}) not found; looked in: ${candidates.join(', ')}.`);
55
+ }
56
+ let cached = null;
57
+ /** Load (once) + return the mise SYSTEM config source + path + sha256. Throws if absent. */
58
+ export function loadMiseConfigAsset() {
59
+ if (cached)
60
+ return cached;
61
+ const { source, resolvedPath } = loadMiseConfigSource();
62
+ cached = { source, resolvedPath, sha256: createHash('sha256').update(source, 'utf8').digest('hex') };
63
+ return cached;
64
+ }
65
+ //# sourceMappingURL=mise-config-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mise-config-asset.js","sourceRoot":"","sources":["../../../../src/shell/adapter/mise-config-asset.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,qFAAqF;AACrF,+EAA+E;AAC/E,oFAAoF;AACpF,kFAAkF;AAClF,uFAAuF;AACvF,+EAA+E;AAC/E,EAAE;AACF,uFAAuF;AACvF,qFAAqF;AACrF,sFAAsF;AACtF,mDAAmD;AACnD,EAAE;AACF,oFAAoF;AACpF,oFAAoF;AACpF,iFAAiF;AACjF,qFAAqF;AACrF,EAAE;AACF,mFAAmF;AACnF,kFAAkF;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,yFAAyF;AACzF,MAAM,UAAU,GAAG,2BAA2B,CAAC;AAE/C;;uFAEuF;AACvF,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,EAAE,UAAU,CAAC;QACjD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,wBAAwB,EAAE,UAAU,CAAC;QACxD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,SAAS,oBAAoB;IAC3B,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC/C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,SAAS,EAAE,CAAC,CAAC;YAC9F,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;QAC7C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACb,oCAAoC,UAAU,2BAA2B,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAClG,CAAC;AACJ,CAAC;AAED,IAAI,MAAM,GAAoE,IAAI,CAAC;AAEnF,4FAA4F;AAC5F,MAAM,UAAU,mBAAmB;IACjC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,oBAAoB,EAAE,CAAC;IACxD,MAAM,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACrG,OAAO,MAAM,CAAC;AAChB,CAAC"}