@pleri/olam-cli 0.1.119 → 0.1.125
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/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 +1 -1
- package/dist/index.js +1643 -183
- 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/mcp-server.js +481 -65
- package/package.json +4 -2
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
|
}
|
|
@@ -23362,7 +23473,7 @@ var stopAndRemove = async (container) => {
|
|
|
23362
23473
|
|
|
23363
23474
|
// ../adapters/dist/docker/exec.js
|
|
23364
23475
|
import { PassThrough } from "node:stream";
|
|
23365
|
-
var demuxStream = (stream) => new Promise((
|
|
23476
|
+
var demuxStream = (stream) => new Promise((resolve10, reject2) => {
|
|
23366
23477
|
const stdoutChunks = [];
|
|
23367
23478
|
const stderrChunks = [];
|
|
23368
23479
|
const stdout = new PassThrough();
|
|
@@ -23376,7 +23487,7 @@ var demuxStream = (stream) => new Promise((resolve9, reject2) => {
|
|
|
23376
23487
|
stream.pipe(stdout);
|
|
23377
23488
|
}
|
|
23378
23489
|
stream.on("end", () => {
|
|
23379
|
-
|
|
23490
|
+
resolve10({
|
|
23380
23491
|
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
23381
23492
|
stderr: Buffer.concat(stderrChunks).toString("utf-8")
|
|
23382
23493
|
});
|
|
@@ -23554,6 +23665,19 @@ var DockerProvider = class extends ComputeProvider {
|
|
|
23554
23665
|
}
|
|
23555
23666
|
const mergedEnv = { ...serviceEnv, ...config2.env };
|
|
23556
23667
|
const container = await createWorldContainer(this.docker, id, name, config2.image, mergedEnv, config2.resources, config2.workspacePath, config2.portOffset, config2.appPorts, config2.cacheArch);
|
|
23668
|
+
for (const networkName3 of config2.extraNetworks ?? []) {
|
|
23669
|
+
try {
|
|
23670
|
+
const network = this.docker.getNetwork(networkName3);
|
|
23671
|
+
await network.connect({ Container: container.id });
|
|
23672
|
+
} catch (err) {
|
|
23673
|
+
const statusCode = err.statusCode;
|
|
23674
|
+
if (statusCode === 404) {
|
|
23675
|
+
throw new Error(`extra network "${networkName3}" not found \u2014 run \`olam bootstrap\` to recreate it`);
|
|
23676
|
+
}
|
|
23677
|
+
if (statusCode !== 403)
|
|
23678
|
+
throw err;
|
|
23679
|
+
}
|
|
23680
|
+
}
|
|
23557
23681
|
return new DockerWorld(id, name, container, "running");
|
|
23558
23682
|
}
|
|
23559
23683
|
// -----------------------------------------------------------------------
|
|
@@ -23721,7 +23845,7 @@ var SSHConnectionPool = class {
|
|
|
23721
23845
|
// -----------------------------------------------------------------------
|
|
23722
23846
|
async exec(host, command) {
|
|
23723
23847
|
const client = await this.getConnection(host);
|
|
23724
|
-
return new Promise((
|
|
23848
|
+
return new Promise((resolve10, reject2) => {
|
|
23725
23849
|
client.exec(command, (err, stream) => {
|
|
23726
23850
|
if (err) {
|
|
23727
23851
|
reject2(new Error(`SSH exec failed on ${host}: ${err.message}`));
|
|
@@ -23736,7 +23860,7 @@ var SSHConnectionPool = class {
|
|
|
23736
23860
|
stderr += data.toString();
|
|
23737
23861
|
});
|
|
23738
23862
|
stream.on("close", (code) => {
|
|
23739
|
-
|
|
23863
|
+
resolve10({
|
|
23740
23864
|
exitCode: code ?? 0,
|
|
23741
23865
|
stdout: stdout.trimEnd(),
|
|
23742
23866
|
stderr: stderr.trimEnd()
|
|
@@ -23767,10 +23891,10 @@ var SSHConnectionPool = class {
|
|
|
23767
23891
|
throw new Error(`No SSH configuration found for host: ${host}`);
|
|
23768
23892
|
}
|
|
23769
23893
|
const client = new SSHClient();
|
|
23770
|
-
return new Promise((
|
|
23894
|
+
return new Promise((resolve10, reject2) => {
|
|
23771
23895
|
client.on("ready", () => {
|
|
23772
23896
|
this.connections.set(host, client);
|
|
23773
|
-
|
|
23897
|
+
resolve10(client);
|
|
23774
23898
|
}).on("error", (err) => {
|
|
23775
23899
|
this.connections.delete(host);
|
|
23776
23900
|
reject2(new Error(`SSH connection to ${host} failed: ${err.message}`));
|
|
@@ -24750,7 +24874,7 @@ function register6(server, ctx, initError) {
|
|
|
24750
24874
|
}
|
|
24751
24875
|
} catch {
|
|
24752
24876
|
}
|
|
24753
|
-
await new Promise((
|
|
24877
|
+
await new Promise((resolve10) => setTimeout(resolve10, POLL_INTERVAL_MS));
|
|
24754
24878
|
}
|
|
24755
24879
|
}
|
|
24756
24880
|
if (authenticated) {
|
|
@@ -26990,15 +27114,15 @@ ${JSON.stringify({ error: reason })}`
|
|
|
26990
27114
|
unlinkSync2(udsPath);
|
|
26991
27115
|
} catch {
|
|
26992
27116
|
}
|
|
26993
|
-
await new Promise((
|
|
26994
|
-
server.listen(udsPath, () =>
|
|
27117
|
+
await new Promise((resolve10, reject2) => {
|
|
27118
|
+
server.listen(udsPath, () => resolve10());
|
|
26995
27119
|
server.once("error", reject2);
|
|
26996
27120
|
});
|
|
26997
27121
|
chmodSync3(udsPath, 384);
|
|
26998
27122
|
port = 0;
|
|
26999
27123
|
} else {
|
|
27000
|
-
await new Promise((
|
|
27001
|
-
server.listen(opts.port ?? 0, "127.0.0.1", () =>
|
|
27124
|
+
await new Promise((resolve10, reject2) => {
|
|
27125
|
+
server.listen(opts.port ?? 0, "127.0.0.1", () => resolve10());
|
|
27002
27126
|
server.once("error", reject2);
|
|
27003
27127
|
});
|
|
27004
27128
|
const addr = server.address();
|
|
@@ -27014,10 +27138,10 @@ ${JSON.stringify({ error: reason })}`
|
|
|
27014
27138
|
} catch {
|
|
27015
27139
|
}
|
|
27016
27140
|
await Promise.race([
|
|
27017
|
-
new Promise((
|
|
27018
|
-
server.close((err) => err ? reject2(err) :
|
|
27141
|
+
new Promise((resolve10, reject2) => {
|
|
27142
|
+
server.close((err) => err ? reject2(err) : resolve10());
|
|
27019
27143
|
}),
|
|
27020
|
-
new Promise((
|
|
27144
|
+
new Promise((resolve10) => setTimeout(resolve10, 5e3))
|
|
27021
27145
|
]);
|
|
27022
27146
|
if (udsPath) {
|
|
27023
27147
|
try {
|
|
@@ -27349,10 +27473,10 @@ async function acquireLaunchSlot() {
|
|
|
27349
27473
|
_inFlightLaunches++;
|
|
27350
27474
|
return releaseLaunchSlot;
|
|
27351
27475
|
}
|
|
27352
|
-
return new Promise((
|
|
27476
|
+
return new Promise((resolve10) => {
|
|
27353
27477
|
_launchQueue.push(() => {
|
|
27354
27478
|
_inFlightLaunches++;
|
|
27355
|
-
|
|
27479
|
+
resolve10(releaseLaunchSlot);
|
|
27356
27480
|
});
|
|
27357
27481
|
});
|
|
27358
27482
|
}
|
|
@@ -27368,8 +27492,8 @@ function resolveShootsRoot() {
|
|
|
27368
27492
|
}
|
|
27369
27493
|
function isUnderRoot(absPath, root) {
|
|
27370
27494
|
if (absPath === root) return true;
|
|
27371
|
-
const
|
|
27372
|
-
return absPath.startsWith(root +
|
|
27495
|
+
const sep2 = root.endsWith("/") ? "" : "/";
|
|
27496
|
+
return absPath.startsWith(root + sep2);
|
|
27373
27497
|
}
|
|
27374
27498
|
function deriveAllowedPaths(shots) {
|
|
27375
27499
|
const paths = /* @__PURE__ */ new Set();
|
|
@@ -29637,7 +29761,7 @@ function loadConfig(startDir) {
|
|
|
29637
29761
|
|
|
29638
29762
|
// ../core/dist/world/manager.js
|
|
29639
29763
|
import * as crypto5 from "node:crypto";
|
|
29640
|
-
import { execSync as execSync5 } from "node:child_process";
|
|
29764
|
+
import { execSync as execSync5, spawnSync as spawnSync4 } from "node:child_process";
|
|
29641
29765
|
import * as fs22 from "node:fs";
|
|
29642
29766
|
import * as os13 from "node:os";
|
|
29643
29767
|
import * as path25 from "node:path";
|
|
@@ -29864,7 +29988,7 @@ import * as path17 from "node:path";
|
|
|
29864
29988
|
|
|
29865
29989
|
// ../core/dist/kg/storage-paths.js
|
|
29866
29990
|
import { homedir as homedir10 } from "node:os";
|
|
29867
|
-
import { join as join18, resolve as
|
|
29991
|
+
import { join as join18, resolve as resolve5 } from "node:path";
|
|
29868
29992
|
|
|
29869
29993
|
// ../core/dist/world/workspace-name.js
|
|
29870
29994
|
var InvalidWorkspaceNameError = class extends Error {
|
|
@@ -29901,7 +30025,7 @@ function assertWithinPrefix(path28, prefix, label) {
|
|
|
29901
30025
|
function kgPristinePath(workspace) {
|
|
29902
30026
|
validateWorkspaceName(workspace);
|
|
29903
30027
|
const root = kgRoot();
|
|
29904
|
-
const path28 =
|
|
30028
|
+
const path28 = resolve5(join18(root, workspace));
|
|
29905
30029
|
assertWithinPrefix(path28, root, "kgPristinePath");
|
|
29906
30030
|
return path28;
|
|
29907
30031
|
}
|
|
@@ -32470,6 +32594,8 @@ ${detail}`);
|
|
|
32470
32594
|
const hostPort = override !== void 0 ? override : ap.port + 1e4 + portOffset;
|
|
32471
32595
|
return { repoName: ap.name, internalPort: ap.port, hostPort, url: `http://localhost:${hostPort}` };
|
|
32472
32596
|
});
|
|
32597
|
+
let worldDbNames = void 0;
|
|
32598
|
+
let worldRoleName = void 0;
|
|
32473
32599
|
try {
|
|
32474
32600
|
const worldEnv = {};
|
|
32475
32601
|
if (opts.task)
|
|
@@ -32570,7 +32696,27 @@ ${detail}`);
|
|
|
32570
32696
|
}
|
|
32571
32697
|
}
|
|
32572
32698
|
}
|
|
32573
|
-
applyPostgresNetworkOverrides(worldEnv, enrichedRepos);
|
|
32699
|
+
applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId);
|
|
32700
|
+
const cloneResult = applyPostgresTemplateClone(worldId, enrichedRepos);
|
|
32701
|
+
worldDbNames = cloneResult.worldDbNames.length > 0 ? cloneResult.worldDbNames : void 0;
|
|
32702
|
+
if (worldDbNames !== void 0) {
|
|
32703
|
+
const roleResult = applyPostgresWorldRole(worldId, worldDbNames);
|
|
32704
|
+
worldRoleName = roleResult.worldRoleName;
|
|
32705
|
+
worldEnv["POSTGRESQL_USERNAME"] = roleResult.worldRoleName;
|
|
32706
|
+
worldEnv["POSTGRESQL_PASSWORD"] = roleResult.password;
|
|
32707
|
+
worldEnv["POSTGRESQL_COMMON_USERNAME"] = roleResult.worldRoleName;
|
|
32708
|
+
worldEnv["POSTGRESQL_COMMON_PASSWORD"] = roleResult.password;
|
|
32709
|
+
worldEnv["POSTGRESQL_MERCHANT_USERNAME"] = roleResult.worldRoleName;
|
|
32710
|
+
worldEnv["POSTGRESQL_MERCHANT_PASSWORD"] = roleResult.password;
|
|
32711
|
+
}
|
|
32712
|
+
if (worldDbNames !== void 0 || worldRoleName !== void 0) {
|
|
32713
|
+
this.registry.update(worldId, {
|
|
32714
|
+
...worldDbNames !== void 0 ? { worldDbNames: [...worldDbNames] } : {},
|
|
32715
|
+
...worldRoleName !== void 0 ? { worldRoleName } : {}
|
|
32716
|
+
});
|
|
32717
|
+
}
|
|
32718
|
+
const hybridActive = worldDbNames !== void 0;
|
|
32719
|
+
const extraNetworks = hybridActive ? ["olam-shared"] : void 0;
|
|
32574
32720
|
await this.provider.createWorld({
|
|
32575
32721
|
id: worldId,
|
|
32576
32722
|
name: opts.name,
|
|
@@ -32580,6 +32726,7 @@ ${detail}`);
|
|
|
32580
32726
|
portOffset,
|
|
32581
32727
|
workspacePath,
|
|
32582
32728
|
appPorts: appPorts.length > 0 ? appPorts : void 0,
|
|
32729
|
+
...extraNetworks ? { extraNetworks } : {},
|
|
32583
32730
|
// Phase 7 A1: per-world cost ceiling sourced from config.cost.
|
|
32584
32731
|
// Cloudflare provider forwards this to /session/start so the DO
|
|
32585
32732
|
// can enforce kill-switch on `cost_update` events. Other providers
|
|
@@ -32603,7 +32750,16 @@ ${detail}`);
|
|
|
32603
32750
|
sm.transition("running");
|
|
32604
32751
|
this.registry.update(worldId, {
|
|
32605
32752
|
status: "running",
|
|
32606
|
-
...appPortUrls.length > 0 ? { appPortUrls } : {}
|
|
32753
|
+
...appPortUrls.length > 0 ? { appPortUrls } : {},
|
|
32754
|
+
// olam-hybrid-shared-postgres A3: persist the cloned per-world DB
|
|
32755
|
+
// names so olam destroy + olam gc can find them later (A7's
|
|
32756
|
+
// world_db_names TEXT JSON column).
|
|
32757
|
+
...worldDbNames !== void 0 ? { worldDbNames: [...worldDbNames] } : {},
|
|
32758
|
+
// olam-hybrid-shared-postgres A4: persist the role name so olam destroy
|
|
32759
|
+
// can DROP it after the world's DBs are gone (A7's world_role_name
|
|
32760
|
+
// column). Password is NOT persisted — it lives only in the world
|
|
32761
|
+
// container's env and the in-memory return value.
|
|
32762
|
+
...worldRoleName !== void 0 ? { worldRoleName } : {}
|
|
32607
32763
|
});
|
|
32608
32764
|
const containerName = `olam-${worldId}-devbox`;
|
|
32609
32765
|
try {
|
|
@@ -32903,6 +33059,14 @@ ${opts.task}`;
|
|
|
32903
33059
|
cleanupWorldTailscale(worldId, this.registry);
|
|
32904
33060
|
} catch {
|
|
32905
33061
|
}
|
|
33062
|
+
const worldDbNames = world.worldDbNames;
|
|
33063
|
+
if (worldDbNames !== void 0 && worldDbNames.length > 0) {
|
|
33064
|
+
dropPostgresWorldDbs(worldId, worldDbNames);
|
|
33065
|
+
}
|
|
33066
|
+
const worldRoleName = world.worldRoleName;
|
|
33067
|
+
if (typeof worldRoleName === "string" && worldRoleName.length > 0) {
|
|
33068
|
+
dropPostgresWorldRole(worldId, worldRoleName);
|
|
33069
|
+
}
|
|
32906
33070
|
try {
|
|
32907
33071
|
await this.provider.destroyWorld(worldId);
|
|
32908
33072
|
} catch {
|
|
@@ -33050,16 +33214,268 @@ ${opts.task}`;
|
|
|
33050
33214
|
return services;
|
|
33051
33215
|
}
|
|
33052
33216
|
};
|
|
33053
|
-
function applyPostgresNetworkOverrides(worldEnv, enrichedRepos) {
|
|
33217
|
+
function applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId) {
|
|
33054
33218
|
const hasPostgres = enrichedRepos.some((r) => r.manifest?.services?.["postgres"] !== void 0);
|
|
33055
33219
|
if (!hasPostgres)
|
|
33056
33220
|
return;
|
|
33057
|
-
|
|
33221
|
+
const hasSeedTemplate = enrichedRepos.some((r) => {
|
|
33222
|
+
const pg = r.manifest?.services?.["postgres"];
|
|
33223
|
+
return pg?.seed_template !== void 0;
|
|
33224
|
+
});
|
|
33225
|
+
const host = hasSeedTemplate ? "olam-postgres" : "postgres";
|
|
33226
|
+
worldEnv["POSTGRESQL_HOST"] = host;
|
|
33058
33227
|
worldEnv["POSTGRESQL_PORT"] = "5432";
|
|
33059
|
-
if (
|
|
33060
|
-
worldEnv["POSTGRESQL_COMMON_HOST"] =
|
|
33061
|
-
if ("POSTGRESQL_COMMON_PORT" in worldEnv)
|
|
33228
|
+
if (hasSeedTemplate) {
|
|
33229
|
+
worldEnv["POSTGRESQL_COMMON_HOST"] = host;
|
|
33062
33230
|
worldEnv["POSTGRESQL_COMMON_PORT"] = "5432";
|
|
33231
|
+
if (worldId !== void 0) {
|
|
33232
|
+
assertSafeWorldId(worldId);
|
|
33233
|
+
const seedTemplates = /* @__PURE__ */ new Set();
|
|
33234
|
+
for (const repo of enrichedRepos) {
|
|
33235
|
+
const pg = repo.manifest?.services?.["postgres"];
|
|
33236
|
+
const raw = pg?.seed_template;
|
|
33237
|
+
if (typeof raw === "string")
|
|
33238
|
+
seedTemplates.add(raw);
|
|
33239
|
+
else if (Array.isArray(raw)) {
|
|
33240
|
+
for (const s of raw)
|
|
33241
|
+
if (typeof s === "string")
|
|
33242
|
+
seedTemplates.add(s);
|
|
33243
|
+
}
|
|
33244
|
+
}
|
|
33245
|
+
for (const seed of seedTemplates) {
|
|
33246
|
+
const match = seed.match(/^atlas_([a-z][a-z0-9_]*?)_seed$/i);
|
|
33247
|
+
if (match && match[1] !== void 0) {
|
|
33248
|
+
const purpose = match[1].toUpperCase();
|
|
33249
|
+
const envKey = `POSTGRESQL_${purpose}_DATABASE`;
|
|
33250
|
+
worldEnv[envKey] = deriveWorldDbName(seed, worldId);
|
|
33251
|
+
}
|
|
33252
|
+
}
|
|
33253
|
+
}
|
|
33254
|
+
} else {
|
|
33255
|
+
if ("POSTGRESQL_COMMON_HOST" in worldEnv)
|
|
33256
|
+
worldEnv["POSTGRESQL_COMMON_HOST"] = "postgres";
|
|
33257
|
+
if ("POSTGRESQL_COMMON_PORT" in worldEnv)
|
|
33258
|
+
worldEnv["POSTGRESQL_COMMON_PORT"] = "5432";
|
|
33259
|
+
}
|
|
33260
|
+
}
|
|
33261
|
+
function applyPostgresTemplateClone(worldId, enrichedRepos, options = {}) {
|
|
33262
|
+
assertSafeWorldId(worldId);
|
|
33263
|
+
const container = options.singletonContainer ?? "olam-postgres";
|
|
33264
|
+
const user = options.postgresUser ?? "development";
|
|
33265
|
+
const seedTemplates = [];
|
|
33266
|
+
const postCloneSqlBySeed = /* @__PURE__ */ new Map();
|
|
33267
|
+
for (const repo of enrichedRepos) {
|
|
33268
|
+
const pg = repo.manifest?.services?.["postgres"];
|
|
33269
|
+
if (!pg?.seed_template)
|
|
33270
|
+
continue;
|
|
33271
|
+
const list = Array.isArray(pg.seed_template) ? pg.seed_template : [pg.seed_template];
|
|
33272
|
+
for (const t of list) {
|
|
33273
|
+
if (typeof t === "string" && t.length > 0 && !seedTemplates.includes(t)) {
|
|
33274
|
+
seedTemplates.push(t);
|
|
33275
|
+
}
|
|
33276
|
+
}
|
|
33277
|
+
if (pg.post_clone_sql && typeof pg.post_clone_sql === "object") {
|
|
33278
|
+
for (const [seedKey, sqlList] of Object.entries(pg.post_clone_sql)) {
|
|
33279
|
+
if (!Array.isArray(sqlList))
|
|
33280
|
+
continue;
|
|
33281
|
+
const existing = postCloneSqlBySeed.get(seedKey) ?? [];
|
|
33282
|
+
for (const stmt of sqlList) {
|
|
33283
|
+
if (typeof stmt === "string" && stmt.length > 0)
|
|
33284
|
+
existing.push(stmt);
|
|
33285
|
+
}
|
|
33286
|
+
postCloneSqlBySeed.set(seedKey, existing);
|
|
33287
|
+
}
|
|
33288
|
+
}
|
|
33289
|
+
}
|
|
33290
|
+
if (seedTemplates.length === 0) {
|
|
33291
|
+
return { worldDbNames: [] };
|
|
33292
|
+
}
|
|
33293
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33294
|
+
const seedToWorldDb = /* @__PURE__ */ new Map();
|
|
33295
|
+
for (const seed of seedTemplates) {
|
|
33296
|
+
seedToWorldDb.set(seed, deriveWorldDbName(seed, worldId));
|
|
33297
|
+
}
|
|
33298
|
+
const created = [];
|
|
33299
|
+
for (const seed of seedTemplates) {
|
|
33300
|
+
const worldDb = seedToWorldDb.get(seed);
|
|
33301
|
+
const exists = spawn4("docker", [
|
|
33302
|
+
"exec",
|
|
33303
|
+
container,
|
|
33304
|
+
"psql",
|
|
33305
|
+
"-U",
|
|
33306
|
+
user,
|
|
33307
|
+
"-tAc",
|
|
33308
|
+
`SELECT 1 FROM pg_database WHERE datname='${worldDb}'`
|
|
33309
|
+
], { encoding: "utf-8" });
|
|
33310
|
+
const dbAlreadyExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
33311
|
+
if (!dbAlreadyExists) {
|
|
33312
|
+
const sql = `CREATE DATABASE "${worldDb}" TEMPLATE "${seed}"`;
|
|
33313
|
+
const create = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", sql], { encoding: "utf-8" });
|
|
33314
|
+
if (create.status !== 0) {
|
|
33315
|
+
throw new Error(`failed to CREATE DATABASE "${worldDb}" TEMPLATE "${seed}": ${(create.stderr || "").trim()}`);
|
|
33316
|
+
}
|
|
33317
|
+
}
|
|
33318
|
+
created.push(worldDb);
|
|
33319
|
+
const stmts = postCloneSqlBySeed.get(seed);
|
|
33320
|
+
if (stmts !== void 0 && stmts.length > 0) {
|
|
33321
|
+
for (const rawStmt of stmts) {
|
|
33322
|
+
const stmt = interpolatePostCloneSql(rawStmt, worldId, seedToWorldDb);
|
|
33323
|
+
const fixup = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", stmt], { encoding: "utf-8" });
|
|
33324
|
+
if (fixup.status !== 0) {
|
|
33325
|
+
throw new Error(`post_clone_sql against "${worldDb}" failed (statement: ${truncate(stmt, 120)}): ${(fixup.stderr || "").trim()}`);
|
|
33326
|
+
}
|
|
33327
|
+
}
|
|
33328
|
+
}
|
|
33329
|
+
}
|
|
33330
|
+
return { worldDbNames: created };
|
|
33331
|
+
}
|
|
33332
|
+
function interpolatePostCloneSql(stmt, worldId, seedToWorldDb) {
|
|
33333
|
+
return stmt.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
33334
|
+
if (expr === "WORLD_ID")
|
|
33335
|
+
return worldId;
|
|
33336
|
+
const dbMatch = expr.match(/^WORLD_DB:(.+)$/);
|
|
33337
|
+
if (dbMatch && dbMatch[1] !== void 0) {
|
|
33338
|
+
const target = seedToWorldDb.get(dbMatch[1]);
|
|
33339
|
+
if (target === void 0) {
|
|
33340
|
+
throw new Error(`post_clone_sql references seed "${dbMatch[1]}" not declared in seed_template`);
|
|
33341
|
+
}
|
|
33342
|
+
return target;
|
|
33343
|
+
}
|
|
33344
|
+
throw new Error(`post_clone_sql contains unknown placeholder "\${${expr}}" \u2014 only \${WORLD_ID} and \${WORLD_DB:<seed>} are supported`);
|
|
33345
|
+
});
|
|
33346
|
+
}
|
|
33347
|
+
function truncate(s, max) {
|
|
33348
|
+
return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
|
|
33349
|
+
}
|
|
33350
|
+
function deriveWorldRoleName(worldId) {
|
|
33351
|
+
return `olam_world_${worldId}`;
|
|
33352
|
+
}
|
|
33353
|
+
function applyPostgresWorldRole(worldId, worldDbNames, options = {}) {
|
|
33354
|
+
assertSafeWorldId(worldId);
|
|
33355
|
+
if (worldDbNames.length === 0) {
|
|
33356
|
+
throw new Error(`applyPostgresWorldRole called with empty worldDbNames for "${worldId}" \u2014 should only run when applyPostgresTemplateClone produced clones`);
|
|
33357
|
+
}
|
|
33358
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33359
|
+
const container = options.singletonContainer ?? "olam-postgres";
|
|
33360
|
+
const user = options.postgresUser ?? "development";
|
|
33361
|
+
const genPassword = options.generatePassword ?? defaultPasswordGenerator;
|
|
33362
|
+
const worldRoleName = deriveWorldRoleName(worldId);
|
|
33363
|
+
const password = genPassword();
|
|
33364
|
+
const exists = spawn4("docker", [
|
|
33365
|
+
"exec",
|
|
33366
|
+
container,
|
|
33367
|
+
"psql",
|
|
33368
|
+
"-U",
|
|
33369
|
+
user,
|
|
33370
|
+
"-tAc",
|
|
33371
|
+
`SELECT 1 FROM pg_roles WHERE rolname='${escapeSqlLiteral(worldRoleName)}'`
|
|
33372
|
+
], { encoding: "utf-8" });
|
|
33373
|
+
const roleExists = exists.status === 0 && (exists.stdout || "").trim() === "1";
|
|
33374
|
+
const escapedPassword = escapeSqlLiteral(password);
|
|
33375
|
+
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`;
|
|
33376
|
+
const dml = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", "postgres", "-v", "ON_ERROR_STOP=1", "-c", roleDdl], { encoding: "utf-8" });
|
|
33377
|
+
if (dml.status !== 0) {
|
|
33378
|
+
throw new Error(`failed to ${roleExists ? "ALTER" : "CREATE"} ROLE "${worldRoleName}": ` + redactCreateRolePassword((dml.stderr || "").trim()));
|
|
33379
|
+
}
|
|
33380
|
+
for (const worldDb of worldDbNames) {
|
|
33381
|
+
const grantConnect = spawn4("docker", [
|
|
33382
|
+
"exec",
|
|
33383
|
+
container,
|
|
33384
|
+
"psql",
|
|
33385
|
+
"-U",
|
|
33386
|
+
user,
|
|
33387
|
+
"-d",
|
|
33388
|
+
"postgres",
|
|
33389
|
+
"-v",
|
|
33390
|
+
"ON_ERROR_STOP=1",
|
|
33391
|
+
"-c",
|
|
33392
|
+
`GRANT CONNECT ON DATABASE "${worldDb}" TO "${worldRoleName}"`
|
|
33393
|
+
], { encoding: "utf-8" });
|
|
33394
|
+
if (grantConnect.status !== 0) {
|
|
33395
|
+
throw new Error(`failed to GRANT CONNECT on "${worldDb}" to "${worldRoleName}": ${(grantConnect.stderr || "").trim()}`);
|
|
33396
|
+
}
|
|
33397
|
+
const perDbGrants = [
|
|
33398
|
+
`GRANT USAGE ON SCHEMA public TO "${worldRoleName}"`,
|
|
33399
|
+
`GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "${worldRoleName}"`,
|
|
33400
|
+
`GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO "${worldRoleName}"`,
|
|
33401
|
+
// DEFAULT PRIVILEGES for future objects — Rails migrations create new
|
|
33402
|
+
// tables/sequences post-spawn and they need to be grantable.
|
|
33403
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${worldRoleName}"`,
|
|
33404
|
+
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "${worldRoleName}"`
|
|
33405
|
+
].join("; ");
|
|
33406
|
+
const grantResult = spawn4("docker", ["exec", container, "psql", "-U", user, "-d", worldDb, "-v", "ON_ERROR_STOP=1", "-c", perDbGrants], { encoding: "utf-8" });
|
|
33407
|
+
if (grantResult.status !== 0) {
|
|
33408
|
+
throw new Error(`failed to GRANT privileges on "${worldDb}" to "${worldRoleName}": ${(grantResult.stderr || "").trim()}`);
|
|
33409
|
+
}
|
|
33410
|
+
}
|
|
33411
|
+
return { worldRoleName, password };
|
|
33412
|
+
}
|
|
33413
|
+
function defaultPasswordGenerator() {
|
|
33414
|
+
return crypto5.randomBytes(24).toString("base64url");
|
|
33415
|
+
}
|
|
33416
|
+
function escapeSqlLiteral(s) {
|
|
33417
|
+
return s.replace(/'/g, "''");
|
|
33418
|
+
}
|
|
33419
|
+
function redactCreateRolePassword(s) {
|
|
33420
|
+
return s.replace(/PASSWORD\s+'[^']*'/g, "PASSWORD '<scrubbed>'");
|
|
33421
|
+
}
|
|
33422
|
+
function dropPostgresWorldDbs(worldId, worldDbNames, options = {}) {
|
|
33423
|
+
if (worldDbNames.length === 0)
|
|
33424
|
+
return;
|
|
33425
|
+
assertSafeWorldId(worldId);
|
|
33426
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33427
|
+
const container = options.container ?? "olam-postgres";
|
|
33428
|
+
const user = options.user ?? "development";
|
|
33429
|
+
for (const db of worldDbNames) {
|
|
33430
|
+
const drop = spawn4("docker", [
|
|
33431
|
+
"exec",
|
|
33432
|
+
container,
|
|
33433
|
+
"psql",
|
|
33434
|
+
"-U",
|
|
33435
|
+
user,
|
|
33436
|
+
"-d",
|
|
33437
|
+
"postgres",
|
|
33438
|
+
"-c",
|
|
33439
|
+
`DROP DATABASE IF EXISTS "${db}" WITH (FORCE)`
|
|
33440
|
+
], { encoding: "utf-8" });
|
|
33441
|
+
if (drop.status !== 0) {
|
|
33442
|
+
console.warn(`[manager] destroyWorld(${worldId}): failed to DROP "${db}" from singleton: ${(drop.stderr || "").trim()}`);
|
|
33443
|
+
}
|
|
33444
|
+
}
|
|
33445
|
+
}
|
|
33446
|
+
function dropPostgresWorldRole(worldId, worldRoleName, options = {}) {
|
|
33447
|
+
if (!worldRoleName)
|
|
33448
|
+
return;
|
|
33449
|
+
assertSafeWorldId(worldId);
|
|
33450
|
+
const spawn4 = options.spawn ?? spawnSync4;
|
|
33451
|
+
const container = options.container ?? "olam-postgres";
|
|
33452
|
+
const user = options.user ?? "development";
|
|
33453
|
+
const cleanup = spawn4("docker", [
|
|
33454
|
+
"exec",
|
|
33455
|
+
container,
|
|
33456
|
+
"psql",
|
|
33457
|
+
"-U",
|
|
33458
|
+
user,
|
|
33459
|
+
"-d",
|
|
33460
|
+
"postgres",
|
|
33461
|
+
"-c",
|
|
33462
|
+
`DROP OWNED BY "${worldRoleName}" CASCADE; DROP ROLE IF EXISTS "${worldRoleName}"`
|
|
33463
|
+
], { encoding: "utf-8" });
|
|
33464
|
+
if (cleanup.status !== 0) {
|
|
33465
|
+
console.warn(`[manager] destroyWorld(${worldId}): failed to DROP role "${worldRoleName}" from singleton: ${(cleanup.stderr || "").trim()}`);
|
|
33466
|
+
}
|
|
33467
|
+
}
|
|
33468
|
+
function assertSafeWorldId(worldId) {
|
|
33469
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(worldId)) {
|
|
33470
|
+
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`);
|
|
33471
|
+
}
|
|
33472
|
+
}
|
|
33473
|
+
function deriveWorldDbName(seed, worldId) {
|
|
33474
|
+
if (seed.endsWith("_seed")) {
|
|
33475
|
+
const prefix = seed.slice(0, -"_seed".length);
|
|
33476
|
+
return `${prefix}_world_${worldId}`;
|
|
33477
|
+
}
|
|
33478
|
+
return `${seed}_world_${worldId}`;
|
|
33063
33479
|
}
|
|
33064
33480
|
|
|
33065
33481
|
// ../core/dist/cost/tracker.js
|
|
@@ -33985,7 +34401,7 @@ function isCloudflaredAvailable() {
|
|
|
33985
34401
|
}
|
|
33986
34402
|
}
|
|
33987
34403
|
function startTunnel(port) {
|
|
33988
|
-
return new Promise((
|
|
34404
|
+
return new Promise((resolve10, reject2) => {
|
|
33989
34405
|
const child = spawn3("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
|
|
33990
34406
|
stdio: ["ignore", "pipe", "pipe"],
|
|
33991
34407
|
detached: false
|
|
@@ -34007,7 +34423,7 @@ function startTunnel(port) {
|
|
|
34007
34423
|
if (match) {
|
|
34008
34424
|
resolved = true;
|
|
34009
34425
|
clearTimeout(timeout);
|
|
34010
|
-
|
|
34426
|
+
resolve10(match[0]);
|
|
34011
34427
|
}
|
|
34012
34428
|
}
|
|
34013
34429
|
child.stdout?.on("data", scan);
|
|
@@ -34075,8 +34491,8 @@ var DashboardManager = class {
|
|
|
34075
34491
|
}
|
|
34076
34492
|
throw err;
|
|
34077
34493
|
}
|
|
34078
|
-
await new Promise((
|
|
34079
|
-
this.server.on("listening",
|
|
34494
|
+
await new Promise((resolve10, reject2) => {
|
|
34495
|
+
this.server.on("listening", resolve10);
|
|
34080
34496
|
this.server.on("error", reject2);
|
|
34081
34497
|
});
|
|
34082
34498
|
this.info = { localUrl: `http://localhost:${port}` };
|
|
@@ -34122,8 +34538,8 @@ var DashboardManager = class {
|
|
|
34122
34538
|
async stop() {
|
|
34123
34539
|
stopTunnel();
|
|
34124
34540
|
if (this.server) {
|
|
34125
|
-
await new Promise((
|
|
34126
|
-
this.server.close(() =>
|
|
34541
|
+
await new Promise((resolve10) => {
|
|
34542
|
+
this.server.close(() => resolve10());
|
|
34127
34543
|
});
|
|
34128
34544
|
this.server = null;
|
|
34129
34545
|
}
|
|
@@ -34237,7 +34653,7 @@ var PleriClient = class {
|
|
|
34237
34653
|
|
|
34238
34654
|
// ../mcp-server/src/env-loader.ts
|
|
34239
34655
|
import { readFileSync as readFileSync17, existsSync as existsSync23, statSync as statSync7 } from "node:fs";
|
|
34240
|
-
import { join as join29, dirname as dirname15, resolve as
|
|
34656
|
+
import { join as join29, dirname as dirname15, resolve as resolve9 } from "node:path";
|
|
34241
34657
|
var PROJECT_MARKERS = [
|
|
34242
34658
|
".olam/config.yaml",
|
|
34243
34659
|
".olam/config.yml",
|
|
@@ -34245,8 +34661,8 @@ var PROJECT_MARKERS = [
|
|
|
34245
34661
|
"olam.yml"
|
|
34246
34662
|
];
|
|
34247
34663
|
function findProjectRoot2(startDir) {
|
|
34248
|
-
let dir =
|
|
34249
|
-
const root =
|
|
34664
|
+
let dir = resolve9(startDir);
|
|
34665
|
+
const root = resolve9("/");
|
|
34250
34666
|
while (true) {
|
|
34251
34667
|
for (const marker of PROJECT_MARKERS) {
|
|
34252
34668
|
if (existsSync23(join29(dir, marker))) return dir;
|