@pleri/olam-cli 0.1.120 → 0.1.126
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/commands/bootstrap.d.ts +4 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +64 -0
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +43 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/mcp/install-shared.d.ts +2 -0
- package/dist/commands/mcp/install-shared.d.ts.map +1 -1
- package/dist/commands/mcp/install-shared.js +13 -0
- package/dist/commands/mcp/install-shared.js.map +1 -1
- package/dist/commands/mcp/uninstall.d.ts.map +1 -1
- package/dist/commands/mcp/uninstall.js +1 -10
- package/dist/commands/mcp/uninstall.js.map +1 -1
- package/dist/commands/memory/_paths.d.ts +15 -0
- package/dist/commands/memory/_paths.d.ts.map +1 -0
- package/dist/commands/memory/_paths.js +34 -0
- package/dist/commands/memory/_paths.js.map +1 -0
- package/dist/commands/memory/index.d.ts +17 -0
- package/dist/commands/memory/index.d.ts.map +1 -0
- package/dist/commands/memory/index.js +34 -0
- package/dist/commands/memory/index.js.map +1 -0
- package/dist/commands/memory/install.d.ts +57 -0
- package/dist/commands/memory/install.d.ts.map +1 -0
- package/dist/commands/memory/install.js +114 -0
- package/dist/commands/memory/install.js.map +1 -0
- package/dist/commands/memory/logs.d.ts +15 -0
- package/dist/commands/memory/logs.d.ts.map +1 -0
- package/dist/commands/memory/logs.js +45 -0
- package/dist/commands/memory/logs.js.map +1 -0
- package/dist/commands/memory/secret.d.ts +16 -0
- package/dist/commands/memory/secret.d.ts.map +1 -0
- package/dist/commands/memory/secret.js +79 -0
- package/dist/commands/memory/secret.js.map +1 -0
- package/dist/commands/memory/start.d.ts +23 -0
- package/dist/commands/memory/start.d.ts.map +1 -0
- package/dist/commands/memory/start.js +166 -0
- package/dist/commands/memory/start.js.map +1 -0
- package/dist/commands/memory/status.d.ts +25 -0
- package/dist/commands/memory/status.d.ts.map +1 -0
- package/dist/commands/memory/status.js +101 -0
- package/dist/commands/memory/status.js.map +1 -0
- package/dist/commands/memory/stop.d.ts +12 -0
- package/dist/commands/memory/stop.d.ts.map +1 -0
- package/dist/commands/memory/stop.js +81 -0
- package/dist/commands/memory/stop.js.map +1 -0
- package/dist/commands/memory/uninstall.d.ts +19 -0
- package/dist/commands/memory/uninstall.d.ts.map +1 -0
- package/dist/commands/memory/uninstall.js +60 -0
- package/dist/commands/memory/uninstall.js.map +1 -0
- package/dist/commands/seed.d.ts +27 -0
- package/dist/commands/seed.d.ts.map +1 -0
- package/dist/commands/seed.js +303 -0
- package/dist/commands/seed.js.map +1 -0
- package/dist/image-digests.json +3 -3
- package/dist/index.js +1835 -259
- package/dist/index.js.map +1 -1
- package/dist/lib/memory-secret.d.ts +48 -0
- package/dist/lib/memory-secret.d.ts.map +1 -0
- package/dist/lib/memory-secret.js +92 -0
- package/dist/lib/memory-secret.js.map +1 -0
- package/dist/lib/world-mcp-register.d.ts +98 -0
- package/dist/lib/world-mcp-register.d.ts.map +1 -0
- package/dist/lib/world-mcp-register.js +117 -0
- package/dist/lib/world-mcp-register.js.map +1 -0
- package/dist/mcp-server.js +502 -65
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -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 = resolve10.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 resolve10(root, ref) {
|
|
3017
3017
|
let sch;
|
|
3018
3018
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3019
3019
|
ref = sch;
|
|
@@ -3588,7 +3588,7 @@ var require_fast_uri = __commonJS({
|
|
|
3588
3588
|
}
|
|
3589
3589
|
return uri;
|
|
3590
3590
|
}
|
|
3591
|
-
function
|
|
3591
|
+
function resolve10(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;
|
|
@@ -3815,7 +3815,7 @@ var require_fast_uri = __commonJS({
|
|
|
3815
3815
|
var fastUri = {
|
|
3816
3816
|
SCHEMES,
|
|
3817
3817
|
normalize,
|
|
3818
|
-
resolve:
|
|
3818
|
+
resolve: resolve10,
|
|
3819
3819
|
resolveComponent,
|
|
3820
3820
|
equal,
|
|
3821
3821
|
serialize,
|
|
@@ -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((resolve10) => {
|
|
12918
12918
|
const json = serializeMessage(message);
|
|
12919
12919
|
if (this._stdout.write(json)) {
|
|
12920
|
-
|
|
12920
|
+
resolve10();
|
|
12921
12921
|
} else {
|
|
12922
|
-
this._stdout.once("drain",
|
|
12922
|
+
this._stdout.once("drain", resolve10);
|
|
12923
12923
|
}
|
|
12924
12924
|
});
|
|
12925
12925
|
}
|
|
@@ -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((resolve10) => setTimeout(resolve10, 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((resolve10, 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
|
+
resolve10(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((resolve10, 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(resolve10, 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((resolve10) => setTimeout(resolve10, 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`);
|
|
@@ -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((resolve10) => setTimeout(resolve10, 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((resolve10) => setTimeout(resolve10, ms));
|
|
21547
21547
|
}
|
|
21548
21548
|
|
|
21549
21549
|
// ../core/dist/auth/preflight.js
|
|
@@ -21788,6 +21788,7 @@ function rowToMetadata(row) {
|
|
|
21788
21788
|
let readinessChain;
|
|
21789
21789
|
let expectedServices;
|
|
21790
21790
|
let appPortUrls;
|
|
21791
|
+
let worldDbNames;
|
|
21791
21792
|
try {
|
|
21792
21793
|
if (row.readiness_chain)
|
|
21793
21794
|
readinessChain = JSON.parse(row.readiness_chain);
|
|
@@ -21803,6 +21804,11 @@ function rowToMetadata(row) {
|
|
|
21803
21804
|
appPortUrls = JSON.parse(row.app_port_urls);
|
|
21804
21805
|
} catch {
|
|
21805
21806
|
}
|
|
21807
|
+
try {
|
|
21808
|
+
if (row.world_db_names)
|
|
21809
|
+
worldDbNames = JSON.parse(row.world_db_names);
|
|
21810
|
+
} catch {
|
|
21811
|
+
}
|
|
21806
21812
|
return {
|
|
21807
21813
|
id: row.id,
|
|
21808
21814
|
name: row.name,
|
|
@@ -21825,7 +21831,9 @@ function rowToMetadata(row) {
|
|
|
21825
21831
|
autoDestroyOnMerge: (row.auto_destroy_on_merge ?? 1) !== 0,
|
|
21826
21832
|
...readinessChain !== void 0 ? { readinessChain } : {},
|
|
21827
21833
|
...expectedServices !== void 0 ? { expectedServices } : {},
|
|
21828
|
-
...appPortUrls !== void 0 ? { appPortUrls } : {}
|
|
21834
|
+
...appPortUrls !== void 0 ? { appPortUrls } : {},
|
|
21835
|
+
...worldDbNames !== void 0 ? { worldDbNames } : {},
|
|
21836
|
+
...row.world_role_name ? { worldRoleName: row.world_role_name } : {}
|
|
21829
21837
|
};
|
|
21830
21838
|
}
|
|
21831
21839
|
var SCHEMA_VERSION = "1";
|
|
@@ -21871,7 +21879,17 @@ var WorldRegistry = class {
|
|
|
21871
21879
|
this.db.pragma("foreign_keys = ON");
|
|
21872
21880
|
this.db.exec(CREATE_TABLE);
|
|
21873
21881
|
this.db.exec(CREATE_META);
|
|
21874
|
-
for (const col of [
|
|
21882
|
+
for (const col of [
|
|
21883
|
+
"readiness_chain TEXT",
|
|
21884
|
+
"expected_services TEXT",
|
|
21885
|
+
"app_port_urls TEXT",
|
|
21886
|
+
"tailscale_paths TEXT",
|
|
21887
|
+
// olam-hybrid-shared-postgres Phase A task A7. JSON-serialised string for
|
|
21888
|
+
// world_db_names (SQLite has no native array; matches repos column shape).
|
|
21889
|
+
// TEXT[] would fail at DDL time (closes OQ13/OQ22 / FS-03).
|
|
21890
|
+
"world_db_names TEXT",
|
|
21891
|
+
"world_role_name TEXT"
|
|
21892
|
+
]) {
|
|
21875
21893
|
try {
|
|
21876
21894
|
this.db.exec(`ALTER TABLE worlds ADD COLUMN ${col}`);
|
|
21877
21895
|
} catch {
|
|
@@ -21897,8 +21915,9 @@ var WorldRegistry = class {
|
|
|
21897
21915
|
(id, name, status, repos, branch, port_offset, workspace_path,
|
|
21898
21916
|
compute_provider, total_cost_usd, thought_count, created_at, updated_at,
|
|
21899
21917
|
pr_url, pr_number, pr_repo, pr_created_at, pr_state, pr_merged_at, auto_destroy_on_merge,
|
|
21900
|
-
readiness_chain, expected_services, app_port_urls
|
|
21901
|
-
|
|
21918
|
+
readiness_chain, expected_services, app_port_urls,
|
|
21919
|
+
world_db_names, world_role_name)
|
|
21920
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(world.id, world.name, world.status, JSON.stringify(world.repos), world.branch, world.portOffset, world.workspacePath, world.computeProvider, world.totalCostUsd, world.thoughtCount, world.createdAt, world.updatedAt, world.prUrl ?? null, world.prNumber ?? null, world.prRepo ?? null, world.prCreatedAt ?? null, world.prState ?? "none", world.prMergedAt ?? null, world.autoDestroyOnMerge === false ? 0 : 1, world.readinessChain !== void 0 ? JSON.stringify(world.readinessChain) : null, world.expectedServices !== void 0 ? JSON.stringify(world.expectedServices) : null, world.appPortUrls !== void 0 ? JSON.stringify(world.appPortUrls) : null, world.worldDbNames !== void 0 ? JSON.stringify(world.worldDbNames) : null, world.worldRoleName ?? null);
|
|
21902
21921
|
}
|
|
21903
21922
|
update(worldId, updates) {
|
|
21904
21923
|
const setClauses = [];
|
|
@@ -21923,9 +21942,12 @@ var WorldRegistry = class {
|
|
|
21923
21942
|
autoDestroyOnMerge: "auto_destroy_on_merge",
|
|
21924
21943
|
readinessChain: "readiness_chain",
|
|
21925
21944
|
expectedServices: "expected_services",
|
|
21926
|
-
appPortUrls: "app_port_urls"
|
|
21945
|
+
appPortUrls: "app_port_urls",
|
|
21946
|
+
// olam-hybrid-shared-postgres Phase A task A7.
|
|
21947
|
+
worldDbNames: "world_db_names",
|
|
21948
|
+
worldRoleName: "world_role_name"
|
|
21927
21949
|
};
|
|
21928
|
-
const jsonColumns = /* @__PURE__ */ new Set(["repos", "readinessChain", "expectedServices", "appPortUrls"]);
|
|
21950
|
+
const jsonColumns = /* @__PURE__ */ new Set(["repos", "readinessChain", "expectedServices", "appPortUrls", "worldDbNames"]);
|
|
21929
21951
|
for (const [key, col] of Object.entries(columnMap)) {
|
|
21930
21952
|
const val = updates[key];
|
|
21931
21953
|
if (val !== void 0) {
|
|
@@ -22326,7 +22348,7 @@ var runtimeVersionSchema = external_exports.object({
|
|
|
22326
22348
|
|
|
22327
22349
|
// ../core/dist/world/repo-manifest.js
|
|
22328
22350
|
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync3 } from "node:fs";
|
|
22329
|
-
import { join as join5 } from "node:path";
|
|
22351
|
+
import { join as join5, resolve as resolve3, sep } from "node:path";
|
|
22330
22352
|
import YAML from "yaml";
|
|
22331
22353
|
|
|
22332
22354
|
// ../core/dist/manifest/version.js
|
|
@@ -22354,7 +22376,44 @@ var serviceSchema = external_exports.object({
|
|
|
22354
22376
|
// - 'world' (default): container is per-world (today's behaviour).
|
|
22355
22377
|
// - 'shared': container is shared across worlds (consumed by later phases).
|
|
22356
22378
|
// TODO(phase-D): enforce shared-service deduplication in planManifestPipeline.
|
|
22357
|
-
scope: external_exports.enum(["world", "shared"]).optional()
|
|
22379
|
+
scope: external_exports.enum(["world", "shared"]).optional(),
|
|
22380
|
+
// olam-hybrid-shared-postgres Phase A task A8.
|
|
22381
|
+
// Declares which postgres template DB(s) Olam should clone at world
|
|
22382
|
+
// create time. Consumed by `applyPostgresTemplateClone` (task A3 in
|
|
22383
|
+
// manager.ts) which runs `CREATE DATABASE <world-db> TEMPLATE <name>`
|
|
22384
|
+
// per entry. Accepts either a single string (single-DB repo) or an
|
|
22385
|
+
// array (atlas-core → ['atlas_common_seed', 'atlas_merchant_seed'];
|
|
22386
|
+
// atlas-pay → adds 'atlas_pay_seed' per Phase B Decision 17).
|
|
22387
|
+
seed_template: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).nonempty()]).optional(),
|
|
22388
|
+
// Optional hash bound to the seed_template content for cross-machine
|
|
22389
|
+
// drift detection. The A1 bake script writes this value via .adb.yaml
|
|
22390
|
+
// post-bake (operator pastes from seed/seed-hash.txt; auto-write CI
|
|
22391
|
+
// tool is a Phase D follow-up per the plan's Out of scope).
|
|
22392
|
+
seed_template_hash: external_exports.string().optional(),
|
|
22393
|
+
// olam-hybrid-shared-postgres Phase A task A5 (closes OQ15 plan-killer).
|
|
22394
|
+
// Per-clone SQL hook: after `CREATE DATABASE <world-db> TEMPLATE <seed>`,
|
|
22395
|
+
// run these statements against the cloned world-DB. Atlas-core uses this
|
|
22396
|
+
// to UPDATE `merchants.identifier` so `Current.merchant` (which resolves
|
|
22397
|
+
// via `Merchant.find_by(identifier: ENV['POSTGRESQL_MERCHANT_DATABASE'])`)
|
|
22398
|
+
// matches the per-world merchant DB name.
|
|
22399
|
+
//
|
|
22400
|
+
// Shape: { <seed_template_name>: [<sql statement>, ...] }
|
|
22401
|
+
// The seed_template_name key selects which cloned DB the statements run
|
|
22402
|
+
// in. Order is preserved within each seed's list.
|
|
22403
|
+
//
|
|
22404
|
+
// Interpolation in statements:
|
|
22405
|
+
// ${WORLD_ID} → world id (e.g. frost-oak-5916)
|
|
22406
|
+
// ${WORLD_DB:<seed>} → corresponding world-DB name for that seed
|
|
22407
|
+
// (e.g. ${WORLD_DB:atlas_merchant_seed} →
|
|
22408
|
+
// atlas_merchant_world_frost-oak-5916)
|
|
22409
|
+
//
|
|
22410
|
+
// SECURITY INVARIANT: statements come from the operator's own checked-out
|
|
22411
|
+
// `.olam.yaml` (same trust class as `BootstrapStep.cmd`). They run as the
|
|
22412
|
+
// singleton's `development` superuser today; A4's per-world role lands
|
|
22413
|
+
// later in the seam. Statements are NOT exposed to operator-controlled
|
|
22414
|
+
// env variables — only the two whitelisted placeholders above. No shell
|
|
22415
|
+
// expansion, no DOLLAR-name expansion.
|
|
22416
|
+
post_clone_sql: external_exports.record(external_exports.string().min(1), external_exports.array(external_exports.string().min(1))).optional()
|
|
22358
22417
|
}).passthrough();
|
|
22359
22418
|
var deploySchema = external_exports.object({
|
|
22360
22419
|
tags: external_exports.array(external_exports.string()).optional()
|
|
@@ -22409,7 +22468,12 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
22409
22468
|
"secrets",
|
|
22410
22469
|
"bootstrap",
|
|
22411
22470
|
"start",
|
|
22412
|
-
"deploy"
|
|
22471
|
+
"deploy",
|
|
22472
|
+
// F-7 (olam-hybrid-shared-postgres dogfood finding): native inheritance —
|
|
22473
|
+
// `.olam.yaml` may declare `inherits: .adb.yaml` to deep-merge an adb-shaped
|
|
22474
|
+
// base manifest into itself. Retires the atlas-one `bin/olam-create-wrap.sh`
|
|
22475
|
+
// stopgap. See the inheritance resolver in `loadRepoManifest`.
|
|
22476
|
+
"inherits"
|
|
22413
22477
|
]);
|
|
22414
22478
|
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
22415
22479
|
function refineForbiddenKeys(value, path28, ctx, rejectSource) {
|
|
@@ -22453,6 +22517,23 @@ function rejectForbiddenKeys(value, path28, rejectSource) {
|
|
|
22453
22517
|
function unknownTopLevelKeys(parsed) {
|
|
22454
22518
|
return Object.keys(parsed).filter((k) => !KNOWN_TOP_LEVEL_KEYS.has(k));
|
|
22455
22519
|
}
|
|
22520
|
+
function deepMergeManifest(base, overlay) {
|
|
22521
|
+
const out = { ...base };
|
|
22522
|
+
for (const key of Object.keys(overlay)) {
|
|
22523
|
+
if (FORBIDDEN_KEYS.has(key))
|
|
22524
|
+
continue;
|
|
22525
|
+
const overlayValue = overlay[key];
|
|
22526
|
+
if (overlayValue === void 0)
|
|
22527
|
+
continue;
|
|
22528
|
+
const baseValue = base[key];
|
|
22529
|
+
const bothObjects = isPlainObject3(baseValue) && isPlainObject3(overlayValue);
|
|
22530
|
+
out[key] = bothObjects ? deepMergeManifest(baseValue, overlayValue) : overlayValue;
|
|
22531
|
+
}
|
|
22532
|
+
return out;
|
|
22533
|
+
}
|
|
22534
|
+
function isPlainObject3(value) {
|
|
22535
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
22536
|
+
}
|
|
22456
22537
|
function loadRepoManifest(repoDir) {
|
|
22457
22538
|
const olamPath = join5(repoDir, ".olam.yaml");
|
|
22458
22539
|
const adbPath = join5(repoDir, ".adb.yaml");
|
|
@@ -22483,7 +22564,37 @@ function loadRepoManifest(repoDir) {
|
|
|
22483
22564
|
throw new Error(`[manifest] ${manifestPath2}: expected a YAML mapping at the top level`);
|
|
22484
22565
|
}
|
|
22485
22566
|
rejectForbiddenKeys(parsed, manifestPath2, true);
|
|
22486
|
-
|
|
22567
|
+
let mergedParsed = parsed;
|
|
22568
|
+
const inheritsRaw = parsed["inherits"];
|
|
22569
|
+
if (typeof inheritsRaw === "string" && inheritsRaw.length > 0) {
|
|
22570
|
+
if (source !== "olam") {
|
|
22571
|
+
throw new Error(`[manifest] ${manifestPath2}: \`inherits\` only valid in .olam.yaml (found in .adb.yaml \u2014 drop the field)`);
|
|
22572
|
+
}
|
|
22573
|
+
const inheritedAbs = resolve3(repoDir, inheritsRaw);
|
|
22574
|
+
const repoDirAbs = resolve3(repoDir);
|
|
22575
|
+
if (!inheritedAbs.startsWith(repoDirAbs + sep) && inheritedAbs !== repoDirAbs) {
|
|
22576
|
+
throw new Error(`[manifest] ${manifestPath2}: inherits target "${inheritsRaw}" escapes repo dir`);
|
|
22577
|
+
}
|
|
22578
|
+
if (!existsSync4(inheritedAbs)) {
|
|
22579
|
+
throw new Error(`[manifest] ${manifestPath2}: inherits target "${inheritsRaw}" not found`);
|
|
22580
|
+
}
|
|
22581
|
+
const inheritedStat = lstatSync(inheritedAbs);
|
|
22582
|
+
if (inheritedStat.isSymbolicLink()) {
|
|
22583
|
+
throw new Error(`[manifest] ${manifestPath2}: inherits target "${inheritsRaw}" is a symlink (not permitted)`);
|
|
22584
|
+
}
|
|
22585
|
+
const inheritedRaw = readFileSync3(inheritedAbs, "utf-8");
|
|
22586
|
+
const inheritedParsed = YAML.parse(inheritedRaw, { maxAliasCount: 100 });
|
|
22587
|
+
if (inheritedParsed !== null && inheritedParsed !== void 0) {
|
|
22588
|
+
if (typeof inheritedParsed !== "object" || Array.isArray(inheritedParsed)) {
|
|
22589
|
+
throw new Error(`[manifest] ${inheritedAbs}: expected a YAML mapping at the top level`);
|
|
22590
|
+
}
|
|
22591
|
+
rejectForbiddenKeys(inheritedParsed, inheritedAbs, true);
|
|
22592
|
+
const overlay = { ...parsed };
|
|
22593
|
+
delete overlay["inherits"];
|
|
22594
|
+
mergedParsed = deepMergeManifest(inheritedParsed, overlay);
|
|
22595
|
+
}
|
|
22596
|
+
}
|
|
22597
|
+
const body = RepoManifestSchema.parse(mergedParsed);
|
|
22487
22598
|
if (parsed["version"] === void 0) {
|
|
22488
22599
|
console.warn(`[manifest] ${manifestPath2}: missing "version: ${MANIFEST_VERSION}" field \u2014 add it to suppress this warning (backward-compat: file still parses)`);
|
|
22489
22600
|
}
|
|
@@ -23012,7 +23123,7 @@ var realDocker = {
|
|
|
23012
23123
|
}
|
|
23013
23124
|
};
|
|
23014
23125
|
function spawnAsync(cmd, args, opts = {}) {
|
|
23015
|
-
return new Promise((
|
|
23126
|
+
return new Promise((resolve10) => {
|
|
23016
23127
|
const child = spawn(cmd, [...args], {
|
|
23017
23128
|
stdio: ["ignore", "pipe", "pipe"],
|
|
23018
23129
|
signal: opts.signal
|
|
@@ -23026,10 +23137,10 @@ function spawnAsync(cmd, args, opts = {}) {
|
|
|
23026
23137
|
stderr += chunk.toString();
|
|
23027
23138
|
});
|
|
23028
23139
|
child.on("error", (err) => {
|
|
23029
|
-
|
|
23140
|
+
resolve10({ exitCode: -1, stdout, stderr: stderr + err.message });
|
|
23030
23141
|
});
|
|
23031
23142
|
child.on("close", (code) => {
|
|
23032
|
-
|
|
23143
|
+
resolve10({ exitCode: code ?? -1, stdout, stderr });
|
|
23033
23144
|
});
|
|
23034
23145
|
});
|
|
23035
23146
|
}
|
|
@@ -23107,6 +23218,18 @@ function readHostCpToken() {
|
|
|
23107
23218
|
return "";
|
|
23108
23219
|
}
|
|
23109
23220
|
}
|
|
23221
|
+
function readMemorySecret() {
|
|
23222
|
+
const fromEnv = process.env["OLAM_MEMORY_SECRET"];
|
|
23223
|
+
if (fromEnv && fromEnv.length > 0)
|
|
23224
|
+
return fromEnv;
|
|
23225
|
+
const file = path7.join(os5.homedir(), ".olam", "memory-secret");
|
|
23226
|
+
try {
|
|
23227
|
+
return fs5.readFileSync(file, "utf-8").trim();
|
|
23228
|
+
} catch {
|
|
23229
|
+
return "";
|
|
23230
|
+
}
|
|
23231
|
+
}
|
|
23232
|
+
var AGENTMEMORY_HOST_INTERNAL_URL = "http://host.docker.internal:3111";
|
|
23110
23233
|
function sanitizeContainerName(name) {
|
|
23111
23234
|
return name.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
23112
23235
|
}
|
|
@@ -23236,6 +23359,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
|
|
|
23236
23359
|
const labels = olamLabels(worldId, worldName);
|
|
23237
23360
|
const authSecret = readAuthSecret();
|
|
23238
23361
|
const hostCpToken = readHostCpToken();
|
|
23362
|
+
const memorySecret = readMemorySecret();
|
|
23239
23363
|
const worldEnv = {
|
|
23240
23364
|
OLAM_WORLD_ID: worldId,
|
|
23241
23365
|
OLAM_WORLD_NAME: worldName,
|
|
@@ -23252,6 +23376,14 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
|
|
|
23252
23376
|
OLAM_HOST_CP_URL: "http://host.docker.internal:19000",
|
|
23253
23377
|
...authSecret ? { OLAM_AUTH_SECRET: authSecret } : {},
|
|
23254
23378
|
...hostCpToken ? { OLAM_HOST_CP_TOKEN: hostCpToken } : {},
|
|
23379
|
+
// Phase B1 — agent-memory wiring. Both vars are injected together
|
|
23380
|
+
// (or neither): without a secret the URL is useless. The in-world
|
|
23381
|
+
// @agentmemory/mcp shim (Phase B2/B3) reads these env vars to find
|
|
23382
|
+
// the host's REST endpoint.
|
|
23383
|
+
...memorySecret ? {
|
|
23384
|
+
AGENTMEMORY_URL: AGENTMEMORY_HOST_INTERNAL_URL,
|
|
23385
|
+
AGENTMEMORY_SECRET: memorySecret
|
|
23386
|
+
} : {},
|
|
23255
23387
|
...env
|
|
23256
23388
|
};
|
|
23257
23389
|
const envList = Object.entries(worldEnv).map(([k, v]) => `${k}=${v}`);
|
|
@@ -23362,7 +23494,7 @@ var stopAndRemove = async (container) => {
|
|
|
23362
23494
|
|
|
23363
23495
|
// ../adapters/dist/docker/exec.js
|
|
23364
23496
|
import { PassThrough } from "node:stream";
|
|
23365
|
-
var demuxStream = (stream) => new Promise((
|
|
23497
|
+
var demuxStream = (stream) => new Promise((resolve10, reject2) => {
|
|
23366
23498
|
const stdoutChunks = [];
|
|
23367
23499
|
const stderrChunks = [];
|
|
23368
23500
|
const stdout = new PassThrough();
|
|
@@ -23376,7 +23508,7 @@ var demuxStream = (stream) => new Promise((resolve9, reject2) => {
|
|
|
23376
23508
|
stream.pipe(stdout);
|
|
23377
23509
|
}
|
|
23378
23510
|
stream.on("end", () => {
|
|
23379
|
-
|
|
23511
|
+
resolve10({
|
|
23380
23512
|
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
23381
23513
|
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
23382
23514
|
});
|
|
@@ -23554,6 +23686,19 @@ var DockerProvider = class extends ComputeProvider {
|
|
|
23554
23686
|
}
|
|
23555
23687
|
const mergedEnv = { ...serviceEnv, ...config2.env };
|
|
23556
23688
|
const container = await createWorldContainer(this.docker, id, name, config2.image, mergedEnv, config2.resources, config2.workspacePath, config2.portOffset, config2.appPorts, config2.cacheArch);
|
|
23689
|
+
for (const networkName3 of config2.extraNetworks ?? []) {
|
|
23690
|
+
try {
|
|
23691
|
+
const network = this.docker.getNetwork(networkName3);
|
|
23692
|
+
await network.connect({ Container: container.id });
|
|
23693
|
+
} catch (err) {
|
|
23694
|
+
const statusCode = err.statusCode;
|
|
23695
|
+
if (statusCode === 404) {
|
|
23696
|
+
throw new Error(`extra network "${networkName3}" not found \u2014 run \`olam bootstrap\` to recreate it`);
|
|
23697
|
+
}
|
|
23698
|
+
if (statusCode !== 403)
|
|
23699
|
+
throw err;
|
|
23700
|
+
}
|
|
23701
|
+
}
|
|
23557
23702
|
return new DockerWorld(id, name, container, "running");
|
|
23558
23703
|
}
|
|
23559
23704
|
// -----------------------------------------------------------------------
|
|
@@ -23721,7 +23866,7 @@ var SSHConnectionPool = class {
|
|
|
23721
23866
|
// -----------------------------------------------------------------------
|
|
23722
23867
|
async exec(host, command) {
|
|
23723
23868
|
const client = await this.getConnection(host);
|
|
23724
|
-
return new Promise((
|
|
23869
|
+
return new Promise((resolve10, reject2) => {
|
|
23725
23870
|
client.exec(command, (err, stream) => {
|
|
23726
23871
|
if (err) {
|
|
23727
23872
|
reject2(new Error(`SSH exec failed on ${host}: ${err.message}`));
|
|
@@ -23736,7 +23881,7 @@ var SSHConnectionPool = class {
|
|
|
23736
23881
|
stderr += data.toString();
|
|
23737
23882
|
});
|
|
23738
23883
|
stream.on("close", (code) => {
|
|
23739
|
-
|
|
23884
|
+
resolve10({
|
|
23740
23885
|
exitCode: code ?? 0,
|
|
23741
23886
|
stdout: stdout.trimEnd(),
|
|
23742
23887
|
stderr: stderr.trimEnd()
|
|
@@ -23767,10 +23912,10 @@ var SSHConnectionPool = class {
|
|
|
23767
23912
|
throw new Error(`No SSH configuration found for host: ${host}`);
|
|
23768
23913
|
}
|
|
23769
23914
|
const client = new SSHClient();
|
|
23770
|
-
return new Promise((
|
|
23915
|
+
return new Promise((resolve10, reject2) => {
|
|
23771
23916
|
client.on("ready", () => {
|
|
23772
23917
|
this.connections.set(host, client);
|
|
23773
|
-
|
|
23918
|
+
resolve10(client);
|
|
23774
23919
|
}).on("error", (err) => {
|
|
23775
23920
|
this.connections.delete(host);
|
|
23776
23921
|
reject2(new Error(`SSH connection to ${host} failed: ${err.message}`));
|
|
@@ -24750,7 +24895,7 @@ function register6(server, ctx, initError) {
|
|
|
24750
24895
|
}
|
|
24751
24896
|
} catch {
|
|
24752
24897
|
}
|
|
24753
|
-
await new Promise((
|
|
24898
|
+
await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
|
|
24754
24899
|
}
|
|
24755
24900
|
}
|
|
24756
24901
|
if (authenticated) {
|
|
@@ -26990,15 +27135,15 @@ ${JSON.stringify({ error: reason })}`
|
|
|
26990
27135
|
unlinkSync2(udsPath);
|
|
26991
27136
|
} catch {
|
|
26992
27137
|
}
|
|
26993
|
-
await new Promise((
|
|
26994
|
-
server.listen(udsPath, () =>
|
|
27138
|
+
await new Promise((resolve10, reject2) => {
|
|
27139
|
+
server.listen(udsPath, () => resolve10());
|
|
26995
27140
|
server.once("error", reject2);
|
|
26996
27141
|
});
|
|
26997
27142
|
chmodSync3(udsPath, 384);
|
|
26998
27143
|
port = 0;
|
|
26999
27144
|
} else {
|
|
27000
|
-
await new Promise((
|
|
27001
|
-
server.listen(opts.port ?? 0, "127.0.0.1", () =>
|
|
27145
|
+
await new Promise((resolve10, reject2) => {
|
|
27146
|
+
server.listen(opts.port ?? 0, "127.0.0.1", () => resolve10());
|
|
27002
27147
|
server.once("error", reject2);
|
|
27003
27148
|
});
|
|
27004
27149
|
const addr = server.address();
|
|
@@ -27014,10 +27159,10 @@ ${JSON.stringify({ error: reason })}`
|
|
|
27014
27159
|
} catch {
|
|
27015
27160
|
}
|
|
27016
27161
|
await Promise.race([
|
|
27017
|
-
new Promise((
|
|
27018
|
-
server.close((err) => err ? reject2(err) :
|
|
27162
|
+
new Promise((resolve10, reject2) => {
|
|
27163
|
+
server.close((err) => err ? reject2(err) : resolve10());
|
|
27019
27164
|
}),
|
|
27020
|
-
new Promise((
|
|
27165
|
+
new Promise((resolve10) => setTimeout(resolve10, 5e3))
|
|
27021
27166
|
]);
|
|
27022
27167
|
if (udsPath) {
|
|
27023
27168
|
try {
|
|
@@ -27349,10 +27494,10 @@ async function acquireLaunchSlot() {
|
|
|
27349
27494
|
_inFlightLaunches++;
|
|
27350
27495
|
return releaseLaunchSlot;
|
|
27351
27496
|
}
|
|
27352
|
-
return new Promise((
|
|
27497
|
+
return new Promise((resolve10) => {
|
|
27353
27498
|
_launchQueue.push(() => {
|
|
27354
27499
|
_inFlightLaunches++;
|
|
27355
|
-
|
|
27500
|
+
resolve10(releaseLaunchSlot);
|
|
27356
27501
|
});
|
|
27357
27502
|
});
|
|
27358
27503
|
}
|
|
@@ -27368,8 +27513,8 @@ function resolveShootsRoot() {
|
|
|
27368
27513
|
}
|
|
27369
27514
|
function isUnderRoot(absPath, root) {
|
|
27370
27515
|
if (absPath === root) return true;
|
|
27371
|
-
const
|
|
27372
|
-
return absPath.startsWith(root +
|
|
27516
|
+
const sep2 = root.endsWith("/") ? "" : "/";
|
|
27517
|
+
return absPath.startsWith(root + sep2);
|
|
27373
27518
|
}
|
|
27374
27519
|
function deriveAllowedPaths(shots) {
|
|
27375
27520
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -29637,7 +29782,7 @@ function loadConfig(startDir) {
|
|
|
29637
29782
|
|
|
29638
29783
|
// ../core/dist/world/manager.js
|
|
29639
29784
|
import * as crypto5 from "node:crypto";
|
|
29640
|
-
import { execSync as execSync5 } from "node:child_process";
|
|
29785
|
+
import { execSync as execSync5, spawnSync as spawnSync4 } from "node:child_process";
|
|
29641
29786
|
import * as fs22 from "node:fs";
|
|
29642
29787
|
import * as os13 from "node:os";
|
|
29643
29788
|
import * as path25 from "node:path";
|
|
@@ -29864,7 +30009,7 @@ import * as path17 from "node:path";
|
|
|
29864
30009
|
|
|
29865
30010
|
// ../core/dist/kg/storage-paths.js
|
|
29866
30011
|
import { homedir as homedir10 } from "node:os";
|
|
29867
|
-
import { join as join18, resolve as
|
|
30012
|
+
import { join as join18, resolve as resolve5 } from "node:path";
|
|
29868
30013
|
|
|
29869
30014
|
// ../core/dist/world/workspace-name.js
|
|
29870
30015
|
var InvalidWorkspaceNameError = class extends Error {
|
|
@@ -29901,7 +30046,7 @@ function assertWithinPrefix(path28, prefix, label) {
|
|
|
29901
30046
|
function kgPristinePath(workspace) {
|
|
29902
30047
|
validateWorkspaceName(workspace);
|
|
29903
30048
|
const root = kgRoot();
|
|
29904
|
-
const path28 =
|
|
30049
|
+
const path28 = resolve5(join18(root, workspace));
|
|
29905
30050
|
assertWithinPrefix(path28, root, "kgPristinePath");
|
|
29906
30051
|
return path28;
|
|
29907
30052
|
}
|
|
@@ -32470,6 +32615,8 @@ ${detail}`);
|
|
|
32470
32615
|
const hostPort = override !== void 0 ? override : ap.port + 1e4 + portOffset;
|
|
32471
32616
|
return { repoName: ap.name, internalPort: ap.port, hostPort, url: `http://localhost:${hostPort}` };
|
|
32472
32617
|
});
|
|
32618
|
+
let worldDbNames = void 0;
|
|
32619
|
+
let worldRoleName = void 0;
|
|
32473
32620
|
try {
|
|
32474
32621
|
const worldEnv = {};
|
|
32475
32622
|
if (opts.task)
|
|
@@ -32570,7 +32717,27 @@ ${detail}`);
|
|
|
32570
32717
|
}
|
|
32571
32718
|
}
|
|
32572
32719
|
}
|
|
32573
|
-
applyPostgresNetworkOverrides(worldEnv, enrichedRepos);
|
|
32720
|
+
applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId);
|
|
32721
|
+
const cloneResult = applyPostgresTemplateClone(worldId, enrichedRepos);
|
|
32722
|
+
worldDbNames = cloneResult.worldDbNames.length > 0 ? cloneResult.worldDbNames : void 0;
|
|
32723
|
+
if (worldDbNames !== void 0) {
|
|
32724
|
+
const roleResult = applyPostgresWorldRole(worldId, worldDbNames);
|
|
32725
|
+
worldRoleName = roleResult.worldRoleName;
|
|
32726
|
+
worldEnv["POSTGRESQL_USERNAME"] = roleResult.worldRoleName;
|
|
32727
|
+
worldEnv["POSTGRESQL_PASSWORD"] = roleResult.password;
|
|
32728
|
+
worldEnv["POSTGRESQL_COMMON_USERNAME"] = roleResult.worldRoleName;
|
|
32729
|
+
worldEnv["POSTGRESQL_COMMON_PASSWORD"] = roleResult.password;
|
|
32730
|
+
worldEnv["POSTGRESQL_MERCHANT_USERNAME"] = roleResult.worldRoleName;
|
|
32731
|
+
worldEnv["POSTGRESQL_MERCHANT_PASSWORD"] = roleResult.password;
|
|
32732
|
+
}
|
|
32733
|
+
if (worldDbNames !== void 0 || worldRoleName !== void 0) {
|
|
32734
|
+
this.registry.update(worldId, {
|
|
32735
|
+
...worldDbNames !== void 0 ? { worldDbNames: [...worldDbNames] } : {},
|
|
32736
|
+
...worldRoleName !== void 0 ? { worldRoleName } : {}
|
|
32737
|
+
});
|
|
32738
|
+
}
|
|
32739
|
+
const hybridActive = worldDbNames !== void 0;
|
|
32740
|
+
const extraNetworks = hybridActive ? ["olam-shared"] : void 0;
|
|
32574
32741
|
await this.provider.createWorld({
|
|
32575
32742
|
id: worldId,
|
|
32576
32743
|
name: opts.name,
|
|
@@ -32580,6 +32747,7 @@ ${detail}`);
|
|
|
32580
32747
|
portOffset,
|
|
32581
32748
|
workspacePath,
|
|
32582
32749
|
appPorts: appPorts.length > 0 ? appPorts : void 0,
|
|
32750
|
+
...extraNetworks ? { extraNetworks } : {},
|
|
32583
32751
|
// Phase 7 A1: per-world cost ceiling sourced from config.cost.
|
|
32584
32752
|
// Cloudflare provider forwards this to /session/start so the DO
|
|
32585
32753
|
// can enforce kill-switch on `cost_update` events. Other providers
|
|
@@ -32603,7 +32771,16 @@ ${detail}`);
|
|
|
32603
32771
|
sm.transition("running");
|
|
32604
32772
|
this.registry.update(worldId, {
|
|
32605
32773
|
status: "running",
|
|
32606
|
-
...appPortUrls.length > 0 ? { appPortUrls } : {}
|
|
32774
|
+
...appPortUrls.length > 0 ? { appPortUrls } : {},
|
|
32775
|
+
// olam-hybrid-shared-postgres A3: persist the cloned per-world DB
|
|
32776
|
+
// names so olam destroy + olam gc can find them later (A7's
|
|
32777
|
+
// world_db_names TEXT JSON column).
|
|
32778
|
+
...worldDbNames !== void 0 ? { worldDbNames: [...worldDbNames] } : {},
|
|
32779
|
+
// olam-hybrid-shared-postgres A4: persist the role name so olam destroy
|
|
32780
|
+
// can DROP it after the world's DBs are gone (A7's world_role_name
|
|
32781
|
+
// column). Password is NOT persisted — it lives only in the world
|
|
32782
|
+
// container's env and the in-memory return value.
|
|
32783
|
+
...worldRoleName !== void 0 ? { worldRoleName } : {}
|
|
32607
32784
|
});
|
|
32608
32785
|
const containerName = `olam-${worldId}-devbox`;
|
|
32609
32786
|
try {
|
|
@@ -32903,6 +33080,14 @@ ${opts.task}`;
|
|
|
32903
33080
|
cleanupWorldTailscale(worldId, this.registry);
|
|
32904
33081
|
} catch {
|
|
32905
33082
|
}
|
|
33083
|
+
const worldDbNames = world.worldDbNames;
|
|
33084
|
+
if (worldDbNames !== void 0 && worldDbNames.length > 0) {
|
|
33085
|
+
dropPostgresWorldDbs(worldId, worldDbNames);
|
|
33086
|
+
}
|
|
33087
|
+
const worldRoleName = world.worldRoleName;
|
|
33088
|
+
if (typeof worldRoleName === "string" && worldRoleName.length > 0) {
|
|
33089
|
+
dropPostgresWorldRole(worldId, worldRoleName);
|
|
33090
|
+
}
|
|
32906
33091
|
try {
|
|
32907
33092
|
await this.provider.destroyWorld(worldId);
|
|
32908
33093
|
} catch {
|
|
@@ -33050,16 +33235,268 @@ ${opts.task}`;
|
|
|
33050
33235
|
return services;
|
|
33051
33236
|
}
|
|
33052
33237
|
};
|
|
33053
|
-
function applyPostgresNetworkOverrides(worldEnv, enrichedRepos) {
|
|
33238
|
+
function applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId) {
|
|
33054
33239
|
const hasPostgres = enrichedRepos.some((r) => r.manifest?.services?.["postgres"] !== void 0);
|
|
33055
33240
|
if (!hasPostgres)
|
|
33056
33241
|
return;
|
|
33057
|
-
|
|
33242
|
+
const hasSeedTemplate = enrichedRepos.some((r) => {
|
|
33243
|
+
const pg = r.manifest?.services?.["postgres"];
|
|
33244
|
+
return pg?.seed_template !== void 0;
|
|
33245
|
+
});
|
|
33246
|
+
const host = hasSeedTemplate ? "olam-postgres" : "postgres";
|
|
33247
|
+
worldEnv["POSTGRESQL_HOST"] = host;
|
|
33058
33248
|
worldEnv["POSTGRESQL_PORT"] = "5432";
|
|
33059
|
-
if (
|
|
33060
|
-
worldEnv["POSTGRESQL_COMMON_HOST"] =
|
|
33061
|
-
if ("POSTGRESQL_COMMON_PORT" in worldEnv)
|
|
33249
|
+
if (hasSeedTemplate) {
|
|
33250
|
+
worldEnv["POSTGRESQL_COMMON_HOST"] = host;
|
|
33062
33251
|
worldEnv["POSTGRESQL_COMMON_PORT"] = "5432";
|
|
33252
|
+
if (worldId !== void 0) {
|
|
33253
|
+
assertSafeWorldId(worldId);
|
|
33254
|
+
const seedTemplates = /* @__PURE__ */ new Set();
|
|
33255
|
+
for (const repo of enrichedRepos) {
|
|
33256
|
+
const pg = repo.manifest?.services?.["postgres"];
|
|
33257
|
+
const raw = pg?.seed_template;
|
|
33258
|
+
if (typeof raw === "string")
|
|
33259
|
+
seedTemplates.add(raw);
|
|
33260
|
+
else if (Array.isArray(raw)) {
|
|
33261
|
+
for (const s of raw)
|
|
33262
|
+
if (typeof s === "string")
|
|
33263
|
+
seedTemplates.add(s);
|
|
33264
|
+
}
|
|
33265
|
+
}
|
|
33266
|
+
for (const seed of seedTemplates) {
|
|
33267
|
+
const match = seed.match(/^atlas_([a-z][a-z0-9_]*?)_seed$/i);
|
|
33268
|
+
if (match && match[1] !== void 0) {
|
|
33269
|
+
const purpose = match[1].toUpperCase();
|
|
33270
|
+
const envKey = `POSTGRESQL_${purpose}_DATABASE`;
|
|
33271
|
+
worldEnv[envKey] = deriveWorldDbName(seed, worldId);
|
|
33272
|
+
}
|
|
33273
|
+
}
|
|
33274
|
+
}
|
|
33275
|
+
} else {
|
|
33276
|
+
if ("POSTGRESQL_COMMON_HOST" in worldEnv)
|
|
33277
|
+
worldEnv["POSTGRESQL_COMMON_HOST"] = "postgres";
|
|
33278
|
+
if ("POSTGRESQL_COMMON_PORT" in worldEnv)
|
|
33279
|
+
worldEnv["POSTGRESQL_COMMON_PORT"] = "5432";
|
|
33280
|
+
}
|
|
33281
|
+
}
|
|
33282
|
+
function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
33283
|
+
assertSafeWorldId(worldId);
|
|
33284
|
+
const container = options.singletonContainer ?? "olam-postgres";
|
|
33285
|
+
const user = options.postgresUser ?? "development";
|
|
33286
|
+
const seedTemplates = [];
|
|
33287
|
+
const postCloneSqlBySeed = /* @__PURE__ */ new Map();
|
|
33288
|
+
for (const repo of enrichedRepos) {
|
|
33289
|
+
const pg = repo.manifest?.services?.["postgres"];
|
|
33290
|
+
if (!pg?.seed_template)
|
|
33291
|
+
continue;
|
|
33292
|
+
const list = Array.isArray(pg.seed_template) ? pg.seed_template : [pg.seed_template];
|
|
33293
|
+
for (const t of list) {
|
|
33294
|
+
if (typeof t === "string" && t.length > 0 && !seedTemplates.includes(t)) {
|
|
33295
|
+
seedTemplates.push(t);
|
|
33296
|
+
}
|
|
33297
|
+
}
|
|
33298
|
+
if (pg.post_clone_sql && typeof pg.post_clone_sql === "object") {
|
|
33299
|
+
for (const [seedKey, sqlList] of Object.entries(pg.post_clone_sql)) {
|
|
33300
|
+
if (!Array.isArray(sqlList))
|
|
33301
|
+
continue;
|
|
33302
|
+
const existing = postCloneSqlBySeed.get(seedKey) ?? [];
|
|
33303
|
+
for (const stmt of sqlList) {
|
|
33304
|
+
if (typeof stmt === "string" && stmt.length > 0)
|
|
33305
|
+
existing.push(stmt);
|
|
33306
|
+
}
|
|
33307
|
+
postCloneSqlBySeed.set(seedKey, existing);
|
|
33308
|
+
}
|
|
33309
|
+
}
|
|
33310
|
+
}
|
|
33311
|
+
if (seedTemplates.length === 0) {
|
|
33312
|
+
return { worldDbNames: [] };
|
|
33313
|
+
}
|
|
33314
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33315
|
+
const seedToWorldDb = /* @__PURE__ */ new Map();
|
|
33316
|
+
for (const seed of seedTemplates) {
|
|
33317
|
+
seedToWorldDb.set(seed, deriveWorldDbName(seed, worldId));
|
|
33318
|
+
}
|
|
33319
|
+
const created = [];
|
|
33320
|
+
for (const seed of seedTemplates) {
|
|
33321
|
+
const worldDb = seedToWorldDb.get(seed);
|
|
33322
|
+
const exists = spawn4("docker", [
|
|
33323
|
+
"exec",
|
|
33324
|
+
container,
|
|
33325
|
+
"psql",
|
|
33326
|
+
"-U",
|
|
33327
|
+
user,
|
|
33328
|
+
"-tAc",
|
|
33329
|
+
`SELECT 1 FROM pg_database WHERE datname='${worldDb}'`
|
|
33330
|
+
], { encoding: "utf-8" });
|
|
33331
|
+
const dbAlreadyExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
33332
|
+
if (!dbAlreadyExists) {
|
|
33333
|
+
const sql = `CREATE DATABASE "${worldDb}" TEMPLATE "${seed}"`;
|
|
33334
|
+
const create = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", sql], { encoding: "utf-8" });
|
|
33335
|
+
if (create.status !== 0) {
|
|
33336
|
+
throw new Error(`failed to CREATE DATABASE "${worldDb}" TEMPLATE "${seed}": ${(create.stderr || "").trim()}`);
|
|
33337
|
+
}
|
|
33338
|
+
}
|
|
33339
|
+
created.push(worldDb);
|
|
33340
|
+
const stmts = postCloneSqlBySeed.get(seed);
|
|
33341
|
+
if (stmts !== void 0 && stmts.length > 0) {
|
|
33342
|
+
for (const rawStmt of stmts) {
|
|
33343
|
+
const stmt = interpolatePostCloneSql(rawStmt, worldId, seedToWorldDb);
|
|
33344
|
+
const fixup = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", stmt], { encoding: "utf-8" });
|
|
33345
|
+
if (fixup.status !== 0) {
|
|
33346
|
+
throw new Error(`post_clone_sql against "${worldDb}" failed (statement: ${truncate(stmt, 120)}): ${(fixup.stderr || "").trim()}`);
|
|
33347
|
+
}
|
|
33348
|
+
}
|
|
33349
|
+
}
|
|
33350
|
+
}
|
|
33351
|
+
return { worldDbNames: created };
|
|
33352
|
+
}
|
|
33353
|
+
function interpolatePostCloneSql(stmt, worldId, seedToWorldDb) {
|
|
33354
|
+
return stmt.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
33355
|
+
if (expr === "WORLD_ID")
|
|
33356
|
+
return worldId;
|
|
33357
|
+
const dbMatch = expr.match(/^WORLD_DB:(.+)$/);
|
|
33358
|
+
if (dbMatch && dbMatch[1] !== void 0) {
|
|
33359
|
+
const target = seedToWorldDb.get(dbMatch[1]);
|
|
33360
|
+
if (target === void 0) {
|
|
33361
|
+
throw new Error(`post_clone_sql references seed "${dbMatch[1]}" not declared in seed_template`);
|
|
33362
|
+
}
|
|
33363
|
+
return target;
|
|
33364
|
+
}
|
|
33365
|
+
throw new Error(`post_clone_sql contains unknown placeholder "\${${expr}}" \u2014 only \${WORLD_ID} and \${WORLD_DB:<seed>} are supported`);
|
|
33366
|
+
});
|
|
33367
|
+
}
|
|
33368
|
+
function truncate(s, max) {
|
|
33369
|
+
return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
|
|
33370
|
+
}
|
|
33371
|
+
function deriveWorldRoleName(worldId) {
|
|
33372
|
+
return `olam_world_${worldId}`;
|
|
33373
|
+
}
|
|
33374
|
+
function applyPostgresWorldRole(worldId, worldDbNames, options = {}) {
|
|
33375
|
+
assertSafeWorldId(worldId);
|
|
33376
|
+
if (worldDbNames.length === 0) {
|
|
33377
|
+
throw new Error(`applyPostgresWorldRole called with empty worldDbNames for "${worldId}" \u2014 should only run when applyPostgresTemplateClone produced clones`);
|
|
33378
|
+
}
|
|
33379
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33380
|
+
const container = options.singletonContainer ?? "olam-postgres";
|
|
33381
|
+
const user = options.postgresUser ?? "development";
|
|
33382
|
+
const genPassword = options.generatePassword ?? defaultPasswordGenerator;
|
|
33383
|
+
const worldRoleName = deriveWorldRoleName(worldId);
|
|
33384
|
+
const password = genPassword();
|
|
33385
|
+
const exists = spawn4("docker", [
|
|
33386
|
+
"exec",
|
|
33387
|
+
container,
|
|
33388
|
+
"psql",
|
|
33389
|
+
"-U",
|
|
33390
|
+
user,
|
|
33391
|
+
"-tAc",
|
|
33392
|
+
`SELECT 1 FROM pg_roles WHERE rolname='${escapeSqlLiteral(worldRoleName)}'`
|
|
33393
|
+
], { encoding: "utf-8" });
|
|
33394
|
+
const roleExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
33395
|
+
const escapedPassword = escapeSqlLiteral(password);
|
|
33396
|
+
const roleDdl = roleExists ? `ALTER ROLE "${worldRoleName}" WITH LOGIN PASSWORD '${escapedPassword}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION` : `CREATE ROLE "${worldRoleName}" WITH LOGIN PASSWORD '${escapedPassword}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION`;
|
|
33397
|
+
const dml = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", roleDdl], { encoding: "utf-8" });
|
|
33398
|
+
if (dml.status !== 0) {
|
|
33399
|
+
throw new Error(`failed to ${roleExists ? "ALTER" : "CREATE"} ROLE "${worldRoleName}": ` + redactCreateRolePassword((dml.stderr || "").trim()));
|
|
33400
|
+
}
|
|
33401
|
+
for (const worldDb of worldDbNames) {
|
|
33402
|
+
const grantConnect = spawn4("docker", [
|
|
33403
|
+
"exec",
|
|
33404
|
+
container,
|
|
33405
|
+
"psql",
|
|
33406
|
+
"-U",
|
|
33407
|
+
user,
|
|
33408
|
+
"-d",
|
|
33409
|
+
"postgres",
|
|
33410
|
+
"-v",
|
|
33411
|
+
"ON_ERROR_STOP=1",
|
|
33412
|
+
"-c",
|
|
33413
|
+
`GRANT CONNECT ON DATABASE "${worldDb}" TO "${worldRoleName}"`
|
|
33414
|
+
], { encoding: "utf-8" });
|
|
33415
|
+
if (grantConnect.status !== 0) {
|
|
33416
|
+
throw new Error(`failed to GRANT CONNECT on "${worldDb}" to "${worldRoleName}": ${(grantConnect.stderr || "").trim()}`);
|
|
33417
|
+
}
|
|
33418
|
+
const perDbGrants = [
|
|
33419
|
+
`GRANT USAGE ON SCHEMA public TO "${worldRoleName}"`,
|
|
33420
|
+
`GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "${worldRoleName}"`,
|
|
33421
|
+
`GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO "${worldRoleName}"`,
|
|
33422
|
+
// DEFAULT PRIVILEGES for future objects — Rails migrations create new
|
|
33423
|
+
// tables/sequences post-spawn and they need to be grantable.
|
|
33424
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${worldRoleName}"`,
|
|
33425
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "${worldRoleName}"`
|
|
33426
|
+
].join("; ");
|
|
33427
|
+
const grantResult = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", perDbGrants], { encoding: "utf-8" });
|
|
33428
|
+
if (grantResult.status !== 0) {
|
|
33429
|
+
throw new Error(`failed to GRANT privileges on "${worldDb}" to "${worldRoleName}": ${(grantResult.stderr || "").trim()}`);
|
|
33430
|
+
}
|
|
33431
|
+
}
|
|
33432
|
+
return { worldRoleName, password };
|
|
33433
|
+
}
|
|
33434
|
+
function defaultPasswordGenerator() {
|
|
33435
|
+
return crypto5.randomBytes(24).toString("base64url");
|
|
33436
|
+
}
|
|
33437
|
+
function escapeSqlLiteral(s) {
|
|
33438
|
+
return s.replace(/'/g, "''");
|
|
33439
|
+
}
|
|
33440
|
+
function redactCreateRolePassword(s) {
|
|
33441
|
+
return s.replace(/PASSWORD\s+'[^']*'/g, "PASSWORD '<scrubbed>'");
|
|
33442
|
+
}
|
|
33443
|
+
function dropPostgresWorldDbs(worldId, worldDbNames, options = {}) {
|
|
33444
|
+
if (worldDbNames.length === 0)
|
|
33445
|
+
return;
|
|
33446
|
+
assertSafeWorldId(worldId);
|
|
33447
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33448
|
+
const container = options.container ?? "olam-postgres";
|
|
33449
|
+
const user = options.user ?? "development";
|
|
33450
|
+
for (const db of worldDbNames) {
|
|
33451
|
+
const drop = spawn4("docker", [
|
|
33452
|
+
"exec",
|
|
33453
|
+
container,
|
|
33454
|
+
"psql",
|
|
33455
|
+
"-U",
|
|
33456
|
+
user,
|
|
33457
|
+
"-d",
|
|
33458
|
+
"postgres",
|
|
33459
|
+
"-c",
|
|
33460
|
+
`DROP DATABASE IF EXISTS "${db}" WITH (FORCE)`
|
|
33461
|
+
], { encoding: "utf-8" });
|
|
33462
|
+
if (drop.status !== 0) {
|
|
33463
|
+
console.warn(`[manager] destroyWorld(${worldId}): failed to DROP "${db}" from singleton: ${(drop.stderr || "").trim()}`);
|
|
33464
|
+
}
|
|
33465
|
+
}
|
|
33466
|
+
}
|
|
33467
|
+
function dropPostgresWorldRole(worldId, worldRoleName, options = {}) {
|
|
33468
|
+
if (!worldRoleName)
|
|
33469
|
+
return;
|
|
33470
|
+
assertSafeWorldId(worldId);
|
|
33471
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33472
|
+
const container = options.container ?? "olam-postgres";
|
|
33473
|
+
const user = options.user ?? "development";
|
|
33474
|
+
const cleanup = spawn4("docker", [
|
|
33475
|
+
"exec",
|
|
33476
|
+
container,
|
|
33477
|
+
"psql",
|
|
33478
|
+
"-U",
|
|
33479
|
+
user,
|
|
33480
|
+
"-d",
|
|
33481
|
+
"postgres",
|
|
33482
|
+
"-c",
|
|
33483
|
+
`DROP OWNED BY "${worldRoleName}" CASCADE; DROP ROLE IF EXISTS "${worldRoleName}"`
|
|
33484
|
+
], { encoding: "utf-8" });
|
|
33485
|
+
if (cleanup.status !== 0) {
|
|
33486
|
+
console.warn(`[manager] destroyWorld(${worldId}): failed to DROP role "${worldRoleName}" from singleton: ${(cleanup.stderr || "").trim()}`);
|
|
33487
|
+
}
|
|
33488
|
+
}
|
|
33489
|
+
function assertSafeWorldId(worldId) {
|
|
33490
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(worldId)) {
|
|
33491
|
+
throw new Error(`world id "${worldId}" contains characters outside [a-z0-9-] \u2014 hybrid-postgres + post_clone_sql interpolation require the name-generator's [a-z0-9-]+ alphabet`);
|
|
33492
|
+
}
|
|
33493
|
+
}
|
|
33494
|
+
function deriveWorldDbName(seed, worldId) {
|
|
33495
|
+
if (seed.endsWith("_seed")) {
|
|
33496
|
+
const prefix = seed.slice(0, -"_seed".length);
|
|
33497
|
+
return `${prefix}_world_${worldId}`;
|
|
33498
|
+
}
|
|
33499
|
+
return `${seed}_world_${worldId}`;
|
|
33063
33500
|
}
|
|
33064
33501
|
|
|
33065
33502
|
// ../core/dist/cost/tracker.js
|
|
@@ -33985,7 +34422,7 @@ function isCloudflaredAvailable() {
|
|
|
33985
34422
|
}
|
|
33986
34423
|
}
|
|
33987
34424
|
function startTunnel(port) {
|
|
33988
|
-
return new Promise((
|
|
34425
|
+
return new Promise((resolve10, reject2) => {
|
|
33989
34426
|
const child = spawn3("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
|
|
33990
34427
|
stdio: ["ignore", "pipe", "pipe"],
|
|
33991
34428
|
detached: false
|
|
@@ -34007,7 +34444,7 @@ function startTunnel(port) {
|
|
|
34007
34444
|
if (match) {
|
|
34008
34445
|
resolved = true;
|
|
34009
34446
|
clearTimeout(timeout);
|
|
34010
|
-
|
|
34447
|
+
resolve10(match[0]);
|
|
34011
34448
|
}
|
|
34012
34449
|
}
|
|
34013
34450
|
child.stdout?.on("data", scan);
|
|
@@ -34075,8 +34512,8 @@ var DashboardManager = class {
|
|
|
34075
34512
|
}
|
|
34076
34513
|
throw err;
|
|
34077
34514
|
}
|
|
34078
|
-
await new Promise((
|
|
34079
|
-
this.server.on("listening",
|
|
34515
|
+
await new Promise((resolve10, reject2) => {
|
|
34516
|
+
this.server.on("listening", resolve10);
|
|
34080
34517
|
this.server.on("error", reject2);
|
|
34081
34518
|
});
|
|
34082
34519
|
this.info = { localUrl: `http://localhost:${port}` };
|
|
@@ -34122,8 +34559,8 @@ var DashboardManager = class {
|
|
|
34122
34559
|
async stop() {
|
|
34123
34560
|
stopTunnel();
|
|
34124
34561
|
if (this.server) {
|
|
34125
|
-
await new Promise((
|
|
34126
|
-
this.server.close(() =>
|
|
34562
|
+
await new Promise((resolve10) => {
|
|
34563
|
+
this.server.close(() => resolve10());
|
|
34127
34564
|
});
|
|
34128
34565
|
this.server = null;
|
|
34129
34566
|
}
|
|
@@ -34237,7 +34674,7 @@ var PleriClient = class {
|
|
|
34237
34674
|
|
|
34238
34675
|
// ../mcp-server/src/env-loader.ts
|
|
34239
34676
|
import { readFileSync as readFileSync17, existsSync as existsSync23, statSync as statSync7 } from "node:fs";
|
|
34240
|
-
import { join as join29, dirname as dirname15, resolve as
|
|
34677
|
+
import { join as join29, dirname as dirname15, resolve as resolve9 } from "node:path";
|
|
34241
34678
|
var PROJECT_MARKERS = [
|
|
34242
34679
|
".olam/config.yaml",
|
|
34243
34680
|
".olam/config.yml",
|
|
@@ -34245,8 +34682,8 @@ var PROJECT_MARKERS = [
|
|
|
34245
34682
|
"olam.yml"
|
|
34246
34683
|
];
|
|
34247
34684
|
function findProjectRoot2(startDir) {
|
|
34248
|
-
let dir =
|
|
34249
|
-
const root =
|
|
34685
|
+
let dir = resolve9(startDir);
|
|
34686
|
+
const root = resolve9("/");
|
|
34250
34687
|
while (true) {
|
|
34251
34688
|
for (const marker of PROJECT_MARKERS) {
|
|
34252
34689
|
if (existsSync23(join29(dir, marker))) return dir;
|