@pleri/olam-cli 0.1.111 → 0.1.113

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/index.js CHANGED
@@ -21,11 +21,11 @@ import * as path from "node:path";
21
21
  import { fileURLToPath } from "node:url";
22
22
  function readCliVersion() {
23
23
  try {
24
- const here = path.dirname(fileURLToPath(import.meta.url));
24
+ const here2 = path.dirname(fileURLToPath(import.meta.url));
25
25
  for (const candidate of [
26
- path.join(here, "package.json"),
27
- path.join(here, "..", "package.json"),
28
- path.join(here, "..", "..", "package.json")
26
+ path.join(here2, "package.json"),
27
+ path.join(here2, "..", "package.json"),
28
+ path.join(here2, "..", "..", "package.json")
29
29
  ]) {
30
30
  if (fs.existsSync(candidate)) {
31
31
  const pkg = JSON.parse(fs.readFileSync(candidate, "utf-8"));
@@ -5198,7 +5198,7 @@ async function safeText(res) {
5198
5198
  }
5199
5199
  }
5200
5200
  function sleep(ms) {
5201
- return new Promise((resolve11) => setTimeout(resolve11, ms));
5201
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
5202
5202
  }
5203
5203
  var DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, RETRY_COUNT, RETRY_BACKOFF_MS, AuthClient;
5204
5204
  var init_client = __esm({
@@ -5345,12 +5345,12 @@ import { existsSync as existsSync9 } from "node:fs";
5345
5345
  import * as path9 from "node:path";
5346
5346
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5347
5347
  function resolveAuthServicePath() {
5348
- const here = fileURLToPath2(import.meta.url);
5349
- const pkgsDir = path9.resolve(path9.dirname(here), "..", "..", "..");
5348
+ const here2 = fileURLToPath2(import.meta.url);
5349
+ const pkgsDir = path9.resolve(path9.dirname(here2), "..", "..", "..");
5350
5350
  return path9.join(pkgsDir, "auth-service");
5351
5351
  }
5352
5352
  function sleep2(ms) {
5353
- return new Promise((resolve11) => setTimeout(resolve11, ms));
5353
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
5354
5354
  }
5355
5355
  var DEFAULT_PORT, DEFAULT_VOLUME, DEFAULT_CONTAINER, DEFAULT_IMAGE, AuthContainerController;
5356
5356
  var init_container = __esm({
@@ -5645,6 +5645,98 @@ var init_network = __esm({
5645
5645
  }
5646
5646
  });
5647
5647
 
5648
+ // ../adapters/dist/docker/pull.js
5649
+ import { spawn } from "node:child_process";
5650
+ function spawnAsync(cmd, args, opts = {}) {
5651
+ return new Promise((resolve12) => {
5652
+ const child = spawn(cmd, [...args], {
5653
+ stdio: ["ignore", "pipe", "pipe"],
5654
+ signal: opts.signal
5655
+ });
5656
+ let stdout = "";
5657
+ let stderr = "";
5658
+ child.stdout?.on("data", (chunk) => {
5659
+ stdout += chunk.toString();
5660
+ });
5661
+ child.stderr?.on("data", (chunk) => {
5662
+ stderr += chunk.toString();
5663
+ });
5664
+ child.on("error", (err) => {
5665
+ resolve12({ exitCode: -1, stdout, stderr: stderr + err.message });
5666
+ });
5667
+ child.on("close", (code) => {
5668
+ resolve12({ exitCode: code ?? -1, stdout, stderr });
5669
+ });
5670
+ });
5671
+ }
5672
+ function isTransientPullFailure(result) {
5673
+ const s = result.stderr.toLowerCase();
5674
+ return result.exitCode !== 0 && (/timeout|connection|temporarily|429|503|tls handshake|network/.test(s) || result.exitCode === 124 || result.exitCode === -1);
5675
+ }
5676
+ async function pullImageWithRetry(imageRef, docker2 = realDocker, policy = DEFAULT_PULL_POLICY) {
5677
+ const key = policy.platform ? `${imageRef}@@${policy.platform}` : imageRef;
5678
+ const existing = inflightPulls.get(key);
5679
+ if (existing)
5680
+ return existing;
5681
+ const promise = (async () => {
5682
+ let result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs, policy.platform);
5683
+ if (result.exitCode !== 0 && policy.retryOnce && isTransientPullFailure(result)) {
5684
+ result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs, policy.platform);
5685
+ }
5686
+ return result;
5687
+ })();
5688
+ inflightPulls.set(key, promise);
5689
+ try {
5690
+ return await promise;
5691
+ } finally {
5692
+ inflightPulls.delete(key);
5693
+ }
5694
+ }
5695
+ async function pullOnce(imageRef, docker2, timeoutMs, platform) {
5696
+ const ac = new AbortController();
5697
+ const timer = setTimeout(() => ac.abort(), timeoutMs).unref?.();
5698
+ try {
5699
+ return await docker2.pull(imageRef, { signal: ac.signal, ...platform ? { platform } : {} });
5700
+ } finally {
5701
+ if (timer)
5702
+ clearTimeout(timer);
5703
+ }
5704
+ }
5705
+ var realDocker, inflightPulls, DEFAULT_PULL_POLICY;
5706
+ var init_pull = __esm({
5707
+ "../adapters/dist/docker/pull.js"() {
5708
+ "use strict";
5709
+ realDocker = {
5710
+ async info() {
5711
+ return spawnAsync("docker", ["info"]);
5712
+ },
5713
+ async pull(imageRef, opts) {
5714
+ const args = ["pull"];
5715
+ if (opts?.platform)
5716
+ args.push(`--platform=${opts.platform}`);
5717
+ args.push(imageRef);
5718
+ return spawnAsync("docker", args, { signal: opts?.signal });
5719
+ },
5720
+ async inspectLabel(imageRef, label) {
5721
+ return spawnAsync("docker", [
5722
+ "inspect",
5723
+ imageRef,
5724
+ "--format",
5725
+ `{{ index .Config.Labels "${label}" }}`
5726
+ ]);
5727
+ },
5728
+ async tag(source, target) {
5729
+ return spawnAsync("docker", ["tag", source, target]);
5730
+ }
5731
+ };
5732
+ inflightPulls = /* @__PURE__ */ new Map();
5733
+ DEFAULT_PULL_POLICY = {
5734
+ perAttemptTimeoutMs: 18e4,
5735
+ retryOnce: true
5736
+ };
5737
+ }
5738
+ });
5739
+
5648
5740
  // ../adapters/dist/docker/volume.js
5649
5741
  var volumeName, createVolume;
5650
5742
  var init_volume = __esm({
@@ -5736,12 +5828,14 @@ async function auditPortsForZombies(docker2, hostPorts) {
5736
5828
  }
5737
5829
  }
5738
5830
  }
5739
- var serviceContainerName, worldContainerName, miseCacheVolumeName, PACKAGE_MANAGER_CACHE_DIRS, createServiceContainer, DEFAULT_IMAGE2, CONTROL_PLANE_PORT, HOST_CONTROL_PLANE_BASE, HOST_APP_PORT_BASE_DELTA, DEFAULT_MEMORY_BYTES, PortHeldByZombieError, createWorldContainer, stopAndRemove;
5831
+ var realPullImage, serviceContainerName, worldContainerName, miseCacheVolumeName, PACKAGE_MANAGER_CACHE_DIRS, createServiceContainer, DEFAULT_IMAGE2, CONTROL_PLANE_PORT, HOST_CONTROL_PLANE_BASE, HOST_APP_PORT_BASE_DELTA, DEFAULT_MEMORY_BYTES, PortHeldByZombieError, createWorldContainer, stopAndRemove;
5740
5832
  var init_container2 = __esm({
5741
5833
  "../adapters/dist/docker/container.js"() {
5742
5834
  "use strict";
5743
5835
  init_network();
5836
+ init_pull();
5744
5837
  init_volume();
5838
+ realPullImage = (imageRef, platform) => pullImageWithRetry(imageRef, void 0, platform ? { perAttemptTimeoutMs: 18e4, retryOnce: true, platform } : { perAttemptTimeoutMs: 18e4, retryOnce: true });
5745
5839
  serviceContainerName = (worldId, serviceName) => `olam-${sanitizeContainerName(worldId)}-${sanitizeContainerName(serviceName)}`;
5746
5840
  worldContainerName = (worldId) => `olam-${sanitizeContainerName(worldId)}-devbox`;
5747
5841
  miseCacheVolumeName = (arch2 = process.arch) => `olam-mise-cache-${arch2}`;
@@ -5751,10 +5845,15 @@ var init_container2 = __esm({
5751
5845
  { name: "yarn", hostSubpath: ".olam/cache/yarn", containerPath: "/usr/local/share/.cache/yarn" },
5752
5846
  { name: "pip", hostSubpath: ".olam/cache/pip", containerPath: "/home/olam/.cache/pip" }
5753
5847
  ];
5754
- createServiceContainer = async (docker2, worldId, worldName, service, portOffset) => {
5848
+ createServiceContainer = async (docker2, worldId, worldName, service, portOffset, pullImage = realPullImage) => {
5755
5849
  const labels = olamLabels(worldId, worldName);
5756
5850
  const hostPort = service.port + portOffset;
5757
5851
  const envList = service.environment ? Object.entries(service.environment).map(([k, v]) => `${k}=${v}`) : [];
5852
+ const pullResult = await pullImage(service.image, service.platform);
5853
+ if (pullResult.exitCode !== 0) {
5854
+ const reason = pullResult.stderr.trim() || `exit ${pullResult.exitCode}`;
5855
+ throw new Error(`failed to pull ${service.image}${service.platform ? ` (platform=${service.platform})` : ""}: ${reason}`);
5856
+ }
5758
5857
  const container = await docker2.createContainer({
5759
5858
  name: serviceContainerName(worldId, service.name),
5760
5859
  Image: service.image,
@@ -5951,7 +6050,7 @@ var demuxStream, execInContainer;
5951
6050
  var init_exec = __esm({
5952
6051
  "../adapters/dist/docker/exec.js"() {
5953
6052
  "use strict";
5954
- demuxStream = (stream) => new Promise((resolve11, reject) => {
6053
+ demuxStream = (stream) => new Promise((resolve12, reject) => {
5955
6054
  const stdoutChunks = [];
5956
6055
  const stderrChunks = [];
5957
6056
  const stdout = new PassThrough();
@@ -5965,7 +6064,7 @@ var init_exec = __esm({
5965
6064
  stream.pipe(stdout);
5966
6065
  }
5967
6066
  stream.on("end", () => {
5968
- resolve11({
6067
+ resolve12({
5969
6068
  stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
5970
6069
  stderr: Buffer.concat(stderrChunks).toString("utf-8")
5971
6070
  });
@@ -6112,10 +6211,18 @@ var init_provider = __esm({
6112
6211
  };
6113
6212
  docker;
6114
6213
  dockerOptions;
6115
- constructor(dockerOptions) {
6214
+ /**
6215
+ * Injectable image-pull helper. Defaults to the real
6216
+ * `pullImageWithRetry` (docker CLI shell-out). Tests override with a
6217
+ * no-op so they don't need a real Docker daemon to exercise the
6218
+ * dockerode-mocked code paths.
6219
+ */
6220
+ pullImage;
6221
+ constructor(dockerOptions, opts) {
6116
6222
  super();
6117
6223
  this.docker = new Dockerode(dockerOptions);
6118
6224
  this.dockerOptions = dockerOptions;
6225
+ this.pullImage = opts?.pullImage;
6119
6226
  }
6120
6227
  // -----------------------------------------------------------------------
6121
6228
  // createWorld
@@ -6134,7 +6241,7 @@ var init_provider = __esm({
6134
6241
  await createVolume(this.docker, id, name, svc.name);
6135
6242
  }
6136
6243
  for (const svc of services) {
6137
- await createServiceContainer(this.docker, id, name, svc, portOffset);
6244
+ await createServiceContainer(this.docker, id, name, svc, portOffset, this.pullImage);
6138
6245
  }
6139
6246
  const SERVICE_ENV_ALIASES = {
6140
6247
  postgres: "POSTGRESQL",
@@ -6389,7 +6496,7 @@ var init_connection = __esm({
6389
6496
  // -----------------------------------------------------------------------
6390
6497
  async exec(host, command) {
6391
6498
  const client = await this.getConnection(host);
6392
- return new Promise((resolve11, reject) => {
6499
+ return new Promise((resolve12, reject) => {
6393
6500
  client.exec(command, (err, stream) => {
6394
6501
  if (err) {
6395
6502
  reject(new Error(`SSH exec failed on ${host}: ${err.message}`));
@@ -6404,7 +6511,7 @@ var init_connection = __esm({
6404
6511
  stderr += data.toString();
6405
6512
  });
6406
6513
  stream.on("close", (code) => {
6407
- resolve11({
6514
+ resolve12({
6408
6515
  exitCode: code ?? 0,
6409
6516
  stdout: stdout.trimEnd(),
6410
6517
  stderr: stderr.trimEnd()
@@ -6435,10 +6542,10 @@ var init_connection = __esm({
6435
6542
  throw new Error(`No SSH configuration found for host: ${host}`);
6436
6543
  }
6437
6544
  const client = new SSHClient();
6438
- return new Promise((resolve11, reject) => {
6545
+ return new Promise((resolve12, reject) => {
6439
6546
  client.on("ready", () => {
6440
6547
  this.connections.set(host, client);
6441
- resolve11(client);
6548
+ resolve12(client);
6442
6549
  }).on("error", (err) => {
6443
6550
  this.connections.delete(host);
6444
6551
  reject(new Error(`SSH connection to ${host} failed: ${err.message}`));
@@ -7012,6 +7119,8 @@ __export(dist_exports, {
7012
7119
  buildPackageManagerCacheBinds: () => buildPackageManagerCacheBinds,
7013
7120
  cleanupOrphanedResources: () => cleanupOrphanedResources,
7014
7121
  describeDockerEndpoint: () => describeDockerEndpoint,
7122
+ pullImageWithRetry: () => pullImageWithRetry,
7123
+ realDocker: () => realDocker,
7015
7124
  resolveDockerHostOptions: () => resolveDockerHostOptions
7016
7125
  });
7017
7126
  var init_dist = __esm({
@@ -7023,6 +7132,7 @@ var init_dist = __esm({
7023
7132
  init_host();
7024
7133
  init_container2();
7025
7134
  init_container2();
7135
+ init_pull();
7026
7136
  init_provider2();
7027
7137
  init_provider3();
7028
7138
  }
@@ -9056,7 +9166,7 @@ import * as crypto4 from "node:crypto";
9056
9166
  import * as fs16 from "node:fs";
9057
9167
  import * as os10 from "node:os";
9058
9168
  import * as path17 from "node:path";
9059
- import { execFileSync as execFileSync4, spawn } from "node:child_process";
9169
+ import { execFileSync as execFileSync4, spawn as spawn2 } from "node:child_process";
9060
9170
  import { gunzipSync } from "node:zlib";
9061
9171
  function snapshotsDir() {
9062
9172
  return process.env["OLAM_SNAPSHOTS_DIR"] ?? path17.join(os10.homedir(), ".olam", "snapshots");
@@ -9477,7 +9587,7 @@ function spawnAutoCapture(worldId, olamBin = "olam") {
9477
9587
  if (!/^[a-zA-Z0-9_\-.]+$/.test(worldId))
9478
9588
  return null;
9479
9589
  try {
9480
- const child = spawn(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
9590
+ const child = spawn2(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
9481
9591
  detached: true,
9482
9592
  stdio: "ignore",
9483
9593
  // OLAM_INTERNAL_SNAPSHOT=1 sentinel — Phase D D1's deprecation
@@ -12966,7 +13076,7 @@ var init_state2 = __esm({
12966
13076
  });
12967
13077
 
12968
13078
  // ../core/dist/dashboard/tunnel.js
12969
- import { spawn as spawn2, execSync as execSync6 } from "node:child_process";
13079
+ import { spawn as spawn3, execSync as execSync6 } from "node:child_process";
12970
13080
  function isCloudflaredAvailable() {
12971
13081
  try {
12972
13082
  execSync6("which cloudflared", { stdio: "ignore" });
@@ -12976,8 +13086,8 @@ function isCloudflaredAvailable() {
12976
13086
  }
12977
13087
  }
12978
13088
  function startTunnel(port) {
12979
- return new Promise((resolve11, reject) => {
12980
- const child = spawn2("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
13089
+ return new Promise((resolve12, reject) => {
13090
+ const child = spawn3("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
12981
13091
  stdio: ["ignore", "pipe", "pipe"],
12982
13092
  detached: false
12983
13093
  });
@@ -12998,7 +13108,7 @@ function startTunnel(port) {
12998
13108
  if (match2) {
12999
13109
  resolved = true;
13000
13110
  clearTimeout(timeout);
13001
- resolve11(match2[0]);
13111
+ resolve12(match2[0]);
13002
13112
  }
13003
13113
  }
13004
13114
  child.stdout?.on("data", scan);
@@ -13085,8 +13195,8 @@ var init_dashboard = __esm({
13085
13195
  }
13086
13196
  throw err;
13087
13197
  }
13088
- await new Promise((resolve11, reject) => {
13089
- this.server.on("listening", resolve11);
13198
+ await new Promise((resolve12, reject) => {
13199
+ this.server.on("listening", resolve12);
13090
13200
  this.server.on("error", reject);
13091
13201
  });
13092
13202
  this.info = { localUrl: `http://localhost:${port}` };
@@ -13132,8 +13242,8 @@ var init_dashboard = __esm({
13132
13242
  async stop() {
13133
13243
  stopTunnel();
13134
13244
  if (this.server) {
13135
- await new Promise((resolve11) => {
13136
- this.server.close(() => resolve11());
13245
+ await new Promise((resolve12) => {
13246
+ this.server.close(() => resolve12());
13137
13247
  });
13138
13248
  this.server = null;
13139
13249
  }
@@ -13955,8 +14065,8 @@ import { existsSync as existsSync23 } from "node:fs";
13955
14065
  import { dirname as dirname17, join as join28, resolve as resolve8 } from "node:path";
13956
14066
  import { fileURLToPath as fileURLToPath5 } from "node:url";
13957
14067
  function installRoot(metaUrl = import.meta.url) {
13958
- const here = fileURLToPath5(metaUrl);
13959
- return resolve8(dirname17(here), "..");
14068
+ const here2 = fileURLToPath5(metaUrl);
14069
+ return resolve8(dirname17(here2), "..");
13960
14070
  }
13961
14071
  function isDevMode(env = process.env, installRootDir = installRoot()) {
13962
14072
  if (env.OLAM_DEV !== "1") return false;
@@ -14931,11 +15041,12 @@ import ora2 from "ora";
14931
15041
  import pc7 from "picocolors";
14932
15042
 
14933
15043
  // src/commands/bootstrap.ts
15044
+ init_dist();
14934
15045
  init_install_root();
14935
15046
  init_exit_codes();
14936
15047
  init_protocol_version();
14937
15048
  init_output();
14938
- import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
15049
+ import { spawnSync as spawnSync6 } from "node:child_process";
14939
15050
  import { existsSync as existsSync24, readFileSync as readFileSync18 } from "node:fs";
14940
15051
  import { join as join29 } from "node:path";
14941
15052
  import ora from "ora";
@@ -14966,82 +15077,6 @@ function loadImageDigests(installRootDir = installRoot()) {
14966
15077
  }
14967
15078
  return parsed;
14968
15079
  }
14969
- var realDocker = {
14970
- async info() {
14971
- return spawnAsync("docker", ["info"]);
14972
- },
14973
- async pull(imageRef, opts) {
14974
- return spawnAsync("docker", ["pull", imageRef], { signal: opts?.signal });
14975
- },
14976
- async inspectLabel(imageRef, label) {
14977
- return spawnAsync("docker", [
14978
- "inspect",
14979
- imageRef,
14980
- "--format",
14981
- `{{ index .Config.Labels "${label}" }}`
14982
- ]);
14983
- },
14984
- async tag(source, target) {
14985
- return spawnAsync("docker", ["tag", source, target]);
14986
- }
14987
- };
14988
- function spawnAsync(cmd, args, opts = {}) {
14989
- return new Promise((resolve11) => {
14990
- const child = spawn3(cmd, [...args], {
14991
- stdio: ["ignore", "pipe", "pipe"],
14992
- signal: opts.signal
14993
- });
14994
- let stdout = "";
14995
- let stderr = "";
14996
- child.stdout?.on("data", (chunk) => {
14997
- stdout += chunk.toString();
14998
- });
14999
- child.stderr?.on("data", (chunk) => {
15000
- stderr += chunk.toString();
15001
- });
15002
- child.on("error", (err) => {
15003
- resolve11({ exitCode: -1, stdout, stderr: stderr + err.message });
15004
- });
15005
- child.on("close", (code) => {
15006
- resolve11({ exitCode: code ?? -1, stdout, stderr });
15007
- });
15008
- });
15009
- }
15010
- var inflightPulls = /* @__PURE__ */ new Map();
15011
- var DEFAULT_PULL_POLICY = {
15012
- perAttemptTimeoutMs: 18e4,
15013
- retryOnce: true
15014
- };
15015
- function isTransientPullFailure(result) {
15016
- const s = result.stderr.toLowerCase();
15017
- return result.exitCode !== 0 && (/timeout|connection|temporarily|429|503|tls handshake|network/.test(s) || result.exitCode === 124 || result.exitCode === -1);
15018
- }
15019
- async function pullImageWithRetry(imageRef, docker2 = realDocker, policy = DEFAULT_PULL_POLICY) {
15020
- const existing = inflightPulls.get(imageRef);
15021
- if (existing) return existing;
15022
- const promise = (async () => {
15023
- let result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs);
15024
- if (result.exitCode !== 0 && policy.retryOnce && isTransientPullFailure(result)) {
15025
- result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs);
15026
- }
15027
- return result;
15028
- })();
15029
- inflightPulls.set(imageRef, promise);
15030
- try {
15031
- return await promise;
15032
- } finally {
15033
- inflightPulls.delete(imageRef);
15034
- }
15035
- }
15036
- async function pullOnce(imageRef, docker2, timeoutMs) {
15037
- const ac = new AbortController();
15038
- const timer = setTimeout(() => ac.abort(), timeoutMs).unref?.();
15039
- try {
15040
- return await docker2.pull(imageRef, { signal: ac.signal });
15041
- } finally {
15042
- if (timer) clearTimeout(timer);
15043
- }
15044
- }
15045
15080
  var REAL_OLAM_SUBCOMMAND = (args) => {
15046
15081
  const result = spawnSync6(process.execPath, [
15047
15082
  process.argv[1] ?? "olam",
@@ -15361,10 +15396,10 @@ async function confirm(message) {
15361
15396
  if (!process.stdin.isTTY) return true;
15362
15397
  const { createInterface: createInterface6 } = await import("node:readline");
15363
15398
  const rl = createInterface6({ input: process.stdin, output: process.stdout });
15364
- return new Promise((resolve11) => {
15399
+ return new Promise((resolve12) => {
15365
15400
  rl.question(`${message} [y/N] `, (answer) => {
15366
15401
  rl.close();
15367
- resolve11(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
15402
+ resolve12(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
15368
15403
  });
15369
15404
  });
15370
15405
  }
@@ -15738,7 +15773,7 @@ var McpAuthContainerController = class {
15738
15773
  }
15739
15774
  };
15740
15775
  function sleep3(ms) {
15741
- return new Promise((resolve11) => setTimeout(resolve11, ms));
15776
+ return new Promise((resolve12) => setTimeout(resolve12, ms));
15742
15777
  }
15743
15778
  function dumpContainerLogs(container, tail = 40) {
15744
15779
  try {
@@ -17298,14 +17333,14 @@ function printTable(entries) {
17298
17333
  async function confirmInteractive() {
17299
17334
  process.stdout.write(" Type `yes` to proceed: ");
17300
17335
  const buf = [];
17301
- return new Promise((resolve11) => {
17336
+ return new Promise((resolve12) => {
17302
17337
  const onData = (chunk) => {
17303
17338
  buf.push(chunk);
17304
17339
  if (Buffer.concat(buf).toString("utf-8").includes("\n")) {
17305
17340
  process.stdin.removeListener("data", onData);
17306
17341
  process.stdin.pause();
17307
17342
  const answer = Buffer.concat(buf).toString("utf-8").trim();
17308
- resolve11(answer.toLowerCase() === "yes");
17343
+ resolve12(answer.toLowerCase() === "yes");
17309
17344
  }
17310
17345
  };
17311
17346
  process.stdin.resume();
@@ -23020,10 +23055,10 @@ async function confirm2(message) {
23020
23055
  if (!process.stdin.isTTY) return true;
23021
23056
  const { createInterface: createInterface6 } = await import("node:readline");
23022
23057
  const rl = createInterface6({ input: process.stdin, output: process.stdout });
23023
- return new Promise((resolve11) => {
23058
+ return new Promise((resolve12) => {
23024
23059
  rl.question(`${message} [y/N] `, (answer) => {
23025
23060
  rl.close();
23026
- resolve11(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
23061
+ resolve12(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
23027
23062
  });
23028
23063
  });
23029
23064
  }
@@ -25540,10 +25575,10 @@ var NEXT_STEPS_DOCS = [
25540
25575
  "docs/architecture/manifest-spec.md \u2014 per-repo .adb.yaml schema",
25541
25576
  "docs/architecture/config-spec.md \u2014 workspace .olam/config.yaml schema"
25542
25577
  ];
25543
- var defaultSpawn = (cmd, args) => new Promise((resolve11) => {
25578
+ var defaultSpawn = (cmd, args) => new Promise((resolve12) => {
25544
25579
  const child = spawn5(cmd, [...args], { stdio: "inherit" });
25545
- child.on("exit", (code) => resolve11({ status: code }));
25546
- child.on("error", () => resolve11({ status: 1 }));
25580
+ child.on("exit", (code) => resolve12({ status: code }));
25581
+ child.on("error", () => resolve12({ status: 1 }));
25547
25582
  });
25548
25583
  var defaultPrompt = (question, defaultYes) => {
25549
25584
  if (!process.stdin.isTTY) {
@@ -25553,18 +25588,18 @@ var defaultPrompt = (question, defaultYes) => {
25553
25588
  );
25554
25589
  return Promise.resolve(defaultYes);
25555
25590
  }
25556
- return new Promise((resolve11) => {
25591
+ return new Promise((resolve12) => {
25557
25592
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
25558
25593
  const suffix = defaultYes ? " [Y/n]: " : " [y/N]: ";
25559
25594
  rl.question(`${question}${suffix}`, (answer) => {
25560
25595
  rl.close();
25561
25596
  const t = answer.trim().toLowerCase();
25562
- if (t === "") resolve11(defaultYes);
25563
- else if (t === "y" || t === "yes") resolve11(true);
25564
- else if (t === "n" || t === "no") resolve11(false);
25565
- else resolve11(defaultYes);
25597
+ if (t === "") resolve12(defaultYes);
25598
+ else if (t === "y" || t === "yes") resolve12(true);
25599
+ else if (t === "n" || t === "no") resolve12(false);
25600
+ else resolve12(defaultYes);
25566
25601
  });
25567
- rl.on("close", () => resolve11(defaultYes));
25602
+ rl.on("close", () => resolve12(defaultYes));
25568
25603
  });
25569
25604
  };
25570
25605
  async function phase1SystemCheck(deps) {
@@ -26531,6 +26566,157 @@ function registerWorldUpgrade(program2) {
26531
26566
  });
26532
26567
  }
26533
26568
 
26569
+ // src/commands/mcp/serve.ts
26570
+ init_output();
26571
+ import { existsSync as existsSync50 } from "node:fs";
26572
+ import { dirname as dirname25, resolve as resolve10 } from "node:path";
26573
+ import { fileURLToPath as fileURLToPath6 } from "node:url";
26574
+ var here = dirname25(fileURLToPath6(import.meta.url));
26575
+ var BUNDLE_PATH = resolve10(here, "..", "..", "mcp-server.js");
26576
+ var MISSING_BUNDLE_REMEDY = "olam mcp server bundle missing at dist/mcp-server.js. For local dev, run: node packages/cli/scripts/bundle-mcp-server.mjs. Published @pleri/olam-cli tarballs always include this bundle (per prepublishOnly).";
26577
+ function registerMcpServe(cmd) {
26578
+ cmd.command("serve").description(
26579
+ "Run the olam MCP server (stdio transport). Claude Code wires this via `claude mcp add olam --scope user -- npx -y @pleri/olam-cli mcp serve`."
26580
+ ).action(async () => {
26581
+ if (!existsSync50(BUNDLE_PATH)) {
26582
+ printError(MISSING_BUNDLE_REMEDY);
26583
+ process.exitCode = 1;
26584
+ return;
26585
+ }
26586
+ await import(BUNDLE_PATH);
26587
+ });
26588
+ }
26589
+
26590
+ // src/commands/mcp/install.ts
26591
+ init_output();
26592
+
26593
+ // src/commands/mcp/install-shared.ts
26594
+ import { spawnSync as spawnSync15 } from "node:child_process";
26595
+ var DEFAULT_CLAUDE_SHELL_DEPS = {
26596
+ spawn: spawnSync15,
26597
+ log: (msg) => console.log(msg)
26598
+ };
26599
+ function isOnPath(deps, bin) {
26600
+ const probe = process.platform === "win32" ? "where" : "which";
26601
+ const result = deps.spawn(probe, [bin], {
26602
+ encoding: "utf8",
26603
+ stdio: ["ignore", "pipe", "ignore"]
26604
+ });
26605
+ return result.status === 0;
26606
+ }
26607
+ function normaliseScope(raw) {
26608
+ const value = (raw ?? "user").toLowerCase();
26609
+ return value === "user" || value === "project" || value === "local" ? value : null;
26610
+ }
26611
+ var REMEDY_CLAUDE_MISSING = "`claude` not found on PATH. Install Claude Code (https://docs.claude.com/claude-code) or paste the JSON snippet from docs/architecture/mcp-as-npx-served.md.";
26612
+
26613
+ // src/commands/mcp/install.ts
26614
+ var NPM_PACKAGE_NAME = "@pleri/olam-cli";
26615
+ function buildClaudeMcpAddArgs(scope, useGlobal) {
26616
+ const target = useGlobal ? { cmd: "olam", args: ["mcp", "serve"] } : { cmd: "npx", args: ["-y", NPM_PACKAGE_NAME, "mcp", "serve"] };
26617
+ return {
26618
+ command: "claude",
26619
+ args: ["mcp", "add", "olam", "--scope", scope, "--", target.cmd, ...target.args]
26620
+ };
26621
+ }
26622
+ async function runInstall(opts, deps = DEFAULT_CLAUDE_SHELL_DEPS) {
26623
+ if (!isOnPath(deps, "claude")) {
26624
+ printError(REMEDY_CLAUDE_MISSING);
26625
+ return 1;
26626
+ }
26627
+ const useGlobal = isOnPath(deps, "olam");
26628
+ const { command, args } = buildClaudeMcpAddArgs(opts.scope, useGlobal);
26629
+ deps.log(`Wiring: ${command} ${args.join(" ")}`);
26630
+ const result = deps.spawn(command, args, {
26631
+ encoding: "utf8",
26632
+ stdio: ["ignore", "pipe", "pipe"]
26633
+ });
26634
+ if (result.status !== 0) {
26635
+ const detail = result.stderr?.trim() || result.stdout?.trim() || "(no output)";
26636
+ printError(`claude mcp add failed (rc=${result.status ?? "unknown"}): ${detail}`);
26637
+ return result.status ?? 1;
26638
+ }
26639
+ printSuccess(
26640
+ `olam MCP registered with claude (--scope ${opts.scope}; command: ${useGlobal ? "olam" : "npx"}).`
26641
+ );
26642
+ return 0;
26643
+ }
26644
+ function registerMcpInstall(cmd) {
26645
+ cmd.command("install").description(
26646
+ "Add olam to Claude Code as an MCP server (auto-detects whether `olam` is on PATH; shells to `claude mcp add`)"
26647
+ ).option("--scope <scope>", "claude mcp scope: user | project | local", "user").action(async (rawOpts) => {
26648
+ const scope = normaliseScope(rawOpts.scope);
26649
+ if (scope === null) {
26650
+ printError(`--scope must be one of: user, project, local (got: ${rawOpts.scope})`);
26651
+ process.exitCode = 1;
26652
+ return;
26653
+ }
26654
+ const rc = await runInstall({ scope });
26655
+ if (rc !== 0) {
26656
+ process.exitCode = rc;
26657
+ }
26658
+ });
26659
+ }
26660
+
26661
+ // src/commands/mcp/uninstall.ts
26662
+ init_output();
26663
+ var NOT_INSTALLED_PATTERN = /no\s+(?:\S+\s+)*mcp\s+server\s+found/i;
26664
+ function looksLikeNotInstalled(text) {
26665
+ return NOT_INSTALLED_PATTERN.test(text);
26666
+ }
26667
+ function buildClaudeMcpRemoveArgs(scope) {
26668
+ return {
26669
+ command: "claude",
26670
+ args: ["mcp", "remove", "olam", "--scope", scope]
26671
+ };
26672
+ }
26673
+ async function runUninstall(opts, deps = DEFAULT_CLAUDE_SHELL_DEPS) {
26674
+ if (!isOnPath(deps, "claude")) {
26675
+ printError(REMEDY_CLAUDE_MISSING);
26676
+ return 1;
26677
+ }
26678
+ const { command, args } = buildClaudeMcpRemoveArgs(opts.scope);
26679
+ deps.log(`Unwiring: ${command} ${args.join(" ")}`);
26680
+ const result = deps.spawn(command, args, {
26681
+ encoding: "utf8",
26682
+ stdio: ["ignore", "pipe", "pipe"]
26683
+ });
26684
+ if (result.status === 0) {
26685
+ printSuccess(`olam MCP removed from claude (--scope ${opts.scope}).`);
26686
+ return 0;
26687
+ }
26688
+ const stderr = result.stderr?.trim() ?? "";
26689
+ const stdout = result.stdout?.trim() ?? "";
26690
+ const combined = `${stderr}
26691
+ ${stdout}`.trim();
26692
+ if (looksLikeNotInstalled(combined)) {
26693
+ printWarning(
26694
+ `olam MCP not registered with claude (--scope ${opts.scope}); nothing to remove.`
26695
+ );
26696
+ return 0;
26697
+ }
26698
+ printError(
26699
+ `claude mcp remove failed (rc=${result.status ?? "unknown"}): ${combined || "(no output)"}`
26700
+ );
26701
+ return result.status ?? 1;
26702
+ }
26703
+ function registerMcpUninstall(cmd) {
26704
+ cmd.command("uninstall").description(
26705
+ "Remove olam from Claude Code (symmetric with `install`; idempotent \u2014 exits 0 when olam is not registered)"
26706
+ ).option("--scope <scope>", "claude mcp scope: user | project | local", "user").action(async (rawOpts) => {
26707
+ const scope = normaliseScope(rawOpts.scope);
26708
+ if (scope === null) {
26709
+ printError(`--scope must be one of: user, project, local (got: ${rawOpts.scope})`);
26710
+ process.exitCode = 1;
26711
+ return;
26712
+ }
26713
+ const rc = await runUninstall({ scope });
26714
+ if (rc !== 0) {
26715
+ process.exitCode = rc;
26716
+ }
26717
+ });
26718
+ }
26719
+
26534
26720
  // src/commands/mcp/login.ts
26535
26721
  init_output();
26536
26722
 
@@ -26628,7 +26814,7 @@ function registerMcpLogin(cmd) {
26628
26814
  init_output();
26629
26815
  import * as readline2 from "node:readline";
26630
26816
  async function readTokenSilent(prompt) {
26631
- return new Promise((resolve11, reject) => {
26817
+ return new Promise((resolve12, reject) => {
26632
26818
  const rl = readline2.createInterface({
26633
26819
  input: process.stdin,
26634
26820
  output: process.stdout,
@@ -26646,7 +26832,7 @@ async function readTokenSilent(prompt) {
26646
26832
  process.stdin.removeListener("data", onData);
26647
26833
  process.stdout.write("\n");
26648
26834
  rl.close();
26649
- resolve11(token);
26835
+ resolve12(token);
26650
26836
  } else if (char === "") {
26651
26837
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
26652
26838
  process.stdin.removeListener("data", onData);
@@ -26921,7 +27107,7 @@ async function discoverMcpSources(repoPaths) {
26921
27107
  import { spawn as spawn7 } from "node:child_process";
26922
27108
  var VALIDATION_TIMEOUT_MS = 5e3;
26923
27109
  async function validateMcpEntry(entry) {
26924
- return new Promise((resolve11) => {
27110
+ return new Promise((resolve12) => {
26925
27111
  let stdout = "";
26926
27112
  let timedOut = false;
26927
27113
  let child;
@@ -26931,7 +27117,7 @@ async function validateMcpEntry(entry) {
26931
27117
  env: { ...process.env, ...entry.env ?? {} }
26932
27118
  });
26933
27119
  } catch (err) {
26934
- resolve11({
27120
+ resolve12({
26935
27121
  name: entry.name,
26936
27122
  validated: false,
26937
27123
  reason: err instanceof Error ? err.message : "spawn failed"
@@ -26948,11 +27134,11 @@ async function validateMcpEntry(entry) {
26948
27134
  child.on("close", (code) => {
26949
27135
  clearTimeout(timer);
26950
27136
  if (timedOut) {
26951
- resolve11({ name: entry.name, validated: false, reason: "timeout (5s)" });
27137
+ resolve12({ name: entry.name, validated: false, reason: "timeout (5s)" });
26952
27138
  return;
26953
27139
  }
26954
27140
  const validated = code === 0 && stdout.trim().length > 0;
26955
- resolve11({
27141
+ resolve12({
26956
27142
  name: entry.name,
26957
27143
  validated,
26958
27144
  reason: validated ? "ok" : `exit code ${code ?? "null"}`
@@ -26960,7 +27146,7 @@ async function validateMcpEntry(entry) {
26960
27146
  });
26961
27147
  child.on("error", (err) => {
26962
27148
  clearTimeout(timer);
26963
- resolve11({ name: entry.name, validated: false, reason: err.message });
27149
+ resolve12({ name: entry.name, validated: false, reason: err.message });
26964
27150
  });
26965
27151
  });
26966
27152
  }
@@ -26975,11 +27161,11 @@ async function multiSelectPicker(entries) {
26975
27161
  );
26976
27162
  });
26977
27163
  console.log("\n" + pc29.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
26978
- const answer = await new Promise((resolve11) => {
27164
+ const answer = await new Promise((resolve12) => {
26979
27165
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
26980
27166
  rl.question("> ", (ans) => {
26981
27167
  rl.close();
26982
- resolve11(ans.trim());
27168
+ resolve12(ans.trim());
26983
27169
  });
26984
27170
  });
26985
27171
  if (!answer || answer === "") return [];
@@ -27107,7 +27293,10 @@ function registerMcpRevoke(cmd) {
27107
27293
 
27108
27294
  // src/commands/mcp/index.ts
27109
27295
  function registerMcp(program2) {
27110
- const mcp = program2.command("mcp").description("Manage MCP server credentials (login, add, list, remove, import, revoke)");
27296
+ const mcp = program2.command("mcp").description("Run the olam MCP server, wire it into Claude Code, or manage MCP credentials (serve, install, uninstall, login, add, list, remove, import, revoke)");
27297
+ registerMcpServe(mcp);
27298
+ registerMcpInstall(mcp);
27299
+ registerMcpUninstall(mcp);
27111
27300
  registerMcpLogin(mcp);
27112
27301
  registerMcpAdd(mcp);
27113
27302
  registerMcpList(mcp);
@@ -27118,13 +27307,13 @@ function registerMcp(program2) {
27118
27307
  }
27119
27308
 
27120
27309
  // src/commands/kg-build.ts
27121
- import { spawnSync as spawnSync15 } from "node:child_process";
27310
+ import { spawnSync as spawnSync16 } from "node:child_process";
27122
27311
  import fs48 from "node:fs";
27123
27312
  import path53 from "node:path";
27124
27313
 
27125
27314
  // ../core/dist/kg/storage-paths.js
27126
27315
  import { homedir as homedir28 } from "node:os";
27127
- import { join as join48, resolve as resolve10 } from "node:path";
27316
+ import { join as join48, resolve as resolve11 } from "node:path";
27128
27317
 
27129
27318
  // ../core/dist/world/workspace-name.js
27130
27319
  var InvalidWorkspaceNameError = class extends Error {
@@ -27161,7 +27350,7 @@ function assertWithinPrefix(path55, prefix, label) {
27161
27350
  function kgPristinePath(workspace) {
27162
27351
  validateWorkspaceName(workspace);
27163
27352
  const root = kgRoot();
27164
- const path55 = resolve10(join48(root, workspace));
27353
+ const path55 = resolve11(join48(root, workspace));
27165
27354
  assertWithinPrefix(path55, root, "kgPristinePath");
27166
27355
  return path55;
27167
27356
  }
@@ -27532,16 +27721,16 @@ async function runKgWatch(workspaceArg, opts, deps = {}) {
27532
27721
  process.on("SIGINT", () => forward("SIGINT"));
27533
27722
  process.on("SIGTERM", () => forward("SIGTERM"));
27534
27723
  }
27535
- return new Promise((resolve11) => {
27724
+ return new Promise((resolve12) => {
27536
27725
  child.on("exit", (code, signal) => {
27537
27726
  removePidFile(name);
27538
27727
  const exitCode = typeof code === "number" ? code : signal === "SIGINT" || signal === "SIGTERM" ? 0 : 1;
27539
- resolve11({ exitCode, pidWritten: true });
27728
+ resolve12({ exitCode, pidWritten: true });
27540
27729
  });
27541
27730
  child.on("error", (err) => {
27542
27731
  removePidFile(name);
27543
27732
  printError(`graphify subprocess error: ${err.message}`);
27544
- resolve11({ exitCode: 1, pidWritten: true });
27733
+ resolve12({ exitCode: 1, pidWritten: true });
27545
27734
  });
27546
27735
  });
27547
27736
  }
@@ -27564,13 +27753,13 @@ function resolveWorkspace(arg) {
27564
27753
  }
27565
27754
  function copyWorkspaceToScratch(source, scratch) {
27566
27755
  if (process.platform === "darwin") {
27567
- const r2 = spawnSync15("cp", ["-c", "-r", source + "/.", scratch], {
27756
+ const r2 = spawnSync16("cp", ["-c", "-r", source + "/.", scratch], {
27568
27757
  stdio: ["ignore", "ignore", "pipe"],
27569
27758
  encoding: "utf-8"
27570
27759
  });
27571
27760
  if (r2.status === 0) return "cp-c-r-reflink";
27572
27761
  }
27573
- const r = spawnSync15("cp", ["-r", source + "/.", scratch], {
27762
+ const r = spawnSync16("cp", ["-r", source + "/.", scratch], {
27574
27763
  stdio: ["ignore", "ignore", "pipe"],
27575
27764
  encoding: "utf-8"
27576
27765
  });
@@ -27591,7 +27780,7 @@ function parseNodeCount(graphifyOutDir) {
27591
27780
  }
27592
27781
  }
27593
27782
  function readGraphifyVersion(image) {
27594
- const r = spawnSync15(
27783
+ const r = spawnSync16(
27595
27784
  "docker",
27596
27785
  [
27597
27786
  "run",
@@ -27643,7 +27832,7 @@ async function runKgBuild(workspaceArg, options = {}) {
27643
27832
  "update",
27644
27833
  "."
27645
27834
  ];
27646
- const r = human ? spawnSync15("docker", dockerArgs, { stdio: "inherit" }) : spawnSync15("docker", dockerArgs, { stdio: ["ignore", "ignore", "pipe"] });
27835
+ const r = human ? spawnSync16("docker", dockerArgs, { stdio: "inherit" }) : spawnSync16("docker", dockerArgs, { stdio: ["ignore", "ignore", "pipe"] });
27647
27836
  if (r.status !== 0) {
27648
27837
  printError(`graphify update failed (exit ${r.status})`);
27649
27838
  return { exitCode: r.status ?? 1 };
@@ -27720,7 +27909,19 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
27720
27909
 
27721
27910
  // src/index.ts
27722
27911
  var program = new Command();
27723
- program.name("olam").description("Olam \u2014 isolated development worlds with thought graph capture").version(readCliVersion());
27912
+ program.name("olam").description("Olam \u2014 isolated development worlds with thought graph capture").option("--mcp", "Run the olam MCP server (stdio); alias for `olam mcp serve`").version(readCliVersion());
27913
+ var mcpFlagIndex = process.argv.indexOf("--mcp");
27914
+ if (mcpFlagIndex !== -1) {
27915
+ const afterFlag = process.argv.slice(mcpFlagIndex + 1).filter((a) => !a.startsWith("-"));
27916
+ if (afterFlag.length > 0) {
27917
+ process.stderr.write(
27918
+ `error: --mcp does not accept positional arguments (got ${JSON.stringify(afterFlag)}). Use 'olam mcp serve' directly if you need subcommand-level flags.
27919
+ `
27920
+ );
27921
+ process.exit(1);
27922
+ }
27923
+ process.argv.splice(mcpFlagIndex, 1, "mcp", "serve");
27924
+ }
27724
27925
  registerInit(program);
27725
27926
  registerInstall(program);
27726
27927
  registerAuth(program);