@pleri/olam-cli 0.1.125 → 0.1.127
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/create.d.ts.map +1 -1
- package/dist/commands/create.js +43 -0
- package/dist/commands/create.js.map +1 -1
- package/dist/image-digests.json +3 -3
- package/dist/index.js +305 -152
- 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 +63 -5
- package/package.json +1 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-world MCP registration helper.
|
|
3
|
+
*
|
|
4
|
+
* After `olam create` spawns a fresh world container, we register the
|
|
5
|
+
* agentmemory MCP server inside that world's claude config so the
|
|
6
|
+
* in-world claude session reaches the host-side agent-memory service
|
|
7
|
+
* without manual `claude mcp add` from the operator.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
*
|
|
11
|
+
* host docker exec world container
|
|
12
|
+
* ---- ---------- ---------------
|
|
13
|
+
* olam create ─► docker exec ─► claude mcp add agentmemory
|
|
14
|
+
* --scope user
|
|
15
|
+
* --env AGENTMEMORY_URL=...
|
|
16
|
+
* --env AGENTMEMORY_SECRET=...
|
|
17
|
+
* -- agentmemory-mcp
|
|
18
|
+
*
|
|
19
|
+
* The `agentmemory-mcp` bin is pre-baked into the devbox image (B2), so
|
|
20
|
+
* there's no `npx -y` cold-start at first claude invocation (T6).
|
|
21
|
+
*
|
|
22
|
+
* Idempotency: re-running against an already-registered world is a noop.
|
|
23
|
+
* We detect via `claude mcp list` exit code + stderr signature (same
|
|
24
|
+
* "No <scope> MCP server found" pattern reused from the host-side
|
|
25
|
+
* `olam memory uninstall` helper).
|
|
26
|
+
*
|
|
27
|
+
* Graceful skip: when the world wasn't spawned with AGENTMEMORY_* env
|
|
28
|
+
* (because the host memory service was absent at spawn time per B1),
|
|
29
|
+
* we skip registration with an informative log. The world keeps working
|
|
30
|
+
* — the @agentmemory/mcp shim falls back to local InMemoryKV.
|
|
31
|
+
*
|
|
32
|
+
* Plan reference: docs/plans/olam-agent-memory-distributed/phase-b-tasks.md B3
|
|
33
|
+
*/
|
|
34
|
+
import { type SpawnSyncReturns } from 'node:child_process';
|
|
35
|
+
/** Inject points for tests. */
|
|
36
|
+
export interface DockerExecDeps {
|
|
37
|
+
/** Wrap spawnSync so tests can stub claude/docker calls. */
|
|
38
|
+
spawn: (cmd: string, args: string[], opts: {
|
|
39
|
+
encoding: 'utf8';
|
|
40
|
+
stdio: ['ignore', 'pipe', 'pipe'];
|
|
41
|
+
}) => SpawnSyncReturns<string>;
|
|
42
|
+
/** Optional logger; defaults to console.log. */
|
|
43
|
+
log?: (msg: string) => void;
|
|
44
|
+
}
|
|
45
|
+
export declare const DEFAULT_DOCKER_EXEC_DEPS: DockerExecDeps;
|
|
46
|
+
/**
|
|
47
|
+
* Result shape — `outcome` is the load-bearing field. Callers use it to
|
|
48
|
+
* decide whether to print a success line vs. a graceful-skip notice.
|
|
49
|
+
*/
|
|
50
|
+
export type RegisterOutcome = {
|
|
51
|
+
outcome: 'registered';
|
|
52
|
+
scope: 'user';
|
|
53
|
+
} | {
|
|
54
|
+
outcome: 'already-registered';
|
|
55
|
+
scope: 'user';
|
|
56
|
+
} | {
|
|
57
|
+
outcome: 'skipped-no-env';
|
|
58
|
+
reason: string;
|
|
59
|
+
} | {
|
|
60
|
+
outcome: 'failed';
|
|
61
|
+
rc: number;
|
|
62
|
+
detail: string;
|
|
63
|
+
};
|
|
64
|
+
export interface RegisterAgentMemoryMcpOpts {
|
|
65
|
+
/** Full container name, e.g. `olam-<world-id>-devbox`. */
|
|
66
|
+
containerName: string;
|
|
67
|
+
/** The AGENTMEMORY_URL value that was injected at world spawn (B1). */
|
|
68
|
+
agentmemoryUrl: string;
|
|
69
|
+
/** The AGENTMEMORY_SECRET value that was injected at world spawn (B1). */
|
|
70
|
+
agentmemorySecret: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Probe whether `agentmemory` is already registered inside the world's
|
|
74
|
+
* claude. Uses `claude mcp list` which prints a structured list; we
|
|
75
|
+
* just look for the name in the output. Returns:
|
|
76
|
+
* - `'present'` — already registered (idempotent skip path)
|
|
77
|
+
* - `'absent'` — not registered yet (proceed to add)
|
|
78
|
+
* - `'unknown'` — `claude mcp list` failed for any reason; we don't
|
|
79
|
+
* pretend to know the state, caller decides.
|
|
80
|
+
*/
|
|
81
|
+
export declare function probeMcpListed(containerName: string, deps: DockerExecDeps): 'present' | 'absent' | 'unknown';
|
|
82
|
+
/**
|
|
83
|
+
* Register the agentmemory MCP server inside a world container.
|
|
84
|
+
*
|
|
85
|
+
* - Skips silently when `agentmemorySecret` is empty (the world was
|
|
86
|
+
* spawned without B1's env injection because the host memory service
|
|
87
|
+
* was absent at spawn time).
|
|
88
|
+
* - Skips idempotently when `claude mcp list` already shows the server.
|
|
89
|
+
* - Otherwise shells to `docker exec <world> claude mcp add agentmemory
|
|
90
|
+
* --scope user --env ... -- agentmemory-mcp`.
|
|
91
|
+
*
|
|
92
|
+
* The bearer secret transits as an argv element of the spawned `claude`
|
|
93
|
+
* process inside the container (same shape as the host-side
|
|
94
|
+
* `olam memory install`). Documented in the operator-facing
|
|
95
|
+
* docs/architecture/agent-memory.md security model.
|
|
96
|
+
*/
|
|
97
|
+
export declare function registerAgentMemoryMcp(opts: RegisterAgentMemoryMcpOpts, deps?: DockerExecDeps): RegisterOutcome;
|
|
98
|
+
//# sourceMappingURL=world-mcp-register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-mcp-register.d.ts","sourceRoot":"","sources":["../../src/lib/world-mcp-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtE,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,KAAK,EAAE,CACL,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,KAC1D,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC9B,gDAAgD;IAChD,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,wBAAwB,EAAE,cAGtC,CAAC;AAKF;;;GAGG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,OAAO,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,OAAO,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,WAAW,0BAA0B;IACzC,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,cAAc,GACnB,SAAS,GAAG,QAAQ,GAAG,SAAS,CAYlC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,0BAA0B,EAChC,IAAI,GAAE,cAAyC,GAC9C,eAAe,CAoDjB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-world MCP registration helper.
|
|
3
|
+
*
|
|
4
|
+
* After `olam create` spawns a fresh world container, we register the
|
|
5
|
+
* agentmemory MCP server inside that world's claude config so the
|
|
6
|
+
* in-world claude session reaches the host-side agent-memory service
|
|
7
|
+
* without manual `claude mcp add` from the operator.
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
*
|
|
11
|
+
* host docker exec world container
|
|
12
|
+
* ---- ---------- ---------------
|
|
13
|
+
* olam create ─► docker exec ─► claude mcp add agentmemory
|
|
14
|
+
* --scope user
|
|
15
|
+
* --env AGENTMEMORY_URL=...
|
|
16
|
+
* --env AGENTMEMORY_SECRET=...
|
|
17
|
+
* -- agentmemory-mcp
|
|
18
|
+
*
|
|
19
|
+
* The `agentmemory-mcp` bin is pre-baked into the devbox image (B2), so
|
|
20
|
+
* there's no `npx -y` cold-start at first claude invocation (T6).
|
|
21
|
+
*
|
|
22
|
+
* Idempotency: re-running against an already-registered world is a noop.
|
|
23
|
+
* We detect via `claude mcp list` exit code + stderr signature (same
|
|
24
|
+
* "No <scope> MCP server found" pattern reused from the host-side
|
|
25
|
+
* `olam memory uninstall` helper).
|
|
26
|
+
*
|
|
27
|
+
* Graceful skip: when the world wasn't spawned with AGENTMEMORY_* env
|
|
28
|
+
* (because the host memory service was absent at spawn time per B1),
|
|
29
|
+
* we skip registration with an informative log. The world keeps working
|
|
30
|
+
* — the @agentmemory/mcp shim falls back to local InMemoryKV.
|
|
31
|
+
*
|
|
32
|
+
* Plan reference: docs/plans/olam-agent-memory-distributed/phase-b-tasks.md B3
|
|
33
|
+
*/
|
|
34
|
+
import { spawnSync } from 'node:child_process';
|
|
35
|
+
export const DEFAULT_DOCKER_EXEC_DEPS = {
|
|
36
|
+
spawn: spawnSync,
|
|
37
|
+
log: (msg) => console.log(msg),
|
|
38
|
+
};
|
|
39
|
+
const MCP_NAME = 'agentmemory';
|
|
40
|
+
const MCP_BIN = 'agentmemory-mcp';
|
|
41
|
+
/**
|
|
42
|
+
* Probe whether `agentmemory` is already registered inside the world's
|
|
43
|
+
* claude. Uses `claude mcp list` which prints a structured list; we
|
|
44
|
+
* just look for the name in the output. Returns:
|
|
45
|
+
* - `'present'` — already registered (idempotent skip path)
|
|
46
|
+
* - `'absent'` — not registered yet (proceed to add)
|
|
47
|
+
* - `'unknown'` — `claude mcp list` failed for any reason; we don't
|
|
48
|
+
* pretend to know the state, caller decides.
|
|
49
|
+
*/
|
|
50
|
+
export function probeMcpListed(containerName, deps) {
|
|
51
|
+
const result = deps.spawn('docker', ['exec', containerName, 'claude', 'mcp', 'list'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
52
|
+
if (result.status !== 0)
|
|
53
|
+
return 'unknown';
|
|
54
|
+
const combined = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
|
|
55
|
+
// `claude mcp list` prints one row per registered server. Match on
|
|
56
|
+
// the name at the start of a line to avoid false positives from
|
|
57
|
+
// descriptive prose.
|
|
58
|
+
return new RegExp(`^\\s*${MCP_NAME}\\b`, 'm').test(combined) ? 'present' : 'absent';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Register the agentmemory MCP server inside a world container.
|
|
62
|
+
*
|
|
63
|
+
* - Skips silently when `agentmemorySecret` is empty (the world was
|
|
64
|
+
* spawned without B1's env injection because the host memory service
|
|
65
|
+
* was absent at spawn time).
|
|
66
|
+
* - Skips idempotently when `claude mcp list` already shows the server.
|
|
67
|
+
* - Otherwise shells to `docker exec <world> claude mcp add agentmemory
|
|
68
|
+
* --scope user --env ... -- agentmemory-mcp`.
|
|
69
|
+
*
|
|
70
|
+
* The bearer secret transits as an argv element of the spawned `claude`
|
|
71
|
+
* process inside the container (same shape as the host-side
|
|
72
|
+
* `olam memory install`). Documented in the operator-facing
|
|
73
|
+
* docs/architecture/agent-memory.md security model.
|
|
74
|
+
*/
|
|
75
|
+
export function registerAgentMemoryMcp(opts, deps = DEFAULT_DOCKER_EXEC_DEPS) {
|
|
76
|
+
if (!opts.agentmemorySecret || opts.agentmemorySecret.length === 0) {
|
|
77
|
+
return {
|
|
78
|
+
outcome: 'skipped-no-env',
|
|
79
|
+
reason: 'world has no AGENTMEMORY_SECRET (memory service was absent or --skip-memory active at spawn time)',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const probe = probeMcpListed(opts.containerName, deps);
|
|
83
|
+
if (probe === 'present') {
|
|
84
|
+
return { outcome: 'already-registered', scope: 'user' };
|
|
85
|
+
}
|
|
86
|
+
const args = [
|
|
87
|
+
'exec',
|
|
88
|
+
opts.containerName,
|
|
89
|
+
'claude',
|
|
90
|
+
'mcp',
|
|
91
|
+
'add',
|
|
92
|
+
MCP_NAME,
|
|
93
|
+
'--scope',
|
|
94
|
+
'user',
|
|
95
|
+
'--env',
|
|
96
|
+
`AGENTMEMORY_URL=${opts.agentmemoryUrl}`,
|
|
97
|
+
'--env',
|
|
98
|
+
`AGENTMEMORY_SECRET=${opts.agentmemorySecret}`,
|
|
99
|
+
'--',
|
|
100
|
+
MCP_BIN,
|
|
101
|
+
];
|
|
102
|
+
// Log without leaking the secret. Same redaction pattern as
|
|
103
|
+
// packages/cli/src/commands/memory/install.ts.
|
|
104
|
+
const redacted = args.map((a) => a.startsWith('AGENTMEMORY_SECRET=') ? 'AGENTMEMORY_SECRET=<redacted>' : a);
|
|
105
|
+
deps.log?.(`Wiring agent-memory MCP in ${opts.containerName}: docker ${redacted.join(' ')}`);
|
|
106
|
+
const result = deps.spawn('docker', args, {
|
|
107
|
+
encoding: 'utf8',
|
|
108
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
109
|
+
});
|
|
110
|
+
if (result.status === 0) {
|
|
111
|
+
return { outcome: 'registered', scope: 'user' };
|
|
112
|
+
}
|
|
113
|
+
// Defensive redaction of any echo-back of the env value.
|
|
114
|
+
const detail = (result.stderr?.trim() || result.stdout?.trim() || '(no output)').replace(/AGENTMEMORY_SECRET=\S+/g, 'AGENTMEMORY_SECRET=<redacted>');
|
|
115
|
+
return { outcome: 'failed', rc: result.status ?? 1, detail };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=world-mcp-register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-mcp-register.js","sourceRoot":"","sources":["../../src/lib/world-mcp-register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,SAAS,EAAyB,MAAM,oBAAoB,CAAC;AActE,MAAM,CAAC,MAAM,wBAAwB,GAAmB;IACtD,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;CAC/B,CAAC;AAEF,MAAM,QAAQ,GAAG,aAAa,CAAC;AAC/B,MAAM,OAAO,GAAG,iBAAiB,CAAC;AAqBlC;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAC5B,aAAqB,EACrB,IAAoB;IAEpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CACvB,QAAQ,EACR,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,EAChD,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CACxD,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,QAAQ,GAAG,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,KAAK,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IAClE,mEAAmE;IACnE,gEAAgE;IAChE,qBAAqB;IACrB,OAAO,IAAI,MAAM,CAAC,QAAQ,QAAQ,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAgC,EAChC,OAAuB,wBAAwB;IAE/C,IAAI,CAAC,IAAI,CAAC,iBAAiB,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnE,OAAO;YACL,OAAO,EAAE,gBAAgB;YACzB,MAAM,EACJ,mGAAmG;SACtG,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACvD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,GAAG;QACX,MAAM;QACN,IAAI,CAAC,aAAa;QAClB,QAAQ;QACR,KAAK;QACL,KAAK;QACL,QAAQ;QACR,SAAS;QACT,MAAM;QACN,OAAO;QACP,mBAAmB,IAAI,CAAC,cAAc,EAAE;QACxC,OAAO;QACP,sBAAsB,IAAI,CAAC,iBAAiB,EAAE;QAC9C,IAAI;QACJ,OAAO;KACR,CAAC;IACF,4DAA4D;IAC5D,+CAA+C;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9B,CAAC,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,CAC1E,CAAC;IACF,IAAI,CAAC,GAAG,EAAE,CAAC,8BAA8B,IAAI,CAAC,aAAa,YAAY,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAE7F,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE;QACxC,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;KAClC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC;IAED,yDAAyD;IACzD,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC,CAAC,OAAO,CACtF,yBAAyB,EACzB,+BAA+B,CAChC,CAAC;IACF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;AAC/D,CAAC"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -22413,7 +22413,32 @@ var serviceSchema = external_exports.object({
|
|
|
22413
22413
|
// later in the seam. Statements are NOT exposed to operator-controlled
|
|
22414
22414
|
// env variables — only the two whitelisted placeholders above. No shell
|
|
22415
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()
|
|
22416
|
+
post_clone_sql: external_exports.record(external_exports.string().min(1), external_exports.array(external_exports.string().min(1))).optional(),
|
|
22417
|
+
// olam-hybrid-shared-postgres Phase B: per-seed env-var-name override.
|
|
22418
|
+
//
|
|
22419
|
+
// Phase A's F-6 derives the DB-name env var from the seed name using a
|
|
22420
|
+
// POSTGRESQL_<PURPOSE>_DATABASE convention (atlas_common_seed →
|
|
22421
|
+
// POSTGRESQL_COMMON_DATABASE). That works for atlas-core which uses
|
|
22422
|
+
// POSTGRESQL_*-prefixed env vars throughout.
|
|
22423
|
+
//
|
|
22424
|
+
// Atlas-pay (and other future repos) use different env var names —
|
|
22425
|
+
// e.g. ATLAS_PAY_DATABASE, not POSTGRESQL_PAY_DATABASE. This map lets
|
|
22426
|
+
// a manifest declare the override explicitly:
|
|
22427
|
+
//
|
|
22428
|
+
// services:
|
|
22429
|
+
// postgres:
|
|
22430
|
+
// seed_template:
|
|
22431
|
+
// - atlas_common_seed
|
|
22432
|
+
// - atlas_pay_seed
|
|
22433
|
+
// db_env_override:
|
|
22434
|
+
// atlas_pay_seed: ATLAS_PAY_DATABASE
|
|
22435
|
+
// # atlas_common_seed keeps F-6's default POSTGRESQL_COMMON_DATABASE
|
|
22436
|
+
//
|
|
22437
|
+
// Schema: Record<seed_template_name, env_var_name>. The env var name
|
|
22438
|
+
// must be uppercase letters/digits/underscores ([A-Z][A-Z0-9_]*) — same
|
|
22439
|
+
// shape as conventional shell env vars. F-6 honors the override when
|
|
22440
|
+
// present; falls back to the derived default otherwise.
|
|
22441
|
+
db_env_override: external_exports.record(external_exports.string().min(1), external_exports.string().regex(/^[A-Z][A-Z0-9_]*$/)).optional()
|
|
22417
22442
|
}).passthrough();
|
|
22418
22443
|
var deploySchema = external_exports.object({
|
|
22419
22444
|
tags: external_exports.array(external_exports.string()).optional()
|
|
@@ -23218,6 +23243,18 @@ function readHostCpToken() {
|
|
|
23218
23243
|
return "";
|
|
23219
23244
|
}
|
|
23220
23245
|
}
|
|
23246
|
+
function readMemorySecret() {
|
|
23247
|
+
const fromEnv = process.env["OLAM_MEMORY_SECRET"];
|
|
23248
|
+
if (fromEnv && fromEnv.length > 0)
|
|
23249
|
+
return fromEnv;
|
|
23250
|
+
const file = path7.join(os5.homedir(), ".olam", "memory-secret");
|
|
23251
|
+
try {
|
|
23252
|
+
return fs5.readFileSync(file, "utf-8").trim();
|
|
23253
|
+
} catch {
|
|
23254
|
+
return "";
|
|
23255
|
+
}
|
|
23256
|
+
}
|
|
23257
|
+
var AGENTMEMORY_HOST_INTERNAL_URL = "http://host.docker.internal:3111";
|
|
23221
23258
|
function sanitizeContainerName(name) {
|
|
23222
23259
|
return name.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
23223
23260
|
}
|
|
@@ -23347,6 +23384,7 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
|
|
|
23347
23384
|
const labels = olamLabels(worldId, worldName);
|
|
23348
23385
|
const authSecret = readAuthSecret();
|
|
23349
23386
|
const hostCpToken = readHostCpToken();
|
|
23387
|
+
const memorySecret = readMemorySecret();
|
|
23350
23388
|
const worldEnv = {
|
|
23351
23389
|
OLAM_WORLD_ID: worldId,
|
|
23352
23390
|
OLAM_WORLD_NAME: worldName,
|
|
@@ -23363,6 +23401,14 @@ var createWorldContainer = async (docker, worldId, worldName, image, env, resour
|
|
|
23363
23401
|
OLAM_HOST_CP_URL: "http://host.docker.internal:19000",
|
|
23364
23402
|
...authSecret ? { OLAM_AUTH_SECRET: authSecret } : {},
|
|
23365
23403
|
...hostCpToken ? { OLAM_HOST_CP_TOKEN: hostCpToken } : {},
|
|
23404
|
+
// Phase B1 — agent-memory wiring. Both vars are injected together
|
|
23405
|
+
// (or neither): without a secret the URL is useless. The in-world
|
|
23406
|
+
// @agentmemory/mcp shim (Phase B2/B3) reads these env vars to find
|
|
23407
|
+
// the host's REST endpoint.
|
|
23408
|
+
...memorySecret ? {
|
|
23409
|
+
AGENTMEMORY_URL: AGENTMEMORY_HOST_INTERNAL_URL,
|
|
23410
|
+
AGENTMEMORY_SECRET: memorySecret
|
|
23411
|
+
} : {},
|
|
23366
23412
|
...env
|
|
23367
23413
|
};
|
|
23368
23414
|
const envList = Object.entries(worldEnv).map(([k, v]) => `${k}=${v}`);
|
|
@@ -33231,6 +33277,7 @@ function applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId) {
|
|
|
33231
33277
|
if (worldId !== void 0) {
|
|
33232
33278
|
assertSafeWorldId(worldId);
|
|
33233
33279
|
const seedTemplates = /* @__PURE__ */ new Set();
|
|
33280
|
+
const dbEnvOverride = {};
|
|
33234
33281
|
for (const repo of enrichedRepos) {
|
|
33235
33282
|
const pg = repo.manifest?.services?.["postgres"];
|
|
33236
33283
|
const raw = pg?.seed_template;
|
|
@@ -33241,12 +33288,23 @@ function applyPostgresNetworkOverrides(worldEnv, enrichedRepos, worldId) {
|
|
|
33241
33288
|
if (typeof s === "string")
|
|
33242
33289
|
seedTemplates.add(s);
|
|
33243
33290
|
}
|
|
33291
|
+
if (pg?.db_env_override && typeof pg.db_env_override === "object") {
|
|
33292
|
+
for (const [seedKey, envName] of Object.entries(pg.db_env_override)) {
|
|
33293
|
+
if (typeof envName === "string" && envName.length > 0) {
|
|
33294
|
+
dbEnvOverride[seedKey] = envName;
|
|
33295
|
+
}
|
|
33296
|
+
}
|
|
33297
|
+
}
|
|
33244
33298
|
}
|
|
33245
33299
|
for (const seed of seedTemplates) {
|
|
33246
|
-
|
|
33247
|
-
if (
|
|
33248
|
-
const
|
|
33249
|
-
|
|
33300
|
+
let envKey = dbEnvOverride[seed];
|
|
33301
|
+
if (envKey === void 0) {
|
|
33302
|
+
const match = seed.match(/^atlas_([a-z][a-z0-9_]*?)_seed$/i);
|
|
33303
|
+
if (match && match[1] !== void 0) {
|
|
33304
|
+
envKey = `POSTGRESQL_${match[1].toUpperCase()}_DATABASE`;
|
|
33305
|
+
}
|
|
33306
|
+
}
|
|
33307
|
+
if (envKey !== void 0) {
|
|
33250
33308
|
worldEnv[envKey] = deriveWorldDbName(seed, worldId);
|
|
33251
33309
|
}
|
|
33252
33310
|
}
|