@pleri/olam-cli 0.1.70 → 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 (241) hide show
  1. package/dist/commands/create.d.ts.map +1 -1
  2. package/dist/commands/create.js +2 -0
  3. package/dist/commands/create.js.map +1 -1
  4. package/dist/commands/runbooks.d.ts +13 -0
  5. package/dist/commands/runbooks.d.ts.map +1 -0
  6. package/dist/commands/runbooks.js +189 -0
  7. package/dist/commands/runbooks.js.map +1 -0
  8. package/dist/commands/world-snapshot.d.ts +1 -0
  9. package/dist/commands/world-snapshot.d.ts.map +1 -1
  10. package/dist/commands/world-snapshot.js +126 -1
  11. package/dist/commands/world-snapshot.js.map +1 -1
  12. package/dist/commands/worldspec/compile.d.ts +20 -0
  13. package/dist/commands/worldspec/compile.d.ts.map +1 -0
  14. package/dist/commands/worldspec/compile.js +130 -0
  15. package/dist/commands/worldspec/compile.js.map +1 -0
  16. package/dist/commands/worldspec/index.d.ts +12 -0
  17. package/dist/commands/worldspec/index.d.ts.map +1 -0
  18. package/dist/commands/worldspec/index.js +23 -0
  19. package/dist/commands/worldspec/index.js.map +1 -0
  20. package/dist/commands/worldspec/init.d.ts +15 -0
  21. package/dist/commands/worldspec/init.d.ts.map +1 -0
  22. package/dist/commands/worldspec/init.js +166 -0
  23. package/dist/commands/worldspec/init.js.map +1 -0
  24. package/dist/commands/worldspec/schema.d.ts +11 -0
  25. package/dist/commands/worldspec/schema.d.ts.map +1 -0
  26. package/dist/commands/worldspec/schema.js +55 -0
  27. package/dist/commands/worldspec/schema.js.map +1 -0
  28. package/dist/commands/worldspec/validate.d.ts +15 -0
  29. package/dist/commands/worldspec/validate.d.ts.map +1 -0
  30. package/dist/commands/worldspec/validate.js +66 -0
  31. package/dist/commands/worldspec/validate.js.map +1 -0
  32. package/dist/exit-codes.d.ts +32 -0
  33. package/dist/exit-codes.d.ts.map +1 -1
  34. package/dist/exit-codes.js +32 -0
  35. package/dist/exit-codes.js.map +1 -1
  36. package/dist/image-digests.json +5 -5
  37. package/dist/index.js +4379 -897
  38. package/dist/index.js.map +1 -1
  39. package/dist/mcp-server.js +1252 -286
  40. package/host-cp/src/global-config-source.mjs +71 -0
  41. package/host-cp/src/listening-server-poller.mjs +1 -1
  42. package/host-cp/src/plan-orchestrator.mjs +20 -1
  43. package/host-cp/src/port-bridge-manager.mjs +1 -1
  44. package/host-cp/src/server.mjs +46 -7
  45. package/package.json +4 -2
  46. package/dist/__tests__/audit-publish-deps-contract.test.d.ts +0 -26
  47. package/dist/__tests__/audit-publish-deps-contract.test.d.ts.map +0 -1
  48. package/dist/__tests__/audit-publish-deps-contract.test.js +0 -86
  49. package/dist/__tests__/audit-publish-deps-contract.test.js.map +0 -1
  50. package/dist/__tests__/auth-status.test.d.ts +0 -2
  51. package/dist/__tests__/auth-status.test.d.ts.map +0 -1
  52. package/dist/__tests__/auth-status.test.js +0 -291
  53. package/dist/__tests__/auth-status.test.js.map +0 -1
  54. package/dist/__tests__/auth-upgrade.test.d.ts +0 -9
  55. package/dist/__tests__/auth-upgrade.test.d.ts.map +0 -1
  56. package/dist/__tests__/auth-upgrade.test.js +0 -397
  57. package/dist/__tests__/auth-upgrade.test.js.map +0 -1
  58. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts +0 -22
  59. package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts.map +0 -1
  60. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js +0 -63
  61. package/dist/__tests__/bootstrap-tag-mcp-auth.test.js.map +0 -1
  62. package/dist/__tests__/cli-mcp-revoke.test.d.ts +0 -8
  63. package/dist/__tests__/cli-mcp-revoke.test.d.ts.map +0 -1
  64. package/dist/__tests__/cli-mcp-revoke.test.js +0 -124
  65. package/dist/__tests__/cli-mcp-revoke.test.js.map +0 -1
  66. package/dist/__tests__/config.test.d.ts +0 -2
  67. package/dist/__tests__/config.test.d.ts.map +0 -1
  68. package/dist/__tests__/config.test.js +0 -95
  69. package/dist/__tests__/config.test.js.map +0 -1
  70. package/dist/__tests__/create-app-urls.test.d.ts +0 -2
  71. package/dist/__tests__/create-app-urls.test.d.ts.map +0 -1
  72. package/dist/__tests__/create-app-urls.test.js +0 -102
  73. package/dist/__tests__/create-app-urls.test.js.map +0 -1
  74. package/dist/__tests__/docker-host.test.d.ts +0 -14
  75. package/dist/__tests__/docker-host.test.d.ts.map +0 -1
  76. package/dist/__tests__/docker-host.test.js +0 -109
  77. package/dist/__tests__/docker-host.test.js.map +0 -1
  78. package/dist/__tests__/enter.test.d.ts +0 -2
  79. package/dist/__tests__/enter.test.d.ts.map +0 -1
  80. package/dist/__tests__/enter.test.js +0 -90
  81. package/dist/__tests__/enter.test.js.map +0 -1
  82. package/dist/__tests__/help-output.test.d.ts +0 -2
  83. package/dist/__tests__/help-output.test.d.ts.map +0 -1
  84. package/dist/__tests__/help-output.test.js +0 -74
  85. package/dist/__tests__/help-output.test.js.map +0 -1
  86. package/dist/__tests__/host-cp-gh-token.test.d.ts +0 -9
  87. package/dist/__tests__/host-cp-gh-token.test.d.ts.map +0 -1
  88. package/dist/__tests__/host-cp-gh-token.test.js +0 -129
  89. package/dist/__tests__/host-cp-gh-token.test.js.map +0 -1
  90. package/dist/__tests__/host-cp.test.d.ts +0 -9
  91. package/dist/__tests__/host-cp.test.d.ts.map +0 -1
  92. package/dist/__tests__/host-cp.test.js +0 -335
  93. package/dist/__tests__/host-cp.test.js.map +0 -1
  94. package/dist/__tests__/image-presence.test.d.ts +0 -2
  95. package/dist/__tests__/image-presence.test.d.ts.map +0 -1
  96. package/dist/__tests__/image-presence.test.js +0 -44
  97. package/dist/__tests__/image-presence.test.js.map +0 -1
  98. package/dist/__tests__/install-root.test.d.ts +0 -2
  99. package/dist/__tests__/install-root.test.d.ts.map +0 -1
  100. package/dist/__tests__/install-root.test.js +0 -119
  101. package/dist/__tests__/install-root.test.js.map +0 -1
  102. package/dist/__tests__/keys.test.d.ts +0 -9
  103. package/dist/__tests__/keys.test.d.ts.map +0 -1
  104. package/dist/__tests__/keys.test.js +0 -145
  105. package/dist/__tests__/keys.test.js.map +0 -1
  106. package/dist/__tests__/logs.test.d.ts +0 -9
  107. package/dist/__tests__/logs.test.d.ts.map +0 -1
  108. package/dist/__tests__/logs.test.js +0 -124
  109. package/dist/__tests__/logs.test.js.map +0 -1
  110. package/dist/__tests__/mcp-import.test.d.ts +0 -11
  111. package/dist/__tests__/mcp-import.test.d.ts.map +0 -1
  112. package/dist/__tests__/mcp-import.test.js +0 -134
  113. package/dist/__tests__/mcp-import.test.js.map +0 -1
  114. package/dist/__tests__/protocol-version.test.d.ts +0 -2
  115. package/dist/__tests__/protocol-version.test.d.ts.map +0 -1
  116. package/dist/__tests__/protocol-version.test.js +0 -170
  117. package/dist/__tests__/protocol-version.test.js.map +0 -1
  118. package/dist/__tests__/ps.test.d.ts +0 -2
  119. package/dist/__tests__/ps.test.d.ts.map +0 -1
  120. package/dist/__tests__/ps.test.js +0 -172
  121. package/dist/__tests__/ps.test.js.map +0 -1
  122. package/dist/__tests__/registry-allowlist.test.d.ts +0 -2
  123. package/dist/__tests__/registry-allowlist.test.d.ts.map +0 -1
  124. package/dist/__tests__/registry-allowlist.test.js +0 -129
  125. package/dist/__tests__/registry-allowlist.test.js.map +0 -1
  126. package/dist/__tests__/services.test.d.ts +0 -8
  127. package/dist/__tests__/services.test.d.ts.map +0 -1
  128. package/dist/__tests__/services.test.js +0 -185
  129. package/dist/__tests__/services.test.js.map +0 -1
  130. package/dist/__tests__/status-app-urls.test.d.ts +0 -2
  131. package/dist/__tests__/status-app-urls.test.d.ts.map +0 -1
  132. package/dist/__tests__/status-app-urls.test.js +0 -125
  133. package/dist/__tests__/status-app-urls.test.js.map +0 -1
  134. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts +0 -19
  135. package/dist/__tests__/upgrade-gh-token-contract.test.d.ts.map +0 -1
  136. package/dist/__tests__/upgrade-gh-token-contract.test.js +0 -63
  137. package/dist/__tests__/upgrade-gh-token-contract.test.js.map +0 -1
  138. package/dist/__tests__/upgrade.test.d.ts +0 -9
  139. package/dist/__tests__/upgrade.test.d.ts.map +0 -1
  140. package/dist/__tests__/upgrade.test.js +0 -586
  141. package/dist/__tests__/upgrade.test.js.map +0 -1
  142. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts +0 -6
  143. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts.map +0 -1
  144. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js +0 -26
  145. package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js.map +0 -1
  146. package/dist/commands/__tests__/begin.test.d.ts +0 -7
  147. package/dist/commands/__tests__/begin.test.d.ts.map +0 -1
  148. package/dist/commands/__tests__/begin.test.js +0 -72
  149. package/dist/commands/__tests__/begin.test.js.map +0 -1
  150. package/dist/commands/__tests__/bootstrap.test.d.ts +0 -2
  151. package/dist/commands/__tests__/bootstrap.test.d.ts.map +0 -1
  152. package/dist/commands/__tests__/bootstrap.test.js +0 -370
  153. package/dist/commands/__tests__/bootstrap.test.js.map +0 -1
  154. package/dist/commands/__tests__/carry-uncommitted.test.d.ts +0 -14
  155. package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +0 -1
  156. package/dist/commands/__tests__/carry-uncommitted.test.js +0 -83
  157. package/dist/commands/__tests__/carry-uncommitted.test.js.map +0 -1
  158. package/dist/commands/__tests__/clean.test.d.ts +0 -9
  159. package/dist/commands/__tests__/clean.test.d.ts.map +0 -1
  160. package/dist/commands/__tests__/clean.test.js +0 -105
  161. package/dist/commands/__tests__/clean.test.js.map +0 -1
  162. package/dist/commands/__tests__/crystallize.test.d.ts +0 -2
  163. package/dist/commands/__tests__/crystallize.test.d.ts.map +0 -1
  164. package/dist/commands/__tests__/crystallize.test.js +0 -133
  165. package/dist/commands/__tests__/crystallize.test.js.map +0 -1
  166. package/dist/commands/__tests__/diagnose.test.d.ts +0 -9
  167. package/dist/commands/__tests__/diagnose.test.d.ts.map +0 -1
  168. package/dist/commands/__tests__/diagnose.test.js +0 -108
  169. package/dist/commands/__tests__/diagnose.test.js.map +0 -1
  170. package/dist/commands/__tests__/openHostCpUrl.test.d.ts +0 -2
  171. package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +0 -1
  172. package/dist/commands/__tests__/openHostCpUrl.test.js +0 -63
  173. package/dist/commands/__tests__/openHostCpUrl.test.js.map +0 -1
  174. package/dist/commands/__tests__/refresh.test.d.ts +0 -13
  175. package/dist/commands/__tests__/refresh.test.d.ts.map +0 -1
  176. package/dist/commands/__tests__/refresh.test.js +0 -170
  177. package/dist/commands/__tests__/refresh.test.js.map +0 -1
  178. package/dist/commands/__tests__/status.test.d.ts +0 -8
  179. package/dist/commands/__tests__/status.test.d.ts.map +0 -1
  180. package/dist/commands/__tests__/status.test.js +0 -62
  181. package/dist/commands/__tests__/status.test.js.map +0 -1
  182. package/dist/commands/__tests__/stop.test.d.ts +0 -5
  183. package/dist/commands/__tests__/stop.test.d.ts.map +0 -1
  184. package/dist/commands/__tests__/stop.test.js +0 -30
  185. package/dist/commands/__tests__/stop.test.js.map +0 -1
  186. package/dist/commands/__tests__/update.test.d.ts +0 -7
  187. package/dist/commands/__tests__/update.test.d.ts.map +0 -1
  188. package/dist/commands/__tests__/update.test.js +0 -224
  189. package/dist/commands/__tests__/update.test.js.map +0 -1
  190. package/dist/commands/__tests__/upgrade.all-three.test.d.ts +0 -19
  191. package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +0 -1
  192. package/dist/commands/__tests__/upgrade.all-three.test.js +0 -80
  193. package/dist/commands/__tests__/upgrade.all-three.test.js.map +0 -1
  194. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts +0 -20
  195. package/dist/commands/__tests__/upgrade.compose-path.test.d.ts.map +0 -1
  196. package/dist/commands/__tests__/upgrade.compose-path.test.js +0 -140
  197. package/dist/commands/__tests__/upgrade.compose-path.test.js.map +0 -1
  198. package/dist/commands/__tests__/upgrade.history.test.d.ts +0 -15
  199. package/dist/commands/__tests__/upgrade.history.test.d.ts.map +0 -1
  200. package/dist/commands/__tests__/upgrade.history.test.js +0 -199
  201. package/dist/commands/__tests__/upgrade.history.test.js.map +0 -1
  202. package/dist/commands/__tests__/upgrade.lock.test.d.ts +0 -15
  203. package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +0 -1
  204. package/dist/commands/__tests__/upgrade.lock.test.js +0 -253
  205. package/dist/commands/__tests__/upgrade.lock.test.js.map +0 -1
  206. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +0 -21
  207. package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +0 -1
  208. package/dist/commands/__tests__/upgrade.olam-tag.test.js +0 -114
  209. package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +0 -1
  210. package/dist/commands/__tests__/upgrade.poll.test.d.ts +0 -14
  211. package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +0 -1
  212. package/dist/commands/__tests__/upgrade.poll.test.js +0 -136
  213. package/dist/commands/__tests__/upgrade.poll.test.js.map +0 -1
  214. package/dist/commands/__tests__/upgrade.recreate.test.d.ts +0 -17
  215. package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +0 -1
  216. package/dist/commands/__tests__/upgrade.recreate.test.js +0 -83
  217. package/dist/commands/__tests__/upgrade.recreate.test.js.map +0 -1
  218. package/dist/commands/__tests__/upgrade.rollback.test.d.ts +0 -12
  219. package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +0 -1
  220. package/dist/commands/__tests__/upgrade.rollback.test.js +0 -255
  221. package/dist/commands/__tests__/upgrade.rollback.test.js.map +0 -1
  222. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +0 -12
  223. package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +0 -1
  224. package/dist/commands/__tests__/upgrade.sha-capture.test.js +0 -63
  225. package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +0 -1
  226. package/dist/commands/__tests__/upgrade.smoke.test.d.ts +0 -19
  227. package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +0 -1
  228. package/dist/commands/__tests__/upgrade.smoke.test.js +0 -87
  229. package/dist/commands/__tests__/upgrade.smoke.test.js.map +0 -1
  230. package/dist/commands/__tests__/upgrade.swap.test.d.ts +0 -19
  231. package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +0 -1
  232. package/dist/commands/__tests__/upgrade.swap.test.js +0 -312
  233. package/dist/commands/__tests__/upgrade.swap.test.js.map +0 -1
  234. package/dist/commands/__tests__/world-upgrade.test.d.ts +0 -8
  235. package/dist/commands/__tests__/world-upgrade.test.d.ts.map +0 -1
  236. package/dist/commands/__tests__/world-upgrade.test.js +0 -73
  237. package/dist/commands/__tests__/world-upgrade.test.js.map +0 -1
  238. package/dist/lib/__tests__/symlink-reconcile.test.d.ts +0 -2
  239. package/dist/lib/__tests__/symlink-reconcile.test.d.ts.map +0 -1
  240. package/dist/lib/__tests__/symlink-reconcile.test.js +0 -106
  241. 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(path24) {
3232
- let input = path24;
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 [path24, query] = wsComponent.resourceName.split("?");
3432
- wsComponent.path = path24 && path24 !== "/" ? path24 : 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, fs20, 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, fs20[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, path24) {
7006
- if (!path24)
7005
+ function getElementAtPath(obj, path27) {
7006
+ if (!path27)
7007
7007
  return obj;
7008
- return path24.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(path24, 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(path24);
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: path24, errorMaps, issueData } = params;
13407
- const fullPath = [...path24, ...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, path24, key) {
13523
+ constructor(parent, value, path27, key) {
13524
13524
  this._cachedPath = [];
13525
13525
  this.parent = parent;
13526
13526
  this.data = value;
13527
- this._path = path24;
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, path24, body, attempt = 0) {
21369
- const url = `${this.baseUrl}${path24}`;
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, path24, 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: fs20 } = await import("node:fs");
22195
- const { default: os13 } = await import("node:os");
22196
- const { default: path24 } = await import("node:path");
22197
- const tokenPath = path24.join(os13.homedir(), ".olam", "host-cp.token");
22198
- if (fs20.existsSync(tokenPath)) {
22199
- const token = fs20.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, path24, 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, path24, ctx, rejectSource) {
22397
22399
  if (FORBIDDEN_KEYS.has(key)) {
22398
22400
  ctx.addIssue({
22399
22401
  code: external_exports.ZodIssueCode.custom,
22400
- path: [...path24, key],
22402
+ path: [...path27, key],
22401
22403
  message: `forbidden key "${key}" (prototype-pollution surface)`
22402
22404
  });
22403
22405
  continue;
22404
22406
  }
22405
- if (rejectSource && path24.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, path24, ctx, rejectSource) {
22410
22412
  });
22411
22413
  continue;
22412
22414
  }
22413
- refineForbiddenKeys(value[key], [...path24, key], ctx, false);
22415
+ refineForbiddenKeys(value[key], [...path27, key], ctx, false);
22414
22416
  }
22415
22417
  }
22416
- function rejectForbiddenKeys(value, path24, 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] ${path24}: 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] ${path24}: 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], `${path24}.${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(path24) {
22866
- if (!existsSync6(path24)) return null;
22867
+ function readOptional(path27) {
22868
+ if (!existsSync6(path27)) return null;
22867
22869
  try {
22868
- return readFileSync5(path24, "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(path24, method, body) {
24012
- const url = `${this.config.workerUrl}${path24}`;
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 path24 = join12(args.outDir, "manifest.json");
26446
- await writeFile(path24, `${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: path24, 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 path24 = target.pathname;
26703
- if (!liveCompiled.some((re) => re.test(path24))) {
26704
- return httpReject(403, "outside_allow_list", { path: path24 });
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 path24 = join13(outDir, `${shot.name}.${ext}`);
27491
+ const path27 = join13(outDir, `${shot.name}.${ext}`);
27484
27492
  await page.screenshot({
27485
- path: path24,
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: path24, 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: fs20 } = await import("node:fs");
27934
- const { default: os13 } = await import("node:os");
27935
- const { default: path24 } = await import("node:path");
27936
- const tp = path24.join(os13.homedir(), ".olam", "host-cp.token");
27937
- if (!fs20.existsSync(tp)) return { token: null };
27938
- return { token: fs20.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: path24, description, defaultBranch }) => {
28555
+ async ({ name, path: path27, description, defaultBranch }) => {
28386
28556
  try {
28387
- const entry = addRepo({ name, path: path24, 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: path24, description, defaultBranch }) => {
28598
+ async ({ name, path: path27, description, defaultBranch }) => {
28429
28599
  try {
28430
- const entry = updateRepo(name, { path: path24, description, defaultBranch });
28600
+ const entry = updateRepo(name, { path: path27, description, defaultBranch });
28431
28601
  return {
28432
28602
  content: [{
28433
28603
  type: "text",
@@ -28577,6 +28747,163 @@ function register23(server, _ctx, _initError) {
28577
28747
  );
28578
28748
  }
28579
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
+
28580
28907
  // ../mcp-server/src/tools/index.ts
28581
28908
  var toolModules = [
28582
28909
  init_exports,
@@ -28601,7 +28928,8 @@ var toolModules = [
28601
28928
  capture_view_exports,
28602
28929
  create_from_prompt_exports,
28603
28930
  repo_exports,
28604
- process_port_exports
28931
+ process_port_exports,
28932
+ runbook_exports
28605
28933
  ];
28606
28934
  function registerAllTools(server, ctx, initError) {
28607
28935
  for (const mod of toolModules) {
@@ -28618,7 +28946,7 @@ var SERVER_INSTRUCTIONS = [
28618
28946
  "Use olam_observe to watch a world's reasoning. Use olam_crystallize to save thoughts.",
28619
28947
  "Always olam_destroy_world when done to clean up resources."
28620
28948
  ].join("\n");
28621
- function createServer3(ctx, initError) {
28949
+ function createServer4(ctx, initError) {
28622
28950
  const server = new McpServer(
28623
28951
  { name: SERVER_NAME, version: SERVER_VERSION },
28624
28952
  { instructions: SERVER_INSTRUCTIONS }
@@ -29098,11 +29426,11 @@ function loadConfig(startDir) {
29098
29426
  }
29099
29427
 
29100
29428
  // ../core/dist/world/manager.js
29101
- import * as crypto4 from "node:crypto";
29102
- import { execSync as execSync4 } from "node:child_process";
29103
- import * as fs17 from "node:fs";
29104
- import * as os11 from "node:os";
29105
- import * as path21 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";
29106
29434
 
29107
29435
  // ../core/dist/world/state.js
29108
29436
  var VALID_TRANSITIONS = {
@@ -29136,6 +29464,7 @@ var WorldStateMachine = class {
29136
29464
  };
29137
29465
 
29138
29466
  // ../core/dist/world/devbox-image.js
29467
+ var WORLDSPEC_OVERRIDE_TAG = "worldspec-override";
29139
29468
  function selectDevboxImage(config2, repos) {
29140
29469
  const selectors = config2.devbox?.image_selectors;
29141
29470
  if (!selectors || selectors.length === 0)
@@ -29147,7 +29476,8 @@ function selectDevboxImage(config2, repos) {
29147
29476
  return {
29148
29477
  image: resolveDevboxImage(config2, sel.tag),
29149
29478
  cacheArch: sel.cache_arch,
29150
- tag: sel.tag
29479
+ tag: sel.tag,
29480
+ source: "image_selector"
29151
29481
  };
29152
29482
  }
29153
29483
  }
@@ -29168,6 +29498,18 @@ function matches(sel, repoNames, repoTypes) {
29168
29498
  }
29169
29499
  return true;
29170
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
+ }
29171
29513
  function resolveDevboxImage(config2, tag) {
29172
29514
  const registry2 = config2.devbox?.registry;
29173
29515
  if (!registry2)
@@ -29311,8 +29653,8 @@ import * as fs14 from "node:fs";
29311
29653
  import * as os10 from "node:os";
29312
29654
  import * as path17 from "node:path";
29313
29655
  var DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
29314
- function expandHome(p, homedir13) {
29315
- return p.replace(/^~(?=$|\/|\\)/, homedir13());
29656
+ function expandHome(p, homedir15) {
29657
+ return p.replace(/^~(?=$|\/|\\)/, homedir15());
29316
29658
  }
29317
29659
  function sanitizeRepoFilename(name) {
29318
29660
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -29335,7 +29677,7 @@ ${stderr}`;
29335
29677
  }
29336
29678
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
29337
29679
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
29338
- const homedir13 = deps.homedir ?? (() => os10.homedir());
29680
+ const homedir15 = deps.homedir ?? (() => os10.homedir());
29339
29681
  const baselineDir = path17.join(workspacePath, ".olam", "baseline");
29340
29682
  try {
29341
29683
  fs14.mkdirSync(baselineDir, { recursive: true });
@@ -29351,7 +29693,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
29351
29693
  continue;
29352
29694
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
29353
29695
  const outPath = path17.join(baselineDir, filename);
29354
- const repoPath = expandHome(repo.path, homedir13);
29696
+ const repoPath = expandHome(repo.path, homedir15);
29355
29697
  if (!fs14.existsSync(repoPath)) {
29356
29698
  writeBaselineFile(outPath, `# repo: ${repo.name}
29357
29699
  # (skipped: path ${repoPath} does not exist)
@@ -29866,7 +30208,7 @@ async function installStack(exec, repos, stacks) {
29866
30208
  }
29867
30209
 
29868
30210
  // ../core/dist/world/stack-image.js
29869
- import { execSync as execSync2 } from "node:child_process";
30211
+ import { execSync as execSync3 } from "node:child_process";
29870
30212
  import * as crypto3 from "node:crypto";
29871
30213
  var BASE_IMAGE = "olam-devbox";
29872
30214
  var LABEL_PREFIX = "olam.stack-image";
@@ -29898,7 +30240,7 @@ function lookupCachedImage(runtimes) {
29898
30240
  const tag = computeImageTag(runtimes);
29899
30241
  const imageName = `${BASE_IMAGE}:${tag}`;
29900
30242
  try {
29901
- execSync2(`docker image inspect ${imageName} > /dev/null 2>&1`, {
30243
+ execSync3(`docker image inspect ${imageName} > /dev/null 2>&1`, {
29902
30244
  stdio: "pipe",
29903
30245
  timeout: 5e3
29904
30246
  });
@@ -29910,14 +30252,14 @@ function lookupCachedImage(runtimes) {
29910
30252
  function commitAsImage(containerName, imageName) {
29911
30253
  const baseDigest = getBaseImageDigest();
29912
30254
  const now = (/* @__PURE__ */ new Date()).toISOString();
29913
- 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 });
29914
30256
  }
29915
30257
  var cachedBaseDigest;
29916
30258
  function getBaseImageDigest() {
29917
30259
  if (cachedBaseDigest)
29918
30260
  return cachedBaseDigest;
29919
30261
  try {
29920
- 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();
29921
30263
  cachedBaseDigest = digest.replace("sha256:", "").slice(0, 16);
29922
30264
  return cachedBaseDigest;
29923
30265
  } catch {
@@ -30142,6 +30484,9 @@ var SENTINEL_SUFFIX = "-done";
30142
30484
  function sentinelPath(workdir, index) {
30143
30485
  return `${workdir}/${SENTINEL_PREFIX}${index}${SENTINEL_SUFFIX}`;
30144
30486
  }
30487
+ function bootstrapStepSentinelPath(workdir, index) {
30488
+ return sentinelPath(workdir, index);
30489
+ }
30145
30490
  async function runBootstrap(containerName, workdir, steps, exec, options = {}) {
30146
30491
  if (!SAFE_IDENT.test(containerName)) {
30147
30492
  throw new Error(`containerName "${containerName}" must match ${SAFE_IDENT} (defensive guard)`);
@@ -30197,8 +30542,430 @@ function shellQuote(s) {
30197
30542
  return `'${s.replace(/'/g, `'\\''`)}'`;
30198
30543
  }
30199
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
+
30200
30967
  // ../core/dist/world/secrets-fetcher.js
30201
- import { execSync as execSync3 } from "node:child_process";
30968
+ import { execSync as execSync4 } from "node:child_process";
30202
30969
  function parseGcpSecretUrl(url) {
30203
30970
  if (!url.startsWith("gcp://"))
30204
30971
  return null;
@@ -30213,7 +30980,7 @@ function parseGcpSecretUrl(url) {
30213
30980
  return { project, secretName };
30214
30981
  }
30215
30982
  function defaultExecFn(cmd, opts) {
30216
- return execSync3(cmd, { encoding: "utf-8", timeout: opts?.timeout ?? 15e3 });
30983
+ return execSync4(cmd, { encoding: "utf-8", timeout: opts?.timeout ?? 15e3 });
30217
30984
  }
30218
30985
  function fetchGcpSecret(ref, execFn = defaultExecFn) {
30219
30986
  const { project, secretName } = ref;
@@ -30306,14 +31073,14 @@ function gcloudAvailable(execFn = defaultExecFn) {
30306
31073
  }
30307
31074
 
30308
31075
  // ../core/dist/world/olam-yaml.js
30309
- import * as path19 from "node:path";
31076
+ import * as path20 from "node:path";
30310
31077
  import YAML2 from "yaml";
30311
31078
  function enrichReposWithManifests(repos, workspacePath) {
30312
31079
  return repos.map((repo) => {
30313
31080
  if (repo.manifest !== void 0 && repo.manifest !== null) {
30314
31081
  return repo;
30315
31082
  }
30316
- const repoDir = path19.join(workspacePath, repo.name);
31083
+ const repoDir = path20.join(workspacePath, repo.name);
30317
31084
  let manifest = null;
30318
31085
  try {
30319
31086
  manifest = loadRepoManifest(repoDir);
@@ -30328,8 +31095,8 @@ function enrichReposWithManifests(repos, workspacePath) {
30328
31095
  }
30329
31096
 
30330
31097
  // ../core/dist/policies/loader.js
30331
- import * as fs16 from "node:fs";
30332
- import * as path20 from "node:path";
31098
+ import * as fs17 from "node:fs";
31099
+ import * as path21 from "node:path";
30333
31100
  import { parse as parseYaml3 } from "yaml";
30334
31101
  function parseFrontmatter(content) {
30335
31102
  const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
@@ -30349,20 +31116,20 @@ function toStringArray(v) {
30349
31116
  return v.filter((x) => typeof x === "string");
30350
31117
  }
30351
31118
  function loadPolicies(workspaceRoot) {
30352
- const policiesDir = path20.join(workspaceRoot, ".olam", "policies");
30353
- if (!fs16.existsSync(policiesDir))
31119
+ const policiesDir = path21.join(workspaceRoot, ".olam", "policies");
31120
+ if (!fs17.existsSync(policiesDir))
30354
31121
  return [];
30355
31122
  let files;
30356
31123
  try {
30357
- files = fs16.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
31124
+ files = fs17.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
30358
31125
  } catch {
30359
31126
  return [];
30360
31127
  }
30361
31128
  const policies = [];
30362
31129
  for (const file of files) {
30363
- const filePath = path20.join(policiesDir, file);
31130
+ const filePath = path21.join(policiesDir, file);
30364
31131
  try {
30365
- const content = fs16.readFileSync(filePath, "utf8");
31132
+ const content = fs17.readFileSync(filePath, "utf8");
30366
31133
  const parsed = parseFrontmatter(content);
30367
31134
  if (!parsed) {
30368
31135
  console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
@@ -30412,17 +31179,93 @@ function formatPoliciesBrief(policies) {
30412
31179
  return lines.join("\n");
30413
31180
  }
30414
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
+
30415
31258
  // ../core/dist/world/tmux-supervisor.js
30416
31259
  var PortInUseError = class extends Error {
30417
31260
  port;
30418
31261
  repo;
30419
31262
  manifestPath;
30420
- constructor(port, repo, manifestPath) {
30421
- 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}.`);
30422
31265
  this.name = "PortInUseError";
30423
31266
  this.port = port;
30424
31267
  this.repo = repo;
30425
- this.manifestPath = manifestPath;
31268
+ this.manifestPath = manifestPath2;
30426
31269
  }
30427
31270
  };
30428
31271
  var SAFE_IDENT2 = /^[a-z0-9][a-z0-9-]{0,63}$/;
@@ -30517,7 +31360,7 @@ var BotIdentityError = class extends Error {
30517
31360
  this.name = "BotIdentityError";
30518
31361
  }
30519
31362
  };
30520
- function getTokenScopes(ghToken, _exec = execSync4) {
31363
+ function getTokenScopes(ghToken, _exec = execSync5) {
30521
31364
  try {
30522
31365
  const out = _exec("gh auth status 2>&1", {
30523
31366
  encoding: "utf-8",
@@ -30534,13 +31377,13 @@ function getTokenScopes(ghToken, _exec = execSync4) {
30534
31377
  }
30535
31378
  }
30536
31379
  async function setupContainerGit(containerName, repos, branch) {
30537
- 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, "'\\''")}'`, {
30538
31381
  stdio: "pipe",
30539
31382
  timeout: 6e4
30540
31383
  }).toString();
30541
31384
  let ghToken = "";
30542
31385
  try {
30543
- 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();
30544
31387
  } catch {
30545
31388
  }
30546
31389
  const actorName = process.env.OLAM_BOT_NAME ?? "Claude Code (olam)";
@@ -30560,7 +31403,7 @@ async function setupContainerGit(containerName, repos, branch) {
30560
31403
  continue;
30561
31404
  const ownerRepo = ghMatch[1];
30562
31405
  try {
30563
- execSync4(`gh api repos/${ownerRepo} --silent`, {
31406
+ execSync5(`gh api repos/${ownerRepo} --silent`, {
30564
31407
  stdio: "pipe",
30565
31408
  timeout: 5e3,
30566
31409
  env: { ...process.env, GH_TOKEN: ghToken }
@@ -30608,8 +31451,8 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
30608
31451
  if (!olamUserPresent) {
30609
31452
  const imageName = (() => {
30610
31453
  try {
30611
- const { execSync: execSync6 } = __require("node:child_process");
30612
- 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}}'`, {
30613
31456
  encoding: "utf8",
30614
31457
  timeout: 5e3
30615
31458
  }).trim() || "(unknown)";
@@ -30652,7 +31495,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
30652
31495
  function makeHostExecFn() {
30653
31496
  return async (cmd) => {
30654
31497
  try {
30655
- const stdout = execSync4(cmd, { encoding: "utf-8", timeout: 5e3 });
31498
+ const stdout = execSync5(cmd, { encoding: "utf-8", timeout: 5e3 });
30656
31499
  return { stdout, stderr: "", exitCode: 0 };
30657
31500
  } catch {
30658
31501
  return { stdout: "", stderr: "", exitCode: 1 };
@@ -30662,7 +31505,7 @@ function makeHostExecFn() {
30662
31505
  function makeContainerExecFn(containerName) {
30663
31506
  return async (cmd) => {
30664
31507
  try {
30665
- 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 });
30666
31509
  return { stdout: result.toString(), stderr: "", exitCode: 0 };
30667
31510
  } catch (err) {
30668
31511
  const execErr = err;
@@ -30676,7 +31519,7 @@ function makeContainerExecFn(containerName) {
30676
31519
  }
30677
31520
  function defaultDockerExec() {
30678
31521
  return (containerName, cmd) => {
30679
- const result = execSync4(
31522
+ const result = execSync5(
30680
31523
  `docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`,
30681
31524
  // Phase E E5 raise: 10min was too tight on cold-boot for atlas-core's
30682
31525
  // `rails db:create` chain (Rails 7 boot + initializer load + first
@@ -30900,8 +31743,28 @@ var WorldManager = class {
30900
31743
  throw new AuthPreflightError(preflight.verdict, preflight.message, preflight.remedy);
30901
31744
  }
30902
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
+ }
30903
31766
  const worldId = generateWorldId();
30904
- const workspacePath = path21.join(os11.homedir(), ".olam", "worlds", worldId);
31767
+ const workspacePath = path24.join(os13.homedir(), ".olam", "worlds", worldId);
30905
31768
  const portOffset = this.registry.getNextPortOffset();
30906
31769
  const branch = opts.branchName ?? `olam/${worldId}`;
30907
31770
  const repos = this.resolveReposWithWorkspace(opts);
@@ -30933,6 +31796,19 @@ var WorldManager = class {
30933
31796
  this.registry.update(worldId, { status: "error" });
30934
31797
  throw err;
30935
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
+ }
30936
31812
  try {
30937
31813
  const baseline = snapshotBaselineDiff(repos, workspacePath);
30938
31814
  console.log(formatBaselineSummary(baseline));
@@ -30957,38 +31833,38 @@ var WorldManager = class {
30957
31833
  for (const repo of repos) {
30958
31834
  if (!repo.path)
30959
31835
  continue;
30960
- const sourceRoot = repo.path.replace(/^~/, os11.homedir());
30961
- const worktreeRoot = path21.join(workspacePath, repo.name);
30962
- if (!fs17.existsSync(sourceRoot) || !fs17.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))
30963
31839
  continue;
30964
31840
  let copied = 0;
30965
31841
  for (const pattern of RUNTIME_FILE_PATTERNS) {
30966
31842
  const matches2 = [];
30967
31843
  if (pattern.includes("*")) {
30968
- const [dir, glob] = [path21.dirname(pattern), path21.basename(pattern)];
30969
- const sourceDir = path21.join(sourceRoot, dir);
30970
- if (fs17.existsSync(sourceDir)) {
31844
+ const [dir, glob] = [path24.dirname(pattern), path24.basename(pattern)];
31845
+ const sourceDir = path24.join(sourceRoot, dir);
31846
+ if (fs20.existsSync(sourceDir)) {
30971
31847
  const ext = glob.replace(/^\*+/, "");
30972
31848
  try {
30973
- for (const entry of fs17.readdirSync(sourceDir)) {
31849
+ for (const entry of fs20.readdirSync(sourceDir)) {
30974
31850
  if (ext === "" || entry.endsWith(ext))
30975
- matches2.push(path21.join(dir, entry));
31851
+ matches2.push(path24.join(dir, entry));
30976
31852
  }
30977
31853
  } catch {
30978
31854
  }
30979
31855
  }
30980
- } else if (fs17.existsSync(path21.join(sourceRoot, pattern))) {
31856
+ } else if (fs20.existsSync(path24.join(sourceRoot, pattern))) {
30981
31857
  matches2.push(pattern);
30982
31858
  }
30983
31859
  for (const rel of matches2) {
30984
- const src = path21.join(sourceRoot, rel);
30985
- const dst = path21.join(worktreeRoot, rel);
31860
+ const src = path24.join(sourceRoot, rel);
31861
+ const dst = path24.join(worktreeRoot, rel);
30986
31862
  try {
30987
- const st = fs17.statSync(src);
31863
+ const st = fs20.statSync(src);
30988
31864
  if (!st.isFile())
30989
31865
  continue;
30990
- fs17.mkdirSync(path21.dirname(dst), { recursive: true });
30991
- fs17.copyFileSync(src, dst);
31866
+ fs20.mkdirSync(path24.dirname(dst), { recursive: true });
31867
+ fs20.copyFileSync(src, dst);
30992
31868
  copied++;
30993
31869
  } catch {
30994
31870
  }
@@ -31088,7 +31964,7 @@ var WorldManager = class {
31088
31964
  try {
31089
31965
  const hostExec = makeHostExecFn();
31090
31966
  for (const repo of repos) {
31091
- const repoDir = path21.join(workspacePath, repo.name);
31967
+ const repoDir = path24.join(workspacePath, repo.name);
31092
31968
  if (repo.stack && Object.keys(repo.stack).length > 0) {
31093
31969
  preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
31094
31970
  } else {
@@ -31114,11 +31990,19 @@ var WorldManager = class {
31114
31990
  }
31115
31991
  let cacheArchOverride;
31116
31992
  if (!stackCacheHit) {
31117
- const selected = selectDevboxImage(this.config, repos);
31993
+ const selected = selectDevboxImageForWorld({
31994
+ config: this.config,
31995
+ repos,
31996
+ worldspec: opts.worldspec
31997
+ });
31118
31998
  if (selected) {
31119
31999
  selectedImage = selected.image;
31120
32000
  cacheArchOverride = selected.cacheArch;
31121
- 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
+ }
31122
32006
  } else {
31123
32007
  const hasRailsRepo = repos.some((r) => r.type === "rails");
31124
32008
  if (hasRailsRepo) {
@@ -31138,17 +32022,18 @@ var WorldManager = class {
31138
32022
  }
31139
32023
  }
31140
32024
  const appPortUrls = appPorts.map((ap) => {
31141
- const hostPort = ap.port + 1e4 + portOffset;
32025
+ const override = runbookPortOverrides[ap.name]?.["app"];
32026
+ const hostPort = override !== void 0 ? override : ap.port + 1e4 + portOffset;
31142
32027
  return { repoName: ap.name, internalPort: ap.port, hostPort, url: `http://localhost:${hostPort}` };
31143
32028
  });
31144
32029
  try {
31145
32030
  const worldEnv = {};
31146
32031
  if (opts.task)
31147
32032
  worldEnv.OLAM_TASK = opts.task;
31148
- const r2CredsPath = path21.join(os11.homedir(), ".olam", "r2-credentials.json");
31149
- if (fs17.existsSync(r2CredsPath)) {
32033
+ const r2CredsPath = path24.join(os13.homedir(), ".olam", "r2-credentials.json");
32034
+ if (fs20.existsSync(r2CredsPath)) {
31150
32035
  try {
31151
- const r2Raw = fs17.readFileSync(r2CredsPath, "utf-8").trim();
32036
+ const r2Raw = fs20.readFileSync(r2CredsPath, "utf-8").trim();
31152
32037
  if (r2Raw.length > 0) {
31153
32038
  const r2 = JSON.parse(r2Raw);
31154
32039
  if (typeof r2.account_id === "string")
@@ -31165,10 +32050,10 @@ var WorldManager = class {
31165
32050
  } catch {
31166
32051
  }
31167
32052
  }
31168
- const keysYamlPath = path21.join(os11.homedir(), ".olam", "keys.yaml");
31169
- if (fs17.existsSync(keysYamlPath)) {
32053
+ const keysYamlPath = path24.join(os13.homedir(), ".olam", "keys.yaml");
32054
+ if (fs20.existsSync(keysYamlPath)) {
31170
32055
  try {
31171
- const keysRaw = fs17.readFileSync(keysYamlPath, "utf-8").trim();
32056
+ const keysRaw = fs20.readFileSync(keysYamlPath, "utf-8").trim();
31172
32057
  if (keysRaw.length > 0) {
31173
32058
  const parsed = YAML3.parse(keysRaw);
31174
32059
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
@@ -31204,6 +32089,18 @@ var WorldManager = class {
31204
32089
  seenFromRepo.set(k, repo.name);
31205
32090
  }
31206
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
+ }
31207
32104
  if (repoSecretUrls.length > 0) {
31208
32105
  const { env: gcpSecrets, fileWrites } = fetchSecretsForRepos(repoSecretUrls);
31209
32106
  for (const [k, v] of Object.entries(gcpSecrets)) {
@@ -31215,10 +32112,10 @@ var WorldManager = class {
31215
32112
  worldEnv[k] = v;
31216
32113
  }
31217
32114
  for (const { repoName, relativePath, content } of fileWrites) {
31218
- const absPath = path21.join(workspacePath, repoName, relativePath);
32115
+ const absPath = path24.join(workspacePath, repoName, relativePath);
31219
32116
  try {
31220
- fs17.mkdirSync(path21.dirname(absPath), { recursive: true });
31221
- fs17.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", {
31222
32119
  mode: 384
31223
32120
  });
31224
32121
  console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
@@ -31324,6 +32221,44 @@ var WorldManager = class {
31324
32221
  const msg = err instanceof Error ? err.message : String(err);
31325
32222
  console.warn(`[WorldManager] stack installation failed: ${msg}`);
31326
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
+ }
31327
32262
  try {
31328
32263
  await runManifestRuntime(containerName, worldId, enrichedRepos, this.dockerExec);
31329
32264
  } catch (err) {
@@ -31348,12 +32283,30 @@ var WorldManager = class {
31348
32283
  console.warn(`[manifest] step 4e/4f/4g non-fatal failure: ${msg}`);
31349
32284
  }
31350
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
+ }
31351
32304
  for (const repo of repos) {
31352
32305
  if (repo.setup_commands.length > 0) {
31353
32306
  const escapedDir = `/home/olam/workspace/${repo.name.replace(/["$`\\]/g, "\\$&")}`;
31354
32307
  for (const cmd of repo.setup_commands) {
31355
32308
  try {
31356
- 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 });
31357
32310
  } catch (err) {
31358
32311
  const msg = err instanceof Error ? err.message : String(err);
31359
32312
  console.warn(`[WorldManager] setup command failed for ${repo.name}: ${msg}`);
@@ -31363,14 +32316,14 @@ var WorldManager = class {
31363
32316
  }
31364
32317
  if (credentialsInjected.claude) {
31365
32318
  try {
31366
- 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 });
31367
32320
  } catch {
31368
32321
  }
31369
32322
  if (opts.task) {
31370
32323
  let taskWithPolicies = opts.task;
31371
32324
  try {
31372
32325
  const allPolicies = repos.flatMap((repo) => {
31373
- const repoWorktree = path21.join(workspacePath, repo.name);
32326
+ const repoWorktree = path24.join(workspacePath, repo.name);
31374
32327
  return loadPolicies(repoWorktree);
31375
32328
  });
31376
32329
  const seen = /* @__PURE__ */ new Set();
@@ -31384,11 +32337,11 @@ var WorldManager = class {
31384
32337
  const brief = formatPoliciesBrief(uniquePolicies);
31385
32338
  taskWithPolicies = `${brief}## Your task
31386
32339
  ${opts.task}`;
31387
- 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 });
31388
32341
  for (const repo of repos) {
31389
- const policiesDir = path21.join(workspacePath, repo.name, ".olam", "policies");
31390
- if (fs17.existsSync(policiesDir)) {
31391
- 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 });
31392
32345
  }
31393
32346
  }
31394
32347
  }
@@ -31398,7 +32351,7 @@ ${opts.task}`;
31398
32351
  }
31399
32352
  try {
31400
32353
  const payload = JSON.stringify({ prompt: taskWithPolicies }).replace(/'/g, "'\\''");
31401
- 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 });
31402
32355
  console.log("[world] Task auto-dispatched");
31403
32356
  } catch {
31404
32357
  }
@@ -31409,6 +32362,19 @@ ${opts.task}`;
31409
32362
  const terminalPort = 17681 + portOffset;
31410
32363
  const dashboardUrl = `http://localhost:${dashboardPort}`;
31411
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
+ }
31412
32378
  return {
31413
32379
  ...metadata,
31414
32380
  status: "running",
@@ -31466,8 +32432,8 @@ ${opts.task}`;
31466
32432
  } catch {
31467
32433
  }
31468
32434
  try {
31469
- fs17.rmSync(world.workspacePath, { recursive: true, force: true });
31470
- if (fs17.existsSync(world.workspacePath)) {
32435
+ fs20.rmSync(world.workspacePath, { recursive: true, force: true });
32436
+ if (fs20.existsSync(world.workspacePath)) {
31471
32437
  console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
31472
32438
  }
31473
32439
  } catch (err) {
@@ -31552,14 +32518,14 @@ ${opts.task}`;
31552
32518
  return names.map((name) => this.config.repos.find((r) => r.name === name)).filter((r) => r !== void 0);
31553
32519
  }
31554
32520
  transportPlanFile(planFilePath, workspacePath, repoNames) {
31555
- const planContent = fs17.readFileSync(planFilePath, "utf-8");
31556
- const planFileName = path21.basename(planFilePath);
32521
+ const planContent = fs20.readFileSync(planFilePath, "utf-8");
32522
+ const planFileName = path24.basename(planFilePath);
31557
32523
  const targetRepo = repoNames[0];
31558
32524
  if (!targetRepo)
31559
32525
  return;
31560
- const plansDir = path21.join(workspacePath, targetRepo, "docs", "plans");
31561
- fs17.mkdirSync(plansDir, { recursive: true });
31562
- fs17.writeFileSync(path21.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);
31563
32529
  }
31564
32530
  resolveServices(repos) {
31565
32531
  const services = [];
@@ -31644,8 +32610,8 @@ import * as http2 from "node:http";
31644
32610
 
31645
32611
  // ../core/dist/dashboard/server.js
31646
32612
  import * as http from "node:http";
31647
- import * as fs18 from "node:fs";
31648
- import * as path22 from "node:path";
32613
+ import * as fs21 from "node:fs";
32614
+ import * as path25 from "node:path";
31649
32615
  import { fileURLToPath as fileURLToPath2 } from "node:url";
31650
32616
 
31651
32617
  // ../core/dist/dashboard/serialize.js
@@ -31980,7 +32946,7 @@ function notFound(res) {
31980
32946
  }
31981
32947
  function openThoughtStore(workspacePath) {
31982
32948
  const dbPath = getWorldDbPath(workspacePath);
31983
- if (!fs18.existsSync(dbPath))
32949
+ if (!fs21.existsSync(dbPath))
31984
32950
  return null;
31985
32951
  return new ThoughtLocalStore(dbPath);
31986
32952
  }
@@ -32151,13 +33117,13 @@ function findSessionInWorld(registry2, sessionId) {
32151
33117
  }
32152
33118
  function createDashboardServer(opts) {
32153
33119
  const { port, registry: registry2 } = opts;
32154
- const thisDir = path22.dirname(fileURLToPath2(import.meta.url));
32155
- const defaultPublicDir = path22.resolve(thisDir, "../../../control-plane/public");
33120
+ const thisDir = path25.dirname(fileURLToPath2(import.meta.url));
33121
+ const defaultPublicDir = path25.resolve(thisDir, "../../../control-plane/public");
32156
33122
  const publicDir = opts.publicDir ?? defaultPublicDir;
32157
- let hasPublicDir = fs18.existsSync(publicDir);
33123
+ let hasPublicDir = fs21.existsSync(publicDir);
32158
33124
  const server = http.createServer((req, res) => {
32159
33125
  if (!hasPublicDir) {
32160
- hasPublicDir = fs18.existsSync(publicDir);
33126
+ hasPublicDir = fs21.existsSync(publicDir);
32161
33127
  }
32162
33128
  const host = req.headers.host ?? `localhost:${port}`;
32163
33129
  const url = new URL(req.url ?? "/", `http://${host}`);
@@ -32431,22 +33397,22 @@ function createDashboardServer(opts) {
32431
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>`);
32432
33398
  return;
32433
33399
  }
32434
- let filePath = path22.join(publicDir, pathname === "/" ? "index.html" : pathname);
33400
+ let filePath = path25.join(publicDir, pathname === "/" ? "index.html" : pathname);
32435
33401
  if (!filePath.startsWith(publicDir)) {
32436
33402
  notFound(res);
32437
33403
  return;
32438
33404
  }
32439
- if (fs18.existsSync(filePath) && fs18.statSync(filePath).isFile()) {
32440
- const ext = path22.extname(filePath);
33405
+ if (fs21.existsSync(filePath) && fs21.statSync(filePath).isFile()) {
33406
+ const ext = path25.extname(filePath);
32441
33407
  const contentType = MIME[ext] ?? "application/octet-stream";
32442
33408
  res.writeHead(200, { "Content-Type": contentType });
32443
- fs18.createReadStream(filePath).pipe(res);
33409
+ fs21.createReadStream(filePath).pipe(res);
32444
33410
  return;
32445
33411
  }
32446
- filePath = path22.join(publicDir, "index.html");
32447
- if (fs18.existsSync(filePath)) {
33412
+ filePath = path25.join(publicDir, "index.html");
33413
+ if (fs21.existsSync(filePath)) {
32448
33414
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
32449
- fs18.createReadStream(filePath).pipe(res);
33415
+ fs21.createReadStream(filePath).pipe(res);
32450
33416
  return;
32451
33417
  }
32452
33418
  notFound(res);
@@ -32456,17 +33422,17 @@ function createDashboardServer(opts) {
32456
33422
  }
32457
33423
 
32458
33424
  // ../core/dist/dashboard/state.js
32459
- import * as fs19 from "node:fs";
32460
- import * as os12 from "node:os";
32461
- import * as path23 from "node:path";
32462
- var STATE_PATH = path23.join(os12.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");
32463
33429
  function saveDashboardState(state) {
32464
- fs19.mkdirSync(path23.dirname(STATE_PATH), { recursive: true });
32465
- fs19.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));
32466
33432
  }
32467
33433
  function loadDashboardState() {
32468
33434
  try {
32469
- const raw = fs19.readFileSync(STATE_PATH, "utf-8");
33435
+ const raw = fs22.readFileSync(STATE_PATH, "utf-8");
32470
33436
  return JSON.parse(raw);
32471
33437
  } catch {
32472
33438
  return null;
@@ -32474,7 +33440,7 @@ function loadDashboardState() {
32474
33440
  }
32475
33441
  function clearDashboardState() {
32476
33442
  try {
32477
- fs19.unlinkSync(STATE_PATH);
33443
+ fs22.unlinkSync(STATE_PATH);
32478
33444
  } catch {
32479
33445
  }
32480
33446
  }
@@ -32492,19 +33458,19 @@ function isDashboardRunning() {
32492
33458
  }
32493
33459
 
32494
33460
  // ../core/dist/dashboard/tunnel.js
32495
- import { spawn, execSync as execSync5 } from "node:child_process";
33461
+ import { spawn as spawn2, execSync as execSync6 } from "node:child_process";
32496
33462
  var tunnelProcess = null;
32497
33463
  function isCloudflaredAvailable() {
32498
33464
  try {
32499
- execSync5("which cloudflared", { stdio: "ignore" });
33465
+ execSync6("which cloudflared", { stdio: "ignore" });
32500
33466
  return true;
32501
33467
  } catch {
32502
33468
  return false;
32503
33469
  }
32504
33470
  }
32505
33471
  function startTunnel(port) {
32506
- return new Promise((resolve6, reject2) => {
32507
- 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}`], {
32508
33474
  stdio: ["ignore", "pipe", "pipe"],
32509
33475
  detached: false
32510
33476
  });
@@ -32525,7 +33491,7 @@ function startTunnel(port) {
32525
33491
  if (match) {
32526
33492
  resolved = true;
32527
33493
  clearTimeout(timeout);
32528
- resolve6(match[0]);
33494
+ resolve8(match[0]);
32529
33495
  }
32530
33496
  }
32531
33497
  child.stdout?.on("data", scan);
@@ -32593,8 +33559,8 @@ var DashboardManager = class {
32593
33559
  }
32594
33560
  throw err;
32595
33561
  }
32596
- await new Promise((resolve6, reject2) => {
32597
- this.server.on("listening", resolve6);
33562
+ await new Promise((resolve8, reject2) => {
33563
+ this.server.on("listening", resolve8);
32598
33564
  this.server.on("error", reject2);
32599
33565
  });
32600
33566
  this.info = { localUrl: `http://localhost:${port}` };
@@ -32640,8 +33606,8 @@ var DashboardManager = class {
32640
33606
  async stop() {
32641
33607
  stopTunnel();
32642
33608
  if (this.server) {
32643
- await new Promise((resolve6) => {
32644
- this.server.close(() => resolve6());
33609
+ await new Promise((resolve8) => {
33610
+ this.server.close(() => resolve8());
32645
33611
  });
32646
33612
  this.server = null;
32647
33613
  }
@@ -32754,8 +33720,8 @@ var PleriClient = class {
32754
33720
  };
32755
33721
 
32756
33722
  // ../mcp-server/src/env-loader.ts
32757
- import { readFileSync as readFileSync15, existsSync as existsSync19, statSync as statSync6 } from "node:fs";
32758
- 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";
32759
33725
  var PROJECT_MARKERS = [
32760
33726
  ".olam/config.yaml",
32761
33727
  ".olam/config.yml",
@@ -32763,30 +33729,30 @@ var PROJECT_MARKERS = [
32763
33729
  "olam.yml"
32764
33730
  ];
32765
33731
  function findProjectRoot2(startDir) {
32766
- let dir = resolve5(startDir);
32767
- const root = resolve5("/");
33732
+ let dir = resolve7(startDir);
33733
+ const root = resolve7("/");
32768
33734
  while (true) {
32769
33735
  for (const marker of PROJECT_MARKERS) {
32770
- if (existsSync19(join25(dir, marker))) return dir;
33736
+ if (existsSync21(join27(dir, marker))) return dir;
32771
33737
  }
32772
- const pkg = join25(dir, "package.json");
32773
- if (existsSync19(pkg)) {
33738
+ const pkg = join27(dir, "package.json");
33739
+ if (existsSync21(pkg)) {
32774
33740
  try {
32775
- const json = JSON.parse(readFileSync15(pkg, "utf8"));
33741
+ const json = JSON.parse(readFileSync16(pkg, "utf8"));
32776
33742
  const isOlamWorkspace = typeof json.name === "string" && json.name.startsWith("@olam/");
32777
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/"));
32778
33744
  if (isOlamWorkspace || hasOlamDep) return dir;
32779
33745
  } catch {
32780
33746
  }
32781
33747
  }
32782
- const parent = dirname13(dir);
33748
+ const parent = dirname15(dir);
32783
33749
  if (parent === dir || parent === root) return null;
32784
33750
  dir = parent;
32785
33751
  }
32786
33752
  }
32787
- function parseEnvFile(path24) {
33753
+ function parseEnvFile(path27) {
32788
33754
  const out = {};
32789
- const raw = readFileSync15(path24, "utf8");
33755
+ const raw = readFileSync16(path27, "utf8");
32790
33756
  for (const line of raw.split(/\r?\n/)) {
32791
33757
  const trimmed = line.trim();
32792
33758
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -32809,8 +33775,8 @@ function loadProjectEnv(startDir = process.cwd()) {
32809
33775
  const filesRead = [];
32810
33776
  const merged = {};
32811
33777
  for (const name of [".env", ".env.local"]) {
32812
- const p = join25(root, name);
32813
- if (existsSync19(p) && statSync6(p).isFile()) {
33778
+ const p = join27(root, name);
33779
+ if (existsSync21(p) && statSync7(p).isFile()) {
32814
33780
  Object.assign(merged, parseEnvFile(p));
32815
33781
  filesRead.push(p);
32816
33782
  }
@@ -32880,7 +33846,7 @@ async function main() {
32880
33846
  initError = err instanceof Error ? err : new Error(String(err));
32881
33847
  logger.error("Failed to initialize Olam", { error: initError.message });
32882
33848
  }
32883
- const server = createServer3(ctx, initError);
33849
+ const server = createServer4(ctx, initError);
32884
33850
  const transport = new StdioServerTransport();
32885
33851
  await server.connect(transport);
32886
33852
  logger.info("Olam MCP server running on stdio transport");