@pleri/olam-cli 0.1.13 → 0.1.19

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.
Files changed (37) hide show
  1. package/dist/__tests__/auth-upgrade.test.js +236 -1
  2. package/dist/__tests__/auth-upgrade.test.js.map +1 -1
  3. package/dist/__tests__/install-root.test.d.ts +2 -0
  4. package/dist/__tests__/install-root.test.d.ts.map +1 -0
  5. package/dist/__tests__/install-root.test.js +119 -0
  6. package/dist/__tests__/install-root.test.js.map +1 -0
  7. package/dist/__tests__/upgrade.test.js +292 -2
  8. package/dist/__tests__/upgrade.test.js.map +1 -1
  9. package/dist/commands/__tests__/bootstrap.test.d.ts +2 -0
  10. package/dist/commands/__tests__/bootstrap.test.d.ts.map +1 -0
  11. package/dist/commands/__tests__/bootstrap.test.js +288 -0
  12. package/dist/commands/__tests__/bootstrap.test.js.map +1 -0
  13. package/dist/commands/__tests__/upgrade.rollback.test.js +1 -1
  14. package/dist/commands/__tests__/upgrade.swap.test.js +1 -1
  15. package/dist/commands/auth-upgrade.d.ts +41 -0
  16. package/dist/commands/auth-upgrade.d.ts.map +1 -1
  17. package/dist/commands/auth-upgrade.js +158 -6
  18. package/dist/commands/auth-upgrade.js.map +1 -1
  19. package/dist/commands/bootstrap.d.ts +95 -0
  20. package/dist/commands/bootstrap.d.ts.map +1 -0
  21. package/dist/commands/bootstrap.js +329 -0
  22. package/dist/commands/bootstrap.js.map +1 -0
  23. package/dist/commands/create.d.ts.map +1 -1
  24. package/dist/commands/create.js +19 -1
  25. package/dist/commands/create.js.map +1 -1
  26. package/dist/commands/upgrade.d.ts +76 -1
  27. package/dist/commands/upgrade.d.ts.map +1 -1
  28. package/dist/commands/upgrade.js +221 -14
  29. package/dist/commands/upgrade.js.map +1 -1
  30. package/dist/image-digests.json +8 -0
  31. package/dist/index.js +1014 -240
  32. package/dist/index.js.map +1 -1
  33. package/dist/install-root.d.ts +74 -0
  34. package/dist/install-root.d.ts.map +1 -0
  35. package/dist/install-root.js +98 -0
  36. package/dist/install-root.js.map +1 -0
  37. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -421,8 +421,8 @@ var init_parseUtil = __esm({
421
421
  init_errors();
422
422
  init_en();
423
423
  makeIssue = (params) => {
424
- const { data, path: path35, errorMaps, issueData } = params;
425
- const fullPath = [...path35, ...issueData.path || []];
424
+ const { data, path: path36, errorMaps, issueData } = params;
425
+ const fullPath = [...path36, ...issueData.path || []];
426
426
  const fullIssue = {
427
427
  ...issueData,
428
428
  path: fullPath
@@ -730,11 +730,11 @@ var init_types = __esm({
730
730
  init_parseUtil();
731
731
  init_util();
732
732
  ParseInputLazyPath = class {
733
- constructor(parent, value, path35, key) {
733
+ constructor(parent, value, path36, key) {
734
734
  this._cachedPath = [];
735
735
  this.parent = parent;
736
736
  this.data = value;
737
- this._path = path35;
737
+ this._path = path36;
738
738
  this._key = key;
739
739
  }
740
740
  get path() {
@@ -4221,7 +4221,7 @@ import YAML from "yaml";
4221
4221
  function bootstrapStepCmd(entry) {
4222
4222
  return typeof entry === "string" ? entry : entry.cmd;
4223
4223
  }
4224
- function refineForbiddenKeys(value, path35, ctx, rejectSource) {
4224
+ function refineForbiddenKeys(value, path36, ctx, rejectSource) {
4225
4225
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4226
4226
  return;
4227
4227
  }
@@ -4229,12 +4229,12 @@ function refineForbiddenKeys(value, path35, ctx, rejectSource) {
4229
4229
  if (FORBIDDEN_KEYS.has(key)) {
4230
4230
  ctx.addIssue({
4231
4231
  code: external_exports.ZodIssueCode.custom,
4232
- path: [...path35, key],
4232
+ path: [...path36, key],
4233
4233
  message: `forbidden key "${key}" (prototype-pollution surface)`
4234
4234
  });
4235
4235
  continue;
4236
4236
  }
4237
- if (rejectSource && path35.length === 0 && key === "source") {
4237
+ if (rejectSource && path36.length === 0 && key === "source") {
4238
4238
  ctx.addIssue({
4239
4239
  code: external_exports.ZodIssueCode.custom,
4240
4240
  path: ["source"],
@@ -4244,30 +4244,30 @@ function refineForbiddenKeys(value, path35, ctx, rejectSource) {
4244
4244
  }
4245
4245
  refineForbiddenKeys(
4246
4246
  value[key],
4247
- [...path35, key],
4247
+ [...path36, key],
4248
4248
  ctx,
4249
4249
  false
4250
4250
  );
4251
4251
  }
4252
4252
  }
4253
- function rejectForbiddenKeys(value, path35, rejectSource) {
4253
+ function rejectForbiddenKeys(value, path36, rejectSource) {
4254
4254
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4255
4255
  return;
4256
4256
  }
4257
4257
  for (const key of Object.keys(value)) {
4258
4258
  if (FORBIDDEN_KEYS.has(key)) {
4259
4259
  throw new Error(
4260
- `[manifest] ${path35}: forbidden key "${key}" (prototype-pollution surface)`
4260
+ `[manifest] ${path36}: forbidden key "${key}" (prototype-pollution surface)`
4261
4261
  );
4262
4262
  }
4263
4263
  if (rejectSource && key === "source") {
4264
4264
  throw new Error(
4265
- `[manifest] ${path35}: top-level "source" is loader-stamped \u2014 manifests must not author it`
4265
+ `[manifest] ${path36}: top-level "source" is loader-stamped \u2014 manifests must not author it`
4266
4266
  );
4267
4267
  }
4268
4268
  rejectForbiddenKeys(
4269
4269
  value[key],
4270
- `${path35}.${key}`,
4270
+ `${path36}.${key}`,
4271
4271
  false
4272
4272
  );
4273
4273
  }
@@ -5088,7 +5088,7 @@ async function safeText(res) {
5088
5088
  }
5089
5089
  }
5090
5090
  function sleep(ms) {
5091
- return new Promise((resolve7) => setTimeout(resolve7, ms));
5091
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
5092
5092
  }
5093
5093
  var DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, RETRY_COUNT, RETRY_BACKOFF_MS, AuthClient;
5094
5094
  var init_client = __esm({
@@ -5208,8 +5208,8 @@ var init_client = __esm({
5208
5208
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
5209
5209
  }
5210
5210
  }
5211
- async request(method, path35, body, attempt = 0) {
5212
- const url = `${this.baseUrl}${path35}`;
5211
+ async request(method, path36, body, attempt = 0) {
5212
+ const url = `${this.baseUrl}${path36}`;
5213
5213
  const controller = new AbortController();
5214
5214
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
5215
5215
  const headers = {};
@@ -5225,7 +5225,7 @@ var init_client = __esm({
5225
5225
  } catch (err) {
5226
5226
  if (attempt < RETRY_COUNT && isTransient(err)) {
5227
5227
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
5228
- return this.request(method, path35, body, attempt + 1);
5228
+ return this.request(method, path36, body, attempt + 1);
5229
5229
  }
5230
5230
  throw err;
5231
5231
  } finally {
@@ -5246,7 +5246,7 @@ function resolveAuthServicePath() {
5246
5246
  return path8.join(pkgsDir, "auth-service");
5247
5247
  }
5248
5248
  function sleep2(ms) {
5249
- return new Promise((resolve7) => setTimeout(resolve7, ms));
5249
+ return new Promise((resolve8) => setTimeout(resolve8, ms));
5250
5250
  }
5251
5251
  var DEFAULT_PORT, DEFAULT_VOLUME, DEFAULT_CONTAINER, DEFAULT_IMAGE, AuthContainerController;
5252
5252
  var init_container = __esm({
@@ -5800,7 +5800,7 @@ var demuxStream, execInContainer;
5800
5800
  var init_exec = __esm({
5801
5801
  "../adapters/dist/docker/exec.js"() {
5802
5802
  "use strict";
5803
- demuxStream = (stream) => new Promise((resolve7, reject) => {
5803
+ demuxStream = (stream) => new Promise((resolve8, reject) => {
5804
5804
  const stdoutChunks = [];
5805
5805
  const stderrChunks = [];
5806
5806
  const stdout = new PassThrough();
@@ -5814,7 +5814,7 @@ var init_exec = __esm({
5814
5814
  stream.pipe(stdout);
5815
5815
  }
5816
5816
  stream.on("end", () => {
5817
- resolve7({
5817
+ resolve8({
5818
5818
  stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
5819
5819
  stderr: Buffer.concat(stderrChunks).toString("utf-8")
5820
5820
  });
@@ -6188,7 +6188,7 @@ var init_connection = __esm({
6188
6188
  // -----------------------------------------------------------------------
6189
6189
  async exec(host, command) {
6190
6190
  const client = await this.getConnection(host);
6191
- return new Promise((resolve7, reject) => {
6191
+ return new Promise((resolve8, reject) => {
6192
6192
  client.exec(command, (err, stream) => {
6193
6193
  if (err) {
6194
6194
  reject(new Error(`SSH exec failed on ${host}: ${err.message}`));
@@ -6203,7 +6203,7 @@ var init_connection = __esm({
6203
6203
  stderr += data.toString();
6204
6204
  });
6205
6205
  stream.on("close", (code) => {
6206
- resolve7({
6206
+ resolve8({
6207
6207
  exitCode: code ?? 0,
6208
6208
  stdout: stdout.trimEnd(),
6209
6209
  stderr: stderr.trimEnd()
@@ -6234,10 +6234,10 @@ var init_connection = __esm({
6234
6234
  throw new Error(`No SSH configuration found for host: ${host}`);
6235
6235
  }
6236
6236
  const client = new SSHClient();
6237
- return new Promise((resolve7, reject) => {
6237
+ return new Promise((resolve8, reject) => {
6238
6238
  client.on("ready", () => {
6239
6239
  this.connections.set(host, client);
6240
- resolve7(client);
6240
+ resolve8(client);
6241
6241
  }).on("error", (err) => {
6242
6242
  this.connections.delete(host);
6243
6243
  reject(new Error(`SSH connection to ${host} failed: ${err.message}`));
@@ -6676,8 +6676,8 @@ var init_provider3 = __esm({
6676
6676
  // -----------------------------------------------------------------------
6677
6677
  // Internal fetch helper
6678
6678
  // -----------------------------------------------------------------------
6679
- async request(path35, method, body) {
6680
- const url = `${this.config.workerUrl}${path35}`;
6679
+ async request(path36, method, body) {
6680
+ const url = `${this.config.workerUrl}${path36}`;
6681
6681
  const bearer = await this.config.mintToken();
6682
6682
  const headers = {
6683
6683
  Authorization: `Bearer ${bearer}`
@@ -11420,7 +11420,7 @@ function isCloudflaredAvailable() {
11420
11420
  }
11421
11421
  }
11422
11422
  function startTunnel(port) {
11423
- return new Promise((resolve7, reject) => {
11423
+ return new Promise((resolve8, reject) => {
11424
11424
  const child = spawn("cloudflared", ["tunnel", "--url", `http://localhost:${port}`], {
11425
11425
  stdio: ["ignore", "pipe", "pipe"],
11426
11426
  detached: false
@@ -11441,7 +11441,7 @@ function startTunnel(port) {
11441
11441
  if (match2) {
11442
11442
  resolved = true;
11443
11443
  clearTimeout(timeout);
11444
- resolve7(match2[0]);
11444
+ resolve8(match2[0]);
11445
11445
  }
11446
11446
  }
11447
11447
  child.stdout?.on("data", scan);
@@ -11528,8 +11528,8 @@ var init_dashboard = __esm({
11528
11528
  }
11529
11529
  throw err;
11530
11530
  }
11531
- await new Promise((resolve7, reject) => {
11532
- this.server.on("listening", resolve7);
11531
+ await new Promise((resolve8, reject) => {
11532
+ this.server.on("listening", resolve8);
11533
11533
  this.server.on("error", reject);
11534
11534
  });
11535
11535
  this.info = { localUrl: `http://localhost:${port}` };
@@ -11574,8 +11574,8 @@ var init_dashboard = __esm({
11574
11574
  async stop() {
11575
11575
  stopTunnel();
11576
11576
  if (this.server) {
11577
- await new Promise((resolve7) => {
11578
- this.server.close(() => resolve7());
11577
+ await new Promise((resolve8) => {
11578
+ this.server.close(() => resolve8());
11579
11579
  });
11580
11580
  this.server = null;
11581
11581
  }
@@ -11780,6 +11780,54 @@ var init_context = __esm({
11780
11780
  }
11781
11781
  });
11782
11782
 
11783
+ // src/install-root.ts
11784
+ var install_root_exports = {};
11785
+ __export(install_root_exports, {
11786
+ MissingBuildScriptError: () => MissingBuildScriptError,
11787
+ installRoot: () => installRoot,
11788
+ isDevMode: () => isDevMode,
11789
+ resolveBuildScript: () => resolveBuildScript
11790
+ });
11791
+ import { existsSync as existsSync17 } from "node:fs";
11792
+ import { dirname as dirname13, join as join24, resolve as resolve6 } from "node:path";
11793
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
11794
+ function installRoot(metaUrl = import.meta.url) {
11795
+ const here = fileURLToPath3(metaUrl);
11796
+ return resolve6(dirname13(here), "..");
11797
+ }
11798
+ function isDevMode(env = process.env, installRootDir = installRoot()) {
11799
+ if (env.OLAM_DEV !== "1") return false;
11800
+ const repoRoot = resolve6(installRootDir, "..", "..");
11801
+ return existsSync17(join24(repoRoot, "packages")) && existsSync17(join24(repoRoot, "package.json"));
11802
+ }
11803
+ function resolveBuildScript(input) {
11804
+ const { scriptRelPath, env = process.env, installRootDir = installRoot() } = input;
11805
+ if (!isDevMode(env, installRootDir)) {
11806
+ throw new MissingBuildScriptError(scriptRelPath);
11807
+ }
11808
+ const repoRoot = resolve6(installRootDir, "..", "..");
11809
+ return join24(repoRoot, scriptRelPath);
11810
+ }
11811
+ var MissingBuildScriptError;
11812
+ var init_install_root = __esm({
11813
+ "src/install-root.ts"() {
11814
+ "use strict";
11815
+ MissingBuildScriptError = class extends Error {
11816
+ constructor(scriptRelPath) {
11817
+ super(
11818
+ `Build script ${scriptRelPath} is not available in this CLI install.
11819
+ Source-build paths require a monorepo clone:
11820
+ git clone https://github.com/pleri/olam && cd olam
11821
+ OLAM_DEV=1 olam <command> --from-source
11822
+ For published-image upgrades (Phase B+), drop the --from-source flag
11823
+ and the CLI will pull pre-built images from ghcr.io/pleri/* by digest.`
11824
+ );
11825
+ this.name = "MissingBuildScriptError";
11826
+ }
11827
+ };
11828
+ }
11829
+ });
11830
+
11783
11831
  // src/registry-allowlist.ts
11784
11832
  var registry_allowlist_exports = {};
11785
11833
  __export(registry_allowlist_exports, {
@@ -11898,6 +11946,9 @@ var init_checksum = __esm({
11898
11946
 
11899
11947
  // src/index.ts
11900
11948
  import { Command } from "commander";
11949
+ import * as fs31 from "node:fs";
11950
+ import * as path35 from "node:path";
11951
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
11901
11952
 
11902
11953
  // src/commands/init.ts
11903
11954
  import * as fs5 from "node:fs";
@@ -12304,11 +12355,11 @@ var UnknownArchetypeError = class extends Error {
12304
12355
  known;
12305
12356
  };
12306
12357
  var ArchetypeCycleError = class extends Error {
12307
- constructor(path35) {
12358
+ constructor(path36) {
12308
12359
  super(
12309
- `Archetype inheritance cycle detected: ${path35.join(" \u2192 ")} \u2192 ${path35[0] ?? "?"}`
12360
+ `Archetype inheritance cycle detected: ${path36.join(" \u2192 ")} \u2192 ${path36[0] ?? "?"}`
12310
12361
  );
12311
- this.path = path35;
12362
+ this.path = path36;
12312
12363
  this.name = "ArchetypeCycleError";
12313
12364
  }
12314
12365
  path;
@@ -12501,9 +12552,9 @@ function registerInstall(program2) {
12501
12552
 
12502
12553
  // src/commands/auth.ts
12503
12554
  init_auth();
12504
- import pc7 from "picocolors";
12555
+ import pc8 from "picocolors";
12505
12556
  import * as readline from "node:readline/promises";
12506
- import { spawn as spawn2 } from "node:child_process";
12557
+ import { spawn as spawn3 } from "node:child_process";
12507
12558
 
12508
12559
  // src/commands/auth-status.ts
12509
12560
  import * as fs8 from "node:fs";
@@ -12555,6 +12606,8 @@ init_auth();
12555
12606
  // src/exit-codes.ts
12556
12607
  var EXIT_GENERIC_ERROR = 1;
12557
12608
  var EXIT_PLERI_NOT_CONFIGURED = 2;
12609
+ var EXIT_BOOTSTRAP_PULL_FAILED = 3;
12610
+ var EXIT_PROTOCOL_MISMATCH = 4;
12558
12611
  var EXIT_AUTH_NEEDS_ATTENTION = 5;
12559
12612
 
12560
12613
  // src/commands/auth-status.ts
@@ -12680,8 +12733,9 @@ async function runAuthStatus(getStatus) {
12680
12733
  // src/commands/auth-upgrade.ts
12681
12734
  import * as fs20 from "node:fs";
12682
12735
  import * as path23 from "node:path";
12683
- import { spawnSync as spawnSync4 } from "node:child_process";
12684
- import pc6 from "picocolors";
12736
+ import { spawnSync as spawnSync6 } from "node:child_process";
12737
+ import ora2 from "ora";
12738
+ import pc7 from "picocolors";
12685
12739
 
12686
12740
  // src/commands/host-cp.ts
12687
12741
  init_dist();
@@ -13127,10 +13181,10 @@ async function readHostCpToken2() {
13127
13181
  if (!fs19.existsSync(tp)) return null;
13128
13182
  return fs19.readFileSync(tp, "utf-8").trim();
13129
13183
  }
13130
- async function callHostCpProxy(method, worldId, path35, body) {
13184
+ async function callHostCpProxy(method, worldId, path36, body) {
13131
13185
  const token = await readHostCpToken2();
13132
13186
  if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
13133
- const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path35}`;
13187
+ const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path36}`;
13134
13188
  try {
13135
13189
  const headers = {
13136
13190
  Authorization: `Bearer ${token}`
@@ -13237,13 +13291,348 @@ async function handleDeregister(opts) {
13237
13291
 
13238
13292
  // src/commands/auth-upgrade.ts
13239
13293
  init_auth();
13294
+
13295
+ // src/commands/bootstrap.ts
13296
+ init_install_root();
13297
+ import { spawn as spawn2, spawnSync as spawnSync5 } from "node:child_process";
13298
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
13299
+ import { join as join25 } from "node:path";
13300
+ import ora from "ora";
13301
+ import pc6 from "picocolors";
13302
+
13303
+ // src/protocol-version.ts
13304
+ import { spawnSync as spawnSync4 } from "node:child_process";
13305
+ var OLAM_PROTOCOL_VERSIONS_SUPPORTED = [1];
13306
+ function parseProtocolVersionsLabel(labelValue) {
13307
+ if (!labelValue) return [];
13308
+ const parts = labelValue.split(",").map((s) => s.trim()).filter(Boolean);
13309
+ const versions = /* @__PURE__ */ new Set();
13310
+ for (const part of parts) {
13311
+ const n = Number.parseInt(part, 10);
13312
+ if (Number.isFinite(n) && n > 0 && String(n) === part) {
13313
+ versions.add(n);
13314
+ }
13315
+ }
13316
+ return Array.from(versions).sort((a, b) => a - b);
13317
+ }
13318
+ function checkProtocolOverlap(imageVersions, cliVersions = OLAM_PROTOCOL_VERSIONS_SUPPORTED) {
13319
+ const cliSet = new Set(cliVersions);
13320
+ const overlap = imageVersions.filter((v) => cliSet.has(v));
13321
+ if (imageVersions.length === 0) {
13322
+ return {
13323
+ overlap: [],
13324
+ imageVersions: [],
13325
+ cliVersions,
13326
+ compatible: false,
13327
+ remedy: `Devbox image is missing the \`olam.protocol.versions\` LABEL. This CLI requires versions [${cliVersions.join(", ")}]. See docs/architecture/devbox-contract.md \xA71 for the contract; rebuild the image with \`LABEL olam.protocol.versions="1"\`.`
13328
+ };
13329
+ }
13330
+ if (overlap.length === 0) {
13331
+ const imgLow = Math.min(...imageVersions);
13332
+ const imgHigh = Math.max(...imageVersions);
13333
+ const cliLow = Math.min(...cliVersions);
13334
+ const cliHigh = Math.max(...cliVersions);
13335
+ return {
13336
+ overlap: [],
13337
+ imageVersions: [...imageVersions],
13338
+ cliVersions,
13339
+ compatible: false,
13340
+ remedy: `Devbox image protocol versions [${imageVersions.join(", ")}] don't overlap CLI's [${cliVersions.join(", ")}]. ` + (imgHigh < cliLow ? `The image is older than this CLI supports \u2014 rebuild against the contract version ${cliLow}+ at docs/architecture/devbox-contract.md.` : imgLow > cliHigh ? `The image is newer than this CLI supports \u2014 pin a compatible CLI: \`npm install -g @pleri/olam-cli@<version-with-protocol-${imgLow}>\`.` : `Pin a compatible CLI version that overlaps with the image's range.`)
13341
+ };
13342
+ }
13343
+ return {
13344
+ overlap,
13345
+ imageVersions: [...imageVersions],
13346
+ cliVersions,
13347
+ compatible: true,
13348
+ remedy: ""
13349
+ };
13350
+ }
13351
+
13352
+ // src/commands/bootstrap.ts
13353
+ function loadImageDigests(installRootDir = installRoot()) {
13354
+ const digestsPath = join25(installRootDir, "dist", "image-digests.json");
13355
+ if (!existsSync18(digestsPath)) {
13356
+ throw new Error(
13357
+ `image-digests.json missing at ${digestsPath}. Re-run \`npm install -g @pleri/olam-cli@<version>\` to refresh the tarball.`
13358
+ );
13359
+ }
13360
+ let parsed;
13361
+ try {
13362
+ parsed = JSON.parse(readFileSync15(digestsPath, "utf8"));
13363
+ } catch (err) {
13364
+ throw new Error(`image-digests.json is not valid JSON: ${err.message}`);
13365
+ }
13366
+ if (typeof parsed !== "object" || parsed === null) {
13367
+ throw new Error("image-digests.json is not an object");
13368
+ }
13369
+ const obj = parsed;
13370
+ const required = ["host-cp", "auth", "devbox"];
13371
+ const missing = required.filter((k) => typeof obj[k] !== "string" || !obj[k]);
13372
+ if (missing.length > 0) {
13373
+ throw new Error(
13374
+ `image-digests.json missing required key(s): ${missing.join(", ")}. Tarball may be corrupted; reinstall the CLI.`
13375
+ );
13376
+ }
13377
+ return parsed;
13378
+ }
13379
+ var realDocker = {
13380
+ async info() {
13381
+ return spawnAsync("docker", ["info"]);
13382
+ },
13383
+ async pull(imageRef, opts) {
13384
+ return spawnAsync("docker", ["pull", imageRef], { signal: opts?.signal });
13385
+ },
13386
+ async inspectLabel(imageRef, label) {
13387
+ return spawnAsync("docker", [
13388
+ "inspect",
13389
+ imageRef,
13390
+ "--format",
13391
+ `{{ index .Config.Labels "${label}" }}`
13392
+ ]);
13393
+ }
13394
+ };
13395
+ function spawnAsync(cmd, args, opts = {}) {
13396
+ return new Promise((resolve8) => {
13397
+ const child = spawn2(cmd, [...args], {
13398
+ stdio: ["ignore", "pipe", "pipe"],
13399
+ signal: opts.signal
13400
+ });
13401
+ let stdout = "";
13402
+ let stderr = "";
13403
+ child.stdout?.on("data", (chunk) => {
13404
+ stdout += chunk.toString();
13405
+ });
13406
+ child.stderr?.on("data", (chunk) => {
13407
+ stderr += chunk.toString();
13408
+ });
13409
+ child.on("error", (err) => {
13410
+ resolve8({ exitCode: -1, stdout, stderr: stderr + err.message });
13411
+ });
13412
+ child.on("close", (code) => {
13413
+ resolve8({ exitCode: code ?? -1, stdout, stderr });
13414
+ });
13415
+ });
13416
+ }
13417
+ var inflightPulls = /* @__PURE__ */ new Map();
13418
+ var DEFAULT_PULL_POLICY = {
13419
+ perAttemptTimeoutMs: 18e4,
13420
+ retryOnce: true
13421
+ };
13422
+ function isTransientPullFailure(result) {
13423
+ const s = result.stderr.toLowerCase();
13424
+ return result.exitCode !== 0 && (/timeout|connection|temporarily|429|503|tls handshake|network/.test(s) || result.exitCode === 124 || result.exitCode === -1);
13425
+ }
13426
+ async function pullImageWithRetry(imageRef, docker2 = realDocker, policy = DEFAULT_PULL_POLICY) {
13427
+ const existing = inflightPulls.get(imageRef);
13428
+ if (existing) return existing;
13429
+ const promise = (async () => {
13430
+ let result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs);
13431
+ if (result.exitCode !== 0 && policy.retryOnce && isTransientPullFailure(result)) {
13432
+ result = await pullOnce(imageRef, docker2, policy.perAttemptTimeoutMs);
13433
+ }
13434
+ return result;
13435
+ })();
13436
+ inflightPulls.set(imageRef, promise);
13437
+ try {
13438
+ return await promise;
13439
+ } finally {
13440
+ inflightPulls.delete(imageRef);
13441
+ }
13442
+ }
13443
+ async function pullOnce(imageRef, docker2, timeoutMs) {
13444
+ const ac = new AbortController();
13445
+ const timer = setTimeout(() => ac.abort(), timeoutMs).unref?.();
13446
+ try {
13447
+ return await docker2.pull(imageRef, { signal: ac.signal });
13448
+ } finally {
13449
+ if (timer) clearTimeout(timer);
13450
+ }
13451
+ }
13452
+ var REAL_OLAM_SUBCOMMAND = (args) => {
13453
+ const result = spawnSync5(process.execPath, [
13454
+ process.argv[1] ?? "olam",
13455
+ ...args
13456
+ ], {
13457
+ encoding: "utf8",
13458
+ stdio: ["ignore", "pipe", "pipe"]
13459
+ });
13460
+ return {
13461
+ exitCode: result.status ?? -1,
13462
+ stdout: result.stdout ?? "",
13463
+ stderr: result.stderr ?? ""
13464
+ };
13465
+ };
13466
+ async function runBootstrap2(opts, deps = {}) {
13467
+ const docker2 = deps.docker ?? realDocker;
13468
+ const digests = deps.digests ?? loadImageDigests();
13469
+ const registry = opts.registry ?? deps.registry ?? digests.$registry ?? "ghcr.io/pleri";
13470
+ printHeader("olam bootstrap");
13471
+ const infoSpinner = ora("Checking docker daemon").start();
13472
+ const info = await docker2.info();
13473
+ if (info.exitCode !== 0) {
13474
+ infoSpinner.fail("docker daemon not reachable");
13475
+ process.stderr.write(
13476
+ `${pc6.red("error")} docker info exited with ${info.exitCode}.
13477
+ Ensure Docker Desktop / Colima / Rancher is running, then retry.
13478
+ stderr: ${info.stderr.split("\n")[0] ?? ""}
13479
+ `
13480
+ );
13481
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "docker daemon not reachable" };
13482
+ }
13483
+ infoSpinner.succeed("docker daemon reachable");
13484
+ const imageRefs = [
13485
+ { name: "host-cp", ref: `${registry}/olam-host-cp@${digests["host-cp"]}` },
13486
+ { name: "auth", ref: `${registry}/olam-auth@${digests.auth}` },
13487
+ { name: "devbox", ref: `${registry}/olam-devbox@${digests.devbox}` }
13488
+ ];
13489
+ const pullSpinner = ora("Pulling images (3 parallel)").start();
13490
+ const pullStart = Date.now();
13491
+ const pullResults = await Promise.all(
13492
+ imageRefs.map(async ({ name, ref }) => ({
13493
+ name,
13494
+ ref,
13495
+ result: await pullImageWithRetry(ref, docker2)
13496
+ }))
13497
+ );
13498
+ const pullElapsed = ((Date.now() - pullStart) / 1e3).toFixed(1);
13499
+ const failed = pullResults.filter((r) => r.result.exitCode !== 0);
13500
+ if (failed.length > 0) {
13501
+ pullSpinner.fail(`Pull failed for ${failed.map((r) => r.name).join(", ")}`);
13502
+ for (const f of failed) {
13503
+ process.stderr.write(
13504
+ ` ${pc6.red(f.name)} (${f.ref}):
13505
+ exit=${f.result.exitCode}
13506
+ stderr: ${f.result.stderr.split("\n")[0] ?? "(empty)"}
13507
+ `
13508
+ );
13509
+ }
13510
+ process.stderr.write(
13511
+ "\n Remedy: `olam bootstrap` is the only on-ramp. Re-run after\n resolving network / GHCR availability. If a specific image is\n permanently unavailable, file a bug at github.com/pleri/olam/issues.\n"
13512
+ );
13513
+ return {
13514
+ exitCode: EXIT_BOOTSTRAP_PULL_FAILED,
13515
+ summary: `pull failed: ${failed.map((r) => r.name).join(", ")}`
13516
+ };
13517
+ }
13518
+ pullSpinner.succeed(`Pulled 3 images in ${pullElapsed}s`);
13519
+ const handshakeSpinner = ora("Verifying olam.protocol.versions handshake").start();
13520
+ for (const { name, ref } of imageRefs) {
13521
+ const inspect = await docker2.inspectLabel(ref, "olam.protocol.versions");
13522
+ if (inspect.exitCode !== 0) {
13523
+ handshakeSpinner.fail(`Could not inspect ${name}`);
13524
+ process.stderr.write(
13525
+ `${pc6.red("error")} docker inspect ${ref} failed: ${inspect.stderr}
13526
+ `
13527
+ );
13528
+ return { exitCode: EXIT_GENERIC_ERROR, summary: `inspect failed: ${name}` };
13529
+ }
13530
+ const labelValue = (inspect.stdout || "").trim();
13531
+ const versions = parseProtocolVersionsLabel(
13532
+ labelValue === "<no value>" ? "" : labelValue
13533
+ );
13534
+ const decision = checkProtocolOverlap(versions);
13535
+ if (!decision.compatible) {
13536
+ handshakeSpinner.fail(`Protocol mismatch on ${name}`);
13537
+ process.stderr.write(`${pc6.red("error")} ${decision.remedy}
13538
+ `);
13539
+ return {
13540
+ exitCode: EXIT_PROTOCOL_MISMATCH,
13541
+ summary: `protocol mismatch: ${name}`
13542
+ };
13543
+ }
13544
+ }
13545
+ handshakeSpinner.succeed("Protocol handshake passed (all 3 images)");
13546
+ const runOlam = deps.runOlamSubcommand ?? REAL_OLAM_SUBCOMMAND;
13547
+ const hostCpSpinner = ora("Starting host-cp").start();
13548
+ const hostCp = runOlam(["host-cp", "start"]);
13549
+ if (hostCp.exitCode !== 0) {
13550
+ hostCpSpinner.fail("host-cp start failed");
13551
+ process.stderr.write(` stderr: ${hostCp.stderr.split("\n").slice(0, 3).join("\n ")}
13552
+ `);
13553
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "host-cp start failed" };
13554
+ }
13555
+ hostCpSpinner.succeed("host-cp running");
13556
+ const authUpSpinner = ora("Starting auth-service").start();
13557
+ const authUp = runOlam(["auth", "up"]);
13558
+ if (authUp.exitCode !== 0) {
13559
+ authUpSpinner.fail("auth up failed");
13560
+ process.stderr.write(` stderr: ${authUp.stderr.split("\n").slice(0, 3).join("\n ")}
13561
+ `);
13562
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "auth up failed" };
13563
+ }
13564
+ authUpSpinner.succeed("auth-service running");
13565
+ if (opts.skipAuthLogin || process.env.OLAM_BOOTSTRAP_SKIP_AUTH_LOGIN === "1") {
13566
+ printInfo("auth login", "skipped");
13567
+ } else {
13568
+ printHeader("Authenticate with Anthropic");
13569
+ process.stdout.write(
13570
+ " This opens an OAuth PKCE flow against Anthropic. The auth-service\n will hold your refresh token; per-world access tokens are issued\n on demand and rotated every 6h.\n\n Press Ctrl+C to cancel \u2014 re-run `olam bootstrap` to retry.\n\n"
13571
+ );
13572
+ const login = runOlam(["auth", "login"]);
13573
+ if (login.exitCode !== 0) {
13574
+ printError("auth login failed (or was cancelled)");
13575
+ process.stderr.write(` Re-run \`olam bootstrap\` after resolving.
13576
+ `);
13577
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "auth login failed" };
13578
+ }
13579
+ printSuccess("auth login complete");
13580
+ }
13581
+ if (opts.withSmoke) {
13582
+ printHeader("Smoke test");
13583
+ const smoke = runOlam([
13584
+ "create",
13585
+ "--workspace",
13586
+ "smoke",
13587
+ "--task",
13588
+ "smoke test from olam bootstrap"
13589
+ ]);
13590
+ if (smoke.exitCode !== 0) {
13591
+ printError("smoke world create failed");
13592
+ process.stderr.write(` stderr: ${smoke.stderr.split("\n").slice(0, 3).join("\n ")}
13593
+ `);
13594
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "smoke create failed" };
13595
+ }
13596
+ printSuccess("Smoke world created");
13597
+ }
13598
+ printHeader("Stack ready");
13599
+ printInfo("host-cp", `running (${digests["host-cp"].slice(0, 19)}\u2026)`);
13600
+ printInfo("auth", `running (${digests.auth.slice(0, 19)}\u2026)`);
13601
+ printInfo("devbox", `pulled (${digests.devbox.slice(0, 19)}\u2026)`);
13602
+ printInfo("next", '`olam create --task "your task"` to spawn a world');
13603
+ return { exitCode: 0, summary: "stack ready" };
13604
+ }
13605
+ function registerBootstrap(program2) {
13606
+ program2.command("bootstrap").description(
13607
+ "Bootstrap the olam stack: pull 3 images by digest, verify protocol handshake, start host-cp + auth, run auth login."
13608
+ ).option("--with-smoke", "After bootstrap, create a smoke-test world to verify end-to-end").option("--skip-auth-login", "Skip the interactive PKCE step (CI / scripted use only)").option(
13609
+ "--registry <ref>",
13610
+ "Override the registry prefix (default: read from image-digests.json or fall back to ghcr.io/pleri)"
13611
+ ).action(async (opts) => {
13612
+ try {
13613
+ const result = await runBootstrap2({
13614
+ withSmoke: opts.withSmoke === true,
13615
+ skipAuthLogin: opts.skipAuthLogin === true,
13616
+ registry: opts.registry
13617
+ });
13618
+ process.exitCode = result.exitCode;
13619
+ } catch (err) {
13620
+ printError(err instanceof Error ? err.message : String(err));
13621
+ process.exitCode = EXIT_GENERIC_ERROR;
13622
+ }
13623
+ });
13624
+ }
13625
+
13626
+ // src/commands/auth-upgrade.ts
13627
+ init_install_root();
13240
13628
  var AUTH_PORT = 9999;
13241
13629
  var AUTH_HEALTH_URL = `http://127.0.0.1:${AUTH_PORT}/health`;
13242
13630
  var AUTH_ADD_URL = `http://127.0.0.1:${AUTH_PORT}/credentials/add`;
13243
13631
  function parseAuthUpgradeOpts(raw) {
13244
13632
  return {
13245
13633
  yes: raw.yes === true,
13246
- skipRecreate: raw.skipRecreate === true
13634
+ skipRecreate: raw.skipRecreate === true,
13635
+ fromSource: raw.fromSource === true
13247
13636
  };
13248
13637
  }
13249
13638
  function validateAuthRepoRoot(cwd) {
@@ -13304,8 +13693,8 @@ async function smokeTestCodexProvider(authSecret) {
13304
13693
  }
13305
13694
  function runStep(label, cmd, args, opts = {}) {
13306
13695
  const start = Date.now();
13307
- process.stdout.write(` ${pc6.dim(label.padEnd(34))}`);
13308
- const result = spawnSync4(cmd, [...args], {
13696
+ process.stdout.write(` ${pc7.dim(label.padEnd(34))}`);
13697
+ const result = spawnSync6(cmd, [...args], {
13309
13698
  encoding: "utf-8",
13310
13699
  stdio: ["ignore", "pipe", "pipe"],
13311
13700
  cwd: opts.cwd ?? process.cwd(),
@@ -13314,7 +13703,7 @@ function runStep(label, cmd, args, opts = {}) {
13314
13703
  const durationMs = Date.now() - start;
13315
13704
  const ok = result.status === 0 && result.error === void 0;
13316
13705
  const dur = `${(durationMs / 1e3).toFixed(1)}s`;
13317
- process.stdout.write(`${ok ? pc6.green("\u2713") : pc6.red("\u2717")} ${dur}
13706
+ process.stdout.write(`${ok ? pc7.green("\u2713") : pc7.red("\u2717")} ${dur}
13318
13707
  `);
13319
13708
  return {
13320
13709
  ok,
@@ -13327,10 +13716,10 @@ async function confirm(message) {
13327
13716
  if (!process.stdin.isTTY) return true;
13328
13717
  const { createInterface: createInterface2 } = await import("node:readline");
13329
13718
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
13330
- return new Promise((resolve7) => {
13719
+ return new Promise((resolve8) => {
13331
13720
  rl.question(`${message} [y/N] `, (answer) => {
13332
13721
  rl.close();
13333
- resolve7(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
13722
+ resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
13334
13723
  });
13335
13724
  });
13336
13725
  }
@@ -13342,6 +13731,126 @@ function printTimings(timings) {
13342
13731
  }
13343
13732
  printInfo("total", `${(total / 1e3).toFixed(1)}s`);
13344
13733
  }
13734
+ async function runAuthUpgradePullByDigest(deps = {}) {
13735
+ const docker2 = deps.docker ?? realDocker;
13736
+ const digests = deps.digests ?? loadImageDigests();
13737
+ const registry = deps.registry ?? digests.$registry ?? "ghcr.io/pleri";
13738
+ printHeader("olam auth upgrade (pull-by-digest)");
13739
+ const infoSpinner = ora2("Checking docker daemon").start();
13740
+ const info = await docker2.info();
13741
+ if (info.exitCode !== 0) {
13742
+ infoSpinner.fail("docker daemon not reachable");
13743
+ process.stderr.write(
13744
+ `${pc7.red("error")} docker info exited with ${info.exitCode}.
13745
+ Ensure Docker is running, then retry.
13746
+ `
13747
+ );
13748
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "docker daemon not reachable" };
13749
+ }
13750
+ infoSpinner.succeed("docker daemon reachable");
13751
+ const ref = `${registry}/olam-auth@${digests.auth}`;
13752
+ const pullSpinner = ora2(`Pulling ${ref.slice(0, 60)}\u2026`).start();
13753
+ const pullResult = await pullImageWithRetry(ref, docker2);
13754
+ if (pullResult.exitCode !== 0) {
13755
+ pullSpinner.fail("Pull failed");
13756
+ process.stderr.write(
13757
+ `${pc7.red("error")} pull failed (exit ${pullResult.exitCode}):
13758
+ ${pullResult.stderr.split("\n")[0] ?? "(empty)"}
13759
+
13760
+ Remedy: re-run after resolving network / GHCR availability.
13761
+ For monorepo dev, \`OLAM_DEV=1 olam auth upgrade --from-source\` builds locally.
13762
+ `
13763
+ );
13764
+ return { exitCode: EXIT_BOOTSTRAP_PULL_FAILED, summary: "pull failed: auth" };
13765
+ }
13766
+ pullSpinner.succeed("Image pulled");
13767
+ const handshakeSpinner = ora2("Verifying olam.protocol.versions handshake").start();
13768
+ const inspect = await docker2.inspectLabel(ref, "olam.protocol.versions");
13769
+ if (inspect.exitCode !== 0) {
13770
+ handshakeSpinner.fail("Could not inspect auth image");
13771
+ process.stderr.write(
13772
+ `${pc7.red("error")} docker inspect ${ref} failed: ${inspect.stderr}
13773
+ `
13774
+ );
13775
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "inspect failed: auth" };
13776
+ }
13777
+ const labelValue = (inspect.stdout || "").trim();
13778
+ const versions = parseProtocolVersionsLabel(
13779
+ labelValue === "<no value>" ? "" : labelValue
13780
+ );
13781
+ const decision = checkProtocolOverlap(versions);
13782
+ if (!decision.compatible) {
13783
+ handshakeSpinner.fail("Protocol mismatch on auth image");
13784
+ process.stderr.write(`${pc7.red("error")} ${decision.remedy}
13785
+ `);
13786
+ return { exitCode: EXIT_PROTOCOL_MISMATCH, summary: "protocol mismatch: auth" };
13787
+ }
13788
+ handshakeSpinner.succeed("Protocol handshake passed");
13789
+ const tagSpinner = ora2("Tagging digest \u2192 olam-auth:local").start();
13790
+ const tagger = deps.tagImpl ?? defaultTagAuth;
13791
+ const tagResult = tagger(ref, "olam-auth:local");
13792
+ if (!tagResult.ok) {
13793
+ tagSpinner.fail("docker tag failed");
13794
+ process.stderr.write(`${pc7.red("error")} ${tagResult.error ?? "docker tag failed"}
13795
+ `);
13796
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "tag failed: auth" };
13797
+ }
13798
+ tagSpinner.succeed("Re-tagged \u2192 olam-auth:local");
13799
+ const authSpinner = ora2("Recreating auth-service container").start();
13800
+ const recreate = deps.recreateAuth ?? defaultRecreateAuth;
13801
+ const recreateResult = await recreate();
13802
+ if (!recreateResult.ok) {
13803
+ authSpinner.fail("auth-service recreate failed");
13804
+ process.stderr.write(
13805
+ `${pc7.red("error")} ${recreateResult.error ?? "unknown failure"}
13806
+ `
13807
+ );
13808
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "auth recreate failed" };
13809
+ }
13810
+ authSpinner.succeed("auth-service running on new image");
13811
+ printSuccess("Auth upgrade complete (pull-by-digest)");
13812
+ printInfo("digest", digests.auth.slice(0, 32) + "\u2026");
13813
+ return { exitCode: 0, summary: "auth upgraded" };
13814
+ }
13815
+ function defaultTagAuth(from, to) {
13816
+ try {
13817
+ const r = spawnSync6("docker", ["tag", from, to], {
13818
+ encoding: "utf-8",
13819
+ stdio: ["ignore", "ignore", "pipe"]
13820
+ });
13821
+ if (r.status === 0 && r.error === void 0) return { ok: true };
13822
+ return {
13823
+ ok: false,
13824
+ error: (r.stderr ?? "").trim() || r.error?.message || "docker tag failed"
13825
+ };
13826
+ } catch (err) {
13827
+ return {
13828
+ ok: false,
13829
+ error: err instanceof Error ? `spawnSync threw: ${err.message}` : "spawnSync threw"
13830
+ };
13831
+ }
13832
+ }
13833
+ async function defaultRecreateAuth() {
13834
+ try {
13835
+ spawnSync6("docker", ["stop", "olam-auth"], {
13836
+ encoding: "utf-8",
13837
+ stdio: ["ignore", "ignore", "ignore"]
13838
+ });
13839
+ spawnSync6("docker", ["rm", "olam-auth"], {
13840
+ encoding: "utf-8",
13841
+ stdio: ["ignore", "ignore", "ignore"]
13842
+ });
13843
+ const controller = new AuthContainerController();
13844
+ controller.start();
13845
+ const healthy = await waitForAuthHealth(15e3);
13846
+ if (!healthy) {
13847
+ return { ok: false, error: "auth-service /health did not respond within 15s" };
13848
+ }
13849
+ return { ok: true };
13850
+ } catch (err) {
13851
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
13852
+ }
13853
+ }
13345
13854
  async function handleAuthUpgrade(opts) {
13346
13855
  const cwd = process.cwd();
13347
13856
  const rootCheck = validateAuthRepoRoot(cwd);
@@ -13369,7 +13878,17 @@ async function handleAuthUpgrade(opts) {
13369
13878
  }
13370
13879
  }
13371
13880
  const timings = [];
13372
- const buildScript = path23.join(cwd, "packages/adapters/src/docker/build-auth.sh");
13881
+ let buildScript;
13882
+ try {
13883
+ const { resolveBuildScript: resolveBuildScript2 } = await Promise.resolve().then(() => (init_install_root(), install_root_exports));
13884
+ buildScript = resolveBuildScript2({
13885
+ scriptRelPath: "packages/adapters/src/docker/build-auth.sh"
13886
+ });
13887
+ } catch (err) {
13888
+ printError(err instanceof Error ? err.message : String(err));
13889
+ process.exitCode = 1;
13890
+ return;
13891
+ }
13373
13892
  const imageResult = runStep("bash build-auth.sh", "bash", [buildScript], { cwd });
13374
13893
  timings.push({ label: "docker image build", durationMs: imageResult.durationMs });
13375
13894
  if (!imageResult.ok) {
@@ -13385,49 +13904,49 @@ ${imageResult.stderr}`);
13385
13904
  return;
13386
13905
  }
13387
13906
  const stopStart = Date.now();
13388
- process.stdout.write(` ${pc6.dim("docker stop olam-auth".padEnd(34))}`);
13389
- spawnSync4("docker", ["stop", "olam-auth"], {
13907
+ process.stdout.write(` ${pc7.dim("docker stop olam-auth".padEnd(34))}`);
13908
+ spawnSync6("docker", ["stop", "olam-auth"], {
13390
13909
  encoding: "utf-8",
13391
13910
  stdio: ["ignore", "pipe", "pipe"]
13392
13911
  });
13393
13912
  const stopDurationMs = Date.now() - stopStart;
13394
- process.stdout.write(`${pc6.green("\u2713")} ${(stopDurationMs / 1e3).toFixed(1)}s
13913
+ process.stdout.write(`${pc7.green("\u2713")} ${(stopDurationMs / 1e3).toFixed(1)}s
13395
13914
  `);
13396
13915
  timings.push({ label: "container stop", durationMs: stopDurationMs });
13397
13916
  const rmStart = Date.now();
13398
- process.stdout.write(` ${pc6.dim("docker rm olam-auth".padEnd(34))}`);
13399
- spawnSync4("docker", ["rm", "olam-auth"], {
13917
+ process.stdout.write(` ${pc7.dim("docker rm olam-auth".padEnd(34))}`);
13918
+ spawnSync6("docker", ["rm", "olam-auth"], {
13400
13919
  encoding: "utf-8",
13401
13920
  stdio: ["ignore", "pipe", "pipe"]
13402
13921
  });
13403
13922
  const rmDurationMs = Date.now() - rmStart;
13404
- process.stdout.write(`${pc6.green("\u2713")} ${(rmDurationMs / 1e3).toFixed(1)}s
13923
+ process.stdout.write(`${pc7.green("\u2713")} ${(rmDurationMs / 1e3).toFixed(1)}s
13405
13924
  `);
13406
13925
  timings.push({ label: "container remove", durationMs: rmDurationMs });
13407
13926
  const startStart = Date.now();
13408
- process.stdout.write(` ${pc6.dim("docker run olam-auth:local".padEnd(34))}`);
13927
+ process.stdout.write(` ${pc7.dim("docker run olam-auth:local".padEnd(34))}`);
13409
13928
  try {
13410
13929
  const controller = new AuthContainerController();
13411
13930
  controller.start();
13412
13931
  const startDurationMs = Date.now() - startStart;
13413
- process.stdout.write(`${pc6.green("\u2713")} ${(startDurationMs / 1e3).toFixed(1)}s
13932
+ process.stdout.write(`${pc7.green("\u2713")} ${(startDurationMs / 1e3).toFixed(1)}s
13414
13933
  `);
13415
13934
  timings.push({ label: "container start", durationMs: startDurationMs });
13416
13935
  } catch (err) {
13417
13936
  const startDurationMs = Date.now() - startStart;
13418
- process.stdout.write(`${pc6.red("\u2717")} ${(startDurationMs / 1e3).toFixed(1)}s
13937
+ process.stdout.write(`${pc7.red("\u2717")} ${(startDurationMs / 1e3).toFixed(1)}s
13419
13938
  `);
13420
13939
  timings.push({ label: "container start", durationMs: startDurationMs });
13421
13940
  printError(`Failed to start auth container: ${err instanceof Error ? err.message : String(err)}`);
13422
13941
  process.exitCode = 1;
13423
13942
  return;
13424
13943
  }
13425
- process.stdout.write(` ${pc6.dim("waiting for /health".padEnd(34))}`);
13944
+ process.stdout.write(` ${pc7.dim("waiting for /health".padEnd(34))}`);
13426
13945
  const healthStart = Date.now();
13427
13946
  const healthy = await waitForAuthHealth(1e4);
13428
13947
  const healthDurationMs = Date.now() - healthStart;
13429
13948
  const healthDur = `${(healthDurationMs / 1e3).toFixed(1)}s`;
13430
- process.stdout.write(`${healthy ? pc6.green("\u2713") : pc6.yellow("?")} ${healthDur}
13949
+ process.stdout.write(`${healthy ? pc7.green("\u2713") : pc7.yellow("?")} ${healthDur}
13431
13950
  `);
13432
13951
  timings.push({ label: "/health", durationMs: healthDurationMs });
13433
13952
  if (!healthy) {
@@ -13436,13 +13955,13 @@ ${imageResult.stderr}`);
13436
13955
  printTimings(timings);
13437
13956
  return;
13438
13957
  }
13439
- process.stdout.write(` ${pc6.dim("smoke test: codex provider".padEnd(34))}`);
13958
+ process.stdout.write(` ${pc7.dim("smoke test: codex provider".padEnd(34))}`);
13440
13959
  const smokeStart = Date.now();
13441
13960
  const authSecret = readAuthSecret2();
13442
13961
  const smoke = await smokeTestCodexProvider(authSecret);
13443
13962
  const smokeDurationMs = Date.now() - smokeStart;
13444
13963
  const smokeDur = `${(smokeDurationMs / 1e3).toFixed(1)}s`;
13445
- process.stdout.write(`${smoke.ok ? pc6.green("\u2713") : pc6.red("\u2717")} ${smokeDur}
13964
+ process.stdout.write(`${smoke.ok ? pc7.green("\u2713") : pc7.red("\u2717")} ${smokeDur}
13446
13965
  `);
13447
13966
  timings.push({ label: "smoke test", durationMs: smokeDurationMs });
13448
13967
  if (!smoke.ok) {
@@ -13457,8 +13976,31 @@ ${imageResult.stderr}`);
13457
13976
  printTimings(timings);
13458
13977
  }
13459
13978
  function registerAuthUpgrade(auth) {
13460
- auth.command("upgrade").description("Rebuild the olam-auth image and force-recreate the container").option("-y, --yes", "Skip the confirmation prompt").option("--skip-recreate", "Build the image only; skip container stop/rm/start").action(async (opts) => {
13461
- await handleAuthUpgrade(parseAuthUpgradeOpts(opts));
13979
+ auth.command("upgrade").description(
13980
+ "Upgrade the olam-auth container. Default: pull olam-auth@<digest> from ghcr.io and recreate. Use --from-source to rebuild from monorepo source (requires OLAM_DEV=1)."
13981
+ ).option("-y, --yes", "Skip the confirmation prompt").option("--skip-recreate", "Build the image only; skip container stop/rm/start (only with --from-source)").option(
13982
+ "--from-source",
13983
+ "Build olam-auth from monorepo source (legacy path).\n Requires OLAM_DEV=1 + a `packages/` sibling at the install root."
13984
+ ).action(async (opts) => {
13985
+ const parsed = parseAuthUpgradeOpts(opts);
13986
+ if (parsed.fromSource) {
13987
+ if (!isDevMode()) {
13988
+ printError(
13989
+ new MissingBuildScriptError("packages/adapters/src/docker/build-auth.sh").message
13990
+ );
13991
+ process.exitCode = 1;
13992
+ return;
13993
+ }
13994
+ await handleAuthUpgrade(parsed);
13995
+ return;
13996
+ }
13997
+ try {
13998
+ const result = await runAuthUpgradePullByDigest();
13999
+ process.exitCode = result.exitCode;
14000
+ } catch (err) {
14001
+ printError(err instanceof Error ? err.message : String(err));
14002
+ process.exitCode = EXIT_GENERIC_ERROR;
14003
+ }
13462
14004
  });
13463
14005
  }
13464
14006
 
@@ -13466,7 +14008,7 @@ function registerAuthUpgrade(auth) {
13466
14008
  function openBrowser(url) {
13467
14009
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
13468
14010
  try {
13469
- const child = spawn2(cmd, [url], { detached: true, stdio: "ignore" });
14011
+ const child = spawn3(cmd, [url], { detached: true, stdio: "ignore" });
13470
14012
  child.unref();
13471
14013
  } catch {
13472
14014
  }
@@ -13506,7 +14048,7 @@ function registerAuth(program2) {
13506
14048
  printInfo("Port", String(initial.port));
13507
14049
  printInfo("Volume", "olam-auth-data");
13508
14050
  console.log(`
13509
- ${pc7.dim("Next: olam auth login")}`);
14051
+ ${pc8.dim("Next: olam auth login")}`);
13510
14052
  });
13511
14053
  auth.command("down").description("Stop the auth container (tokens persist in the volume)").action(() => {
13512
14054
  const controller = new AuthContainerController();
@@ -13531,15 +14073,15 @@ ${pc7.dim("Next: olam auth login")}`);
13531
14073
  }
13532
14074
  printHeader(`Credentials (${status.accounts.length})`);
13533
14075
  if (status.accounts.length === 0) {
13534
- console.log(` ${pc7.dim("No credentials \u2014 run: olam auth login --label primary")}`);
14076
+ console.log(` ${pc8.dim("No credentials \u2014 run: olam auth login --label primary")}`);
13535
14077
  return;
13536
14078
  }
13537
14079
  const stateColor = (s) => {
13538
- if (s === "active") return pc7.green("active");
13539
- if (s === "cooldown") return pc7.yellow("cooldown");
13540
- if (s === "expired") return pc7.red("expired");
13541
- if (s === "disabled") return pc7.dim("disabled");
13542
- return pc7.dim(s ?? "unknown");
14080
+ if (s === "active") return pc8.green("active");
14081
+ if (s === "cooldown") return pc8.yellow("cooldown");
14082
+ if (s === "expired") return pc8.red("expired");
14083
+ if (s === "disabled") return pc8.dim("disabled");
14084
+ return pc8.dim(s ?? "unknown");
13543
14085
  };
13544
14086
  for (const a of status.accounts) {
13545
14087
  const label = a.accountLabel ?? a.id;
@@ -13547,7 +14089,7 @@ ${pc7.dim("Next: olam auth login")}`);
13547
14089
  const last429 = a.usage?.last429At ? `last429=${a.usage.last429At}` : "last429=never";
13548
14090
  const reset = a.rateLimitResetsAt ? `resets=${a.rateLimitResetsAt}` : "";
13549
14091
  console.log(
13550
- ` ${pc7.bold(label.padEnd(18))} ${stateColor(a.state).padEnd(18)} ${pc7.dim(`req5h=${reqs}`)} ${pc7.dim(`exp=${a.expiresIn}`)} ${pc7.dim(last429)} ${pc7.yellow(reset)}`
14092
+ ` ${pc8.bold(label.padEnd(18))} ${stateColor(a.state).padEnd(18)} ${pc8.dim(`req5h=${reqs}`)} ${pc8.dim(`exp=${a.expiresIn}`)} ${pc8.dim(last429)} ${pc8.yellow(reset)}`
13551
14093
  );
13552
14094
  }
13553
14095
  });
@@ -13585,7 +14127,7 @@ ${pc7.dim("Next: olam auth login")}`);
13585
14127
  const preflight = await runAuthPreflight({ autoStart: true });
13586
14128
  if (preflight.verdict !== "ok" && preflight.verdict !== "no-accounts") {
13587
14129
  printError(preflight.message);
13588
- console.log(` ${pc7.dim(preflight.remedy)}`);
14130
+ console.log(` ${pc8.dim(preflight.remedy)}`);
13589
14131
  process.exitCode = 1;
13590
14132
  return;
13591
14133
  }
@@ -13618,14 +14160,14 @@ ${pc7.dim("Next: olam auth login")}`);
13618
14160
  }
13619
14161
  printHeader("Claude OAuth \u2014 PKCE flow");
13620
14162
  console.log(`
13621
- ${pc7.bold("1.")} Opening Claude in your default browser\u2026`);
13622
- console.log(` ${pc7.dim(pending.loginUrl)}`);
14163
+ ${pc8.bold("1.")} Opening Claude in your default browser\u2026`);
14164
+ console.log(` ${pc8.dim(pending.loginUrl)}`);
13623
14165
  openBrowser(pending.loginUrl);
13624
14166
  console.log(`
13625
- ${pc7.bold("2.")} After approving, paste the authorization code from the redirect page.`);
13626
- console.log(` ${pc7.dim("(Format: <code>#<state> or just <code>.)")}
14167
+ ${pc8.bold("2.")} After approving, paste the authorization code from the redirect page.`);
14168
+ console.log(` ${pc8.dim("(Format: <code>#<state> or just <code>.)")}
13627
14169
  `);
13628
- const raw = await promptLine(`${pc7.dim("code:")} `);
14170
+ const raw = await promptLine(`${pc8.dim("code:")} `);
13629
14171
  if (!raw) {
13630
14172
  printError("No code provided.");
13631
14173
  process.exitCode = 1;
@@ -13636,7 +14178,7 @@ ${pc7.dim("Next: olam auth login")}`);
13636
14178
  const result = await client.completeLogin(statePart, codePart);
13637
14179
  printSuccess(`Account stored: ${result.account} (${result.expiresIn})`);
13638
14180
  console.log(`
13639
- ${pc7.dim("Next: olam create --name my-world")}`);
14181
+ ${pc8.dim("Next: olam create --name my-world")}`);
13640
14182
  } catch (err) {
13641
14183
  printError(err instanceof Error ? err.message : "token exchange failed");
13642
14184
  process.exitCode = 1;
@@ -13667,16 +14209,16 @@ ${pc7.dim("Next: olam create --name my-world")}`);
13667
14209
 
13668
14210
  // src/commands/create.ts
13669
14211
  init_manager();
13670
- import { spawnSync as spawnSync5 } from "node:child_process";
13671
- import { existsSync as existsSync19 } from "node:fs";
13672
- import { dirname as dirname13, resolve as resolve6 } from "node:path";
13673
- import ora from "ora";
13674
- import pc8 from "picocolors";
14212
+ import { spawnSync as spawnSync7 } from "node:child_process";
14213
+ import { existsSync as existsSync21 } from "node:fs";
14214
+ import { dirname as dirname14, resolve as resolve7 } from "node:path";
14215
+ import ora3 from "ora";
14216
+ import pc9 from "picocolors";
13675
14217
 
13676
14218
  // ../core/src/world/devbox-freshness.ts
13677
14219
  import { execSync as execSync6 } from "node:child_process";
13678
- import { existsSync as existsSync18, statSync as statSync5 } from "node:fs";
13679
- import { join as join25 } from "node:path";
14220
+ import { existsSync as existsSync20, statSync as statSync5 } from "node:fs";
14221
+ import { join as join27 } from "node:path";
13680
14222
  var DEFAULT_DEVBOX_IMAGE = "olam-devbox:latest";
13681
14223
  var DEVBOX_BAKED_SOURCES = [
13682
14224
  "packages/adapters/src/docker/devbox.Dockerfile",
@@ -13711,7 +14253,7 @@ function getDevboxFreshness(deps) {
13711
14253
  }
13712
14254
  const newerSources = [];
13713
14255
  for (const relPath of DEVBOX_BAKED_SOURCES) {
13714
- const absPath = join25(deps.repoRoot, relPath);
14256
+ const absPath = join27(deps.repoRoot, relPath);
13715
14257
  const mtimeMs = statMtime(absPath);
13716
14258
  if (mtimeMs === null) continue;
13717
14259
  if (mtimeMs > imageCreatedAtMs) {
@@ -13741,9 +14283,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
13741
14283
  "These source files have changed since the image was built; the",
13742
14284
  "changes will NOT take effect in fresh worlds until you rebuild:"
13743
14285
  ];
13744
- for (const { path: path35, mtimeMs } of result.newerSources) {
14286
+ for (const { path: path36, mtimeMs } of result.newerSources) {
13745
14287
  const when = new Date(mtimeMs).toISOString();
13746
- lines.push(` \u2022 ${path35} (modified ${when})`);
14288
+ lines.push(` \u2022 ${path36} (modified ${when})`);
13747
14289
  }
13748
14290
  lines.push("");
13749
14291
  lines.push("Rebuild with:");
@@ -13765,7 +14307,7 @@ function defaultDockerInspect(image) {
13765
14307
  }
13766
14308
  function defaultStatMtime(absPath) {
13767
14309
  try {
13768
- if (!existsSync18(absPath)) return null;
14310
+ if (!existsSync20(absPath)) return null;
13769
14311
  return statSync5(absPath).mtimeMs;
13770
14312
  } catch {
13771
14313
  return null;
@@ -13903,15 +14445,15 @@ init_context();
13903
14445
  var HOST_CP_URL = "http://127.0.0.1:19000";
13904
14446
  async function readHostCpTokenForCreate() {
13905
14447
  try {
13906
- const { default: fs31 } = await import("node:fs");
14448
+ const { default: fs32 } = await import("node:fs");
13907
14449
  const { default: os18 } = await import("node:os");
13908
- const { default: path35 } = await import("node:path");
13909
- const tp = path35.join(
13910
- process.env.OLAM_HOME ?? path35.join(os18.homedir(), ".olam"),
14450
+ const { default: path36 } = await import("node:path");
14451
+ const tp = path36.join(
14452
+ process.env.OLAM_HOME ?? path36.join(os18.homedir(), ".olam"),
13911
14453
  "host-cp.token"
13912
14454
  );
13913
- if (!fs31.existsSync(tp)) return null;
13914
- return fs31.readFileSync(tp, "utf-8").trim();
14455
+ if (!fs32.existsSync(tp)) return null;
14456
+ return fs32.readFileSync(tp, "utf-8").trim();
13915
14457
  } catch {
13916
14458
  return null;
13917
14459
  }
@@ -13952,7 +14494,7 @@ function registerCreate(program2) {
13952
14494
  resolvedName = defaultNameFromPrompt(opts.fromPrompt);
13953
14495
  }
13954
14496
  if (!resolvedWorkspace && !resolvedRepos) {
13955
- const inferenceSpinner = ora("Inferring workspace from prompt\u2026").start();
14497
+ const inferenceSpinner = ora3("Inferring workspace from prompt\u2026").start();
13956
14498
  try {
13957
14499
  const catalog = await fetchHostCpWorkspaces();
13958
14500
  const catalogRepos = Array.from(
@@ -13967,9 +14509,9 @@ function registerCreate(program2) {
13967
14509
  const reason = inferred.repos.length === 0 ? "no repo names extracted from prompt" : `inference confidence ${inferred.confidence.toFixed(2)} below ${PICKER_CONFIDENCE_THRESHOLD}`;
13968
14510
  printError(`Picker needed: ${reason}`);
13969
14511
  if (catalogRepos.length > 0) {
13970
- console.log(pc8.dim(` available repos: ${catalogRepos.join(", ")}`));
14512
+ console.log(pc9.dim(` available repos: ${catalogRepos.join(", ")}`));
13971
14513
  }
13972
- console.log(pc8.dim(" rerun with explicit --workspace <name> or --repos <a> <b> <c>"));
14514
+ console.log(pc9.dim(" rerun with explicit --workspace <name> or --repos <a> <b> <c>"));
13973
14515
  process.exitCode = 1;
13974
14516
  return;
13975
14517
  }
@@ -13987,7 +14529,7 @@ function registerCreate(program2) {
13987
14529
  inferenceSpinner.fail(`Multiple workspaces match (${decision.result})`);
13988
14530
  const cands = decision.result === "exact-N" ? decision.candidates.map((w) => w.name) : decision.candidates.map((c) => c.workspace.name);
13989
14531
  printError(`Picker needed: ${cands.join(", ")}`);
13990
- console.log(pc8.dim(" rerun with explicit --workspace <name>"));
14532
+ console.log(pc9.dim(" rerun with explicit --workspace <name>"));
13991
14533
  process.exitCode = 1;
13992
14534
  return;
13993
14535
  } else {
@@ -14020,10 +14562,24 @@ function registerCreate(program2) {
14020
14562
  const freshness = getDevboxFreshness({ repoRoot });
14021
14563
  if (freshness.isStale) {
14022
14564
  if (opts.rebuildBase) {
14023
- const spinner2 = ora("Rebuilding olam-devbox:latest\u2026").start();
14024
- const rebuild = spawnSync5(
14565
+ const { resolveBuildScript: resolveBuildScript2, MissingBuildScriptError: MissingBuildScriptError2 } = await Promise.resolve().then(() => (init_install_root(), install_root_exports));
14566
+ let buildScript;
14567
+ try {
14568
+ buildScript = resolveBuildScript2({
14569
+ scriptRelPath: "packages/adapters/src/docker/build-devbox.sh"
14570
+ });
14571
+ } catch (err) {
14572
+ if (err instanceof MissingBuildScriptError2) {
14573
+ printError(err.message);
14574
+ process.exitCode = 1;
14575
+ return;
14576
+ }
14577
+ throw err;
14578
+ }
14579
+ const spinner2 = ora3("Rebuilding olam-devbox:latest\u2026").start();
14580
+ const rebuild = spawnSync7(
14025
14581
  "bash",
14026
- [resolve6(repoRoot, "packages/adapters/src/docker/build-devbox.sh")],
14582
+ [buildScript],
14027
14583
  { cwd: repoRoot, stdio: "inherit" }
14028
14584
  );
14029
14585
  if (rebuild.status !== 0) {
@@ -14038,7 +14594,7 @@ function registerCreate(program2) {
14038
14594
  }
14039
14595
  }
14040
14596
  }
14041
- const spinner = ora("Creating world...").start();
14597
+ const spinner = ora3("Creating world...").start();
14042
14598
  try {
14043
14599
  const world = await ctx.worldManager.createWorld({
14044
14600
  name: resolvedName,
@@ -14087,10 +14643,10 @@ function registerCreate(program2) {
14087
14643
  }
14088
14644
  if (world.credentialsInjected?.claude) {
14089
14645
  console.log(`
14090
- ${pc8.green("Credentials injected. World is ready for dispatch.")}`);
14646
+ ${pc9.green("Credentials injected. World is ready for dispatch.")}`);
14091
14647
  } else if (world.dashboardUrl) {
14092
14648
  console.log(`
14093
- ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
14649
+ ${pc9.yellow("Authenticate at")} ${world.dashboardUrl}`);
14094
14650
  }
14095
14651
  if (opts.hostCp !== false) {
14096
14652
  const probeResult = await probeHostCp().catch(() => null);
@@ -14102,14 +14658,14 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
14102
14658
  process.stderr.write(
14103
14659
  [
14104
14660
  "",
14105
- pc8.red("Host CP probe failed."),
14661
+ pc9.red("Host CP probe failed."),
14106
14662
  ` tried: http://127.0.0.1:19000/api/bootstrap \u2192 ${diag.bootstrapStatus}`,
14107
14663
  ` tried: docker container "olam-host-cp" \u2192 ${diag.containerStatus}`,
14108
14664
  "",
14109
- pc8.yellow("World was created but the SPA inbox will not show it until you:"),
14110
- ` ${pc8.cyan("olam host-cp start")} (start the host CP)`,
14111
- ` ${pc8.cyan(`olam host-cp register --world ${world.id}`)} (register manually)`,
14112
- ` OR pass ${pc8.dim("--no-host-cp")} on next create to suppress this check.`,
14665
+ pc9.yellow("World was created but the SPA inbox will not show it until you:"),
14666
+ ` ${pc9.cyan("olam host-cp start")} (start the host CP)`,
14667
+ ` ${pc9.cyan(`olam host-cp register --world ${world.id}`)} (register manually)`,
14668
+ ` OR pass ${pc9.dim("--no-host-cp")} on next create to suppress this check.`,
14113
14669
  ""
14114
14670
  ].join("\n")
14115
14671
  );
@@ -14141,13 +14697,13 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
14141
14697
  process.stderr.write(
14142
14698
  [
14143
14699
  "",
14144
- pc8.red("Host CP registry POST failed."),
14700
+ pc9.red("Host CP registry POST failed."),
14145
14701
  ` url: ${hostCpUrl}/api/admin/registry`,
14146
14702
  ` status: ${reg.status}`,
14147
14703
  ` error: ${reg.error}`,
14148
14704
  "",
14149
- pc8.yellow("World was created but not registered. Run manually:"),
14150
- ` ${pc8.cyan(`olam host-cp register --world ${world.id}`)}`,
14705
+ pc9.yellow("World was created but not registered. Run manually:"),
14706
+ ` ${pc9.cyan(`olam host-cp register --world ${world.id}`)}`,
14151
14707
  ""
14152
14708
  ].join("\n")
14153
14709
  );
@@ -14202,7 +14758,7 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
14202
14758
  }
14203
14759
  const worldUrl = `${hostCpUrl}/world/${encodeURIComponent(world.id)}`;
14204
14760
  console.log(`
14205
- ${pc8.cyan("Host CP UI:")} ${worldUrl}`);
14761
+ ${pc9.cyan("Host CP UI:")} ${worldUrl}`);
14206
14762
  if (opts.open !== false && reg.ok) {
14207
14763
  const openResult = openUrl(worldUrl);
14208
14764
  if (openResult.opened) {
@@ -14216,7 +14772,7 @@ ${pc8.cyan("Host CP UI:")} ${worldUrl}`);
14216
14772
  spinner.fail("Failed to create world");
14217
14773
  if (err instanceof AuthPreflightError) {
14218
14774
  printError(err.message);
14219
- if (err.remedy) console.log(` ${pc8.dim(err.remedy)}`);
14775
+ if (err.remedy) console.log(` ${pc9.dim(err.remedy)}`);
14220
14776
  } else {
14221
14777
  printError(err instanceof Error ? err.message : String(err));
14222
14778
  }
@@ -14227,10 +14783,10 @@ ${pc8.cyan("Host CP UI:")} ${worldUrl}`);
14227
14783
  function resolveRepoRoot(start) {
14228
14784
  let cur = start;
14229
14785
  while (true) {
14230
- if (existsSync19(resolve6(cur, "packages")) && existsSync19(resolve6(cur, "package.json"))) {
14786
+ if (existsSync21(resolve7(cur, "packages")) && existsSync21(resolve7(cur, "package.json"))) {
14231
14787
  return cur;
14232
14788
  }
14233
- const parent = dirname13(cur);
14789
+ const parent = dirname14(cur);
14234
14790
  if (parent === cur) return start;
14235
14791
  cur = parent;
14236
14792
  }
@@ -14241,12 +14797,12 @@ function defaultNameFromPrompt(prompt) {
14241
14797
  }
14242
14798
  async function readHostCpToken3() {
14243
14799
  try {
14244
- const { default: fs31 } = await import("node:fs");
14800
+ const { default: fs32 } = await import("node:fs");
14245
14801
  const { default: os18 } = await import("node:os");
14246
- const { default: path35 } = await import("node:path");
14247
- const tp = path35.join(os18.homedir(), ".olam", "host-cp.token");
14248
- if (!fs31.existsSync(tp)) return null;
14249
- const raw = fs31.readFileSync(tp, "utf-8").trim();
14802
+ const { default: path36 } = await import("node:path");
14803
+ const tp = path36.join(os18.homedir(), ".olam", "host-cp.token");
14804
+ if (!fs32.existsSync(tp)) return null;
14805
+ const raw = fs32.readFileSync(tp, "utf-8").trim();
14250
14806
  return raw.length > 0 ? raw : null;
14251
14807
  } catch {
14252
14808
  return null;
@@ -14289,8 +14845,8 @@ async function fetchHostCpExactMatches(projects) {
14289
14845
 
14290
14846
  // src/commands/dispatch.ts
14291
14847
  init_context();
14292
- import ora2 from "ora";
14293
- import pc9 from "picocolors";
14848
+ import ora4 from "ora";
14849
+ import pc10 from "picocolors";
14294
14850
 
14295
14851
  // ../core/src/orchestrator/dispatch.ts
14296
14852
  var DEEP_MODE_SUFFIX = "\n\nUltrathink and use sub-agents (the Agent tool) to parallelize independent work. Break complex tasks into focused sub-tasks.";
@@ -14316,7 +14872,7 @@ function registerDispatch(program2) {
14316
14872
  process.exitCode = 1;
14317
14873
  return;
14318
14874
  }
14319
- const spinner = ora2("Dispatching...").start();
14875
+ const spinner = ora4("Dispatching...").start();
14320
14876
  try {
14321
14877
  const computeWorld = await ctx.computeProvider.getWorld(worldId);
14322
14878
  if (!computeWorld) {
@@ -14373,7 +14929,7 @@ OLAM_EOF`
14373
14929
  const containerName = `olam-${worldId}-devbox`;
14374
14930
  console.log(
14375
14931
  `
14376
- ${pc9.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
14932
+ ${pc10.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
14377
14933
  );
14378
14934
  } catch (err) {
14379
14935
  spinner.fail("Dispatch failed");
@@ -14385,7 +14941,7 @@ ${pc9.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-ma
14385
14941
 
14386
14942
  // src/commands/observe.ts
14387
14943
  init_context();
14388
- import pc10 from "picocolors";
14944
+ import pc11 from "picocolors";
14389
14945
  function registerObserve(program2) {
14390
14946
  program2.command("observe").description("Stream thoughts from a world (coming soon)").argument("<world>", "World ID").action(async (worldId) => {
14391
14947
  const { ctx, error } = await loadContext();
@@ -14401,22 +14957,22 @@ function registerObserve(program2) {
14401
14957
  return;
14402
14958
  }
14403
14959
  console.log(
14404
- pc10.yellow("Observation is coming in a future release.")
14960
+ pc11.yellow("Observation is coming in a future release.")
14405
14961
  );
14406
14962
  console.log(
14407
- pc10.dim("This will stream the world's reasoning in real-time.")
14963
+ pc11.dim("This will stream the world's reasoning in real-time.")
14408
14964
  );
14409
14965
  const containerName = `olam-${worldId}-devbox`;
14410
14966
  console.log(
14411
14967
  `
14412
- ${pc10.dim(`For now: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
14968
+ ${pc11.dim(`For now: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
14413
14969
  );
14414
14970
  });
14415
14971
  }
14416
14972
 
14417
14973
  // src/commands/list.ts
14418
14974
  init_context();
14419
- import pc11 from "picocolors";
14975
+ import pc12 from "picocolors";
14420
14976
  function registerList(program2) {
14421
14977
  program2.command("list").alias("ls").description("List active worlds").action(async () => {
14422
14978
  const { ctx, error } = await loadContext();
@@ -14427,18 +14983,18 @@ function registerList(program2) {
14427
14983
  }
14428
14984
  const worlds = ctx.worldManager.listWorlds();
14429
14985
  if (worlds.length === 0) {
14430
- console.log(pc11.dim("No worlds. Create one with `olam create --name my-world`."));
14986
+ console.log(pc12.dim("No worlds. Create one with `olam create --name my-world`."));
14431
14987
  return;
14432
14988
  }
14433
- console.log(`${pc11.bold(String(worlds.length))} world(s)
14989
+ console.log(`${pc12.bold(String(worlds.length))} world(s)
14434
14990
  `);
14435
14991
  for (const w of worlds) {
14436
- const statusColor = w.status === "running" ? pc11.green(w.status) : w.status === "error" ? pc11.red(w.status) : pc11.yellow(w.status);
14992
+ const statusColor = w.status === "running" ? pc12.green(w.status) : w.status === "error" ? pc12.red(w.status) : pc12.yellow(w.status);
14437
14993
  const age = formatAge(w.createdAt);
14438
14994
  const cost = `$${w.totalCostUsd.toFixed(2)}`;
14439
- console.log(` ${pc11.bold(w.name)} ${pc11.dim(`(${w.id})`)}`);
14995
+ console.log(` ${pc12.bold(w.name)} ${pc12.dim(`(${w.id})`)}`);
14440
14996
  console.log(
14441
- ` ${statusColor} ${pc11.dim("|")} ${w.repos.join(", ")} ${pc11.dim("|")} ${cost} ${pc11.dim("|")} ${age}`
14997
+ ` ${statusColor} ${pc12.dim("|")} ${w.repos.join(", ")} ${pc12.dim("|")} ${cost} ${pc12.dim("|")} ${age}`
14442
14998
  );
14443
14999
  console.log();
14444
15000
  }
@@ -14490,7 +15046,7 @@ function registerStatus(program2) {
14490
15046
 
14491
15047
  // src/commands/destroy.ts
14492
15048
  init_context();
14493
- import ora3 from "ora";
15049
+ import ora5 from "ora";
14494
15050
  function registerDestroy(program2) {
14495
15051
  program2.command("destroy").description("Destroy a world and clean up resources").argument("<world>", "World ID").option("--skip-crystallize", "Skip thought crystallization").action(async (worldId, opts) => {
14496
15052
  const { ctx, error } = await loadContext();
@@ -14505,7 +15061,7 @@ function registerDestroy(program2) {
14505
15061
  process.exitCode = 1;
14506
15062
  return;
14507
15063
  }
14508
- const spinner = ora3(`Destroying ${world.name}...`).start();
15064
+ const spinner = ora5(`Destroying ${world.name}...`).start();
14509
15065
  try {
14510
15066
  await ctx.worldManager.destroyWorld(worldId, {
14511
15067
  skipCrystallize: opts.skipCrystallize
@@ -14529,7 +15085,7 @@ function registerDestroy(program2) {
14529
15085
  // src/commands/enter.ts
14530
15086
  init_context();
14531
15087
  import { execSync as execSync7 } from "node:child_process";
14532
- import pc12 from "picocolors";
15088
+ import pc13 from "picocolors";
14533
15089
  var SAFE_IDENT3 = /^[a-z0-9][a-z0-9-]{0,63}$/;
14534
15090
  function buildStartClaudeCommands(containerName, sessionName, task) {
14535
15091
  if (!SAFE_IDENT3.test(containerName)) {
@@ -14631,7 +15187,7 @@ function registerEnter(program2) {
14631
15187
  }
14632
15188
  console.log("Run these commands in order to enter the world:\n");
14633
15189
  for (const step of steps) {
14634
- console.log(` ${pc12.dim(`# ${step.description}`)}`);
15190
+ console.log(` ${pc13.dim(`# ${step.description}`)}`);
14635
15191
  if (step.stdin !== void 0) {
14636
15192
  const escaped = step.stdin.replace(/'/g, "'\\''");
14637
15193
  console.log(` printf '%s' '${escaped}' | ${step.command}`);
@@ -14665,11 +15221,11 @@ function registerEnter(program2) {
14665
15221
  return;
14666
15222
  }
14667
15223
  console.log("Run this command to enter the world:\n");
14668
- console.log(` ${pc12.bold(result.command)}`);
15224
+ console.log(` ${pc13.bold(result.command)}`);
14669
15225
  const containerName = `olam-${worldId}-devbox`;
14670
15226
  console.log(
14671
15227
  `
14672
- ${pc12.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`
15228
+ ${pc13.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`
14673
15229
  );
14674
15230
  });
14675
15231
  }
@@ -14678,7 +15234,7 @@ ${pc12.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t ol
14678
15234
  init_context();
14679
15235
  import * as fs21 from "node:fs";
14680
15236
  import "node:path";
14681
- import ora4 from "ora";
15237
+ import ora6 from "ora";
14682
15238
  init_world_paths();
14683
15239
  function registerCrystallize(program2, options = {}) {
14684
15240
  const cmd = program2.command("crystallize").description("Crystallize thoughts from a world to Pleri Plane").argument("<world>", "World ID").action(async (worldId) => {
@@ -14714,7 +15270,7 @@ function registerCrystallize(program2, options = {}) {
14714
15270
  process.exitCode = EXIT_GENERIC_ERROR;
14715
15271
  return;
14716
15272
  }
14717
- const spinner = ora4("Crystallizing thoughts...").start();
15273
+ const spinner = ora6("Crystallizing thoughts...").start();
14718
15274
  try {
14719
15275
  const { ThoughtLocalStore: ThoughtLocalStore2 } = await Promise.resolve().then(() => (init_local_store(), local_store_exports));
14720
15276
  const { computeGraphChecksum: computeGraphChecksum2 } = await Promise.resolve().then(() => (init_checksum(), checksum_exports));
@@ -14779,7 +15335,7 @@ function registerCrystallize(program2, options = {}) {
14779
15335
 
14780
15336
  // src/commands/pr.ts
14781
15337
  init_registry();
14782
- import pc13 from "picocolors";
15338
+ import pc14 from "picocolors";
14783
15339
 
14784
15340
  // ../core/src/pr-gate/client.ts
14785
15341
  var HOST_CONTROL_PLANE_BASE2 = 19080;
@@ -14859,26 +15415,26 @@ async function findGate(id) {
14859
15415
  return null;
14860
15416
  }
14861
15417
  function formatStateBadge(state) {
14862
- if (state === "approve") return pc13.green("approve");
14863
- if (state === "block") return pc13.red("block");
14864
- if (state === "denied") return pc13.gray("denied");
14865
- return pc13.yellow("pending");
15418
+ if (state === "approve") return pc14.green("approve");
15419
+ if (state === "block") return pc14.red("block");
15420
+ if (state === "denied") return pc14.gray("denied");
15421
+ return pc14.yellow("pending");
14866
15422
  }
14867
15423
  function registerPr(program2) {
14868
15424
  const pr = program2.command("pr").description("Review and decide PR-gate requests from running worlds");
14869
15425
  pr.command("list").description("List all PR-gate requests across every running world").action(async () => {
14870
15426
  const all = await fanoutList();
14871
15427
  if (all.length === 0) {
14872
- console.log(pc13.dim("No gates open."));
15428
+ console.log(pc14.dim("No gates open."));
14873
15429
  return;
14874
15430
  }
14875
15431
  printHeader(`${all.length} gate(s)`);
14876
15432
  for (const { world, gate } of all) {
14877
15433
  const badge = formatStateBadge(gate.state);
14878
15434
  console.log(
14879
- ` ${pc13.bold(gate.id.slice(0, 8))} ${badge.padEnd(20)} ${pc13.dim(world.name)} ${pc13.dim(gate.branch)}`
15435
+ ` ${pc14.bold(gate.id.slice(0, 8))} ${badge.padEnd(20)} ${pc14.dim(world.name)} ${pc14.dim(gate.branch)}`
14880
15436
  );
14881
- console.log(` ${pc13.dim(gate.command.slice(0, 100))}`);
15437
+ console.log(` ${pc14.dim(gate.command.slice(0, 100))}`);
14882
15438
  }
14883
15439
  });
14884
15440
  pr.command("show").description("Show full gate detail (diff, command, commits)").argument("<id>", "Gate id (prefix match ok)").action(async (id) => {
@@ -14901,9 +15457,9 @@ function registerPr(program2) {
14901
15457
  if (gate.reason) printInfo("Reason", gate.reason);
14902
15458
  }
14903
15459
  printHeader("Commits");
14904
- console.log(gate.commitLog || pc13.dim(" (empty)"));
15460
+ console.log(gate.commitLog || pc14.dim(" (empty)"));
14905
15461
  printHeader("Diff stat");
14906
- console.log(gate.diffStat || pc13.dim(" (empty)"));
15462
+ console.log(gate.diffStat || pc14.dim(" (empty)"));
14907
15463
  });
14908
15464
  function decideCommand(verb, decision) {
14909
15465
  pr.command(verb).description(`${verb[0].toUpperCase()}${verb.slice(1)} a pending gate`).argument("<id>", "Gate id").option("--reason <reason>", "Short free-text reason").option("--by <name>", "Deciding identity (defaults to $USER)", process.env["USER"] ?? "human").action(async (id, opts) => {
@@ -15023,7 +15579,7 @@ function registerLanes(program2) {
15023
15579
  // src/commands/policy-check.ts
15024
15580
  init_loader2();
15025
15581
  import { execSync as execSync8 } from "node:child_process";
15026
- import pc14 from "picocolors";
15582
+ import pc15 from "picocolors";
15027
15583
 
15028
15584
  // ../../node_modules/balanced-match/dist/esm/index.js
15029
15585
  var balanced = (a, b, str) => {
@@ -16887,7 +17443,7 @@ function registerPolicyCheck(program2) {
16887
17443
  const workspaceRoot = opts.workspace ?? process.cwd();
16888
17444
  const policies = loadPolicies(workspaceRoot);
16889
17445
  if (policies.length === 0) {
16890
- console.log(pc14.dim("policy-check: no policies found in .olam/policies/ \u2014 nothing to enforce"));
17446
+ console.log(pc15.dim("policy-check: no policies found in .olam/policies/ \u2014 nothing to enforce"));
16891
17447
  return;
16892
17448
  }
16893
17449
  const diff = getDiff(opts.base, workspaceRoot);
@@ -16896,11 +17452,11 @@ function registerPolicyCheck(program2) {
16896
17452
  for (const result of results) {
16897
17453
  if (result.passed) continue;
16898
17454
  if (result.severity === "error") {
16899
- console.error(`${pc14.red("policy error")} [${result.policyId}]`);
17455
+ console.error(`${pc15.red("policy error")} [${result.policyId}]`);
16900
17456
  console.error(result.message);
16901
17457
  hasErrorFailure = true;
16902
17458
  } else {
16903
- console.warn(`${pc14.yellow("policy warn")} [${result.policyId}]`);
17459
+ console.warn(`${pc15.yellow("policy warn")} [${result.policyId}]`);
16904
17460
  console.warn(result.message);
16905
17461
  }
16906
17462
  }
@@ -16913,14 +17469,15 @@ function registerPolicyCheck(program2) {
16913
17469
  // src/commands/upgrade.ts
16914
17470
  import * as fs24 from "node:fs";
16915
17471
  import * as path28 from "node:path";
16916
- import { spawnSync as spawnSync7 } from "node:child_process";
16917
- import pc15 from "picocolors";
17472
+ import { spawnSync as spawnSync9 } from "node:child_process";
17473
+ import ora7 from "ora";
17474
+ import pc16 from "picocolors";
16918
17475
 
16919
17476
  // src/commands/upgrade-lock.ts
16920
17477
  import * as fs22 from "node:fs";
16921
17478
  import * as os13 from "node:os";
16922
17479
  import * as path26 from "node:path";
16923
- import { spawnSync as spawnSync6 } from "node:child_process";
17480
+ import { spawnSync as spawnSync8 } from "node:child_process";
16924
17481
  var LOCK_FILE_PATH = path26.join(os13.homedir(), ".olam", ".upgrade.lock");
16925
17482
  var STALE_LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
16926
17483
  function readLockFile(lockPath) {
@@ -16945,7 +17502,7 @@ function isPidAlive(pid) {
16945
17502
  }
16946
17503
  var PS_UNAVAILABLE = "__ps_unavailable__";
16947
17504
  function getPidCommand(pid) {
16948
- const result = spawnSync6("ps", ["-p", String(pid), "-o", "comm="], {
17505
+ const result = spawnSync8("ps", ["-p", String(pid), "-o", "comm="], {
16949
17506
  encoding: "utf-8",
16950
17507
  stdio: ["ignore", "pipe", "ignore"]
16951
17508
  });
@@ -17143,6 +17700,7 @@ function handleHistory(opts) {
17143
17700
 
17144
17701
  // src/commands/upgrade.ts
17145
17702
  init_auth();
17703
+ init_install_root();
17146
17704
  var AUTH_HEALTH_URL2 = "http://127.0.0.1:9999/health";
17147
17705
  function isNodeModulesInSync(cwd) {
17148
17706
  const lockPath = path28.join(cwd, "package-lock.json");
@@ -17189,7 +17747,8 @@ function parseUpgradeOpts(raw) {
17189
17747
  noCache: raw.noCache === true,
17190
17748
  history: raw.history === true,
17191
17749
  historyN: Number.isFinite(historyN) && historyN > 0 ? historyN : 10,
17192
- historyJson: raw.json === true
17750
+ historyJson: raw.json === true,
17751
+ fromSource: raw.fromSource === true
17193
17752
  };
17194
17753
  }
17195
17754
  function extractBundleHash(indexHtml) {
@@ -17198,8 +17757,8 @@ function extractBundleHash(indexHtml) {
17198
17757
  }
17199
17758
  function runStep2(label, cmd, args, opts = {}) {
17200
17759
  const start = Date.now();
17201
- process.stdout.write(` ${pc15.dim(label.padEnd(34))}`);
17202
- const result = spawnSync7(cmd, [...args], {
17760
+ process.stdout.write(` ${pc16.dim(label.padEnd(34))}`);
17761
+ const result = spawnSync9(cmd, [...args], {
17203
17762
  encoding: "utf-8",
17204
17763
  stdio: ["ignore", "pipe", "pipe"],
17205
17764
  cwd: opts.cwd ?? process.cwd(),
@@ -17208,7 +17767,7 @@ function runStep2(label, cmd, args, opts = {}) {
17208
17767
  const durationMs = Date.now() - start;
17209
17768
  const ok = result.status === 0 && result.error === void 0;
17210
17769
  const dur = `${(durationMs / 1e3).toFixed(1)}s`;
17211
- process.stdout.write(`${ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${dur}
17770
+ process.stdout.write(`${ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${dur}
17212
17771
  `);
17213
17772
  return {
17214
17773
  ok,
@@ -17218,7 +17777,7 @@ function runStep2(label, cmd, args, opts = {}) {
17218
17777
  };
17219
17778
  }
17220
17779
  function isGitDirty(cwd) {
17221
- const result = spawnSync7("git", ["status", "--porcelain"], {
17780
+ const result = spawnSync9("git", ["status", "--porcelain"], {
17222
17781
  encoding: "utf-8",
17223
17782
  stdio: ["ignore", "pipe", "pipe"],
17224
17783
  cwd
@@ -17226,7 +17785,7 @@ function isGitDirty(cwd) {
17226
17785
  return (result.stdout ?? "").trim().length > 0;
17227
17786
  }
17228
17787
  function hasGitUpstream(cwd) {
17229
- const result = spawnSync7("git", ["rev-parse", "--abbrev-ref", "@{u}"], {
17788
+ const result = spawnSync9("git", ["rev-parse", "--abbrev-ref", "@{u}"], {
17230
17789
  encoding: "utf-8",
17231
17790
  stdio: ["ignore", "pipe", "pipe"],
17232
17791
  cwd
@@ -17234,7 +17793,7 @@ function hasGitUpstream(cwd) {
17234
17793
  return result.status === 0;
17235
17794
  }
17236
17795
  function captureHeadSha(cwd) {
17237
- const result = spawnSync7("git", ["rev-parse", "HEAD"], {
17796
+ const result = spawnSync9("git", ["rev-parse", "HEAD"], {
17238
17797
  encoding: "utf-8",
17239
17798
  stdio: ["ignore", "pipe", "pipe"],
17240
17799
  cwd
@@ -17249,7 +17808,7 @@ function abbreviateSha(sha) {
17249
17808
  }
17250
17809
  function imageExists(tag) {
17251
17810
  try {
17252
- const result = spawnSync7("docker", ["image", "inspect", "--format", "{{.Id}}", tag], {
17811
+ const result = spawnSync9("docker", ["image", "inspect", "--format", "{{.Id}}", tag], {
17253
17812
  encoding: "utf-8",
17254
17813
  stdio: ["ignore", "pipe", "ignore"]
17255
17814
  });
@@ -17264,7 +17823,7 @@ function checkRollbackSetExists(plan) {
17264
17823
  return missing.join(", ");
17265
17824
  }
17266
17825
  function smokeImage(image, targetSha) {
17267
- const createResult = spawnSync7("docker", ["create", "--name", `olam-smoke-${Date.now()}`, image], {
17826
+ const createResult = spawnSync9("docker", ["create", "--name", `olam-smoke-${Date.now()}`, image], {
17268
17827
  encoding: "utf-8",
17269
17828
  stdio: ["ignore", "pipe", "pipe"]
17270
17829
  });
@@ -17277,16 +17836,16 @@ function smokeImage(image, targetSha) {
17277
17836
  };
17278
17837
  }
17279
17838
  const containerId = (createResult.stdout ?? "").trim();
17280
- const inspectResult = spawnSync7(
17839
+ const inspectResult = spawnSync9(
17281
17840
  "docker",
17282
- ["inspect", "--format", '{{index .Config.Labels "olam_build_sha"}}', image],
17841
+ ["inspect", "--format", '{{index .Config.Labels "olam.build.sha"}}', image],
17283
17842
  {
17284
17843
  encoding: "utf-8",
17285
17844
  stdio: ["ignore", "pipe", "pipe"]
17286
17845
  }
17287
17846
  );
17288
17847
  if (containerId.length > 0) {
17289
- spawnSync7("docker", ["rm", "-f", containerId], {
17848
+ spawnSync9("docker", ["rm", "-f", containerId], {
17290
17849
  encoding: "utf-8",
17291
17850
  stdio: ["ignore", "ignore", "ignore"]
17292
17851
  });
@@ -17305,7 +17864,7 @@ function smokeImage(image, targetSha) {
17305
17864
  image,
17306
17865
  ok: false,
17307
17866
  bakedSha: null,
17308
- error: "olam_build_sha label is missing or empty"
17867
+ error: "olam.build.sha label is missing or empty"
17309
17868
  };
17310
17869
  }
17311
17870
  if (bakedSha !== targetSha) {
@@ -17325,7 +17884,7 @@ var PRODUCTION_SWAP_PLAN = [
17325
17884
  ];
17326
17885
  function dockerTag(source, dest) {
17327
17886
  try {
17328
- const result = spawnSync7("docker", ["tag", source, dest], {
17887
+ const result = spawnSync9("docker", ["tag", source, dest], {
17329
17888
  encoding: "utf-8",
17330
17889
  stdio: ["ignore", "ignore", "pipe"]
17331
17890
  });
@@ -17418,10 +17977,10 @@ async function confirm2(message) {
17418
17977
  if (!process.stdin.isTTY) return true;
17419
17978
  const { createInterface: createInterface2 } = await import("node:readline");
17420
17979
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
17421
- return new Promise((resolve7) => {
17980
+ return new Promise((resolve8) => {
17422
17981
  rl.question(`${message} [y/N] `, (answer) => {
17423
17982
  rl.close();
17424
- resolve7(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
17983
+ resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
17425
17984
  });
17426
17985
  });
17427
17986
  }
@@ -17488,11 +18047,11 @@ async function waitForAuthHealthLocal(timeoutMs = 15e3) {
17488
18047
  async function recreateAuthService() {
17489
18048
  const start = Date.now();
17490
18049
  try {
17491
- spawnSync7("docker", ["stop", "olam-auth"], {
18050
+ spawnSync9("docker", ["stop", "olam-auth"], {
17492
18051
  encoding: "utf-8",
17493
18052
  stdio: ["ignore", "ignore", "ignore"]
17494
18053
  });
17495
- spawnSync7("docker", ["rm", "olam-auth"], {
18054
+ spawnSync9("docker", ["rm", "olam-auth"], {
17496
18055
  encoding: "utf-8",
17497
18056
  stdio: ["ignore", "ignore", "ignore"]
17498
18057
  });
@@ -17521,6 +18080,163 @@ function readBundleHash(cwd) {
17521
18080
  if (!fs24.existsSync(indexPath)) return null;
17522
18081
  return extractBundleHash(fs24.readFileSync(indexPath, "utf-8"));
17523
18082
  }
18083
+ async function runUpgradePullByDigest(deps = {}) {
18084
+ const docker2 = deps.docker ?? realDocker;
18085
+ const digests = deps.digests ?? loadImageDigests();
18086
+ const registry = deps.registry ?? digests.$registry ?? "ghcr.io/pleri";
18087
+ printHeader("olam upgrade (pull-by-digest)");
18088
+ const infoSpinner = ora7("Checking docker daemon").start();
18089
+ const info = await docker2.info();
18090
+ if (info.exitCode !== 0) {
18091
+ infoSpinner.fail("docker daemon not reachable");
18092
+ process.stderr.write(
18093
+ `${pc16.red("error")} docker info exited with ${info.exitCode}.
18094
+ Ensure Docker Desktop / Colima / Rancher is running, then retry.
18095
+ `
18096
+ );
18097
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "docker daemon not reachable" };
18098
+ }
18099
+ infoSpinner.succeed("docker daemon reachable");
18100
+ const imageRefs = [
18101
+ { name: "host-cp", ref: `${registry}/olam-host-cp@${digests["host-cp"]}` },
18102
+ { name: "auth", ref: `${registry}/olam-auth@${digests.auth}` },
18103
+ { name: "devbox", ref: `${registry}/olam-devbox@${digests.devbox}` }
18104
+ ];
18105
+ const pullSpinner = ora7("Pulling images (3 parallel)").start();
18106
+ const pullStart = Date.now();
18107
+ const pullResults = await Promise.all(
18108
+ imageRefs.map(async ({ name, ref }) => ({
18109
+ name,
18110
+ ref,
18111
+ result: await pullImageWithRetry(ref, docker2)
18112
+ }))
18113
+ );
18114
+ const pullElapsed = ((Date.now() - pullStart) / 1e3).toFixed(1);
18115
+ const failed = pullResults.filter((r) => r.result.exitCode !== 0);
18116
+ if (failed.length > 0) {
18117
+ pullSpinner.fail(`Pull failed for ${failed.map((r) => r.name).join(", ")}`);
18118
+ for (const f of failed) {
18119
+ process.stderr.write(
18120
+ ` ${pc16.red(f.name)} (${f.ref}):
18121
+ exit=${f.result.exitCode}
18122
+ stderr: ${f.result.stderr.split("\n")[0] ?? "(empty)"}
18123
+ `
18124
+ );
18125
+ }
18126
+ process.stderr.write(
18127
+ "\n Remedy: re-run after resolving network / GHCR availability.\n For monorepo dev, `OLAM_DEV=1 olam upgrade --from-source` builds locally.\n"
18128
+ );
18129
+ return {
18130
+ exitCode: EXIT_BOOTSTRAP_PULL_FAILED,
18131
+ summary: `pull failed: ${failed.map((r) => r.name).join(", ")}`
18132
+ };
18133
+ }
18134
+ pullSpinner.succeed(`Pulled 3 images in ${pullElapsed}s`);
18135
+ const handshakeSpinner = ora7("Verifying olam.protocol.versions handshake").start();
18136
+ for (const { name, ref } of imageRefs) {
18137
+ const inspect = await docker2.inspectLabel(ref, "olam.protocol.versions");
18138
+ if (inspect.exitCode !== 0) {
18139
+ handshakeSpinner.fail(`Could not inspect ${name}`);
18140
+ process.stderr.write(
18141
+ `${pc16.red("error")} docker inspect ${ref} failed: ${inspect.stderr}
18142
+ `
18143
+ );
18144
+ return { exitCode: EXIT_GENERIC_ERROR, summary: `inspect failed: ${name}` };
18145
+ }
18146
+ const labelValue = (inspect.stdout || "").trim();
18147
+ const versions = parseProtocolVersionsLabel(
18148
+ labelValue === "<no value>" ? "" : labelValue
18149
+ );
18150
+ const decision = checkProtocolOverlap(versions);
18151
+ if (!decision.compatible) {
18152
+ handshakeSpinner.fail(`Protocol mismatch on ${name}`);
18153
+ process.stderr.write(`${pc16.red("error")} ${decision.remedy}
18154
+ `);
18155
+ return {
18156
+ exitCode: EXIT_PROTOCOL_MISMATCH,
18157
+ summary: `protocol mismatch: ${name}`
18158
+ };
18159
+ }
18160
+ }
18161
+ handshakeSpinner.succeed("Protocol handshake passed (all 3 images)");
18162
+ const tagPlan = [
18163
+ { from: imageRefs[0].ref, to: "olam-host-cp:latest", name: "host-cp" },
18164
+ { from: imageRefs[1].ref, to: "olam-auth:local", name: "auth" },
18165
+ { from: imageRefs[2].ref, to: "olam-devbox:latest", name: "devbox" }
18166
+ ];
18167
+ const tagSpinner = ora7("Tagging digests \u2192 canonical local refs").start();
18168
+ const tagger = deps.tagImpl ?? dockerTag;
18169
+ for (const t of tagPlan) {
18170
+ const r = tagger(t.from, t.to);
18171
+ if (!r.ok) {
18172
+ tagSpinner.fail(`docker tag failed for ${t.name}`);
18173
+ process.stderr.write(`${pc16.red("error")} ${r.error ?? "docker tag failed"}
18174
+ `);
18175
+ return { exitCode: EXIT_GENERIC_ERROR, summary: `tag failed: ${t.name}` };
18176
+ }
18177
+ }
18178
+ tagSpinner.succeed("Re-tagged 3 images to canonical refs");
18179
+ const composeFile = deps.composeFile ?? path28.join(process.cwd(), "packages/host-cp/compose.yaml");
18180
+ const authSecret = deps.authSecret ?? readAuthSecret2();
18181
+ const composeRunner = deps.runComposeImpl ?? runCompose;
18182
+ const composeSpinner = ora7("docker compose recreate host-cp").start();
18183
+ const composeResult = composeRunner(
18184
+ ["up", "-d", "--force-recreate", "host-cp"],
18185
+ composeFile,
18186
+ buildComposeEnv(authSecret)
18187
+ );
18188
+ if (!composeResult.ok) {
18189
+ composeSpinner.fail("compose recreate failed");
18190
+ process.stderr.write(
18191
+ `${pc16.red("error")} docker compose up --force-recreate host-cp failed:
18192
+ ${composeResult.stderr.split("\n").slice(0, 3).join("\n ")}
18193
+ `
18194
+ );
18195
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "compose recreate failed" };
18196
+ }
18197
+ composeSpinner.succeed("host-cp recreated");
18198
+ const authSpinner = ora7("recreate auth-service").start();
18199
+ const authRecreate = deps.recreateAuth ?? defaultRecreateAuthForUpgrade;
18200
+ const authResult = await authRecreate();
18201
+ if (!authResult.ok) {
18202
+ authSpinner.fail("auth-service recreate failed");
18203
+ process.stderr.write(
18204
+ `${pc16.red("error")} ${authResult.error ?? "unknown failure"}
18205
+ `
18206
+ );
18207
+ return { exitCode: EXIT_GENERIC_ERROR, summary: "auth recreate failed" };
18208
+ }
18209
+ authSpinner.succeed("auth-service running on new image");
18210
+ printSuccess("Upgrade complete (pull-by-digest)");
18211
+ printInfo("host-cp", `running (${digests["host-cp"].slice(0, 19)}\u2026)`);
18212
+ printInfo("auth", `running (${digests.auth.slice(0, 19)}\u2026)`);
18213
+ printInfo("devbox", `pulled (${digests.devbox.slice(0, 19)}\u2026)`);
18214
+ return { exitCode: 0, summary: "stack upgraded" };
18215
+ }
18216
+ async function defaultRecreateAuthForUpgrade() {
18217
+ try {
18218
+ spawnSync9("docker", ["stop", "olam-auth"], {
18219
+ encoding: "utf-8",
18220
+ stdio: ["ignore", "ignore", "ignore"]
18221
+ });
18222
+ spawnSync9("docker", ["rm", "olam-auth"], {
18223
+ encoding: "utf-8",
18224
+ stdio: ["ignore", "ignore", "ignore"]
18225
+ });
18226
+ const controller = new AuthContainerController();
18227
+ controller.start();
18228
+ const healthy = await waitForAuthHealthLocal(15e3);
18229
+ if (!healthy) {
18230
+ return { ok: false, error: "auth-service /health did not respond within 15s" };
18231
+ }
18232
+ return { ok: true };
18233
+ } catch (err) {
18234
+ return {
18235
+ ok: false,
18236
+ error: err instanceof Error ? err.message : String(err)
18237
+ };
18238
+ }
18239
+ }
17524
18240
  async function handleUpgrade(opts) {
17525
18241
  const cwd = process.cwd();
17526
18242
  const rootCheck = validateRepoRoot(cwd);
@@ -17529,6 +18245,10 @@ async function handleUpgrade(opts) {
17529
18245
  process.exitCode = 1;
17530
18246
  return;
17531
18247
  }
18248
+ if (opts.history) {
18249
+ handleHistory(parseHistoryOpts({ n: opts.historyN, json: opts.historyJson }));
18250
+ return;
18251
+ }
17532
18252
  printHeader("olam upgrade");
17533
18253
  const steps = [
17534
18254
  "git fetch origin --prune",
@@ -17537,9 +18257,13 @@ async function handleUpgrade(opts) {
17537
18257
  "npm run build (TS workspaces)",
17538
18258
  "vite build (SPA)",
17539
18259
  ...opts.skipImage ? [] : [
17540
- "bash build-host-cp.sh (docker image)",
17541
- "docker compose up -d --force-recreate",
17542
- "wait for /health"
18260
+ "bash build-auth.sh (auth-service image)",
18261
+ "bash build-devbox.sh (devbox image)",
18262
+ "bash build-host-cp.sh (host-cp image)",
18263
+ "smoke (docker create + inspect)",
18264
+ "atomic 6-tag swap (canonical -> :olam-rollback; :olam-next -> canonical)",
18265
+ "docker compose --force-recreate host-cp + AuthContainerController.start auth",
18266
+ "poll /api/version/status until SHAs match"
17543
18267
  ]
17544
18268
  ];
17545
18269
  printInfo("Steps", steps.join(", "));
@@ -17554,10 +18278,6 @@ async function handleUpgrade(opts) {
17554
18278
  return;
17555
18279
  }
17556
18280
  }
17557
- if (opts.history) {
17558
- handleHistory(parseHistoryOpts({ n: opts.historyN, json: opts.historyJson }));
17559
- return;
17560
- }
17561
18281
  if (opts.rollback) {
17562
18282
  return await handleRollback();
17563
18283
  }
@@ -17641,11 +18361,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
17641
18361
  process.once("SIGINT", releaseOnSignal);
17642
18362
  process.once("SIGTERM", releaseOnSignal);
17643
18363
  try {
17644
- process.stdout.write(` ${pc15.dim("rollback retag (3 ops)".padEnd(34))}`);
18364
+ process.stdout.write(` ${pc16.dim("rollback retag (3 ops)".padEnd(34))}`);
17645
18365
  const swapStart = Date.now();
17646
18366
  const swapResult = performRollbackSwap(PRODUCTION_SWAP_PLAN);
17647
18367
  const swapDur = `${((Date.now() - swapStart) / 1e3).toFixed(1)}s`;
17648
- process.stdout.write(`${swapResult.ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${swapDur}
18368
+ process.stdout.write(`${swapResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${swapDur}
17649
18369
  `);
17650
18370
  if (!swapResult.ok) {
17651
18371
  printError(`Rollback retag failed: ${swapResult.summary}`);
@@ -17656,11 +18376,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
17656
18376
  const cwd = process.cwd();
17657
18377
  const composeFile = path28.join(cwd, "packages/host-cp/compose.yaml");
17658
18378
  const authSecret = readAuthSecret2();
17659
- process.stdout.write(` ${pc15.dim("docker compose recreate host-cp".padEnd(34))}`);
18379
+ process.stdout.write(` ${pc16.dim("docker compose recreate host-cp".padEnd(34))}`);
17660
18380
  const composeStart = Date.now();
17661
18381
  const composeResult = runCompose(["up", "-d", "--force-recreate", "host-cp"], composeFile, buildComposeEnv(authSecret));
17662
18382
  const composeDur = `${((Date.now() - composeStart) / 1e3).toFixed(1)}s`;
17663
- process.stdout.write(`${composeResult.ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${composeDur}
18383
+ process.stdout.write(`${composeResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${composeDur}
17664
18384
  `);
17665
18385
  if (!composeResult.ok) {
17666
18386
  printError(
@@ -17671,10 +18391,10 @@ Canonical tags are at :olam-rollback (good); container restart pending. Manually
17671
18391
  process.exitCode = 1;
17672
18392
  return;
17673
18393
  }
17674
- process.stdout.write(` ${pc15.dim("recreate auth-service".padEnd(34))}`);
18394
+ process.stdout.write(` ${pc16.dim("recreate auth-service".padEnd(34))}`);
17675
18395
  const authResult = await recreateAuthService();
17676
18396
  const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
17677
- process.stdout.write(`${authResult.ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${authDur}
18397
+ process.stdout.write(`${authResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${authDur}
17678
18398
  `);
17679
18399
  if (!authResult.ok) {
17680
18400
  printError(`Auth-service recreate failed: ${authResult.error ?? "unknown"}`);
@@ -17826,13 +18546,24 @@ ${spaResult.stderr}`);
17826
18546
  { label: "bash build-devbox.sh", relPath: "packages/adapters/src/docker/build-devbox.sh", tee: true },
17827
18547
  { label: "bash build-host-cp.sh", relPath: "packages/adapters/src/docker/build-host-cp.sh", tee: false }
17828
18548
  ];
18549
+ const { resolveBuildScript: resolveBuildScript2, MissingBuildScriptError: MissingBuildScriptError2 } = await Promise.resolve().then(() => (init_install_root(), install_root_exports));
17829
18550
  for (const step of buildScripts) {
17830
- const scriptPath = path28.join(cwd, step.relPath);
18551
+ let scriptPath;
18552
+ try {
18553
+ scriptPath = resolveBuildScript2({ scriptRelPath: step.relPath });
18554
+ } catch (err) {
18555
+ if (err instanceof MissingBuildScriptError2) {
18556
+ printError(err.message);
18557
+ process.exitCode = 1;
18558
+ return;
18559
+ }
18560
+ throw err;
18561
+ }
17831
18562
  if (step.tee) {
17832
- process.stdout.write(` ${pc15.dim(step.label.padEnd(34))}
18563
+ process.stdout.write(` ${pc16.dim(step.label.padEnd(34))}
17833
18564
  `);
17834
18565
  const start = Date.now();
17835
- const result = spawnSync7("bash", [scriptPath], {
18566
+ const result = spawnSync9("bash", [scriptPath], {
17836
18567
  stdio: "inherit",
17837
18568
  cwd,
17838
18569
  env: { ...process.env, ...olamTagEnv }
@@ -17840,7 +18571,7 @@ ${spaResult.stderr}`);
17840
18571
  const durationMs = Date.now() - start;
17841
18572
  const ok = result.status === 0 && result.error === void 0;
17842
18573
  const dur = `${(durationMs / 1e3).toFixed(1)}s`;
17843
- process.stdout.write(` ${pc15.dim(step.label.padEnd(34))}${ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${dur}
18574
+ process.stdout.write(` ${pc16.dim(step.label.padEnd(34))}${ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${dur}
17844
18575
  `);
17845
18576
  timings.push({ label: step.label, durationMs });
17846
18577
  if (!ok) {
@@ -17866,7 +18597,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
17866
18597
  }
17867
18598
  for (const t of timings) logRow.durations_ms[t.label] = t.durationMs;
17868
18599
  const smokeStart = Date.now();
17869
- process.stdout.write(` ${pc15.dim("smoke (docker create + inspect)".padEnd(34))}`);
18600
+ process.stdout.write(` ${pc16.dim("smoke (docker create + inspect)".padEnd(34))}`);
17870
18601
  const smokeImages = [
17871
18602
  "olam-auth:olam-next",
17872
18603
  "olam-devbox:olam-next",
@@ -17876,7 +18607,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
17876
18607
  const smokeFailures = smokeResults.filter((r) => !r.ok);
17877
18608
  const smokeDurationMs = Date.now() - smokeStart;
17878
18609
  const smokeDur = `${(smokeDurationMs / 1e3).toFixed(1)}s`;
17879
- process.stdout.write(`${smokeFailures.length === 0 ? pc15.green("\u2713") : pc15.red("\u2717")} ${smokeDur}
18610
+ process.stdout.write(`${smokeFailures.length === 0 ? pc16.green("\u2713") : pc16.red("\u2717")} ${smokeDur}
17880
18611
  `);
17881
18612
  timings.push({ label: "smoke", durationMs: smokeDurationMs });
17882
18613
  if (smokeFailures.length > 0) {
@@ -17903,12 +18634,12 @@ Recovery options:
17903
18634
  process.exitCode = 1;
17904
18635
  return;
17905
18636
  }
17906
- process.stdout.write(` ${pc15.dim("atomic 6-tag swap".padEnd(34))}`);
18637
+ process.stdout.write(` ${pc16.dim("atomic 6-tag swap".padEnd(34))}`);
17907
18638
  const swapStart = Date.now();
17908
18639
  const swapResult = performAtomicSwap(PRODUCTION_SWAP_PLAN);
17909
18640
  const swapDurationMs = Date.now() - swapStart;
17910
18641
  const swapDur = `${(swapDurationMs / 1e3).toFixed(1)}s`;
17911
- process.stdout.write(`${swapResult.ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${swapDur}
18642
+ process.stdout.write(`${swapResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${swapDur}
17912
18643
  `);
17913
18644
  timings.push({ label: "atomic swap", durationMs: swapDurationMs });
17914
18645
  if (!swapResult.ok) {
@@ -17918,7 +18649,7 @@ Recovery options:
17918
18649
  }
17919
18650
  printInfo("Swap", swapResult.summary);
17920
18651
  const composeFile = path28.join(cwd, "packages/host-cp/compose.yaml");
17921
- process.stdout.write(` ${pc15.dim("docker compose recreate".padEnd(34))}`);
18652
+ process.stdout.write(` ${pc16.dim("docker compose recreate".padEnd(34))}`);
17922
18653
  const composeStart = Date.now();
17923
18654
  const composeResult = runCompose(
17924
18655
  ["up", "-d", "--force-recreate"],
@@ -17928,7 +18659,7 @@ Recovery options:
17928
18659
  const composeDurationMs = Date.now() - composeStart;
17929
18660
  const composeOk = composeResult.ok;
17930
18661
  const composeDur = `${(composeDurationMs / 1e3).toFixed(1)}s`;
17931
- process.stdout.write(`${composeOk ? pc15.green("\u2713") : pc15.red("\u2717")} ${composeDur}
18662
+ process.stdout.write(`${composeOk ? pc16.green("\u2713") : pc16.red("\u2717")} ${composeDur}
17932
18663
  `);
17933
18664
  timings.push({ label: "container recreate", durationMs: composeDurationMs });
17934
18665
  if (!composeOk) {
@@ -17944,10 +18675,10 @@ Recovery options:
17944
18675
  process.exitCode = 1;
17945
18676
  return;
17946
18677
  }
17947
- process.stdout.write(` ${pc15.dim("recreate auth-service".padEnd(34))}`);
18678
+ process.stdout.write(` ${pc16.dim("recreate auth-service".padEnd(34))}`);
17948
18679
  const authResult = await recreateAuthService();
17949
18680
  const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
17950
- process.stdout.write(`${authResult.ok ? pc15.green("\u2713") : pc15.red("\u2717")} ${authDur}
18681
+ process.stdout.write(`${authResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${authDur}
17951
18682
  `);
17952
18683
  timings.push({ label: "auth recreate", durationMs: authResult.durationMs });
17953
18684
  if (!authResult.ok) {
@@ -17962,12 +18693,12 @@ Recovery options:
17962
18693
  process.exitCode = 1;
17963
18694
  return;
17964
18695
  }
17965
- process.stdout.write(` ${pc15.dim("waiting for /health".padEnd(34))}`);
18696
+ process.stdout.write(` ${pc16.dim("waiting for /health".padEnd(34))}`);
17966
18697
  const healthStart = Date.now();
17967
18698
  const healthy = await waitForHealth(1e4);
17968
18699
  const healthDurationMs = Date.now() - healthStart;
17969
18700
  const healthDur = `${(healthDurationMs / 1e3).toFixed(1)}s`;
17970
- process.stdout.write(`${healthy ? pc15.green("\u2713") : pc15.yellow("?")} ${healthDur}
18701
+ process.stdout.write(`${healthy ? pc16.green("\u2713") : pc16.yellow("?")} ${healthDur}
17971
18702
  `);
17972
18703
  timings.push({ label: "/health", durationMs: healthDurationMs });
17973
18704
  if (!healthy) {
@@ -17975,12 +18706,12 @@ Recovery options:
17975
18706
  "Host CP started but /health did not respond within 10s.\n \u2022 Check: docker logs olam-host-cp\n \u2022 If the new SHA is broken: `olam upgrade --rollback` restores the prior set in <30s."
17976
18707
  );
17977
18708
  }
17978
- process.stdout.write(` ${pc15.dim("verify /version/status round-trip".padEnd(34))}`);
18709
+ process.stdout.write(` ${pc16.dim("verify /version/status round-trip".padEnd(34))}`);
17979
18710
  const versionStart = Date.now();
17980
18711
  const versionMatch = await waitForVersionMatch(_targetSha, 6e4);
17981
18712
  const versionDurationMs = Date.now() - versionStart;
17982
18713
  const versionDur = `${(versionDurationMs / 1e3).toFixed(1)}s`;
17983
- process.stdout.write(`${versionMatch.matched ? pc15.green("\u2713") : pc15.yellow("?")} ${versionDur}
18714
+ process.stdout.write(`${versionMatch.matched ? pc16.green("\u2713") : pc16.yellow("?")} ${versionDur}
17984
18715
  `);
17985
18716
  timings.push({ label: "/version/status round-trip", durationMs: versionDurationMs });
17986
18717
  if (!versionMatch.matched) {
@@ -18004,7 +18735,9 @@ function printTimings2(timings) {
18004
18735
  printInfo("total", `${(total / 1e3).toFixed(1)}s`);
18005
18736
  }
18006
18737
  function registerUpgrade(program2) {
18007
- program2.command("upgrade").description("Self-upgrade the local Olam dev stack (pull + rebuild + restart all three components)").option("-y, --yes", "Skip the confirmation prompt").option("--skip-image", "Skip docker image rebuild + container recreate (source rebuild only)").option(
18738
+ program2.command("upgrade").description(
18739
+ "Upgrade the local Olam stack to the CLI's pinned image digests. Default: pull pre-built images from ghcr.io and recreate containers. Use --from-source for the legacy monorepo build path (requires OLAM_DEV=1)."
18740
+ ).option("-y, --yes", "Skip the confirmation prompt").option("--skip-image", "Skip docker image rebuild + container recreate (source rebuild only)").option(
18008
18741
  "--skip-install",
18009
18742
  "Skip npm install entirely (use existing node_modules as-is). Useful when a native-module build failure blocks the normal upgrade path."
18010
18743
  ).option("--branch <name>", "Switch to this branch before pulling (refuses if working tree is dirty)").option(
@@ -18019,24 +18752,47 @@ function registerUpgrade(program2) {
18019
18752
  ).option(
18020
18753
  "--history",
18021
18754
  "Print the upgrade history (~/.olam/upgrade.log) and exit.\n No upgrade is performed."
18022
- ).option("-n <count>", "Number of history rows to print (default 10)", "10").option("--json", "Emit history as JSONL instead of a table").action(async (opts) => {
18023
- await handleUpgrade(parseUpgradeOpts(opts));
18755
+ ).option("-n <count>", "Number of history rows to print (default 10)", "10").option("--json", "Emit history as JSONL instead of a table").option(
18756
+ "--from-source",
18757
+ "Build host-cp + auth + devbox from monorepo source (legacy path).\n Requires OLAM_DEV=1 + a `packages/` sibling at the install root.\n Default (no flag): pull pre-built images from ghcr.io by digest."
18758
+ ).action(async (opts) => {
18759
+ const parsed = parseUpgradeOpts(opts);
18760
+ if (parsed.fromSource) {
18761
+ if (!isDevMode()) {
18762
+ printError(new MissingBuildScriptError("packages/adapters/src/docker/build-*.sh").message);
18763
+ process.exitCode = 1;
18764
+ return;
18765
+ }
18766
+ await handleUpgrade(parsed);
18767
+ return;
18768
+ }
18769
+ if (parsed.history) {
18770
+ handleHistory(parseHistoryOpts({ n: parsed.historyN, json: parsed.historyJson }));
18771
+ return;
18772
+ }
18773
+ try {
18774
+ const result = await runUpgradePullByDigest();
18775
+ process.exitCode = result.exitCode;
18776
+ } catch (err) {
18777
+ printError(err instanceof Error ? err.message : String(err));
18778
+ process.exitCode = EXIT_GENERIC_ERROR;
18779
+ }
18024
18780
  });
18025
18781
  }
18026
18782
 
18027
18783
  // src/commands/logs.ts
18028
18784
  import * as http3 from "node:http";
18029
- import pc16 from "picocolors";
18785
+ import pc17 from "picocolors";
18030
18786
  init_context();
18031
18787
  var HOST_CP_PORT2 = 19e3;
18032
18788
  function colorLine(line) {
18033
- if (/\bERROR\b/.test(line)) return pc16.red(line);
18034
- if (/\bWARN\b/.test(line)) return pc16.yellow(line);
18035
- if (/\bINFO\b/.test(line)) return pc16.dim(line);
18789
+ if (/\bERROR\b/.test(line)) return pc17.red(line);
18790
+ if (/\bWARN\b/.test(line)) return pc17.yellow(line);
18791
+ if (/\bINFO\b/.test(line)) return pc17.dim(line);
18036
18792
  return line;
18037
18793
  }
18038
18794
  function formatLine(line, service, showService) {
18039
- const prefix = showService && service ? `${pc16.cyan(`[${service}]`)} ` : "";
18795
+ const prefix = showService && service ? `${pc17.cyan(`[${service}]`)} ` : "";
18040
18796
  return prefix + colorLine(line);
18041
18797
  }
18042
18798
  function parseSseEvent(raw) {
@@ -18152,8 +18908,8 @@ function registerLogs(program2) {
18152
18908
 
18153
18909
  // src/commands/ps.ts
18154
18910
  init_context();
18155
- import pc17 from "picocolors";
18156
- import { spawnSync as spawnSync8 } from "node:child_process";
18911
+ import pc18 from "picocolors";
18912
+ import { spawnSync as spawnSync10 } from "node:child_process";
18157
18913
  var SAFE_IDENT4 = /^[a-z0-9][a-z0-9-]{0,63}$/;
18158
18914
  function parseDockerTop(stdout) {
18159
18915
  const trimmed = stdout.trim();
@@ -18213,18 +18969,18 @@ function printTable(rows) {
18213
18969
  const fixedWidth = 5 + 1 + 8 + 1 + 6 + 1 + 6 + 1 + 10 + 1 + 6 + 1;
18214
18970
  const cmdWidth = Math.max(20, cols - fixedWidth);
18215
18971
  const header = [
18216
- pc17.bold(pc17.dim("PID".padEnd(5))),
18217
- pc17.bold(pc17.dim("USER".padEnd(8))),
18218
- pc17.bold(pc17.dim("%CPU".padEnd(6))),
18219
- pc17.bold(pc17.dim("%MEM".padEnd(6))),
18220
- pc17.bold(pc17.dim("STARTED".padEnd(10))),
18221
- pc17.bold(pc17.dim("STATE".padEnd(6))),
18222
- pc17.bold(pc17.dim("COMMAND"))
18972
+ pc18.bold(pc18.dim("PID".padEnd(5))),
18973
+ pc18.bold(pc18.dim("USER".padEnd(8))),
18974
+ pc18.bold(pc18.dim("%CPU".padEnd(6))),
18975
+ pc18.bold(pc18.dim("%MEM".padEnd(6))),
18976
+ pc18.bold(pc18.dim("STARTED".padEnd(10))),
18977
+ pc18.bold(pc18.dim("STATE".padEnd(6))),
18978
+ pc18.bold(pc18.dim("COMMAND"))
18223
18979
  ].join(" ");
18224
18980
  console.log(header);
18225
18981
  for (const row of rows) {
18226
18982
  const cmd = row.command.length > cmdWidth ? row.command.slice(0, cmdWidth - 1) + "\u2026" : row.command;
18227
- const stateFn = row.state.startsWith("R") ? pc17.green : row.state.startsWith("Z") ? pc17.red : pc17.dim;
18983
+ const stateFn = row.state.startsWith("R") ? pc18.green : row.state.startsWith("Z") ? pc18.red : pc18.dim;
18228
18984
  console.log([
18229
18985
  row.pid.padEnd(5),
18230
18986
  row.user.slice(0, 8).padEnd(8),
@@ -18253,7 +19009,7 @@ function registerPs(program2) {
18253
19009
  const containerName = `olam-${worldId}-devbox`;
18254
19010
  let watchInterval;
18255
19011
  function fetchAndPrint() {
18256
- const result = spawnSync8(
19012
+ const result = spawnSync10(
18257
19013
  "docker",
18258
19014
  ["top", containerName, "pid", "user", "pcpu", "pmem", "stime", "stat", "cmd"],
18259
19015
  { encoding: "utf-8", timeout: 3e3 }
@@ -18273,7 +19029,7 @@ function registerPs(program2) {
18273
19029
  printTable(rows);
18274
19030
  if (opts.watch) {
18275
19031
  process.stdout.write(`
18276
- ${pc17.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
19032
+ ${pc18.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
18277
19033
  `);
18278
19034
  }
18279
19035
  }
@@ -18393,7 +19149,7 @@ function registerKeys(program2) {
18393
19149
  import * as fs27 from "node:fs";
18394
19150
  import * as path31 from "node:path";
18395
19151
  import { execSync as execSync9 } from "node:child_process";
18396
- import pc18 from "picocolors";
19152
+ import pc19 from "picocolors";
18397
19153
 
18398
19154
  // ../core/src/world/snapshot.ts
18399
19155
  import * as crypto6 from "node:crypto";
@@ -18562,7 +19318,7 @@ async function handleCreate2(worldId, kindArg) {
18562
19318
  const label = r.repo ? `${r.kind}/${r.repo}` : r.kind;
18563
19319
  if (r.ok) {
18564
19320
  printSuccess(`${label}`);
18565
- console.log(` ${pc18.dim(r.tarPath)}`);
19321
+ console.log(` ${pc19.dim(r.tarPath)}`);
18566
19322
  } else {
18567
19323
  printWarning(`${label}: ${r.msg ?? "skipped"}`);
18568
19324
  }
@@ -18712,7 +19468,7 @@ function handleList2(worldIdFilter) {
18712
19468
  const entries = listSnapshots(worldIdFilter);
18713
19469
  if (entries.length === 0) {
18714
19470
  const where = worldIdFilter ? ` for world "${worldIdFilter}"` : "";
18715
- console.log(pc18.dim(`No snapshots found${where}. Run \`olam world snapshot create <worldId>\` first.`));
19471
+ console.log(pc19.dim(`No snapshots found${where}. Run \`olam world snapshot create <worldId>\` first.`));
18716
19472
  return;
18717
19473
  }
18718
19474
  printHeader(`${entries.length} snapshot(s)`);
@@ -18721,15 +19477,15 @@ function handleList2(worldIdFilter) {
18721
19477
  for (const entry of entries) {
18722
19478
  const { manifest } = entry;
18723
19479
  if (manifest.worldId !== lastWorldId) {
18724
- console.log(pc18.bold(manifest.worldId));
19480
+ console.log(pc19.bold(manifest.worldId));
18725
19481
  lastWorldId = manifest.worldId;
18726
19482
  }
18727
19483
  const repo = manifest.repo ?? "(all repos)";
18728
19484
  const size = formatBytes(manifest.sizeBytes);
18729
19485
  const age = formatAge2(entry.ageMs);
18730
- const kindColor = manifest.kind === "gems" ? pc18.magenta(manifest.kind) : manifest.kind === "node" ? pc18.cyan(manifest.kind) : pc18.yellow(manifest.kind);
19486
+ const kindColor = manifest.kind === "gems" ? pc19.magenta(manifest.kind) : manifest.kind === "node" ? pc19.cyan(manifest.kind) : pc19.yellow(manifest.kind);
18731
19487
  console.log(
18732
- ` ${kindColor.padEnd(6)} ${pc18.dim(repo.padEnd(24))} ${manifest.fingerprint} ${size.padStart(8)} ${age}`
19488
+ ` ${kindColor.padEnd(6)} ${pc19.dim(repo.padEnd(24))} ${manifest.fingerprint} ${size.padStart(8)} ${age}`
18733
19489
  );
18734
19490
  }
18735
19491
  console.log();
@@ -18766,8 +19522,8 @@ init_context();
18766
19522
  import * as fs29 from "node:fs";
18767
19523
  import * as os17 from "node:os";
18768
19524
  import * as path33 from "node:path";
18769
- import { spawnSync as spawnSync9 } from "node:child_process";
18770
- import ora5 from "ora";
19525
+ import { spawnSync as spawnSync11 } from "node:child_process";
19526
+ import ora8 from "ora";
18771
19527
 
18772
19528
  // src/commands/refresh-helpers.ts
18773
19529
  import * as fs28 from "node:fs";
@@ -18810,7 +19566,7 @@ var RESTART_TIMEOUT_S = 30;
18810
19566
  var HEALTH_POLL_MS = 500;
18811
19567
  var HEALTH_TIMEOUT_MS = 3e4;
18812
19568
  function docker(args) {
18813
- const result = spawnSync9("docker", args, {
19569
+ const result = spawnSync11("docker", args, {
18814
19570
  encoding: "utf-8",
18815
19571
  stdio: ["ignore", "pipe", "pipe"]
18816
19572
  });
@@ -18950,7 +19706,7 @@ Run \`olam refresh\` from the olam repo root.`
18950
19706
  process.stdout.write("\n");
18951
19707
  const results = [];
18952
19708
  for (const world of worldsToRefresh) {
18953
- const spinner = ora5(`${world.name}: copying CP source...`).start();
19709
+ const spinner = ora8(`${world.name}: copying CP source...`).start();
18954
19710
  try {
18955
19711
  const result = await refreshWorld(
18956
19712
  world.id,
@@ -19013,7 +19769,24 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
19013
19769
 
19014
19770
  // src/index.ts
19015
19771
  var program = new Command();
19016
- program.name("olam").description("Olam \u2014 isolated development worlds with thought graph capture").version("0.1.0");
19772
+ function readCliVersion() {
19773
+ try {
19774
+ const here = path35.dirname(fileURLToPath4(import.meta.url));
19775
+ for (const candidate of [
19776
+ path35.join(here, "package.json"),
19777
+ path35.join(here, "..", "package.json"),
19778
+ path35.join(here, "..", "..", "package.json")
19779
+ ]) {
19780
+ if (fs31.existsSync(candidate)) {
19781
+ const pkg = JSON.parse(fs31.readFileSync(candidate, "utf-8"));
19782
+ if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
19783
+ }
19784
+ }
19785
+ } catch {
19786
+ }
19787
+ return "0.0.0-unknown";
19788
+ }
19789
+ program.name("olam").description("Olam \u2014 isolated development worlds with thought graph capture").version(readCliVersion());
19017
19790
  registerInit(program);
19018
19791
  registerInstall(program);
19019
19792
  registerAuth(program);
@@ -19036,4 +19809,5 @@ registerPs(program);
19036
19809
  registerKeys(program);
19037
19810
  registerWorldSnapshot(program);
19038
19811
  registerRefresh(program);
19812
+ registerBootstrap(program);
19039
19813
  program.parse();