@pleri/olam-cli 0.1.7
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/__tests__/auth-status.test.d.ts +2 -0
- package/dist/__tests__/auth-status.test.d.ts.map +1 -0
- package/dist/__tests__/auth-status.test.js +290 -0
- package/dist/__tests__/auth-status.test.js.map +1 -0
- package/dist/__tests__/auth-upgrade.test.d.ts +9 -0
- package/dist/__tests__/auth-upgrade.test.d.ts.map +1 -0
- package/dist/__tests__/auth-upgrade.test.js +161 -0
- package/dist/__tests__/auth-upgrade.test.js.map +1 -0
- package/dist/__tests__/create-app-urls.test.d.ts +2 -0
- package/dist/__tests__/create-app-urls.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-urls.test.js +102 -0
- package/dist/__tests__/create-app-urls.test.js.map +1 -0
- package/dist/__tests__/enter.test.d.ts +2 -0
- package/dist/__tests__/enter.test.d.ts.map +1 -0
- package/dist/__tests__/enter.test.js +90 -0
- package/dist/__tests__/enter.test.js.map +1 -0
- package/dist/__tests__/host-cp-gh-token.test.d.ts +9 -0
- package/dist/__tests__/host-cp-gh-token.test.d.ts.map +1 -0
- package/dist/__tests__/host-cp-gh-token.test.js +119 -0
- package/dist/__tests__/host-cp-gh-token.test.js.map +1 -0
- package/dist/__tests__/host-cp.test.d.ts +9 -0
- package/dist/__tests__/host-cp.test.d.ts.map +1 -0
- package/dist/__tests__/host-cp.test.js +254 -0
- package/dist/__tests__/host-cp.test.js.map +1 -0
- package/dist/__tests__/keys.test.d.ts +9 -0
- package/dist/__tests__/keys.test.d.ts.map +1 -0
- package/dist/__tests__/keys.test.js +145 -0
- package/dist/__tests__/keys.test.js.map +1 -0
- package/dist/__tests__/logs.test.d.ts +9 -0
- package/dist/__tests__/logs.test.d.ts.map +1 -0
- package/dist/__tests__/logs.test.js +124 -0
- package/dist/__tests__/logs.test.js.map +1 -0
- package/dist/__tests__/ps.test.d.ts +2 -0
- package/dist/__tests__/ps.test.d.ts.map +1 -0
- package/dist/__tests__/ps.test.js +172 -0
- package/dist/__tests__/ps.test.js.map +1 -0
- package/dist/__tests__/status-app-urls.test.d.ts +2 -0
- package/dist/__tests__/status-app-urls.test.d.ts.map +1 -0
- package/dist/__tests__/status-app-urls.test.js +125 -0
- package/dist/__tests__/status-app-urls.test.js.map +1 -0
- package/dist/__tests__/upgrade.test.d.ts +9 -0
- package/dist/__tests__/upgrade.test.d.ts.map +1 -0
- package/dist/__tests__/upgrade.test.js +262 -0
- package/dist/__tests__/upgrade.test.js.map +1 -0
- package/dist/commands/__tests__/carry-uncommitted.test.d.ts +14 -0
- package/dist/commands/__tests__/carry-uncommitted.test.d.ts.map +1 -0
- package/dist/commands/__tests__/carry-uncommitted.test.js +83 -0
- package/dist/commands/__tests__/carry-uncommitted.test.js.map +1 -0
- package/dist/commands/__tests__/openHostCpUrl.test.d.ts +2 -0
- package/dist/commands/__tests__/openHostCpUrl.test.d.ts.map +1 -0
- package/dist/commands/__tests__/openHostCpUrl.test.js +63 -0
- package/dist/commands/__tests__/openHostCpUrl.test.js.map +1 -0
- package/dist/commands/__tests__/refresh.test.d.ts +13 -0
- package/dist/commands/__tests__/refresh.test.d.ts.map +1 -0
- package/dist/commands/__tests__/refresh.test.js +170 -0
- package/dist/commands/__tests__/refresh.test.js.map +1 -0
- package/dist/commands/auth-status.d.ts +43 -0
- package/dist/commands/auth-status.d.ts.map +1 -0
- package/dist/commands/auth-status.js +208 -0
- package/dist/commands/auth-status.js.map +1 -0
- package/dist/commands/auth-upgrade.d.ts +47 -0
- package/dist/commands/auth-upgrade.d.ts.map +1 -0
- package/dist/commands/auth-upgrade.js +277 -0
- package/dist/commands/auth-upgrade.js.map +1 -0
- package/dist/commands/auth.d.ts +16 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +283 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/create.d.ts +8 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +512 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/crystallize.d.ts +8 -0
- package/dist/commands/crystallize.d.ts.map +1 -0
- package/dist/commands/crystallize.js +101 -0
- package/dist/commands/crystallize.js.map +1 -0
- package/dist/commands/destroy.d.ts +6 -0
- package/dist/commands/destroy.d.ts.map +1 -0
- package/dist/commands/destroy.js +54 -0
- package/dist/commands/destroy.js.map +1 -0
- package/dist/commands/dispatch.d.ts +9 -0
- package/dist/commands/dispatch.d.ts.map +1 -0
- package/dist/commands/dispatch.js +94 -0
- package/dist/commands/dispatch.js.map +1 -0
- package/dist/commands/enter.d.ts +63 -0
- package/dist/commands/enter.d.ts.map +1 -0
- package/dist/commands/enter.js +206 -0
- package/dist/commands/enter.js.map +1 -0
- package/dist/commands/host-cp.d.ts +191 -0
- package/dist/commands/host-cp.d.ts.map +1 -0
- package/dist/commands/host-cp.js +797 -0
- package/dist/commands/host-cp.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +143 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +22 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +203 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/keys.d.ts +26 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +151 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/lanes.d.ts +18 -0
- package/dist/commands/lanes.d.ts.map +1 -0
- package/dist/commands/lanes.js +122 -0
- package/dist/commands/lanes.js.map +1 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +39 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/logs.d.ts +38 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +177 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/observe.d.ts +9 -0
- package/dist/commands/observe.d.ts.map +1 -0
- package/dist/commands/observe.js +34 -0
- package/dist/commands/observe.js.map +1 -0
- package/dist/commands/policy-check.d.ts +14 -0
- package/dist/commands/policy-check.d.ts.map +1 -0
- package/dist/commands/policy-check.js +76 -0
- package/dist/commands/policy-check.js.map +1 -0
- package/dist/commands/pr.d.ts +17 -0
- package/dist/commands/pr.d.ts.map +1 -0
- package/dist/commands/pr.js +148 -0
- package/dist/commands/pr.js.map +1 -0
- package/dist/commands/ps.d.ts +25 -0
- package/dist/commands/ps.d.ts.map +1 -0
- package/dist/commands/ps.js +164 -0
- package/dist/commands/ps.js.map +1 -0
- package/dist/commands/refresh-helpers.d.ts +25 -0
- package/dist/commands/refresh-helpers.d.ts.map +1 -0
- package/dist/commands/refresh-helpers.js +56 -0
- package/dist/commands/refresh-helpers.js.map +1 -0
- package/dist/commands/refresh.d.ts +23 -0
- package/dist/commands/refresh.d.ts.map +1 -0
- package/dist/commands/refresh.js +237 -0
- package/dist/commands/refresh.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.d.ts +67 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +358 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/workspace.d.ts +23 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +198 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/commands/world-snapshot.d.ts +18 -0
- package/dist/commands/world-snapshot.d.ts.map +1 -0
- package/dist/commands/world-snapshot.js +327 -0
- package/dist/commands/world-snapshot.js.map +1 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +51 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18007 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.js +32236 -0
- package/dist/output.d.ts +10 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +31 -0
- package/dist/output.js.map +1 -0
- package/host-cp/compose.yaml +126 -0
- package/host-cp/src/auth-secret-hint.mjs +45 -0
- package/host-cp/src/auth.mjs +155 -0
- package/host-cp/src/compose-worlds-sources.mjs +170 -0
- package/host-cp/src/container-secret-fetcher.mjs +163 -0
- package/host-cp/src/docker-events.mjs +184 -0
- package/host-cp/src/local-worlds-source.mjs +83 -0
- package/host-cp/src/plan-orchestrator.mjs +829 -0
- package/host-cp/src/plan-progress.mjs +282 -0
- package/host-cp/src/pr-cache.mjs +201 -0
- package/host-cp/src/pr-merge-poller.mjs +154 -0
- package/host-cp/src/process-poller.mjs +250 -0
- package/host-cp/src/proxy.mjs +245 -0
- package/host-cp/src/pylon-worlds-source.mjs +68 -0
- package/host-cp/src/redact.mjs +67 -0
- package/host-cp/src/secret-cache.mjs +104 -0
- package/host-cp/src/server.mjs +2215 -0
- package/host-cp/src/sse-gate.mjs +117 -0
- package/host-cp/src/version-status.mjs +209 -0
- package/host-cp/src/workspace-catalog.mjs +149 -0
- package/host-cp/src/world-names-store.mjs +176 -0
- package/host-cp/src/world-pr-state.mjs +97 -0
- package/host-cp/src/world-progress.mjs +322 -0
- package/host-cp/src/world-tunnel-manager.mjs +288 -0
- package/host-cp/src/worlds-db-source.mjs +191 -0
- package/host-cp/src/worlds-source.mjs +59 -0
- package/package.json +38 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crystallize.js","sourceRoot":"","sources":["../../src/commands/crystallize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destroy.d.ts","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMzC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8CtD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* olam destroy <world> — Tear down a world and clean up resources.
|
|
3
|
+
*/
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { loadContext } from '../context.js';
|
|
6
|
+
import { printError, printSuccess } from '../output.js';
|
|
7
|
+
import { findHostCpContainer, callHostCpRegistry } from './host-cp.js';
|
|
8
|
+
export function registerDestroy(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('destroy')
|
|
11
|
+
.description('Destroy a world and clean up resources')
|
|
12
|
+
.argument('<world>', 'World ID')
|
|
13
|
+
.option('--skip-crystallize', 'Skip thought crystallization')
|
|
14
|
+
.action(async (worldId, opts) => {
|
|
15
|
+
const { ctx, error } = await loadContext();
|
|
16
|
+
if (!ctx) {
|
|
17
|
+
printError(error?.message ?? 'Olam is not configured. Run `olam init` first.');
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const world = ctx.worldManager.getWorld(worldId);
|
|
22
|
+
if (!world) {
|
|
23
|
+
printError(`World "${worldId}" not found. Run \`olam list\` to see worlds.`);
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const spinner = ora(`Destroying ${world.name}...`).start();
|
|
28
|
+
try {
|
|
29
|
+
await ctx.worldManager.destroyWorld(worldId, {
|
|
30
|
+
skipCrystallize: opts.skipCrystallize,
|
|
31
|
+
});
|
|
32
|
+
spinner.succeed(`Destroyed ${world.name} (${worldId})`);
|
|
33
|
+
// Auto-deregister from host CP if it's running. Best-effort —
|
|
34
|
+
// a failed deregister leaves a stale registry entry that
|
|
35
|
+
// resolves to a 502 the next time the SPA tries to fetch the
|
|
36
|
+
// world's data, which is non-fatal but ugly.
|
|
37
|
+
try {
|
|
38
|
+
const hostCp = await findHostCpContainer();
|
|
39
|
+
if (hostCp && hostCp.state === 'running') {
|
|
40
|
+
await callHostCpRegistry('DELETE', { id: worldId });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Docker unreachable / host-cp probe failed — silently skip.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
spinner.fail('Failed to destroy world');
|
|
49
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=destroy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destroy.js","sourceRoot":"","sources":["../../src/commands/destroy.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,wCAAwC,CAAC;SACrD,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;SAC/B,MAAM,CAAC,oBAAoB,EAAE,8BAA8B,CAAC;SAC5D,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,IAAmC,EAAE,EAAE;QACrE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,WAAW,EAAE,CAAC;QAC3C,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,UAAU,CAAC,KAAK,EAAE,OAAO,IAAI,gDAAgD,CAAC,CAAC;YAC/E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,UAAU,CAAC,UAAU,OAAO,+CAA+C,CAAC,CAAC;YAC7E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,CAAC,cAAc,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;QAE3D,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,YAAY,CAAC,YAAY,CAAC,OAAO,EAAE;gBAC3C,eAAe,EAAE,IAAI,CAAC,eAAe;aACtC,CAAC,CAAC;YACH,OAAO,CAAC,OAAO,CAAC,aAAa,KAAK,CAAC,IAAI,KAAK,OAAO,GAAG,CAAC,CAAC;YACxD,8DAA8D;YAC9D,yDAAyD;YACzD,6DAA6D;YAC7D,6CAA6C;YAC7C,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;gBAC3C,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBACzC,MAAM,kBAAkB,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,6DAA6D;YAC/D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YACxC,UAAU,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* olam dispatch <world> <prompt> — Send a task to a running world.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the MCP server's olam_dispatch tool, delegating to the
|
|
5
|
+
* same container API for persistent Claude session dispatch.
|
|
6
|
+
*/
|
|
7
|
+
import type { Command } from 'commander';
|
|
8
|
+
export declare function registerDispatch(program: Command): void;
|
|
9
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/commands/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6GvD"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* olam dispatch <world> <prompt> — Send a task to a running world.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the MCP server's olam_dispatch tool, delegating to the
|
|
5
|
+
* same container API for persistent Claude session dispatch.
|
|
6
|
+
*/
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import pc from 'picocolors';
|
|
9
|
+
import { loadContext } from '../context.js';
|
|
10
|
+
import { printError, printInfo, printHeader } from '../output.js';
|
|
11
|
+
import { DEEP_MODE_SUFFIX, COMPLEX_PROMPT_RE } from '@olam/core/src/orchestrator/dispatch.js';
|
|
12
|
+
export function registerDispatch(program) {
|
|
13
|
+
program
|
|
14
|
+
.command('dispatch')
|
|
15
|
+
.description('Send a prompt to a world for execution')
|
|
16
|
+
.argument('<world>', 'World ID')
|
|
17
|
+
.argument('<prompt>', 'Task prompt')
|
|
18
|
+
.option('--mode <mode>', 'Execution mode: deep, fast, auto', 'auto')
|
|
19
|
+
.option('--max-turns <n>', 'Maximum turns', parseInt)
|
|
20
|
+
.action(async (worldId, prompt, opts) => {
|
|
21
|
+
const { ctx, error } = await loadContext();
|
|
22
|
+
if (!ctx) {
|
|
23
|
+
printError(error?.message ?? 'Olam is not configured. Run `olam init` first.');
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const worldMeta = ctx.worldManager.getWorld(worldId);
|
|
28
|
+
if (!worldMeta) {
|
|
29
|
+
printError(`World "${worldId}" not found. Run \`olam list\` to see worlds.`);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (worldMeta.status !== 'running') {
|
|
34
|
+
printError(`World "${worldId}" is ${worldMeta.status}. Only running worlds accept dispatches.`);
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const spinner = ora('Dispatching...').start();
|
|
39
|
+
try {
|
|
40
|
+
const computeWorld = await ctx.computeProvider.getWorld(worldId);
|
|
41
|
+
if (!computeWorld) {
|
|
42
|
+
spinner.fail('Compute world not found');
|
|
43
|
+
printError('The world may have been destroyed externally.');
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Start Claude session if needed
|
|
48
|
+
await computeWorld.exec('curl -s -X POST http://localhost:8080/api/claude/start');
|
|
49
|
+
// Enhance prompt based on mode (shared regex with MCP server)
|
|
50
|
+
let finalPrompt = prompt;
|
|
51
|
+
if (opts.mode === 'deep') {
|
|
52
|
+
finalPrompt += DEEP_MODE_SUFFIX;
|
|
53
|
+
}
|
|
54
|
+
else if (opts.mode === 'auto') {
|
|
55
|
+
const isComplex = prompt.length > 100 ||
|
|
56
|
+
COMPLEX_PROMPT_RE.test(prompt);
|
|
57
|
+
if (isComplex) {
|
|
58
|
+
finalPrompt += DEEP_MODE_SUFFIX;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Write intent node (mirrors MCP server's world-dispatch.ts)
|
|
62
|
+
const intentPayloadFile = '/tmp/olam-dispatch-intent.json';
|
|
63
|
+
await computeWorld.exec(`cat > ${intentPayloadFile} << 'OLAM_EOF'\n${JSON.stringify({
|
|
64
|
+
tool_name: '__intent__',
|
|
65
|
+
tool_input: { prompt: finalPrompt },
|
|
66
|
+
hook_event_name: 'intent',
|
|
67
|
+
})}\nOLAM_EOF`);
|
|
68
|
+
await computeWorld.exec(`curl -s -X POST -H "Content-Type: application/json" -d @${intentPayloadFile} http://localhost:8080/hooks/thought`);
|
|
69
|
+
// Send prompt via temp file to avoid shell injection
|
|
70
|
+
const dispatchPayloadFile = '/tmp/olam-dispatch.json';
|
|
71
|
+
await computeWorld.exec(`cat > ${dispatchPayloadFile} << 'OLAM_EOF'\n${JSON.stringify({ prompt: finalPrompt })}\nOLAM_EOF`);
|
|
72
|
+
const result = await computeWorld.exec(`curl -s -X POST -H "Content-Type: application/json" -d @${dispatchPayloadFile} http://localhost:8080/dispatch`);
|
|
73
|
+
if (result.exitCode !== 0) {
|
|
74
|
+
throw new Error(result.stderr || 'Dispatch POST failed');
|
|
75
|
+
}
|
|
76
|
+
spinner.succeed('Dispatch sent');
|
|
77
|
+
const dashboardPort = 19080 + worldMeta.portOffset;
|
|
78
|
+
const terminalPort = 17681 + worldMeta.portOffset;
|
|
79
|
+
printHeader('Dispatch');
|
|
80
|
+
printInfo('World', `${worldMeta.name} (${worldId})`);
|
|
81
|
+
printInfo('Mode', opts.mode);
|
|
82
|
+
printInfo('Dashboard', `http://localhost:${dashboardPort}`);
|
|
83
|
+
printInfo('Terminal', `http://localhost:${terminalPort}`);
|
|
84
|
+
const containerName = `olam-${worldId}-devbox`;
|
|
85
|
+
console.log(`\n${pc.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
spinner.fail('Dispatch failed');
|
|
89
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../src/commands/dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* olam enter <world> — Open terminal into a world's Claude session.
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Default** (no --start-claude): print the shell command the operator
|
|
7
|
+
* can run to attach an interactive terminal to the world. Optionally
|
|
8
|
+
* `--exec` runs that command in-process instead of printing it.
|
|
9
|
+
*
|
|
10
|
+
* 2. **--start-claude** (Phase C/C4): open a tmux session inside the world,
|
|
11
|
+
* pre-fill the Claude prompt with `--task <text>` if supplied, then
|
|
12
|
+
* attach. Task text is delivered to tmux's paste buffer via STDIN
|
|
13
|
+
* (`docker exec -i ... tmux load-buffer -`) and pasted with
|
|
14
|
+
* `tmux paste-buffer` — LITERAL bytes, no shell evaluation. T17
|
|
15
|
+
* mitigation. See {@link buildStartClaudeCommands} for the contract.
|
|
16
|
+
*/
|
|
17
|
+
import type { Command } from 'commander';
|
|
18
|
+
export interface StartClaudeStep {
|
|
19
|
+
/** Human-readable description (operator-facing). */
|
|
20
|
+
readonly description: string;
|
|
21
|
+
/**
|
|
22
|
+
* The shell command to execute.
|
|
23
|
+
*
|
|
24
|
+
* SECURITY INVARIANT: this string is composed from defensively-validated
|
|
25
|
+
* identifiers (containerName, sessionName via {@link SAFE_IDENT}). It
|
|
26
|
+
* NEVER includes user-supplied free-form text — the only free-form input
|
|
27
|
+
* (`task`) rides via {@link stdin} and is delivered to tmux's
|
|
28
|
+
* `load-buffer -` STDIN, not argv.
|
|
29
|
+
*/
|
|
30
|
+
readonly command: string;
|
|
31
|
+
/**
|
|
32
|
+
* Optional STDIN payload. Set ONLY for the `tmux load-buffer -` step.
|
|
33
|
+
* The contents are written to the tmux paste buffer LITERALLY, with
|
|
34
|
+
* NO shell expansion. Shell metacharacters (`;`, `|`, `$(...)`, etc.)
|
|
35
|
+
* appear in Claude's input verbatim.
|
|
36
|
+
*/
|
|
37
|
+
readonly stdin?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the `olam enter --start-claude` command sequence.
|
|
41
|
+
*
|
|
42
|
+
* SECURITY INVARIANT (T17 mitigation, mirroring C4 spec):
|
|
43
|
+
*
|
|
44
|
+
* When `task` is supplied, the task text is delivered to tmux's paste-buffer
|
|
45
|
+
* via STDIN of `docker exec -i <c> tmux load-buffer -`. It is NEVER passed
|
|
46
|
+
* as a shell argument and NEVER interpolated into any command-line position.
|
|
47
|
+
* `tmux paste-buffer` writes buffer contents literally — without shell
|
|
48
|
+
* evaluation — so a `task` like `"Fix bug; rm -rf /tmp"` arrives in Claude's
|
|
49
|
+
* prompt area as literal text. The user reviews then presses Enter manually
|
|
50
|
+
* (no newline is appended by this builder).
|
|
51
|
+
*
|
|
52
|
+
* Mirrors the same trust class as `BootstrapStep` (bootstrap-runner.ts) and
|
|
53
|
+
* `SupervisedRepo.start` (tmux-supervisor.ts). Do not change without
|
|
54
|
+
* security review — replacing the stdin path with `tmux send-keys
|
|
55
|
+
* "$task"` (or any shell-interpolated form) re-opens the T17 RCE.
|
|
56
|
+
*
|
|
57
|
+
* @throws Error if `containerName` or `sessionName` fail the defensive
|
|
58
|
+
* regex guard (these become unquoted argv tokens in the returned
|
|
59
|
+
* commands; metachar injection there bypasses the stdin contract).
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildStartClaudeCommands(containerName: string, sessionName: string, task: string | undefined): StartClaudeStep[];
|
|
62
|
+
export declare function registerEnter(program: Command): void;
|
|
63
|
+
//# sourceMappingURL=enter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enter.d.ts","sourceRoot":"","sources":["../../src/commands/enter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,eAAe,EAAE,CAsEnB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuIpD"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* olam enter <world> — Open terminal into a world's Claude session.
|
|
3
|
+
*
|
|
4
|
+
* Two modes:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Default** (no --start-claude): print the shell command the operator
|
|
7
|
+
* can run to attach an interactive terminal to the world. Optionally
|
|
8
|
+
* `--exec` runs that command in-process instead of printing it.
|
|
9
|
+
*
|
|
10
|
+
* 2. **--start-claude** (Phase C/C4): open a tmux session inside the world,
|
|
11
|
+
* pre-fill the Claude prompt with `--task <text>` if supplied, then
|
|
12
|
+
* attach. Task text is delivered to tmux's paste buffer via STDIN
|
|
13
|
+
* (`docker exec -i ... tmux load-buffer -`) and pasted with
|
|
14
|
+
* `tmux paste-buffer` — LITERAL bytes, no shell evaluation. T17
|
|
15
|
+
* mitigation. See {@link buildStartClaudeCommands} for the contract.
|
|
16
|
+
*/
|
|
17
|
+
import { execSync } from 'node:child_process';
|
|
18
|
+
import pc from 'picocolors';
|
|
19
|
+
import { loadContext } from '../context.js';
|
|
20
|
+
import { printError } from '../output.js';
|
|
21
|
+
/** Same SAFE_IDENT contract as world/tmux-supervisor.ts and
|
|
22
|
+
* world/bootstrap-runner.ts — caller-side schema validates upstream;
|
|
23
|
+
* we re-check defensively at this seam. */
|
|
24
|
+
const SAFE_IDENT = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
25
|
+
/**
|
|
26
|
+
* Build the `olam enter --start-claude` command sequence.
|
|
27
|
+
*
|
|
28
|
+
* SECURITY INVARIANT (T17 mitigation, mirroring C4 spec):
|
|
29
|
+
*
|
|
30
|
+
* When `task` is supplied, the task text is delivered to tmux's paste-buffer
|
|
31
|
+
* via STDIN of `docker exec -i <c> tmux load-buffer -`. It is NEVER passed
|
|
32
|
+
* as a shell argument and NEVER interpolated into any command-line position.
|
|
33
|
+
* `tmux paste-buffer` writes buffer contents literally — without shell
|
|
34
|
+
* evaluation — so a `task` like `"Fix bug; rm -rf /tmp"` arrives in Claude's
|
|
35
|
+
* prompt area as literal text. The user reviews then presses Enter manually
|
|
36
|
+
* (no newline is appended by this builder).
|
|
37
|
+
*
|
|
38
|
+
* Mirrors the same trust class as `BootstrapStep` (bootstrap-runner.ts) and
|
|
39
|
+
* `SupervisedRepo.start` (tmux-supervisor.ts). Do not change without
|
|
40
|
+
* security review — replacing the stdin path with `tmux send-keys
|
|
41
|
+
* "$task"` (or any shell-interpolated form) re-opens the T17 RCE.
|
|
42
|
+
*
|
|
43
|
+
* @throws Error if `containerName` or `sessionName` fail the defensive
|
|
44
|
+
* regex guard (these become unquoted argv tokens in the returned
|
|
45
|
+
* commands; metachar injection there bypasses the stdin contract).
|
|
46
|
+
*/
|
|
47
|
+
export function buildStartClaudeCommands(containerName, sessionName, task) {
|
|
48
|
+
if (!SAFE_IDENT.test(containerName)) {
|
|
49
|
+
throw new Error(`containerName "${containerName}" must match ${SAFE_IDENT} (defensive guard)`);
|
|
50
|
+
}
|
|
51
|
+
if (!SAFE_IDENT.test(sessionName)) {
|
|
52
|
+
throw new Error(`sessionName "${sessionName}" must match ${SAFE_IDENT} (defensive guard)`);
|
|
53
|
+
}
|
|
54
|
+
if (task !== undefined && task.includes('\0')) {
|
|
55
|
+
// CP3 audit ADV-C4-001 closure: tmux paste-buffer's behavior on embedded
|
|
56
|
+
// NUL is implementation-defined (older tmux truncates at first NUL,
|
|
57
|
+
// silently submitting only the prefix). Reject early with a clear error.
|
|
58
|
+
throw new Error('task contains a NUL byte; tmux paste-buffer truncates on NUL — re-encode or remove the byte');
|
|
59
|
+
}
|
|
60
|
+
const steps = [];
|
|
61
|
+
// 1a. Open or attach to the per-world tmux session. With `-A`, if the
|
|
62
|
+
// session already exists (e.g. C1's supervisor opened it with repo
|
|
63
|
+
// windows), the -n/-cmd args are IGNORED — so step 1b ensures the
|
|
64
|
+
// `claude` window exists idempotently regardless of attach-vs-create.
|
|
65
|
+
steps.push({
|
|
66
|
+
description: 'Open or attach to per-world tmux session',
|
|
67
|
+
command: `docker exec ${containerName} tmux new-session -A -d ` +
|
|
68
|
+
`-s ${sessionName} -n claude 'claude'`,
|
|
69
|
+
});
|
|
70
|
+
// 1b. Idempotently ensure the claude window exists. CP3 audit ADV-C4-002
|
|
71
|
+
// closure: -A attach on an existing session without a `claude` window
|
|
72
|
+
// would silently no-op step 1's window arg, then step 3's paste-buffer
|
|
73
|
+
// target would fail mid-flight with the buffer already loaded.
|
|
74
|
+
steps.push({
|
|
75
|
+
description: 'Ensure claude window exists in the session',
|
|
76
|
+
command: `docker exec ${containerName} sh -c ` +
|
|
77
|
+
`"tmux new-window -t ${sessionName} -n claude 'claude' 2>/dev/null || true"`,
|
|
78
|
+
});
|
|
79
|
+
// 2. (Conditional) Deliver task text to the paste buffer via STDIN.
|
|
80
|
+
// NOTE — this is the load-bearing security primitive: task text is
|
|
81
|
+
// NEVER part of the argv. tmux load-buffer reads from stdin in
|
|
82
|
+
// literal-byte mode.
|
|
83
|
+
if (task !== undefined && task.length > 0) {
|
|
84
|
+
steps.push({
|
|
85
|
+
description: 'Load task text into tmux paste buffer via STDIN (literal — no shell eval)',
|
|
86
|
+
command: `docker exec -i ${containerName} tmux load-buffer -`,
|
|
87
|
+
stdin: task,
|
|
88
|
+
});
|
|
89
|
+
steps.push({
|
|
90
|
+
description: 'Paste buffer into the claude window (no Enter — user reviews then submits)',
|
|
91
|
+
command: `docker exec ${containerName} tmux paste-buffer ` +
|
|
92
|
+
`-t ${sessionName}:claude`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// 3. Attach an interactive terminal so the operator sees the session.
|
|
96
|
+
steps.push({
|
|
97
|
+
description: 'Attach interactive terminal to the session',
|
|
98
|
+
command: `docker exec -it ${containerName} tmux attach -t ${sessionName}`,
|
|
99
|
+
});
|
|
100
|
+
return steps;
|
|
101
|
+
}
|
|
102
|
+
export function registerEnter(program) {
|
|
103
|
+
program
|
|
104
|
+
.command('enter')
|
|
105
|
+
.description('Open terminal to a world')
|
|
106
|
+
.argument('<world>', 'World ID')
|
|
107
|
+
.option('--exec', 'Execute the command directly instead of printing it')
|
|
108
|
+
.option('--start-claude', 'Open a Claude session inside the world via tmux (literal-paste task contract)')
|
|
109
|
+
.option('--task <text>', 'Optional task text to pre-fill Claude with (delivered via tmux load-buffer; never shell-evaluated)')
|
|
110
|
+
.action(async (worldId, opts) => {
|
|
111
|
+
const { ctx, error } = await loadContext();
|
|
112
|
+
if (!ctx) {
|
|
113
|
+
printError(error?.message ?? 'Olam is not configured. Run `olam init` first.');
|
|
114
|
+
process.exitCode = 1;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const computeWorld = await ctx.computeProvider.getWorld(worldId);
|
|
118
|
+
if (!computeWorld) {
|
|
119
|
+
printError(`World "${worldId}" not found.`);
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const connectionInfo = await computeWorld.getConnectionInfo();
|
|
124
|
+
// --start-claude path: tmux session with literal-paste task contract.
|
|
125
|
+
if (opts.startClaude) {
|
|
126
|
+
if (computeWorld.provider !== 'docker') {
|
|
127
|
+
printError(`--start-claude is only supported on the docker provider today (got "${computeWorld.provider}")`);
|
|
128
|
+
process.exitCode = 1;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const containerName = connectionInfo.containerId;
|
|
132
|
+
const sessionName = `olam-${worldId}`;
|
|
133
|
+
let steps;
|
|
134
|
+
try {
|
|
135
|
+
steps = buildStartClaudeCommands(containerName, sessionName, opts.task);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
139
|
+
process.exitCode = 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (opts.exec) {
|
|
143
|
+
for (const step of steps) {
|
|
144
|
+
try {
|
|
145
|
+
execSync(step.command, {
|
|
146
|
+
stdio: step.stdin !== undefined
|
|
147
|
+
? ['pipe', 'inherit', 'inherit']
|
|
148
|
+
: 'inherit',
|
|
149
|
+
input: step.stdin,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
printError(`Step "${step.description}" failed: ` +
|
|
154
|
+
(err instanceof Error ? err.message : String(err)));
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
console.log('Run these commands in order to enter the world:\n');
|
|
162
|
+
// CP3 audit ADV-C4-004 closure: do NOT apply pc.bold to commands in
|
|
163
|
+
// --start-claude print mode — these lines are meant to be copy-pasted
|
|
164
|
+
// into a shell or saved to a script. With FORCE_COLOR=1 set in the
|
|
165
|
+
// operator's env, pc.bold injects ANSI escapes that break the saved
|
|
166
|
+
// script ("command not found: \e[1mdocker"). Plain text only.
|
|
167
|
+
for (const step of steps) {
|
|
168
|
+
console.log(` ${pc.dim(`# ${step.description}`)}`);
|
|
169
|
+
if (step.stdin !== undefined) {
|
|
170
|
+
// Print form: shell-quote the task and pipe via printf. Same
|
|
171
|
+
// security boundary — outer single-quotes neutralize all shell
|
|
172
|
+
// metacharacters inside the payload.
|
|
173
|
+
const escaped = step.stdin.replace(/'/g, "'\\''");
|
|
174
|
+
console.log(` printf '%s' '${escaped}' | ${step.command}`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log(` ${step.command}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Legacy default path — unchanged.
|
|
183
|
+
const { getEnterCommand } = await import('@olam/core/src/orchestrator/enter.js');
|
|
184
|
+
const result = getEnterCommand(worldId, connectionInfo.containerId, computeWorld.provider, connectionInfo.ssh?.host);
|
|
185
|
+
if (!result.command) {
|
|
186
|
+
printError(result.instructions);
|
|
187
|
+
process.exitCode = 1;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (opts.exec) {
|
|
191
|
+
try {
|
|
192
|
+
execSync(result.command, { stdio: 'inherit' });
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
printError(err instanceof Error ? err.message : `Command failed: ${result.command}`);
|
|
196
|
+
process.exitCode = 1;
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
console.log('Run this command to enter the world:\n');
|
|
201
|
+
console.log(` ${pc.bold(result.command)}`);
|
|
202
|
+
const containerName = `olam-${worldId}-devbox`;
|
|
203
|
+
console.log(`\n${pc.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=enter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enter.js","sourceRoot":"","sources":["../../src/commands/enter.ts"],"names":[],"mappings":"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"}
|