@pleri/olam-cli 0.1.69 → 0.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-version.d.ts +16 -0
- package/dist/cli-version.d.ts.map +1 -0
- package/dist/cli-version.js +39 -0
- package/dist/cli-version.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +2 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/host-cp.d.ts.map +1 -1
- package/dist/commands/host-cp.js +10 -0
- package/dist/commands/host-cp.js.map +1 -1
- package/dist/commands/runbooks.d.ts +13 -0
- package/dist/commands/runbooks.d.ts.map +1 -0
- package/dist/commands/runbooks.js +189 -0
- package/dist/commands/runbooks.js.map +1 -0
- package/dist/commands/world-snapshot.d.ts +1 -0
- package/dist/commands/world-snapshot.d.ts.map +1 -1
- package/dist/commands/world-snapshot.js +126 -1
- package/dist/commands/world-snapshot.js.map +1 -1
- package/dist/commands/worldspec/compile.d.ts +20 -0
- package/dist/commands/worldspec/compile.d.ts.map +1 -0
- package/dist/commands/worldspec/compile.js +130 -0
- package/dist/commands/worldspec/compile.js.map +1 -0
- package/dist/commands/worldspec/index.d.ts +12 -0
- package/dist/commands/worldspec/index.d.ts.map +1 -0
- package/dist/commands/worldspec/index.js +23 -0
- package/dist/commands/worldspec/index.js.map +1 -0
- package/dist/commands/worldspec/init.d.ts +15 -0
- package/dist/commands/worldspec/init.d.ts.map +1 -0
- package/dist/commands/worldspec/init.js +166 -0
- package/dist/commands/worldspec/init.js.map +1 -0
- package/dist/commands/worldspec/schema.d.ts +11 -0
- package/dist/commands/worldspec/schema.d.ts.map +1 -0
- package/dist/commands/worldspec/schema.js +55 -0
- package/dist/commands/worldspec/schema.js.map +1 -0
- package/dist/commands/worldspec/validate.d.ts +15 -0
- package/dist/commands/worldspec/validate.d.ts.map +1 -0
- package/dist/commands/worldspec/validate.js +66 -0
- package/dist/commands/worldspec/validate.js.map +1 -0
- package/dist/exit-codes.d.ts +32 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +32 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/image-digests.json +5 -5
- package/dist/index.js +4726 -1231
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +1424 -321
- package/host-cp/compose.yaml +6 -0
- package/host-cp/src/global-config-source.mjs +71 -0
- package/host-cp/src/listening-server-poller.mjs +1 -1
- package/host-cp/src/plan-orchestrator.mjs +20 -1
- package/host-cp/src/port-bridge-manager.mjs +1 -1
- package/host-cp/src/server.mjs +46 -7
- package/host-cp/src/version-status.mjs +36 -0
- package/package.json +4 -2
- package/dist/__tests__/audit-publish-deps-contract.test.d.ts +0 -26
- package/dist/__tests__/audit-publish-deps-contract.test.d.ts.map +0 -1
- package/dist/__tests__/audit-publish-deps-contract.test.js +0 -86
- package/dist/__tests__/audit-publish-deps-contract.test.js.map +0 -1
- package/dist/__tests__/auth-status.test.d.ts +0 -2
- package/dist/__tests__/auth-status.test.d.ts.map +0 -1
- package/dist/__tests__/auth-status.test.js +0 -291
- package/dist/__tests__/auth-status.test.js.map +0 -1
- package/dist/__tests__/auth-upgrade.test.d.ts +0 -9
- package/dist/__tests__/auth-upgrade.test.d.ts.map +0 -1
- package/dist/__tests__/auth-upgrade.test.js +0 -397
- package/dist/__tests__/auth-upgrade.test.js.map +0 -1
- package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts +0 -22
- package/dist/__tests__/bootstrap-tag-mcp-auth.test.d.ts.map +0 -1
- package/dist/__tests__/bootstrap-tag-mcp-auth.test.js +0 -63
- package/dist/__tests__/bootstrap-tag-mcp-auth.test.js.map +0 -1
- package/dist/__tests__/cli-mcp-revoke.test.d.ts +0 -8
- package/dist/__tests__/cli-mcp-revoke.test.d.ts.map +0 -1
- package/dist/__tests__/cli-mcp-revoke.test.js +0 -124
- package/dist/__tests__/cli-mcp-revoke.test.js.map +0 -1
- package/dist/__tests__/config.test.d.ts +0 -2
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -95
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/create-app-urls.test.d.ts +0 -2
- package/dist/__tests__/create-app-urls.test.d.ts.map +0 -1
- package/dist/__tests__/create-app-urls.test.js +0 -102
- package/dist/__tests__/create-app-urls.test.js.map +0 -1
- package/dist/__tests__/docker-host.test.d.ts +0 -14
- package/dist/__tests__/docker-host.test.d.ts.map +0 -1
- package/dist/__tests__/docker-host.test.js +0 -109
- package/dist/__tests__/docker-host.test.js.map +0 -1
- package/dist/__tests__/enter.test.d.ts +0 -2
- package/dist/__tests__/enter.test.d.ts.map +0 -1
- package/dist/__tests__/enter.test.js +0 -90
- package/dist/__tests__/enter.test.js.map +0 -1
- package/dist/__tests__/help-output.test.d.ts +0 -2
- package/dist/__tests__/help-output.test.d.ts.map +0 -1
- package/dist/__tests__/help-output.test.js +0 -74
- package/dist/__tests__/help-output.test.js.map +0 -1
- package/dist/__tests__/host-cp-gh-token.test.d.ts +0 -9
- package/dist/__tests__/host-cp-gh-token.test.d.ts.map +0 -1
- package/dist/__tests__/host-cp-gh-token.test.js +0 -119
- package/dist/__tests__/host-cp-gh-token.test.js.map +0 -1
- package/dist/__tests__/host-cp.test.d.ts +0 -9
- package/dist/__tests__/host-cp.test.d.ts.map +0 -1
- package/dist/__tests__/host-cp.test.js +0 -327
- package/dist/__tests__/host-cp.test.js.map +0 -1
- package/dist/__tests__/image-presence.test.d.ts +0 -2
- package/dist/__tests__/image-presence.test.d.ts.map +0 -1
- package/dist/__tests__/image-presence.test.js +0 -44
- package/dist/__tests__/image-presence.test.js.map +0 -1
- package/dist/__tests__/install-root.test.d.ts +0 -2
- package/dist/__tests__/install-root.test.d.ts.map +0 -1
- package/dist/__tests__/install-root.test.js +0 -119
- package/dist/__tests__/install-root.test.js.map +0 -1
- package/dist/__tests__/keys.test.d.ts +0 -9
- package/dist/__tests__/keys.test.d.ts.map +0 -1
- package/dist/__tests__/keys.test.js +0 -145
- package/dist/__tests__/keys.test.js.map +0 -1
- package/dist/__tests__/logs.test.d.ts +0 -9
- package/dist/__tests__/logs.test.d.ts.map +0 -1
- package/dist/__tests__/logs.test.js +0 -124
- package/dist/__tests__/logs.test.js.map +0 -1
- package/dist/__tests__/mcp-import.test.d.ts +0 -11
- package/dist/__tests__/mcp-import.test.d.ts.map +0 -1
- package/dist/__tests__/mcp-import.test.js +0 -134
- package/dist/__tests__/mcp-import.test.js.map +0 -1
- package/dist/__tests__/protocol-version.test.d.ts +0 -2
- package/dist/__tests__/protocol-version.test.d.ts.map +0 -1
- package/dist/__tests__/protocol-version.test.js +0 -170
- package/dist/__tests__/protocol-version.test.js.map +0 -1
- package/dist/__tests__/ps.test.d.ts +0 -2
- package/dist/__tests__/ps.test.d.ts.map +0 -1
- package/dist/__tests__/ps.test.js +0 -172
- package/dist/__tests__/ps.test.js.map +0 -1
- package/dist/__tests__/registry-allowlist.test.d.ts +0 -2
- package/dist/__tests__/registry-allowlist.test.d.ts.map +0 -1
- package/dist/__tests__/registry-allowlist.test.js +0 -129
- package/dist/__tests__/registry-allowlist.test.js.map +0 -1
- package/dist/__tests__/services.test.d.ts +0 -8
- package/dist/__tests__/services.test.d.ts.map +0 -1
- package/dist/__tests__/services.test.js +0 -185
- package/dist/__tests__/services.test.js.map +0 -1
- package/dist/__tests__/status-app-urls.test.d.ts +0 -2
- package/dist/__tests__/status-app-urls.test.d.ts.map +0 -1
- package/dist/__tests__/status-app-urls.test.js +0 -125
- package/dist/__tests__/status-app-urls.test.js.map +0 -1
- package/dist/__tests__/upgrade-gh-token-contract.test.d.ts +0 -19
- package/dist/__tests__/upgrade-gh-token-contract.test.d.ts.map +0 -1
- package/dist/__tests__/upgrade-gh-token-contract.test.js +0 -63
- package/dist/__tests__/upgrade-gh-token-contract.test.js.map +0 -1
- package/dist/__tests__/upgrade.test.d.ts +0 -9
- package/dist/__tests__/upgrade.test.d.ts.map +0 -1
- package/dist/__tests__/upgrade.test.js +0 -586
- package/dist/__tests__/upgrade.test.js.map +0 -1
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts +0 -6
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts.map +0 -1
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js +0 -26
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js.map +0 -1
- package/dist/commands/__tests__/begin.test.d.ts +0 -7
- package/dist/commands/__tests__/begin.test.d.ts.map +0 -1
- package/dist/commands/__tests__/begin.test.js +0 -72
- package/dist/commands/__tests__/begin.test.js.map +0 -1
- package/dist/commands/__tests__/bootstrap.test.d.ts +0 -2
- package/dist/commands/__tests__/bootstrap.test.d.ts.map +0 -1
- package/dist/commands/__tests__/bootstrap.test.js +0 -370
- package/dist/commands/__tests__/bootstrap.test.js.map +0 -1
- package/dist/commands/__tests__/carry-uncommitted.test.d.ts +0 -14
- package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +0 -1
- package/dist/commands/__tests__/carry-uncommitted.test.js +0 -83
- package/dist/commands/__tests__/carry-uncommitted.test.js.map +0 -1
- package/dist/commands/__tests__/clean.test.d.ts +0 -9
- package/dist/commands/__tests__/clean.test.d.ts.map +0 -1
- package/dist/commands/__tests__/clean.test.js +0 -105
- package/dist/commands/__tests__/clean.test.js.map +0 -1
- package/dist/commands/__tests__/crystallize.test.d.ts +0 -2
- package/dist/commands/__tests__/crystallize.test.d.ts.map +0 -1
- package/dist/commands/__tests__/crystallize.test.js +0 -133
- package/dist/commands/__tests__/crystallize.test.js.map +0 -1
- package/dist/commands/__tests__/diagnose.test.d.ts +0 -9
- package/dist/commands/__tests__/diagnose.test.d.ts.map +0 -1
- package/dist/commands/__tests__/diagnose.test.js +0 -108
- package/dist/commands/__tests__/diagnose.test.js.map +0 -1
- package/dist/commands/__tests__/openHostCpUrl.test.d.ts +0 -2
- package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +0 -1
- package/dist/commands/__tests__/openHostCpUrl.test.js +0 -63
- package/dist/commands/__tests__/openHostCpUrl.test.js.map +0 -1
- package/dist/commands/__tests__/refresh.test.d.ts +0 -13
- package/dist/commands/__tests__/refresh.test.d.ts.map +0 -1
- package/dist/commands/__tests__/refresh.test.js +0 -170
- package/dist/commands/__tests__/refresh.test.js.map +0 -1
- package/dist/commands/__tests__/status.test.d.ts +0 -8
- package/dist/commands/__tests__/status.test.d.ts.map +0 -1
- package/dist/commands/__tests__/status.test.js +0 -62
- package/dist/commands/__tests__/status.test.js.map +0 -1
- package/dist/commands/__tests__/stop.test.d.ts +0 -5
- package/dist/commands/__tests__/stop.test.d.ts.map +0 -1
- package/dist/commands/__tests__/stop.test.js +0 -30
- package/dist/commands/__tests__/stop.test.js.map +0 -1
- package/dist/commands/__tests__/update.test.d.ts +0 -7
- package/dist/commands/__tests__/update.test.d.ts.map +0 -1
- package/dist/commands/__tests__/update.test.js +0 -224
- package/dist/commands/__tests__/update.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.all-three.test.d.ts +0 -19
- package/dist/commands/__tests__/upgrade.all-three.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.all-three.test.js +0 -80
- package/dist/commands/__tests__/upgrade.all-three.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.compose-path.test.d.ts +0 -20
- package/dist/commands/__tests__/upgrade.compose-path.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.compose-path.test.js +0 -140
- package/dist/commands/__tests__/upgrade.compose-path.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.history.test.d.ts +0 -15
- package/dist/commands/__tests__/upgrade.history.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.history.test.js +0 -199
- package/dist/commands/__tests__/upgrade.history.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.lock.test.d.ts +0 -15
- package/dist/commands/__tests__/upgrade.lock.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.lock.test.js +0 -253
- package/dist/commands/__tests__/upgrade.lock.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts +0 -21
- package/dist/commands/__tests__/upgrade.olam-tag.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.olam-tag.test.js +0 -114
- package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.poll.test.d.ts +0 -14
- package/dist/commands/__tests__/upgrade.poll.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.poll.test.js +0 -136
- package/dist/commands/__tests__/upgrade.poll.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.recreate.test.d.ts +0 -17
- package/dist/commands/__tests__/upgrade.recreate.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.recreate.test.js +0 -83
- package/dist/commands/__tests__/upgrade.recreate.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.rollback.test.d.ts +0 -12
- package/dist/commands/__tests__/upgrade.rollback.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.rollback.test.js +0 -255
- package/dist/commands/__tests__/upgrade.rollback.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts +0 -12
- package/dist/commands/__tests__/upgrade.sha-capture.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.sha-capture.test.js +0 -63
- package/dist/commands/__tests__/upgrade.sha-capture.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.smoke.test.d.ts +0 -19
- package/dist/commands/__tests__/upgrade.smoke.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.smoke.test.js +0 -87
- package/dist/commands/__tests__/upgrade.smoke.test.js.map +0 -1
- package/dist/commands/__tests__/upgrade.swap.test.d.ts +0 -19
- package/dist/commands/__tests__/upgrade.swap.test.d.ts.map +0 -1
- package/dist/commands/__tests__/upgrade.swap.test.js +0 -312
- package/dist/commands/__tests__/upgrade.swap.test.js.map +0 -1
- package/dist/commands/__tests__/world-upgrade.test.d.ts +0 -8
- package/dist/commands/__tests__/world-upgrade.test.d.ts.map +0 -1
- package/dist/commands/__tests__/world-upgrade.test.js +0 -73
- package/dist/commands/__tests__/world-upgrade.test.js.map +0 -1
- package/dist/lib/__tests__/symlink-reconcile.test.d.ts +0 -2
- package/dist/lib/__tests__/symlink-reconcile.test.d.ts.map +0 -1
- package/dist/lib/__tests__/symlink-reconcile.test.js +0 -106
- package/dist/lib/__tests__/symlink-reconcile.test.js.map +0 -1
package/dist/mcp-server.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
501
|
-
this.code = optimizeExpr(this.code, names,
|
|
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,
|
|
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,
|
|
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,
|
|
588
|
+
optimizeNames(names, constants2) {
|
|
589
589
|
var _a;
|
|
590
|
-
this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
591
|
-
if (!(super.optimizeNames(names,
|
|
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,
|
|
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,
|
|
617
|
-
if (!super.optimizeNames(names,
|
|
616
|
+
optimizeNames(names, constants2) {
|
|
617
|
+
if (!super.optimizeNames(names, constants2))
|
|
618
618
|
return;
|
|
619
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
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,
|
|
656
|
-
if (!super.optimizeNames(names,
|
|
655
|
+
optimizeNames(names, constants2) {
|
|
656
|
+
if (!super.optimizeNames(names, constants2))
|
|
657
657
|
return;
|
|
658
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
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,
|
|
700
|
+
optimizeNames(names, constants2) {
|
|
701
701
|
var _a, _b;
|
|
702
|
-
super.optimizeNames(names,
|
|
703
|
-
(_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names,
|
|
704
|
-
(_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names,
|
|
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,
|
|
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 =
|
|
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 &&
|
|
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 =
|
|
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
|
|
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(
|
|
3232
|
-
let input =
|
|
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 [
|
|
3432
|
-
wsComponent.path =
|
|
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
|
|
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,
|
|
3597
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
3598
3598
|
const target = {};
|
|
3599
3599
|
if (!skipNormalization) {
|
|
3600
3600
|
base = parse3(serialize(base, options), options);
|
|
3601
|
-
|
|
3601
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
3602
3602
|
}
|
|
3603
3603
|
options = options || {};
|
|
3604
|
-
if (!options.tolerant &&
|
|
3605
|
-
target.scheme =
|
|
3606
|
-
target.userinfo =
|
|
3607
|
-
target.host =
|
|
3608
|
-
target.port =
|
|
3609
|
-
target.path = removeDotSegments(
|
|
3610
|
-
target.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 (
|
|
3613
|
-
target.userinfo =
|
|
3614
|
-
target.host =
|
|
3615
|
-
target.port =
|
|
3616
|
-
target.path = removeDotSegments(
|
|
3617
|
-
target.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 (!
|
|
3619
|
+
if (!relative3.path) {
|
|
3620
3620
|
target.path = base.path;
|
|
3621
|
-
if (
|
|
3622
|
-
target.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 (
|
|
3628
|
-
target.path = removeDotSegments(
|
|
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 = "/" +
|
|
3631
|
+
target.path = "/" + relative3.path;
|
|
3632
3632
|
} else if (!base.path) {
|
|
3633
|
-
target.path =
|
|
3633
|
+
target.path = relative3.path;
|
|
3634
3634
|
} else {
|
|
3635
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
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 =
|
|
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 =
|
|
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:
|
|
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,
|
|
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,
|
|
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,
|
|
7006
|
-
if (!
|
|
7005
|
+
function getElementAtPath(obj, path27) {
|
|
7006
|
+
if (!path27)
|
|
7007
7007
|
return obj;
|
|
7008
|
-
return
|
|
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(
|
|
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(
|
|
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((
|
|
12917
|
+
return new Promise((resolve8) => {
|
|
12918
12918
|
const json = serializeMessage(message);
|
|
12919
12919
|
if (this._stdout.write(json)) {
|
|
12920
|
-
|
|
12920
|
+
resolve8();
|
|
12921
12921
|
} else {
|
|
12922
|
-
this._stdout.once("drain",
|
|
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:
|
|
13407
|
-
const fullPath = [...
|
|
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,
|
|
13523
|
+
constructor(parent, value, path27, key) {
|
|
13524
13524
|
this._cachedPath = [];
|
|
13525
13525
|
this.parent = parent;
|
|
13526
13526
|
this.data = value;
|
|
13527
|
-
this._path =
|
|
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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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(
|
|
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((
|
|
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,
|
|
21369
|
-
const url = `${this.baseUrl}${
|
|
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,
|
|
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((
|
|
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((
|
|
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:
|
|
22195
|
-
const { default:
|
|
22196
|
-
const { default:
|
|
22197
|
-
const tokenPath =
|
|
22198
|
-
if (
|
|
22199
|
-
const token =
|
|
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,
|
|
22394
|
+
function refineForbiddenKeys(value, path27, ctx, rejectSource) {
|
|
22393
22395
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
22394
22396
|
return;
|
|
22395
22397
|
}
|
|
@@ -22397,12 +22399,12 @@ function refineForbiddenKeys(value, path23, ctx, rejectSource) {
|
|
|
22397
22399
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
22398
22400
|
ctx.addIssue({
|
|
22399
22401
|
code: external_exports.ZodIssueCode.custom,
|
|
22400
|
-
path: [...
|
|
22402
|
+
path: [...path27, key],
|
|
22401
22403
|
message: `forbidden key "${key}" (prototype-pollution surface)`
|
|
22402
22404
|
});
|
|
22403
22405
|
continue;
|
|
22404
22406
|
}
|
|
22405
|
-
if (rejectSource &&
|
|
22407
|
+
if (rejectSource && path27.length === 0 && key === "source") {
|
|
22406
22408
|
ctx.addIssue({
|
|
22407
22409
|
code: external_exports.ZodIssueCode.custom,
|
|
22408
22410
|
path: ["source"],
|
|
@@ -22410,21 +22412,21 @@ function refineForbiddenKeys(value, path23, ctx, rejectSource) {
|
|
|
22410
22412
|
});
|
|
22411
22413
|
continue;
|
|
22412
22414
|
}
|
|
22413
|
-
refineForbiddenKeys(value[key], [...
|
|
22415
|
+
refineForbiddenKeys(value[key], [...path27, key], ctx, false);
|
|
22414
22416
|
}
|
|
22415
22417
|
}
|
|
22416
|
-
function rejectForbiddenKeys(value,
|
|
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] ${
|
|
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] ${
|
|
22427
|
+
throw new Error(`[manifest] ${path27}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
|
|
22426
22428
|
}
|
|
22427
|
-
rejectForbiddenKeys(value[key], `${
|
|
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
|
|
22438
|
+
let manifestPath2;
|
|
22437
22439
|
let source;
|
|
22438
22440
|
if (existsSync4(olamPath)) {
|
|
22439
|
-
|
|
22441
|
+
manifestPath2 = olamPath;
|
|
22440
22442
|
source = "olam";
|
|
22441
22443
|
} else if (existsSync4(adbPath)) {
|
|
22442
|
-
|
|
22444
|
+
manifestPath2 = adbPath;
|
|
22443
22445
|
source = "adb";
|
|
22444
22446
|
} else {
|
|
22445
22447
|
return null;
|
|
22446
22448
|
}
|
|
22447
|
-
const stat = lstatSync(
|
|
22449
|
+
const stat = lstatSync(manifestPath2);
|
|
22448
22450
|
if (stat.isSymbolicLink()) {
|
|
22449
|
-
throw new Error(`[manifest] ${
|
|
22451
|
+
throw new Error(`[manifest] ${manifestPath2}: symbolic links are not permitted`);
|
|
22450
22452
|
}
|
|
22451
|
-
const raw = readFileSync3(
|
|
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] ${
|
|
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] ${
|
|
22462
|
+
throw new Error(`[manifest] ${manifestPath2}: expected a YAML mapping at the top level`);
|
|
22461
22463
|
}
|
|
22462
|
-
rejectForbiddenKeys(parsed,
|
|
22464
|
+
rejectForbiddenKeys(parsed, manifestPath2, true);
|
|
22463
22465
|
const body = RepoManifestSchema.parse(parsed);
|
|
22464
22466
|
if (parsed["version"] === void 0) {
|
|
22465
|
-
console.warn(`[manifest] ${
|
|
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] ${
|
|
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(
|
|
22866
|
-
if (!existsSync6(
|
|
22867
|
+
function readOptional(path27) {
|
|
22868
|
+
if (!existsSync6(path27)) return null;
|
|
22867
22869
|
try {
|
|
22868
|
-
return readFileSync5(
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
23597
|
+
return new Promise((resolve8, reject2) => {
|
|
23592
23598
|
client.on("ready", () => {
|
|
23593
23599
|
this.connections.set(host, client);
|
|
23594
|
-
|
|
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(
|
|
24012
|
-
const url = `${this.config.workerUrl}${
|
|
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((
|
|
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:
|
|
25305
|
-
const dockerExec = (cmd) =>
|
|
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:
|
|
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
|
-
|
|
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
|
|
25503
|
-
const dest = path9.join(destPath,
|
|
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
|
|
26446
|
-
await writeFile(
|
|
26453
|
+
const path27 = join12(args.outDir, "manifest.json");
|
|
26454
|
+
await writeFile(path27, `${JSON.stringify(manifest, null, 2)}
|
|
26447
26455
|
`, "utf8");
|
|
26448
|
-
return { path:
|
|
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
|
|
26703
|
-
if (!liveCompiled.some((re) => re.test(
|
|
26704
|
-
return httpReject(403, "outside_allow_list", { path:
|
|
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((
|
|
26809
|
-
server.listen(udsPath, () =>
|
|
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((
|
|
26816
|
-
server.listen(opts.port ?? 0, "127.0.0.1", () =>
|
|
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((
|
|
26833
|
-
server.close((err) => err ? reject2(err) :
|
|
26840
|
+
new Promise((resolve8, reject2) => {
|
|
26841
|
+
server.close((err) => err ? reject2(err) : resolve8());
|
|
26834
26842
|
}),
|
|
26835
|
-
new Promise((
|
|
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((
|
|
27175
|
+
return new Promise((resolve8) => {
|
|
27168
27176
|
_launchQueue.push(() => {
|
|
27169
27177
|
_inFlightLaunches++;
|
|
27170
|
-
|
|
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
|
|
27491
|
+
const path27 = join13(outDir, `${shot.name}.${ext}`);
|
|
27484
27492
|
await page.screenshot({
|
|
27485
|
-
path:
|
|
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:
|
|
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:
|
|
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 ${
|
|
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:
|
|
27934
|
-
const { default:
|
|
27935
|
-
const { default:
|
|
27936
|
-
const tp =
|
|
27937
|
-
if (!
|
|
27938
|
-
return { token:
|
|
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:
|
|
28555
|
+
async ({ name, path: path27, description, defaultBranch }) => {
|
|
28386
28556
|
try {
|
|
28387
|
-
const entry = addRepo({ name, path:
|
|
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:
|
|
28598
|
+
async ({ name, path: path27, description, defaultBranch }) => {
|
|
28429
28599
|
try {
|
|
28430
|
-
const entry = updateRepo(name, { path:
|
|
28600
|
+
const entry = updateRepo(name, { path: path27, description, defaultBranch });
|
|
28431
28601
|
return {
|
|
28432
28602
|
content: [{
|
|
28433
28603
|
type: "text",
|
|
@@ -28441,6 +28611,299 @@ function register22(server, _ctx, _initError) {
|
|
|
28441
28611
|
);
|
|
28442
28612
|
}
|
|
28443
28613
|
|
|
28614
|
+
// ../mcp-server/src/tools/process-port.ts
|
|
28615
|
+
var process_port_exports = {};
|
|
28616
|
+
__export(process_port_exports, {
|
|
28617
|
+
register: () => register23,
|
|
28618
|
+
resolveHostCpToken: () => resolveHostCpToken
|
|
28619
|
+
});
|
|
28620
|
+
import fs10 from "node:fs";
|
|
28621
|
+
import os9 from "node:os";
|
|
28622
|
+
import path14 from "node:path";
|
|
28623
|
+
var HOST_CP_BASE = "http://127.0.0.1:19000";
|
|
28624
|
+
function resolveHostCpToken() {
|
|
28625
|
+
const envToken = process.env["OLAM_HOST_CP_TOKEN"];
|
|
28626
|
+
if (envToken) return envToken;
|
|
28627
|
+
const tokenPath = path14.join(os9.homedir(), ".olam", "host-cp.token");
|
|
28628
|
+
if (fs10.existsSync(tokenPath)) return fs10.readFileSync(tokenPath, "utf-8").trim();
|
|
28629
|
+
return null;
|
|
28630
|
+
}
|
|
28631
|
+
function tokenMissingError() {
|
|
28632
|
+
return {
|
|
28633
|
+
content: [{
|
|
28634
|
+
type: "text",
|
|
28635
|
+
text: "host-cp token not found. Set OLAM_HOST_CP_TOKEN or write ~/.olam/host-cp.token."
|
|
28636
|
+
}],
|
|
28637
|
+
isError: true
|
|
28638
|
+
};
|
|
28639
|
+
}
|
|
28640
|
+
function asMessage5(err) {
|
|
28641
|
+
return err instanceof Error ? err.message : String(err);
|
|
28642
|
+
}
|
|
28643
|
+
function register23(server, _ctx, _initError) {
|
|
28644
|
+
server.tool(
|
|
28645
|
+
"olam_process_list",
|
|
28646
|
+
"List running processes inside a world container. Returns a process table snapshot via host-cp. Fields per process: pid, user, cpu, mem, started, state, command.",
|
|
28647
|
+
{
|
|
28648
|
+
world_id: external_exports.string().describe("World ID.")
|
|
28649
|
+
},
|
|
28650
|
+
async ({ world_id }) => {
|
|
28651
|
+
const token = resolveHostCpToken();
|
|
28652
|
+
if (!token) return tokenMissingError();
|
|
28653
|
+
try {
|
|
28654
|
+
const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/processes`, {
|
|
28655
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
28656
|
+
signal: AbortSignal.timeout(1e4)
|
|
28657
|
+
});
|
|
28658
|
+
const body = await res.text();
|
|
28659
|
+
if (!res.ok) {
|
|
28660
|
+
return {
|
|
28661
|
+
content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
|
|
28662
|
+
isError: true
|
|
28663
|
+
};
|
|
28664
|
+
}
|
|
28665
|
+
const data = JSON.parse(body);
|
|
28666
|
+
return {
|
|
28667
|
+
content: [{ type: "text", text: JSON.stringify({ processes: data.processes }, null, 2) }]
|
|
28668
|
+
};
|
|
28669
|
+
} catch (err) {
|
|
28670
|
+
return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
|
|
28671
|
+
}
|
|
28672
|
+
}
|
|
28673
|
+
);
|
|
28674
|
+
server.tool(
|
|
28675
|
+
"olam_port_expose",
|
|
28676
|
+
"Expose a port inside a world container to the host by creating a socat bridge. Returns the host-side URL and bridge metadata. The bridge persists until removed with olam_port_unexpose.",
|
|
28677
|
+
{
|
|
28678
|
+
world_id: external_exports.string().describe("World ID."),
|
|
28679
|
+
port: external_exports.number().int().min(1).max(65535).describe("Container port to expose on the host.")
|
|
28680
|
+
},
|
|
28681
|
+
async ({ world_id, port }) => {
|
|
28682
|
+
const token = resolveHostCpToken();
|
|
28683
|
+
if (!token) return tokenMissingError();
|
|
28684
|
+
try {
|
|
28685
|
+
const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/server-bridges`, {
|
|
28686
|
+
method: "POST",
|
|
28687
|
+
headers: {
|
|
28688
|
+
"Content-Type": "application/json",
|
|
28689
|
+
Authorization: `Bearer ${token}`
|
|
28690
|
+
},
|
|
28691
|
+
body: JSON.stringify({ port }),
|
|
28692
|
+
signal: AbortSignal.timeout(3e4)
|
|
28693
|
+
});
|
|
28694
|
+
const body = await res.text();
|
|
28695
|
+
if (!res.ok) {
|
|
28696
|
+
return {
|
|
28697
|
+
content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
|
|
28698
|
+
isError: true
|
|
28699
|
+
};
|
|
28700
|
+
}
|
|
28701
|
+
const data = JSON.parse(body);
|
|
28702
|
+
return {
|
|
28703
|
+
content: [{
|
|
28704
|
+
type: "text",
|
|
28705
|
+
text: JSON.stringify({
|
|
28706
|
+
url: `http://localhost:${data.hostPort}`,
|
|
28707
|
+
host_port: data.hostPort,
|
|
28708
|
+
sidecar_container_id: data.containerId,
|
|
28709
|
+
container_port: data.containerPort,
|
|
28710
|
+
container_name: data.containerName
|
|
28711
|
+
}, null, 2)
|
|
28712
|
+
}]
|
|
28713
|
+
};
|
|
28714
|
+
} catch (err) {
|
|
28715
|
+
return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
|
|
28716
|
+
}
|
|
28717
|
+
}
|
|
28718
|
+
);
|
|
28719
|
+
server.tool(
|
|
28720
|
+
"olam_port_unexpose",
|
|
28721
|
+
"Tear down a port bridge for a world container. Removes the socat sidecar and frees the host port.",
|
|
28722
|
+
{
|
|
28723
|
+
world_id: external_exports.string().describe("World ID."),
|
|
28724
|
+
port: external_exports.number().int().min(1).max(65535).describe("Container port whose bridge should be removed.")
|
|
28725
|
+
},
|
|
28726
|
+
async ({ world_id, port }) => {
|
|
28727
|
+
const token = resolveHostCpToken();
|
|
28728
|
+
if (!token) return tokenMissingError();
|
|
28729
|
+
try {
|
|
28730
|
+
const res = await fetch(`${HOST_CP_BASE}/api/worlds/${world_id}/server-bridges/${port}`, {
|
|
28731
|
+
method: "DELETE",
|
|
28732
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
28733
|
+
signal: AbortSignal.timeout(1e4)
|
|
28734
|
+
});
|
|
28735
|
+
if (!res.ok) {
|
|
28736
|
+
const body = await res.text();
|
|
28737
|
+
return {
|
|
28738
|
+
content: [{ type: "text", text: `host-cp ${res.status}: ${body}` }],
|
|
28739
|
+
isError: true
|
|
28740
|
+
};
|
|
28741
|
+
}
|
|
28742
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true }) }] };
|
|
28743
|
+
} catch (err) {
|
|
28744
|
+
return { content: [{ type: "text", text: asMessage5(err) }], isError: true };
|
|
28745
|
+
}
|
|
28746
|
+
}
|
|
28747
|
+
);
|
|
28748
|
+
}
|
|
28749
|
+
|
|
28750
|
+
// ../mcp-server/src/tools/runbook.ts
|
|
28751
|
+
var runbook_exports = {};
|
|
28752
|
+
__export(runbook_exports, {
|
|
28753
|
+
register: () => register24
|
|
28754
|
+
});
|
|
28755
|
+
function asMessage6(err) {
|
|
28756
|
+
return err instanceof Error ? err.message : String(err);
|
|
28757
|
+
}
|
|
28758
|
+
function formatConflicts(conflicts) {
|
|
28759
|
+
const lines = conflicts.map((c) => {
|
|
28760
|
+
const occupant = c.occupant.type === "olam-world" ? `in use by world "${c.occupant.worldId}" (olam)` : `in use by PID ${c.occupant.pid ?? "unknown"} (non-olam)`;
|
|
28761
|
+
return ` \u2717 ${c.repoName}.${c.serviceName}:${c.port} \u2014 ${occupant}`;
|
|
28762
|
+
});
|
|
28763
|
+
return lines.join("\n");
|
|
28764
|
+
}
|
|
28765
|
+
function register24(server, ctx, _initError) {
|
|
28766
|
+
server.tool(
|
|
28767
|
+
"olam_runbook_list",
|
|
28768
|
+
"List all runbooks in ~/.olam/config.json. Returns { runbooks: Runbook[] }.",
|
|
28769
|
+
{},
|
|
28770
|
+
async () => {
|
|
28771
|
+
const runbooks = listRunbooks();
|
|
28772
|
+
return {
|
|
28773
|
+
content: [{ type: "text", text: JSON.stringify({ runbooks }, null, 2) }]
|
|
28774
|
+
};
|
|
28775
|
+
}
|
|
28776
|
+
);
|
|
28777
|
+
server.tool(
|
|
28778
|
+
"olam_runbook_show",
|
|
28779
|
+
"Show a single runbook by name. Returns { runbook: Runbook }.",
|
|
28780
|
+
{
|
|
28781
|
+
name: external_exports.string().min(1).describe("Runbook name.")
|
|
28782
|
+
},
|
|
28783
|
+
async ({ name }) => {
|
|
28784
|
+
try {
|
|
28785
|
+
const runbook = getRunbook(name);
|
|
28786
|
+
return {
|
|
28787
|
+
content: [{ type: "text", text: JSON.stringify({ runbook }, null, 2) }]
|
|
28788
|
+
};
|
|
28789
|
+
} catch (err) {
|
|
28790
|
+
return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
|
|
28791
|
+
}
|
|
28792
|
+
}
|
|
28793
|
+
);
|
|
28794
|
+
server.tool(
|
|
28795
|
+
"olam_runbook_add",
|
|
28796
|
+
"Create a new runbook. Validates that all referenced repos exist in the global registry and that portMap has no duplicates.",
|
|
28797
|
+
{
|
|
28798
|
+
name: external_exports.string().min(1).describe("Runbook name (lowercase, digits, dash; 1\u201364 chars)."),
|
|
28799
|
+
repos: external_exports.array(external_exports.string().min(1)).min(1).describe("Repo names (must exist in registry)."),
|
|
28800
|
+
description: external_exports.string().optional().describe("Optional human-readable description."),
|
|
28801
|
+
portMap: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.number().int())).optional().describe("Port mappings: { repoName: { serviceName: hostPort } }."),
|
|
28802
|
+
seeds: external_exports.array(
|
|
28803
|
+
external_exports.union([
|
|
28804
|
+
external_exports.object({ type: external_exports.literal("sql-file"), repo: external_exports.string(), service: external_exports.string(), path: external_exports.string() }),
|
|
28805
|
+
external_exports.object({ type: external_exports.literal("command"), repo: external_exports.string(), run: external_exports.string() }),
|
|
28806
|
+
external_exports.object({ type: external_exports.literal("fixture-copy"), repo: external_exports.string(), src: external_exports.string(), dest: external_exports.string() })
|
|
28807
|
+
])
|
|
28808
|
+
).optional().describe("Optional seed operations to run when applying the runbook."),
|
|
28809
|
+
env: external_exports.record(external_exports.string().min(1), external_exports.record(external_exports.string().min(1), external_exports.string())).optional().describe("Per-repo environment variable overrides.")
|
|
28810
|
+
},
|
|
28811
|
+
async ({ name, repos, description, portMap, seeds, env }) => {
|
|
28812
|
+
try {
|
|
28813
|
+
const runbook = addRunbook({ name, repos, description, portMap, seeds, env });
|
|
28814
|
+
return {
|
|
28815
|
+
content: [{
|
|
28816
|
+
type: "text",
|
|
28817
|
+
text: JSON.stringify({ name: runbook.name, message: `Created runbook "${runbook.name}".` }, null, 2)
|
|
28818
|
+
}]
|
|
28819
|
+
};
|
|
28820
|
+
} catch (err) {
|
|
28821
|
+
return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
|
|
28822
|
+
}
|
|
28823
|
+
}
|
|
28824
|
+
);
|
|
28825
|
+
server.tool(
|
|
28826
|
+
"olam_runbook_remove",
|
|
28827
|
+
"Remove a runbook from the global config. Does not affect any running worlds.",
|
|
28828
|
+
{
|
|
28829
|
+
name: external_exports.string().min(1).describe("Runbook name to remove.")
|
|
28830
|
+
},
|
|
28831
|
+
async ({ name }) => {
|
|
28832
|
+
try {
|
|
28833
|
+
removeRunbook(name);
|
|
28834
|
+
return {
|
|
28835
|
+
content: [{
|
|
28836
|
+
type: "text",
|
|
28837
|
+
text: JSON.stringify({ name, message: `Removed runbook "${name}".` }, null, 2)
|
|
28838
|
+
}]
|
|
28839
|
+
};
|
|
28840
|
+
} catch (err) {
|
|
28841
|
+
return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
|
|
28842
|
+
}
|
|
28843
|
+
}
|
|
28844
|
+
);
|
|
28845
|
+
server.tool(
|
|
28846
|
+
"olam_runbook_apply",
|
|
28847
|
+
"Validate ports then create a world from a runbook. Errors (isError: true) on port conflicts. Returns world metadata on success.",
|
|
28848
|
+
{
|
|
28849
|
+
name: external_exports.string().min(1).describe("Runbook name."),
|
|
28850
|
+
worldName: external_exports.string().optional().describe("Override the world name."),
|
|
28851
|
+
task: external_exports.string().optional().describe("Initial task to dispatch into the world."),
|
|
28852
|
+
branchName: external_exports.string().optional().describe("Override the default branch name.")
|
|
28853
|
+
},
|
|
28854
|
+
async ({ name, worldName, task, branchName }) => {
|
|
28855
|
+
if (!ctx) {
|
|
28856
|
+
return {
|
|
28857
|
+
content: [{ type: "text", text: "Olam is not configured. Run /olam:init to set up." }],
|
|
28858
|
+
isError: true
|
|
28859
|
+
};
|
|
28860
|
+
}
|
|
28861
|
+
let runbook;
|
|
28862
|
+
try {
|
|
28863
|
+
runbook = getRunbook(name);
|
|
28864
|
+
} catch (err) {
|
|
28865
|
+
return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
|
|
28866
|
+
}
|
|
28867
|
+
const { conflicts } = validateRunbookPorts(runbook);
|
|
28868
|
+
if (conflicts.length > 0) {
|
|
28869
|
+
const detail = formatConflicts(conflicts);
|
|
28870
|
+
const msg = `Port conflicts detected for runbook "${name}":
|
|
28871
|
+
${detail}
|
|
28872
|
+
|
|
28873
|
+
${conflicts.length} port conflict(s). Stop the conflicting processes or update portMap in runbook "${name}".`;
|
|
28874
|
+
return {
|
|
28875
|
+
content: [{ type: "text", text: msg }],
|
|
28876
|
+
isError: true
|
|
28877
|
+
};
|
|
28878
|
+
}
|
|
28879
|
+
try {
|
|
28880
|
+
const worldOpts = {
|
|
28881
|
+
name: worldName ?? name,
|
|
28882
|
+
repos: runbook.repos,
|
|
28883
|
+
task: task ?? `Apply runbook "${name}"`,
|
|
28884
|
+
...branchName ? { branchName } : {}
|
|
28885
|
+
};
|
|
28886
|
+
const world = await ctx.worldManager.createWorld(worldOpts);
|
|
28887
|
+
const result = {
|
|
28888
|
+
worldId: world.id,
|
|
28889
|
+
name: world.name,
|
|
28890
|
+
branch: world.branch,
|
|
28891
|
+
status: world.status,
|
|
28892
|
+
repos: world.repos,
|
|
28893
|
+
portOffset: world.portOffset,
|
|
28894
|
+
...world.appPortUrls ? { appPortUrls: world.appPortUrls } : {},
|
|
28895
|
+
message: `World "${world.name}" created from runbook "${name}".`
|
|
28896
|
+
};
|
|
28897
|
+
return {
|
|
28898
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
28899
|
+
};
|
|
28900
|
+
} catch (err) {
|
|
28901
|
+
return { content: [{ type: "text", text: asMessage6(err) }], isError: true };
|
|
28902
|
+
}
|
|
28903
|
+
}
|
|
28904
|
+
);
|
|
28905
|
+
}
|
|
28906
|
+
|
|
28444
28907
|
// ../mcp-server/src/tools/index.ts
|
|
28445
28908
|
var toolModules = [
|
|
28446
28909
|
init_exports,
|
|
@@ -28464,7 +28927,9 @@ var toolModules = [
|
|
|
28464
28927
|
lane_merge_exports,
|
|
28465
28928
|
capture_view_exports,
|
|
28466
28929
|
create_from_prompt_exports,
|
|
28467
|
-
repo_exports
|
|
28930
|
+
repo_exports,
|
|
28931
|
+
process_port_exports,
|
|
28932
|
+
runbook_exports
|
|
28468
28933
|
];
|
|
28469
28934
|
function registerAllTools(server, ctx, initError) {
|
|
28470
28935
|
for (const mod of toolModules) {
|
|
@@ -28481,7 +28946,7 @@ var SERVER_INSTRUCTIONS = [
|
|
|
28481
28946
|
"Use olam_observe to watch a world's reasoning. Use olam_crystallize to save thoughts.",
|
|
28482
28947
|
"Always olam_destroy_world when done to clean up resources."
|
|
28483
28948
|
].join("\n");
|
|
28484
|
-
function
|
|
28949
|
+
function createServer4(ctx, initError) {
|
|
28485
28950
|
const server = new McpServer(
|
|
28486
28951
|
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
28487
28952
|
{ instructions: SERVER_INSTRUCTIONS }
|
|
@@ -28496,8 +28961,8 @@ function createServer3(ctx, initError) {
|
|
|
28496
28961
|
}
|
|
28497
28962
|
|
|
28498
28963
|
// ../core/dist/config/loader.js
|
|
28499
|
-
import * as
|
|
28500
|
-
import * as
|
|
28964
|
+
import * as fs12 from "node:fs";
|
|
28965
|
+
import * as path15 from "node:path";
|
|
28501
28966
|
import { parse as parseYaml2 } from "yaml";
|
|
28502
28967
|
|
|
28503
28968
|
// ../core/dist/config/schema.js
|
|
@@ -28877,11 +29342,11 @@ function substituteEnvVars(obj) {
|
|
|
28877
29342
|
}
|
|
28878
29343
|
|
|
28879
29344
|
// ../core/dist/config/dotenv.js
|
|
28880
|
-
import * as
|
|
29345
|
+
import * as fs11 from "node:fs";
|
|
28881
29346
|
function loadDotEnv(envPath) {
|
|
28882
|
-
if (!
|
|
29347
|
+
if (!fs11.existsSync(envPath))
|
|
28883
29348
|
return;
|
|
28884
|
-
const content =
|
|
29349
|
+
const content = fs11.readFileSync(envPath, "utf-8");
|
|
28885
29350
|
for (const line of content.split("\n")) {
|
|
28886
29351
|
const trimmed = line.trim();
|
|
28887
29352
|
if (!trimmed || trimmed.startsWith("#"))
|
|
@@ -28900,18 +29365,18 @@ function loadDotEnv(envPath) {
|
|
|
28900
29365
|
// ../core/dist/config/loader.js
|
|
28901
29366
|
function findConfigFile(startDir) {
|
|
28902
29367
|
const searched = [];
|
|
28903
|
-
let current =
|
|
29368
|
+
let current = path15.resolve(startDir);
|
|
28904
29369
|
while (true) {
|
|
28905
|
-
const newLayout =
|
|
28906
|
-
const legacyLayout =
|
|
29370
|
+
const newLayout = path15.join(current, CONFIG_DIR_NAME, CONFIG_FILENAME);
|
|
29371
|
+
const legacyLayout = path15.join(current, LEGACY_CONFIG_FILENAME);
|
|
28907
29372
|
searched.push(newLayout, legacyLayout);
|
|
28908
|
-
if (
|
|
29373
|
+
if (fs12.existsSync(newLayout)) {
|
|
28909
29374
|
return { path: newLayout, isLegacy: false };
|
|
28910
29375
|
}
|
|
28911
|
-
if (
|
|
29376
|
+
if (fs12.existsSync(legacyLayout)) {
|
|
28912
29377
|
return { path: legacyLayout, isLegacy: true };
|
|
28913
29378
|
}
|
|
28914
|
-
const parent =
|
|
29379
|
+
const parent = path15.dirname(current);
|
|
28915
29380
|
if (parent === current)
|
|
28916
29381
|
break;
|
|
28917
29382
|
current = parent;
|
|
@@ -28926,12 +29391,12 @@ function loadConfig(startDir) {
|
|
|
28926
29391
|
Run /olam:init to migrate to .olam/config.yaml
|
|
28927
29392
|
`);
|
|
28928
29393
|
} else {
|
|
28929
|
-
const envPath =
|
|
29394
|
+
const envPath = path15.join(path15.dirname(found.path), ".env");
|
|
28930
29395
|
loadDotEnv(envPath);
|
|
28931
29396
|
}
|
|
28932
29397
|
let rawContent;
|
|
28933
29398
|
try {
|
|
28934
|
-
rawContent =
|
|
29399
|
+
rawContent = fs12.readFileSync(found.path, "utf-8");
|
|
28935
29400
|
} catch (err) {
|
|
28936
29401
|
throw new Error(`Failed to read ${found.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
28937
29402
|
}
|
|
@@ -28961,11 +29426,11 @@ function loadConfig(startDir) {
|
|
|
28961
29426
|
}
|
|
28962
29427
|
|
|
28963
29428
|
// ../core/dist/world/manager.js
|
|
28964
|
-
import * as
|
|
28965
|
-
import { execSync as
|
|
28966
|
-
import * as
|
|
28967
|
-
import * as
|
|
28968
|
-
import * as
|
|
29429
|
+
import * as crypto5 from "node:crypto";
|
|
29430
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
29431
|
+
import * as fs20 from "node:fs";
|
|
29432
|
+
import * as os13 from "node:os";
|
|
29433
|
+
import * as path24 from "node:path";
|
|
28969
29434
|
|
|
28970
29435
|
// ../core/dist/world/state.js
|
|
28971
29436
|
var VALID_TRANSITIONS = {
|
|
@@ -28999,6 +29464,7 @@ var WorldStateMachine = class {
|
|
|
28999
29464
|
};
|
|
29000
29465
|
|
|
29001
29466
|
// ../core/dist/world/devbox-image.js
|
|
29467
|
+
var WORLDSPEC_OVERRIDE_TAG = "worldspec-override";
|
|
29002
29468
|
function selectDevboxImage(config2, repos) {
|
|
29003
29469
|
const selectors = config2.devbox?.image_selectors;
|
|
29004
29470
|
if (!selectors || selectors.length === 0)
|
|
@@ -29010,7 +29476,8 @@ function selectDevboxImage(config2, repos) {
|
|
|
29010
29476
|
return {
|
|
29011
29477
|
image: resolveDevboxImage(config2, sel.tag),
|
|
29012
29478
|
cacheArch: sel.cache_arch,
|
|
29013
|
-
tag: sel.tag
|
|
29479
|
+
tag: sel.tag,
|
|
29480
|
+
source: "image_selector"
|
|
29014
29481
|
};
|
|
29015
29482
|
}
|
|
29016
29483
|
}
|
|
@@ -29031,6 +29498,18 @@ function matches(sel, repoNames, repoTypes) {
|
|
|
29031
29498
|
}
|
|
29032
29499
|
return true;
|
|
29033
29500
|
}
|
|
29501
|
+
function selectDevboxImageForWorld(args) {
|
|
29502
|
+
if (args.worldspec) {
|
|
29503
|
+
return {
|
|
29504
|
+
image: args.worldspec.images.devbox,
|
|
29505
|
+
cacheArch: void 0,
|
|
29506
|
+
// v1 worldspec schema doesn't carry cache_arch
|
|
29507
|
+
tag: WORLDSPEC_OVERRIDE_TAG,
|
|
29508
|
+
source: "worldspec"
|
|
29509
|
+
};
|
|
29510
|
+
}
|
|
29511
|
+
return selectDevboxImage(args.config, args.repos);
|
|
29512
|
+
}
|
|
29034
29513
|
function resolveDevboxImage(config2, tag) {
|
|
29035
29514
|
const registry2 = config2.devbox?.registry;
|
|
29036
29515
|
if (!registry2)
|
|
@@ -29047,8 +29526,8 @@ function resolveDevboxImage(config2, tag) {
|
|
|
29047
29526
|
|
|
29048
29527
|
// ../core/dist/world/worktree.js
|
|
29049
29528
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
29050
|
-
import * as
|
|
29051
|
-
import * as
|
|
29529
|
+
import * as fs13 from "node:fs";
|
|
29530
|
+
import * as path16 from "node:path";
|
|
29052
29531
|
function resolveGitDir(repo) {
|
|
29053
29532
|
if (repo.path) {
|
|
29054
29533
|
return repo.path;
|
|
@@ -29058,11 +29537,11 @@ function resolveGitDir(repo) {
|
|
|
29058
29537
|
async function createWorktrees(repos, worldId, workspacePath, branch) {
|
|
29059
29538
|
const created = [];
|
|
29060
29539
|
for (const repo of repos) {
|
|
29061
|
-
const worktreePath =
|
|
29540
|
+
const worktreePath = path16.join(workspacePath, repo.name);
|
|
29062
29541
|
const gitDir = resolveGitDir(repo);
|
|
29063
29542
|
const branchName = branch || `olam/${worldId}`;
|
|
29064
29543
|
try {
|
|
29065
|
-
|
|
29544
|
+
fs13.mkdirSync(path16.dirname(worktreePath), { recursive: true });
|
|
29066
29545
|
execFileSync2("git", ["worktree", "add", worktreePath, "-b", branchName], {
|
|
29067
29546
|
cwd: gitDir,
|
|
29068
29547
|
stdio: "pipe"
|
|
@@ -29095,7 +29574,7 @@ async function createWorktrees(repos, worldId, workspacePath, branch) {
|
|
|
29095
29574
|
}
|
|
29096
29575
|
async function removeWorktrees(repos, workspacePath) {
|
|
29097
29576
|
for (const repo of repos) {
|
|
29098
|
-
const worktreePath =
|
|
29577
|
+
const worktreePath = path16.join(workspacePath, repo.name);
|
|
29099
29578
|
let gitDir;
|
|
29100
29579
|
try {
|
|
29101
29580
|
gitDir = resolveGitDir(repo);
|
|
@@ -29170,12 +29649,12 @@ function removeBranch(repo, branch) {
|
|
|
29170
29649
|
|
|
29171
29650
|
// ../core/dist/world/baseline-diff.js
|
|
29172
29651
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
29173
|
-
import * as
|
|
29174
|
-
import * as
|
|
29175
|
-
import * as
|
|
29652
|
+
import * as fs14 from "node:fs";
|
|
29653
|
+
import * as os10 from "node:os";
|
|
29654
|
+
import * as path17 from "node:path";
|
|
29176
29655
|
var DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
29177
|
-
function expandHome(p,
|
|
29178
|
-
return p.replace(/^~(?=$|\/|\\)/,
|
|
29656
|
+
function expandHome(p, homedir15) {
|
|
29657
|
+
return p.replace(/^~(?=$|\/|\\)/, homedir15());
|
|
29179
29658
|
}
|
|
29180
29659
|
function sanitizeRepoFilename(name) {
|
|
29181
29660
|
const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
@@ -29198,10 +29677,10 @@ ${stderr}`;
|
|
|
29198
29677
|
}
|
|
29199
29678
|
function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
29200
29679
|
const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
|
|
29201
|
-
const
|
|
29202
|
-
const baselineDir =
|
|
29680
|
+
const homedir15 = deps.homedir ?? (() => os10.homedir());
|
|
29681
|
+
const baselineDir = path17.join(workspacePath, ".olam", "baseline");
|
|
29203
29682
|
try {
|
|
29204
|
-
|
|
29683
|
+
fs14.mkdirSync(baselineDir, { recursive: true });
|
|
29205
29684
|
} catch (err) {
|
|
29206
29685
|
const msg = err instanceof Error ? err.message : String(err);
|
|
29207
29686
|
console.warn(`[baseline-diff] mkdir ${baselineDir} failed: ${msg}; reaper will see no baseline at all`);
|
|
@@ -29213,9 +29692,9 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
29213
29692
|
if (!repo.path)
|
|
29214
29693
|
continue;
|
|
29215
29694
|
const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
|
|
29216
|
-
const outPath =
|
|
29217
|
-
const repoPath = expandHome(repo.path,
|
|
29218
|
-
if (!
|
|
29695
|
+
const outPath = path17.join(baselineDir, filename);
|
|
29696
|
+
const repoPath = expandHome(repo.path, homedir15);
|
|
29697
|
+
if (!fs14.existsSync(repoPath)) {
|
|
29219
29698
|
writeBaselineFile(outPath, `# repo: ${repo.name}
|
|
29220
29699
|
# (skipped: path ${repoPath} does not exist)
|
|
29221
29700
|
`);
|
|
@@ -29282,7 +29761,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
29282
29761
|
}
|
|
29283
29762
|
function writeBaselineFile(outPath, content) {
|
|
29284
29763
|
try {
|
|
29285
|
-
|
|
29764
|
+
fs14.writeFileSync(outPath, content);
|
|
29286
29765
|
} catch (err) {
|
|
29287
29766
|
const msg = err instanceof Error ? err.message : String(err);
|
|
29288
29767
|
console.warn(`[baseline-diff] write to ${outPath} failed: ${msg}`);
|
|
@@ -29290,8 +29769,8 @@ function writeBaselineFile(outPath, content) {
|
|
|
29290
29769
|
}
|
|
29291
29770
|
function stripWorktreeEdits(repos, workspacePath) {
|
|
29292
29771
|
for (const repo of repos) {
|
|
29293
|
-
const worktreePath =
|
|
29294
|
-
if (!
|
|
29772
|
+
const worktreePath = path17.join(workspacePath, repo.name);
|
|
29773
|
+
if (!fs14.existsSync(worktreePath))
|
|
29295
29774
|
continue;
|
|
29296
29775
|
try {
|
|
29297
29776
|
execFileSync3("git", ["checkout", "--", "."], {
|
|
@@ -29321,12 +29800,12 @@ function formatBaselineSummary(result) {
|
|
|
29321
29800
|
}
|
|
29322
29801
|
|
|
29323
29802
|
// ../core/dist/world/context-injection.js
|
|
29324
|
-
import * as
|
|
29325
|
-
import * as
|
|
29803
|
+
import * as fs15 from "node:fs";
|
|
29804
|
+
import * as path18 from "node:path";
|
|
29326
29805
|
function injectWorldContext(opts) {
|
|
29327
29806
|
const { world, task, linearTicketId, claudeMdExtra, taskContext, services, pleriPlaneUrl } = opts;
|
|
29328
|
-
const claudeDir =
|
|
29329
|
-
|
|
29807
|
+
const claudeDir = path18.join(world.workspacePath, ".claude");
|
|
29808
|
+
fs15.mkdirSync(claudeDir, { recursive: true });
|
|
29330
29809
|
const sections = [];
|
|
29331
29810
|
sections.push(`# Olam World: ${world.name}`);
|
|
29332
29811
|
sections.push("");
|
|
@@ -29487,7 +29966,7 @@ function injectWorldContext(opts) {
|
|
|
29487
29966
|
sections.push("");
|
|
29488
29967
|
}
|
|
29489
29968
|
const content = sections.join("\n");
|
|
29490
|
-
|
|
29969
|
+
fs15.writeFileSync(path18.join(claudeDir, "CLAUDE.md"), content);
|
|
29491
29970
|
}
|
|
29492
29971
|
function formatTaskSource(ctx) {
|
|
29493
29972
|
if (ctx.source === "linear" && ctx.ticketId) {
|
|
@@ -29501,9 +29980,9 @@ function formatTaskSource(ctx) {
|
|
|
29501
29980
|
function hasPlanFile(world) {
|
|
29502
29981
|
if (world.repos.length === 0)
|
|
29503
29982
|
return false;
|
|
29504
|
-
const plansDir =
|
|
29983
|
+
const plansDir = path18.join(world.workspacePath, world.repos[0], "docs", "plans");
|
|
29505
29984
|
try {
|
|
29506
|
-
return
|
|
29985
|
+
return fs15.existsSync(plansDir) && fs15.readdirSync(plansDir).length > 0;
|
|
29507
29986
|
} catch {
|
|
29508
29987
|
return false;
|
|
29509
29988
|
}
|
|
@@ -29729,7 +30208,7 @@ async function installStack(exec, repos, stacks) {
|
|
|
29729
30208
|
}
|
|
29730
30209
|
|
|
29731
30210
|
// ../core/dist/world/stack-image.js
|
|
29732
|
-
import { execSync as
|
|
30211
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
29733
30212
|
import * as crypto3 from "node:crypto";
|
|
29734
30213
|
var BASE_IMAGE = "olam-devbox";
|
|
29735
30214
|
var LABEL_PREFIX = "olam.stack-image";
|
|
@@ -29761,7 +30240,7 @@ function lookupCachedImage(runtimes) {
|
|
|
29761
30240
|
const tag = computeImageTag(runtimes);
|
|
29762
30241
|
const imageName = `${BASE_IMAGE}:${tag}`;
|
|
29763
30242
|
try {
|
|
29764
|
-
|
|
30243
|
+
execSync3(`docker image inspect ${imageName} > /dev/null 2>&1`, {
|
|
29765
30244
|
stdio: "pipe",
|
|
29766
30245
|
timeout: 5e3
|
|
29767
30246
|
});
|
|
@@ -29773,14 +30252,14 @@ function lookupCachedImage(runtimes) {
|
|
|
29773
30252
|
function commitAsImage(containerName, imageName) {
|
|
29774
30253
|
const baseDigest = getBaseImageDigest();
|
|
29775
30254
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
29776
|
-
|
|
30255
|
+
execSync3(`docker commit --change 'LABEL ${LABEL_PREFIX}=true' --change 'LABEL ${LABEL_PREFIX}.created-at=${now}' --change 'LABEL ${LABEL_PREFIX}.base-digest=${baseDigest}' ${containerName} ${imageName}`, { stdio: "pipe", timeout: 12e4 });
|
|
29777
30256
|
}
|
|
29778
30257
|
var cachedBaseDigest;
|
|
29779
30258
|
function getBaseImageDigest() {
|
|
29780
30259
|
if (cachedBaseDigest)
|
|
29781
30260
|
return cachedBaseDigest;
|
|
29782
30261
|
try {
|
|
29783
|
-
const digest =
|
|
30262
|
+
const digest = execSync3(`docker inspect ${BASE_IMAGE}:latest --format '{{.Id}}'`, { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
29784
30263
|
cachedBaseDigest = digest.replace("sha256:", "").slice(0, 16);
|
|
29785
30264
|
return cachedBaseDigest;
|
|
29786
30265
|
} catch {
|
|
@@ -30005,6 +30484,9 @@ var SENTINEL_SUFFIX = "-done";
|
|
|
30005
30484
|
function sentinelPath(workdir, index) {
|
|
30006
30485
|
return `${workdir}/${SENTINEL_PREFIX}${index}${SENTINEL_SUFFIX}`;
|
|
30007
30486
|
}
|
|
30487
|
+
function bootstrapStepSentinelPath(workdir, index) {
|
|
30488
|
+
return sentinelPath(workdir, index);
|
|
30489
|
+
}
|
|
30008
30490
|
async function runBootstrap(containerName, workdir, steps, exec, options = {}) {
|
|
30009
30491
|
if (!SAFE_IDENT.test(containerName)) {
|
|
30010
30492
|
throw new Error(`containerName "${containerName}" must match ${SAFE_IDENT} (defensive guard)`);
|
|
@@ -30060,8 +30542,430 @@ function shellQuote(s) {
|
|
|
30060
30542
|
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
30061
30543
|
}
|
|
30062
30544
|
|
|
30545
|
+
// ../core/dist/world/snapshot.js
|
|
30546
|
+
import * as crypto4 from "node:crypto";
|
|
30547
|
+
import * as fs16 from "node:fs";
|
|
30548
|
+
import * as os11 from "node:os";
|
|
30549
|
+
import * as path19 from "node:path";
|
|
30550
|
+
import { execFileSync as execFileSync4, spawn } from "node:child_process";
|
|
30551
|
+
import { gunzipSync } from "node:zlib";
|
|
30552
|
+
function snapshotsDir() {
|
|
30553
|
+
return process.env["OLAM_SNAPSHOTS_DIR"] ?? path19.join(os11.homedir(), ".olam", "snapshots");
|
|
30554
|
+
}
|
|
30555
|
+
function snapshotKindDirByWorkspace(workspace, arch, kind) {
|
|
30556
|
+
return path19.join(snapshotsDir(), "by-workspace", workspace, arch, kind);
|
|
30557
|
+
}
|
|
30558
|
+
function cleanupLegacyByWorldDir(worldId) {
|
|
30559
|
+
const legacyDir = path19.join(snapshotsDir(), worldId);
|
|
30560
|
+
if (worldId === "by-workspace")
|
|
30561
|
+
return;
|
|
30562
|
+
if (!fs16.existsSync(legacyDir))
|
|
30563
|
+
return;
|
|
30564
|
+
try {
|
|
30565
|
+
fs16.rmSync(legacyDir, { recursive: true, force: true });
|
|
30566
|
+
} catch {
|
|
30567
|
+
}
|
|
30568
|
+
}
|
|
30569
|
+
function manifestPath(tarPath) {
|
|
30570
|
+
return tarPath.replace(/\.tar\.gz$/, ".manifest.json");
|
|
30571
|
+
}
|
|
30572
|
+
function hashBuffers(entries) {
|
|
30573
|
+
const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path));
|
|
30574
|
+
const hash = crypto4.createHash("sha256");
|
|
30575
|
+
for (const entry of sorted) {
|
|
30576
|
+
hash.update(entry.path);
|
|
30577
|
+
hash.update("\0");
|
|
30578
|
+
hash.update(entry.content);
|
|
30579
|
+
hash.update("\0");
|
|
30580
|
+
}
|
|
30581
|
+
return hash.digest("hex").slice(0, 12);
|
|
30582
|
+
}
|
|
30583
|
+
function computeGemsFingerprint(repoDir, imageDigest) {
|
|
30584
|
+
const lockfile = path19.join(repoDir, "Gemfile.lock");
|
|
30585
|
+
if (!fs16.existsSync(lockfile))
|
|
30586
|
+
return null;
|
|
30587
|
+
const entries = [
|
|
30588
|
+
{ path: "Gemfile.lock", content: fs16.readFileSync(lockfile) }
|
|
30589
|
+
];
|
|
30590
|
+
if (imageDigest) {
|
|
30591
|
+
entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
|
|
30592
|
+
}
|
|
30593
|
+
return hashBuffers(entries);
|
|
30594
|
+
}
|
|
30595
|
+
function computeNodeFingerprint(repoDir, imageDigest) {
|
|
30596
|
+
const candidates = ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"];
|
|
30597
|
+
for (const name of candidates) {
|
|
30598
|
+
const lockfile = path19.join(repoDir, name);
|
|
30599
|
+
if (fs16.existsSync(lockfile)) {
|
|
30600
|
+
const entries = [
|
|
30601
|
+
{ path: name, content: fs16.readFileSync(lockfile) }
|
|
30602
|
+
];
|
|
30603
|
+
if (imageDigest) {
|
|
30604
|
+
entries.push({ path: "__image_digest__", content: Buffer.from(imageDigest, "utf-8") });
|
|
30605
|
+
}
|
|
30606
|
+
return hashBuffers(entries);
|
|
30607
|
+
}
|
|
30608
|
+
}
|
|
30609
|
+
return null;
|
|
30610
|
+
}
|
|
30611
|
+
function unpackTarballAtomic(srcPath, destDir) {
|
|
30612
|
+
const validation = enumerateAndValidateTarballEntries(srcPath, destDir);
|
|
30613
|
+
if (!validation.valid) {
|
|
30614
|
+
return {
|
|
30615
|
+
ok: false,
|
|
30616
|
+
reason: validation.reason,
|
|
30617
|
+
detail: validation.detail ?? `unsafe entry: ${validation.unsafePath}`
|
|
30618
|
+
};
|
|
30619
|
+
}
|
|
30620
|
+
const parent = path19.dirname(destDir);
|
|
30621
|
+
fs16.mkdirSync(parent, { recursive: true });
|
|
30622
|
+
const tmpSuffix = `.tmp-${process.pid}-${crypto4.randomBytes(4).toString("hex")}`;
|
|
30623
|
+
const tmpDir = `${destDir}${tmpSuffix}`;
|
|
30624
|
+
try {
|
|
30625
|
+
fs16.mkdirSync(tmpDir, { recursive: true });
|
|
30626
|
+
execFileSync4("tar", ["-xzf", srcPath, "-C", tmpDir], { stdio: "pipe" });
|
|
30627
|
+
fs16.renameSync(tmpDir, destDir);
|
|
30628
|
+
return { ok: true, entryCount: validation.entries.length };
|
|
30629
|
+
} catch (err) {
|
|
30630
|
+
try {
|
|
30631
|
+
fs16.rmSync(tmpDir, { recursive: true, force: true });
|
|
30632
|
+
} catch {
|
|
30633
|
+
}
|
|
30634
|
+
return {
|
|
30635
|
+
ok: false,
|
|
30636
|
+
reason: "extract-error",
|
|
30637
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
30638
|
+
};
|
|
30639
|
+
}
|
|
30640
|
+
}
|
|
30641
|
+
function resolvesWithin(base, target) {
|
|
30642
|
+
const resolved = path19.resolve(base, target);
|
|
30643
|
+
const baseResolved = path19.resolve(base);
|
|
30644
|
+
const rel = path19.relative(baseResolved, resolved);
|
|
30645
|
+
if (rel === "")
|
|
30646
|
+
return true;
|
|
30647
|
+
return !rel.startsWith("..") && !path19.isAbsolute(rel);
|
|
30648
|
+
}
|
|
30649
|
+
var TYPE_CHAR_TO_TYPE = {
|
|
30650
|
+
"-": "file",
|
|
30651
|
+
"d": "dir",
|
|
30652
|
+
"l": "symlink",
|
|
30653
|
+
"h": "hardlink"
|
|
30654
|
+
};
|
|
30655
|
+
var DATE_MARKER_RE = /(?:\d{1,2}:\d{2}|(?:19|20)\d{2})\s+(.+)$/;
|
|
30656
|
+
function parseTarListLine(line) {
|
|
30657
|
+
const trimmed = line.trimEnd();
|
|
30658
|
+
if (trimmed.length === 0)
|
|
30659
|
+
return null;
|
|
30660
|
+
const typeChar = trimmed[0];
|
|
30661
|
+
if (typeChar === void 0)
|
|
30662
|
+
return null;
|
|
30663
|
+
const type = TYPE_CHAR_TO_TYPE[typeChar];
|
|
30664
|
+
if (!type)
|
|
30665
|
+
return null;
|
|
30666
|
+
const match = trimmed.match(DATE_MARKER_RE);
|
|
30667
|
+
if (!match)
|
|
30668
|
+
return null;
|
|
30669
|
+
const remainderRaw = match[1];
|
|
30670
|
+
if (remainderRaw === void 0)
|
|
30671
|
+
return null;
|
|
30672
|
+
let remainder = remainderRaw;
|
|
30673
|
+
if (remainder.startsWith("./"))
|
|
30674
|
+
remainder = remainder.slice(2);
|
|
30675
|
+
const symlinkSplit = remainder.indexOf(" -> ");
|
|
30676
|
+
if (type === "symlink" && symlinkSplit !== -1) {
|
|
30677
|
+
return {
|
|
30678
|
+
type: "symlink",
|
|
30679
|
+
name: remainder.slice(0, symlinkSplit),
|
|
30680
|
+
linkname: remainder.slice(symlinkSplit + 4)
|
|
30681
|
+
};
|
|
30682
|
+
}
|
|
30683
|
+
const hardlinkMarker = " link to ";
|
|
30684
|
+
const hardlinkSplit = remainder.indexOf(hardlinkMarker);
|
|
30685
|
+
if (type === "hardlink" && hardlinkSplit !== -1) {
|
|
30686
|
+
return {
|
|
30687
|
+
type: "hardlink",
|
|
30688
|
+
name: remainder.slice(0, hardlinkSplit),
|
|
30689
|
+
linkname: remainder.slice(hardlinkSplit + hardlinkMarker.length)
|
|
30690
|
+
};
|
|
30691
|
+
}
|
|
30692
|
+
return { type, name: remainder };
|
|
30693
|
+
}
|
|
30694
|
+
function validateHardlinksBinary(tarPath, targetDir) {
|
|
30695
|
+
let raw;
|
|
30696
|
+
try {
|
|
30697
|
+
raw = gunzipSync(fs16.readFileSync(tarPath));
|
|
30698
|
+
} catch {
|
|
30699
|
+
return null;
|
|
30700
|
+
}
|
|
30701
|
+
let offset = 0;
|
|
30702
|
+
while (offset + 512 <= raw.length) {
|
|
30703
|
+
const block = raw.subarray(offset, offset + 512);
|
|
30704
|
+
if (!block.some((b) => b !== 0))
|
|
30705
|
+
break;
|
|
30706
|
+
const typeflag = block[156] !== void 0 ? String.fromCharCode(block[156]) : "";
|
|
30707
|
+
if (typeflag === "1") {
|
|
30708
|
+
const nameNull = block.indexOf(0, 0);
|
|
30709
|
+
const name = block.subarray(0, nameNull >= 0 && nameNull <= 99 ? nameNull : 100).toString("utf-8");
|
|
30710
|
+
const linkNull = block.indexOf(0, 157);
|
|
30711
|
+
const linkname = block.subarray(157, linkNull >= 157 && linkNull <= 256 ? linkNull : 257).toString("utf-8");
|
|
30712
|
+
if (linkname && (path19.isAbsolute(linkname) || !resolvesWithin(targetDir, linkname))) {
|
|
30713
|
+
return {
|
|
30714
|
+
valid: false,
|
|
30715
|
+
reason: "hardlink-escape",
|
|
30716
|
+
unsafePath: name || tarPath,
|
|
30717
|
+
detail: `linkname=${linkname} resolves outside targetDir`
|
|
30718
|
+
};
|
|
30719
|
+
}
|
|
30720
|
+
}
|
|
30721
|
+
const sizeRaw = block.subarray(124, 136).toString("utf-8").replace(/[\0 ]/g, "");
|
|
30722
|
+
const size = sizeRaw ? parseInt(sizeRaw, 8) : 0;
|
|
30723
|
+
offset += (1 + Math.ceil((isNaN(size) ? 0 : size) / 512)) * 512;
|
|
30724
|
+
}
|
|
30725
|
+
return null;
|
|
30726
|
+
}
|
|
30727
|
+
function enumerateAndValidateTarballEntries(tarPath, targetDir) {
|
|
30728
|
+
let raw;
|
|
30729
|
+
try {
|
|
30730
|
+
raw = execFileSync4("tar", ["-tvf", tarPath], {
|
|
30731
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
30732
|
+
env: { ...process.env, LC_ALL: "C", TZ: "UTC" },
|
|
30733
|
+
encoding: "utf-8",
|
|
30734
|
+
maxBuffer: 64 * 1024 * 1024
|
|
30735
|
+
// 64 MiB ceiling for very large tarballs
|
|
30736
|
+
});
|
|
30737
|
+
} catch (err) {
|
|
30738
|
+
return {
|
|
30739
|
+
valid: false,
|
|
30740
|
+
reason: "parse-error",
|
|
30741
|
+
unsafePath: tarPath,
|
|
30742
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
30743
|
+
};
|
|
30744
|
+
}
|
|
30745
|
+
const entries = [];
|
|
30746
|
+
for (const line of raw.split("\n")) {
|
|
30747
|
+
const entry = parseTarListLine(line);
|
|
30748
|
+
if (!entry)
|
|
30749
|
+
continue;
|
|
30750
|
+
if (path19.isAbsolute(entry.name) || !resolvesWithin(targetDir, entry.name)) {
|
|
30751
|
+
return {
|
|
30752
|
+
valid: false,
|
|
30753
|
+
reason: "path-traversal",
|
|
30754
|
+
unsafePath: entry.name
|
|
30755
|
+
};
|
|
30756
|
+
}
|
|
30757
|
+
if (entry.type === "symlink" && entry.linkname !== void 0) {
|
|
30758
|
+
const symlinkParent = path19.join(targetDir, path19.dirname(entry.name));
|
|
30759
|
+
if (path19.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, path19.join(path19.dirname(entry.name), entry.linkname))) {
|
|
30760
|
+
return {
|
|
30761
|
+
valid: false,
|
|
30762
|
+
reason: "symlink-escape",
|
|
30763
|
+
unsafePath: entry.name,
|
|
30764
|
+
detail: `linkname=${entry.linkname} resolves outside ${symlinkParent}`
|
|
30765
|
+
};
|
|
30766
|
+
}
|
|
30767
|
+
}
|
|
30768
|
+
if (entry.type === "hardlink" && entry.linkname !== void 0) {
|
|
30769
|
+
if (path19.isAbsolute(entry.linkname) || !resolvesWithin(targetDir, entry.linkname)) {
|
|
30770
|
+
return {
|
|
30771
|
+
valid: false,
|
|
30772
|
+
reason: "hardlink-escape",
|
|
30773
|
+
unsafePath: entry.name,
|
|
30774
|
+
detail: `linkname=${entry.linkname} resolves outside targetDir`
|
|
30775
|
+
};
|
|
30776
|
+
}
|
|
30777
|
+
}
|
|
30778
|
+
entries.push(entry);
|
|
30779
|
+
}
|
|
30780
|
+
const hardlinkResult = validateHardlinksBinary(tarPath, targetDir);
|
|
30781
|
+
if (hardlinkResult !== null)
|
|
30782
|
+
return hardlinkResult;
|
|
30783
|
+
return { valid: true, entries };
|
|
30784
|
+
}
|
|
30785
|
+
var KINDS_BY_REPO = [
|
|
30786
|
+
{ kind: "gems", targetSubpath: "vendor/bundle", computeFp: computeGemsFingerprint },
|
|
30787
|
+
{ kind: "node", targetSubpath: "node_modules", computeFp: computeNodeFingerprint }
|
|
30788
|
+
];
|
|
30789
|
+
function restoreSnapshotsForRepos(input) {
|
|
30790
|
+
const restored = /* @__PURE__ */ new Map();
|
|
30791
|
+
const outcomes = [];
|
|
30792
|
+
for (const repo of input.repos) {
|
|
30793
|
+
const repoRestored = [];
|
|
30794
|
+
for (const { kind, targetSubpath, computeFp } of KINDS_BY_REPO) {
|
|
30795
|
+
const fingerprint = computeFp(repo.worktreeDir, input.imageDigest);
|
|
30796
|
+
if (!fingerprint) {
|
|
30797
|
+
outcomes.push({ repo: repo.name, kind, outcome: "miss", reason: "no-lockfile" });
|
|
30798
|
+
continue;
|
|
30799
|
+
}
|
|
30800
|
+
const archDir = snapshotKindDirByWorkspace(input.workspace, input.arch, kind);
|
|
30801
|
+
const tarFilename = `${repo.name}-${input.arch}-${fingerprint}.tar.gz`;
|
|
30802
|
+
const tarPath = path19.join(archDir, tarFilename);
|
|
30803
|
+
if (!fs16.existsSync(tarPath)) {
|
|
30804
|
+
outcomes.push({ repo: repo.name, kind, outcome: "miss", reason: "no-tarball", fingerprint });
|
|
30805
|
+
continue;
|
|
30806
|
+
}
|
|
30807
|
+
const manifest = readManifest(tarPath);
|
|
30808
|
+
if (!manifest || manifest.arch !== input.arch) {
|
|
30809
|
+
outcomes.push({
|
|
30810
|
+
repo: repo.name,
|
|
30811
|
+
kind,
|
|
30812
|
+
outcome: "refused",
|
|
30813
|
+
reason: "arch-mismatch",
|
|
30814
|
+
fingerprint
|
|
30815
|
+
});
|
|
30816
|
+
continue;
|
|
30817
|
+
}
|
|
30818
|
+
const targetDir = path19.join(repo.worktreeDir, targetSubpath);
|
|
30819
|
+
try {
|
|
30820
|
+
fs16.rmSync(targetDir, { recursive: true, force: true });
|
|
30821
|
+
} catch {
|
|
30822
|
+
}
|
|
30823
|
+
const result = unpackTarballAtomic(tarPath, targetDir);
|
|
30824
|
+
if (!result.ok) {
|
|
30825
|
+
outcomes.push({
|
|
30826
|
+
repo: repo.name,
|
|
30827
|
+
kind,
|
|
30828
|
+
outcome: "refused",
|
|
30829
|
+
reason: result.reason,
|
|
30830
|
+
fingerprint
|
|
30831
|
+
});
|
|
30832
|
+
try {
|
|
30833
|
+
fs16.rmSync(tarPath, { force: true });
|
|
30834
|
+
fs16.rmSync(manifestPath(tarPath), { force: true });
|
|
30835
|
+
} catch {
|
|
30836
|
+
}
|
|
30837
|
+
continue;
|
|
30838
|
+
}
|
|
30839
|
+
outcomes.push({ repo: repo.name, kind, outcome: "hit", fingerprint });
|
|
30840
|
+
repoRestored.push(kind);
|
|
30841
|
+
}
|
|
30842
|
+
if (repoRestored.length > 0) {
|
|
30843
|
+
restored.set(repo.name, repoRestored);
|
|
30844
|
+
}
|
|
30845
|
+
}
|
|
30846
|
+
return { restored, outcomes };
|
|
30847
|
+
}
|
|
30848
|
+
function readManifest(tarPath) {
|
|
30849
|
+
const mPath = manifestPath(tarPath);
|
|
30850
|
+
if (!fs16.existsSync(mPath))
|
|
30851
|
+
return null;
|
|
30852
|
+
try {
|
|
30853
|
+
return JSON.parse(fs16.readFileSync(mPath, "utf-8"));
|
|
30854
|
+
} catch {
|
|
30855
|
+
return null;
|
|
30856
|
+
}
|
|
30857
|
+
}
|
|
30858
|
+
var EVICT_LOCK_FILENAME = ".evict.lock";
|
|
30859
|
+
function isPidAlive(pid) {
|
|
30860
|
+
try {
|
|
30861
|
+
process.kill(pid, 0);
|
|
30862
|
+
return true;
|
|
30863
|
+
} catch {
|
|
30864
|
+
return false;
|
|
30865
|
+
}
|
|
30866
|
+
}
|
|
30867
|
+
function evictOldSnapshotsWithFlock(maxBytes, dir = snapshotsDir()) {
|
|
30868
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
30869
|
+
const lockPath = path19.join(dir, EVICT_LOCK_FILENAME);
|
|
30870
|
+
let fd;
|
|
30871
|
+
try {
|
|
30872
|
+
fd = fs16.openSync(lockPath, fs16.constants.O_WRONLY | fs16.constants.O_CREAT | fs16.constants.O_EXCL, 384);
|
|
30873
|
+
} catch (err) {
|
|
30874
|
+
if (err.code !== "EEXIST")
|
|
30875
|
+
return 0;
|
|
30876
|
+
let holderPid = null;
|
|
30877
|
+
try {
|
|
30878
|
+
holderPid = parseInt(fs16.readFileSync(lockPath, "utf-8").trim(), 10);
|
|
30879
|
+
} catch {
|
|
30880
|
+
holderPid = null;
|
|
30881
|
+
}
|
|
30882
|
+
if (holderPid && Number.isInteger(holderPid) && isPidAlive(holderPid)) {
|
|
30883
|
+
return 0;
|
|
30884
|
+
}
|
|
30885
|
+
try {
|
|
30886
|
+
fs16.unlinkSync(lockPath);
|
|
30887
|
+
fd = fs16.openSync(lockPath, fs16.constants.O_WRONLY | fs16.constants.O_CREAT | fs16.constants.O_EXCL, 384);
|
|
30888
|
+
} catch {
|
|
30889
|
+
return 0;
|
|
30890
|
+
}
|
|
30891
|
+
}
|
|
30892
|
+
try {
|
|
30893
|
+
fs16.writeSync(fd, `${process.pid}
|
|
30894
|
+
`);
|
|
30895
|
+
} finally {
|
|
30896
|
+
fs16.closeSync(fd);
|
|
30897
|
+
}
|
|
30898
|
+
try {
|
|
30899
|
+
return evictOldSnapshots(maxBytes, dir);
|
|
30900
|
+
} finally {
|
|
30901
|
+
try {
|
|
30902
|
+
fs16.unlinkSync(lockPath);
|
|
30903
|
+
} catch {
|
|
30904
|
+
}
|
|
30905
|
+
}
|
|
30906
|
+
}
|
|
30907
|
+
function spawnAutoCapture(worldId, olamBin = "olam") {
|
|
30908
|
+
if (process.env["OLAM_SNAPSHOT_AUTO_CAPTURE"] === "0")
|
|
30909
|
+
return null;
|
|
30910
|
+
if (!/^[a-zA-Z0-9_\-.]+$/.test(worldId))
|
|
30911
|
+
return null;
|
|
30912
|
+
try {
|
|
30913
|
+
const child = spawn(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
|
|
30914
|
+
detached: true,
|
|
30915
|
+
stdio: "ignore",
|
|
30916
|
+
// OLAM_INTERNAL_SNAPSHOT=1 sentinel — Phase D D1's deprecation
|
|
30917
|
+
// counter (in packages/cli/src/commands/world-snapshot.ts) skips
|
|
30918
|
+
// the bump when this env var is set. Preserves D22 signal
|
|
30919
|
+
// integrity: the counter measures operator-driven invocations
|
|
30920
|
+
// only, not auto-capture-triggered ones. See CP3 review HIGH
|
|
30921
|
+
// finding for context.
|
|
30922
|
+
env: {
|
|
30923
|
+
...process.env,
|
|
30924
|
+
OLAM_INTERNAL_SNAPSHOT: "1",
|
|
30925
|
+
NODE_OPTIONS: process.env["NODE_OPTIONS"] ?? ""
|
|
30926
|
+
}
|
|
30927
|
+
});
|
|
30928
|
+
child.unref();
|
|
30929
|
+
return child.pid ?? null;
|
|
30930
|
+
} catch {
|
|
30931
|
+
return null;
|
|
30932
|
+
}
|
|
30933
|
+
}
|
|
30934
|
+
function evictOldSnapshots(maxBytes, dir = snapshotsDir()) {
|
|
30935
|
+
if (!fs16.existsSync(dir))
|
|
30936
|
+
return 0;
|
|
30937
|
+
const allTars = [];
|
|
30938
|
+
const walk = (d) => {
|
|
30939
|
+
for (const entry of fs16.readdirSync(d, { withFileTypes: true })) {
|
|
30940
|
+
const full = path19.join(d, entry.name);
|
|
30941
|
+
if (entry.isDirectory()) {
|
|
30942
|
+
walk(full);
|
|
30943
|
+
} else if (entry.name.endsWith(".tar.gz")) {
|
|
30944
|
+
const stat = fs16.statSync(full);
|
|
30945
|
+
allTars.push({ path: full, size: stat.size, mtime: stat.mtimeMs });
|
|
30946
|
+
}
|
|
30947
|
+
}
|
|
30948
|
+
};
|
|
30949
|
+
walk(dir);
|
|
30950
|
+
const total = allTars.reduce((acc, t) => acc + t.size, 0);
|
|
30951
|
+
if (total <= maxBytes)
|
|
30952
|
+
return 0;
|
|
30953
|
+
allTars.sort((a, b) => a.mtime - b.mtime);
|
|
30954
|
+
let freed = 0;
|
|
30955
|
+
let remaining = total;
|
|
30956
|
+
for (const tar of allTars) {
|
|
30957
|
+
if (remaining <= maxBytes)
|
|
30958
|
+
break;
|
|
30959
|
+
fs16.rmSync(tar.path, { force: true });
|
|
30960
|
+
fs16.rmSync(manifestPath(tar.path), { force: true });
|
|
30961
|
+
freed += tar.size;
|
|
30962
|
+
remaining -= tar.size;
|
|
30963
|
+
}
|
|
30964
|
+
return freed;
|
|
30965
|
+
}
|
|
30966
|
+
|
|
30063
30967
|
// ../core/dist/world/secrets-fetcher.js
|
|
30064
|
-
import { execSync as
|
|
30968
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
30065
30969
|
function parseGcpSecretUrl(url) {
|
|
30066
30970
|
if (!url.startsWith("gcp://"))
|
|
30067
30971
|
return null;
|
|
@@ -30076,7 +30980,7 @@ function parseGcpSecretUrl(url) {
|
|
|
30076
30980
|
return { project, secretName };
|
|
30077
30981
|
}
|
|
30078
30982
|
function defaultExecFn(cmd, opts) {
|
|
30079
|
-
return
|
|
30983
|
+
return execSync4(cmd, { encoding: "utf-8", timeout: opts?.timeout ?? 15e3 });
|
|
30080
30984
|
}
|
|
30081
30985
|
function fetchGcpSecret(ref, execFn = defaultExecFn) {
|
|
30082
30986
|
const { project, secretName } = ref;
|
|
@@ -30169,14 +31073,14 @@ function gcloudAvailable(execFn = defaultExecFn) {
|
|
|
30169
31073
|
}
|
|
30170
31074
|
|
|
30171
31075
|
// ../core/dist/world/olam-yaml.js
|
|
30172
|
-
import * as
|
|
31076
|
+
import * as path20 from "node:path";
|
|
30173
31077
|
import YAML2 from "yaml";
|
|
30174
31078
|
function enrichReposWithManifests(repos, workspacePath) {
|
|
30175
31079
|
return repos.map((repo) => {
|
|
30176
31080
|
if (repo.manifest !== void 0 && repo.manifest !== null) {
|
|
30177
31081
|
return repo;
|
|
30178
31082
|
}
|
|
30179
|
-
const repoDir =
|
|
31083
|
+
const repoDir = path20.join(workspacePath, repo.name);
|
|
30180
31084
|
let manifest = null;
|
|
30181
31085
|
try {
|
|
30182
31086
|
manifest = loadRepoManifest(repoDir);
|
|
@@ -30191,8 +31095,8 @@ function enrichReposWithManifests(repos, workspacePath) {
|
|
|
30191
31095
|
}
|
|
30192
31096
|
|
|
30193
31097
|
// ../core/dist/policies/loader.js
|
|
30194
|
-
import * as
|
|
30195
|
-
import * as
|
|
31098
|
+
import * as fs17 from "node:fs";
|
|
31099
|
+
import * as path21 from "node:path";
|
|
30196
31100
|
import { parse as parseYaml3 } from "yaml";
|
|
30197
31101
|
function parseFrontmatter(content) {
|
|
30198
31102
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
|
|
@@ -30212,20 +31116,20 @@ function toStringArray(v) {
|
|
|
30212
31116
|
return v.filter((x) => typeof x === "string");
|
|
30213
31117
|
}
|
|
30214
31118
|
function loadPolicies(workspaceRoot) {
|
|
30215
|
-
const policiesDir =
|
|
30216
|
-
if (!
|
|
31119
|
+
const policiesDir = path21.join(workspaceRoot, ".olam", "policies");
|
|
31120
|
+
if (!fs17.existsSync(policiesDir))
|
|
30217
31121
|
return [];
|
|
30218
31122
|
let files;
|
|
30219
31123
|
try {
|
|
30220
|
-
files =
|
|
31124
|
+
files = fs17.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
|
|
30221
31125
|
} catch {
|
|
30222
31126
|
return [];
|
|
30223
31127
|
}
|
|
30224
31128
|
const policies = [];
|
|
30225
31129
|
for (const file of files) {
|
|
30226
|
-
const filePath =
|
|
31130
|
+
const filePath = path21.join(policiesDir, file);
|
|
30227
31131
|
try {
|
|
30228
|
-
const content =
|
|
31132
|
+
const content = fs17.readFileSync(filePath, "utf8");
|
|
30229
31133
|
const parsed = parseFrontmatter(content);
|
|
30230
31134
|
if (!parsed) {
|
|
30231
31135
|
console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
|
|
@@ -30275,17 +31179,93 @@ function formatPoliciesBrief(policies) {
|
|
|
30275
31179
|
return lines.join("\n");
|
|
30276
31180
|
}
|
|
30277
31181
|
|
|
31182
|
+
// ../core/dist/global-config/runbook-resolver.js
|
|
31183
|
+
import * as fs18 from "node:fs";
|
|
31184
|
+
import * as os12 from "node:os";
|
|
31185
|
+
import * as path22 from "node:path";
|
|
31186
|
+
function expandTilde(p) {
|
|
31187
|
+
if (p === "~" || p.startsWith("~/")) {
|
|
31188
|
+
return path22.join(os12.homedir(), p.slice(1));
|
|
31189
|
+
}
|
|
31190
|
+
return p;
|
|
31191
|
+
}
|
|
31192
|
+
function resolveRunbookToWorldParams(runbook, repoRegistry) {
|
|
31193
|
+
const registryMap = new Map(repoRegistry.map((r) => [r.name, r]));
|
|
31194
|
+
for (const repoName of runbook.repos) {
|
|
31195
|
+
const entry = registryMap.get(repoName);
|
|
31196
|
+
if (!entry) {
|
|
31197
|
+
throw new Error(`repo "${repoName}" is referenced by runbook "${runbook.name}" but is not in the registry. Run "olam repos add ${repoName} --path <path>" to register it.`);
|
|
31198
|
+
}
|
|
31199
|
+
const resolvedPath = expandTilde(entry.path);
|
|
31200
|
+
if (!fs18.existsSync(resolvedPath)) {
|
|
31201
|
+
throw new Error(`repo "${repoName}" path "${resolvedPath}" no longer exists. Run "olam repos update ${repoName} --path <new-path>" to fix.`);
|
|
31202
|
+
}
|
|
31203
|
+
}
|
|
31204
|
+
return {
|
|
31205
|
+
repoNames: [...runbook.repos],
|
|
31206
|
+
portOverrides: runbook.portMap ?? {},
|
|
31207
|
+
envOverrides: runbook.env ?? {},
|
|
31208
|
+
seeds: runbook.seeds
|
|
31209
|
+
};
|
|
31210
|
+
}
|
|
31211
|
+
|
|
31212
|
+
// ../core/dist/world/bootstrap-hooks.js
|
|
31213
|
+
import * as fs19 from "node:fs";
|
|
31214
|
+
import * as path23 from "node:path";
|
|
31215
|
+
function runFixtureCopySeeds(seeds, workspacePath) {
|
|
31216
|
+
if (!seeds)
|
|
31217
|
+
return;
|
|
31218
|
+
for (const seed of seeds) {
|
|
31219
|
+
if (seed.type !== "fixture-copy")
|
|
31220
|
+
continue;
|
|
31221
|
+
const srcAbs = path23.resolve(workspacePath, seed.repo, seed.src);
|
|
31222
|
+
const destAbs = path23.resolve(workspacePath, seed.repo, seed.dest);
|
|
31223
|
+
const destDir = path23.dirname(destAbs);
|
|
31224
|
+
fs19.mkdirSync(destDir, { recursive: true });
|
|
31225
|
+
fs19.cpSync(srcAbs, destAbs, { recursive: true, force: true });
|
|
31226
|
+
}
|
|
31227
|
+
}
|
|
31228
|
+
async function runSeedHooks(seeds, containerName, servicePortMap, exec) {
|
|
31229
|
+
if (!seeds)
|
|
31230
|
+
return;
|
|
31231
|
+
for (const seed of seeds) {
|
|
31232
|
+
if (seed.type === "fixture-copy")
|
|
31233
|
+
continue;
|
|
31234
|
+
if (seed.type === "sql-file") {
|
|
31235
|
+
const servicePorts = servicePortMap[seed.repo] ?? {};
|
|
31236
|
+
const port = servicePorts[seed.service] ?? 5432;
|
|
31237
|
+
const waitCmd = `until pg_isready -h localhost -p ${port} -t 1 2>/dev/null; do sleep 2; done`;
|
|
31238
|
+
const psqlCmd = `psql -h localhost -p ${port} -f /home/olam/workspace/${seed.repo}/${seed.path}`;
|
|
31239
|
+
const combined = `${waitCmd} && ${psqlCmd}`;
|
|
31240
|
+
try {
|
|
31241
|
+
exec(containerName, combined);
|
|
31242
|
+
} catch (err) {
|
|
31243
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31244
|
+
throw new Error(`seed failed for repo "${seed.repo}" (type: sql-file, service: "${seed.service}", path: "${seed.path}"): ${msg}`);
|
|
31245
|
+
}
|
|
31246
|
+
} else if (seed.type === "command") {
|
|
31247
|
+
const workdir = `/home/olam/workspace/${seed.repo}`;
|
|
31248
|
+
try {
|
|
31249
|
+
exec(containerName, `cd ${workdir} && ${seed.run}`);
|
|
31250
|
+
} catch (err) {
|
|
31251
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31252
|
+
throw new Error(`seed failed for repo "${seed.repo}" (type: command, run: "${seed.run}"): ${msg}`);
|
|
31253
|
+
}
|
|
31254
|
+
}
|
|
31255
|
+
}
|
|
31256
|
+
}
|
|
31257
|
+
|
|
30278
31258
|
// ../core/dist/world/tmux-supervisor.js
|
|
30279
31259
|
var PortInUseError = class extends Error {
|
|
30280
31260
|
port;
|
|
30281
31261
|
repo;
|
|
30282
31262
|
manifestPath;
|
|
30283
|
-
constructor(port, repo,
|
|
30284
|
-
super(`Port ${port} already in use on host (claimed by repo "${repo}"). Free the port or change \`app_port\` in ${
|
|
31263
|
+
constructor(port, repo, manifestPath2) {
|
|
31264
|
+
super(`Port ${port} already in use on host (claimed by repo "${repo}"). Free the port or change \`app_port\` in ${manifestPath2}.`);
|
|
30285
31265
|
this.name = "PortInUseError";
|
|
30286
31266
|
this.port = port;
|
|
30287
31267
|
this.repo = repo;
|
|
30288
|
-
this.manifestPath =
|
|
31268
|
+
this.manifestPath = manifestPath2;
|
|
30289
31269
|
}
|
|
30290
31270
|
};
|
|
30291
31271
|
var SAFE_IDENT2 = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
@@ -30380,7 +31360,7 @@ var BotIdentityError = class extends Error {
|
|
|
30380
31360
|
this.name = "BotIdentityError";
|
|
30381
31361
|
}
|
|
30382
31362
|
};
|
|
30383
|
-
function getTokenScopes(ghToken, _exec =
|
|
31363
|
+
function getTokenScopes(ghToken, _exec = execSync5) {
|
|
30384
31364
|
try {
|
|
30385
31365
|
const out = _exec("gh auth status 2>&1", {
|
|
30386
31366
|
encoding: "utf-8",
|
|
@@ -30397,13 +31377,13 @@ function getTokenScopes(ghToken, _exec = execSync4) {
|
|
|
30397
31377
|
}
|
|
30398
31378
|
}
|
|
30399
31379
|
async function setupContainerGit(containerName, repos, branch) {
|
|
30400
|
-
const dockerExec = (cmd) =>
|
|
31380
|
+
const dockerExec = (cmd) => execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, {
|
|
30401
31381
|
stdio: "pipe",
|
|
30402
31382
|
timeout: 6e4
|
|
30403
31383
|
}).toString();
|
|
30404
31384
|
let ghToken = "";
|
|
30405
31385
|
try {
|
|
30406
|
-
ghToken =
|
|
31386
|
+
ghToken = execSync5("gh auth token 2>/dev/null", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
30407
31387
|
} catch {
|
|
30408
31388
|
}
|
|
30409
31389
|
const actorName = process.env.OLAM_BOT_NAME ?? "Claude Code (olam)";
|
|
@@ -30423,7 +31403,7 @@ async function setupContainerGit(containerName, repos, branch) {
|
|
|
30423
31403
|
continue;
|
|
30424
31404
|
const ownerRepo = ghMatch[1];
|
|
30425
31405
|
try {
|
|
30426
|
-
|
|
31406
|
+
execSync5(`gh api repos/${ownerRepo} --silent`, {
|
|
30427
31407
|
stdio: "pipe",
|
|
30428
31408
|
timeout: 5e3,
|
|
30429
31409
|
env: { ...process.env, GH_TOKEN: ghToken }
|
|
@@ -30471,8 +31451,8 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
|
|
|
30471
31451
|
if (!olamUserPresent) {
|
|
30472
31452
|
const imageName = (() => {
|
|
30473
31453
|
try {
|
|
30474
|
-
const { execSync:
|
|
30475
|
-
return
|
|
31454
|
+
const { execSync: execSync7 } = __require("node:child_process");
|
|
31455
|
+
return execSync7(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
|
|
30476
31456
|
encoding: "utf8",
|
|
30477
31457
|
timeout: 5e3
|
|
30478
31458
|
}).trim() || "(unknown)";
|
|
@@ -30515,7 +31495,7 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
|
|
|
30515
31495
|
function makeHostExecFn() {
|
|
30516
31496
|
return async (cmd) => {
|
|
30517
31497
|
try {
|
|
30518
|
-
const stdout =
|
|
31498
|
+
const stdout = execSync5(cmd, { encoding: "utf-8", timeout: 5e3 });
|
|
30519
31499
|
return { stdout, stderr: "", exitCode: 0 };
|
|
30520
31500
|
} catch {
|
|
30521
31501
|
return { stdout: "", stderr: "", exitCode: 1 };
|
|
@@ -30525,7 +31505,7 @@ function makeHostExecFn() {
|
|
|
30525
31505
|
function makeContainerExecFn(containerName) {
|
|
30526
31506
|
return async (cmd) => {
|
|
30527
31507
|
try {
|
|
30528
|
-
const result =
|
|
31508
|
+
const result = execSync5(`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
|
|
30529
31509
|
return { stdout: result.toString(), stderr: "", exitCode: 0 };
|
|
30530
31510
|
} catch (err) {
|
|
30531
31511
|
const execErr = err;
|
|
@@ -30539,7 +31519,7 @@ function makeContainerExecFn(containerName) {
|
|
|
30539
31519
|
}
|
|
30540
31520
|
function defaultDockerExec() {
|
|
30541
31521
|
return (containerName, cmd) => {
|
|
30542
|
-
const result =
|
|
31522
|
+
const result = execSync5(
|
|
30543
31523
|
`docker exec ${containerName} sh -c '${cmd.replace(/'/g, "'\\''")}'`,
|
|
30544
31524
|
// Phase E E5 raise: 10min was too tight on cold-boot for atlas-core's
|
|
30545
31525
|
// `rails db:create` chain (Rails 7 boot + initializer load + first
|
|
@@ -30763,8 +31743,28 @@ var WorldManager = class {
|
|
|
30763
31743
|
throw new AuthPreflightError(preflight.verdict, preflight.message, preflight.remedy);
|
|
30764
31744
|
}
|
|
30765
31745
|
}
|
|
31746
|
+
let runbookPortOverrides = {};
|
|
31747
|
+
let runbookEnvOverrides = {};
|
|
31748
|
+
let runbookSeeds;
|
|
31749
|
+
if (opts.runbookName) {
|
|
31750
|
+
const runbook = getRunbook(opts.runbookName);
|
|
31751
|
+
const globalConfig2 = readGlobalConfig();
|
|
31752
|
+
const resolved = resolveRunbookToWorldParams(runbook, globalConfig2.repos);
|
|
31753
|
+
const { conflicts } = validateRunbookPorts(runbook);
|
|
31754
|
+
if (conflicts.length > 0) {
|
|
31755
|
+
const detail = conflicts.map((c) => {
|
|
31756
|
+
const occ = c.occupant.type === "olam-world" ? `in use by world "${c.occupant.worldId}" (olam)` : `in use by PID ${c.occupant.pid ?? "unknown"} (non-olam)`;
|
|
31757
|
+
return ` ${c.repoName}.${c.serviceName}:${c.port} \u2014 ${occ}`;
|
|
31758
|
+
}).join("\n");
|
|
31759
|
+
throw new Error(`Port conflicts detected for runbook "${opts.runbookName}":
|
|
31760
|
+
${detail}`);
|
|
31761
|
+
}
|
|
31762
|
+
runbookPortOverrides = resolved.portOverrides;
|
|
31763
|
+
runbookEnvOverrides = resolved.envOverrides;
|
|
31764
|
+
runbookSeeds = resolved.seeds;
|
|
31765
|
+
}
|
|
30766
31766
|
const worldId = generateWorldId();
|
|
30767
|
-
const workspacePath =
|
|
31767
|
+
const workspacePath = path24.join(os13.homedir(), ".olam", "worlds", worldId);
|
|
30768
31768
|
const portOffset = this.registry.getNextPortOffset();
|
|
30769
31769
|
const branch = opts.branchName ?? `olam/${worldId}`;
|
|
30770
31770
|
const repos = this.resolveReposWithWorkspace(opts);
|
|
@@ -30796,6 +31796,19 @@ var WorldManager = class {
|
|
|
30796
31796
|
this.registry.update(worldId, { status: "error" });
|
|
30797
31797
|
throw err;
|
|
30798
31798
|
}
|
|
31799
|
+
if (opts.runbookName && runbookSeeds) {
|
|
31800
|
+
try {
|
|
31801
|
+
runFixtureCopySeeds(runbookSeeds, workspacePath);
|
|
31802
|
+
} catch (err) {
|
|
31803
|
+
sm.transition("error");
|
|
31804
|
+
this.registry.update(worldId, { status: "error" });
|
|
31805
|
+
try {
|
|
31806
|
+
await removeWorktrees(repos, workspacePath);
|
|
31807
|
+
} catch {
|
|
31808
|
+
}
|
|
31809
|
+
throw err;
|
|
31810
|
+
}
|
|
31811
|
+
}
|
|
30799
31812
|
try {
|
|
30800
31813
|
const baseline = snapshotBaselineDiff(repos, workspacePath);
|
|
30801
31814
|
console.log(formatBaselineSummary(baseline));
|
|
@@ -30820,38 +31833,38 @@ var WorldManager = class {
|
|
|
30820
31833
|
for (const repo of repos) {
|
|
30821
31834
|
if (!repo.path)
|
|
30822
31835
|
continue;
|
|
30823
|
-
const sourceRoot = repo.path.replace(/^~/,
|
|
30824
|
-
const worktreeRoot =
|
|
30825
|
-
if (!
|
|
31836
|
+
const sourceRoot = repo.path.replace(/^~/, os13.homedir());
|
|
31837
|
+
const worktreeRoot = path24.join(workspacePath, repo.name);
|
|
31838
|
+
if (!fs20.existsSync(sourceRoot) || !fs20.existsSync(worktreeRoot))
|
|
30826
31839
|
continue;
|
|
30827
31840
|
let copied = 0;
|
|
30828
31841
|
for (const pattern of RUNTIME_FILE_PATTERNS) {
|
|
30829
31842
|
const matches2 = [];
|
|
30830
31843
|
if (pattern.includes("*")) {
|
|
30831
|
-
const [dir, glob] = [
|
|
30832
|
-
const sourceDir =
|
|
30833
|
-
if (
|
|
31844
|
+
const [dir, glob] = [path24.dirname(pattern), path24.basename(pattern)];
|
|
31845
|
+
const sourceDir = path24.join(sourceRoot, dir);
|
|
31846
|
+
if (fs20.existsSync(sourceDir)) {
|
|
30834
31847
|
const ext = glob.replace(/^\*+/, "");
|
|
30835
31848
|
try {
|
|
30836
|
-
for (const entry of
|
|
31849
|
+
for (const entry of fs20.readdirSync(sourceDir)) {
|
|
30837
31850
|
if (ext === "" || entry.endsWith(ext))
|
|
30838
|
-
matches2.push(
|
|
31851
|
+
matches2.push(path24.join(dir, entry));
|
|
30839
31852
|
}
|
|
30840
31853
|
} catch {
|
|
30841
31854
|
}
|
|
30842
31855
|
}
|
|
30843
|
-
} else if (
|
|
31856
|
+
} else if (fs20.existsSync(path24.join(sourceRoot, pattern))) {
|
|
30844
31857
|
matches2.push(pattern);
|
|
30845
31858
|
}
|
|
30846
31859
|
for (const rel of matches2) {
|
|
30847
|
-
const src =
|
|
30848
|
-
const dst =
|
|
31860
|
+
const src = path24.join(sourceRoot, rel);
|
|
31861
|
+
const dst = path24.join(worktreeRoot, rel);
|
|
30849
31862
|
try {
|
|
30850
|
-
const st =
|
|
31863
|
+
const st = fs20.statSync(src);
|
|
30851
31864
|
if (!st.isFile())
|
|
30852
31865
|
continue;
|
|
30853
|
-
|
|
30854
|
-
|
|
31866
|
+
fs20.mkdirSync(path24.dirname(dst), { recursive: true });
|
|
31867
|
+
fs20.copyFileSync(src, dst);
|
|
30855
31868
|
copied++;
|
|
30856
31869
|
} catch {
|
|
30857
31870
|
}
|
|
@@ -30951,7 +31964,7 @@ var WorldManager = class {
|
|
|
30951
31964
|
try {
|
|
30952
31965
|
const hostExec = makeHostExecFn();
|
|
30953
31966
|
for (const repo of repos) {
|
|
30954
|
-
const repoDir =
|
|
31967
|
+
const repoDir = path24.join(workspacePath, repo.name);
|
|
30955
31968
|
if (repo.stack && Object.keys(repo.stack).length > 0) {
|
|
30956
31969
|
preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
|
|
30957
31970
|
} else {
|
|
@@ -30977,11 +31990,19 @@ var WorldManager = class {
|
|
|
30977
31990
|
}
|
|
30978
31991
|
let cacheArchOverride;
|
|
30979
31992
|
if (!stackCacheHit) {
|
|
30980
|
-
const selected =
|
|
31993
|
+
const selected = selectDevboxImageForWorld({
|
|
31994
|
+
config: this.config,
|
|
31995
|
+
repos,
|
|
31996
|
+
worldspec: opts.worldspec
|
|
31997
|
+
});
|
|
30981
31998
|
if (selected) {
|
|
30982
31999
|
selectedImage = selected.image;
|
|
30983
32000
|
cacheArchOverride = selected.cacheArch;
|
|
30984
|
-
|
|
32001
|
+
if (selected.source === "worldspec") {
|
|
32002
|
+
console.log(`[WorldManager] worldspec override \u2014 using ${selected.image} (tag=${selected.tag})`);
|
|
32003
|
+
} else {
|
|
32004
|
+
console.log(`[WorldManager] image_selector matched \u2014 using ${selected.image} (tag=${selected.tag}${selected.cacheArch ? `, cache_arch=${selected.cacheArch}` : ""})`);
|
|
32005
|
+
}
|
|
30985
32006
|
} else {
|
|
30986
32007
|
const hasRailsRepo = repos.some((r) => r.type === "rails");
|
|
30987
32008
|
if (hasRailsRepo) {
|
|
@@ -31001,17 +32022,18 @@ var WorldManager = class {
|
|
|
31001
32022
|
}
|
|
31002
32023
|
}
|
|
31003
32024
|
const appPortUrls = appPorts.map((ap) => {
|
|
31004
|
-
const
|
|
32025
|
+
const override = runbookPortOverrides[ap.name]?.["app"];
|
|
32026
|
+
const hostPort = override !== void 0 ? override : ap.port + 1e4 + portOffset;
|
|
31005
32027
|
return { repoName: ap.name, internalPort: ap.port, hostPort, url: `http://localhost:${hostPort}` };
|
|
31006
32028
|
});
|
|
31007
32029
|
try {
|
|
31008
32030
|
const worldEnv = {};
|
|
31009
32031
|
if (opts.task)
|
|
31010
32032
|
worldEnv.OLAM_TASK = opts.task;
|
|
31011
|
-
const r2CredsPath =
|
|
31012
|
-
if (
|
|
32033
|
+
const r2CredsPath = path24.join(os13.homedir(), ".olam", "r2-credentials.json");
|
|
32034
|
+
if (fs20.existsSync(r2CredsPath)) {
|
|
31013
32035
|
try {
|
|
31014
|
-
const r2Raw =
|
|
32036
|
+
const r2Raw = fs20.readFileSync(r2CredsPath, "utf-8").trim();
|
|
31015
32037
|
if (r2Raw.length > 0) {
|
|
31016
32038
|
const r2 = JSON.parse(r2Raw);
|
|
31017
32039
|
if (typeof r2.account_id === "string")
|
|
@@ -31028,10 +32050,10 @@ var WorldManager = class {
|
|
|
31028
32050
|
} catch {
|
|
31029
32051
|
}
|
|
31030
32052
|
}
|
|
31031
|
-
const keysYamlPath =
|
|
31032
|
-
if (
|
|
32053
|
+
const keysYamlPath = path24.join(os13.homedir(), ".olam", "keys.yaml");
|
|
32054
|
+
if (fs20.existsSync(keysYamlPath)) {
|
|
31033
32055
|
try {
|
|
31034
|
-
const keysRaw =
|
|
32056
|
+
const keysRaw = fs20.readFileSync(keysYamlPath, "utf-8").trim();
|
|
31035
32057
|
if (keysRaw.length > 0) {
|
|
31036
32058
|
const parsed = YAML3.parse(keysRaw);
|
|
31037
32059
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -31067,6 +32089,18 @@ var WorldManager = class {
|
|
|
31067
32089
|
seenFromRepo.set(k, repo.name);
|
|
31068
32090
|
}
|
|
31069
32091
|
}
|
|
32092
|
+
if (opts.runbookName) {
|
|
32093
|
+
for (const [repoName, repoEnv] of Object.entries(runbookEnvOverrides)) {
|
|
32094
|
+
for (const [k, v] of Object.entries(repoEnv)) {
|
|
32095
|
+
if (PROTECTED_ENV_KEY_SET.has(k))
|
|
32096
|
+
continue;
|
|
32097
|
+
if (k.startsWith("OLAM_"))
|
|
32098
|
+
continue;
|
|
32099
|
+
worldEnv[k] = v;
|
|
32100
|
+
}
|
|
32101
|
+
void repoName;
|
|
32102
|
+
}
|
|
32103
|
+
}
|
|
31070
32104
|
if (repoSecretUrls.length > 0) {
|
|
31071
32105
|
const { env: gcpSecrets, fileWrites } = fetchSecretsForRepos(repoSecretUrls);
|
|
31072
32106
|
for (const [k, v] of Object.entries(gcpSecrets)) {
|
|
@@ -31078,10 +32112,10 @@ var WorldManager = class {
|
|
|
31078
32112
|
worldEnv[k] = v;
|
|
31079
32113
|
}
|
|
31080
32114
|
for (const { repoName, relativePath, content } of fileWrites) {
|
|
31081
|
-
const absPath =
|
|
32115
|
+
const absPath = path24.join(workspacePath, repoName, relativePath);
|
|
31082
32116
|
try {
|
|
31083
|
-
|
|
31084
|
-
|
|
32117
|
+
fs20.mkdirSync(path24.dirname(absPath), { recursive: true });
|
|
32118
|
+
fs20.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
|
|
31085
32119
|
mode: 384
|
|
31086
32120
|
});
|
|
31087
32121
|
console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
|
|
@@ -31187,6 +32221,44 @@ var WorldManager = class {
|
|
|
31187
32221
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31188
32222
|
console.warn(`[WorldManager] stack installation failed: ${msg}`);
|
|
31189
32223
|
}
|
|
32224
|
+
try {
|
|
32225
|
+
cleanupLegacyByWorldDir(worldId);
|
|
32226
|
+
const restoreResult = restoreSnapshotsForRepos({
|
|
32227
|
+
workspace: opts.workspace ?? "default",
|
|
32228
|
+
arch: process.arch,
|
|
32229
|
+
imageDigest: void 0,
|
|
32230
|
+
repos: enrichedRepos.map((r) => ({
|
|
32231
|
+
name: r.name,
|
|
32232
|
+
worktreeDir: path24.join(workspacePath, r.name)
|
|
32233
|
+
}))
|
|
32234
|
+
});
|
|
32235
|
+
for (const out of restoreResult.outcomes) {
|
|
32236
|
+
if (out.outcome === "hit") {
|
|
32237
|
+
console.log(`[snapshot] Restored ${out.kind} for ${out.repo} (${out.fingerprint?.slice(0, 12)})`);
|
|
32238
|
+
} else if (out.outcome === "refused") {
|
|
32239
|
+
console.warn(`[snapshot] Skipped ${out.repo} ${out.kind}: ${out.reason ?? "unknown"}`);
|
|
32240
|
+
}
|
|
32241
|
+
}
|
|
32242
|
+
for (const [repoName, restoredKinds] of restoreResult.restored) {
|
|
32243
|
+
const repo = enrichedRepos.find((r) => r.name === repoName);
|
|
32244
|
+
if (!repo?.manifest?.bootstrap)
|
|
32245
|
+
continue;
|
|
32246
|
+
const containerWorkdir = `/home/olam/workspace/${repoName}`;
|
|
32247
|
+
repo.manifest.bootstrap.forEach((step, idx) => {
|
|
32248
|
+
const produces = typeof step === "object" && step !== null ? step.produces : void 0;
|
|
32249
|
+
if (produces && restoredKinds.includes(produces)) {
|
|
32250
|
+
const sentinel = bootstrapStepSentinelPath(containerWorkdir, idx);
|
|
32251
|
+
try {
|
|
32252
|
+
this.dockerExec(containerName, `touch ${sentinel}`);
|
|
32253
|
+
} catch {
|
|
32254
|
+
}
|
|
32255
|
+
}
|
|
32256
|
+
});
|
|
32257
|
+
}
|
|
32258
|
+
} catch (err) {
|
|
32259
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32260
|
+
console.warn(`[snapshot] restore failed (cold path): ${msg}`);
|
|
32261
|
+
}
|
|
31190
32262
|
try {
|
|
31191
32263
|
await runManifestRuntime(containerName, worldId, enrichedRepos, this.dockerExec);
|
|
31192
32264
|
} catch (err) {
|
|
@@ -31211,12 +32283,30 @@ var WorldManager = class {
|
|
|
31211
32283
|
console.warn(`[manifest] step 4e/4f/4g non-fatal failure: ${msg}`);
|
|
31212
32284
|
}
|
|
31213
32285
|
}
|
|
32286
|
+
if (opts.runbookName && runbookSeeds) {
|
|
32287
|
+
const servicePortMap = {};
|
|
32288
|
+
for (const repo of enrichedRepos) {
|
|
32289
|
+
if (!repo.manifest?.services)
|
|
32290
|
+
continue;
|
|
32291
|
+
const svcPorts = {};
|
|
32292
|
+
for (const [svcName, svc] of Object.entries(repo.manifest.services)) {
|
|
32293
|
+
svcPorts[svcName] = svc.port ?? 5432;
|
|
32294
|
+
}
|
|
32295
|
+
servicePortMap[repo.name] = svcPorts;
|
|
32296
|
+
}
|
|
32297
|
+
try {
|
|
32298
|
+
await runSeedHooks(runbookSeeds, containerName, servicePortMap, this.dockerExec);
|
|
32299
|
+
} catch (err) {
|
|
32300
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
32301
|
+
console.warn(`[runbook] seed hook failed: ${msg}`);
|
|
32302
|
+
}
|
|
32303
|
+
}
|
|
31214
32304
|
for (const repo of repos) {
|
|
31215
32305
|
if (repo.setup_commands.length > 0) {
|
|
31216
32306
|
const escapedDir = `/home/olam/workspace/${repo.name.replace(/["$`\\]/g, "\\$&")}`;
|
|
31217
32307
|
for (const cmd of repo.setup_commands) {
|
|
31218
32308
|
try {
|
|
31219
|
-
|
|
32309
|
+
execSync5(`docker exec ${containerName} sh -c 'cd "${escapedDir}" && ${cmd.replace(/'/g, "'\\''")}'`, { stdio: "pipe", timeout: 6e5 });
|
|
31220
32310
|
} catch (err) {
|
|
31221
32311
|
const msg = err instanceof Error ? err.message : String(err);
|
|
31222
32312
|
console.warn(`[WorldManager] setup command failed for ${repo.name}: ${msg}`);
|
|
@@ -31226,14 +32316,14 @@ var WorldManager = class {
|
|
|
31226
32316
|
}
|
|
31227
32317
|
if (credentialsInjected.claude) {
|
|
31228
32318
|
try {
|
|
31229
|
-
|
|
32319
|
+
execSync5(`docker exec ${containerName} curl -sf -X POST http://localhost:8080/session/start-agent 2>/dev/null || true`, { stdio: "pipe", timeout: 45e3 });
|
|
31230
32320
|
} catch {
|
|
31231
32321
|
}
|
|
31232
32322
|
if (opts.task) {
|
|
31233
32323
|
let taskWithPolicies = opts.task;
|
|
31234
32324
|
try {
|
|
31235
32325
|
const allPolicies = repos.flatMap((repo) => {
|
|
31236
|
-
const repoWorktree =
|
|
32326
|
+
const repoWorktree = path24.join(workspacePath, repo.name);
|
|
31237
32327
|
return loadPolicies(repoWorktree);
|
|
31238
32328
|
});
|
|
31239
32329
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -31247,11 +32337,11 @@ var WorldManager = class {
|
|
|
31247
32337
|
const brief = formatPoliciesBrief(uniquePolicies);
|
|
31248
32338
|
taskWithPolicies = `${brief}## Your task
|
|
31249
32339
|
${opts.task}`;
|
|
31250
|
-
|
|
32340
|
+
execSync5(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
|
|
31251
32341
|
for (const repo of repos) {
|
|
31252
|
-
const policiesDir =
|
|
31253
|
-
if (
|
|
31254
|
-
|
|
32342
|
+
const policiesDir = path24.join(workspacePath, repo.name, ".olam", "policies");
|
|
32343
|
+
if (fs20.existsSync(policiesDir)) {
|
|
32344
|
+
execSync5(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
|
|
31255
32345
|
}
|
|
31256
32346
|
}
|
|
31257
32347
|
}
|
|
@@ -31261,7 +32351,7 @@ ${opts.task}`;
|
|
|
31261
32351
|
}
|
|
31262
32352
|
try {
|
|
31263
32353
|
const payload = JSON.stringify({ prompt: taskWithPolicies }).replace(/'/g, "'\\''");
|
|
31264
|
-
|
|
32354
|
+
execSync5(`docker exec ${containerName} curl -sf -X POST -H 'Content-Type: application/json' -d '${payload}' http://localhost:8080/dispatch 2>/dev/null || true`, { stdio: "pipe", timeout: 3e4 });
|
|
31265
32355
|
console.log("[world] Task auto-dispatched");
|
|
31266
32356
|
} catch {
|
|
31267
32357
|
}
|
|
@@ -31272,6 +32362,19 @@ ${opts.task}`;
|
|
|
31272
32362
|
const terminalPort = 17681 + portOffset;
|
|
31273
32363
|
const dashboardUrl = `http://localhost:${dashboardPort}`;
|
|
31274
32364
|
const terminalUrl = `http://localhost:${terminalPort}`;
|
|
32365
|
+
try {
|
|
32366
|
+
spawnAutoCapture(worldId);
|
|
32367
|
+
} catch {
|
|
32368
|
+
}
|
|
32369
|
+
try {
|
|
32370
|
+
const cap = parseInt(process.env["OLAM_SNAPSHOT_MAX_BYTES"] ?? "", 10);
|
|
32371
|
+
const maxBytes = Number.isInteger(cap) && cap > 0 ? cap : 5 * 1024 * 1024 * 1024;
|
|
32372
|
+
const freed = evictOldSnapshotsWithFlock(maxBytes);
|
|
32373
|
+
if (freed > 0) {
|
|
32374
|
+
console.log(`[snapshot] evicted ${(freed / 1024 / 1024).toFixed(0)}MB to stay under ${(maxBytes / 1024 / 1024 / 1024).toFixed(1)}GB cap`);
|
|
32375
|
+
}
|
|
32376
|
+
} catch {
|
|
32377
|
+
}
|
|
31275
32378
|
return {
|
|
31276
32379
|
...metadata,
|
|
31277
32380
|
status: "running",
|
|
@@ -31329,8 +32432,8 @@ ${opts.task}`;
|
|
|
31329
32432
|
} catch {
|
|
31330
32433
|
}
|
|
31331
32434
|
try {
|
|
31332
|
-
|
|
31333
|
-
if (
|
|
32435
|
+
fs20.rmSync(world.workspacePath, { recursive: true, force: true });
|
|
32436
|
+
if (fs20.existsSync(world.workspacePath)) {
|
|
31334
32437
|
console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
|
|
31335
32438
|
}
|
|
31336
32439
|
} catch (err) {
|
|
@@ -31415,14 +32518,14 @@ ${opts.task}`;
|
|
|
31415
32518
|
return names.map((name) => this.config.repos.find((r) => r.name === name)).filter((r) => r !== void 0);
|
|
31416
32519
|
}
|
|
31417
32520
|
transportPlanFile(planFilePath, workspacePath, repoNames) {
|
|
31418
|
-
const planContent =
|
|
31419
|
-
const planFileName =
|
|
32521
|
+
const planContent = fs20.readFileSync(planFilePath, "utf-8");
|
|
32522
|
+
const planFileName = path24.basename(planFilePath);
|
|
31420
32523
|
const targetRepo = repoNames[0];
|
|
31421
32524
|
if (!targetRepo)
|
|
31422
32525
|
return;
|
|
31423
|
-
const plansDir =
|
|
31424
|
-
|
|
31425
|
-
|
|
32526
|
+
const plansDir = path24.join(workspacePath, targetRepo, "docs", "plans");
|
|
32527
|
+
fs20.mkdirSync(plansDir, { recursive: true });
|
|
32528
|
+
fs20.writeFileSync(path24.join(plansDir, planFileName), planContent);
|
|
31426
32529
|
}
|
|
31427
32530
|
resolveServices(repos) {
|
|
31428
32531
|
const services = [];
|
|
@@ -31507,8 +32610,8 @@ import * as http2 from "node:http";
|
|
|
31507
32610
|
|
|
31508
32611
|
// ../core/dist/dashboard/server.js
|
|
31509
32612
|
import * as http from "node:http";
|
|
31510
|
-
import * as
|
|
31511
|
-
import * as
|
|
32613
|
+
import * as fs21 from "node:fs";
|
|
32614
|
+
import * as path25 from "node:path";
|
|
31512
32615
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
31513
32616
|
|
|
31514
32617
|
// ../core/dist/dashboard/serialize.js
|
|
@@ -31843,7 +32946,7 @@ function notFound(res) {
|
|
|
31843
32946
|
}
|
|
31844
32947
|
function openThoughtStore(workspacePath) {
|
|
31845
32948
|
const dbPath = getWorldDbPath(workspacePath);
|
|
31846
|
-
if (!
|
|
32949
|
+
if (!fs21.existsSync(dbPath))
|
|
31847
32950
|
return null;
|
|
31848
32951
|
return new ThoughtLocalStore(dbPath);
|
|
31849
32952
|
}
|
|
@@ -32014,13 +33117,13 @@ function findSessionInWorld(registry2, sessionId) {
|
|
|
32014
33117
|
}
|
|
32015
33118
|
function createDashboardServer(opts) {
|
|
32016
33119
|
const { port, registry: registry2 } = opts;
|
|
32017
|
-
const thisDir =
|
|
32018
|
-
const defaultPublicDir =
|
|
33120
|
+
const thisDir = path25.dirname(fileURLToPath2(import.meta.url));
|
|
33121
|
+
const defaultPublicDir = path25.resolve(thisDir, "../../../control-plane/public");
|
|
32019
33122
|
const publicDir = opts.publicDir ?? defaultPublicDir;
|
|
32020
|
-
let hasPublicDir =
|
|
33123
|
+
let hasPublicDir = fs21.existsSync(publicDir);
|
|
32021
33124
|
const server = http.createServer((req, res) => {
|
|
32022
33125
|
if (!hasPublicDir) {
|
|
32023
|
-
hasPublicDir =
|
|
33126
|
+
hasPublicDir = fs21.existsSync(publicDir);
|
|
32024
33127
|
}
|
|
32025
33128
|
const host = req.headers.host ?? `localhost:${port}`;
|
|
32026
33129
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
@@ -32294,22 +33397,22 @@ function createDashboardServer(opts) {
|
|
|
32294
33397
|
res.end(`<html><body style="font-family:system-ui;padding:2rem"><h1>Olam Dashboard</h1><p>The React app has not been built yet.</p><p>Run <code>npm run build:app</code> in <code>packages/control-plane</code> to build it.</p><p>API routes are available at <code>/api/*</code>.</p></body></html>`);
|
|
32295
33398
|
return;
|
|
32296
33399
|
}
|
|
32297
|
-
let filePath =
|
|
33400
|
+
let filePath = path25.join(publicDir, pathname === "/" ? "index.html" : pathname);
|
|
32298
33401
|
if (!filePath.startsWith(publicDir)) {
|
|
32299
33402
|
notFound(res);
|
|
32300
33403
|
return;
|
|
32301
33404
|
}
|
|
32302
|
-
if (
|
|
32303
|
-
const ext =
|
|
33405
|
+
if (fs21.existsSync(filePath) && fs21.statSync(filePath).isFile()) {
|
|
33406
|
+
const ext = path25.extname(filePath);
|
|
32304
33407
|
const contentType = MIME[ext] ?? "application/octet-stream";
|
|
32305
33408
|
res.writeHead(200, { "Content-Type": contentType });
|
|
32306
|
-
|
|
33409
|
+
fs21.createReadStream(filePath).pipe(res);
|
|
32307
33410
|
return;
|
|
32308
33411
|
}
|
|
32309
|
-
filePath =
|
|
32310
|
-
if (
|
|
33412
|
+
filePath = path25.join(publicDir, "index.html");
|
|
33413
|
+
if (fs21.existsSync(filePath)) {
|
|
32311
33414
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
32312
|
-
|
|
33415
|
+
fs21.createReadStream(filePath).pipe(res);
|
|
32313
33416
|
return;
|
|
32314
33417
|
}
|
|
32315
33418
|
notFound(res);
|
|
@@ -32319,17 +33422,17 @@ function createDashboardServer(opts) {
|
|
|
32319
33422
|
}
|
|
32320
33423
|
|
|
32321
33424
|
// ../core/dist/dashboard/state.js
|
|
32322
|
-
import * as
|
|
32323
|
-
import * as
|
|
32324
|
-
import * as
|
|
32325
|
-
var STATE_PATH =
|
|
33425
|
+
import * as fs22 from "node:fs";
|
|
33426
|
+
import * as os14 from "node:os";
|
|
33427
|
+
import * as path26 from "node:path";
|
|
33428
|
+
var STATE_PATH = path26.join(os14.homedir(), ".olam", "dashboard.json");
|
|
32326
33429
|
function saveDashboardState(state) {
|
|
32327
|
-
|
|
32328
|
-
|
|
33430
|
+
fs22.mkdirSync(path26.dirname(STATE_PATH), { recursive: true });
|
|
33431
|
+
fs22.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
32329
33432
|
}
|
|
32330
33433
|
function loadDashboardState() {
|
|
32331
33434
|
try {
|
|
32332
|
-
const raw =
|
|
33435
|
+
const raw = fs22.readFileSync(STATE_PATH, "utf-8");
|
|
32333
33436
|
return JSON.parse(raw);
|
|
32334
33437
|
} catch {
|
|
32335
33438
|
return null;
|
|
@@ -32337,7 +33440,7 @@ function loadDashboardState() {
|
|
|
32337
33440
|
}
|
|
32338
33441
|
function clearDashboardState() {
|
|
32339
33442
|
try {
|
|
32340
|
-
|
|
33443
|
+
fs22.unlinkSync(STATE_PATH);
|
|
32341
33444
|
} catch {
|
|
32342
33445
|
}
|
|
32343
33446
|
}
|
|
@@ -32355,19 +33458,19 @@ function isDashboardRunning() {
|
|
|
32355
33458
|
}
|
|
32356
33459
|
|
|
32357
33460
|
// ../core/dist/dashboard/tunnel.js
|
|
32358
|
-
import { spawn, execSync as
|
|
33461
|
+
import { spawn as spawn2, execSync as execSync6 } from "node:child_process";
|
|
32359
33462
|
var tunnelProcess = null;
|
|
32360
33463
|
function isCloudflaredAvailable() {
|
|
32361
33464
|
try {
|
|
32362
|
-
|
|
33465
|
+
execSync6("which cloudflared", { stdio: "ignore" });
|
|
32363
33466
|
return true;
|
|
32364
33467
|
} catch {
|
|
32365
33468
|
return false;
|
|
32366
33469
|
}
|
|
32367
33470
|
}
|
|
32368
33471
|
function startTunnel(port) {
|
|
32369
|
-
return new Promise((
|
|
32370
|
-
const child =
|
|
33472
|
+
return new Promise((resolve8, reject2) => {
|
|
33473
|
+
const child = spawn2("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
|
|
32371
33474
|
stdio: ["ignore", "pipe", "pipe"],
|
|
32372
33475
|
detached: false
|
|
32373
33476
|
});
|
|
@@ -32388,7 +33491,7 @@ function startTunnel(port) {
|
|
|
32388
33491
|
if (match) {
|
|
32389
33492
|
resolved = true;
|
|
32390
33493
|
clearTimeout(timeout);
|
|
32391
|
-
|
|
33494
|
+
resolve8(match[0]);
|
|
32392
33495
|
}
|
|
32393
33496
|
}
|
|
32394
33497
|
child.stdout?.on("data", scan);
|
|
@@ -32456,8 +33559,8 @@ var DashboardManager = class {
|
|
|
32456
33559
|
}
|
|
32457
33560
|
throw err;
|
|
32458
33561
|
}
|
|
32459
|
-
await new Promise((
|
|
32460
|
-
this.server.on("listening",
|
|
33562
|
+
await new Promise((resolve8, reject2) => {
|
|
33563
|
+
this.server.on("listening", resolve8);
|
|
32461
33564
|
this.server.on("error", reject2);
|
|
32462
33565
|
});
|
|
32463
33566
|
this.info = { localUrl: `http://localhost:${port}` };
|
|
@@ -32503,8 +33606,8 @@ var DashboardManager = class {
|
|
|
32503
33606
|
async stop() {
|
|
32504
33607
|
stopTunnel();
|
|
32505
33608
|
if (this.server) {
|
|
32506
|
-
await new Promise((
|
|
32507
|
-
this.server.close(() =>
|
|
33609
|
+
await new Promise((resolve8) => {
|
|
33610
|
+
this.server.close(() => resolve8());
|
|
32508
33611
|
});
|
|
32509
33612
|
this.server = null;
|
|
32510
33613
|
}
|
|
@@ -32617,8 +33720,8 @@ var PleriClient = class {
|
|
|
32617
33720
|
};
|
|
32618
33721
|
|
|
32619
33722
|
// ../mcp-server/src/env-loader.ts
|
|
32620
|
-
import { readFileSync as
|
|
32621
|
-
import { join as
|
|
33723
|
+
import { readFileSync as readFileSync16, existsSync as existsSync21, statSync as statSync7 } from "node:fs";
|
|
33724
|
+
import { join as join27, dirname as dirname15, resolve as resolve7 } from "node:path";
|
|
32622
33725
|
var PROJECT_MARKERS = [
|
|
32623
33726
|
".olam/config.yaml",
|
|
32624
33727
|
".olam/config.yml",
|
|
@@ -32626,30 +33729,30 @@ var PROJECT_MARKERS = [
|
|
|
32626
33729
|
"olam.yml"
|
|
32627
33730
|
];
|
|
32628
33731
|
function findProjectRoot2(startDir) {
|
|
32629
|
-
let dir =
|
|
32630
|
-
const root =
|
|
33732
|
+
let dir = resolve7(startDir);
|
|
33733
|
+
const root = resolve7("/");
|
|
32631
33734
|
while (true) {
|
|
32632
33735
|
for (const marker of PROJECT_MARKERS) {
|
|
32633
|
-
if (
|
|
33736
|
+
if (existsSync21(join27(dir, marker))) return dir;
|
|
32634
33737
|
}
|
|
32635
|
-
const pkg =
|
|
32636
|
-
if (
|
|
33738
|
+
const pkg = join27(dir, "package.json");
|
|
33739
|
+
if (existsSync21(pkg)) {
|
|
32637
33740
|
try {
|
|
32638
|
-
const json = JSON.parse(
|
|
33741
|
+
const json = JSON.parse(readFileSync16(pkg, "utf8"));
|
|
32639
33742
|
const isOlamWorkspace = typeof json.name === "string" && json.name.startsWith("@olam/");
|
|
32640
33743
|
const hasOlamDep = json.dependencies && Object.keys(json.dependencies).some((k) => k.startsWith("@olam/")) || json.devDependencies && Object.keys(json.devDependencies).some((k) => k.startsWith("@olam/"));
|
|
32641
33744
|
if (isOlamWorkspace || hasOlamDep) return dir;
|
|
32642
33745
|
} catch {
|
|
32643
33746
|
}
|
|
32644
33747
|
}
|
|
32645
|
-
const parent =
|
|
33748
|
+
const parent = dirname15(dir);
|
|
32646
33749
|
if (parent === dir || parent === root) return null;
|
|
32647
33750
|
dir = parent;
|
|
32648
33751
|
}
|
|
32649
33752
|
}
|
|
32650
|
-
function parseEnvFile(
|
|
33753
|
+
function parseEnvFile(path27) {
|
|
32651
33754
|
const out = {};
|
|
32652
|
-
const raw =
|
|
33755
|
+
const raw = readFileSync16(path27, "utf8");
|
|
32653
33756
|
for (const line of raw.split(/\r?\n/)) {
|
|
32654
33757
|
const trimmed = line.trim();
|
|
32655
33758
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -32672,8 +33775,8 @@ function loadProjectEnv(startDir = process.cwd()) {
|
|
|
32672
33775
|
const filesRead = [];
|
|
32673
33776
|
const merged = {};
|
|
32674
33777
|
for (const name of [".env", ".env.local"]) {
|
|
32675
|
-
const p =
|
|
32676
|
-
if (
|
|
33778
|
+
const p = join27(root, name);
|
|
33779
|
+
if (existsSync21(p) && statSync7(p).isFile()) {
|
|
32677
33780
|
Object.assign(merged, parseEnvFile(p));
|
|
32678
33781
|
filesRead.push(p);
|
|
32679
33782
|
}
|
|
@@ -32743,7 +33846,7 @@ async function main() {
|
|
|
32743
33846
|
initError = err instanceof Error ? err : new Error(String(err));
|
|
32744
33847
|
logger.error("Failed to initialize Olam", { error: initError.message });
|
|
32745
33848
|
}
|
|
32746
|
-
const server =
|
|
33849
|
+
const server = createServer4(ctx, initError);
|
|
32747
33850
|
const transport = new StdioServerTransport();
|
|
32748
33851
|
await server.connect(transport);
|
|
32749
33852
|
logger.info("Olam MCP server running on stdio transport");
|