@pleri/olam-cli 0.1.69 → 0.1.72

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 (250) hide show
  1. package/dist/cli-version.d.ts +16 -0
  2. package/dist/cli-version.d.ts.map +1 -0
  3. package/dist/cli-version.js +39 -0
  4. package/dist/cli-version.js.map +1 -0
  5. package/dist/commands/create.d.ts.map +1 -1
  6. package/dist/commands/create.js +2 -0
  7. package/dist/commands/create.js.map +1 -1
  8. package/dist/commands/host-cp.d.ts.map +1 -1
  9. package/dist/commands/host-cp.js +10 -0
  10. package/dist/commands/host-cp.js.map +1 -1
  11. package/dist/commands/runbooks.d.ts +13 -0
  12. package/dist/commands/runbooks.d.ts.map +1 -0
  13. package/dist/commands/runbooks.js +189 -0
  14. package/dist/commands/runbooks.js.map +1 -0
  15. package/dist/commands/world-snapshot.d.ts +1 -0
  16. package/dist/commands/world-snapshot.d.ts.map +1 -1
  17. package/dist/commands/world-snapshot.js +126 -1
  18. package/dist/commands/world-snapshot.js.map +1 -1
  19. package/dist/commands/worldspec/compile.d.ts +20 -0
  20. package/dist/commands/worldspec/compile.d.ts.map +1 -0
  21. package/dist/commands/worldspec/compile.js +130 -0
  22. package/dist/commands/worldspec/compile.js.map +1 -0
  23. package/dist/commands/worldspec/index.d.ts +12 -0
  24. package/dist/commands/worldspec/index.d.ts.map +1 -0
  25. package/dist/commands/worldspec/index.js +23 -0
  26. package/dist/commands/worldspec/index.js.map +1 -0
  27. package/dist/commands/worldspec/init.d.ts +15 -0
  28. package/dist/commands/worldspec/init.d.ts.map +1 -0
  29. package/dist/commands/worldspec/init.js +166 -0
  30. package/dist/commands/worldspec/init.js.map +1 -0
  31. package/dist/commands/worldspec/schema.d.ts +11 -0
  32. package/dist/commands/worldspec/schema.d.ts.map +1 -0
  33. package/dist/commands/worldspec/schema.js +55 -0
  34. package/dist/commands/worldspec/schema.js.map +1 -0
  35. package/dist/commands/worldspec/validate.d.ts +15 -0
  36. package/dist/commands/worldspec/validate.d.ts.map +1 -0
  37. package/dist/commands/worldspec/validate.js +66 -0
  38. package/dist/commands/worldspec/validate.js.map +1 -0
  39. package/dist/exit-codes.d.ts +32 -0
  40. package/dist/exit-codes.d.ts.map +1 -1
  41. package/dist/exit-codes.js +32 -0
  42. package/dist/exit-codes.js.map +1 -1
  43. package/dist/image-digests.json +5 -5
  44. package/dist/index.js +4726 -1231
  45. package/dist/index.js.map +1 -1
  46. package/dist/mcp-server.js +1424 -321
  47. package/host-cp/compose.yaml +6 -0
  48. package/host-cp/src/global-config-source.mjs +71 -0
  49. package/host-cp/src/listening-server-poller.mjs +1 -1
  50. package/host-cp/src/plan-orchestrator.mjs +20 -1
  51. package/host-cp/src/port-bridge-manager.mjs +1 -1
  52. package/host-cp/src/server.mjs +46 -7
  53. package/host-cp/src/version-status.mjs +36 -0
  54. package/package.json +4 -2
  55. package/dist/__tests__/audit-publish-deps-contract.test.d.ts +0 -26
  56. package/dist/__tests__/audit-publish-deps-contract.test.d.ts.map +0 -1
  57. package/dist/__tests__/audit-publish-deps-contract.test.js +0 -86
  58. package/dist/__tests__/audit-publish-deps-contract.test.js.map +0 -1
  59. package/dist/__tests__/auth-status.test.d.ts +0 -2
  60. package/dist/__tests__/auth-status.test.d.ts.map +0 -1
  61. package/dist/__tests__/auth-status.test.js +0 -291
  62. package/dist/__tests__/auth-status.test.js.map +0 -1
  63. package/dist/__tests__/auth-upgrade.test.d.ts +0 -9
  64. package/dist/__tests__/auth-upgrade.test.d.ts.map +0 -1
  65. package/dist/__tests__/auth-upgrade.test.js +0 -397
  66. package/dist/__tests__/auth-upgrade.test.js.map +0 -1
  67. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts +0 -22
  68. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts.map +0 -1
  69. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js +0 -63
  70. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js.map +0 -1
  71. package/dist/__tests__/cli-mcp-revoke.test.d.ts +0 -8
  72. package/dist/__tests__/cli-mcp-revoke.test.d.ts.map +0 -1
  73. package/dist/__tests__/cli-mcp-revoke.test.js +0 -124
  74. package/dist/__tests__/cli-mcp-revoke.test.js.map +0 -1
  75. package/dist/__tests__/config.test.d.ts +0 -2
  76. package/dist/__tests__/config.test.d.ts.map +0 -1
  77. package/dist/__tests__/config.test.js +0 -95
  78. package/dist/__tests__/config.test.js.map +0 -1
  79. package/dist/__tests__/create-app-urls.test.d.ts +0 -2
  80. package/dist/__tests__/create-app-urls.test.d.ts.map +0 -1
  81. package/dist/__tests__/create-app-urls.test.js +0 -102
  82. package/dist/__tests__/create-app-urls.test.js.map +0 -1
  83. package/dist/__tests__/docker-host.test.d.ts +0 -14
  84. package/dist/__tests__/docker-host.test.d.ts.map +0 -1
  85. package/dist/__tests__/docker-host.test.js +0 -109
  86. package/dist/__tests__/docker-host.test.js.map +0 -1
  87. package/dist/__tests__/enter.test.d.ts +0 -2
  88. package/dist/__tests__/enter.test.d.ts.map +0 -1
  89. package/dist/__tests__/enter.test.js +0 -90
  90. package/dist/__tests__/enter.test.js.map +0 -1
  91. package/dist/__tests__/help-output.test.d.ts +0 -2
  92. package/dist/__tests__/help-output.test.d.ts.map +0 -1
  93. package/dist/__tests__/help-output.test.js +0 -74
  94. package/dist/__tests__/help-output.test.js.map +0 -1
  95. package/dist/__tests__/host-cp-gh-token.test.d.ts +0 -9
  96. package/dist/__tests__/host-cp-gh-token.test.d.ts.map +0 -1
  97. package/dist/__tests__/host-cp-gh-token.test.js +0 -119
  98. package/dist/__tests__/host-cp-gh-token.test.js.map +0 -1
  99. package/dist/__tests__/host-cp.test.d.ts +0 -9
  100. package/dist/__tests__/host-cp.test.d.ts.map +0 -1
  101. package/dist/__tests__/host-cp.test.js +0 -327
  102. package/dist/__tests__/host-cp.test.js.map +0 -1
  103. package/dist/__tests__/image-presence.test.d.ts +0 -2
  104. package/dist/__tests__/image-presence.test.d.ts.map +0 -1
  105. package/dist/__tests__/image-presence.test.js +0 -44
  106. package/dist/__tests__/image-presence.test.js.map +0 -1
  107. package/dist/__tests__/install-root.test.d.ts +0 -2
  108. package/dist/__tests__/install-root.test.d.ts.map +0 -1
  109. package/dist/__tests__/install-root.test.js +0 -119
  110. package/dist/__tests__/install-root.test.js.map +0 -1
  111. package/dist/__tests__/keys.test.d.ts +0 -9
  112. package/dist/__tests__/keys.test.d.ts.map +0 -1
  113. package/dist/__tests__/keys.test.js +0 -145
  114. package/dist/__tests__/keys.test.js.map +0 -1
  115. package/dist/__tests__/logs.test.d.ts +0 -9
  116. package/dist/__tests__/logs.test.d.ts.map +0 -1
  117. package/dist/__tests__/logs.test.js +0 -124
  118. package/dist/__tests__/logs.test.js.map +0 -1
  119. package/dist/__tests__/mcp-import.test.d.ts +0 -11
  120. package/dist/__tests__/mcp-import.test.d.ts.map +0 -1
  121. package/dist/__tests__/mcp-import.test.js +0 -134
  122. package/dist/__tests__/mcp-import.test.js.map +0 -1
  123. package/dist/__tests__/protocol-version.test.d.ts +0 -2
  124. package/dist/__tests__/protocol-version.test.d.ts.map +0 -1
  125. package/dist/__tests__/protocol-version.test.js +0 -170
  126. package/dist/__tests__/protocol-version.test.js.map +0 -1
  127. package/dist/__tests__/ps.test.d.ts +0 -2
  128. package/dist/__tests__/ps.test.d.ts.map +0 -1
  129. package/dist/__tests__/ps.test.js +0 -172
  130. package/dist/__tests__/ps.test.js.map +0 -1
  131. package/dist/__tests__/registry-allowlist.test.d.ts +0 -2
  132. package/dist/__tests__/registry-allowlist.test.d.ts.map +0 -1
  133. package/dist/__tests__/registry-allowlist.test.js +0 -129
  134. package/dist/__tests__/registry-allowlist.test.js.map +0 -1
  135. package/dist/__tests__/services.test.d.ts +0 -8
  136. package/dist/__tests__/services.test.d.ts.map +0 -1
  137. package/dist/__tests__/services.test.js +0 -185
  138. package/dist/__tests__/services.test.js.map +0 -1
  139. package/dist/__tests__/status-app-urls.test.d.ts +0 -2
  140. package/dist/__tests__/status-app-urls.test.d.ts.map +0 -1
  141. package/dist/__tests__/status-app-urls.test.js +0 -125
  142. package/dist/__tests__/status-app-urls.test.js.map +0 -1
  143. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts +0 -19
  144. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts.map +0 -1
  145. package/dist/__tests__/upgrade-gh-token-contract.test.js +0 -63
  146. package/dist/__tests__/upgrade-gh-token-contract.test.js.map +0 -1
  147. package/dist/__tests__/upgrade.test.d.ts +0 -9
  148. package/dist/__tests__/upgrade.test.d.ts.map +0 -1
  149. package/dist/__tests__/upgrade.test.js +0 -586
  150. package/dist/__tests__/upgrade.test.js.map +0 -1
  151. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts +0 -6
  152. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts.map +0 -1
  153. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js +0 -26
  154. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js.map +0 -1
  155. package/dist/commands/__tests__/begin.test.d.ts +0 -7
  156. package/dist/commands/__tests__/begin.test.d.ts.map +0 -1
  157. package/dist/commands/__tests__/begin.test.js +0 -72
  158. package/dist/commands/__tests__/begin.test.js.map +0 -1
  159. package/dist/commands/__tests__/bootstrap.test.d.ts +0 -2
  160. package/dist/commands/__tests__/bootstrap.test.d.ts.map +0 -1
  161. package/dist/commands/__tests__/bootstrap.test.js +0 -370
  162. package/dist/commands/__tests__/bootstrap.test.js.map +0 -1
  163. package/dist/commands/__tests__/carry-uncommitted.test.d.ts +0 -14
  164. package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +0 -1
  165. package/dist/commands/__tests__/carry-uncommitted.test.js +0 -83
  166. package/dist/commands/__tests__/carry-uncommitted.test.js.map +0 -1
  167. package/dist/commands/__tests__/clean.test.d.ts +0 -9
  168. package/dist/commands/__tests__/clean.test.d.ts.map +0 -1
  169. package/dist/commands/__tests__/clean.test.js +0 -105
  170. package/dist/commands/__tests__/clean.test.js.map +0 -1
  171. package/dist/commands/__tests__/crystallize.test.d.ts +0 -2
  172. package/dist/commands/__tests__/crystallize.test.d.ts.map +0 -1
  173. package/dist/commands/__tests__/crystallize.test.js +0 -133
  174. package/dist/commands/__tests__/crystallize.test.js.map +0 -1
  175. package/dist/commands/__tests__/diagnose.test.d.ts +0 -9
  176. package/dist/commands/__tests__/diagnose.test.d.ts.map +0 -1
  177. package/dist/commands/__tests__/diagnose.test.js +0 -108
  178. package/dist/commands/__tests__/diagnose.test.js.map +0 -1
  179. package/dist/commands/__tests__/openHostCpUrl.test.d.ts +0 -2
  180. package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +0 -1
  181. package/dist/commands/__tests__/openHostCpUrl.test.js +0 -63
  182. package/dist/commands/__tests__/openHostCpUrl.test.js.map +0 -1
  183. package/dist/commands/__tests__/refresh.test.d.ts +0 -13
  184. package/dist/commands/__tests__/refresh.test.d.ts.map +0 -1
  185. package/dist/commands/__tests__/refresh.test.js +0 -170
  186. package/dist/commands/__tests__/refresh.test.js.map +0 -1
  187. package/dist/commands/__tests__/status.test.d.ts +0 -8
  188. package/dist/commands/__tests__/status.test.d.ts.map +0 -1
  189. package/dist/commands/__tests__/status.test.js +0 -62
  190. package/dist/commands/__tests__/status.test.js.map +0 -1
  191. package/dist/commands/__tests__/stop.test.d.ts +0 -5
  192. package/dist/commands/__tests__/stop.test.d.ts.map +0 -1
  193. package/dist/commands/__tests__/stop.test.js +0 -30
  194. package/dist/commands/__tests__/stop.test.js.map +0 -1
  195. package/dist/commands/__tests__/update.test.d.ts +0 -7
  196. package/dist/commands/__tests__/update.test.d.ts.map +0 -1
  197. package/dist/commands/__tests__/update.test.js +0 -224
  198. package/dist/commands/__tests__/update.test.js.map +0 -1
  199. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +0 -19
  200. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +0 -1
  201. package/dist/commands/__tests__/upgrade.all-three.test.js +0 -80
  202. package/dist/commands/__tests__/upgrade.all-three.test.js.map +0 -1
  203. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts +0 -20
  204. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts.map +0 -1
  205. package/dist/commands/__tests__/upgrade.compose-path.test.js +0 -140
  206. package/dist/commands/__tests__/upgrade.compose-path.test.js.map +0 -1
  207. package/dist/commands/__tests__/upgrade.history.test.d.ts +0 -15
  208. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +0 -1
  209. package/dist/commands/__tests__/upgrade.history.test.js +0 -199
  210. package/dist/commands/__tests__/upgrade.history.test.js.map +0 -1
  211. package/dist/commands/__tests__/upgrade.lock.test.d.ts +0 -15
  212. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +0 -1
  213. package/dist/commands/__tests__/upgrade.lock.test.js +0 -253
  214. package/dist/commands/__tests__/upgrade.lock.test.js.map +0 -1
  215. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +0 -21
  216. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +0 -1
  217. package/dist/commands/__tests__/upgrade.olam-tag.test.js +0 -114
  218. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +0 -1
  219. package/dist/commands/__tests__/upgrade.poll.test.d.ts +0 -14
  220. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +0 -1
  221. package/dist/commands/__tests__/upgrade.poll.test.js +0 -136
  222. package/dist/commands/__tests__/upgrade.poll.test.js.map +0 -1
  223. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +0 -17
  224. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +0 -1
  225. package/dist/commands/__tests__/upgrade.recreate.test.js +0 -83
  226. package/dist/commands/__tests__/upgrade.recreate.test.js.map +0 -1
  227. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +0 -12
  228. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +0 -1
  229. package/dist/commands/__tests__/upgrade.rollback.test.js +0 -255
  230. package/dist/commands/__tests__/upgrade.rollback.test.js.map +0 -1
  231. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +0 -12
  232. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +0 -1
  233. package/dist/commands/__tests__/upgrade.sha-capture.test.js +0 -63
  234. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +0 -1
  235. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +0 -19
  236. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +0 -1
  237. package/dist/commands/__tests__/upgrade.smoke.test.js +0 -87
  238. package/dist/commands/__tests__/upgrade.smoke.test.js.map +0 -1
  239. package/dist/commands/__tests__/upgrade.swap.test.d.ts +0 -19
  240. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +0 -1
  241. package/dist/commands/__tests__/upgrade.swap.test.js +0 -312
  242. package/dist/commands/__tests__/upgrade.swap.test.js.map +0 -1
  243. package/dist/commands/__tests__/world-upgrade.test.d.ts +0 -8
  244. package/dist/commands/__tests__/world-upgrade.test.d.ts.map +0 -1
  245. package/dist/commands/__tests__/world-upgrade.test.js +0 -73
  246. package/dist/commands/__tests__/world-upgrade.test.js.map +0 -1
  247. package/dist/lib/__tests__/symlink-reconcile.test.d.ts +0 -2
  248. package/dist/lib/__tests__/symlink-reconcile.test.d.ts.map +0 -1
  249. package/dist/lib/__tests__/symlink-reconcile.test.js +0 -106
  250. package/dist/lib/__tests__/symlink-reconcile.test.js.map +0 -1
@@ -412,11 +412,11 @@ var require_codegen = __commonJS({
412
412
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
413
413
  return `${varKind} ${this.name}${rhs};` + _n;
414
414
  }
415
- optimizeNames(names, constants) {
415
+ optimizeNames(names, constants2) {
416
416
  if (!names[this.name.str])
417
417
  return;
418
418
  if (this.rhs)
419
- this.rhs = optimizeExpr(this.rhs, names, constants);
419
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
420
420
  return this;
421
421
  }
422
422
  get names() {
@@ -433,10 +433,10 @@ var require_codegen = __commonJS({
433
433
  render({ _n }) {
434
434
  return `${this.lhs} = ${this.rhs};` + _n;
435
435
  }
436
- optimizeNames(names, constants) {
436
+ optimizeNames(names, constants2) {
437
437
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
438
438
  return;
439
- this.rhs = optimizeExpr(this.rhs, names, constants);
439
+ this.rhs = optimizeExpr(this.rhs, names, constants2);
440
440
  return this;
441
441
  }
442
442
  get names() {
@@ -497,8 +497,8 @@ var require_codegen = __commonJS({
497
497
  optimizeNodes() {
498
498
  return `${this.code}` ? this : void 0;
499
499
  }
500
- optimizeNames(names, constants) {
501
- this.code = optimizeExpr(this.code, names, constants);
500
+ optimizeNames(names, constants2) {
501
+ this.code = optimizeExpr(this.code, names, constants2);
502
502
  return this;
503
503
  }
504
504
  get names() {
@@ -527,12 +527,12 @@ var require_codegen = __commonJS({
527
527
  }
528
528
  return nodes.length > 0 ? this : void 0;
529
529
  }
530
- optimizeNames(names, constants) {
530
+ optimizeNames(names, constants2) {
531
531
  const { nodes } = this;
532
532
  let i = nodes.length;
533
533
  while (i--) {
534
534
  const n = nodes[i];
535
- if (n.optimizeNames(names, constants))
535
+ if (n.optimizeNames(names, constants2))
536
536
  continue;
537
537
  subtractNames(names, n.names);
538
538
  nodes.splice(i, 1);
@@ -585,12 +585,12 @@ var require_codegen = __commonJS({
585
585
  return void 0;
586
586
  return this;
587
587
  }
588
- optimizeNames(names, constants) {
588
+ optimizeNames(names, constants2) {
589
589
  var _a;
590
- this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
591
- if (!(super.optimizeNames(names, constants) || this.else))
590
+ this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
591
+ if (!(super.optimizeNames(names, constants2) || this.else))
592
592
  return;
593
- this.condition = optimizeExpr(this.condition, names, constants);
593
+ this.condition = optimizeExpr(this.condition, names, constants2);
594
594
  return this;
595
595
  }
596
596
  get names() {
@@ -613,10 +613,10 @@ var require_codegen = __commonJS({
613
613
  render(opts) {
614
614
  return `for(${this.iteration})` + super.render(opts);
615
615
  }
616
- optimizeNames(names, constants) {
617
- if (!super.optimizeNames(names, constants))
616
+ optimizeNames(names, constants2) {
617
+ if (!super.optimizeNames(names, constants2))
618
618
  return;
619
- this.iteration = optimizeExpr(this.iteration, names, constants);
619
+ this.iteration = optimizeExpr(this.iteration, names, constants2);
620
620
  return this;
621
621
  }
622
622
  get names() {
@@ -652,10 +652,10 @@ var require_codegen = __commonJS({
652
652
  render(opts) {
653
653
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
654
654
  }
655
- optimizeNames(names, constants) {
656
- if (!super.optimizeNames(names, constants))
655
+ optimizeNames(names, constants2) {
656
+ if (!super.optimizeNames(names, constants2))
657
657
  return;
658
- this.iterable = optimizeExpr(this.iterable, names, constants);
658
+ this.iterable = optimizeExpr(this.iterable, names, constants2);
659
659
  return this;
660
660
  }
661
661
  get names() {
@@ -697,11 +697,11 @@ var require_codegen = __commonJS({
697
697
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
698
698
  return this;
699
699
  }
700
- optimizeNames(names, constants) {
700
+ optimizeNames(names, constants2) {
701
701
  var _a, _b;
702
- super.optimizeNames(names, constants);
703
- (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants);
704
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants);
702
+ super.optimizeNames(names, constants2);
703
+ (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants2);
704
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants2);
705
705
  return this;
706
706
  }
707
707
  get names() {
@@ -1002,7 +1002,7 @@ var require_codegen = __commonJS({
1002
1002
  function addExprNames(names, from) {
1003
1003
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
1004
1004
  }
1005
- function optimizeExpr(expr, names, constants) {
1005
+ function optimizeExpr(expr, names, constants2) {
1006
1006
  if (expr instanceof code_1.Name)
1007
1007
  return replaceName(expr);
1008
1008
  if (!canOptimize(expr))
@@ -1017,14 +1017,14 @@ var require_codegen = __commonJS({
1017
1017
  return items;
1018
1018
  }, []));
1019
1019
  function replaceName(n) {
1020
- const c = constants[n.str];
1020
+ const c = constants2[n.str];
1021
1021
  if (c === void 0 || names[n.str] !== 1)
1022
1022
  return n;
1023
1023
  delete names[n.str];
1024
1024
  return c;
1025
1025
  }
1026
1026
  function canOptimize(e) {
1027
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== void 0);
1027
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants2[c.str] !== void 0);
1028
1028
  }
1029
1029
  }
1030
1030
  function subtractNames(names, from) {
@@ -2986,7 +2986,7 @@ var require_compile = __commonJS({
2986
2986
  const schOrFunc = root.refs[ref];
2987
2987
  if (schOrFunc)
2988
2988
  return schOrFunc;
2989
- let _sch = resolve6.call(this, root, ref);
2989
+ let _sch = resolve8.call(this, root, ref);
2990
2990
  if (_sch === void 0) {
2991
2991
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2992
2992
  const { schemaId } = this.opts;
@@ -3013,7 +3013,7 @@ var require_compile = __commonJS({
3013
3013
  function sameSchemaEnv(s1, s2) {
3014
3014
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3015
3015
  }
3016
- function resolve6(root, ref) {
3016
+ function resolve8(root, ref) {
3017
3017
  let sch;
3018
3018
  while (typeof (sch = this.refs[ref]) == "string")
3019
3019
  ref = sch;
@@ -3228,8 +3228,8 @@ var require_utils = __commonJS({
3228
3228
  }
3229
3229
  return ind;
3230
3230
  }
3231
- function removeDotSegments(path23) {
3232
- let input = path23;
3231
+ function removeDotSegments(path27) {
3232
+ let input = path27;
3233
3233
  const output = [];
3234
3234
  let nextSlash = -1;
3235
3235
  let len = 0;
@@ -3428,8 +3428,8 @@ var require_schemes = __commonJS({
3428
3428
  wsComponent.secure = void 0;
3429
3429
  }
3430
3430
  if (wsComponent.resourceName) {
3431
- const [path23, query] = wsComponent.resourceName.split("?");
3432
- wsComponent.path = path23 && path23 !== "/" ? path23 : void 0;
3431
+ const [path27, query] = wsComponent.resourceName.split("?");
3432
+ wsComponent.path = path27 && path27 !== "/" ? path27 : void 0;
3433
3433
  wsComponent.query = query;
3434
3434
  wsComponent.resourceName = void 0;
3435
3435
  }
@@ -3588,55 +3588,55 @@ var require_fast_uri = __commonJS({
3588
3588
  }
3589
3589
  return uri;
3590
3590
  }
3591
- function resolve6(baseURI, relativeURI, options) {
3591
+ function resolve8(baseURI, relativeURI, options) {
3592
3592
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3593
3593
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3594
3594
  schemelessOptions.skipEscape = true;
3595
3595
  return serialize(resolved, schemelessOptions);
3596
3596
  }
3597
- function resolveComponent(base, relative2, options, skipNormalization) {
3597
+ function resolveComponent(base, relative3, options, skipNormalization) {
3598
3598
  const target = {};
3599
3599
  if (!skipNormalization) {
3600
3600
  base = parse3(serialize(base, options), options);
3601
- relative2 = parse3(serialize(relative2, options), options);
3601
+ relative3 = parse3(serialize(relative3, options), options);
3602
3602
  }
3603
3603
  options = options || {};
3604
- if (!options.tolerant && relative2.scheme) {
3605
- target.scheme = relative2.scheme;
3606
- target.userinfo = relative2.userinfo;
3607
- target.host = relative2.host;
3608
- target.port = relative2.port;
3609
- target.path = removeDotSegments(relative2.path || "");
3610
- target.query = relative2.query;
3604
+ if (!options.tolerant && relative3.scheme) {
3605
+ target.scheme = relative3.scheme;
3606
+ target.userinfo = relative3.userinfo;
3607
+ target.host = relative3.host;
3608
+ target.port = relative3.port;
3609
+ target.path = removeDotSegments(relative3.path || "");
3610
+ target.query = relative3.query;
3611
3611
  } else {
3612
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3613
- target.userinfo = relative2.userinfo;
3614
- target.host = relative2.host;
3615
- target.port = relative2.port;
3616
- target.path = removeDotSegments(relative2.path || "");
3617
- target.query = relative2.query;
3612
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3613
+ target.userinfo = relative3.userinfo;
3614
+ target.host = relative3.host;
3615
+ target.port = relative3.port;
3616
+ target.path = removeDotSegments(relative3.path || "");
3617
+ target.query = relative3.query;
3618
3618
  } else {
3619
- if (!relative2.path) {
3619
+ if (!relative3.path) {
3620
3620
  target.path = base.path;
3621
- if (relative2.query !== void 0) {
3622
- target.query = relative2.query;
3621
+ if (relative3.query !== void 0) {
3622
+ target.query = relative3.query;
3623
3623
  } else {
3624
3624
  target.query = base.query;
3625
3625
  }
3626
3626
  } else {
3627
- if (relative2.path[0] === "/") {
3628
- target.path = removeDotSegments(relative2.path);
3627
+ if (relative3.path[0] === "/") {
3628
+ target.path = removeDotSegments(relative3.path);
3629
3629
  } else {
3630
3630
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3631
- target.path = "/" + relative2.path;
3631
+ target.path = "/" + relative3.path;
3632
3632
  } else if (!base.path) {
3633
- target.path = relative2.path;
3633
+ target.path = relative3.path;
3634
3634
  } else {
3635
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3635
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3636
3636
  }
3637
3637
  target.path = removeDotSegments(target.path);
3638
3638
  }
3639
- target.query = relative2.query;
3639
+ target.query = relative3.query;
3640
3640
  }
3641
3641
  target.userinfo = base.userinfo;
3642
3642
  target.host = base.host;
@@ -3644,7 +3644,7 @@ var require_fast_uri = __commonJS({
3644
3644
  }
3645
3645
  target.scheme = base.scheme;
3646
3646
  }
3647
- target.fragment = relative2.fragment;
3647
+ target.fragment = relative3.fragment;
3648
3648
  return target;
3649
3649
  }
3650
3650
  function equal(uriA, uriB, options) {
@@ -3815,7 +3815,7 @@ var require_fast_uri = __commonJS({
3815
3815
  var fastUri = {
3816
3816
  SCHEMES,
3817
3817
  normalize,
3818
- resolve: resolve6,
3818
+ resolve: resolve8,
3819
3819
  resolveComponent,
3820
3820
  equal,
3821
3821
  serialize,
@@ -6791,12 +6791,12 @@ var require_dist = __commonJS({
6791
6791
  throw new Error(`Unknown format "${name}"`);
6792
6792
  return f;
6793
6793
  };
6794
- function addFormats(ajv, list, fs19, exportName) {
6794
+ function addFormats(ajv, list, fs23, exportName) {
6795
6795
  var _a;
6796
6796
  var _b;
6797
6797
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
6798
6798
  for (const f of list)
6799
- ajv.addFormat(f, fs19[f]);
6799
+ ajv.addFormat(f, fs23[f]);
6800
6800
  }
6801
6801
  module.exports = exports = formatsPlugin;
6802
6802
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -7002,10 +7002,10 @@ function assignProp(target, prop, value) {
7002
7002
  configurable: true
7003
7003
  });
7004
7004
  }
7005
- function getElementAtPath(obj, path23) {
7006
- if (!path23)
7005
+ function getElementAtPath(obj, path27) {
7006
+ if (!path27)
7007
7007
  return obj;
7008
- return path23.reduce((acc, key) => acc?.[key], obj);
7008
+ return path27.reduce((acc, key) => acc?.[key], obj);
7009
7009
  }
7010
7010
  function promiseAllObject(promisesObj) {
7011
7011
  const keys = Object.keys(promisesObj);
@@ -7325,11 +7325,11 @@ function aborted(x, startIndex = 0) {
7325
7325
  }
7326
7326
  return false;
7327
7327
  }
7328
- function prefixIssues(path23, issues) {
7328
+ function prefixIssues(path27, issues) {
7329
7329
  return issues.map((iss) => {
7330
7330
  var _a;
7331
7331
  (_a = iss).path ?? (_a.path = []);
7332
- iss.path.unshift(path23);
7332
+ iss.path.unshift(path27);
7333
7333
  return iss;
7334
7334
  });
7335
7335
  }
@@ -12914,12 +12914,12 @@ var StdioServerTransport = class {
12914
12914
  this.onclose?.();
12915
12915
  }
12916
12916
  send(message) {
12917
- return new Promise((resolve6) => {
12917
+ return new Promise((resolve8) => {
12918
12918
  const json = serializeMessage(message);
12919
12919
  if (this._stdout.write(json)) {
12920
- resolve6();
12920
+ resolve8();
12921
12921
  } else {
12922
- this._stdout.once("drain", resolve6);
12922
+ this._stdout.once("drain", resolve8);
12923
12923
  }
12924
12924
  });
12925
12925
  }
@@ -13403,8 +13403,8 @@ function getErrorMap() {
13403
13403
 
13404
13404
  // ../../node_modules/zod/v3/helpers/parseUtil.js
13405
13405
  var makeIssue = (params) => {
13406
- const { data, path: path23, errorMaps, issueData } = params;
13407
- const fullPath = [...path23, ...issueData.path || []];
13406
+ const { data, path: path27, errorMaps, issueData } = params;
13407
+ const fullPath = [...path27, ...issueData.path || []];
13408
13408
  const fullIssue = {
13409
13409
  ...issueData,
13410
13410
  path: fullPath
@@ -13520,11 +13520,11 @@ var errorUtil;
13520
13520
 
13521
13521
  // ../../node_modules/zod/v3/types.js
13522
13522
  var ParseInputLazyPath = class {
13523
- constructor(parent, value, path23, key) {
13523
+ constructor(parent, value, path27, key) {
13524
13524
  this._cachedPath = [];
13525
13525
  this.parent = parent;
13526
13526
  this.data = value;
13527
- this._path = path23;
13527
+ this._path = path27;
13528
13528
  this._key = key;
13529
13529
  }
13530
13530
  get path() {
@@ -18987,7 +18987,7 @@ var Protocol = class {
18987
18987
  return;
18988
18988
  }
18989
18989
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
18990
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
18990
+ await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
18991
18991
  options?.signal?.throwIfAborted();
18992
18992
  }
18993
18993
  } catch (error2) {
@@ -19004,7 +19004,7 @@ var Protocol = class {
19004
19004
  */
19005
19005
  request(request2, resultSchema, options) {
19006
19006
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
19007
- return new Promise((resolve6, reject2) => {
19007
+ return new Promise((resolve8, reject2) => {
19008
19008
  const earlyReject = (error2) => {
19009
19009
  reject2(error2);
19010
19010
  };
@@ -19082,7 +19082,7 @@ var Protocol = class {
19082
19082
  if (!parseResult.success) {
19083
19083
  reject2(parseResult.error);
19084
19084
  } else {
19085
- resolve6(parseResult.data);
19085
+ resolve8(parseResult.data);
19086
19086
  }
19087
19087
  } catch (error2) {
19088
19088
  reject2(error2);
@@ -19343,12 +19343,12 @@ var Protocol = class {
19343
19343
  }
19344
19344
  } catch {
19345
19345
  }
19346
- return new Promise((resolve6, reject2) => {
19346
+ return new Promise((resolve8, reject2) => {
19347
19347
  if (signal.aborted) {
19348
19348
  reject2(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
19349
19349
  return;
19350
19350
  }
19351
- const timeoutId = setTimeout(resolve6, interval);
19351
+ const timeoutId = setTimeout(resolve8, interval);
19352
19352
  signal.addEventListener("abort", () => {
19353
19353
  clearTimeout(timeoutId);
19354
19354
  reject2(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -20448,7 +20448,7 @@ var McpServer = class {
20448
20448
  let task = createTaskResult.task;
20449
20449
  const pollInterval = task.pollInterval ?? 5e3;
20450
20450
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
20451
- await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
20451
+ await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
20452
20452
  const updatedTask = await extra.taskStore.getTask(taskId);
20453
20453
  if (!updatedTask) {
20454
20454
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -21365,8 +21365,8 @@ var AuthClient = class {
21365
21365
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
21366
21366
  }
21367
21367
  }
21368
- async request(method, path23, body, attempt = 0) {
21369
- const url = `${this.baseUrl}${path23}`;
21368
+ async request(method, path27, body, attempt = 0) {
21369
+ const url = `${this.baseUrl}${path27}`;
21370
21370
  const controller = new AbortController();
21371
21371
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
21372
21372
  const headers = {};
@@ -21384,7 +21384,7 @@ var AuthClient = class {
21384
21384
  } catch (err) {
21385
21385
  if (attempt < RETRY_COUNT && isTransient(err)) {
21386
21386
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
21387
- return this.request(method, path23, body, attempt + 1);
21387
+ return this.request(method, path27, body, attempt + 1);
21388
21388
  }
21389
21389
  throw err;
21390
21390
  } finally {
@@ -21411,7 +21411,7 @@ async function safeText(res) {
21411
21411
  }
21412
21412
  }
21413
21413
  function sleep(ms) {
21414
- return new Promise((resolve6) => setTimeout(resolve6, ms));
21414
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
21415
21415
  }
21416
21416
 
21417
21417
  // ../core/dist/auth/container.js
@@ -21543,7 +21543,7 @@ function resolveAuthServicePath() {
21543
21543
  return path3.join(pkgsDir, "auth-service");
21544
21544
  }
21545
21545
  function sleep2(ms) {
21546
- return new Promise((resolve6) => setTimeout(resolve6, ms));
21546
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
21547
21547
  }
21548
21548
 
21549
21549
  // ../core/dist/auth/preflight.js
@@ -22191,12 +22191,12 @@ function register3(server, _ctx, _initError) {
22191
22191
  registry2.close();
22192
22192
  }
22193
22193
  try {
22194
- const { default: fs19 } = await import("node:fs");
22195
- const { default: os12 } = await import("node:os");
22196
- const { default: path23 } = await import("node:path");
22197
- const tokenPath = path23.join(os12.homedir(), ".olam", "host-cp.token");
22198
- if (fs19.existsSync(tokenPath)) {
22199
- const token = fs19.readFileSync(tokenPath, "utf-8").trim();
22194
+ const { default: fs23 } = await import("node:fs");
22195
+ const { default: os15 } = await import("node:os");
22196
+ const { default: path27 } = await import("node:path");
22197
+ const tokenPath = path27.join(os15.homedir(), ".olam", "host-cp.token");
22198
+ if (fs23.existsSync(tokenPath)) {
22199
+ const token = fs23.readFileSync(tokenPath, "utf-8").trim();
22200
22200
  await fetch("http://127.0.0.1:19000/api/admin/world-pr", {
22201
22201
  method: "POST",
22202
22202
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
@@ -22338,11 +22338,13 @@ var serviceSchema = external_exports.object({
22338
22338
  var deploySchema = external_exports.object({
22339
22339
  tags: external_exports.array(external_exports.string()).optional()
22340
22340
  }).passthrough();
22341
+ var BootstrapKindSchema = external_exports.enum(["gems", "node", "pg"]);
22341
22342
  var BootstrapStepSchema = external_exports.union([
22342
22343
  external_exports.string(),
22343
22344
  external_exports.object({
22344
22345
  cmd: external_exports.string().min(1),
22345
- idempotent_check: external_exports.string().min(1).optional()
22346
+ idempotent_check: external_exports.string().min(1).optional(),
22347
+ produces: BootstrapKindSchema.nullable().optional()
22346
22348
  }).passthrough()
22347
22349
  ]);
22348
22350
  function bootstrapStepCmd(entry) {
@@ -22389,7 +22391,7 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
22389
22391
  "deploy"
22390
22392
  ]);
22391
22393
  var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
22392
- function refineForbiddenKeys(value, path23, ctx, rejectSource) {
22394
+ function refineForbiddenKeys(value, path27, ctx, rejectSource) {
22393
22395
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
22394
22396
  return;
22395
22397
  }
@@ -22397,12 +22399,12 @@ function refineForbiddenKeys(value, path23, ctx, rejectSource) {
22397
22399
  if (FORBIDDEN_KEYS.has(key)) {
22398
22400
  ctx.addIssue({
22399
22401
  code: external_exports.ZodIssueCode.custom,
22400
- path: [...path23, key],
22402
+ path: [...path27, key],
22401
22403
  message: `forbidden key "${key}" (prototype-pollution surface)`
22402
22404
  });
22403
22405
  continue;
22404
22406
  }
22405
- if (rejectSource && path23.length === 0 && key === "source") {
22407
+ if (rejectSource && path27.length === 0 && key === "source") {
22406
22408
  ctx.addIssue({
22407
22409
  code: external_exports.ZodIssueCode.custom,
22408
22410
  path: ["source"],
@@ -22410,21 +22412,21 @@ function refineForbiddenKeys(value, path23, ctx, rejectSource) {
22410
22412
  });
22411
22413
  continue;
22412
22414
  }
22413
- refineForbiddenKeys(value[key], [...path23, key], ctx, false);
22415
+ refineForbiddenKeys(value[key], [...path27, key], ctx, false);
22414
22416
  }
22415
22417
  }
22416
- function rejectForbiddenKeys(value, path23, rejectSource) {
22418
+ function rejectForbiddenKeys(value, path27, rejectSource) {
22417
22419
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
22418
22420
  return;
22419
22421
  }
22420
22422
  for (const key of Object.keys(value)) {
22421
22423
  if (FORBIDDEN_KEYS.has(key)) {
22422
- throw new Error(`[manifest] ${path23}: forbidden key "${key}" (prototype-pollution surface)`);
22424
+ throw new Error(`[manifest] ${path27}: forbidden key "${key}" (prototype-pollution surface)`);
22423
22425
  }
22424
22426
  if (rejectSource && key === "source") {
22425
- throw new Error(`[manifest] ${path23}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
22427
+ throw new Error(`[manifest] ${path27}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
22426
22428
  }
22427
- rejectForbiddenKeys(value[key], `${path23}.${key}`, false);
22429
+ rejectForbiddenKeys(value[key], `${path27}.${key}`, false);
22428
22430
  }
22429
22431
  }
22430
22432
  function unknownTopLevelKeys(parsed) {
@@ -22433,40 +22435,40 @@ function unknownTopLevelKeys(parsed) {
22433
22435
  function loadRepoManifest(repoDir) {
22434
22436
  const olamPath = join5(repoDir, ".olam.yaml");
22435
22437
  const adbPath = join5(repoDir, ".adb.yaml");
22436
- let manifestPath;
22438
+ let manifestPath2;
22437
22439
  let source;
22438
22440
  if (existsSync4(olamPath)) {
22439
- manifestPath = olamPath;
22441
+ manifestPath2 = olamPath;
22440
22442
  source = "olam";
22441
22443
  } else if (existsSync4(adbPath)) {
22442
- manifestPath = adbPath;
22444
+ manifestPath2 = adbPath;
22443
22445
  source = "adb";
22444
22446
  } else {
22445
22447
  return null;
22446
22448
  }
22447
- const stat = lstatSync(manifestPath);
22449
+ const stat = lstatSync(manifestPath2);
22448
22450
  if (stat.isSymbolicLink()) {
22449
- throw new Error(`[manifest] ${manifestPath}: symbolic links are not permitted`);
22451
+ throw new Error(`[manifest] ${manifestPath2}: symbolic links are not permitted`);
22450
22452
  }
22451
- const raw = readFileSync3(manifestPath, "utf-8");
22453
+ const raw = readFileSync3(manifestPath2, "utf-8");
22452
22454
  const parsed = YAML.parse(raw, { maxAliasCount: 100 });
22453
22455
  if (parsed === null || parsed === void 0) {
22454
22456
  if (source === "olam" && existsSync4(adbPath)) {
22455
- console.warn(`[manifest] ${manifestPath}: file is empty; .adb.yaml is NOT consulted (delete .olam.yaml to fall back)`);
22457
+ console.warn(`[manifest] ${manifestPath2}: file is empty; .adb.yaml is NOT consulted (delete .olam.yaml to fall back)`);
22456
22458
  }
22457
22459
  return null;
22458
22460
  }
22459
22461
  if (typeof parsed !== "object" || Array.isArray(parsed)) {
22460
- throw new Error(`[manifest] ${manifestPath}: expected a YAML mapping at the top level`);
22462
+ throw new Error(`[manifest] ${manifestPath2}: expected a YAML mapping at the top level`);
22461
22463
  }
22462
- rejectForbiddenKeys(parsed, manifestPath, true);
22464
+ rejectForbiddenKeys(parsed, manifestPath2, true);
22463
22465
  const body = RepoManifestSchema.parse(parsed);
22464
22466
  if (parsed["version"] === void 0) {
22465
- console.warn(`[manifest] ${manifestPath}: missing "version: ${MANIFEST_VERSION}" field \u2014 add it to suppress this warning (backward-compat: file still parses)`);
22467
+ console.warn(`[manifest] ${manifestPath2}: missing "version: ${MANIFEST_VERSION}" field \u2014 add it to suppress this warning (backward-compat: file still parses)`);
22466
22468
  }
22467
22469
  const unknown2 = unknownTopLevelKeys(parsed);
22468
22470
  if (unknown2.length > 0) {
22469
- console.warn(`[manifest] ${manifestPath}: unknown top-level fields preserved (passthrough): ${unknown2.join(", ")}`);
22471
+ console.warn(`[manifest] ${manifestPath2}: unknown top-level fields preserved (passthrough): ${unknown2.join(", ")}`);
22470
22472
  }
22471
22473
  return { ...body, source };
22472
22474
  }
@@ -22862,10 +22864,10 @@ function extractMcpConfig(claudeJsonPath) {
22862
22864
  }
22863
22865
  return { mcpServers, secrets };
22864
22866
  }
22865
- function readOptional(path23) {
22866
- if (!existsSync6(path23)) return null;
22867
+ function readOptional(path27) {
22868
+ if (!existsSync6(path27)) return null;
22867
22869
  try {
22868
- return readFileSync5(path23, "utf8");
22870
+ return readFileSync5(path27, "utf8");
22869
22871
  } catch {
22870
22872
  return null;
22871
22873
  }
@@ -23163,6 +23165,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
23163
23165
  }
23164
23166
  const hostControlPlanePort = HOST_CONTROL_PLANE_BASE2 + (portOffset ?? 0);
23165
23167
  const hostTtydPort = 17681 + (portOffset ?? 0);
23168
+ const hostTtydShellPort = 17682 + (portOffset ?? 0);
23166
23169
  const appPortBindings = {};
23167
23170
  const appExposedPorts = {};
23168
23171
  const appHostPorts = [];
@@ -23177,6 +23180,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
23177
23180
  await auditPortsForZombies(docker, [
23178
23181
  hostControlPlanePort,
23179
23182
  hostTtydPort,
23183
+ hostTtydShellPort,
23180
23184
  ...appHostPorts
23181
23185
  ]);
23182
23186
  const container = await docker.createContainer({
@@ -23190,6 +23194,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
23190
23194
  ExposedPorts: {
23191
23195
  [`${CONTROL_PLANE_PORT}/tcp`]: {},
23192
23196
  "7681/tcp": {},
23197
+ "7682/tcp": {},
23193
23198
  ...appExposedPorts
23194
23199
  },
23195
23200
  HostConfig: {
@@ -23202,6 +23207,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
23202
23207
  PortBindings: {
23203
23208
  [`${CONTROL_PLANE_PORT}/tcp`]: [{ HostPort: String(hostControlPlanePort), HostIp: "127.0.0.1" }],
23204
23209
  "7681/tcp": [{ HostPort: String(hostTtydPort), HostIp: "127.0.0.1" }],
23210
+ "7682/tcp": [{ HostPort: String(hostTtydShellPort), HostIp: "127.0.0.1" }],
23205
23211
  ...appPortBindings
23206
23212
  },
23207
23213
  NanoCpus: resources?.cpuCores ? resources.cpuCores * 1e9 : void 0,
@@ -23235,7 +23241,7 @@ var stopAndRemove = async (container) => {
23235
23241
 
23236
23242
  // ../adapters/dist/docker/exec.js
23237
23243
  import { PassThrough } from "node:stream";
23238
- var demuxStream = (stream) => new Promise((resolve6, reject2) => {
23244
+ var demuxStream = (stream) => new Promise((resolve8, reject2) => {
23239
23245
  const stdoutChunks = [];
23240
23246
  const stderrChunks = [];
23241
23247
  const stdout = new PassThrough();
@@ -23249,7 +23255,7 @@ var demuxStream = (stream) => new Promise((resolve6, reject2) => {
23249
23255
  stream.pipe(stdout);
23250
23256
  }
23251
23257
  stream.on("end", () => {
23252
- resolve6({
23258
+ resolve8({
23253
23259
  stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
23254
23260
  stderr: Buffer.concat(stderrChunks).toString("utf-8")
23255
23261
  });
@@ -23542,7 +23548,7 @@ var SSHConnectionPool = class {
23542
23548
  // -----------------------------------------------------------------------
23543
23549
  async exec(host, command) {
23544
23550
  const client = await this.getConnection(host);
23545
- return new Promise((resolve6, reject2) => {
23551
+ return new Promise((resolve8, reject2) => {
23546
23552
  client.exec(command, (err, stream) => {
23547
23553
  if (err) {
23548
23554
  reject2(new Error(`SSH exec failed on ${host}: ${err.message}`));
@@ -23557,7 +23563,7 @@ var SSHConnectionPool = class {
23557
23563
  stderr += data.toString();
23558
23564
  });
23559
23565
  stream.on("close", (code) => {
23560
- resolve6({
23566
+ resolve8({
23561
23567
  exitCode: code ?? 0,
23562
23568
  stdout: stdout.trimEnd(),
23563
23569
  stderr: stderr.trimEnd()
@@ -23588,10 +23594,10 @@ var SSHConnectionPool = class {
23588
23594
  throw new Error(`No SSH configuration found for host: ${host}`);
23589
23595
  }
23590
23596
  const client = new SSHClient();
23591
- return new Promise((resolve6, reject2) => {
23597
+ return new Promise((resolve8, reject2) => {
23592
23598
  client.on("ready", () => {
23593
23599
  this.connections.set(host, client);
23594
- resolve6(client);
23600
+ resolve8(client);
23595
23601
  }).on("error", (err) => {
23596
23602
  this.connections.delete(host);
23597
23603
  reject2(new Error(`SSH connection to ${host} failed: ${err.message}`));
@@ -24008,8 +24014,8 @@ var CloudflareProvider = class extends ComputeProvider {
24008
24014
  // -----------------------------------------------------------------------
24009
24015
  // Internal fetch helper
24010
24016
  // -----------------------------------------------------------------------
24011
- async request(path23, method, body) {
24012
- const url = `${this.config.workerUrl}${path23}`;
24017
+ async request(path27, method, body) {
24018
+ const url = `${this.config.workerUrl}${path27}`;
24013
24019
  const bearer = await this.config.mintToken();
24014
24020
  const headers = {
24015
24021
  Authorization: `Bearer ${bearer}`
@@ -24446,7 +24452,8 @@ function register6(server, ctx, initError) {
24446
24452
  // strip step so any operator-side uncommitted edits survive into
24447
24453
  // the world's worktree. The baseline-diff snapshot still runs
24448
24454
  // unconditionally (Phase C reaper still needs it).
24449
- carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false")
24455
+ carryUncommitted: external_exports.boolean().optional().describe("Preserve operator's uncommitted edits in the world's worktree (B3); default false"),
24456
+ runbookName: external_exports.string().optional().describe("Named runbook profile from ~/.olam/config.json \u2014 seeds ports, env overrides, and fixture-copy steps")
24450
24457
  },
24451
24458
  async (params) => {
24452
24459
  if (!ctx) {
@@ -24510,7 +24517,8 @@ function register6(server, ctx, initError) {
24510
24517
  branchName: params.branchName,
24511
24518
  planFile: params.planFile,
24512
24519
  taskContext: params.taskContext,
24513
- carryUncommitted: params.carryUncommitted ?? false
24520
+ carryUncommitted: params.carryUncommitted ?? false,
24521
+ ...params.runbookName ? { runbookName: params.runbookName } : {}
24514
24522
  });
24515
24523
  const lines = [
24516
24524
  `World created successfully.`,
@@ -24569,7 +24577,7 @@ function register6(server, ctx, initError) {
24569
24577
  }
24570
24578
  } catch {
24571
24579
  }
24572
- await new Promise((resolve6) => setTimeout(resolve6, POLL_INTERVAL_MS));
24580
+ await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL_MS));
24573
24581
  }
24574
24582
  }
24575
24583
  if (authenticated) {
@@ -25301,8 +25309,8 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
25301
25309
  }
25302
25310
  }
25303
25311
  async function copyClaudeConfigIntoContainer(containerName) {
25304
- const { execSync: execSync6 } = await import("node:child_process");
25305
- const dockerExec = (cmd) => execSync6(`docker exec ${containerName} sh -c '${cmd}'`, { stdio: "pipe" });
25312
+ const { execSync: execSync7 } = await import("node:child_process");
25313
+ const dockerExec = (cmd) => execSync7(`docker exec ${containerName} sh -c '${cmd}'`, { stdio: "pipe" });
25306
25314
  dockerExec("mkdir -p /home/olam/.claude");
25307
25315
  dockerExec("test -f /home/olam/workspace/.claude-host-config/settings.json && cp /home/olam/workspace/.claude-host-config/settings.json /home/olam/.claude/settings.json || true");
25308
25316
  dockerExec("test -f /home/olam/workspace/.claude-host-config/CLAUDE.md && cp /home/olam/workspace/.claude-host-config/CLAUDE.md /home/olam/.claude/CLAUDE.md || true");
@@ -25318,7 +25326,7 @@ async function copyClaudeConfigIntoContainer(containerName) {
25318
25326
  await sanitizeContainerClaudeHooks(containerName);
25319
25327
  }
25320
25328
  async function sanitizeContainerClaudeHooks(containerName) {
25321
- const { execSync: execSync6 } = await import("node:child_process");
25329
+ const { execSync: execSync7 } = await import("node:child_process");
25322
25330
  const script = `
25323
25331
  const fs = require('fs');
25324
25332
  const p = '/home/olam/.claude/settings.json';
@@ -25362,7 +25370,7 @@ if (changed) {
25362
25370
  }
25363
25371
  `;
25364
25372
  try {
25365
- execSync6(`docker exec ${containerName} /usr/local/bin/node -e ${shQuote(script)}`, { stdio: "pipe" });
25373
+ execSync7(`docker exec ${containerName} /usr/local/bin/node -e ${shQuote(script)}`, { stdio: "pipe" });
25366
25374
  } catch {
25367
25375
  }
25368
25376
  }
@@ -25499,8 +25507,8 @@ function copyMatchingFiles(sourcePath, destPath, pattern) {
25499
25507
  try {
25500
25508
  const matches2 = globSync(fullPattern);
25501
25509
  for (const match of matches2) {
25502
- const relative2 = path9.relative(sourcePath, match);
25503
- const dest = path9.join(destPath, relative2);
25510
+ const relative3 = path9.relative(sourcePath, match);
25511
+ const dest = path9.join(destPath, relative3);
25504
25512
  fs6.mkdirSync(path9.dirname(dest), { recursive: true });
25505
25513
  fs6.copyFileSync(match, dest);
25506
25514
  }
@@ -26442,10 +26450,10 @@ async function writeManifest(args) {
26442
26450
  capturedAt: args.capturedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
26443
26451
  shots: entries
26444
26452
  };
26445
- const path23 = join12(args.outDir, "manifest.json");
26446
- await writeFile(path23, `${JSON.stringify(manifest, null, 2)}
26453
+ const path27 = join12(args.outDir, "manifest.json");
26454
+ await writeFile(path27, `${JSON.stringify(manifest, null, 2)}
26447
26455
  `, "utf8");
26448
- return { path: path23, manifest };
26456
+ return { path: path27, manifest };
26449
26457
  }
26450
26458
 
26451
26459
  // ../mcp-server/src/tools/_capture/proxy.ts
@@ -26699,9 +26707,9 @@ async function startProxy(opts) {
26699
26707
  const liveCompiled = verified.allowedPaths.map(compileGlob);
26700
26708
  const target = parseRequestTarget(req);
26701
26709
  if (!target) return httpReject(400, "invalid_target");
26702
- const path23 = target.pathname;
26703
- if (!liveCompiled.some((re) => re.test(path23))) {
26704
- return httpReject(403, "outside_allow_list", { path: path23 });
26710
+ const path27 = target.pathname;
26711
+ if (!liveCompiled.some((re) => re.test(path27))) {
26712
+ return httpReject(403, "outside_allow_list", { path: path27 });
26705
26713
  }
26706
26714
  const headerWorld = req.headers[WORLD_ASSERT_HEADER];
26707
26715
  const headerWorldStr = typeof headerWorld === "string" ? headerWorld : Array.isArray(headerWorld) && headerWorld.length > 0 ? headerWorld[0] : void 0;
@@ -26805,15 +26813,15 @@ ${JSON.stringify({ error: reason })}`
26805
26813
  unlinkSync2(udsPath);
26806
26814
  } catch {
26807
26815
  }
26808
- await new Promise((resolve6, reject2) => {
26809
- server.listen(udsPath, () => resolve6());
26816
+ await new Promise((resolve8, reject2) => {
26817
+ server.listen(udsPath, () => resolve8());
26810
26818
  server.once("error", reject2);
26811
26819
  });
26812
26820
  chmodSync3(udsPath, 384);
26813
26821
  port = 0;
26814
26822
  } else {
26815
- await new Promise((resolve6, reject2) => {
26816
- server.listen(opts.port ?? 0, "127.0.0.1", () => resolve6());
26823
+ await new Promise((resolve8, reject2) => {
26824
+ server.listen(opts.port ?? 0, "127.0.0.1", () => resolve8());
26817
26825
  server.once("error", reject2);
26818
26826
  });
26819
26827
  const addr = server.address();
@@ -26829,10 +26837,10 @@ ${JSON.stringify({ error: reason })}`
26829
26837
  } catch {
26830
26838
  }
26831
26839
  await Promise.race([
26832
- new Promise((resolve6, reject2) => {
26833
- server.close((err) => err ? reject2(err) : resolve6());
26840
+ new Promise((resolve8, reject2) => {
26841
+ server.close((err) => err ? reject2(err) : resolve8());
26834
26842
  }),
26835
- new Promise((resolve6) => setTimeout(resolve6, 5e3))
26843
+ new Promise((resolve8) => setTimeout(resolve8, 5e3))
26836
26844
  ]);
26837
26845
  if (udsPath) {
26838
26846
  try {
@@ -27164,10 +27172,10 @@ async function acquireLaunchSlot() {
27164
27172
  _inFlightLaunches++;
27165
27173
  return releaseLaunchSlot;
27166
27174
  }
27167
- return new Promise((resolve6) => {
27175
+ return new Promise((resolve8) => {
27168
27176
  _launchQueue.push(() => {
27169
27177
  _inFlightLaunches++;
27170
- resolve6(releaseLaunchSlot);
27178
+ resolve8(releaseLaunchSlot);
27171
27179
  });
27172
27180
  });
27173
27181
  }
@@ -27480,14 +27488,14 @@ async function runShot(browser, shot, outDir, format, jpegQuality, allowEval, as
27480
27488
  await page.waitForTimeout(shot.afterLoadMs);
27481
27489
  }
27482
27490
  const ext = format === "jpeg" ? "jpg" : "png";
27483
- const path23 = join13(outDir, `${shot.name}.${ext}`);
27491
+ const path27 = join13(outDir, `${shot.name}.${ext}`);
27484
27492
  await page.screenshot({
27485
- path: path23,
27493
+ path: path27,
27486
27494
  type: format,
27487
27495
  ...format === "jpeg" ? { quality: jpegQuality } : {},
27488
27496
  fullPage: false
27489
27497
  });
27490
- return { name: shot.name, path: path23, urlRedacted: redactUrl(shot.url), viewport };
27498
+ return { name: shot.name, path: path27, urlRedacted: redactUrl(shot.url), viewport };
27491
27499
  } finally {
27492
27500
  await context.close();
27493
27501
  }
@@ -27708,7 +27716,7 @@ function register20(server, ctx, initError) {
27708
27716
  shot.url === navigableShot.url ? result : { ...result, urlRedacted: redactUrl(shot.url) }
27709
27717
  );
27710
27718
  }
27711
- const { path: manifestPath } = await writeManifest({
27719
+ const { path: manifestPath2 } = await writeManifest({
27712
27720
  outDir: absOutDir,
27713
27721
  correlationId,
27714
27722
  shots: results
@@ -27720,7 +27728,7 @@ function register20(server, ctx, initError) {
27720
27728
  type: "text",
27721
27729
  text: `Captured ${results.length} shot(s) to ${absOutDir} (correlationId=${correlationId}):
27722
27730
  ${lines.join("\n")}
27723
- manifest \u2192 ${manifestPath}`
27731
+ manifest \u2192 ${manifestPath2}`
27724
27732
  }
27725
27733
  ]
27726
27734
  };
@@ -27930,12 +27938,12 @@ function openUrl(url) {
27930
27938
  var HOST_CP_URL = "http://127.0.0.1:19000";
27931
27939
  async function readHostCpToken2() {
27932
27940
  try {
27933
- const { default: fs19 } = await import("node:fs");
27934
- const { default: os12 } = await import("node:os");
27935
- const { default: path23 } = await import("node:path");
27936
- const tp = path23.join(os12.homedir(), ".olam", "host-cp.token");
27937
- if (!fs19.existsSync(tp)) return { token: null };
27938
- return { token: fs19.readFileSync(tp, "utf-8").trim() };
27941
+ const { default: fs23 } = await import("node:fs");
27942
+ const { default: os15 } = await import("node:os");
27943
+ const { default: path27 } = await import("node:path");
27944
+ const tp = path27.join(os15.homedir(), ".olam", "host-cp.token");
27945
+ if (!fs23.existsSync(tp)) return { token: null };
27946
+ return { token: fs23.readFileSync(tp, "utf-8").trim() };
27939
27947
  } catch {
27940
27948
  return { token: null };
27941
27949
  }
@@ -28354,6 +28362,168 @@ function updateRepo(name, updates) {
28354
28362
  return updated;
28355
28363
  }
28356
28364
 
28365
+ // ../core/dist/global-config/runbooks.js
28366
+ function validateRunbookIntegrity(rb, registeredRepoNames) {
28367
+ const runbookRepoSet = new Set(rb.repos);
28368
+ for (const repoName of rb.repos) {
28369
+ if (!registeredRepoNames.has(repoName)) {
28370
+ throw new Error(`repo "${repoName}" is not registered. Add it with "olam repos add".`);
28371
+ }
28372
+ }
28373
+ if (rb.seeds) {
28374
+ for (const seed of rb.seeds) {
28375
+ if (seed.repo && !runbookRepoSet.has(seed.repo)) {
28376
+ throw new Error(`seed references repo "${seed.repo}" which is not in runbook repos [${rb.repos.join(", ")}].`);
28377
+ }
28378
+ }
28379
+ }
28380
+ if (rb.portMap) {
28381
+ for (const repoKey of Object.keys(rb.portMap)) {
28382
+ if (!runbookRepoSet.has(repoKey)) {
28383
+ throw new Error(`portMap references repo "${repoKey}" which is not in runbook repos [${rb.repos.join(", ")}].`);
28384
+ }
28385
+ }
28386
+ }
28387
+ if (rb.env) {
28388
+ for (const repoKey of Object.keys(rb.env)) {
28389
+ if (!runbookRepoSet.has(repoKey)) {
28390
+ throw new Error(`env references repo "${repoKey}" which is not in runbook repos [${rb.repos.join(", ")}].`);
28391
+ }
28392
+ }
28393
+ }
28394
+ if (rb.portMap) {
28395
+ const seen = /* @__PURE__ */ new Map();
28396
+ for (const [repoName, svcMap] of Object.entries(rb.portMap)) {
28397
+ for (const [svcName, port] of Object.entries(svcMap)) {
28398
+ if (port < 1024 || port > 65535) {
28399
+ throw new Error(`port ${port} for ${repoName}.${svcName} is out of range. Ports must be 1024\u201365535.`);
28400
+ }
28401
+ const label = `${repoName}.${svcName}`;
28402
+ const previous = seen.get(port);
28403
+ if (previous !== void 0) {
28404
+ throw new Error(`port ${port} declared twice in runbook "${rb.name}" (${previous} and ${label}). Each port must be unique within a runbook.`);
28405
+ }
28406
+ seen.set(port, label);
28407
+ }
28408
+ }
28409
+ }
28410
+ }
28411
+ function listRunbooks() {
28412
+ return readGlobalConfig().runbooks;
28413
+ }
28414
+ function addRunbook(entry) {
28415
+ const config2 = readGlobalConfig();
28416
+ if (config2.runbooks.some((r) => r.name === entry.name)) {
28417
+ throw new Error(`runbook "${entry.name}" already exists. Use "olam runbooks update" to modify it.`);
28418
+ }
28419
+ const registeredRepoNames = new Set(config2.repos.map((r) => r.name));
28420
+ validateRunbookIntegrity({ ...entry, seeds: entry.seeds }, registeredRepoNames);
28421
+ const now = Date.now();
28422
+ const newRunbook = {
28423
+ name: entry.name,
28424
+ repos: entry.repos,
28425
+ updatedAt: now,
28426
+ ...entry.description !== void 0 ? { description: entry.description } : {},
28427
+ ...entry.portMap !== void 0 ? { portMap: entry.portMap } : {},
28428
+ ...entry.seeds !== void 0 ? { seeds: entry.seeds } : {},
28429
+ ...entry.env !== void 0 ? { env: entry.env } : {}
28430
+ };
28431
+ writeGlobalConfig({ ...config2, runbooks: [...config2.runbooks, newRunbook] });
28432
+ return newRunbook;
28433
+ }
28434
+ function removeRunbook(name) {
28435
+ const config2 = readGlobalConfig();
28436
+ if (!config2.runbooks.some((r) => r.name === name)) {
28437
+ throw new Error(`runbook "${name}" not found. Run "olam runbooks list" to see available runbooks.`);
28438
+ }
28439
+ writeGlobalConfig({ ...config2, runbooks: config2.runbooks.filter((r) => r.name !== name) });
28440
+ }
28441
+ function getRunbook(name) {
28442
+ const config2 = readGlobalConfig();
28443
+ const found = config2.runbooks.find((r) => r.name === name);
28444
+ if (!found) {
28445
+ throw new Error(`runbook "${name}" not found. Run "olam runbooks list" to see available runbooks.`);
28446
+ }
28447
+ return found;
28448
+ }
28449
+
28450
+ // ../core/dist/global-config/port-validator.js
28451
+ import * as net2 from "node:net";
28452
+ import * as childProcess from "node:child_process";
28453
+ import { createRequire as createRequire3 } from "node:module";
28454
+ var _require3;
28455
+ function getRequire() {
28456
+ if (!_require3) {
28457
+ _require3 = createRequire3(import.meta.url);
28458
+ }
28459
+ return _require3;
28460
+ }
28461
+ function checkPortInUse(port) {
28462
+ try {
28463
+ const server = net2.createServer();
28464
+ server.unref();
28465
+ let inUse = false;
28466
+ try {
28467
+ server.listen({ port, host: "0.0.0.0" });
28468
+ server.close();
28469
+ } catch {
28470
+ inUse = true;
28471
+ }
28472
+ return inUse;
28473
+ } catch {
28474
+ return false;
28475
+ }
28476
+ }
28477
+ function getPidForPort(port) {
28478
+ try {
28479
+ const out = childProcess.execSync(`lsof -ti :${port}`, {
28480
+ encoding: "utf8",
28481
+ stdio: ["pipe", "pipe", "pipe"],
28482
+ timeout: 2e3
28483
+ });
28484
+ const pid = parseInt(out.trim().split("\n")[0] ?? "", 10);
28485
+ return Number.isNaN(pid) ? void 0 : pid;
28486
+ } catch {
28487
+ return void 0;
28488
+ }
28489
+ }
28490
+ function getOlamWorldForPortDefault(port) {
28491
+ try {
28492
+ const { WorldRegistry: WorldRegistry2 } = getRequire()("../world/registry.js");
28493
+ const registry2 = new WorldRegistry2();
28494
+ try {
28495
+ const worlds = registry2.list();
28496
+ for (const world of worlds) {
28497
+ if (world.appPortUrls?.some((apu) => apu.hostPort === port)) {
28498
+ return world.id;
28499
+ }
28500
+ }
28501
+ } finally {
28502
+ registry2.close();
28503
+ }
28504
+ } catch {
28505
+ }
28506
+ return void 0;
28507
+ }
28508
+ function validateRunbookPorts(runbook, deps) {
28509
+ if (!runbook.portMap || Object.keys(runbook.portMap).length === 0) {
28510
+ return { conflicts: [] };
28511
+ }
28512
+ const isInUse = deps?.isPortInUse ?? checkPortInUse;
28513
+ const getWorldId = deps?.getOlamWorldForPort ?? getOlamWorldForPortDefault;
28514
+ const conflicts = [];
28515
+ for (const [repoName, svcMap] of Object.entries(runbook.portMap)) {
28516
+ for (const [serviceName, port] of Object.entries(svcMap)) {
28517
+ if (!isInUse(port))
28518
+ continue;
28519
+ const worldId = getWorldId(port);
28520
+ const occupant = worldId ? { type: "olam-world", worldId } : { type: "non-olam", pid: getPidForPort(port) };
28521
+ conflicts.push({ port, repoName, serviceName, occupant });
28522
+ }
28523
+ }
28524
+ return { conflicts };
28525
+ }
28526
+
28357
28527
  // ../mcp-server/src/tools/repo.ts
28358
28528
  function asMessage4(err) {
28359
28529
  return err instanceof Error ? err.message : String(err);
@@ -28382,9 +28552,9 @@ function register22(server, _ctx, _initError) {
28382
28552
  description: external_exports.string().optional().describe("Optional human-readable description."),
28383
28553
  defaultBranch: external_exports.string().optional().describe("Default branch name (e.g. main).")
28384
28554
  },
28385
- async ({ name, path: path23, description, defaultBranch }) => {
28555
+ async ({ name, path: path27, description, defaultBranch }) => {
28386
28556
  try {
28387
- const entry = addRepo({ name, path: path23, description, defaultBranch });
28557
+ const entry = addRepo({ name, path: path27, description, defaultBranch });
28388
28558
  return {
28389
28559
  content: [{
28390
28560
  type: "text",
@@ -28425,9 +28595,9 @@ function register22(server, _ctx, _initError) {
28425
28595
  description: external_exports.string().optional().describe("New description."),
28426
28596
  defaultBranch: external_exports.string().optional().describe("New default branch.")
28427
28597
  },
28428
- async ({ name, path: path23, description, defaultBranch }) => {
28598
+ async ({ name, path: path27, description, defaultBranch }) => {
28429
28599
  try {
28430
- const entry = updateRepo(name, { path: path23, description, defaultBranch });
28600
+ const entry = updateRepo(name, { path: path27, description, defaultBranch });
28431
28601
  return {
28432
28602
  content: [{
28433
28603
  type: "text",
@@ -28441,6 +28611,299 @@ function register22(server, _ctx, _initError) {
28441
28611
  );
28442
28612
  }
28443
28613
 
28614
+ // ../mcp-server/src/tools/process-port.ts
28615
+ var process_port_exports = {};
28616
+ __export(process_port_exports, {
28617
+ register: () => register23,
28618
+ resolveHostCpToken: () => resolveHostCpToken
28619
+ });
28620
+ import fs10 from "node:fs";
28621
+ import os9 from "node:os";
28622
+ import path14 from "node:path";
28623
+ var HOST_CP_BASE = "http://127.0.0.1:19000";
28624
+ function resolveHostCpToken() {
28625
+ const envToken = process.env["OLAM_HOST_CP_TOKEN"];
28626
+ if (envToken) return envToken;
28627
+ const tokenPath = path14.join(os9.homedir(), ".olam", "host-cp.token");
28628
+ if (fs10.existsSync(tokenPath)) return fs10.readFileSync(tokenPath, "utf-8").trim();
28629
+ return null;
28630
+ }
28631
+ function tokenMissingError() {
28632
+ return {
28633
+ content: [{
28634
+ type: "text",
28635
+ text: "host-cp token not found. Set OLAM_HOST_CP_TOKEN or write ~/.olam/host-cp.token."
28636
+ }],
28637
+ isError: true
28638
+ };
28639
+ }
28640
+ function asMessage5(err) {
28641
+ return err instanceof Error ? err.message : String(err);
28642
+ }
28643
+ function register23(server, _ctx, _initError) {
28644
+ server.tool(
28645
+ "olam_process_list",
28646
+ "List running processes inside a world container. Returns a process table snapshot via host-cp. Fields per process: pid, user, cpu, mem, started, state, command.",
28647
+ {
28648
+ world_id: external_exports.string().describe("World ID.")
28649
+ },
28650
+ async ({ world_id }) => {
28651
+ const token = resolveHostCpToken();
28652
+ if (!token) return tokenMissingError();
28653
+ try {
28654
+ const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/processes`, {
28655
+ headers: { Authorization: `Bearer ${token}` },
28656
+ signal: AbortSignal.timeout(1e4)
28657
+ });
28658
+ const body = await res.text();
28659
+ if (!res.ok) {
28660
+ return {
28661
+ content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
28662
+ isError: true
28663
+ };
28664
+ }
28665
+ const data = JSON.parse(body);
28666
+ return {
28667
+ content: [{ type: "text", text: JSON.stringify({ processes: data.processes }, null, 2) }]
28668
+ };
28669
+ } catch (err) {
28670
+ return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
28671
+ }
28672
+ }
28673
+ );
28674
+ server.tool(
28675
+ "olam_port_expose",
28676
+ "Expose a port inside a world container to the host by creating a socat bridge. Returns the host-side URL and bridge metadata. The bridge persists until removed with olam_port_unexpose.",
28677
+ {
28678
+ world_id: external_exports.string().describe("World ID."),
28679
+ port: external_exports.number().int().min(1).max(65535).describe("Container port to expose on the host.")
28680
+ },
28681
+ async ({ world_id, port }) => {
28682
+ const token = resolveHostCpToken();
28683
+ if (!token) return tokenMissingError();
28684
+ try {
28685
+ const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/server-bridges`, {
28686
+ method: "POST",
28687
+ headers: {
28688
+ "Content-Type": "application/json",
28689
+ Authorization: `Bearer ${token}`
28690
+ },
28691
+ body: JSON.stringify({ port }),
28692
+ signal: AbortSignal.timeout(3e4)
28693
+ });
28694
+ const body = await res.text();
28695
+ if (!res.ok) {
28696
+ return {
28697
+ content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
28698
+ isError: true
28699
+ };
28700
+ }
28701
+ const data = JSON.parse(body);
28702
+ return {
28703
+ content: [{
28704
+ type: "text",
28705
+ text: JSON.stringify({
28706
+ url: `http://localhost:${data.hostPort}`,
28707
+ host_port: data.hostPort,
28708
+ sidecar_container_id: data.containerId,
28709
+ container_port: data.containerPort,
28710
+ container_name: data.containerName
28711
+ }, null, 2)
28712
+ }]
28713
+ };
28714
+ } catch (err) {
28715
+ return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
28716
+ }
28717
+ }
28718
+ );
28719
+ server.tool(
28720
+ "olam_port_unexpose",
28721
+ "Tear down a port bridge for a world container. Removes the socat sidecar and frees the host port.",
28722
+ {
28723
+ world_id: external_exports.string().describe("World ID."),
28724
+ port: external_exports.number().int().min(1).max(65535).describe("Container port whose bridge should be removed.")
28725
+ },
28726
+ async ({ world_id, port }) => {
28727
+ const token = resolveHostCpToken();
28728
+ if (!token) return tokenMissingError();
28729
+ try {
28730
+ const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/server-bridges/${port}`, {
28731
+ method: "DELETE",
28732
+ headers: { Authorization: `Bearer ${token}` },
28733
+ signal: AbortSignal.timeout(1e4)
28734
+ });
28735
+ if (!res.ok) {
28736
+ const body = await res.text();
28737
+ return {
28738
+ content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
28739
+ isError: true
28740
+ };
28741
+ }
28742
+ return { content: [{ type: "text", text: JSON.stringify({ ok: true }) }] };
28743
+ } catch (err) {
28744
+ return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
28745
+ }
28746
+ }
28747
+ );
28748
+ }
28749
+
28750
+ // ../mcp-server/src/tools/runbook.ts
28751
+ var runbook_exports = {};
28752
+ __export(runbook_exports, {
28753
+ register: () => register24
28754
+ });
28755
+ function asMessage6(err) {
28756
+ return err instanceof Error ? err.message : String(err);
28757
+ }
28758
+ function formatConflicts(conflicts) {
28759
+ const lines = conflicts.map((c) => {
28760
+ const occupant = c.occupant.type === "olam-world" ? `in use by world "${c.occupant.worldId}" (olam)` : `in use by PID ${c.occupant.pid ?? "unknown"} (non-olam)`;
28761
+ return ` \u2717 ${c.repoName}.${c.serviceName}:${c.port} \u2014 ${occupant}`;
28762
+ });
28763
+ return lines.join("\n");
28764
+ }
28765
+ function register24(server, ctx, _initError) {
28766
+ server.tool(
28767
+ "olam_runbook_list",
28768
+ "List all runbooks in ~/.olam/config.json. Returns { runbooks: Runbook[] }.",
28769
+ {},
28770
+ async () => {
28771
+ const runbooks = listRunbooks();
28772
+ return {
28773
+ content: [{ type: "text", text: JSON.stringify({ runbooks }, null, 2) }]
28774
+ };
28775
+ }
28776
+ );
28777
+ server.tool(
28778
+ "olam_runbook_show",
28779
+ "Show a single runbook by name. Returns { runbook: Runbook }.",
28780
+ {
28781
+ name: external_exports.string().min(1).describe("Runbook name.")
28782
+ },
28783
+ async ({ name }) => {
28784
+ try {
28785
+ const runbook = getRunbook(name);
28786
+ return {
28787
+ content: [{ type: "text", text: JSON.stringify({ runbook }, null, 2) }]
28788
+ };
28789
+ } catch (err) {
28790
+ return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
28791
+ }
28792
+ }
28793
+ );
28794
+ server.tool(
28795
+ "olam_runbook_add",
28796
+ "Create a new runbook. Validates that all referenced repos exist in the global registry and that portMap has no duplicates.",
28797
+ {
28798
+ name: external_exports.string().min(1).describe("Runbook name (lowercase, digits, dash; 1\u201364 chars)."),
28799
+ repos: external_exports.array(external_exports.string().min(1)).min(1).describe("Repo names (must exist in registry)."),
28800
+ description: external_exports.string().optional().describe("Optional human-readable description."),
28801
+ portMap: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.number().int())).optional().describe("Port mappings: { repoName: { serviceName: hostPort } }."),
28802
+ seeds: external_exports.array(
28803
+ external_exports.union([
28804
+ external_exports.object({ type: external_exports.literal("sql-file"), repo: external_exports.string(), service: external_exports.string(), path: external_exports.string() }),
28805
+ external_exports.object({ type: external_exports.literal("command"), repo: external_exports.string(), run: external_exports.string() }),
28806
+ external_exports.object({ type: external_exports.literal("fixture-copy"), repo: external_exports.string(), src: external_exports.string(), dest: external_exports.string() })
28807
+ ])
28808
+ ).optional().describe("Optional seed operations to run when applying the runbook."),
28809
+ env: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.string())).optional().describe("Per-repo environment variable overrides.")
28810
+ },
28811
+ async ({ name, repos, description, portMap, seeds, env }) => {
28812
+ try {
28813
+ const runbook = addRunbook({ name, repos, description, portMap, seeds, env });
28814
+ return {
28815
+ content: [{
28816
+ type: "text",
28817
+ text: JSON.stringify({ name: runbook.name, message: `Created runbook "${runbook.name}".` }, null, 2)
28818
+ }]
28819
+ };
28820
+ } catch (err) {
28821
+ return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
28822
+ }
28823
+ }
28824
+ );
28825
+ server.tool(
28826
+ "olam_runbook_remove",
28827
+ "Remove a runbook from the global config. Does not affect any running worlds.",
28828
+ {
28829
+ name: external_exports.string().min(1).describe("Runbook name to remove.")
28830
+ },
28831
+ async ({ name }) => {
28832
+ try {
28833
+ removeRunbook(name);
28834
+ return {
28835
+ content: [{
28836
+ type: "text",
28837
+ text: JSON.stringify({ name, message: `Removed runbook "${name}".` }, null, 2)
28838
+ }]
28839
+ };
28840
+ } catch (err) {
28841
+ return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
28842
+ }
28843
+ }
28844
+ );
28845
+ server.tool(
28846
+ "olam_runbook_apply",
28847
+ "Validate ports then create a world from a runbook. Errors (isError: true) on port conflicts. Returns world metadata on success.",
28848
+ {
28849
+ name: external_exports.string().min(1).describe("Runbook name."),
28850
+ worldName: external_exports.string().optional().describe("Override the world name."),
28851
+ task: external_exports.string().optional().describe("Initial task to dispatch into the world."),
28852
+ branchName: external_exports.string().optional().describe("Override the default branch name.")
28853
+ },
28854
+ async ({ name, worldName, task, branchName }) => {
28855
+ if (!ctx) {
28856
+ return {
28857
+ content: [{ type: "text", text: "Olam is not configured. Run /olam:init to set up." }],
28858
+ isError: true
28859
+ };
28860
+ }
28861
+ let runbook;
28862
+ try {
28863
+ runbook = getRunbook(name);
28864
+ } catch (err) {
28865
+ return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
28866
+ }
28867
+ const { conflicts } = validateRunbookPorts(runbook);
28868
+ if (conflicts.length > 0) {
28869
+ const detail = formatConflicts(conflicts);
28870
+ const msg = `Port conflicts detected for runbook "${name}":
28871
+ ${detail}
28872
+
28873
+ ${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`;
28874
+ return {
28875
+ content: [{ type: "text", text: msg }],
28876
+ isError: true
28877
+ };
28878
+ }
28879
+ try {
28880
+ const worldOpts = {
28881
+ name: worldName ?? name,
28882
+ repos: runbook.repos,
28883
+ task: task ?? `Apply runbook "${name}"`,
28884
+ ...branchName ? { branchName } : {}
28885
+ };
28886
+ const world = await ctx.worldManager.createWorld(worldOpts);
28887
+ const result = {
28888
+ worldId: world.id,
28889
+ name: world.name,
28890
+ branch: world.branch,
28891
+ status: world.status,
28892
+ repos: world.repos,
28893
+ portOffset: world.portOffset,
28894
+ ...world.appPortUrls ? { appPortUrls: world.appPortUrls } : {},
28895
+ message: `World "${world.name}" created from runbook "${name}".`
28896
+ };
28897
+ return {
28898
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
28899
+ };
28900
+ } catch (err) {
28901
+ return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
28902
+ }
28903
+ }
28904
+ );
28905
+ }
28906
+
28444
28907
  // ../mcp-server/src/tools/index.ts
28445
28908
  var toolModules = [
28446
28909
  init_exports,
@@ -28464,7 +28927,9 @@ var toolModules = [
28464
28927
  lane_merge_exports,
28465
28928
  capture_view_exports,
28466
28929
  create_from_prompt_exports,
28467
- repo_exports
28930
+ repo_exports,
28931
+ process_port_exports,
28932
+ runbook_exports
28468
28933
  ];
28469
28934
  function registerAllTools(server, ctx, initError) {
28470
28935
  for (const mod of toolModules) {
@@ -28481,7 +28946,7 @@ var SERVER_INSTRUCTIONS = [
28481
28946
  "Use olam_observe to watch a world's reasoning. Use olam_crystallize to save thoughts.",
28482
28947
  "Always olam_destroy_world when done to clean up resources."
28483
28948
  ].join("\n");
28484
- function createServer3(ctx, initError) {
28949
+ function createServer4(ctx, initError) {
28485
28950
  const server = new McpServer(
28486
28951
  { name: SERVER_NAME, version: SERVER_VERSION },
28487
28952
  { instructions: SERVER_INSTRUCTIONS }
@@ -28496,8 +28961,8 @@ function createServer3(ctx, initError) {
28496
28961
  }
28497
28962
 
28498
28963
  // ../core/dist/config/loader.js
28499
- import * as fs11 from "node:fs";
28500
- import * as path14 from "node:path";
28964
+ import * as fs12 from "node:fs";
28965
+ import * as path15 from "node:path";
28501
28966
  import { parse as parseYaml2 } from "yaml";
28502
28967
 
28503
28968
  // ../core/dist/config/schema.js
@@ -28877,11 +29342,11 @@ function substituteEnvVars(obj) {
28877
29342
  }
28878
29343
 
28879
29344
  // ../core/dist/config/dotenv.js
28880
- import * as fs10 from "node:fs";
29345
+ import * as fs11 from "node:fs";
28881
29346
  function loadDotEnv(envPath) {
28882
- if (!fs10.existsSync(envPath))
29347
+ if (!fs11.existsSync(envPath))
28883
29348
  return;
28884
- const content = fs10.readFileSync(envPath, "utf-8");
29349
+ const content = fs11.readFileSync(envPath, "utf-8");
28885
29350
  for (const line of content.split("\n")) {
28886
29351
  const trimmed = line.trim();
28887
29352
  if (!trimmed || trimmed.startsWith("#"))
@@ -28900,18 +29365,18 @@ function loadDotEnv(envPath) {
28900
29365
  // ../core/dist/config/loader.js
28901
29366
  function findConfigFile(startDir) {
28902
29367
  const searched = [];
28903
- let current = path14.resolve(startDir);
29368
+ let current = path15.resolve(startDir);
28904
29369
  while (true) {
28905
- const newLayout = path14.join(current, CONFIG_DIR_NAME, CONFIG_FILENAME);
28906
- const legacyLayout = path14.join(current, LEGACY_CONFIG_FILENAME);
29370
+ const newLayout = path15.join(current, CONFIG_DIR_NAME, CONFIG_FILENAME);
29371
+ const legacyLayout = path15.join(current, LEGACY_CONFIG_FILENAME);
28907
29372
  searched.push(newLayout, legacyLayout);
28908
- if (fs11.existsSync(newLayout)) {
29373
+ if (fs12.existsSync(newLayout)) {
28909
29374
  return { path: newLayout, isLegacy: false };
28910
29375
  }
28911
- if (fs11.existsSync(legacyLayout)) {
29376
+ if (fs12.existsSync(legacyLayout)) {
28912
29377
  return { path: legacyLayout, isLegacy: true };
28913
29378
  }
28914
- const parent = path14.dirname(current);
29379
+ const parent = path15.dirname(current);
28915
29380
  if (parent === current)
28916
29381
  break;
28917
29382
  current = parent;
@@ -28926,12 +29391,12 @@ function loadConfig(startDir) {
28926
29391
  Run /olam:init to migrate to .olam/config.yaml
28927
29392
  `);
28928
29393
  } else {
28929
- const envPath = path14.join(path14.dirname(found.path), ".env");
29394
+ const envPath = path15.join(path15.dirname(found.path), ".env");
28930
29395
  loadDotEnv(envPath);
28931
29396
  }
28932
29397
  let rawContent;
28933
29398
  try {
28934
- rawContent = fs11.readFileSync(found.path, "utf-8");
29399
+ rawContent = fs12.readFileSync(found.path, "utf-8");
28935
29400
  } catch (err) {
28936
29401
  throw new Error(`Failed to read ${found.path}: ${err instanceof Error ? err.message : String(err)}`);
28937
29402
  }
@@ -28961,11 +29426,11 @@ function loadConfig(startDir) {
28961
29426
  }
28962
29427
 
28963
29428
  // ../core/dist/world/manager.js
28964
- import * as crypto4 from "node:crypto";
28965
- import { execSync as execSync4 } from "node:child_process";
28966
- import * as fs16 from "node:fs";
28967
- import * as os10 from "node:os";
28968
- import * as path20 from "node:path";
29429
+ import * as crypto5 from "node:crypto";
29430
+ import { execSync as execSync5 } from "node:child_process";
29431
+ import * as fs20 from "node:fs";
29432
+ import * as os13 from "node:os";
29433
+ import * as path24 from "node:path";
28969
29434
 
28970
29435
  // ../core/dist/world/state.js
28971
29436
  var VALID_TRANSITIONS = {
@@ -28999,6 +29464,7 @@ var WorldStateMachine = class {
28999
29464
  };
29000
29465
 
29001
29466
  // ../core/dist/world/devbox-image.js
29467
+ var WORLDSPEC_OVERRIDE_TAG = "worldspec-override";
29002
29468
  function selectDevboxImage(config2, repos) {
29003
29469
  const selectors = config2.devbox?.image_selectors;
29004
29470
  if (!selectors || selectors.length === 0)
@@ -29010,7 +29476,8 @@ function selectDevboxImage(config2, repos) {
29010
29476
  return {
29011
29477
  image: resolveDevboxImage(config2, sel.tag),
29012
29478
  cacheArch: sel.cache_arch,
29013
- tag: sel.tag
29479
+ tag: sel.tag,
29480
+ source: "image_selector"
29014
29481
  };
29015
29482
  }
29016
29483
  }
@@ -29031,6 +29498,18 @@ function matches(sel, repoNames, repoTypes) {
29031
29498
  }
29032
29499
  return true;
29033
29500
  }
29501
+ function selectDevboxImageForWorld(args) {
29502
+ if (args.worldspec) {
29503
+ return {
29504
+ image: args.worldspec.images.devbox,
29505
+ cacheArch: void 0,
29506
+ // v1 worldspec schema doesn't carry cache_arch
29507
+ tag: WORLDSPEC_OVERRIDE_TAG,
29508
+ source: "worldspec"
29509
+ };
29510
+ }
29511
+ return selectDevboxImage(args.config, args.repos);
29512
+ }
29034
29513
  function resolveDevboxImage(config2, tag) {
29035
29514
  const registry2 = config2.devbox?.registry;
29036
29515
  if (!registry2)
@@ -29047,8 +29526,8 @@ function resolveDevboxImage(config2, tag) {
29047
29526
 
29048
29527
  // ../core/dist/world/worktree.js
29049
29528
  import { execFileSync as execFileSync2 } from "node:child_process";
29050
- import * as fs12 from "node:fs";
29051
- import * as path15 from "node:path";
29529
+ import * as fs13 from "node:fs";
29530
+ import * as path16 from "node:path";
29052
29531
  function resolveGitDir(repo) {
29053
29532
  if (repo.path) {
29054
29533
  return repo.path;
@@ -29058,11 +29537,11 @@ function resolveGitDir(repo) {
29058
29537
  async function createWorktrees(repos, worldId, workspacePath, branch) {
29059
29538
  const created = [];
29060
29539
  for (const repo of repos) {
29061
- const worktreePath = path15.join(workspacePath, repo.name);
29540
+ const worktreePath = path16.join(workspacePath, repo.name);
29062
29541
  const gitDir = resolveGitDir(repo);
29063
29542
  const branchName = branch || `olam/${worldId}`;
29064
29543
  try {
29065
- fs12.mkdirSync(path15.dirname(worktreePath), { recursive: true });
29544
+ fs13.mkdirSync(path16.dirname(worktreePath), { recursive: true });
29066
29545
  execFileSync2("git", ["worktree", "add", worktreePath, "-b", branchName], {
29067
29546
  cwd: gitDir,
29068
29547
  stdio: "pipe"
@@ -29095,7 +29574,7 @@ async function createWorktrees(repos, worldId, workspacePath, branch) {
29095
29574
  }
29096
29575
  async function removeWorktrees(repos, workspacePath) {
29097
29576
  for (const repo of repos) {
29098
- const worktreePath = path15.join(workspacePath, repo.name);
29577
+ const worktreePath = path16.join(workspacePath, repo.name);
29099
29578
  let gitDir;
29100
29579
  try {
29101
29580
  gitDir = resolveGitDir(repo);
@@ -29170,12 +29649,12 @@ function removeBranch(repo, branch) {
29170
29649
 
29171
29650
  // ../core/dist/world/baseline-diff.js
29172
29651
  import { execFileSync as execFileSync3 } from "node:child_process";
29173
- import * as fs13 from "node:fs";
29174
- import * as os9 from "node:os";
29175
- import * as path16 from "node:path";
29652
+ import * as fs14 from "node:fs";
29653
+ import * as os10 from "node:os";
29654
+ import * as path17 from "node:path";
29176
29655
  var DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
29177
- function expandHome(p, homedir13) {
29178
- return p.replace(/^~(?=$|\/|\\)/, homedir13());
29656
+ function expandHome(p, homedir15) {
29657
+ return p.replace(/^~(?=$|\/|\\)/, homedir15());
29179
29658
  }
29180
29659
  function sanitizeRepoFilename(name) {
29181
29660
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -29198,10 +29677,10 @@ ${stderr}`;
29198
29677
  }
29199
29678
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
29200
29679
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
29201
- const homedir13 = deps.homedir ?? (() => os9.homedir());
29202
- const baselineDir = path16.join(workspacePath, ".olam", "baseline");
29680
+ const homedir15 = deps.homedir ?? (() => os10.homedir());
29681
+ const baselineDir = path17.join(workspacePath, ".olam", "baseline");
29203
29682
  try {
29204
- fs13.mkdirSync(baselineDir, { recursive: true });
29683
+ fs14.mkdirSync(baselineDir, { recursive: true });
29205
29684
  } catch (err) {
29206
29685
  const msg = err instanceof Error ? err.message : String(err);
29207
29686
  console.warn(`[baseline-diff] mkdir ${baselineDir} failed: ${msg}; reaper will see no baseline at all`);
@@ -29213,9 +29692,9 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
29213
29692
  if (!repo.path)
29214
29693
  continue;
29215
29694
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
29216
- const outPath = path16.join(baselineDir, filename);
29217
- const repoPath = expandHome(repo.path, homedir13);
29218
- if (!fs13.existsSync(repoPath)) {
29695
+ const outPath = path17.join(baselineDir, filename);
29696
+ const repoPath = expandHome(repo.path, homedir15);
29697
+ if (!fs14.existsSync(repoPath)) {
29219
29698
  writeBaselineFile(outPath, `# repo: ${repo.name}
29220
29699
  # (skipped: path ${repoPath} does not exist)
29221
29700
  `);
@@ -29282,7 +29761,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
29282
29761
  }
29283
29762
  function writeBaselineFile(outPath, content) {
29284
29763
  try {
29285
- fs13.writeFileSync(outPath, content);
29764
+ fs14.writeFileSync(outPath, content);
29286
29765
  } catch (err) {
29287
29766
  const msg = err instanceof Error ? err.message : String(err);
29288
29767
  console.warn(`[baseline-diff] write to ${outPath} failed: ${msg}`);
@@ -29290,8 +29769,8 @@ function writeBaselineFile(outPath, content) {
29290
29769
  }
29291
29770
  function stripWorktreeEdits(repos, workspacePath) {
29292
29771
  for (const repo of repos) {
29293
- const worktreePath = path16.join(workspacePath, repo.name);
29294
- if (!fs13.existsSync(worktreePath))
29772
+ const worktreePath = path17.join(workspacePath, repo.name);
29773
+ if (!fs14.existsSync(worktreePath))
29295
29774
  continue;
29296
29775
  try {
29297
29776
  execFileSync3("git", ["checkout", "--", "."], {
@@ -29321,12 +29800,12 @@ function formatBaselineSummary(result) {
29321
29800
  }
29322
29801
 
29323
29802
  // ../core/dist/world/context-injection.js
29324
- import * as fs14 from "node:fs";
29325
- import * as path17 from "node:path";
29803
+ import * as fs15 from "node:fs";
29804
+ import * as path18 from "node:path";
29326
29805
  function injectWorldContext(opts) {
29327
29806
  const { world, task, linearTicketId, claudeMdExtra, taskContext, services, pleriPlaneUrl } = opts;
29328
- const claudeDir = path17.join(world.workspacePath, ".claude");
29329
- fs14.mkdirSync(claudeDir, { recursive: true });
29807
+ const claudeDir = path18.join(world.workspacePath, ".claude");
29808
+ fs15.mkdirSync(claudeDir, { recursive: true });
29330
29809
  const sections = [];
29331
29810
  sections.push(`# Olam World: ${world.name}`);
29332
29811
  sections.push("");
@@ -29487,7 +29966,7 @@ function injectWorldContext(opts) {
29487
29966
  sections.push("");
29488
29967
  }
29489
29968
  const content = sections.join("\n");
29490
- fs14.writeFileSync(path17.join(claudeDir, "CLAUDE.md"), content);
29969
+ fs15.writeFileSync(path18.join(claudeDir, "CLAUDE.md"), content);
29491
29970
  }
29492
29971
  function formatTaskSource(ctx) {
29493
29972
  if (ctx.source === "linear" && ctx.ticketId) {
@@ -29501,9 +29980,9 @@ function formatTaskSource(ctx) {
29501
29980
  function hasPlanFile(world) {
29502
29981
  if (world.repos.length === 0)
29503
29982
  return false;
29504
- const plansDir = path17.join(world.workspacePath, world.repos[0], "docs", "plans");
29983
+ const plansDir = path18.join(world.workspacePath, world.repos[0], "docs", "plans");
29505
29984
  try {
29506
- return fs14.existsSync(plansDir) && fs14.readdirSync(plansDir).length > 0;
29985
+ return fs15.existsSync(plansDir) && fs15.readdirSync(plansDir).length > 0;
29507
29986
  } catch {
29508
29987
  return false;
29509
29988
  }
@@ -29729,7 +30208,7 @@ async function installStack(exec, repos, stacks) {
29729
30208
  }
29730
30209
 
29731
30210
  // ../core/dist/world/stack-image.js
29732
- import { execSync as execSync2 } from "node:child_process";
30211
+ import { execSync as execSync3 } from "node:child_process";
29733
30212
  import * as crypto3 from "node:crypto";
29734
30213
  var BASE_IMAGE = "olam-devbox";
29735
30214
  var LABEL_PREFIX = "olam.stack-image";
@@ -29761,7 +30240,7 @@ function lookupCachedImage(runtimes) {
29761
30240
  const tag = computeImageTag(runtimes);
29762
30241
  const imageName = `${BASE_IMAGE}:${tag}`;
29763
30242
  try {
29764
- execSync2(`docker image inspect ${imageName} > /dev/null 2>&1`, {
30243
+ execSync3(`docker image inspect ${imageName} > /dev/null 2>&1`, {
29765
30244
  stdio: "pipe",
29766
30245
  timeout: 5e3
29767
30246
  });
@@ -29773,14 +30252,14 @@ function lookupCachedImage(runtimes) {
29773
30252
  function commitAsImage(containerName, imageName) {
29774
30253
  const baseDigest = getBaseImageDigest();
29775
30254
  const now = (/* @__PURE__ */ new Date()).toISOString();
29776
- execSync2(`docker commit --change 'LABEL ${LABEL_PREFIX}=true' --change 'LABEL ${LABEL_PREFIX}.created-at=${now}' --change 'LABEL ${LABEL_PREFIX}.base-digest=${baseDigest}' ${containerName} ${imageName}`, { stdio: "pipe", timeout: 12e4 });
30255
+ execSync3(`docker commit --change 'LABEL ${LABEL_PREFIX}=true' --change 'LABEL ${LABEL_PREFIX}.created-at=${now}' --change 'LABEL ${LABEL_PREFIX}.base-digest=${baseDigest}' ${containerName} ${imageName}`, { stdio: "pipe", timeout: 12e4 });
29777
30256
  }
29778
30257
  var cachedBaseDigest;
29779
30258
  function getBaseImageDigest() {
29780
30259
  if (cachedBaseDigest)
29781
30260
  return cachedBaseDigest;
29782
30261
  try {
29783
- const digest = execSync2(`docker inspect ${BASE_IMAGE}:latest --format '{{.Id}}'`, { encoding: "utf-8", timeout: 5e3 }).trim();
30262
+ const digest = execSync3(`docker inspect ${BASE_IMAGE}:latest --format '{{.Id}}'`, { encoding: "utf-8", timeout: 5e3 }).trim();
29784
30263
  cachedBaseDigest = digest.replace("sha256:", "").slice(0, 16);
29785
30264
  return cachedBaseDigest;
29786
30265
  } catch {
@@ -30005,6 +30484,9 @@ var SENTINEL_SUFFIX = "-done";
30005
30484
  function sentinelPath(workdir, index) {
30006
30485
  return `${workdir}/${SENTINEL_PREFIX}${index}${SENTINEL_SUFFIX}`;
30007
30486
  }
30487
+ function bootstrapStepSentinelPath(workdir, index) {
30488
+ return sentinelPath(workdir, index);
30489
+ }
30008
30490
  async function runBootstrap(containerName, workdir, steps, exec, options = {}) {
30009
30491
  if (!SAFE_IDENT.test(containerName)) {
30010
30492
  throw new Error(`containerName "${containerName}" must match ${SAFE_IDENT} (defensive guard)`);
@@ -30060,8 +30542,430 @@ function shellQuote(s) {
30060
30542
  return `'${s.replace(/'/g, `'\\''`)}'`;
30061
30543
  }
30062
30544
 
30545
+ // ../core/dist/world/snapshot.js
30546
+ import * as crypto4 from "node:crypto";
30547
+ import * as fs16 from "node:fs";
30548
+ import * as os11 from "node:os";
30549
+ import * as path19 from "node:path";
30550
+ import { execFileSync as execFileSync4, spawn } from "node:child_process";
30551
+ import { gunzipSync } from "node:zlib";
30552
+ function snapshotsDir() {
30553
+ return process.env["OLAM_SNAPSHOTS_DIR"] ?? path19.join(os11.homedir(), ".olam", "snapshots");
30554
+ }
30555
+ function snapshotKindDirByWorkspace(workspace, arch, kind) {
30556
+ return path19.join(snapshotsDir(), "by-workspace", workspace, arch, kind);
30557
+ }
30558
+ function cleanupLegacyByWorldDir(worldId) {
30559
+ const legacyDir = path19.join(snapshotsDir(), worldId);
30560
+ if (worldId === "by-workspace")
30561
+ return;
30562
+ if (!fs16.existsSync(legacyDir))
30563
+ return;
30564
+ try {
30565
+ fs16.rmSync(legacyDir, { recursive: true, force: true });
30566
+ } catch {
30567
+ }
30568
+ }
30569
+ function manifestPath(tarPath) {
30570
+ return tarPath.replace(/\.tar\.gz$/, ".manifest.json");
30571
+ }
30572
+ function hashBuffers(entries) {
30573
+ const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path));
30574
+ const hash = crypto4.createHash("sha256");
30575
+ for (const entry of sorted) {
30576
+ hash.update(entry.path);
30577
+ hash.update("\0");
30578
+ hash.update(entry.content);
30579
+ hash.update("\0");
30580
+ }
30581
+ return hash.digest("hex").slice(0, 12);
30582
+ }
30583
+ function computeGemsFingerprint(repoDir, imageDigest) {
30584
+ const lockfile = path19.join(repoDir, "Gemfile.lock");
30585
+ if (!fs16.existsSync(lockfile))
30586
+ return null;
30587
+ const entries = [
30588
+ { path: "Gemfile.lock", content: fs16.readFileSync(lockfile) }
30589
+ ];
30590
+ if (imageDigest) {
30591
+ entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
30592
+ }
30593
+ return hashBuffers(entries);
30594
+ }
30595
+ function computeNodeFingerprint(repoDir, imageDigest) {
30596
+ const candidates = ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"];
30597
+ for (const name of candidates) {
30598
+ const lockfile = path19.join(repoDir, name);
30599
+ if (fs16.existsSync(lockfile)) {
30600
+ const entries = [
30601
+ { path: name, content: fs16.readFileSync(lockfile) }
30602
+ ];
30603
+ if (imageDigest) {
30604
+ entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
30605
+ }
30606
+ return hashBuffers(entries);
30607
+ }
30608
+ }
30609
+ return null;
30610
+ }
30611
+ function unpackTarballAtomic(srcPath, destDir) {
30612
+ const validation = enumerateAndValidateTarballEntries(srcPath, destDir);
30613
+ if (!validation.valid) {
30614
+ return {
30615
+ ok: false,
30616
+ reason: validation.reason,
30617
+ detail: validation.detail ?? `unsafe entry: ${validation.unsafePath}`
30618
+ };
30619
+ }
30620
+ const parent = path19.dirname(destDir);
30621
+ fs16.mkdirSync(parent, { recursive: true });
30622
+ const tmpSuffix = `.tmp-${process.pid}-${crypto4.randomBytes(4).toString("hex")}`;
30623
+ const tmpDir = `${destDir}${tmpSuffix}`;
30624
+ try {
30625
+ fs16.mkdirSync(tmpDir, { recursive: true });
30626
+ execFileSync4("tar", ["-xzf", srcPath, "-C", tmpDir], { stdio: "pipe" });
30627
+ fs16.renameSync(tmpDir, destDir);
30628
+ return { ok: true, entryCount: validation.entries.length };
30629
+ } catch (err) {
30630
+ try {
30631
+ fs16.rmSync(tmpDir, { recursive: true, force: true });
30632
+ } catch {
30633
+ }
30634
+ return {
30635
+ ok: false,
30636
+ reason: "extract-error",
30637
+ detail: err instanceof Error ? err.message : String(err)
30638
+ };
30639
+ }
30640
+ }
30641
+ function resolvesWithin(base, target) {
30642
+ const resolved = path19.resolve(base, target);
30643
+ const baseResolved = path19.resolve(base);
30644
+ const rel = path19.relative(baseResolved, resolved);
30645
+ if (rel === "")
30646
+ return true;
30647
+ return !rel.startsWith("..") && !path19.isAbsolute(rel);
30648
+ }
30649
+ var TYPE_CHAR_TO_TYPE = {
30650
+ "-": "file",
30651
+ "d": "dir",
30652
+ "l": "symlink",
30653
+ "h": "hardlink"
30654
+ };
30655
+ var DATE_MARKER_RE = /(?:\d{1,2}:\d{2}|(?:19|20)\d{2})\s+(.+)$/;
30656
+ function parseTarListLine(line) {
30657
+ const trimmed = line.trimEnd();
30658
+ if (trimmed.length === 0)
30659
+ return null;
30660
+ const typeChar = trimmed[0];
30661
+ if (typeChar === void 0)
30662
+ return null;
30663
+ const type = TYPE_CHAR_TO_TYPE[typeChar];
30664
+ if (!type)
30665
+ return null;
30666
+ const match = trimmed.match(DATE_MARKER_RE);
30667
+ if (!match)
30668
+ return null;
30669
+ const remainderRaw = match[1];
30670
+ if (remainderRaw === void 0)
30671
+ return null;
30672
+ let remainder = remainderRaw;
30673
+ if (remainder.startsWith("./"))
30674
+ remainder = remainder.slice(2);
30675
+ const symlinkSplit = remainder.indexOf(" -> ");
30676
+ if (type === "symlink" && symlinkSplit !== -1) {
30677
+ return {
30678
+ type: "symlink",
30679
+ name: remainder.slice(0, symlinkSplit),
30680
+ linkname: remainder.slice(symlinkSplit + 4)
30681
+ };
30682
+ }
30683
+ const hardlinkMarker = " link to ";
30684
+ const hardlinkSplit = remainder.indexOf(hardlinkMarker);
30685
+ if (type === "hardlink" && hardlinkSplit !== -1) {
30686
+ return {
30687
+ type: "hardlink",
30688
+ name: remainder.slice(0, hardlinkSplit),
30689
+ linkname: remainder.slice(hardlinkSplit + hardlinkMarker.length)
30690
+ };
30691
+ }
30692
+ return { type, name: remainder };
30693
+ }
30694
+ function validateHardlinksBinary(tarPath, targetDir) {
30695
+ let raw;
30696
+ try {
30697
+ raw = gunzipSync(fs16.readFileSync(tarPath));
30698
+ } catch {
30699
+ return null;
30700
+ }
30701
+ let offset = 0;
30702
+ while (offset + 512 <= raw.length) {
30703
+ const block = raw.subarray(offset, offset + 512);
30704
+ if (!block.some((b) => b !== 0))
30705
+ break;
30706
+ const typeflag = block[156] !== void 0 ? String.fromCharCode(block[156]) : "";
30707
+ if (typeflag === "1") {
30708
+ const nameNull = block.indexOf(0, 0);
30709
+ const name = block.subarray(0, nameNull >= 0 && nameNull <= 99 ? nameNull : 100).toString("utf-8");
30710
+ const linkNull = block.indexOf(0, 157);
30711
+ const linkname = block.subarray(157, linkNull >= 157 && linkNull <= 256 ? linkNull : 257).toString("utf-8");
30712
+ if (linkname && (path19.isAbsolute(linkname) || !resolvesWithin(targetDir, linkname))) {
30713
+ return {
30714
+ valid: false,
30715
+ reason: "hardlink-escape",
30716
+ unsafePath: name || tarPath,
30717
+ detail: `linkname=${linkname} resolves outside targetDir`
30718
+ };
30719
+ }
30720
+ }
30721
+ const sizeRaw = block.subarray(124, 136).toString("utf-8").replace(/[\0 ]/g, "");
30722
+ const size = sizeRaw ? parseInt(sizeRaw, 8) : 0;
30723
+ offset += (1 + Math.ceil((isNaN(size) ? 0 : size) / 512)) * 512;
30724
+ }
30725
+ return null;
30726
+ }
30727
+ function enumerateAndValidateTarballEntries(tarPath, targetDir) {
30728
+ let raw;
30729
+ try {
30730
+ raw = execFileSync4("tar", ["-tvf", tarPath], {
30731
+ stdio: ["ignore", "pipe", "pipe"],
30732
+ env: { ...process.env, LC_ALL: "C", TZ: "UTC" },
30733
+ encoding: "utf-8",
30734
+ maxBuffer: 64 * 1024 * 1024
30735
+ // 64 MiB ceiling for very large tarballs
30736
+ });
30737
+ } catch (err) {
30738
+ return {
30739
+ valid: false,
30740
+ reason: "parse-error",
30741
+ unsafePath: tarPath,
30742
+ detail: err instanceof Error ? err.message : String(err)
30743
+ };
30744
+ }
30745
+ const entries = [];
30746
+ for (const line of raw.split("\n")) {
30747
+ const entry = parseTarListLine(line);
30748
+ if (!entry)
30749
+ continue;
30750
+ if (path19.isAbsolute(entry.name) || !resolvesWithin(targetDir, entry.name)) {
30751
+ return {
30752
+ valid: false,
30753
+ reason: "path-traversal",
30754
+ unsafePath: entry.name
30755
+ };
30756
+ }
30757
+ if (entry.type === "symlink" && entry.linkname !== void 0) {
30758
+ const symlinkParent = path19.join(targetDir, path19.dirname(entry.name));
30759
+ if (path19.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, path19.join(path19.dirname(entry.name), entry.linkname))) {
30760
+ return {
30761
+ valid: false,
30762
+ reason: "symlink-escape",
30763
+ unsafePath: entry.name,
30764
+ detail: `linkname=${entry.linkname} resolves outside ${symlinkParent}`
30765
+ };
30766
+ }
30767
+ }
30768
+ if (entry.type === "hardlink" && entry.linkname !== void 0) {
30769
+ if (path19.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, entry.linkname)) {
30770
+ return {
30771
+ valid: false,
30772
+ reason: "hardlink-escape",
30773
+ unsafePath: entry.name,
30774
+ detail: `linkname=${entry.linkname} resolves outside targetDir`
30775
+ };
30776
+ }
30777
+ }
30778
+ entries.push(entry);
30779
+ }
30780
+ const hardlinkResult = validateHardlinksBinary(tarPath, targetDir);
30781
+ if (hardlinkResult !== null)
30782
+ return hardlinkResult;
30783
+ return { valid: true, entries };
30784
+ }
30785
+ var KINDS_BY_REPO = [
30786
+ { kind: "gems", targetSubpath: "vendor/bundle", computeFp: computeGemsFingerprint },
30787
+ { kind: "node", targetSubpath: "node_modules", computeFp: computeNodeFingerprint }
30788
+ ];
30789
+ function restoreSnapshotsForRepos(input) {
30790
+ const restored = /* @__PURE__ */ new Map();
30791
+ const outcomes = [];
30792
+ for (const repo of input.repos) {
30793
+ const repoRestored = [];
30794
+ for (const { kind, targetSubpath, computeFp } of KINDS_BY_REPO) {
30795
+ const fingerprint = computeFp(repo.worktreeDir, input.imageDigest);
30796
+ if (!fingerprint) {
30797
+ outcomes.push({ repo: repo.name, kind, outcome: "miss", reason: "no-lockfile" });
30798
+ continue;
30799
+ }
30800
+ const archDir = snapshotKindDirByWorkspace(input.workspace, input.arch, kind);
30801
+ const tarFilename = `${repo.name}-${input.arch}-${fingerprint}.tar.gz`;
30802
+ const tarPath = path19.join(archDir, tarFilename);
30803
+ if (!fs16.existsSync(tarPath)) {
30804
+ outcomes.push({ repo: repo.name, kind, outcome: "miss", reason: "no-tarball", fingerprint });
30805
+ continue;
30806
+ }
30807
+ const manifest = readManifest(tarPath);
30808
+ if (!manifest || manifest.arch !== input.arch) {
30809
+ outcomes.push({
30810
+ repo: repo.name,
30811
+ kind,
30812
+ outcome: "refused",
30813
+ reason: "arch-mismatch",
30814
+ fingerprint
30815
+ });
30816
+ continue;
30817
+ }
30818
+ const targetDir = path19.join(repo.worktreeDir, targetSubpath);
30819
+ try {
30820
+ fs16.rmSync(targetDir, { recursive: true, force: true });
30821
+ } catch {
30822
+ }
30823
+ const result = unpackTarballAtomic(tarPath, targetDir);
30824
+ if (!result.ok) {
30825
+ outcomes.push({
30826
+ repo: repo.name,
30827
+ kind,
30828
+ outcome: "refused",
30829
+ reason: result.reason,
30830
+ fingerprint
30831
+ });
30832
+ try {
30833
+ fs16.rmSync(tarPath, { force: true });
30834
+ fs16.rmSync(manifestPath(tarPath), { force: true });
30835
+ } catch {
30836
+ }
30837
+ continue;
30838
+ }
30839
+ outcomes.push({ repo: repo.name, kind, outcome: "hit", fingerprint });
30840
+ repoRestored.push(kind);
30841
+ }
30842
+ if (repoRestored.length > 0) {
30843
+ restored.set(repo.name, repoRestored);
30844
+ }
30845
+ }
30846
+ return { restored, outcomes };
30847
+ }
30848
+ function readManifest(tarPath) {
30849
+ const mPath = manifestPath(tarPath);
30850
+ if (!fs16.existsSync(mPath))
30851
+ return null;
30852
+ try {
30853
+ return JSON.parse(fs16.readFileSync(mPath, "utf-8"));
30854
+ } catch {
30855
+ return null;
30856
+ }
30857
+ }
30858
+ var EVICT_LOCK_FILENAME = ".evict.lock";
30859
+ function isPidAlive(pid) {
30860
+ try {
30861
+ process.kill(pid, 0);
30862
+ return true;
30863
+ } catch {
30864
+ return false;
30865
+ }
30866
+ }
30867
+ function evictOldSnapshotsWithFlock(maxBytes, dir = snapshotsDir()) {
30868
+ fs16.mkdirSync(dir, { recursive: true });
30869
+ const lockPath = path19.join(dir, EVICT_LOCK_FILENAME);
30870
+ let fd;
30871
+ try {
30872
+ fd = fs16.openSync(lockPath, fs16.constants.O_WRONLY | fs16.constants.O_CREAT | fs16.constants.O_EXCL, 384);
30873
+ } catch (err) {
30874
+ if (err.code !== "EEXIST")
30875
+ return 0;
30876
+ let holderPid = null;
30877
+ try {
30878
+ holderPid = parseInt(fs16.readFileSync(lockPath, "utf-8").trim(), 10);
30879
+ } catch {
30880
+ holderPid = null;
30881
+ }
30882
+ if (holderPid && Number.isInteger(holderPid) && isPidAlive(holderPid)) {
30883
+ return 0;
30884
+ }
30885
+ try {
30886
+ fs16.unlinkSync(lockPath);
30887
+ fd = fs16.openSync(lockPath, fs16.constants.O_WRONLY | fs16.constants.O_CREAT | fs16.constants.O_EXCL, 384);
30888
+ } catch {
30889
+ return 0;
30890
+ }
30891
+ }
30892
+ try {
30893
+ fs16.writeSync(fd, `${process.pid}
30894
+ `);
30895
+ } finally {
30896
+ fs16.closeSync(fd);
30897
+ }
30898
+ try {
30899
+ return evictOldSnapshots(maxBytes, dir);
30900
+ } finally {
30901
+ try {
30902
+ fs16.unlinkSync(lockPath);
30903
+ } catch {
30904
+ }
30905
+ }
30906
+ }
30907
+ function spawnAutoCapture(worldId, olamBin = "olam") {
30908
+ if (process.env["OLAM_SNAPSHOT_AUTO_CAPTURE"] === "0")
30909
+ return null;
30910
+ if (!/^[a-zA-Z0-9_\-.]+$/.test(worldId))
30911
+ return null;
30912
+ try {
30913
+ const child = spawn(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
30914
+ detached: true,
30915
+ stdio: "ignore",
30916
+ // OLAM_INTERNAL_SNAPSHOT=1 sentinel — Phase D D1's deprecation
30917
+ // counter (in packages/cli/src/commands/world-snapshot.ts) skips
30918
+ // the bump when this env var is set. Preserves D22 signal
30919
+ // integrity: the counter measures operator-driven invocations
30920
+ // only, not auto-capture-triggered ones. See CP3 review HIGH
30921
+ // finding for context.
30922
+ env: {
30923
+ ...process.env,
30924
+ OLAM_INTERNAL_SNAPSHOT: "1",
30925
+ NODE_OPTIONS: process.env["NODE_OPTIONS"] ?? ""
30926
+ }
30927
+ });
30928
+ child.unref();
30929
+ return child.pid ?? null;
30930
+ } catch {
30931
+ return null;
30932
+ }
30933
+ }
30934
+ function evictOldSnapshots(maxBytes, dir = snapshotsDir()) {
30935
+ if (!fs16.existsSync(dir))
30936
+ return 0;
30937
+ const allTars = [];
30938
+ const walk = (d) => {
30939
+ for (const entry of fs16.readdirSync(d, { withFileTypes: true })) {
30940
+ const full = path19.join(d, entry.name);
30941
+ if (entry.isDirectory()) {
30942
+ walk(full);
30943
+ } else if (entry.name.endsWith(".tar.gz")) {
30944
+ const stat = fs16.statSync(full);
30945
+ allTars.push({ path: full, size: stat.size, mtime: stat.mtimeMs });
30946
+ }
30947
+ }
30948
+ };
30949
+ walk(dir);
30950
+ const total = allTars.reduce((acc, t) => acc + t.size, 0);
30951
+ if (total <= maxBytes)
30952
+ return 0;
30953
+ allTars.sort((a, b) => a.mtime - b.mtime);
30954
+ let freed = 0;
30955
+ let remaining = total;
30956
+ for (const tar of allTars) {
30957
+ if (remaining <= maxBytes)
30958
+ break;
30959
+ fs16.rmSync(tar.path, { force: true });
30960
+ fs16.rmSync(manifestPath(tar.path), { force: true });
30961
+ freed += tar.size;
30962
+ remaining -= tar.size;
30963
+ }
30964
+ return freed;
30965
+ }
30966
+
30063
30967
  // ../core/dist/world/secrets-fetcher.js
30064
- import { execSync as execSync3 } from "node:child_process";
30968
+ import { execSync as execSync4 } from "node:child_process";
30065
30969
  function parseGcpSecretUrl(url) {
30066
30970
  if (!url.startsWith("gcp://"))
30067
30971
  return null;
@@ -30076,7 +30980,7 @@ function parseGcpSecretUrl(url) {
30076
30980
  return { project, secretName };
30077
30981
  }
30078
30982
  function defaultExecFn(cmd, opts) {
30079
- return execSync3(cmd, { encoding: "utf-8", timeout: opts?.timeout ?? 15e3 });
30983
+ return execSync4(cmd, { encoding: "utf-8", timeout: opts?.timeout ?? 15e3 });
30080
30984
  }
30081
30985
  function fetchGcpSecret(ref, execFn = defaultExecFn) {
30082
30986
  const { project, secretName } = ref;
@@ -30169,14 +31073,14 @@ function gcloudAvailable(execFn = defaultExecFn) {
30169
31073
  }
30170
31074
 
30171
31075
  // ../core/dist/world/olam-yaml.js
30172
- import * as path18 from "node:path";
31076
+ import * as path20 from "node:path";
30173
31077
  import YAML2 from "yaml";
30174
31078
  function enrichReposWithManifests(repos, workspacePath) {
30175
31079
  return repos.map((repo) => {
30176
31080
  if (repo.manifest !== void 0 && repo.manifest !== null) {
30177
31081
  return repo;
30178
31082
  }
30179
- const repoDir = path18.join(workspacePath, repo.name);
31083
+ const repoDir = path20.join(workspacePath, repo.name);
30180
31084
  let manifest = null;
30181
31085
  try {
30182
31086
  manifest = loadRepoManifest(repoDir);
@@ -30191,8 +31095,8 @@ function enrichReposWithManifests(repos, workspacePath) {
30191
31095
  }
30192
31096
 
30193
31097
  // ../core/dist/policies/loader.js
30194
- import * as fs15 from "node:fs";
30195
- import * as path19 from "node:path";
31098
+ import * as fs17 from "node:fs";
31099
+ import * as path21 from "node:path";
30196
31100
  import { parse as parseYaml3 } from "yaml";
30197
31101
  function parseFrontmatter(content) {
30198
31102
  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
@@ -30212,20 +31116,20 @@ function toStringArray(v) {
30212
31116
  return v.filter((x) => typeof x === "string");
30213
31117
  }
30214
31118
  function loadPolicies(workspaceRoot) {
30215
- const policiesDir = path19.join(workspaceRoot, ".olam", "policies");
30216
- if (!fs15.existsSync(policiesDir))
31119
+ const policiesDir = path21.join(workspaceRoot, ".olam", "policies");
31120
+ if (!fs17.existsSync(policiesDir))
30217
31121
  return [];
30218
31122
  let files;
30219
31123
  try {
30220
- files = fs15.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
31124
+ files = fs17.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
30221
31125
  } catch {
30222
31126
  return [];
30223
31127
  }
30224
31128
  const policies = [];
30225
31129
  for (const file of files) {
30226
- const filePath = path19.join(policiesDir, file);
31130
+ const filePath = path21.join(policiesDir, file);
30227
31131
  try {
30228
- const content = fs15.readFileSync(filePath, "utf8");
31132
+ const content = fs17.readFileSync(filePath, "utf8");
30229
31133
  const parsed = parseFrontmatter(content);
30230
31134
  if (!parsed) {
30231
31135
  console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
@@ -30275,17 +31179,93 @@ function formatPoliciesBrief(policies) {
30275
31179
  return lines.join("\n");
30276
31180
  }
30277
31181
 
31182
+ // ../core/dist/global-config/runbook-resolver.js
31183
+ import * as fs18 from "node:fs";
31184
+ import * as os12 from "node:os";
31185
+ import * as path22 from "node:path";
31186
+ function expandTilde(p) {
31187
+ if (p === "~" || p.startsWith("~/")) {
31188
+ return path22.join(os12.homedir(), p.slice(1));
31189
+ }
31190
+ return p;
31191
+ }
31192
+ function resolveRunbookToWorldParams(runbook, repoRegistry) {
31193
+ const registryMap = new Map(repoRegistry.map((r) => [r.name, r]));
31194
+ for (const repoName of runbook.repos) {
31195
+ const entry = registryMap.get(repoName);
31196
+ if (!entry) {
31197
+ throw new Error(`repo "${repoName}" is referenced by runbook "${runbook.name}" but is not in the registry. Run "olam repos add ${repoName} --path <path>" to register it.`);
31198
+ }
31199
+ const resolvedPath = expandTilde(entry.path);
31200
+ if (!fs18.existsSync(resolvedPath)) {
31201
+ throw new Error(`repo "${repoName}" path "${resolvedPath}" no longer exists. Run "olam repos update ${repoName} --path <new-path>" to fix.`);
31202
+ }
31203
+ }
31204
+ return {
31205
+ repoNames: [...runbook.repos],
31206
+ portOverrides: runbook.portMap ?? {},
31207
+ envOverrides: runbook.env ?? {},
31208
+ seeds: runbook.seeds
31209
+ };
31210
+ }
31211
+
31212
+ // ../core/dist/world/bootstrap-hooks.js
31213
+ import * as fs19 from "node:fs";
31214
+ import * as path23 from "node:path";
31215
+ function runFixtureCopySeeds(seeds, workspacePath) {
31216
+ if (!seeds)
31217
+ return;
31218
+ for (const seed of seeds) {
31219
+ if (seed.type !== "fixture-copy")
31220
+ continue;
31221
+ const srcAbs = path23.resolve(workspacePath, seed.repo, seed.src);
31222
+ const destAbs = path23.resolve(workspacePath, seed.repo, seed.dest);
31223
+ const destDir = path23.dirname(destAbs);
31224
+ fs19.mkdirSync(destDir, { recursive: true });
31225
+ fs19.cpSync(srcAbs, destAbs, { recursive: true, force: true });
31226
+ }
31227
+ }
31228
+ async function runSeedHooks(seeds, containerName, servicePortMap, exec) {
31229
+ if (!seeds)
31230
+ return;
31231
+ for (const seed of seeds) {
31232
+ if (seed.type === "fixture-copy")
31233
+ continue;
31234
+ if (seed.type === "sql-file") {
31235
+ const servicePorts = servicePortMap[seed.repo] ?? {};
31236
+ const port = servicePorts[seed.service] ?? 5432;
31237
+ const waitCmd = `until pg_isready -h localhost -p ${port} -t 1 2>/dev/null; do sleep 2; done`;
31238
+ const psqlCmd = `psql -h localhost -p ${port} -f /home/olam/workspace/${seed.repo}/${seed.path}`;
31239
+ const combined = `${waitCmd} && ${psqlCmd}`;
31240
+ try {
31241
+ exec(containerName, combined);
31242
+ } catch (err) {
31243
+ const msg = err instanceof Error ? err.message : String(err);
31244
+ throw new Error(`seed failed for repo "${seed.repo}" (type: sql-file, service: "${seed.service}", path: "${seed.path}"): ${msg}`);
31245
+ }
31246
+ } else if (seed.type === "command") {
31247
+ const workdir = `/home/olam/workspace/${seed.repo}`;
31248
+ try {
31249
+ exec(containerName, `cd ${workdir} && ${seed.run}`);
31250
+ } catch (err) {
31251
+ const msg = err instanceof Error ? err.message : String(err);
31252
+ throw new Error(`seed failed for repo "${seed.repo}" (type: command, run: "${seed.run}"): ${msg}`);
31253
+ }
31254
+ }
31255
+ }
31256
+ }
31257
+
30278
31258
  // ../core/dist/world/tmux-supervisor.js
30279
31259
  var PortInUseError = class extends Error {
30280
31260
  port;
30281
31261
  repo;
30282
31262
  manifestPath;
30283
- constructor(port, repo, manifestPath) {
30284
- super(`Port ${port} already in use on host (claimed by repo "${repo}"). Free the port or change \`app_port\` in ${manifestPath}.`);
31263
+ constructor(port, repo, manifestPath2) {
31264
+ super(`Port ${port} already in use on host (claimed by repo "${repo}"). Free the port or change \`app_port\` in ${manifestPath2}.`);
30285
31265
  this.name = "PortInUseError";
30286
31266
  this.port = port;
30287
31267
  this.repo = repo;
30288
- this.manifestPath = manifestPath;
31268
+ this.manifestPath = manifestPath2;
30289
31269
  }
30290
31270
  };
30291
31271
  var SAFE_IDENT2 = /^[a-z0-9][a-z0-9-]{0,63}$/;
@@ -30380,7 +31360,7 @@ var BotIdentityError = class extends Error {
30380
31360
  this.name = "BotIdentityError";
30381
31361
  }
30382
31362
  };
30383
- function getTokenScopes(ghToken, _exec = execSync4) {
31363
+ function getTokenScopes(ghToken, _exec = execSync5) {
30384
31364
  try {
30385
31365
  const out = _exec("gh auth status 2>&1", {
30386
31366
  encoding: "utf-8",
@@ -30397,13 +31377,13 @@ function getTokenScopes(ghToken, _exec = execSync4) {
30397
31377
  }
30398
31378
  }
30399
31379
  async function setupContainerGit(containerName, repos, branch) {
30400
- const dockerExec = (cmd) => execSync4(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, {
31380
+ const dockerExec = (cmd) => execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, {
30401
31381
  stdio: "pipe",
30402
31382
  timeout: 6e4
30403
31383
  }).toString();
30404
31384
  let ghToken = "";
30405
31385
  try {
30406
- ghToken = execSync4("gh auth token 2>/dev/null", { encoding: "utf-8", timeout: 5e3 }).trim();
31386
+ ghToken = execSync5("gh auth token 2>/dev/null", { encoding: "utf-8", timeout: 5e3 }).trim();
30407
31387
  } catch {
30408
31388
  }
30409
31389
  const actorName = process.env.OLAM_BOT_NAME ?? "Claude Code (olam)";
@@ -30423,7 +31403,7 @@ async function setupContainerGit(containerName, repos, branch) {
30423
31403
  continue;
30424
31404
  const ownerRepo = ghMatch[1];
30425
31405
  try {
30426
- execSync4(`gh api repos/${ownerRepo} --silent`, {
31406
+ execSync5(`gh api repos/${ownerRepo} --silent`, {
30427
31407
  stdio: "pipe",
30428
31408
  timeout: 5e3,
30429
31409
  env: { ...process.env, GH_TOKEN: ghToken }
@@ -30471,8 +31451,8 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
30471
31451
  if (!olamUserPresent) {
30472
31452
  const imageName = (() => {
30473
31453
  try {
30474
- const { execSync: execSync6 } = __require("node:child_process");
30475
- return execSync6(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
31454
+ const { execSync: execSync7 } = __require("node:child_process");
31455
+ return execSync7(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
30476
31456
  encoding: "utf8",
30477
31457
  timeout: 5e3
30478
31458
  }).trim() || "(unknown)";
@@ -30515,7 +31495,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
30515
31495
  function makeHostExecFn() {
30516
31496
  return async (cmd) => {
30517
31497
  try {
30518
- const stdout = execSync4(cmd, { encoding: "utf-8", timeout: 5e3 });
31498
+ const stdout = execSync5(cmd, { encoding: "utf-8", timeout: 5e3 });
30519
31499
  return { stdout, stderr: "", exitCode: 0 };
30520
31500
  } catch {
30521
31501
  return { stdout: "", stderr: "", exitCode: 1 };
@@ -30525,7 +31505,7 @@ function makeHostExecFn() {
30525
31505
  function makeContainerExecFn(containerName) {
30526
31506
  return async (cmd) => {
30527
31507
  try {
30528
- const result = execSync4(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
31508
+ const result = execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
30529
31509
  return { stdout: result.toString(), stderr: "", exitCode: 0 };
30530
31510
  } catch (err) {
30531
31511
  const execErr = err;
@@ -30539,7 +31519,7 @@ function makeContainerExecFn(containerName) {
30539
31519
  }
30540
31520
  function defaultDockerExec() {
30541
31521
  return (containerName, cmd) => {
30542
- const result = execSync4(
31522
+ const result = execSync5(
30543
31523
  `docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`,
30544
31524
  // Phase E E5 raise: 10min was too tight on cold-boot for atlas-core's
30545
31525
  // `rails db:create` chain (Rails 7 boot + initializer load + first
@@ -30763,8 +31743,28 @@ var WorldManager = class {
30763
31743
  throw new AuthPreflightError(preflight.verdict, preflight.message, preflight.remedy);
30764
31744
  }
30765
31745
  }
31746
+ let runbookPortOverrides = {};
31747
+ let runbookEnvOverrides = {};
31748
+ let runbookSeeds;
31749
+ if (opts.runbookName) {
31750
+ const runbook = getRunbook(opts.runbookName);
31751
+ const globalConfig2 = readGlobalConfig();
31752
+ const resolved = resolveRunbookToWorldParams(runbook, globalConfig2.repos);
31753
+ const { conflicts } = validateRunbookPorts(runbook);
31754
+ if (conflicts.length > 0) {
31755
+ const detail = conflicts.map((c) => {
31756
+ const occ = c.occupant.type === "olam-world" ? `in use by world "${c.occupant.worldId}" (olam)` : `in use by PID ${c.occupant.pid ?? "unknown"} (non-olam)`;
31757
+ return ` ${c.repoName}.${c.serviceName}:${c.port} \u2014 ${occ}`;
31758
+ }).join("\n");
31759
+ throw new Error(`Port conflicts detected for runbook "${opts.runbookName}":
31760
+ ${detail}`);
31761
+ }
31762
+ runbookPortOverrides = resolved.portOverrides;
31763
+ runbookEnvOverrides = resolved.envOverrides;
31764
+ runbookSeeds = resolved.seeds;
31765
+ }
30766
31766
  const worldId = generateWorldId();
30767
- const workspacePath = path20.join(os10.homedir(), ".olam", "worlds", worldId);
31767
+ const workspacePath = path24.join(os13.homedir(), ".olam", "worlds", worldId);
30768
31768
  const portOffset = this.registry.getNextPortOffset();
30769
31769
  const branch = opts.branchName ?? `olam/${worldId}`;
30770
31770
  const repos = this.resolveReposWithWorkspace(opts);
@@ -30796,6 +31796,19 @@ var WorldManager = class {
30796
31796
  this.registry.update(worldId, { status: "error" });
30797
31797
  throw err;
30798
31798
  }
31799
+ if (opts.runbookName && runbookSeeds) {
31800
+ try {
31801
+ runFixtureCopySeeds(runbookSeeds, workspacePath);
31802
+ } catch (err) {
31803
+ sm.transition("error");
31804
+ this.registry.update(worldId, { status: "error" });
31805
+ try {
31806
+ await removeWorktrees(repos, workspacePath);
31807
+ } catch {
31808
+ }
31809
+ throw err;
31810
+ }
31811
+ }
30799
31812
  try {
30800
31813
  const baseline = snapshotBaselineDiff(repos, workspacePath);
30801
31814
  console.log(formatBaselineSummary(baseline));
@@ -30820,38 +31833,38 @@ var WorldManager = class {
30820
31833
  for (const repo of repos) {
30821
31834
  if (!repo.path)
30822
31835
  continue;
30823
- const sourceRoot = repo.path.replace(/^~/, os10.homedir());
30824
- const worktreeRoot = path20.join(workspacePath, repo.name);
30825
- if (!fs16.existsSync(sourceRoot) || !fs16.existsSync(worktreeRoot))
31836
+ const sourceRoot = repo.path.replace(/^~/, os13.homedir());
31837
+ const worktreeRoot = path24.join(workspacePath, repo.name);
31838
+ if (!fs20.existsSync(sourceRoot) || !fs20.existsSync(worktreeRoot))
30826
31839
  continue;
30827
31840
  let copied = 0;
30828
31841
  for (const pattern of RUNTIME_FILE_PATTERNS) {
30829
31842
  const matches2 = [];
30830
31843
  if (pattern.includes("*")) {
30831
- const [dir, glob] = [path20.dirname(pattern), path20.basename(pattern)];
30832
- const sourceDir = path20.join(sourceRoot, dir);
30833
- if (fs16.existsSync(sourceDir)) {
31844
+ const [dir, glob] = [path24.dirname(pattern), path24.basename(pattern)];
31845
+ const sourceDir = path24.join(sourceRoot, dir);
31846
+ if (fs20.existsSync(sourceDir)) {
30834
31847
  const ext = glob.replace(/^\*+/, "");
30835
31848
  try {
30836
- for (const entry of fs16.readdirSync(sourceDir)) {
31849
+ for (const entry of fs20.readdirSync(sourceDir)) {
30837
31850
  if (ext === "" || entry.endsWith(ext))
30838
- matches2.push(path20.join(dir, entry));
31851
+ matches2.push(path24.join(dir, entry));
30839
31852
  }
30840
31853
  } catch {
30841
31854
  }
30842
31855
  }
30843
- } else if (fs16.existsSync(path20.join(sourceRoot, pattern))) {
31856
+ } else if (fs20.existsSync(path24.join(sourceRoot, pattern))) {
30844
31857
  matches2.push(pattern);
30845
31858
  }
30846
31859
  for (const rel of matches2) {
30847
- const src = path20.join(sourceRoot, rel);
30848
- const dst = path20.join(worktreeRoot, rel);
31860
+ const src = path24.join(sourceRoot, rel);
31861
+ const dst = path24.join(worktreeRoot, rel);
30849
31862
  try {
30850
- const st = fs16.statSync(src);
31863
+ const st = fs20.statSync(src);
30851
31864
  if (!st.isFile())
30852
31865
  continue;
30853
- fs16.mkdirSync(path20.dirname(dst), { recursive: true });
30854
- fs16.copyFileSync(src, dst);
31866
+ fs20.mkdirSync(path24.dirname(dst), { recursive: true });
31867
+ fs20.copyFileSync(src, dst);
30855
31868
  copied++;
30856
31869
  } catch {
30857
31870
  }
@@ -30951,7 +31964,7 @@ var WorldManager = class {
30951
31964
  try {
30952
31965
  const hostExec = makeHostExecFn();
30953
31966
  for (const repo of repos) {
30954
- const repoDir = path20.join(workspacePath, repo.name);
31967
+ const repoDir = path24.join(workspacePath, repo.name);
30955
31968
  if (repo.stack && Object.keys(repo.stack).length > 0) {
30956
31969
  preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
30957
31970
  } else {
@@ -30977,11 +31990,19 @@ var WorldManager = class {
30977
31990
  }
30978
31991
  let cacheArchOverride;
30979
31992
  if (!stackCacheHit) {
30980
- const selected = selectDevboxImage(this.config, repos);
31993
+ const selected = selectDevboxImageForWorld({
31994
+ config: this.config,
31995
+ repos,
31996
+ worldspec: opts.worldspec
31997
+ });
30981
31998
  if (selected) {
30982
31999
  selectedImage = selected.image;
30983
32000
  cacheArchOverride = selected.cacheArch;
30984
- console.log(`[WorldManager] image_selector matched \u2014 using ${selected.image} (tag=${selected.tag}${selected.cacheArch ? `, cache_arch=${selected.cacheArch}` : ""})`);
32001
+ if (selected.source === "worldspec") {
32002
+ console.log(`[WorldManager] worldspec override \u2014 using ${selected.image} (tag=${selected.tag})`);
32003
+ } else {
32004
+ console.log(`[WorldManager] image_selector matched \u2014 using ${selected.image} (tag=${selected.tag}${selected.cacheArch ? `, cache_arch=${selected.cacheArch}` : ""})`);
32005
+ }
30985
32006
  } else {
30986
32007
  const hasRailsRepo = repos.some((r) => r.type === "rails");
30987
32008
  if (hasRailsRepo) {
@@ -31001,17 +32022,18 @@ var WorldManager = class {
31001
32022
  }
31002
32023
  }
31003
32024
  const appPortUrls = appPorts.map((ap) => {
31004
- const hostPort = ap.port + 1e4 + portOffset;
32025
+ const override = runbookPortOverrides[ap.name]?.["app"];
32026
+ const hostPort = override !== void 0 ? override : ap.port + 1e4 + portOffset;
31005
32027
  return { repoName: ap.name, internalPort: ap.port, hostPort, url: `http://localhost:${hostPort}` };
31006
32028
  });
31007
32029
  try {
31008
32030
  const worldEnv = {};
31009
32031
  if (opts.task)
31010
32032
  worldEnv.OLAM_TASK = opts.task;
31011
- const r2CredsPath = path20.join(os10.homedir(), ".olam", "r2-credentials.json");
31012
- if (fs16.existsSync(r2CredsPath)) {
32033
+ const r2CredsPath = path24.join(os13.homedir(), ".olam", "r2-credentials.json");
32034
+ if (fs20.existsSync(r2CredsPath)) {
31013
32035
  try {
31014
- const r2Raw = fs16.readFileSync(r2CredsPath, "utf-8").trim();
32036
+ const r2Raw = fs20.readFileSync(r2CredsPath, "utf-8").trim();
31015
32037
  if (r2Raw.length > 0) {
31016
32038
  const r2 = JSON.parse(r2Raw);
31017
32039
  if (typeof r2.account_id === "string")
@@ -31028,10 +32050,10 @@ var WorldManager = class {
31028
32050
  } catch {
31029
32051
  }
31030
32052
  }
31031
- const keysYamlPath = path20.join(os10.homedir(), ".olam", "keys.yaml");
31032
- if (fs16.existsSync(keysYamlPath)) {
32053
+ const keysYamlPath = path24.join(os13.homedir(), ".olam", "keys.yaml");
32054
+ if (fs20.existsSync(keysYamlPath)) {
31033
32055
  try {
31034
- const keysRaw = fs16.readFileSync(keysYamlPath, "utf-8").trim();
32056
+ const keysRaw = fs20.readFileSync(keysYamlPath, "utf-8").trim();
31035
32057
  if (keysRaw.length > 0) {
31036
32058
  const parsed = YAML3.parse(keysRaw);
31037
32059
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
@@ -31067,6 +32089,18 @@ var WorldManager = class {
31067
32089
  seenFromRepo.set(k, repo.name);
31068
32090
  }
31069
32091
  }
32092
+ if (opts.runbookName) {
32093
+ for (const [repoName, repoEnv] of Object.entries(runbookEnvOverrides)) {
32094
+ for (const [k, v] of Object.entries(repoEnv)) {
32095
+ if (PROTECTED_ENV_KEY_SET.has(k))
32096
+ continue;
32097
+ if (k.startsWith("OLAM_"))
32098
+ continue;
32099
+ worldEnv[k] = v;
32100
+ }
32101
+ void repoName;
32102
+ }
32103
+ }
31070
32104
  if (repoSecretUrls.length > 0) {
31071
32105
  const { env: gcpSecrets, fileWrites } = fetchSecretsForRepos(repoSecretUrls);
31072
32106
  for (const [k, v] of Object.entries(gcpSecrets)) {
@@ -31078,10 +32112,10 @@ var WorldManager = class {
31078
32112
  worldEnv[k] = v;
31079
32113
  }
31080
32114
  for (const { repoName, relativePath, content } of fileWrites) {
31081
- const absPath = path20.join(workspacePath, repoName, relativePath);
32115
+ const absPath = path24.join(workspacePath, repoName, relativePath);
31082
32116
  try {
31083
- fs16.mkdirSync(path20.dirname(absPath), { recursive: true });
31084
- fs16.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
32117
+ fs20.mkdirSync(path24.dirname(absPath), { recursive: true });
32118
+ fs20.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
31085
32119
  mode: 384
31086
32120
  });
31087
32121
  console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
@@ -31187,6 +32221,44 @@ var WorldManager = class {
31187
32221
  const msg = err instanceof Error ? err.message : String(err);
31188
32222
  console.warn(`[WorldManager] stack installation failed: ${msg}`);
31189
32223
  }
32224
+ try {
32225
+ cleanupLegacyByWorldDir(worldId);
32226
+ const restoreResult = restoreSnapshotsForRepos({
32227
+ workspace: opts.workspace ?? "default",
32228
+ arch: process.arch,
32229
+ imageDigest: void 0,
32230
+ repos: enrichedRepos.map((r) => ({
32231
+ name: r.name,
32232
+ worktreeDir: path24.join(workspacePath, r.name)
32233
+ }))
32234
+ });
32235
+ for (const out of restoreResult.outcomes) {
32236
+ if (out.outcome === "hit") {
32237
+ console.log(`[snapshot] Restored ${out.kind} for ${out.repo} (${out.fingerprint?.slice(0, 12)})`);
32238
+ } else if (out.outcome === "refused") {
32239
+ console.warn(`[snapshot] Skipped ${out.repo} ${out.kind}: ${out.reason ?? "unknown"}`);
32240
+ }
32241
+ }
32242
+ for (const [repoName, restoredKinds] of restoreResult.restored) {
32243
+ const repo = enrichedRepos.find((r) => r.name === repoName);
32244
+ if (!repo?.manifest?.bootstrap)
32245
+ continue;
32246
+ const containerWorkdir = `/home/olam/workspace/${repoName}`;
32247
+ repo.manifest.bootstrap.forEach((step, idx) => {
32248
+ const produces = typeof step === "object" && step !== null ? step.produces : void 0;
32249
+ if (produces && restoredKinds.includes(produces)) {
32250
+ const sentinel = bootstrapStepSentinelPath(containerWorkdir, idx);
32251
+ try {
32252
+ this.dockerExec(containerName, `touch ${sentinel}`);
32253
+ } catch {
32254
+ }
32255
+ }
32256
+ });
32257
+ }
32258
+ } catch (err) {
32259
+ const msg = err instanceof Error ? err.message : String(err);
32260
+ console.warn(`[snapshot] restore failed (cold path): ${msg}`);
32261
+ }
31190
32262
  try {
31191
32263
  await runManifestRuntime(containerName, worldId, enrichedRepos, this.dockerExec);
31192
32264
  } catch (err) {
@@ -31211,12 +32283,30 @@ var WorldManager = class {
31211
32283
  console.warn(`[manifest] step 4e/4f/4g non-fatal failure: ${msg}`);
31212
32284
  }
31213
32285
  }
32286
+ if (opts.runbookName && runbookSeeds) {
32287
+ const servicePortMap = {};
32288
+ for (const repo of enrichedRepos) {
32289
+ if (!repo.manifest?.services)
32290
+ continue;
32291
+ const svcPorts = {};
32292
+ for (const [svcName, svc] of Object.entries(repo.manifest.services)) {
32293
+ svcPorts[svcName] = svc.port ?? 5432;
32294
+ }
32295
+ servicePortMap[repo.name] = svcPorts;
32296
+ }
32297
+ try {
32298
+ await runSeedHooks(runbookSeeds, containerName, servicePortMap, this.dockerExec);
32299
+ } catch (err) {
32300
+ const msg = err instanceof Error ? err.message : String(err);
32301
+ console.warn(`[runbook] seed hook failed: ${msg}`);
32302
+ }
32303
+ }
31214
32304
  for (const repo of repos) {
31215
32305
  if (repo.setup_commands.length > 0) {
31216
32306
  const escapedDir = `/home/olam/workspace/${repo.name.replace(/["$`\\]/g, "\\$&")}`;
31217
32307
  for (const cmd of repo.setup_commands) {
31218
32308
  try {
31219
- execSync4(`docker exec ${containerName} sh -c 'cd "${escapedDir}" && ${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
32309
+ execSync5(`docker exec ${containerName} sh -c 'cd "${escapedDir}" && ${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
31220
32310
  } catch (err) {
31221
32311
  const msg = err instanceof Error ? err.message : String(err);
31222
32312
  console.warn(`[WorldManager] setup command failed for ${repo.name}: ${msg}`);
@@ -31226,14 +32316,14 @@ var WorldManager = class {
31226
32316
  }
31227
32317
  if (credentialsInjected.claude) {
31228
32318
  try {
31229
- execSync4(`docker exec ${containerName} curl -sf -X POST http://localhost:8080/session/start-agent 2>/dev/null || true`, { stdio: "pipe", timeout: 45e3 });
32319
+ execSync5(`docker exec ${containerName} curl -sf -X POST http://localhost:8080/session/start-agent 2>/dev/null || true`, { stdio: "pipe", timeout: 45e3 });
31230
32320
  } catch {
31231
32321
  }
31232
32322
  if (opts.task) {
31233
32323
  let taskWithPolicies = opts.task;
31234
32324
  try {
31235
32325
  const allPolicies = repos.flatMap((repo) => {
31236
- const repoWorktree = path20.join(workspacePath, repo.name);
32326
+ const repoWorktree = path24.join(workspacePath, repo.name);
31237
32327
  return loadPolicies(repoWorktree);
31238
32328
  });
31239
32329
  const seen = /* @__PURE__ */ new Set();
@@ -31247,11 +32337,11 @@ var WorldManager = class {
31247
32337
  const brief = formatPoliciesBrief(uniquePolicies);
31248
32338
  taskWithPolicies = `${brief}## Your task
31249
32339
  ${opts.task}`;
31250
- execSync4(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
32340
+ execSync5(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
31251
32341
  for (const repo of repos) {
31252
- const policiesDir = path20.join(workspacePath, repo.name, ".olam", "policies");
31253
- if (fs16.existsSync(policiesDir)) {
31254
- execSync4(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
32342
+ const policiesDir = path24.join(workspacePath, repo.name, ".olam", "policies");
32343
+ if (fs20.existsSync(policiesDir)) {
32344
+ execSync5(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
31255
32345
  }
31256
32346
  }
31257
32347
  }
@@ -31261,7 +32351,7 @@ ${opts.task}`;
31261
32351
  }
31262
32352
  try {
31263
32353
  const payload = JSON.stringify({ prompt: taskWithPolicies }).replace(/'/g, "'\\''");
31264
- execSync4(`docker exec ${containerName} curl -sf -X POST -H 'Content-Type: application/json' -d '${payload}' http://localhost:8080/dispatch 2>/dev/null || true`, { stdio: "pipe", timeout: 3e4 });
32354
+ execSync5(`docker exec ${containerName} curl -sf -X POST -H 'Content-Type: application/json' -d '${payload}' http://localhost:8080/dispatch 2>/dev/null || true`, { stdio: "pipe", timeout: 3e4 });
31265
32355
  console.log("[world] Task auto-dispatched");
31266
32356
  } catch {
31267
32357
  }
@@ -31272,6 +32362,19 @@ ${opts.task}`;
31272
32362
  const terminalPort = 17681 + portOffset;
31273
32363
  const dashboardUrl = `http://localhost:${dashboardPort}`;
31274
32364
  const terminalUrl = `http://localhost:${terminalPort}`;
32365
+ try {
32366
+ spawnAutoCapture(worldId);
32367
+ } catch {
32368
+ }
32369
+ try {
32370
+ const cap = parseInt(process.env["OLAM_SNAPSHOT_MAX_BYTES"] ?? "", 10);
32371
+ const maxBytes = Number.isInteger(cap) && cap > 0 ? cap : 5 * 1024 * 1024 * 1024;
32372
+ const freed = evictOldSnapshotsWithFlock(maxBytes);
32373
+ if (freed > 0) {
32374
+ console.log(`[snapshot] evicted ${(freed / 1024 / 1024).toFixed(0)}MB to stay under ${(maxBytes / 1024 / 1024 / 1024).toFixed(1)}GB cap`);
32375
+ }
32376
+ } catch {
32377
+ }
31275
32378
  return {
31276
32379
  ...metadata,
31277
32380
  status: "running",
@@ -31329,8 +32432,8 @@ ${opts.task}`;
31329
32432
  } catch {
31330
32433
  }
31331
32434
  try {
31332
- fs16.rmSync(world.workspacePath, { recursive: true, force: true });
31333
- if (fs16.existsSync(world.workspacePath)) {
32435
+ fs20.rmSync(world.workspacePath, { recursive: true, force: true });
32436
+ if (fs20.existsSync(world.workspacePath)) {
31334
32437
  console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
31335
32438
  }
31336
32439
  } catch (err) {
@@ -31415,14 +32518,14 @@ ${opts.task}`;
31415
32518
  return names.map((name) => this.config.repos.find((r) => r.name === name)).filter((r) => r !== void 0);
31416
32519
  }
31417
32520
  transportPlanFile(planFilePath, workspacePath, repoNames) {
31418
- const planContent = fs16.readFileSync(planFilePath, "utf-8");
31419
- const planFileName = path20.basename(planFilePath);
32521
+ const planContent = fs20.readFileSync(planFilePath, "utf-8");
32522
+ const planFileName = path24.basename(planFilePath);
31420
32523
  const targetRepo = repoNames[0];
31421
32524
  if (!targetRepo)
31422
32525
  return;
31423
- const plansDir = path20.join(workspacePath, targetRepo, "docs", "plans");
31424
- fs16.mkdirSync(plansDir, { recursive: true });
31425
- fs16.writeFileSync(path20.join(plansDir, planFileName), planContent);
32526
+ const plansDir = path24.join(workspacePath, targetRepo, "docs", "plans");
32527
+ fs20.mkdirSync(plansDir, { recursive: true });
32528
+ fs20.writeFileSync(path24.join(plansDir, planFileName), planContent);
31426
32529
  }
31427
32530
  resolveServices(repos) {
31428
32531
  const services = [];
@@ -31507,8 +32610,8 @@ import * as http2 from "node:http";
31507
32610
 
31508
32611
  // ../core/dist/dashboard/server.js
31509
32612
  import * as http from "node:http";
31510
- import * as fs17 from "node:fs";
31511
- import * as path21 from "node:path";
32613
+ import * as fs21 from "node:fs";
32614
+ import * as path25 from "node:path";
31512
32615
  import { fileURLToPath as fileURLToPath2 } from "node:url";
31513
32616
 
31514
32617
  // ../core/dist/dashboard/serialize.js
@@ -31843,7 +32946,7 @@ function notFound(res) {
31843
32946
  }
31844
32947
  function openThoughtStore(workspacePath) {
31845
32948
  const dbPath = getWorldDbPath(workspacePath);
31846
- if (!fs17.existsSync(dbPath))
32949
+ if (!fs21.existsSync(dbPath))
31847
32950
  return null;
31848
32951
  return new ThoughtLocalStore(dbPath);
31849
32952
  }
@@ -32014,13 +33117,13 @@ function findSessionInWorld(registry2, sessionId) {
32014
33117
  }
32015
33118
  function createDashboardServer(opts) {
32016
33119
  const { port, registry: registry2 } = opts;
32017
- const thisDir = path21.dirname(fileURLToPath2(import.meta.url));
32018
- const defaultPublicDir = path21.resolve(thisDir, "../../../control-plane/public");
33120
+ const thisDir = path25.dirname(fileURLToPath2(import.meta.url));
33121
+ const defaultPublicDir = path25.resolve(thisDir, "../../../control-plane/public");
32019
33122
  const publicDir = opts.publicDir ?? defaultPublicDir;
32020
- let hasPublicDir = fs17.existsSync(publicDir);
33123
+ let hasPublicDir = fs21.existsSync(publicDir);
32021
33124
  const server = http.createServer((req, res) => {
32022
33125
  if (!hasPublicDir) {
32023
- hasPublicDir = fs17.existsSync(publicDir);
33126
+ hasPublicDir = fs21.existsSync(publicDir);
32024
33127
  }
32025
33128
  const host = req.headers.host ?? `localhost:${port}`;
32026
33129
  const url = new URL(req.url ?? "/", `http://${host}`);
@@ -32294,22 +33397,22 @@ function createDashboardServer(opts) {
32294
33397
  res.end(`<html><body style="font-family:system-ui;padding:2rem"><h1>Olam Dashboard</h1><p>The React app has not been built yet.</p><p>Run <code>npm run build:app</code> in <code>packages/control-plane</code> to build it.</p><p>API routes are available at <code>/api/*</code>.</p></body></html>`);
32295
33398
  return;
32296
33399
  }
32297
- let filePath = path21.join(publicDir, pathname === "/" ? "index.html" : pathname);
33400
+ let filePath = path25.join(publicDir, pathname === "/" ? "index.html" : pathname);
32298
33401
  if (!filePath.startsWith(publicDir)) {
32299
33402
  notFound(res);
32300
33403
  return;
32301
33404
  }
32302
- if (fs17.existsSync(filePath) && fs17.statSync(filePath).isFile()) {
32303
- const ext = path21.extname(filePath);
33405
+ if (fs21.existsSync(filePath) && fs21.statSync(filePath).isFile()) {
33406
+ const ext = path25.extname(filePath);
32304
33407
  const contentType = MIME[ext] ?? "application/octet-stream";
32305
33408
  res.writeHead(200, { "Content-Type": contentType });
32306
- fs17.createReadStream(filePath).pipe(res);
33409
+ fs21.createReadStream(filePath).pipe(res);
32307
33410
  return;
32308
33411
  }
32309
- filePath = path21.join(publicDir, "index.html");
32310
- if (fs17.existsSync(filePath)) {
33412
+ filePath = path25.join(publicDir, "index.html");
33413
+ if (fs21.existsSync(filePath)) {
32311
33414
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
32312
- fs17.createReadStream(filePath).pipe(res);
33415
+ fs21.createReadStream(filePath).pipe(res);
32313
33416
  return;
32314
33417
  }
32315
33418
  notFound(res);
@@ -32319,17 +33422,17 @@ function createDashboardServer(opts) {
32319
33422
  }
32320
33423
 
32321
33424
  // ../core/dist/dashboard/state.js
32322
- import * as fs18 from "node:fs";
32323
- import * as os11 from "node:os";
32324
- import * as path22 from "node:path";
32325
- var STATE_PATH = path22.join(os11.homedir(), ".olam", "dashboard.json");
33425
+ import * as fs22 from "node:fs";
33426
+ import * as os14 from "node:os";
33427
+ import * as path26 from "node:path";
33428
+ var STATE_PATH = path26.join(os14.homedir(), ".olam", "dashboard.json");
32326
33429
  function saveDashboardState(state) {
32327
- fs18.mkdirSync(path22.dirname(STATE_PATH), { recursive: true });
32328
- fs18.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
33430
+ fs22.mkdirSync(path26.dirname(STATE_PATH), { recursive: true });
33431
+ fs22.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
32329
33432
  }
32330
33433
  function loadDashboardState() {
32331
33434
  try {
32332
- const raw = fs18.readFileSync(STATE_PATH, "utf-8");
33435
+ const raw = fs22.readFileSync(STATE_PATH, "utf-8");
32333
33436
  return JSON.parse(raw);
32334
33437
  } catch {
32335
33438
  return null;
@@ -32337,7 +33440,7 @@ function loadDashboardState() {
32337
33440
  }
32338
33441
  function clearDashboardState() {
32339
33442
  try {
32340
- fs18.unlinkSync(STATE_PATH);
33443
+ fs22.unlinkSync(STATE_PATH);
32341
33444
  } catch {
32342
33445
  }
32343
33446
  }
@@ -32355,19 +33458,19 @@ function isDashboardRunning() {
32355
33458
  }
32356
33459
 
32357
33460
  // ../core/dist/dashboard/tunnel.js
32358
- import { spawn, execSync as execSync5 } from "node:child_process";
33461
+ import { spawn as spawn2, execSync as execSync6 } from "node:child_process";
32359
33462
  var tunnelProcess = null;
32360
33463
  function isCloudflaredAvailable() {
32361
33464
  try {
32362
- execSync5("which cloudflared", { stdio: "ignore" });
33465
+ execSync6("which cloudflared", { stdio: "ignore" });
32363
33466
  return true;
32364
33467
  } catch {
32365
33468
  return false;
32366
33469
  }
32367
33470
  }
32368
33471
  function startTunnel(port) {
32369
- return new Promise((resolve6, reject2) => {
32370
- const child = spawn("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
33472
+ return new Promise((resolve8, reject2) => {
33473
+ const child = spawn2("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
32371
33474
  stdio: ["ignore", "pipe", "pipe"],
32372
33475
  detached: false
32373
33476
  });
@@ -32388,7 +33491,7 @@ function startTunnel(port) {
32388
33491
  if (match) {
32389
33492
  resolved = true;
32390
33493
  clearTimeout(timeout);
32391
- resolve6(match[0]);
33494
+ resolve8(match[0]);
32392
33495
  }
32393
33496
  }
32394
33497
  child.stdout?.on("data", scan);
@@ -32456,8 +33559,8 @@ var DashboardManager = class {
32456
33559
  }
32457
33560
  throw err;
32458
33561
  }
32459
- await new Promise((resolve6, reject2) => {
32460
- this.server.on("listening", resolve6);
33562
+ await new Promise((resolve8, reject2) => {
33563
+ this.server.on("listening", resolve8);
32461
33564
  this.server.on("error", reject2);
32462
33565
  });
32463
33566
  this.info = { localUrl: `http://localhost:${port}` };
@@ -32503,8 +33606,8 @@ var DashboardManager = class {
32503
33606
  async stop() {
32504
33607
  stopTunnel();
32505
33608
  if (this.server) {
32506
- await new Promise((resolve6) => {
32507
- this.server.close(() => resolve6());
33609
+ await new Promise((resolve8) => {
33610
+ this.server.close(() => resolve8());
32508
33611
  });
32509
33612
  this.server = null;
32510
33613
  }
@@ -32617,8 +33720,8 @@ var PleriClient = class {
32617
33720
  };
32618
33721
 
32619
33722
  // ../mcp-server/src/env-loader.ts
32620
- import { readFileSync as readFileSync15, existsSync as existsSync19, statSync as statSync6 } from "node:fs";
32621
- import { join as join25, dirname as dirname13, resolve as resolve5 } from "node:path";
33723
+ import { readFileSync as readFileSync16, existsSync as existsSync21, statSync as statSync7 } from "node:fs";
33724
+ import { join as join27, dirname as dirname15, resolve as resolve7 } from "node:path";
32622
33725
  var PROJECT_MARKERS = [
32623
33726
  ".olam/config.yaml",
32624
33727
  ".olam/config.yml",
@@ -32626,30 +33729,30 @@ var PROJECT_MARKERS = [
32626
33729
  "olam.yml"
32627
33730
  ];
32628
33731
  function findProjectRoot2(startDir) {
32629
- let dir = resolve5(startDir);
32630
- const root = resolve5("/");
33732
+ let dir = resolve7(startDir);
33733
+ const root = resolve7("/");
32631
33734
  while (true) {
32632
33735
  for (const marker of PROJECT_MARKERS) {
32633
- if (existsSync19(join25(dir, marker))) return dir;
33736
+ if (existsSync21(join27(dir, marker))) return dir;
32634
33737
  }
32635
- const pkg = join25(dir, "package.json");
32636
- if (existsSync19(pkg)) {
33738
+ const pkg = join27(dir, "package.json");
33739
+ if (existsSync21(pkg)) {
32637
33740
  try {
32638
- const json = JSON.parse(readFileSync15(pkg, "utf8"));
33741
+ const json = JSON.parse(readFileSync16(pkg, "utf8"));
32639
33742
  const isOlamWorkspace = typeof json.name === "string" && json.name.startsWith("@olam/");
32640
33743
  const hasOlamDep = json.dependencies && Object.keys(json.dependencies).some((k) => k.startsWith("@olam/")) || json.devDependencies && Object.keys(json.devDependencies).some((k) => k.startsWith("@olam/"));
32641
33744
  if (isOlamWorkspace || hasOlamDep) return dir;
32642
33745
  } catch {
32643
33746
  }
32644
33747
  }
32645
- const parent = dirname13(dir);
33748
+ const parent = dirname15(dir);
32646
33749
  if (parent === dir || parent === root) return null;
32647
33750
  dir = parent;
32648
33751
  }
32649
33752
  }
32650
- function parseEnvFile(path23) {
33753
+ function parseEnvFile(path27) {
32651
33754
  const out = {};
32652
- const raw = readFileSync15(path23, "utf8");
33755
+ const raw = readFileSync16(path27, "utf8");
32653
33756
  for (const line of raw.split(/\r?\n/)) {
32654
33757
  const trimmed = line.trim();
32655
33758
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -32672,8 +33775,8 @@ function loadProjectEnv(startDir = process.cwd()) {
32672
33775
  const filesRead = [];
32673
33776
  const merged = {};
32674
33777
  for (const name of [".env", ".env.local"]) {
32675
- const p = join25(root, name);
32676
- if (existsSync19(p) && statSync6(p).isFile()) {
33778
+ const p = join27(root, name);
33779
+ if (existsSync21(p) && statSync7(p).isFile()) {
32677
33780
  Object.assign(merged, parseEnvFile(p));
32678
33781
  filesRead.push(p);
32679
33782
  }
@@ -32743,7 +33846,7 @@ async function main() {
32743
33846
  initError = err instanceof Error ? err : new Error(String(err));
32744
33847
  logger.error("Failed to initialize Olam", { error: initError.message });
32745
33848
  }
32746
- const server = createServer3(ctx, initError);
33849
+ const server = createServer4(ctx, initError);
32747
33850
  const transport = new StdioServerTransport();
32748
33851
  await server.connect(transport);
32749
33852
  logger.info("Olam MCP server running on stdio transport");