@pleri/olam-cli 0.1.110 → 0.1.112

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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * shell-rc.ts — Idempotent shell-rc file append + timestamped backup.
3
+ *
4
+ * Phase C1 of olam-operator-onboarding-parity (plan
5
+ * ~/.claude/plans/olam-operator-onboarding-parity.md).
6
+ *
7
+ * Phase C's `olam setup` wizard offers to add
8
+ * eval "$(olam completion zsh)"
9
+ * to the operator's `~/.zshrc` / `~/.bashrc`. This module is the safety
10
+ * layer that:
11
+ *
12
+ * 1. Grep-checks a marker substring before writing — re-running the
13
+ * wizard is a no-op (no duplicate eval lines, no n-times appended).
14
+ * 2. Backs the file up to `<rcPath>.olam-bak.<ISO-timestamp>` before
15
+ * ANY mutation — recoverable if the operator decides they didn't
16
+ * want the line.
17
+ * 3. Writes atomically via tmp-file + rename — `kill -9` mid-write
18
+ * can't leave a half-written rc file.
19
+ *
20
+ * Decision 6 (plan): mirrors adb's `phase_mise_activation` pattern; the
21
+ * idempotency + backup pair is the operator-UX floor.
22
+ *
23
+ * Risk T4 mitigation (per plan + design doc): the grep-for-marker check
24
+ * uses `String.includes` not a per-line scan — a marker can match across
25
+ * whitespace if needed. The backup runs unconditionally before the
26
+ * append; corrupted rc states are recoverable via `mv <backup> <rcPath>`.
27
+ */
28
+ export type AppendStatus = 'appended' | 'already-present' | 'no-rc-file';
29
+ export interface AppendResult {
30
+ readonly status: AppendStatus;
31
+ /** Path to the timestamped backup file. `null` when `status !== 'appended'`. */
32
+ readonly backupPath: string | null;
33
+ }
34
+ export interface AppendOptions {
35
+ /** Absolute path to the operator's shell rc (e.g. `/Users/me/.zshrc`). */
36
+ readonly rcPath: string;
37
+ /**
38
+ * Substring searched for in the rc to detect "already present."
39
+ * Convention: a short stable token (e.g. `olam completion`) — distinct
40
+ * enough to avoid collisions with unrelated mentions, short enough that
41
+ * the operator can edit the exact line shape without breaking
42
+ * idempotency.
43
+ */
44
+ readonly marker: string;
45
+ /**
46
+ * The exact line to append. The function adds a trailing newline if
47
+ * not already present + ensures the previous line of the rc file ends
48
+ * with a newline (so the appended line isn't concatenated onto the
49
+ * previous line on rcs that don't end with `\n`).
50
+ */
51
+ readonly contentLine: string;
52
+ /**
53
+ * Optional clock override for deterministic test timestamps. Defaults
54
+ * to `() => new Date()`.
55
+ */
56
+ readonly clock?: () => Date;
57
+ }
58
+ /**
59
+ * Append `contentLine` to `rcPath` exactly once.
60
+ *
61
+ * - rcPath doesn't exist → returns `{status: 'no-rc-file', backupPath: null}`
62
+ * - rcPath content already includes the marker substring
63
+ * → returns `{status: 'already-present', backupPath: null}`
64
+ * - otherwise: backup + append
65
+ * → returns `{status: 'appended', backupPath: <ts-path>}`
66
+ *
67
+ * The write is atomic: content is staged to `<rcPath>.olam-tmp.<pid>`
68
+ * and then `renameSync`'d to `rcPath`. POSIX rename is atomic within a
69
+ * filesystem; a `kill -9` mid-call leaves either the backup-only state
70
+ * (no rc change) or the renamed-final state (atomic apply), never a
71
+ * half-written rc.
72
+ *
73
+ * The backup path uses a filesystem-safe ISO timestamp with `:` + `.`
74
+ * replaced by `-` (avoids colons in filenames on Windows for cross-
75
+ * platform consumers; not strictly required on macOS / Linux but
76
+ * cosmetically nicer).
77
+ */
78
+ export declare function appendIdempotent(opts: AppendOptions): AppendResult;
79
+ /**
80
+ * Resolve the operator's shell rc path based on `$SHELL`.
81
+ *
82
+ * /bin/zsh, /usr/bin/zsh, /opt/homebrew/bin/zsh → ~/.zshrc
83
+ * /bin/bash, /usr/bin/bash → ~/.bashrc
84
+ * anything else → null (caller must surface)
85
+ *
86
+ * The caller decides what to do on null — typically Phase C surfaces a
87
+ * remedy message naming the supported shells.
88
+ */
89
+ export declare function resolveShellRc(home: string, shellEnv: string | undefined): string | null;
90
+ //# sourceMappingURL=shell-rc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-rc.d.ts","sourceRoot":"","sources":["../../src/lib/shell-rc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAEzE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CA0BlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMxF"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * shell-rc.ts — Idempotent shell-rc file append + timestamped backup.
3
+ *
4
+ * Phase C1 of olam-operator-onboarding-parity (plan
5
+ * ~/.claude/plans/olam-operator-onboarding-parity.md).
6
+ *
7
+ * Phase C's `olam setup` wizard offers to add
8
+ * eval "$(olam completion zsh)"
9
+ * to the operator's `~/.zshrc` / `~/.bashrc`. This module is the safety
10
+ * layer that:
11
+ *
12
+ * 1. Grep-checks a marker substring before writing — re-running the
13
+ * wizard is a no-op (no duplicate eval lines, no n-times appended).
14
+ * 2. Backs the file up to `<rcPath>.olam-bak.<ISO-timestamp>` before
15
+ * ANY mutation — recoverable if the operator decides they didn't
16
+ * want the line.
17
+ * 3. Writes atomically via tmp-file + rename — `kill -9` mid-write
18
+ * can't leave a half-written rc file.
19
+ *
20
+ * Decision 6 (plan): mirrors adb's `phase_mise_activation` pattern; the
21
+ * idempotency + backup pair is the operator-UX floor.
22
+ *
23
+ * Risk T4 mitigation (per plan + design doc): the grep-for-marker check
24
+ * uses `String.includes` not a per-line scan — a marker can match across
25
+ * whitespace if needed. The backup runs unconditionally before the
26
+ * append; corrupted rc states are recoverable via `mv <backup> <rcPath>`.
27
+ */
28
+ import { copyFileSync, existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
29
+ import path from 'node:path';
30
+ /**
31
+ * Append `contentLine` to `rcPath` exactly once.
32
+ *
33
+ * - rcPath doesn't exist → returns `{status: 'no-rc-file', backupPath: null}`
34
+ * - rcPath content already includes the marker substring
35
+ * → returns `{status: 'already-present', backupPath: null}`
36
+ * - otherwise: backup + append
37
+ * → returns `{status: 'appended', backupPath: <ts-path>}`
38
+ *
39
+ * The write is atomic: content is staged to `<rcPath>.olam-tmp.<pid>`
40
+ * and then `renameSync`'d to `rcPath`. POSIX rename is atomic within a
41
+ * filesystem; a `kill -9` mid-call leaves either the backup-only state
42
+ * (no rc change) or the renamed-final state (atomic apply), never a
43
+ * half-written rc.
44
+ *
45
+ * The backup path uses a filesystem-safe ISO timestamp with `:` + `.`
46
+ * replaced by `-` (avoids colons in filenames on Windows for cross-
47
+ * platform consumers; not strictly required on macOS / Linux but
48
+ * cosmetically nicer).
49
+ */
50
+ export function appendIdempotent(opts) {
51
+ const { rcPath, marker, contentLine, clock = () => new Date() } = opts;
52
+ if (!existsSync(rcPath)) {
53
+ return { status: 'no-rc-file', backupPath: null };
54
+ }
55
+ const content = readFileSync(rcPath, 'utf-8');
56
+ if (content.includes(marker)) {
57
+ return { status: 'already-present', backupPath: null };
58
+ }
59
+ const timestamp = clock().toISOString().replace(/[:.]/g, '-');
60
+ const backupPath = `${rcPath}.olam-bak.${timestamp}`;
61
+ copyFileSync(rcPath, backupPath);
62
+ const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
63
+ const trailing = contentLine.endsWith('\n') ? '' : '\n';
64
+ const nextContent = `${content}${separator}${contentLine}${trailing}`;
65
+ // Atomic write: tmp + rename. POSIX rename is atomic within filesystem.
66
+ const tmpPath = `${rcPath}.olam-tmp.${process.pid}`;
67
+ writeFileSync(tmpPath, nextContent, { encoding: 'utf-8' });
68
+ renameSync(tmpPath, rcPath);
69
+ return { status: 'appended', backupPath };
70
+ }
71
+ /**
72
+ * Resolve the operator's shell rc path based on `$SHELL`.
73
+ *
74
+ * /bin/zsh, /usr/bin/zsh, /opt/homebrew/bin/zsh → ~/.zshrc
75
+ * /bin/bash, /usr/bin/bash → ~/.bashrc
76
+ * anything else → null (caller must surface)
77
+ *
78
+ * The caller decides what to do on null — typically Phase C surfaces a
79
+ * remedy message naming the supported shells.
80
+ */
81
+ export function resolveShellRc(home, shellEnv) {
82
+ if (!shellEnv)
83
+ return null;
84
+ const basename = path.basename(shellEnv);
85
+ if (basename === 'zsh')
86
+ return path.join(home, '.zshrc');
87
+ if (basename === 'bash')
88
+ return path.join(home, '.bashrc');
89
+ return null;
90
+ }
91
+ //# sourceMappingURL=shell-rc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-rc.js","sourceRoot":"","sources":["../../src/lib/shell-rc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,IAAI,MAAM,WAAW,CAAC;AAmC7B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAmB;IAClD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC;IAEvE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,GAAG,MAAM,aAAa,SAAS,EAAE,CAAC;IACrD,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAEjC,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACxD,MAAM,WAAW,GAAG,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;IAEtE,wEAAwE;IACxE,MAAM,OAAO,GAAG,GAAG,MAAM,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,QAA4B;IACvE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -22986,6 +22986,92 @@ var removeNetwork = async (docker, worldId) => {
22986
22986
  }
22987
22987
  };
22988
22988
 
22989
+ // ../adapters/dist/docker/pull.js
22990
+ import { spawn } from "node:child_process";
22991
+ var realDocker = {
22992
+ async info() {
22993
+ return spawnAsync("docker", ["info"]);
22994
+ },
22995
+ async pull(imageRef, opts) {
22996
+ const args = ["pull"];
22997
+ if (opts?.platform)
22998
+ args.push(`--platform=${opts.platform}`);
22999
+ args.push(imageRef);
23000
+ return spawnAsync("docker", args, { signal: opts?.signal });
23001
+ },
23002
+ async inspectLabel(imageRef, label) {
23003
+ return spawnAsync("docker", [
23004
+ "inspect",
23005
+ imageRef,
23006
+ "--format",
23007
+ `{{ index .Config.Labels "${label}" }}`
23008
+ ]);
23009
+ },
23010
+ async tag(source, target) {
23011
+ return spawnAsync("docker", ["tag", source, target]);
23012
+ }
23013
+ };
23014
+ function spawnAsync(cmd, args, opts = {}) {
23015
+ return new Promise((resolve8) => {
23016
+ const child = spawn(cmd, [...args], {
23017
+ stdio: ["ignore", "pipe", "pipe"],
23018
+ signal: opts.signal
23019
+ });
23020
+ let stdout = "";
23021
+ let stderr = "";
23022
+ child.stdout?.on("data", (chunk) => {
23023
+ stdout += chunk.toString();
23024
+ });
23025
+ child.stderr?.on("data", (chunk) => {
23026
+ stderr += chunk.toString();
23027
+ });
23028
+ child.on("error", (err) => {
23029
+ resolve8({ exitCode: -1, stdout, stderr: stderr + err.message });
23030
+ });
23031
+ child.on("close", (code) => {
23032
+ resolve8({ exitCode: code ?? -1, stdout, stderr });
23033
+ });
23034
+ });
23035
+ }
23036
+ var inflightPulls = /* @__PURE__ */ new Map();
23037
+ var DEFAULT_PULL_POLICY = {
23038
+ perAttemptTimeoutMs: 18e4,
23039
+ retryOnce: true
23040
+ };
23041
+ function isTransientPullFailure(result) {
23042
+ const s = result.stderr.toLowerCase();
23043
+ return result.exitCode !== 0 && (/timeout|connection|temporarily|429|503|tls handshake|network/.test(s) || result.exitCode === 124 || result.exitCode === -1);
23044
+ }
23045
+ async function pullImageWithRetry(imageRef, docker = realDocker, policy = DEFAULT_PULL_POLICY) {
23046
+ const key = policy.platform ? `${imageRef}@@${policy.platform}` : imageRef;
23047
+ const existing = inflightPulls.get(key);
23048
+ if (existing)
23049
+ return existing;
23050
+ const promise = (async () => {
23051
+ let result = await pullOnce(imageRef, docker, policy.perAttemptTimeoutMs, policy.platform);
23052
+ if (result.exitCode !== 0 && policy.retryOnce && isTransientPullFailure(result)) {
23053
+ result = await pullOnce(imageRef, docker, policy.perAttemptTimeoutMs, policy.platform);
23054
+ }
23055
+ return result;
23056
+ })();
23057
+ inflightPulls.set(key, promise);
23058
+ try {
23059
+ return await promise;
23060
+ } finally {
23061
+ inflightPulls.delete(key);
23062
+ }
23063
+ }
23064
+ async function pullOnce(imageRef, docker, timeoutMs, platform) {
23065
+ const ac = new AbortController();
23066
+ const timer = setTimeout(() => ac.abort(), timeoutMs).unref?.();
23067
+ try {
23068
+ return await docker.pull(imageRef, { signal: ac.signal, ...platform ? { platform } : {} });
23069
+ } finally {
23070
+ if (timer)
23071
+ clearTimeout(timer);
23072
+ }
23073
+ }
23074
+
22989
23075
  // ../adapters/dist/docker/volume.js
22990
23076
  var volumeName = (worldId, serviceName) => `olam-${worldId}-${serviceName}-data`;
22991
23077
  var createVolume = async (docker, worldId, worldName, serviceName) => {
@@ -22998,6 +23084,7 @@ var createVolume = async (docker, worldId, worldName, serviceName) => {
22998
23084
  };
22999
23085
 
23000
23086
  // ../adapters/dist/docker/container.js
23087
+ var realPullImage = (imageRef, platform) => pullImageWithRetry(imageRef, void 0, platform ? { perAttemptTimeoutMs: 18e4, retryOnce: true, platform } : { perAttemptTimeoutMs: 18e4, retryOnce: true });
23001
23088
  function readAuthSecret() {
23002
23089
  const fromEnv = process.env["OLAM_AUTH_SECRET"];
23003
23090
  if (fromEnv && fromEnv.length > 0)
@@ -23048,10 +23135,15 @@ function buildPackageManagerCacheBinds(homeDir) {
23048
23135
  }
23049
23136
  return result;
23050
23137
  }
23051
- var createServiceContainer = async (docker, worldId, worldName, service, portOffset) => {
23138
+ var createServiceContainer = async (docker, worldId, worldName, service, portOffset, pullImage = realPullImage) => {
23052
23139
  const labels = olamLabels(worldId, worldName);
23053
23140
  const hostPort = service.port + portOffset;
23054
23141
  const envList = service.environment ? Object.entries(service.environment).map(([k, v]) => `${k}=${v}`) : [];
23142
+ const pullResult = await pullImage(service.image, service.platform);
23143
+ if (pullResult.exitCode !== 0) {
23144
+ const reason = pullResult.stderr.trim() || `exit ${pullResult.exitCode}`;
23145
+ throw new Error(`failed to pull ${service.image}${service.platform ? ` (platform=${service.platform})` : ""}: ${reason}`);
23146
+ }
23055
23147
  const container = await docker.createContainer({
23056
23148
  name: serviceContainerName(worldId, service.name),
23057
23149
  Image: service.image,
@@ -23413,10 +23505,18 @@ var DockerProvider = class extends ComputeProvider {
23413
23505
  };
23414
23506
  docker;
23415
23507
  dockerOptions;
23416
- constructor(dockerOptions) {
23508
+ /**
23509
+ * Injectable image-pull helper. Defaults to the real
23510
+ * `pullImageWithRetry` (docker CLI shell-out). Tests override with a
23511
+ * no-op so they don't need a real Docker daemon to exercise the
23512
+ * dockerode-mocked code paths.
23513
+ */
23514
+ pullImage;
23515
+ constructor(dockerOptions, opts) {
23417
23516
  super();
23418
23517
  this.docker = new Dockerode(dockerOptions);
23419
23518
  this.dockerOptions = dockerOptions;
23519
+ this.pullImage = opts?.pullImage;
23420
23520
  }
23421
23521
  // -----------------------------------------------------------------------
23422
23522
  // createWorld
@@ -23435,7 +23535,7 @@ var DockerProvider = class extends ComputeProvider {
23435
23535
  await createVolume(this.docker, id, name, svc.name);
23436
23536
  }
23437
23537
  for (const svc of services) {
23438
- await createServiceContainer(this.docker, id, name, svc, portOffset);
23538
+ await createServiceContainer(this.docker, id, name, svc, portOffset, this.pullImage);
23439
23539
  }
23440
23540
  const SERVICE_ENV_ALIASES = {
23441
23541
  postgres: "POSTGRESQL",
@@ -30609,7 +30709,7 @@ import * as crypto4 from "node:crypto";
30609
30709
  import * as fs17 from "node:fs";
30610
30710
  import * as os11 from "node:os";
30611
30711
  import * as path19 from "node:path";
30612
- import { execFileSync as execFileSync4, spawn } from "node:child_process";
30712
+ import { execFileSync as execFileSync4, spawn as spawn2 } from "node:child_process";
30613
30713
  import { gunzipSync } from "node:zlib";
30614
30714
  function snapshotsDir() {
30615
30715
  return process.env["OLAM_SNAPSHOTS_DIR"] ?? path19.join(os11.homedir(), ".olam", "snapshots");
@@ -30972,7 +31072,7 @@ function spawnAutoCapture(worldId, olamBin = "olam") {
30972
31072
  if (!/^[a-zA-Z0-9_\-.]+$/.test(worldId))
30973
31073
  return null;
30974
31074
  try {
30975
- const child = spawn(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
31075
+ const child = spawn2(olamBin, ["world", "snapshot", "create", worldId, "--kind", "all"], {
30976
31076
  detached: true,
30977
31077
  stdio: "ignore",
30978
31078
  // OLAM_INTERNAL_SNAPSHOT=1 sentinel — Phase D D1's deprecation
@@ -33678,7 +33778,7 @@ function isDashboardRunning() {
33678
33778
  }
33679
33779
 
33680
33780
  // ../core/dist/dashboard/tunnel.js
33681
- import { spawn as spawn2, execSync as execSync6 } from "node:child_process";
33781
+ import { spawn as spawn3, execSync as execSync6 } from "node:child_process";
33682
33782
  var tunnelProcess = null;
33683
33783
  function isCloudflaredAvailable() {
33684
33784
  try {
@@ -33690,7 +33790,7 @@ function isCloudflaredAvailable() {
33690
33790
  }
33691
33791
  function startTunnel(port) {
33692
33792
  return new Promise((resolve8, reject2) => {
33693
- const child = spawn2("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
33793
+ const child = spawn3("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
33694
33794
  stdio: ["ignore", "pipe", "pipe"],
33695
33795
  detached: false
33696
33796
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pleri/olam-cli",
3
- "version": "0.1.110",
3
+ "version": "0.1.112",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "olam": "./bin/olam.cjs"