@geminixiang/mikan 1.0.0-beta.2 → 1.0.0-beta.21

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 (497) hide show
  1. package/CHANGELOG.md +388 -0
  2. package/README.md +49 -16
  3. package/dist/adapter.d.ts +1 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/adapters/buffered-responder.d.ts +60 -0
  7. package/dist/adapters/buffered-responder.d.ts.map +1 -0
  8. package/dist/adapters/buffered-responder.js +201 -0
  9. package/dist/adapters/buffered-responder.js.map +1 -0
  10. package/dist/adapters/discord/bot.d.ts +1 -0
  11. package/dist/adapters/discord/bot.d.ts.map +1 -1
  12. package/dist/adapters/discord/bot.js +36 -67
  13. package/dist/adapters/discord/bot.js.map +1 -1
  14. package/dist/adapters/discord/context.d.ts.map +1 -1
  15. package/dist/adapters/discord/context.js +44 -204
  16. package/dist/adapters/discord/context.js.map +1 -1
  17. package/dist/adapters/github/bot.d.ts +86 -0
  18. package/dist/adapters/github/bot.d.ts.map +1 -0
  19. package/dist/adapters/github/bot.js +640 -0
  20. package/dist/adapters/github/bot.js.map +1 -0
  21. package/dist/adapters/github/client.d.ts +122 -0
  22. package/dist/adapters/github/client.d.ts.map +1 -0
  23. package/dist/adapters/github/client.js +314 -0
  24. package/dist/adapters/github/client.js.map +1 -0
  25. package/dist/adapters/github/cloudbuild.d.ts +24 -0
  26. package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
  27. package/dist/adapters/github/cloudbuild.js +71 -0
  28. package/dist/adapters/github/cloudbuild.js.map +1 -0
  29. package/dist/adapters/github/context.d.ts +9 -0
  30. package/dist/adapters/github/context.d.ts.map +1 -0
  31. package/dist/adapters/github/context.js +103 -0
  32. package/dist/adapters/github/context.js.map +1 -0
  33. package/dist/adapters/github/gcp-auth.d.ts +31 -0
  34. package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
  35. package/dist/adapters/github/gcp-auth.js +196 -0
  36. package/dist/adapters/github/gcp-auth.js.map +1 -0
  37. package/dist/adapters/github/github-ops.d.ts +90 -0
  38. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  39. package/dist/adapters/github/github-ops.js +405 -0
  40. package/dist/adapters/github/github-ops.js.map +1 -0
  41. package/dist/adapters/github/ids.d.ts +34 -0
  42. package/dist/adapters/github/ids.d.ts.map +1 -0
  43. package/dist/adapters/github/ids.js +53 -0
  44. package/dist/adapters/github/ids.js.map +1 -0
  45. package/dist/adapters/github/repo.d.ts +32 -0
  46. package/dist/adapters/github/repo.d.ts.map +1 -0
  47. package/dist/adapters/github/repo.js +141 -0
  48. package/dist/adapters/github/repo.js.map +1 -0
  49. package/dist/adapters/github/tool-pack.d.ts +14 -0
  50. package/dist/adapters/github/tool-pack.d.ts.map +1 -0
  51. package/dist/adapters/github/tool-pack.js +56 -0
  52. package/dist/adapters/github/tool-pack.js.map +1 -0
  53. package/dist/adapters/github/tools/checks.d.ts +24 -0
  54. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  55. package/dist/adapters/github/tools/checks.js +105 -0
  56. package/dist/adapters/github/tools/checks.js.map +1 -0
  57. package/dist/adapters/github/tools/issue.d.ts +22 -0
  58. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  59. package/dist/adapters/github/tools/issue.js +48 -0
  60. package/dist/adapters/github/tools/issue.js.map +1 -0
  61. package/dist/adapters/github/tools/pr.d.ts +22 -0
  62. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  63. package/dist/adapters/github/tools/pr.js +48 -0
  64. package/dist/adapters/github/tools/pr.js.map +1 -0
  65. package/dist/adapters/github/tools/read.d.ts +22 -0
  66. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  67. package/dist/adapters/github/tools/read.js +141 -0
  68. package/dist/adapters/github/tools/read.js.map +1 -0
  69. package/dist/adapters/github/tools/review-reply.d.ts +19 -0
  70. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  71. package/dist/adapters/github/tools/review-reply.js +38 -0
  72. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  73. package/dist/adapters/github/tools/sync.d.ts +17 -0
  74. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  75. package/dist/adapters/github/tools/sync.js +34 -0
  76. package/dist/adapters/github/tools/sync.js.map +1 -0
  77. package/dist/adapters/github/types.d.ts +350 -0
  78. package/dist/adapters/github/types.d.ts.map +1 -0
  79. package/dist/adapters/github/types.js +2 -0
  80. package/dist/adapters/github/types.js.map +1 -0
  81. package/dist/adapters/intake.d.ts +16 -5
  82. package/dist/adapters/intake.d.ts.map +1 -1
  83. package/dist/adapters/intake.js +79 -22
  84. package/dist/adapters/intake.js.map +1 -1
  85. package/dist/adapters/slack/bot.d.ts +8 -6
  86. package/dist/adapters/slack/bot.d.ts.map +1 -1
  87. package/dist/adapters/slack/bot.js +82 -134
  88. package/dist/adapters/slack/bot.js.map +1 -1
  89. package/dist/adapters/slack/context.d.ts.map +1 -1
  90. package/dist/adapters/slack/context.js +3 -14
  91. package/dist/adapters/slack/context.js.map +1 -1
  92. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  93. package/dist/adapters/slack/response-lifecycle.js +14 -17
  94. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  95. package/dist/adapters/slack/session.d.ts.map +1 -1
  96. package/dist/adapters/slack/session.js +5 -8
  97. package/dist/adapters/slack/session.js.map +1 -1
  98. package/dist/adapters/streaming.d.ts +4 -0
  99. package/dist/adapters/streaming.d.ts.map +1 -1
  100. package/dist/adapters/streaming.js +11 -0
  101. package/dist/adapters/streaming.js.map +1 -1
  102. package/dist/adapters/telegram/bot.d.ts +1 -2
  103. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  104. package/dist/adapters/telegram/bot.js +27 -62
  105. package/dist/adapters/telegram/bot.js.map +1 -1
  106. package/dist/adapters/telegram/context.d.ts.map +1 -1
  107. package/dist/adapters/telegram/context.js +37 -174
  108. package/dist/adapters/telegram/context.js.map +1 -1
  109. package/dist/adapters/types.d.ts +28 -2
  110. package/dist/adapters/types.d.ts.map +1 -1
  111. package/dist/adapters/types.js.map +1 -1
  112. package/dist/agent.d.ts +37 -14
  113. package/dist/agent.d.ts.map +1 -1
  114. package/dist/agent.js +359 -229
  115. package/dist/agent.js.map +1 -1
  116. package/dist/cli/arg-grammar.d.ts +25 -0
  117. package/dist/cli/arg-grammar.d.ts.map +1 -0
  118. package/dist/cli/arg-grammar.js +59 -0
  119. package/dist/cli/arg-grammar.js.map +1 -0
  120. package/dist/cli/boot.d.ts +16 -0
  121. package/dist/cli/boot.d.ts.map +1 -0
  122. package/dist/cli/boot.js +119 -0
  123. package/dist/cli/boot.js.map +1 -0
  124. package/dist/cli/download.d.ts.map +1 -0
  125. package/dist/cli/download.js.map +1 -0
  126. package/dist/cli/ext-git.d.ts +22 -0
  127. package/dist/cli/ext-git.d.ts.map +1 -0
  128. package/dist/cli/ext-git.js +76 -0
  129. package/dist/cli/ext-git.js.map +1 -0
  130. package/dist/cli/ext.d.ts +2 -0
  131. package/dist/cli/ext.d.ts.map +1 -0
  132. package/dist/cli/ext.js +215 -0
  133. package/dist/cli/ext.js.map +1 -0
  134. package/dist/commands/admin.d.ts.map +1 -1
  135. package/dist/commands/admin.js +2 -1
  136. package/dist/commands/admin.js.map +1 -1
  137. package/dist/commands/auto-reply.d.ts.map +1 -1
  138. package/dist/commands/auto-reply.js +2 -6
  139. package/dist/commands/auto-reply.js.map +1 -1
  140. package/dist/commands/extensions.d.ts.map +1 -1
  141. package/dist/commands/extensions.js +18 -8
  142. package/dist/commands/extensions.js.map +1 -1
  143. package/dist/commands/login.d.ts +2 -1
  144. package/dist/commands/login.d.ts.map +1 -1
  145. package/dist/commands/login.js +35 -3
  146. package/dist/commands/login.js.map +1 -1
  147. package/dist/commands/manifest.d.ts +72 -0
  148. package/dist/commands/manifest.d.ts.map +1 -0
  149. package/dist/commands/manifest.js +121 -0
  150. package/dist/commands/manifest.js.map +1 -0
  151. package/dist/commands/model.d.ts.map +1 -1
  152. package/dist/commands/model.js +11 -9
  153. package/dist/commands/model.js.map +1 -1
  154. package/dist/commands/new.d.ts.map +1 -1
  155. package/dist/commands/new.js +2 -1
  156. package/dist/commands/new.js.map +1 -1
  157. package/dist/commands/sandbox.d.ts.map +1 -1
  158. package/dist/commands/sandbox.js +23 -17
  159. package/dist/commands/sandbox.js.map +1 -1
  160. package/dist/commands/text.d.ts +2 -0
  161. package/dist/commands/text.d.ts.map +1 -0
  162. package/dist/commands/text.js +16 -0
  163. package/dist/commands/text.js.map +1 -0
  164. package/dist/commands/types.d.ts +14 -1
  165. package/dist/commands/types.d.ts.map +1 -1
  166. package/dist/commands/types.js.map +1 -1
  167. package/dist/config.d.ts +1 -3
  168. package/dist/config.d.ts.map +1 -1
  169. package/dist/config.js +98 -95
  170. package/dist/config.js.map +1 -1
  171. package/dist/env-manifest.d.ts +35 -0
  172. package/dist/env-manifest.d.ts.map +1 -0
  173. package/dist/env-manifest.js +225 -0
  174. package/dist/env-manifest.js.map +1 -0
  175. package/dist/events.d.ts.map +1 -1
  176. package/dist/events.js +9 -73
  177. package/dist/events.js.map +1 -1
  178. package/dist/execution-resolver.d.ts +4 -2
  179. package/dist/execution-resolver.d.ts.map +1 -1
  180. package/dist/execution-resolver.js +54 -41
  181. package/dist/execution-resolver.js.map +1 -1
  182. package/dist/harness/event-format.d.ts +66 -0
  183. package/dist/harness/event-format.d.ts.map +1 -0
  184. package/dist/harness/event-format.js +112 -0
  185. package/dist/harness/event-format.js.map +1 -0
  186. package/dist/harness/extensions/loader.d.ts +44 -11
  187. package/dist/harness/extensions/loader.d.ts.map +1 -1
  188. package/dist/harness/extensions/loader.js +273 -84
  189. package/dist/harness/extensions/loader.js.map +1 -1
  190. package/dist/harness/extensions/registry.d.ts +48 -1
  191. package/dist/harness/extensions/registry.d.ts.map +1 -1
  192. package/dist/harness/extensions/registry.js +146 -0
  193. package/dist/harness/extensions/registry.js.map +1 -1
  194. package/dist/harness/extensions/types.d.ts +165 -22
  195. package/dist/harness/extensions/types.d.ts.map +1 -1
  196. package/dist/harness/extensions/types.js.map +1 -1
  197. package/dist/harness/index.d.ts +6 -5
  198. package/dist/harness/index.d.ts.map +1 -1
  199. package/dist/harness/index.js +3 -2
  200. package/dist/harness/index.js.map +1 -1
  201. package/dist/harness/models.d.ts +5 -0
  202. package/dist/harness/models.d.ts.map +1 -1
  203. package/dist/harness/models.js +10 -0
  204. package/dist/harness/models.js.map +1 -1
  205. package/dist/harness/runner.d.ts +37 -2
  206. package/dist/harness/runner.d.ts.map +1 -1
  207. package/dist/harness/runner.js +107 -23
  208. package/dist/harness/runner.js.map +1 -1
  209. package/dist/harness/session-store.d.ts +4 -1
  210. package/dist/harness/session-store.d.ts.map +1 -1
  211. package/dist/harness/session-store.js +11 -2
  212. package/dist/harness/session-store.js.map +1 -1
  213. package/dist/harness/subagent-runner.d.ts +34 -0
  214. package/dist/harness/subagent-runner.d.ts.map +1 -0
  215. package/dist/harness/subagent-runner.js +339 -0
  216. package/dist/harness/subagent-runner.js.map +1 -0
  217. package/dist/harness/types.d.ts +53 -0
  218. package/dist/harness/types.d.ts.map +1 -1
  219. package/dist/harness/types.js.map +1 -1
  220. package/dist/index.d.ts +3 -3
  221. package/dist/index.d.ts.map +1 -1
  222. package/dist/index.js +2 -2
  223. package/dist/index.js.map +1 -1
  224. package/dist/main.js +260 -109
  225. package/dist/main.js.map +1 -1
  226. package/dist/observability/instrument.js +6 -4
  227. package/dist/observability/instrument.js.map +1 -1
  228. package/dist/observability/sentry.d.ts.map +1 -1
  229. package/dist/observability/sentry.js +3 -2
  230. package/dist/observability/sentry.js.map +1 -1
  231. package/dist/provisioner.d.ts.map +1 -1
  232. package/dist/provisioner.js +2 -5
  233. package/dist/provisioner.js.map +1 -1
  234. package/dist/runtime/conversation-runtime.d.ts +1 -1
  235. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  236. package/dist/runtime/conversation-runtime.js +276 -43
  237. package/dist/runtime/conversation-runtime.js.map +1 -1
  238. package/dist/runtime/types.d.ts +36 -5
  239. package/dist/runtime/types.d.ts.map +1 -1
  240. package/dist/runtime/types.js.map +1 -1
  241. package/dist/sandbox/cloudflare.d.ts +3 -0
  242. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  243. package/dist/sandbox/cloudflare.js +10 -0
  244. package/dist/sandbox/cloudflare.js.map +1 -1
  245. package/dist/sandbox/container.d.ts +4 -0
  246. package/dist/sandbox/container.d.ts.map +1 -1
  247. package/dist/sandbox/container.js +11 -2
  248. package/dist/sandbox/container.js.map +1 -1
  249. package/dist/sandbox/firecracker.d.ts +3 -0
  250. package/dist/sandbox/firecracker.d.ts.map +1 -1
  251. package/dist/sandbox/firecracker.js +10 -1
  252. package/dist/sandbox/firecracker.js.map +1 -1
  253. package/dist/sandbox/gondolin-bootstrap.d.ts +26 -0
  254. package/dist/sandbox/gondolin-bootstrap.d.ts.map +1 -0
  255. package/dist/sandbox/gondolin-bootstrap.js +27 -0
  256. package/dist/sandbox/gondolin-bootstrap.js.map +1 -0
  257. package/dist/sandbox/gondolin-contract.d.ts +125 -0
  258. package/dist/sandbox/gondolin-contract.d.ts.map +1 -0
  259. package/dist/sandbox/gondolin-contract.js +155 -0
  260. package/dist/sandbox/gondolin-contract.js.map +1 -0
  261. package/dist/sandbox/gondolin-fleet.d.ts +104 -0
  262. package/dist/sandbox/gondolin-fleet.d.ts.map +1 -0
  263. package/dist/sandbox/gondolin-fleet.js +330 -0
  264. package/dist/sandbox/gondolin-fleet.js.map +1 -0
  265. package/dist/sandbox/gondolin-gateway.d.ts +86 -0
  266. package/dist/sandbox/gondolin-gateway.d.ts.map +1 -0
  267. package/dist/sandbox/gondolin-gateway.js +407 -0
  268. package/dist/sandbox/gondolin-gateway.js.map +1 -0
  269. package/dist/sandbox/gondolin-inventory.d.ts +75 -0
  270. package/dist/sandbox/gondolin-inventory.d.ts.map +1 -0
  271. package/dist/sandbox/gondolin-inventory.js +257 -0
  272. package/dist/sandbox/gondolin-inventory.js.map +1 -0
  273. package/dist/sandbox/gondolin-join.d.ts +62 -0
  274. package/dist/sandbox/gondolin-join.d.ts.map +1 -0
  275. package/dist/sandbox/gondolin-join.js +261 -0
  276. package/dist/sandbox/gondolin-join.js.map +1 -0
  277. package/dist/sandbox/gondolin-nfs-advisory.d.ts +15 -0
  278. package/dist/sandbox/gondolin-nfs-advisory.d.ts.map +1 -0
  279. package/dist/sandbox/gondolin-nfs-advisory.js +60 -0
  280. package/dist/sandbox/gondolin-nfs-advisory.js.map +1 -0
  281. package/dist/sandbox/gondolin-placement.d.ts +30 -0
  282. package/dist/sandbox/gondolin-placement.d.ts.map +1 -0
  283. package/dist/sandbox/gondolin-placement.js +77 -0
  284. package/dist/sandbox/gondolin-placement.js.map +1 -0
  285. package/dist/sandbox/gondolin-recovery.d.ts +33 -0
  286. package/dist/sandbox/gondolin-recovery.d.ts.map +1 -0
  287. package/dist/sandbox/gondolin-recovery.js +39 -0
  288. package/dist/sandbox/gondolin-recovery.js.map +1 -0
  289. package/dist/sandbox/gondolin-remote.d.ts +70 -0
  290. package/dist/sandbox/gondolin-remote.d.ts.map +1 -0
  291. package/dist/sandbox/gondolin-remote.js +325 -0
  292. package/dist/sandbox/gondolin-remote.js.map +1 -0
  293. package/dist/sandbox/gondolin-worker-client.d.ts +135 -0
  294. package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -0
  295. package/dist/sandbox/gondolin-worker-client.js +323 -0
  296. package/dist/sandbox/gondolin-worker-client.js.map +1 -0
  297. package/dist/sandbox/gondolin-worker-main.d.ts +2 -0
  298. package/dist/sandbox/gondolin-worker-main.d.ts.map +1 -0
  299. package/dist/sandbox/gondolin-worker-main.js +22 -0
  300. package/dist/sandbox/gondolin-worker-main.js.map +1 -0
  301. package/dist/sandbox/gondolin-worker.d.ts +17 -0
  302. package/dist/sandbox/gondolin-worker.d.ts.map +1 -0
  303. package/dist/sandbox/gondolin-worker.js +201 -0
  304. package/dist/sandbox/gondolin-worker.js.map +1 -0
  305. package/dist/sandbox/gondolin.d.ts +53 -0
  306. package/dist/sandbox/gondolin.d.ts.map +1 -0
  307. package/dist/sandbox/gondolin.js +429 -0
  308. package/dist/sandbox/gondolin.js.map +1 -0
  309. package/dist/sandbox/host.d.ts +3 -0
  310. package/dist/sandbox/host.d.ts.map +1 -1
  311. package/dist/sandbox/host.js +14 -0
  312. package/dist/sandbox/host.js.map +1 -1
  313. package/dist/sandbox/identity.d.ts +21 -0
  314. package/dist/sandbox/identity.d.ts.map +1 -0
  315. package/dist/sandbox/identity.js +36 -0
  316. package/dist/sandbox/identity.js.map +1 -0
  317. package/dist/sandbox/index.d.ts +13 -4
  318. package/dist/sandbox/index.d.ts.map +1 -1
  319. package/dist/sandbox/index.js +19 -3
  320. package/dist/sandbox/index.js.map +1 -1
  321. package/dist/sandbox/types.d.ts +31 -1
  322. package/dist/sandbox/types.d.ts.map +1 -1
  323. package/dist/sandbox/types.js.map +1 -1
  324. package/dist/sandbox/utils.d.ts +12 -0
  325. package/dist/sandbox/utils.d.ts.map +1 -1
  326. package/dist/sandbox/utils.js +38 -0
  327. package/dist/sandbox/utils.js.map +1 -1
  328. package/dist/sessions/chat-history-sync.d.ts +32 -0
  329. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  330. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +40 -113
  331. package/dist/sessions/chat-history-sync.js.map +1 -0
  332. package/dist/sessions/conversation-log.d.ts +13 -0
  333. package/dist/sessions/conversation-log.d.ts.map +1 -0
  334. package/dist/sessions/conversation-log.js +47 -0
  335. package/dist/sessions/conversation-log.js.map +1 -0
  336. package/dist/sessions/history-line.d.ts +20 -0
  337. package/dist/sessions/history-line.d.ts.map +1 -0
  338. package/dist/sessions/history-line.js +24 -0
  339. package/dist/sessions/history-line.js.map +1 -0
  340. package/dist/sessions/policy.d.ts.map +1 -1
  341. package/dist/sessions/policy.js +5 -3
  342. package/dist/sessions/policy.js.map +1 -1
  343. package/dist/sessions/rotation.d.ts +2 -0
  344. package/dist/sessions/rotation.d.ts.map +1 -0
  345. package/dist/sessions/rotation.js +39 -0
  346. package/dist/sessions/rotation.js.map +1 -0
  347. package/dist/sessions/session-key.d.ts +31 -0
  348. package/dist/sessions/session-key.d.ts.map +1 -0
  349. package/dist/sessions/session-key.js +44 -0
  350. package/dist/sessions/session-key.js.map +1 -0
  351. package/dist/sessions/store.d.ts.map +1 -1
  352. package/dist/sessions/store.js +6 -12
  353. package/dist/sessions/store.js.map +1 -1
  354. package/dist/sessions/types.d.ts +2 -2
  355. package/dist/sessions/types.d.ts.map +1 -1
  356. package/dist/sessions/types.js.map +1 -1
  357. package/dist/settings-mutation.d.ts +23 -0
  358. package/dist/settings-mutation.d.ts.map +1 -0
  359. package/dist/settings-mutation.js +42 -0
  360. package/dist/settings-mutation.js.map +1 -0
  361. package/dist/tools/bash.d.ts +3 -1
  362. package/dist/tools/bash.d.ts.map +1 -1
  363. package/dist/tools/bash.js +24 -17
  364. package/dist/tools/bash.js.map +1 -1
  365. package/dist/tools/edit.d.ts.map +1 -1
  366. package/dist/tools/edit.js +9 -12
  367. package/dist/tools/edit.js.map +1 -1
  368. package/dist/tools/event.d.ts +1 -1
  369. package/dist/tools/event.d.ts.map +1 -1
  370. package/dist/tools/event.js +53 -43
  371. package/dist/tools/event.js.map +1 -1
  372. package/dist/tools/generate-image.d.ts +17 -0
  373. package/dist/tools/generate-image.d.ts.map +1 -0
  374. package/dist/tools/generate-image.js +63 -0
  375. package/dist/tools/generate-image.js.map +1 -0
  376. package/dist/tools/host-fn-tool.d.ts +21 -0
  377. package/dist/tools/host-fn-tool.d.ts.map +1 -0
  378. package/dist/tools/host-fn-tool.js +32 -0
  379. package/dist/tools/host-fn-tool.js.map +1 -0
  380. package/dist/tools/index.d.ts +17 -2
  381. package/dist/tools/index.d.ts.map +1 -1
  382. package/dist/tools/index.js +28 -3
  383. package/dist/tools/index.js.map +1 -1
  384. package/dist/tools/react.d.ts +15 -0
  385. package/dist/tools/react.d.ts.map +1 -0
  386. package/dist/tools/react.js +29 -0
  387. package/dist/tools/react.js.map +1 -0
  388. package/dist/tools/read.d.ts.map +1 -1
  389. package/dist/tools/read.js +7 -26
  390. package/dist/tools/read.js.map +1 -1
  391. package/dist/tools/sandbox.d.ts +2 -2
  392. package/dist/tools/sandbox.d.ts.map +1 -1
  393. package/dist/tools/sandbox.js +13 -9
  394. package/dist/tools/sandbox.js.map +1 -1
  395. package/dist/tools/subagent-slots.d.ts +28 -0
  396. package/dist/tools/subagent-slots.d.ts.map +1 -0
  397. package/dist/tools/subagent-slots.js +57 -0
  398. package/dist/tools/subagent-slots.js.map +1 -0
  399. package/dist/tools/subagent.d.ts +74 -0
  400. package/dist/tools/subagent.d.ts.map +1 -0
  401. package/dist/tools/subagent.js +335 -0
  402. package/dist/tools/subagent.js.map +1 -0
  403. package/dist/tools/types.d.ts +40 -6
  404. package/dist/tools/types.d.ts.map +1 -1
  405. package/dist/tools/types.js.map +1 -1
  406. package/dist/tools/write.d.ts.map +1 -1
  407. package/dist/tools/write.js +1 -8
  408. package/dist/tools/write.js.map +1 -1
  409. package/dist/trigger.d.ts.map +1 -1
  410. package/dist/trigger.js +1 -2
  411. package/dist/trigger.js.map +1 -1
  412. package/dist/types.d.ts +144 -31
  413. package/dist/types.d.ts.map +1 -1
  414. package/dist/types.js.map +1 -1
  415. package/dist/utils/date.d.ts +1 -1
  416. package/dist/utils/date.js +1 -1
  417. package/dist/utils/date.js.map +1 -1
  418. package/dist/vault/disabled.d.ts +9 -0
  419. package/dist/vault/disabled.d.ts.map +1 -0
  420. package/dist/vault/disabled.js +22 -0
  421. package/dist/vault/disabled.js.map +1 -0
  422. package/dist/vault/index.d.ts +0 -2
  423. package/dist/vault/index.d.ts.map +1 -1
  424. package/dist/vault/index.js +1 -32
  425. package/dist/vault/index.js.map +1 -1
  426. package/dist/vault/injection.d.ts +20 -0
  427. package/dist/vault/injection.d.ts.map +1 -0
  428. package/dist/vault/injection.js +27 -0
  429. package/dist/vault/injection.js.map +1 -0
  430. package/dist/vault/policy.d.ts +20 -0
  431. package/dist/vault/policy.d.ts.map +1 -0
  432. package/dist/vault/policy.js +20 -0
  433. package/dist/vault/policy.js.map +1 -0
  434. package/dist/vault/types.d.ts +0 -2
  435. package/dist/vault/types.d.ts.map +1 -1
  436. package/dist/vault/types.js.map +1 -1
  437. package/dist/web/admin/portal.d.ts +27 -0
  438. package/dist/web/admin/portal.d.ts.map +1 -1
  439. package/dist/web/admin/portal.js +117 -154
  440. package/dist/web/admin/portal.js.map +1 -1
  441. package/dist/web/admin/types.d.ts +6 -0
  442. package/dist/web/admin/types.d.ts.map +1 -1
  443. package/dist/web/admin/types.js.map +1 -1
  444. package/dist/web/agent-events/portal.d.ts.map +1 -1
  445. package/dist/web/agent-events/portal.js +2 -1
  446. package/dist/web/agent-events/portal.js.map +1 -1
  447. package/dist/web/login/oauth.d.ts +2 -3
  448. package/dist/web/login/oauth.d.ts.map +1 -1
  449. package/dist/web/login/oauth.js +0 -31
  450. package/dist/web/login/oauth.js.map +1 -1
  451. package/dist/web/login/portal.js +1 -1
  452. package/dist/web/login/portal.js.map +1 -1
  453. package/dist/web/login/types.d.ts +0 -11
  454. package/dist/web/login/types.d.ts.map +1 -1
  455. package/dist/web/login/types.js.map +1 -1
  456. package/dist/web/portal-shell.d.ts +4 -0
  457. package/dist/web/portal-shell.d.ts.map +1 -0
  458. package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
  459. package/dist/web/portal-shell.js.map +1 -0
  460. package/dist/web/server.d.ts.map +1 -1
  461. package/dist/web/server.js +7 -0
  462. package/dist/web/server.js.map +1 -1
  463. package/dist/web/session-view/command.d.ts.map +1 -1
  464. package/dist/web/session-view/command.js +4 -1
  465. package/dist/web/session-view/command.js.map +1 -1
  466. package/dist/web/session-view/portal.d.ts.map +1 -1
  467. package/dist/web/session-view/portal.js +4 -3
  468. package/dist/web/session-view/portal.js.map +1 -1
  469. package/dist/web/session-view/service.d.ts.map +1 -1
  470. package/dist/web/session-view/service.js +2 -1
  471. package/dist/web/session-view/service.js.map +1 -1
  472. package/dist/web/session-view/types.d.ts +2 -1
  473. package/dist/web/session-view/types.d.ts.map +1 -1
  474. package/dist/web/session-view/types.js.map +1 -1
  475. package/package.json +7 -4
  476. package/dist/download.d.ts.map +0 -1
  477. package/dist/download.js.map +0 -1
  478. package/dist/model-registry.d.ts +0 -4
  479. package/dist/model-registry.d.ts.map +0 -1
  480. package/dist/model-registry.js +0 -7
  481. package/dist/model-registry.js.map +0 -1
  482. package/dist/portal-shell.d.ts +0 -4
  483. package/dist/portal-shell.d.ts.map +0 -1
  484. package/dist/portal-shell.js.map +0 -1
  485. package/dist/runtime/agent-run-controller.d.ts +0 -31
  486. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  487. package/dist/runtime/agent-run-controller.js +0 -195
  488. package/dist/runtime/agent-run-controller.js.map +0 -1
  489. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  490. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  491. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  492. package/dist/vault/routing.d.ts +0 -3
  493. package/dist/vault/routing.d.ts.map +0 -1
  494. package/dist/vault/routing.js +0 -13
  495. package/dist/vault/routing.js.map +0 -1
  496. /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
  497. /package/dist/{download.js → cli/download.js} +0 -0
@@ -0,0 +1,323 @@
1
+ import { spawn } from "node:child_process";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { SandboxError } from "./errors.js";
6
+ import { gondolinInventory } from "./gondolin-inventory.js";
7
+ import { GondolinRuntimeGoneError, GondolinRuntimeInterruptedError } from "./gondolin-recovery.js";
8
+ function defaultSpawnProcess(command, args) {
9
+ return spawn(command, args, { detached: true, stdio: ["ignore", "pipe", "pipe"] });
10
+ }
11
+ async function defaultConnect(socketPath, callbacks) {
12
+ const { connectToSession } = await import("@earendil-works/gondolin");
13
+ return connectToSession(socketPath, callbacks);
14
+ }
15
+ function abortError() {
16
+ return new Error("Error: command aborted");
17
+ }
18
+ /**
19
+ * A command may quietly work for a long time, but a session that produces no
20
+ * frame at all for this long is indistinguishable from one wedged on dead
21
+ * storage (a hung NFS mount under the workspace blocks guest I/O forever) —
22
+ * kill it and say so instead of hanging the run.
23
+ */
24
+ const EXEC_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
25
+ /**
26
+ * Run one command through a fresh session connection — the shared state
27
+ * machine for both transports. Closing the connection (abort, timeout, or the
28
+ * far side dying) kills the in-flight guest process.
29
+ */
30
+ export async function execOverSessionConnect(connect, command, options = {}) {
31
+ if (options.signal?.aborted)
32
+ throw abortError();
33
+ const idleTimeoutMs = options.idleTimeoutMs ?? EXEC_IDLE_TIMEOUT_MS;
34
+ return await new Promise((resolve, reject) => {
35
+ let stdout = "";
36
+ let stderr = "";
37
+ let received = false;
38
+ let settled = false;
39
+ let client;
40
+ let idleTimer;
41
+ const settle = (fn) => {
42
+ if (settled)
43
+ return;
44
+ settled = true;
45
+ clearTimeout(idleTimer);
46
+ options.signal?.removeEventListener("abort", onAbort);
47
+ fn();
48
+ };
49
+ const onAbort = () => settle(() => {
50
+ client?.close();
51
+ reject(abortError());
52
+ });
53
+ const onIdle = () => settle(() => {
54
+ client?.close();
55
+ reject(new GondolinRuntimeInterruptedError(`no session activity for ${Math.round(idleTimeoutMs / 1000)}s — killed the command ` +
56
+ `(guest I/O hangs when the workspace sits on a dead mount; check the worker's workspace health)`));
57
+ });
58
+ const armIdleTimer = () => {
59
+ if (idleTimeoutMs <= 0 || settled)
60
+ return;
61
+ clearTimeout(idleTimer);
62
+ idleTimer = setTimeout(onIdle, idleTimeoutMs);
63
+ idleTimer.unref?.();
64
+ };
65
+ armIdleTimer();
66
+ const callbacks = {
67
+ onJson: (message) => {
68
+ received = true;
69
+ armIdleTimer();
70
+ if (message.type === "exec_response" && message.id === 1) {
71
+ settle(() => {
72
+ client?.close();
73
+ resolve({
74
+ stdout,
75
+ stderr,
76
+ code: typeof message.exit_code === "number" ? message.exit_code : 1,
77
+ });
78
+ });
79
+ }
80
+ else if (message.type === "error") {
81
+ settle(() => {
82
+ client?.close();
83
+ reject(new Error(`Error: Gondolin exec failed (${message.code}): ${message.message}`));
84
+ });
85
+ }
86
+ },
87
+ onBinary: (frame) => {
88
+ received = true;
89
+ armIdleTimer();
90
+ const tag = frame.readUInt8(0);
91
+ const data = frame.subarray(5).toString("utf8");
92
+ if (tag === 1)
93
+ stdout += data;
94
+ else
95
+ stderr += data;
96
+ },
97
+ onClose: (error) => {
98
+ settle(() => {
99
+ const detail = error?.message ?? "connection closed before the command finished";
100
+ const code = error?.code;
101
+ if (!received && (code === "ENOENT" || code === "ECONNREFUSED")) {
102
+ reject(new GondolinRuntimeGoneError(detail));
103
+ }
104
+ else {
105
+ reject(new GondolinRuntimeInterruptedError(detail));
106
+ }
107
+ });
108
+ },
109
+ };
110
+ options.signal?.addEventListener("abort", onAbort, { once: true });
111
+ Promise.resolve(connect(callbacks))
112
+ .then((created) => {
113
+ client = created;
114
+ if (settled) {
115
+ created.close();
116
+ return;
117
+ }
118
+ created.send({
119
+ type: "exec",
120
+ id: 1,
121
+ cmd: "/bin/sh",
122
+ argv: ["-c", command],
123
+ env: Object.entries(options.env ?? {}).map(([key, value]) => `${key}=${value}`),
124
+ cwd: "/workspace",
125
+ });
126
+ })
127
+ .catch((err) => settle(() => reject(new GondolinRuntimeGoneError(err instanceof Error ? err.message : String(err)))));
128
+ });
129
+ }
130
+ /** Workers self-stop once no mikan has refreshed the heartbeat for this long. */
131
+ const WORKER_HEARTBEAT_STALE_MS = 45 * 60 * 1000;
132
+ class GondolinWorkerClient {
133
+ constructor() {
134
+ /** Session IPC socket per runtime — transport-private, never on the handle. */
135
+ this.socketPaths = new Map();
136
+ this.spawnProcess = defaultSpawnProcess;
137
+ this.connectImpl = defaultConnect;
138
+ this.killImpl = (pid, signal) => process.kill(pid, signal);
139
+ this.handshakeTimeoutMs = 120_000;
140
+ this.stopWaitMs = 15_000;
141
+ this.stopPollIntervalMs = 100;
142
+ }
143
+ configure(overrides) {
144
+ this.spawnProcess = overrides?.spawnProcess ?? defaultSpawnProcess;
145
+ this.connectImpl = overrides?.connect ?? defaultConnect;
146
+ this.killImpl = overrides?.kill ?? ((pid, signal) => process.kill(pid, signal));
147
+ this.handshakeTimeoutMs = overrides?.handshakeTimeoutMs ?? 120_000;
148
+ this.stopWaitMs = overrides?.stopWaitMs ?? 15_000;
149
+ this.stopPollIntervalMs = overrides?.stopPollIntervalMs ?? 100;
150
+ }
151
+ /** Adopt a surviving local worker or spawn a fresh one for the spec. */
152
+ async ensure(instanceId, spec) {
153
+ const adopted = await this.adopt(instanceId, spec.fingerprint);
154
+ if (adopted)
155
+ return adopted;
156
+ return this.spawn({
157
+ instanceId,
158
+ image: spec.image,
159
+ mounts: spec.mounts,
160
+ cpus: spec.vmCpus,
161
+ memory: spec.memory,
162
+ fingerprint: spec.fingerprint,
163
+ inventoryDir: gondolinInventory.directory() ?? join(homedir(), ".mikan", "gondolin-runtimes"),
164
+ heartbeatStaleMs: WORKER_HEARTBEAT_STALE_MS,
165
+ });
166
+ }
167
+ /** Whether the runtime's worker process is still alive. */
168
+ async isRuntimeAlive(handle) {
169
+ return this.isPidAlive(handle.workerPid);
170
+ }
171
+ /** Spawn a detached worker for the config and wait for its ready handshake. */
172
+ async spawn(config) {
173
+ const entry = fileURLToPath(new URL("./gondolin-worker-main.js", import.meta.url));
174
+ const child = this.spawnProcess(process.execPath, [entry, JSON.stringify(config)]);
175
+ const handshake = await this.readHandshake(child);
176
+ if (!handshake.ready || !handshake.sessionId || !handshake.socketPath) {
177
+ throw new SandboxError(`Error: Gondolin worker failed to start: ${handshake.error ?? "no session in handshake"}`);
178
+ }
179
+ child.stdout?.destroy();
180
+ child.stderr?.destroy();
181
+ child.unref();
182
+ this.socketPaths.set(handshake.sessionId, handshake.socketPath);
183
+ return {
184
+ sessionId: handshake.sessionId,
185
+ instanceId: config.instanceId,
186
+ workerPid: handshake.workerPid ?? child.pid,
187
+ fingerprint: config.fingerprint,
188
+ };
189
+ }
190
+ /**
191
+ * Adopt a runtime a previous mikan left running: newest live record for the
192
+ * key whose fingerprint still matches and whose session answers a probe.
193
+ * Anything stale — drifted fingerprint, unresponsive session — is stopped so
194
+ * the caller can spawn fresh.
195
+ */
196
+ async adopt(instanceId, fingerprint) {
197
+ const record = await gondolinInventory.findAdoptable(instanceId);
198
+ if (!record?.socketPath)
199
+ return undefined;
200
+ this.socketPaths.set(record.sessionId, record.socketPath);
201
+ const handle = {
202
+ sessionId: record.sessionId,
203
+ instanceId,
204
+ workerPid: record.ownerPid,
205
+ fingerprint: record.fingerprint ?? "",
206
+ };
207
+ if (record.fingerprint !== fingerprint) {
208
+ await this.stop(handle);
209
+ return undefined;
210
+ }
211
+ try {
212
+ await this.exec(handle, "true", { signal: AbortSignal.timeout(5000) });
213
+ }
214
+ catch {
215
+ await this.stop(handle);
216
+ return undefined;
217
+ }
218
+ return handle;
219
+ }
220
+ /**
221
+ * Stop a worker and wait for it to clean up after itself; escalate to
222
+ * SIGKILL and reap its leftovers if it will not die.
223
+ */
224
+ async stop(handle) {
225
+ this.socketPaths.delete(handle.sessionId);
226
+ if (this.isPidAlive(handle.workerPid)) {
227
+ this.signal(handle.workerPid, "SIGTERM");
228
+ if (!(await this.waitForExit(handle.workerPid, this.stopWaitMs))) {
229
+ this.signal(handle.workerPid, "SIGKILL");
230
+ await this.waitForExit(handle.workerPid, 2000);
231
+ }
232
+ }
233
+ await gondolinInventory.reapSession(handle.sessionId);
234
+ }
235
+ /** Whether the worker process behind a handle is still alive. */
236
+ /**
237
+ * Run one command over a dedicated session IPC connection. Aborting (or the
238
+ * caller's timeout signal firing) destroys the connection, which kills the
239
+ * in-flight guest process.
240
+ */
241
+ async exec(handle, command, options = {}) {
242
+ const socketPath = this.socketPaths.get(handle.sessionId);
243
+ if (!socketPath) {
244
+ // the socket was registered at ensure/adopt; a missing entry means this
245
+ // process no longer owns the runtime — recreate the session cleanly
246
+ throw new GondolinRuntimeGoneError("no session socket known for this runtime");
247
+ }
248
+ return execOverSessionConnect((callbacks) => this.connectImpl(socketPath, callbacks), command, options);
249
+ }
250
+ signal(pid, signal) {
251
+ try {
252
+ this.killImpl(pid, signal);
253
+ }
254
+ catch {
255
+ // already gone
256
+ }
257
+ }
258
+ isPidAlive(pid) {
259
+ try {
260
+ this.killImpl(pid, 0);
261
+ return true;
262
+ }
263
+ catch {
264
+ return false;
265
+ }
266
+ }
267
+ async waitForExit(pid, waitMs) {
268
+ const deadline = Date.now() + waitMs;
269
+ while (this.isPidAlive(pid)) {
270
+ if (Date.now() >= deadline)
271
+ return false;
272
+ await new Promise((resolve) => setTimeout(resolve, this.stopPollIntervalMs));
273
+ }
274
+ return true;
275
+ }
276
+ readHandshake(child) {
277
+ return new Promise((resolve, reject) => {
278
+ let buffer = "";
279
+ let stderr = "";
280
+ let settled = false;
281
+ const settle = (fn) => {
282
+ if (settled)
283
+ return;
284
+ settled = true;
285
+ clearTimeout(timer);
286
+ fn();
287
+ };
288
+ const timer = setTimeout(() => {
289
+ settle(() => {
290
+ child.kill("SIGKILL");
291
+ reject(new SandboxError(`Error: Gondolin worker did not become ready within ${this.handshakeTimeoutMs}ms${stderr ? `: ${stderr.trim()}` : ""}`));
292
+ });
293
+ }, this.handshakeTimeoutMs);
294
+ child.stdout?.on("data", (chunk) => {
295
+ buffer += chunk.toString("utf8");
296
+ const newline = buffer.indexOf("\n");
297
+ if (newline < 0)
298
+ return;
299
+ const line = buffer.slice(0, newline);
300
+ settle(() => {
301
+ try {
302
+ resolve(JSON.parse(line));
303
+ }
304
+ catch {
305
+ child.kill("SIGKILL");
306
+ reject(new SandboxError(`Error: invalid Gondolin worker handshake: ${line}`));
307
+ }
308
+ });
309
+ });
310
+ child.stderr?.on("data", (chunk) => {
311
+ stderr += chunk.toString("utf8");
312
+ });
313
+ child.once("close", (code) => {
314
+ settle(() => reject(new SandboxError(`Error: Gondolin worker exited before becoming ready (code ${code})${stderr ? `: ${stderr.trim()}` : ""}`)));
315
+ });
316
+ child.once("error", (error) => {
317
+ settle(() => reject(new SandboxError(`Error: failed to spawn Gondolin worker: ${error.message}`)));
318
+ });
319
+ });
320
+ }
321
+ }
322
+ export const gondolinWorkers = new GondolinWorkerClient();
323
+ //# sourceMappingURL=gondolin-worker-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-worker-client.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-client.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport { fileURLToPath } from \"node:url\";\nimport { SandboxError } from \"./errors.js\";\nimport { gondolinInventory, type GondolinRuntimeRecord } from \"./gondolin-inventory.js\";\nimport type { GondolinWorkerConfig, GondolinWorkerHandshake } from \"./gondolin-worker.js\";\nimport type { ExecResult } from \"./types.js\";\n\n/**\n * A live worker-hosted runtime as seen from mikan: identity plus fleet\n * routing only. Transport-private connection details (the local worker's\n * session socket, the remote worker's lease/tunnel) stay inside the owning\n * transport — a handle never carries another transport's address fields.\n */\nexport interface GondolinRuntimeHandle {\n sessionId: string;\n instanceId: string;\n workerPid: number;\n fingerprint: string;\n /** Fleet identity of the worker hosting the runtime (remote transport). */\n workerName?: string;\n}\n\n/**\n * What a runtime should look like, resolved by the executor layer. `image` is\n * a local asset directory for the local transport and an image selector for\n * the remote one (resolved on the worker host).\n */\nexport interface GondolinRuntimeSpec {\n image: string;\n mounts: Array<{ source: string; target: string }>;\n /** Raw fractional CPU limit (cgroup enforcement on remote Linux workers). */\n cpus?: string;\n /** Whole vCPUs given to the VM. */\n vmCpus?: number;\n memory?: string;\n fingerprint: string;\n /** mikan-host workspace root, for remote mount prefix translation. */\n workspacePath?: string;\n}\n\n/** One way of running Gondolin runtimes: in local workers or on a remote fleet. */\nexport interface GondolinRuntimeTransport {\n ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle>;\n stop(\n handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\" | \"instanceId\" | \"workerName\">,\n ): Promise<void>;\n exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options?: { env?: Record<string, string>; signal?: AbortSignal },\n ): Promise<ExecResult>;\n isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean>;\n}\n\nimport { GondolinRuntimeGoneError, GondolinRuntimeInterruptedError } from \"./gondolin-recovery.js\";\n\ninterface WorkerProcessLike {\n pid?: number;\n stdout: Readable | null;\n stderr: Readable | null;\n once(event: \"close\", listener: (code: number | null) => void): unknown;\n once(event: \"error\", listener: (error: Error) => void): unknown;\n kill(signal?: NodeJS.Signals): boolean;\n unref(): void;\n}\n\nexport interface SessionClientCallbacks {\n onJson: (message: {\n type: string;\n id?: number;\n exit_code?: number | null;\n code?: string;\n message?: string;\n }) => void;\n onBinary: (frame: Buffer) => void;\n onClose: (error?: Error) => void;\n}\n\nexport interface SessionClient {\n send(message: object): void;\n close(): void;\n}\n\ninterface GondolinWorkerClientOverrides {\n spawnProcess?: (command: string, args: string[]) => WorkerProcessLike;\n connect?: (socketPath: string, callbacks: SessionClientCallbacks) => SessionClient;\n kill?: (pid: number, signal: NodeJS.Signals | 0) => void;\n handshakeTimeoutMs?: number;\n stopWaitMs?: number;\n stopPollIntervalMs?: number;\n}\n\nfunction defaultSpawnProcess(command: string, args: string[]): WorkerProcessLike {\n return spawn(command, args, { detached: true, stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n}\n\nasync function defaultConnect(\n socketPath: string,\n callbacks: SessionClientCallbacks,\n): Promise<SessionClient> {\n const { connectToSession } = await import(\"@earendil-works/gondolin\");\n return connectToSession(socketPath, callbacks);\n}\n\nfunction abortError(): Error {\n return new Error(\"Error: command aborted\");\n}\n\n/**\n * A command may quietly work for a long time, but a session that produces no\n * frame at all for this long is indistinguishable from one wedged on dead\n * storage (a hung NFS mount under the workspace blocks guest I/O forever) —\n * kill it and say so instead of hanging the run.\n */\nconst EXEC_IDLE_TIMEOUT_MS = 10 * 60 * 1000;\n\n/**\n * Run one command through a fresh session connection — the shared state\n * machine for both transports. Closing the connection (abort, timeout, or the\n * far side dying) kills the in-flight guest process.\n */\nexport async function execOverSessionConnect(\n connect: (callbacks: SessionClientCallbacks) => SessionClient | Promise<SessionClient>,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal; idleTimeoutMs?: number } = {},\n): Promise<ExecResult> {\n if (options.signal?.aborted) throw abortError();\n const idleTimeoutMs = options.idleTimeoutMs ?? EXEC_IDLE_TIMEOUT_MS;\n return await new Promise<ExecResult>((resolve, reject) => {\n let stdout = \"\";\n let stderr = \"\";\n let received = false;\n let settled = false;\n let client: SessionClient | undefined;\n let idleTimer: NodeJS.Timeout | undefined;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(idleTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n fn();\n };\n const onAbort = (): void =>\n settle(() => {\n client?.close();\n reject(abortError());\n });\n const onIdle = (): void =>\n settle(() => {\n client?.close();\n reject(\n new GondolinRuntimeInterruptedError(\n `no session activity for ${Math.round(idleTimeoutMs / 1000)}s — killed the command ` +\n `(guest I/O hangs when the workspace sits on a dead mount; check the worker's workspace health)`,\n ),\n );\n });\n const armIdleTimer = (): void => {\n if (idleTimeoutMs <= 0 || settled) return;\n clearTimeout(idleTimer);\n idleTimer = setTimeout(onIdle, idleTimeoutMs);\n idleTimer.unref?.();\n };\n armIdleTimer();\n const callbacks: SessionClientCallbacks = {\n onJson: (message) => {\n received = true;\n armIdleTimer();\n if (message.type === \"exec_response\" && message.id === 1) {\n settle(() => {\n client?.close();\n resolve({\n stdout,\n stderr,\n code: typeof message.exit_code === \"number\" ? message.exit_code : 1,\n });\n });\n } else if (message.type === \"error\") {\n settle(() => {\n client?.close();\n reject(new Error(`Error: Gondolin exec failed (${message.code}): ${message.message}`));\n });\n }\n },\n onBinary: (frame) => {\n received = true;\n armIdleTimer();\n const tag = frame.readUInt8(0);\n const data = frame.subarray(5).toString(\"utf8\");\n if (tag === 1) stdout += data;\n else stderr += data;\n },\n onClose: (error) => {\n settle(() => {\n const detail = error?.message ?? \"connection closed before the command finished\";\n const code = (error as NodeJS.ErrnoException | undefined)?.code;\n if (!received && (code === \"ENOENT\" || code === \"ECONNREFUSED\")) {\n reject(new GondolinRuntimeGoneError(detail));\n } else {\n reject(new GondolinRuntimeInterruptedError(detail));\n }\n });\n },\n };\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n Promise.resolve(connect(callbacks))\n .then((created) => {\n client = created;\n if (settled) {\n created.close();\n return;\n }\n created.send({\n type: \"exec\",\n id: 1,\n cmd: \"/bin/sh\",\n argv: [\"-c\", command],\n env: Object.entries(options.env ?? {}).map(([key, value]) => `${key}=${value}`),\n cwd: \"/workspace\",\n });\n })\n .catch((err: unknown) =>\n settle(() =>\n reject(new GondolinRuntimeGoneError(err instanceof Error ? err.message : String(err))),\n ),\n );\n });\n}\n\n/** Workers self-stop once no mikan has refreshed the heartbeat for this long. */\nconst WORKER_HEARTBEAT_STALE_MS = 45 * 60 * 1000;\n\nclass GondolinWorkerClient implements GondolinRuntimeTransport {\n /** Session IPC socket per runtime — transport-private, never on the handle. */\n private readonly socketPaths = new Map<string, string>();\n private spawnProcess = defaultSpawnProcess;\n private connectImpl: (\n socketPath: string,\n callbacks: SessionClientCallbacks,\n ) => SessionClient | Promise<SessionClient> = defaultConnect;\n private killImpl: (pid: number, signal: NodeJS.Signals | 0) => void = (pid, signal) =>\n process.kill(pid, signal);\n private handshakeTimeoutMs = 120_000;\n private stopWaitMs = 15_000;\n private stopPollIntervalMs = 100;\n\n configure(overrides?: GondolinWorkerClientOverrides): void {\n this.spawnProcess = overrides?.spawnProcess ?? defaultSpawnProcess;\n this.connectImpl = overrides?.connect ?? defaultConnect;\n this.killImpl = overrides?.kill ?? ((pid, signal) => process.kill(pid, signal));\n this.handshakeTimeoutMs = overrides?.handshakeTimeoutMs ?? 120_000;\n this.stopWaitMs = overrides?.stopWaitMs ?? 15_000;\n this.stopPollIntervalMs = overrides?.stopPollIntervalMs ?? 100;\n }\n\n /** Adopt a surviving local worker or spawn a fresh one for the spec. */\n async ensure(instanceId: string, spec: GondolinRuntimeSpec): Promise<GondolinRuntimeHandle> {\n const adopted = await this.adopt(instanceId, spec.fingerprint);\n if (adopted) return adopted;\n return this.spawn({\n instanceId,\n image: spec.image,\n mounts: spec.mounts,\n cpus: spec.vmCpus,\n memory: spec.memory,\n fingerprint: spec.fingerprint,\n inventoryDir: gondolinInventory.directory() ?? join(homedir(), \".mikan\", \"gondolin-runtimes\"),\n heartbeatStaleMs: WORKER_HEARTBEAT_STALE_MS,\n });\n }\n\n /** Whether the runtime's worker process is still alive. */\n async isRuntimeAlive(handle: GondolinRuntimeHandle): Promise<boolean> {\n return this.isPidAlive(handle.workerPid);\n }\n\n /** Spawn a detached worker for the config and wait for its ready handshake. */\n async spawn(config: GondolinWorkerConfig): Promise<GondolinRuntimeHandle> {\n const entry = fileURLToPath(new URL(\"./gondolin-worker-main.js\", import.meta.url));\n const child = this.spawnProcess(process.execPath, [entry, JSON.stringify(config)]);\n const handshake = await this.readHandshake(child);\n if (!handshake.ready || !handshake.sessionId || !handshake.socketPath) {\n throw new SandboxError(\n `Error: Gondolin worker failed to start: ${handshake.error ?? \"no session in handshake\"}`,\n );\n }\n child.stdout?.destroy();\n child.stderr?.destroy();\n child.unref();\n this.socketPaths.set(handshake.sessionId, handshake.socketPath);\n return {\n sessionId: handshake.sessionId,\n instanceId: config.instanceId,\n workerPid: handshake.workerPid ?? (child.pid as number),\n fingerprint: config.fingerprint,\n };\n }\n\n /**\n * Adopt a runtime a previous mikan left running: newest live record for the\n * key whose fingerprint still matches and whose session answers a probe.\n * Anything stale — drifted fingerprint, unresponsive session — is stopped so\n * the caller can spawn fresh.\n */\n async adopt(instanceId: string, fingerprint: string): Promise<GondolinRuntimeHandle | undefined> {\n const record: GondolinRuntimeRecord | undefined =\n await gondolinInventory.findAdoptable(instanceId);\n if (!record?.socketPath) return undefined;\n this.socketPaths.set(record.sessionId, record.socketPath);\n const handle: GondolinRuntimeHandle = {\n sessionId: record.sessionId,\n instanceId,\n workerPid: record.ownerPid,\n fingerprint: record.fingerprint ?? \"\",\n };\n if (record.fingerprint !== fingerprint) {\n await this.stop(handle);\n return undefined;\n }\n try {\n await this.exec(handle, \"true\", { signal: AbortSignal.timeout(5000) });\n } catch {\n await this.stop(handle);\n return undefined;\n }\n return handle;\n }\n\n /**\n * Stop a worker and wait for it to clean up after itself; escalate to\n * SIGKILL and reap its leftovers if it will not die.\n */\n async stop(handle: Pick<GondolinRuntimeHandle, \"workerPid\" | \"sessionId\">): Promise<void> {\n this.socketPaths.delete(handle.sessionId);\n if (this.isPidAlive(handle.workerPid)) {\n this.signal(handle.workerPid, \"SIGTERM\");\n if (!(await this.waitForExit(handle.workerPid, this.stopWaitMs))) {\n this.signal(handle.workerPid, \"SIGKILL\");\n await this.waitForExit(handle.workerPid, 2000);\n }\n }\n await gondolinInventory.reapSession(handle.sessionId);\n }\n\n /** Whether the worker process behind a handle is still alive. */\n /**\n * Run one command over a dedicated session IPC connection. Aborting (or the\n * caller's timeout signal firing) destroys the connection, which kills the\n * in-flight guest process.\n */\n async exec(\n handle: GondolinRuntimeHandle,\n command: string,\n options: { env?: Record<string, string>; signal?: AbortSignal } = {},\n ): Promise<ExecResult> {\n const socketPath = this.socketPaths.get(handle.sessionId);\n if (!socketPath) {\n // the socket was registered at ensure/adopt; a missing entry means this\n // process no longer owns the runtime — recreate the session cleanly\n throw new GondolinRuntimeGoneError(\"no session socket known for this runtime\");\n }\n return execOverSessionConnect(\n (callbacks) => this.connectImpl(socketPath, callbacks),\n command,\n options,\n );\n }\n\n private signal(pid: number, signal: NodeJS.Signals): void {\n try {\n this.killImpl(pid, signal);\n } catch {\n // already gone\n }\n }\n\n private isPidAlive(pid: number): boolean {\n try {\n this.killImpl(pid, 0);\n return true;\n } catch {\n return false;\n }\n }\n\n private async waitForExit(pid: number, waitMs: number): Promise<boolean> {\n const deadline = Date.now() + waitMs;\n while (this.isPidAlive(pid)) {\n if (Date.now() >= deadline) return false;\n await new Promise((resolve) => setTimeout(resolve, this.stopPollIntervalMs));\n }\n return true;\n }\n\n private readHandshake(child: WorkerProcessLike): Promise<GondolinWorkerHandshake> {\n return new Promise((resolve, reject) => {\n let buffer = \"\";\n let stderr = \"\";\n let settled = false;\n const settle = (fn: () => void): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n fn();\n };\n const timer = setTimeout(() => {\n settle(() => {\n child.kill(\"SIGKILL\");\n reject(\n new SandboxError(\n `Error: Gondolin worker did not become ready within ${this.handshakeTimeoutMs}ms${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n );\n });\n }, this.handshakeTimeoutMs);\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString(\"utf8\");\n const newline = buffer.indexOf(\"\\n\");\n if (newline < 0) return;\n const line = buffer.slice(0, newline);\n settle(() => {\n try {\n resolve(JSON.parse(line) as GondolinWorkerHandshake);\n } catch {\n child.kill(\"SIGKILL\");\n reject(new SandboxError(`Error: invalid Gondolin worker handshake: ${line}`));\n }\n });\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString(\"utf8\");\n });\n child.once(\"close\", (code) => {\n settle(() =>\n reject(\n new SandboxError(\n `Error: Gondolin worker exited before becoming ready (code ${code})${\n stderr ? `: ${stderr.trim()}` : \"\"\n }`,\n ),\n ),\n );\n });\n child.once(\"error\", (error) => {\n settle(() =>\n reject(new SandboxError(`Error: failed to spawn Gondolin worker: ${error.message}`)),\n );\n });\n });\n }\n}\n\nexport const gondolinWorkers = new GondolinWorkerClient();\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gondolin-worker-main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-worker-main.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ // Entry point for the detached Gondolin worker process. Spawned by mikan with
2
+ // one JSON argv (the GondolinWorkerConfig); survives the spawning mikan so the
3
+ // VM keeps running across mikan restarts and deploys.
4
+ import { runGondolinWorker } from "./gondolin-worker.js";
5
+ // The spawning mikan may be gone when we write; a broken pipe must not crash
6
+ // a worker that is otherwise healthy.
7
+ process.stdout.on("error", () => { });
8
+ process.stderr.on("error", () => { });
9
+ let config;
10
+ try {
11
+ config = JSON.parse(process.argv[2] ?? "");
12
+ }
13
+ catch {
14
+ process.stdout.write(JSON.stringify({ ready: false, error: "invalid worker config" }) + "\n");
15
+ process.exit(2);
16
+ }
17
+ runGondolinWorker(config).catch((err) => {
18
+ const error = err instanceof Error ? err.message : String(err);
19
+ process.stdout.write(JSON.stringify({ ready: false, error }) + "\n");
20
+ process.exit(1);
21
+ });
22
+ //# sourceMappingURL=gondolin-worker-main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-worker-main.js","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker-main.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,sDAAsD;AACtD,OAAO,EAAE,iBAAiB,EAA6B,MAAM,sBAAsB,CAAC;AAEpF,6EAA6E;AAC7E,sCAAsC;AACtC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAErC,IAAI,MAA4B,CAAC;AACjC,IAAI,CAAC;IACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAyB,CAAC;AACrE,CAAC;AAAC,MAAM,CAAC;IACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,iBAAiB,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC/C,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC","sourcesContent":["// Entry point for the detached Gondolin worker process. Spawned by mikan with\n// one JSON argv (the GondolinWorkerConfig); survives the spawning mikan so the\n// VM keeps running across mikan restarts and deploys.\nimport { runGondolinWorker, type GondolinWorkerConfig } from \"./gondolin-worker.js\";\n\n// The spawning mikan may be gone when we write; a broken pipe must not crash\n// a worker that is otherwise healthy.\nprocess.stdout.on(\"error\", () => {});\nprocess.stderr.on(\"error\", () => {});\n\nlet config: GondolinWorkerConfig;\ntry {\n config = JSON.parse(process.argv[2] ?? \"\") as GondolinWorkerConfig;\n} catch {\n process.stdout.write(JSON.stringify({ ready: false, error: \"invalid worker config\" }) + \"\\n\");\n process.exit(2);\n}\n\nrunGondolinWorker(config).catch((err: unknown) => {\n const error = err instanceof Error ? err.message : String(err);\n process.stdout.write(JSON.stringify({ ready: false, error }) + \"\\n\");\n process.exit(1);\n});\n"]}
@@ -0,0 +1,17 @@
1
+ type GondolinModule = typeof import("@earendil-works/gondolin");
2
+ export type { GondolinWorkerConfig, GondolinWorkerHandshake } from "./gondolin-contract.js";
3
+ import type { GondolinWorkerConfig } from "./gondolin-contract.js";
4
+ interface GondolinWorkerDeps {
5
+ loadGondolin?: () => Promise<GondolinModule>;
6
+ announce?: (line: string) => void;
7
+ exit?: (code: number) => void;
8
+ pollIntervalMs?: number;
9
+ }
10
+ /**
11
+ * Host one Gondolin VM for its whole life: boot it, persist the inventory
12
+ * record other processes adopt it through, announce readiness on stdout, then
13
+ * watch the VM runner and shut down when it dies, when a SIGTERM arrives, or
14
+ * when every mikan that could adopt the runtime is gone (stale heartbeat).
15
+ */
16
+ export declare function runGondolinWorker(config: GondolinWorkerConfig, deps?: GondolinWorkerDeps): Promise<void>;
17
+ //# sourceMappingURL=gondolin-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gondolin-worker.d.ts","sourceRoot":"","sources":["../../src/sandbox/gondolin-worker.ts"],"names":[],"mappings":"AAMA,KAAK,cAAc,GAAG,cAAc,0BAA0B,CAAC,CAAC;AAKhE,YAAY,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,oBAAoB,EAA2B,MAAM,wBAAwB,CAAC;AAE5F,UAAU,kBAAkB;IAC1B,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA8Hf"}
@@ -0,0 +1,201 @@
1
+ import { createHash } from "node:crypto";
2
+ import { chmodSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { gondolinInventory } from "./gondolin-inventory.js";
5
+ import { shellEscape } from "./utils.js";
6
+ function sha256(data) {
7
+ return createHash("sha256").update(data).digest("hex");
8
+ }
9
+ function warn(message) {
10
+ process.stderr.write(`[gondolin-worker] ${message}\n`);
11
+ }
12
+ function heartbeatFresh(path, since, staleMs) {
13
+ let mtimeMs = 0;
14
+ try {
15
+ mtimeMs = statSync(path).mtimeMs;
16
+ }
17
+ catch {
18
+ // no heartbeat yet — fall back to worker start time
19
+ }
20
+ return Date.now() - Math.max(mtimeMs, since) <= staleMs;
21
+ }
22
+ function restrictSocketAccess(socketPath) {
23
+ // The session socket grants arbitrary exec inside the VM (and through its
24
+ // mounts, the workspace) — keep it to this user even under a lax umask.
25
+ try {
26
+ chmodSync(dirname(socketPath), 0o700);
27
+ chmodSync(socketPath, 0o600);
28
+ }
29
+ catch {
30
+ // best-effort hardening
31
+ }
32
+ }
33
+ function partitionMounts(mounts) {
34
+ const directories = [];
35
+ const files = [];
36
+ for (const mount of mounts) {
37
+ let isDirectory;
38
+ try {
39
+ isDirectory = statSync(mount.source).isDirectory();
40
+ }
41
+ catch {
42
+ warn(`skipping missing mount source ${mount.source}`);
43
+ continue;
44
+ }
45
+ if (isDirectory) {
46
+ directories.push(mount);
47
+ }
48
+ else {
49
+ files.push({
50
+ ...mount,
51
+ syncBack: mount.target.startsWith("/workspace/"),
52
+ lastHash: "",
53
+ });
54
+ }
55
+ }
56
+ return { directories, files };
57
+ }
58
+ /**
59
+ * Host one Gondolin VM for its whole life: boot it, persist the inventory
60
+ * record other processes adopt it through, announce readiness on stdout, then
61
+ * watch the VM runner and shut down when it dies, when a SIGTERM arrives, or
62
+ * when every mikan that could adopt the runtime is gone (stale heartbeat).
63
+ */
64
+ export async function runGondolinWorker(config, deps = {}) {
65
+ const announce = deps.announce ?? ((line) => process.stdout.write(line + "\n"));
66
+ const exit = deps.exit ?? ((code) => process.exit(code));
67
+ const pollIntervalMs = deps.pollIntervalMs ?? 2000;
68
+ const loadGondolin = deps.loadGondolin ?? (() => import("@earendil-works/gondolin"));
69
+ gondolinInventory.configure(config.inventoryDir);
70
+ const { VM, RealFSProvider, findSession, ensureImageSelector } = await loadGondolin();
71
+ let imagePath = config.image;
72
+ if (!imagePath) {
73
+ if (!config.imageSelector)
74
+ throw new Error("worker config needs image or imageSelector");
75
+ imagePath = (await ensureImageSelector(config.imageSelector)).assetDir;
76
+ }
77
+ const { directories, files } = partitionMounts(config.mounts);
78
+ const vm = await VM.create({
79
+ sandbox: { imagePath },
80
+ env: { TZ: "Asia/Taipei" },
81
+ sessionLabel: `mikan:${config.instanceId}`,
82
+ cpus: config.cpus,
83
+ memory: config.memory,
84
+ vfs: {
85
+ mounts: Object.fromEntries(directories.map(({ source, target }) => [target, new RealFSProvider(source)])),
86
+ },
87
+ });
88
+ let session;
89
+ try {
90
+ await vm.start();
91
+ await projectFiles(vm, files);
92
+ session = await findSession(vm.id);
93
+ if (!session)
94
+ throw new Error(`Gondolin session '${vm.id}' did not register`);
95
+ }
96
+ catch (error) {
97
+ // never leave a half-booted runner (and its overlay disk) behind
98
+ await vm.close().catch(() => { });
99
+ throw error;
100
+ }
101
+ restrictSocketAccess(session.socketPath);
102
+ gondolinInventory.record({
103
+ sessionId: vm.id,
104
+ instanceId: config.instanceId,
105
+ runnerPid: vm.getHostPid(),
106
+ socketPath: session.socketPath,
107
+ fingerprint: config.fingerprint,
108
+ });
109
+ const startedAt = Date.now();
110
+ let closing = false;
111
+ let syncing = false;
112
+ const syncProjections = async () => {
113
+ if (syncing)
114
+ return;
115
+ syncing = true;
116
+ try {
117
+ for (const projection of files) {
118
+ if (!projection.syncBack)
119
+ continue;
120
+ try {
121
+ const data = await vm.fs.readFile(projection.target);
122
+ const content = Buffer.isBuffer(data) ? data : Buffer.from(String(data));
123
+ const hash = sha256(content);
124
+ if (hash === projection.lastHash)
125
+ continue;
126
+ // atomic host write: a crash mid-sync must not truncate the file
127
+ const staged = `${projection.source}.mikan-sync`;
128
+ writeFileSync(staged, content);
129
+ renameSync(staged, projection.source);
130
+ projection.lastHash = hash;
131
+ }
132
+ catch {
133
+ // guest file missing or read raced a writer; next tick retries
134
+ }
135
+ }
136
+ }
137
+ finally {
138
+ syncing = false;
139
+ }
140
+ };
141
+ const shutdown = async (code) => {
142
+ if (closing)
143
+ return;
144
+ closing = true;
145
+ clearInterval(watchdog);
146
+ try {
147
+ await syncProjections();
148
+ }
149
+ catch {
150
+ // best effort; the periodic sync already captured earlier edits
151
+ }
152
+ try {
153
+ await vm.close();
154
+ }
155
+ catch {
156
+ // the runner may already be gone
157
+ }
158
+ gondolinInventory.release(vm.id);
159
+ exit(code);
160
+ };
161
+ const watchdog = setInterval(() => {
162
+ const runnerPid = vm.getHostPid();
163
+ if (runnerPid === null) {
164
+ // The VM runner died under us; exit so mikan sees the socket close and
165
+ // recreates the runtime instead of hanging on a silent session.
166
+ void shutdown(1);
167
+ return;
168
+ }
169
+ gondolinInventory.refresh(vm.id, runnerPid);
170
+ void syncProjections();
171
+ const heartbeat = gondolinInventory.heartbeatPath();
172
+ if (config.heartbeatStaleMs > 0 &&
173
+ heartbeat &&
174
+ !heartbeatFresh(heartbeat, startedAt, config.heartbeatStaleMs)) {
175
+ void shutdown(0);
176
+ }
177
+ }, pollIntervalMs);
178
+ process.on("SIGTERM", () => void shutdown(0));
179
+ process.on("SIGINT", () => void shutdown(0));
180
+ announce(JSON.stringify({
181
+ ready: true,
182
+ sessionId: vm.id,
183
+ socketPath: session.socketPath,
184
+ workerPid: process.pid,
185
+ runnerPid: vm.getHostPid(),
186
+ }));
187
+ }
188
+ /** Copy file mounts into the guest; credentials get user-only permissions. */
189
+ async function projectFiles(vm, files) {
190
+ for (const projection of files) {
191
+ const content = readFileSync(projection.source);
192
+ await vm.fs.mkdir(dirname(projection.target), { recursive: true });
193
+ await vm.fs.writeFile(projection.target, content);
194
+ if (!projection.syncBack) {
195
+ // credential projection: no write-back, keep it owner-only
196
+ await vm.exec(`chmod 600 ${shellEscape(projection.target)}`);
197
+ }
198
+ projection.lastHash = sha256(content);
199
+ }
200
+ }
201
+ //# sourceMappingURL=gondolin-worker.js.map