@pleri/olam-cli 0.1.14 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/auth-upgrade.test.js +236 -1
- package/dist/__tests__/auth-upgrade.test.js.map +1 -1
- package/dist/__tests__/install-root.test.d.ts +2 -0
- package/dist/__tests__/install-root.test.d.ts.map +1 -0
- package/dist/__tests__/install-root.test.js +119 -0
- package/dist/__tests__/install-root.test.js.map +1 -0
- package/dist/__tests__/upgrade.test.js +292 -2
- package/dist/__tests__/upgrade.test.js.map +1 -1
- package/dist/commands/__tests__/bootstrap.test.d.ts +2 -0
- package/dist/commands/__tests__/bootstrap.test.d.ts.map +1 -0
- package/dist/commands/__tests__/bootstrap.test.js +288 -0
- package/dist/commands/__tests__/bootstrap.test.js.map +1 -0
- package/dist/commands/__tests__/upgrade.rollback.test.js +1 -1
- package/dist/commands/__tests__/upgrade.swap.test.js +1 -1
- package/dist/commands/auth-upgrade.d.ts +41 -0
- package/dist/commands/auth-upgrade.d.ts.map +1 -1
- package/dist/commands/auth-upgrade.js +158 -6
- package/dist/commands/auth-upgrade.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +95 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +329 -0
- package/dist/commands/bootstrap.js.map +1 -0
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +19 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/upgrade.d.ts +76 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +215 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +8 -0
- package/dist/index.js +951 -201
- package/dist/index.js.map +1 -1
- package/dist/install-root.d.ts +74 -0
- package/dist/install-root.d.ts.map +1 -0
- package/dist/install-root.js +98 -0
- package/dist/install-root.js.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5088,7 +5088,7 @@ async function safeText(res) {
|
|
|
5088
5088
|
}
|
|
5089
5089
|
}
|
|
5090
5090
|
function sleep(ms) {
|
|
5091
|
-
return new Promise((
|
|
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({
|
|
@@ -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((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
6237
|
+
return new Promise((resolve8, reject) => {
|
|
6238
6238
|
client.on("ready", () => {
|
|
6239
6239
|
this.connections.set(host, client);
|
|
6240
|
-
|
|
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}`));
|
|
@@ -11420,7 +11420,7 @@ function isCloudflaredAvailable() {
|
|
|
11420
11420
|
}
|
|
11421
11421
|
}
|
|
11422
11422
|
function startTunnel(port) {
|
|
11423
|
-
return new Promise((
|
|
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
|
-
|
|
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((
|
|
11532
|
-
this.server.on("listening",
|
|
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((
|
|
11578
|
-
this.server.close(() =>
|
|
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, {
|
|
@@ -11900,7 +11948,7 @@ var init_checksum = __esm({
|
|
|
11900
11948
|
import { Command } from "commander";
|
|
11901
11949
|
import * as fs31 from "node:fs";
|
|
11902
11950
|
import * as path35 from "node:path";
|
|
11903
|
-
import { fileURLToPath as
|
|
11951
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
11904
11952
|
|
|
11905
11953
|
// src/commands/init.ts
|
|
11906
11954
|
import * as fs5 from "node:fs";
|
|
@@ -12504,9 +12552,9 @@ function registerInstall(program2) {
|
|
|
12504
12552
|
|
|
12505
12553
|
// src/commands/auth.ts
|
|
12506
12554
|
init_auth();
|
|
12507
|
-
import
|
|
12555
|
+
import pc8 from "picocolors";
|
|
12508
12556
|
import * as readline from "node:readline/promises";
|
|
12509
|
-
import { spawn as
|
|
12557
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
12510
12558
|
|
|
12511
12559
|
// src/commands/auth-status.ts
|
|
12512
12560
|
import * as fs8 from "node:fs";
|
|
@@ -12558,6 +12606,8 @@ init_auth();
|
|
|
12558
12606
|
// src/exit-codes.ts
|
|
12559
12607
|
var EXIT_GENERIC_ERROR = 1;
|
|
12560
12608
|
var EXIT_PLERI_NOT_CONFIGURED = 2;
|
|
12609
|
+
var EXIT_BOOTSTRAP_PULL_FAILED = 3;
|
|
12610
|
+
var EXIT_PROTOCOL_MISMATCH = 4;
|
|
12561
12611
|
var EXIT_AUTH_NEEDS_ATTENTION = 5;
|
|
12562
12612
|
|
|
12563
12613
|
// src/commands/auth-status.ts
|
|
@@ -12683,8 +12733,9 @@ async function runAuthStatus(getStatus) {
|
|
|
12683
12733
|
// src/commands/auth-upgrade.ts
|
|
12684
12734
|
import * as fs20 from "node:fs";
|
|
12685
12735
|
import * as path23 from "node:path";
|
|
12686
|
-
import { spawnSync as
|
|
12687
|
-
import
|
|
12736
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
12737
|
+
import ora2 from "ora";
|
|
12738
|
+
import pc7 from "picocolors";
|
|
12688
12739
|
|
|
12689
12740
|
// src/commands/host-cp.ts
|
|
12690
12741
|
init_dist();
|
|
@@ -13240,13 +13291,348 @@ async function handleDeregister(opts) {
|
|
|
13240
13291
|
|
|
13241
13292
|
// src/commands/auth-upgrade.ts
|
|
13242
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();
|
|
13243
13628
|
var AUTH_PORT = 9999;
|
|
13244
13629
|
var AUTH_HEALTH_URL = `http://127.0.0.1:${AUTH_PORT}/health`;
|
|
13245
13630
|
var AUTH_ADD_URL = `http://127.0.0.1:${AUTH_PORT}/credentials/add`;
|
|
13246
13631
|
function parseAuthUpgradeOpts(raw) {
|
|
13247
13632
|
return {
|
|
13248
13633
|
yes: raw.yes === true,
|
|
13249
|
-
skipRecreate: raw.skipRecreate === true
|
|
13634
|
+
skipRecreate: raw.skipRecreate === true,
|
|
13635
|
+
fromSource: raw.fromSource === true
|
|
13250
13636
|
};
|
|
13251
13637
|
}
|
|
13252
13638
|
function validateAuthRepoRoot(cwd) {
|
|
@@ -13307,8 +13693,8 @@ async function smokeTestCodexProvider(authSecret) {
|
|
|
13307
13693
|
}
|
|
13308
13694
|
function runStep(label, cmd, args, opts = {}) {
|
|
13309
13695
|
const start = Date.now();
|
|
13310
|
-
process.stdout.write(` ${
|
|
13311
|
-
const result =
|
|
13696
|
+
process.stdout.write(` ${pc7.dim(label.padEnd(34))}`);
|
|
13697
|
+
const result = spawnSync6(cmd, [...args], {
|
|
13312
13698
|
encoding: "utf-8",
|
|
13313
13699
|
stdio: ["ignore", "pipe", "pipe"],
|
|
13314
13700
|
cwd: opts.cwd ?? process.cwd(),
|
|
@@ -13317,7 +13703,7 @@ function runStep(label, cmd, args, opts = {}) {
|
|
|
13317
13703
|
const durationMs = Date.now() - start;
|
|
13318
13704
|
const ok = result.status === 0 && result.error === void 0;
|
|
13319
13705
|
const dur = `${(durationMs / 1e3).toFixed(1)}s`;
|
|
13320
|
-
process.stdout.write(`${ok ?
|
|
13706
|
+
process.stdout.write(`${ok ? pc7.green("\u2713") : pc7.red("\u2717")} ${dur}
|
|
13321
13707
|
`);
|
|
13322
13708
|
return {
|
|
13323
13709
|
ok,
|
|
@@ -13330,10 +13716,10 @@ async function confirm(message) {
|
|
|
13330
13716
|
if (!process.stdin.isTTY) return true;
|
|
13331
13717
|
const { createInterface: createInterface2 } = await import("node:readline");
|
|
13332
13718
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
13333
|
-
return new Promise((
|
|
13719
|
+
return new Promise((resolve8) => {
|
|
13334
13720
|
rl.question(`${message} [y/N] `, (answer) => {
|
|
13335
13721
|
rl.close();
|
|
13336
|
-
|
|
13722
|
+
resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
13337
13723
|
});
|
|
13338
13724
|
});
|
|
13339
13725
|
}
|
|
@@ -13345,6 +13731,126 @@ function printTimings(timings) {
|
|
|
13345
13731
|
}
|
|
13346
13732
|
printInfo("total", `${(total / 1e3).toFixed(1)}s`);
|
|
13347
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
|
+
}
|
|
13348
13854
|
async function handleAuthUpgrade(opts) {
|
|
13349
13855
|
const cwd = process.cwd();
|
|
13350
13856
|
const rootCheck = validateAuthRepoRoot(cwd);
|
|
@@ -13372,7 +13878,17 @@ async function handleAuthUpgrade(opts) {
|
|
|
13372
13878
|
}
|
|
13373
13879
|
}
|
|
13374
13880
|
const timings = [];
|
|
13375
|
-
|
|
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
|
+
}
|
|
13376
13892
|
const imageResult = runStep("bash build-auth.sh", "bash", [buildScript], { cwd });
|
|
13377
13893
|
timings.push({ label: "docker image build", durationMs: imageResult.durationMs });
|
|
13378
13894
|
if (!imageResult.ok) {
|
|
@@ -13388,49 +13904,49 @@ ${imageResult.stderr}`);
|
|
|
13388
13904
|
return;
|
|
13389
13905
|
}
|
|
13390
13906
|
const stopStart = Date.now();
|
|
13391
|
-
process.stdout.write(` ${
|
|
13392
|
-
|
|
13907
|
+
process.stdout.write(` ${pc7.dim("docker stop olam-auth".padEnd(34))}`);
|
|
13908
|
+
spawnSync6("docker", ["stop", "olam-auth"], {
|
|
13393
13909
|
encoding: "utf-8",
|
|
13394
13910
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13395
13911
|
});
|
|
13396
13912
|
const stopDurationMs = Date.now() - stopStart;
|
|
13397
|
-
process.stdout.write(`${
|
|
13913
|
+
process.stdout.write(`${pc7.green("\u2713")} ${(stopDurationMs / 1e3).toFixed(1)}s
|
|
13398
13914
|
`);
|
|
13399
13915
|
timings.push({ label: "container stop", durationMs: stopDurationMs });
|
|
13400
13916
|
const rmStart = Date.now();
|
|
13401
|
-
process.stdout.write(` ${
|
|
13402
|
-
|
|
13917
|
+
process.stdout.write(` ${pc7.dim("docker rm olam-auth".padEnd(34))}`);
|
|
13918
|
+
spawnSync6("docker", ["rm", "olam-auth"], {
|
|
13403
13919
|
encoding: "utf-8",
|
|
13404
13920
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13405
13921
|
});
|
|
13406
13922
|
const rmDurationMs = Date.now() - rmStart;
|
|
13407
|
-
process.stdout.write(`${
|
|
13923
|
+
process.stdout.write(`${pc7.green("\u2713")} ${(rmDurationMs / 1e3).toFixed(1)}s
|
|
13408
13924
|
`);
|
|
13409
13925
|
timings.push({ label: "container remove", durationMs: rmDurationMs });
|
|
13410
13926
|
const startStart = Date.now();
|
|
13411
|
-
process.stdout.write(` ${
|
|
13927
|
+
process.stdout.write(` ${pc7.dim("docker run olam-auth:local".padEnd(34))}`);
|
|
13412
13928
|
try {
|
|
13413
13929
|
const controller = new AuthContainerController();
|
|
13414
13930
|
controller.start();
|
|
13415
13931
|
const startDurationMs = Date.now() - startStart;
|
|
13416
|
-
process.stdout.write(`${
|
|
13932
|
+
process.stdout.write(`${pc7.green("\u2713")} ${(startDurationMs / 1e3).toFixed(1)}s
|
|
13417
13933
|
`);
|
|
13418
13934
|
timings.push({ label: "container start", durationMs: startDurationMs });
|
|
13419
13935
|
} catch (err) {
|
|
13420
13936
|
const startDurationMs = Date.now() - startStart;
|
|
13421
|
-
process.stdout.write(`${
|
|
13937
|
+
process.stdout.write(`${pc7.red("\u2717")} ${(startDurationMs / 1e3).toFixed(1)}s
|
|
13422
13938
|
`);
|
|
13423
13939
|
timings.push({ label: "container start", durationMs: startDurationMs });
|
|
13424
13940
|
printError(`Failed to start auth container: ${err instanceof Error ? err.message : String(err)}`);
|
|
13425
13941
|
process.exitCode = 1;
|
|
13426
13942
|
return;
|
|
13427
13943
|
}
|
|
13428
|
-
process.stdout.write(` ${
|
|
13944
|
+
process.stdout.write(` ${pc7.dim("waiting for /health".padEnd(34))}`);
|
|
13429
13945
|
const healthStart = Date.now();
|
|
13430
13946
|
const healthy = await waitForAuthHealth(1e4);
|
|
13431
13947
|
const healthDurationMs = Date.now() - healthStart;
|
|
13432
13948
|
const healthDur = `${(healthDurationMs / 1e3).toFixed(1)}s`;
|
|
13433
|
-
process.stdout.write(`${healthy ?
|
|
13949
|
+
process.stdout.write(`${healthy ? pc7.green("\u2713") : pc7.yellow("?")} ${healthDur}
|
|
13434
13950
|
`);
|
|
13435
13951
|
timings.push({ label: "/health", durationMs: healthDurationMs });
|
|
13436
13952
|
if (!healthy) {
|
|
@@ -13439,13 +13955,13 @@ ${imageResult.stderr}`);
|
|
|
13439
13955
|
printTimings(timings);
|
|
13440
13956
|
return;
|
|
13441
13957
|
}
|
|
13442
|
-
process.stdout.write(` ${
|
|
13958
|
+
process.stdout.write(` ${pc7.dim("smoke test: codex provider".padEnd(34))}`);
|
|
13443
13959
|
const smokeStart = Date.now();
|
|
13444
13960
|
const authSecret = readAuthSecret2();
|
|
13445
13961
|
const smoke = await smokeTestCodexProvider(authSecret);
|
|
13446
13962
|
const smokeDurationMs = Date.now() - smokeStart;
|
|
13447
13963
|
const smokeDur = `${(smokeDurationMs / 1e3).toFixed(1)}s`;
|
|
13448
|
-
process.stdout.write(`${smoke.ok ?
|
|
13964
|
+
process.stdout.write(`${smoke.ok ? pc7.green("\u2713") : pc7.red("\u2717")} ${smokeDur}
|
|
13449
13965
|
`);
|
|
13450
13966
|
timings.push({ label: "smoke test", durationMs: smokeDurationMs });
|
|
13451
13967
|
if (!smoke.ok) {
|
|
@@ -13460,8 +13976,31 @@ ${imageResult.stderr}`);
|
|
|
13460
13976
|
printTimings(timings);
|
|
13461
13977
|
}
|
|
13462
13978
|
function registerAuthUpgrade(auth) {
|
|
13463
|
-
auth.command("upgrade").description(
|
|
13464
|
-
|
|
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
|
+
}
|
|
13465
14004
|
});
|
|
13466
14005
|
}
|
|
13467
14006
|
|
|
@@ -13469,7 +14008,7 @@ function registerAuthUpgrade(auth) {
|
|
|
13469
14008
|
function openBrowser(url) {
|
|
13470
14009
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
13471
14010
|
try {
|
|
13472
|
-
const child =
|
|
14011
|
+
const child = spawn3(cmd, [url], { detached: true, stdio: "ignore" });
|
|
13473
14012
|
child.unref();
|
|
13474
14013
|
} catch {
|
|
13475
14014
|
}
|
|
@@ -13509,7 +14048,7 @@ function registerAuth(program2) {
|
|
|
13509
14048
|
printInfo("Port", String(initial.port));
|
|
13510
14049
|
printInfo("Volume", "olam-auth-data");
|
|
13511
14050
|
console.log(`
|
|
13512
|
-
${
|
|
14051
|
+
${pc8.dim("Next: olam auth login")}`);
|
|
13513
14052
|
});
|
|
13514
14053
|
auth.command("down").description("Stop the auth container (tokens persist in the volume)").action(() => {
|
|
13515
14054
|
const controller = new AuthContainerController();
|
|
@@ -13534,15 +14073,15 @@ ${pc7.dim("Next: olam auth login")}`);
|
|
|
13534
14073
|
}
|
|
13535
14074
|
printHeader(`Credentials (${status.accounts.length})`);
|
|
13536
14075
|
if (status.accounts.length === 0) {
|
|
13537
|
-
console.log(` ${
|
|
14076
|
+
console.log(` ${pc8.dim("No credentials \u2014 run: olam auth login --label primary")}`);
|
|
13538
14077
|
return;
|
|
13539
14078
|
}
|
|
13540
14079
|
const stateColor = (s) => {
|
|
13541
|
-
if (s === "active") return
|
|
13542
|
-
if (s === "cooldown") return
|
|
13543
|
-
if (s === "expired") return
|
|
13544
|
-
if (s === "disabled") return
|
|
13545
|
-
return
|
|
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");
|
|
13546
14085
|
};
|
|
13547
14086
|
for (const a of status.accounts) {
|
|
13548
14087
|
const label = a.accountLabel ?? a.id;
|
|
@@ -13550,7 +14089,7 @@ ${pc7.dim("Next: olam auth login")}`);
|
|
|
13550
14089
|
const last429 = a.usage?.last429At ? `last429=${a.usage.last429At}` : "last429=never";
|
|
13551
14090
|
const reset = a.rateLimitResetsAt ? `resets=${a.rateLimitResetsAt}` : "";
|
|
13552
14091
|
console.log(
|
|
13553
|
-
` ${
|
|
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)}`
|
|
13554
14093
|
);
|
|
13555
14094
|
}
|
|
13556
14095
|
});
|
|
@@ -13588,7 +14127,7 @@ ${pc7.dim("Next: olam auth login")}`);
|
|
|
13588
14127
|
const preflight = await runAuthPreflight({ autoStart: true });
|
|
13589
14128
|
if (preflight.verdict !== "ok" && preflight.verdict !== "no-accounts") {
|
|
13590
14129
|
printError(preflight.message);
|
|
13591
|
-
console.log(` ${
|
|
14130
|
+
console.log(` ${pc8.dim(preflight.remedy)}`);
|
|
13592
14131
|
process.exitCode = 1;
|
|
13593
14132
|
return;
|
|
13594
14133
|
}
|
|
@@ -13621,14 +14160,14 @@ ${pc7.dim("Next: olam auth login")}`);
|
|
|
13621
14160
|
}
|
|
13622
14161
|
printHeader("Claude OAuth \u2014 PKCE flow");
|
|
13623
14162
|
console.log(`
|
|
13624
|
-
${
|
|
13625
|
-
console.log(` ${
|
|
14163
|
+
${pc8.bold("1.")} Opening Claude in your default browser\u2026`);
|
|
14164
|
+
console.log(` ${pc8.dim(pending.loginUrl)}`);
|
|
13626
14165
|
openBrowser(pending.loginUrl);
|
|
13627
14166
|
console.log(`
|
|
13628
|
-
${
|
|
13629
|
-
console.log(` ${
|
|
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>.)")}
|
|
13630
14169
|
`);
|
|
13631
|
-
const raw = await promptLine(`${
|
|
14170
|
+
const raw = await promptLine(`${pc8.dim("code:")} `);
|
|
13632
14171
|
if (!raw) {
|
|
13633
14172
|
printError("No code provided.");
|
|
13634
14173
|
process.exitCode = 1;
|
|
@@ -13639,7 +14178,7 @@ ${pc7.dim("Next: olam auth login")}`);
|
|
|
13639
14178
|
const result = await client.completeLogin(statePart, codePart);
|
|
13640
14179
|
printSuccess(`Account stored: ${result.account} (${result.expiresIn})`);
|
|
13641
14180
|
console.log(`
|
|
13642
|
-
${
|
|
14181
|
+
${pc8.dim("Next: olam create --name my-world")}`);
|
|
13643
14182
|
} catch (err) {
|
|
13644
14183
|
printError(err instanceof Error ? err.message : "token exchange failed");
|
|
13645
14184
|
process.exitCode = 1;
|
|
@@ -13670,16 +14209,16 @@ ${pc7.dim("Next: olam create --name my-world")}`);
|
|
|
13670
14209
|
|
|
13671
14210
|
// src/commands/create.ts
|
|
13672
14211
|
init_manager();
|
|
13673
|
-
import { spawnSync as
|
|
13674
|
-
import { existsSync as
|
|
13675
|
-
import { dirname as
|
|
13676
|
-
import
|
|
13677
|
-
import
|
|
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";
|
|
13678
14217
|
|
|
13679
14218
|
// ../core/src/world/devbox-freshness.ts
|
|
13680
14219
|
import { execSync as execSync6 } from "node:child_process";
|
|
13681
|
-
import { existsSync as
|
|
13682
|
-
import { join as
|
|
14220
|
+
import { existsSync as existsSync20, statSync as statSync5 } from "node:fs";
|
|
14221
|
+
import { join as join27 } from "node:path";
|
|
13683
14222
|
var DEFAULT_DEVBOX_IMAGE = "olam-devbox:latest";
|
|
13684
14223
|
var DEVBOX_BAKED_SOURCES = [
|
|
13685
14224
|
"packages/adapters/src/docker/devbox.Dockerfile",
|
|
@@ -13714,7 +14253,7 @@ function getDevboxFreshness(deps) {
|
|
|
13714
14253
|
}
|
|
13715
14254
|
const newerSources = [];
|
|
13716
14255
|
for (const relPath of DEVBOX_BAKED_SOURCES) {
|
|
13717
|
-
const absPath =
|
|
14256
|
+
const absPath = join27(deps.repoRoot, relPath);
|
|
13718
14257
|
const mtimeMs = statMtime(absPath);
|
|
13719
14258
|
if (mtimeMs === null) continue;
|
|
13720
14259
|
if (mtimeMs > imageCreatedAtMs) {
|
|
@@ -13768,7 +14307,7 @@ function defaultDockerInspect(image) {
|
|
|
13768
14307
|
}
|
|
13769
14308
|
function defaultStatMtime(absPath) {
|
|
13770
14309
|
try {
|
|
13771
|
-
if (!
|
|
14310
|
+
if (!existsSync20(absPath)) return null;
|
|
13772
14311
|
return statSync5(absPath).mtimeMs;
|
|
13773
14312
|
} catch {
|
|
13774
14313
|
return null;
|
|
@@ -13955,7 +14494,7 @@ function registerCreate(program2) {
|
|
|
13955
14494
|
resolvedName = defaultNameFromPrompt(opts.fromPrompt);
|
|
13956
14495
|
}
|
|
13957
14496
|
if (!resolvedWorkspace && !resolvedRepos) {
|
|
13958
|
-
const inferenceSpinner =
|
|
14497
|
+
const inferenceSpinner = ora3("Inferring workspace from prompt\u2026").start();
|
|
13959
14498
|
try {
|
|
13960
14499
|
const catalog = await fetchHostCpWorkspaces();
|
|
13961
14500
|
const catalogRepos = Array.from(
|
|
@@ -13970,9 +14509,9 @@ function registerCreate(program2) {
|
|
|
13970
14509
|
const reason = inferred.repos.length === 0 ? "no repo names extracted from prompt" : `inference confidence ${inferred.confidence.toFixed(2)} below ${PICKER_CONFIDENCE_THRESHOLD}`;
|
|
13971
14510
|
printError(`Picker needed: ${reason}`);
|
|
13972
14511
|
if (catalogRepos.length > 0) {
|
|
13973
|
-
console.log(
|
|
14512
|
+
console.log(pc9.dim(` available repos: ${catalogRepos.join(", ")}`));
|
|
13974
14513
|
}
|
|
13975
|
-
console.log(
|
|
14514
|
+
console.log(pc9.dim(" rerun with explicit --workspace <name> or --repos <a> <b> <c>"));
|
|
13976
14515
|
process.exitCode = 1;
|
|
13977
14516
|
return;
|
|
13978
14517
|
}
|
|
@@ -13990,7 +14529,7 @@ function registerCreate(program2) {
|
|
|
13990
14529
|
inferenceSpinner.fail(`Multiple workspaces match (${decision.result})`);
|
|
13991
14530
|
const cands = decision.result === "exact-N" ? decision.candidates.map((w) => w.name) : decision.candidates.map((c) => c.workspace.name);
|
|
13992
14531
|
printError(`Picker needed: ${cands.join(", ")}`);
|
|
13993
|
-
console.log(
|
|
14532
|
+
console.log(pc9.dim(" rerun with explicit --workspace <name>"));
|
|
13994
14533
|
process.exitCode = 1;
|
|
13995
14534
|
return;
|
|
13996
14535
|
} else {
|
|
@@ -14023,10 +14562,24 @@ function registerCreate(program2) {
|
|
|
14023
14562
|
const freshness = getDevboxFreshness({ repoRoot });
|
|
14024
14563
|
if (freshness.isStale) {
|
|
14025
14564
|
if (opts.rebuildBase) {
|
|
14026
|
-
const
|
|
14027
|
-
|
|
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(
|
|
14028
14581
|
"bash",
|
|
14029
|
-
[
|
|
14582
|
+
[buildScript],
|
|
14030
14583
|
{ cwd: repoRoot, stdio: "inherit" }
|
|
14031
14584
|
);
|
|
14032
14585
|
if (rebuild.status !== 0) {
|
|
@@ -14041,7 +14594,7 @@ function registerCreate(program2) {
|
|
|
14041
14594
|
}
|
|
14042
14595
|
}
|
|
14043
14596
|
}
|
|
14044
|
-
const spinner =
|
|
14597
|
+
const spinner = ora3("Creating world...").start();
|
|
14045
14598
|
try {
|
|
14046
14599
|
const world = await ctx.worldManager.createWorld({
|
|
14047
14600
|
name: resolvedName,
|
|
@@ -14090,10 +14643,10 @@ function registerCreate(program2) {
|
|
|
14090
14643
|
}
|
|
14091
14644
|
if (world.credentialsInjected?.claude) {
|
|
14092
14645
|
console.log(`
|
|
14093
|
-
${
|
|
14646
|
+
${pc9.green("Credentials injected. World is ready for dispatch.")}`);
|
|
14094
14647
|
} else if (world.dashboardUrl) {
|
|
14095
14648
|
console.log(`
|
|
14096
|
-
${
|
|
14649
|
+
${pc9.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
14097
14650
|
}
|
|
14098
14651
|
if (opts.hostCp !== false) {
|
|
14099
14652
|
const probeResult = await probeHostCp().catch(() => null);
|
|
@@ -14105,14 +14658,14 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
14105
14658
|
process.stderr.write(
|
|
14106
14659
|
[
|
|
14107
14660
|
"",
|
|
14108
|
-
|
|
14661
|
+
pc9.red("Host CP probe failed."),
|
|
14109
14662
|
` tried: http://127.0.0.1:19000/api/bootstrap \u2192 ${diag.bootstrapStatus}`,
|
|
14110
14663
|
` tried: docker container "olam-host-cp" \u2192 ${diag.containerStatus}`,
|
|
14111
14664
|
"",
|
|
14112
|
-
|
|
14113
|
-
` ${
|
|
14114
|
-
` ${
|
|
14115
|
-
` OR pass ${
|
|
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.`,
|
|
14116
14669
|
""
|
|
14117
14670
|
].join("\n")
|
|
14118
14671
|
);
|
|
@@ -14144,13 +14697,13 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
14144
14697
|
process.stderr.write(
|
|
14145
14698
|
[
|
|
14146
14699
|
"",
|
|
14147
|
-
|
|
14700
|
+
pc9.red("Host CP registry POST failed."),
|
|
14148
14701
|
` url: ${hostCpUrl}/api/admin/registry`,
|
|
14149
14702
|
` status: ${reg.status}`,
|
|
14150
14703
|
` error: ${reg.error}`,
|
|
14151
14704
|
"",
|
|
14152
|
-
|
|
14153
|
-
` ${
|
|
14705
|
+
pc9.yellow("World was created but not registered. Run manually:"),
|
|
14706
|
+
` ${pc9.cyan(`olam host-cp register --world ${world.id}`)}`,
|
|
14154
14707
|
""
|
|
14155
14708
|
].join("\n")
|
|
14156
14709
|
);
|
|
@@ -14205,7 +14758,7 @@ ${pc8.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
14205
14758
|
}
|
|
14206
14759
|
const worldUrl = `${hostCpUrl}/world/${encodeURIComponent(world.id)}`;
|
|
14207
14760
|
console.log(`
|
|
14208
|
-
${
|
|
14761
|
+
${pc9.cyan("Host CP UI:")} ${worldUrl}`);
|
|
14209
14762
|
if (opts.open !== false && reg.ok) {
|
|
14210
14763
|
const openResult = openUrl(worldUrl);
|
|
14211
14764
|
if (openResult.opened) {
|
|
@@ -14219,7 +14772,7 @@ ${pc8.cyan("Host CP UI:")} ${worldUrl}`);
|
|
|
14219
14772
|
spinner.fail("Failed to create world");
|
|
14220
14773
|
if (err instanceof AuthPreflightError) {
|
|
14221
14774
|
printError(err.message);
|
|
14222
|
-
if (err.remedy) console.log(` ${
|
|
14775
|
+
if (err.remedy) console.log(` ${pc9.dim(err.remedy)}`);
|
|
14223
14776
|
} else {
|
|
14224
14777
|
printError(err instanceof Error ? err.message : String(err));
|
|
14225
14778
|
}
|
|
@@ -14230,10 +14783,10 @@ ${pc8.cyan("Host CP UI:")} ${worldUrl}`);
|
|
|
14230
14783
|
function resolveRepoRoot(start) {
|
|
14231
14784
|
let cur = start;
|
|
14232
14785
|
while (true) {
|
|
14233
|
-
if (
|
|
14786
|
+
if (existsSync21(resolve7(cur, "packages")) && existsSync21(resolve7(cur, "package.json"))) {
|
|
14234
14787
|
return cur;
|
|
14235
14788
|
}
|
|
14236
|
-
const parent =
|
|
14789
|
+
const parent = dirname14(cur);
|
|
14237
14790
|
if (parent === cur) return start;
|
|
14238
14791
|
cur = parent;
|
|
14239
14792
|
}
|
|
@@ -14292,8 +14845,8 @@ async function fetchHostCpExactMatches(projects) {
|
|
|
14292
14845
|
|
|
14293
14846
|
// src/commands/dispatch.ts
|
|
14294
14847
|
init_context();
|
|
14295
|
-
import
|
|
14296
|
-
import
|
|
14848
|
+
import ora4 from "ora";
|
|
14849
|
+
import pc10 from "picocolors";
|
|
14297
14850
|
|
|
14298
14851
|
// ../core/src/orchestrator/dispatch.ts
|
|
14299
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.";
|
|
@@ -14319,7 +14872,7 @@ function registerDispatch(program2) {
|
|
|
14319
14872
|
process.exitCode = 1;
|
|
14320
14873
|
return;
|
|
14321
14874
|
}
|
|
14322
|
-
const spinner =
|
|
14875
|
+
const spinner = ora4("Dispatching...").start();
|
|
14323
14876
|
try {
|
|
14324
14877
|
const computeWorld = await ctx.computeProvider.getWorld(worldId);
|
|
14325
14878
|
if (!computeWorld) {
|
|
@@ -14376,7 +14929,7 @@ OLAM_EOF`
|
|
|
14376
14929
|
const containerName = `olam-${worldId}-devbox`;
|
|
14377
14930
|
console.log(
|
|
14378
14931
|
`
|
|
14379
|
-
${
|
|
14932
|
+
${pc10.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
|
|
14380
14933
|
);
|
|
14381
14934
|
} catch (err) {
|
|
14382
14935
|
spinner.fail("Dispatch failed");
|
|
@@ -14388,7 +14941,7 @@ ${pc9.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-ma
|
|
|
14388
14941
|
|
|
14389
14942
|
// src/commands/observe.ts
|
|
14390
14943
|
init_context();
|
|
14391
|
-
import
|
|
14944
|
+
import pc11 from "picocolors";
|
|
14392
14945
|
function registerObserve(program2) {
|
|
14393
14946
|
program2.command("observe").description("Stream thoughts from a world (coming soon)").argument("<world>", "World ID").action(async (worldId) => {
|
|
14394
14947
|
const { ctx, error } = await loadContext();
|
|
@@ -14404,22 +14957,22 @@ function registerObserve(program2) {
|
|
|
14404
14957
|
return;
|
|
14405
14958
|
}
|
|
14406
14959
|
console.log(
|
|
14407
|
-
|
|
14960
|
+
pc11.yellow("Observation is coming in a future release.")
|
|
14408
14961
|
);
|
|
14409
14962
|
console.log(
|
|
14410
|
-
|
|
14963
|
+
pc11.dim("This will stream the world's reasoning in real-time.")
|
|
14411
14964
|
);
|
|
14412
14965
|
const containerName = `olam-${worldId}-devbox`;
|
|
14413
14966
|
console.log(
|
|
14414
14967
|
`
|
|
14415
|
-
${
|
|
14968
|
+
${pc11.dim(`For now: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
|
|
14416
14969
|
);
|
|
14417
14970
|
});
|
|
14418
14971
|
}
|
|
14419
14972
|
|
|
14420
14973
|
// src/commands/list.ts
|
|
14421
14974
|
init_context();
|
|
14422
|
-
import
|
|
14975
|
+
import pc12 from "picocolors";
|
|
14423
14976
|
function registerList(program2) {
|
|
14424
14977
|
program2.command("list").alias("ls").description("List active worlds").action(async () => {
|
|
14425
14978
|
const { ctx, error } = await loadContext();
|
|
@@ -14430,18 +14983,18 @@ function registerList(program2) {
|
|
|
14430
14983
|
}
|
|
14431
14984
|
const worlds = ctx.worldManager.listWorlds();
|
|
14432
14985
|
if (worlds.length === 0) {
|
|
14433
|
-
console.log(
|
|
14986
|
+
console.log(pc12.dim("No worlds. Create one with `olam create --name my-world`."));
|
|
14434
14987
|
return;
|
|
14435
14988
|
}
|
|
14436
|
-
console.log(`${
|
|
14989
|
+
console.log(`${pc12.bold(String(worlds.length))} world(s)
|
|
14437
14990
|
`);
|
|
14438
14991
|
for (const w of worlds) {
|
|
14439
|
-
const statusColor = w.status === "running" ?
|
|
14992
|
+
const statusColor = w.status === "running" ? pc12.green(w.status) : w.status === "error" ? pc12.red(w.status) : pc12.yellow(w.status);
|
|
14440
14993
|
const age = formatAge(w.createdAt);
|
|
14441
14994
|
const cost = `$${w.totalCostUsd.toFixed(2)}`;
|
|
14442
|
-
console.log(` ${
|
|
14995
|
+
console.log(` ${pc12.bold(w.name)} ${pc12.dim(`(${w.id})`)}`);
|
|
14443
14996
|
console.log(
|
|
14444
|
-
` ${statusColor} ${
|
|
14997
|
+
` ${statusColor} ${pc12.dim("|")} ${w.repos.join(", ")} ${pc12.dim("|")} ${cost} ${pc12.dim("|")} ${age}`
|
|
14445
14998
|
);
|
|
14446
14999
|
console.log();
|
|
14447
15000
|
}
|
|
@@ -14493,7 +15046,7 @@ function registerStatus(program2) {
|
|
|
14493
15046
|
|
|
14494
15047
|
// src/commands/destroy.ts
|
|
14495
15048
|
init_context();
|
|
14496
|
-
import
|
|
15049
|
+
import ora5 from "ora";
|
|
14497
15050
|
function registerDestroy(program2) {
|
|
14498
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) => {
|
|
14499
15052
|
const { ctx, error } = await loadContext();
|
|
@@ -14508,7 +15061,7 @@ function registerDestroy(program2) {
|
|
|
14508
15061
|
process.exitCode = 1;
|
|
14509
15062
|
return;
|
|
14510
15063
|
}
|
|
14511
|
-
const spinner =
|
|
15064
|
+
const spinner = ora5(`Destroying ${world.name}...`).start();
|
|
14512
15065
|
try {
|
|
14513
15066
|
await ctx.worldManager.destroyWorld(worldId, {
|
|
14514
15067
|
skipCrystallize: opts.skipCrystallize
|
|
@@ -14532,7 +15085,7 @@ function registerDestroy(program2) {
|
|
|
14532
15085
|
// src/commands/enter.ts
|
|
14533
15086
|
init_context();
|
|
14534
15087
|
import { execSync as execSync7 } from "node:child_process";
|
|
14535
|
-
import
|
|
15088
|
+
import pc13 from "picocolors";
|
|
14536
15089
|
var SAFE_IDENT3 = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
14537
15090
|
function buildStartClaudeCommands(containerName, sessionName, task) {
|
|
14538
15091
|
if (!SAFE_IDENT3.test(containerName)) {
|
|
@@ -14634,7 +15187,7 @@ function registerEnter(program2) {
|
|
|
14634
15187
|
}
|
|
14635
15188
|
console.log("Run these commands in order to enter the world:\n");
|
|
14636
15189
|
for (const step of steps) {
|
|
14637
|
-
console.log(` ${
|
|
15190
|
+
console.log(` ${pc13.dim(`# ${step.description}`)}`);
|
|
14638
15191
|
if (step.stdin !== void 0) {
|
|
14639
15192
|
const escaped = step.stdin.replace(/'/g, "'\\''");
|
|
14640
15193
|
console.log(` printf '%s' '${escaped}' | ${step.command}`);
|
|
@@ -14668,11 +15221,11 @@ function registerEnter(program2) {
|
|
|
14668
15221
|
return;
|
|
14669
15222
|
}
|
|
14670
15223
|
console.log("Run this command to enter the world:\n");
|
|
14671
|
-
console.log(` ${
|
|
15224
|
+
console.log(` ${pc13.bold(result.command)}`);
|
|
14672
15225
|
const containerName = `olam-${worldId}-devbox`;
|
|
14673
15226
|
console.log(
|
|
14674
15227
|
`
|
|
14675
|
-
${
|
|
15228
|
+
${pc13.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`
|
|
14676
15229
|
);
|
|
14677
15230
|
});
|
|
14678
15231
|
}
|
|
@@ -14681,7 +15234,7 @@ ${pc12.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t ol
|
|
|
14681
15234
|
init_context();
|
|
14682
15235
|
import * as fs21 from "node:fs";
|
|
14683
15236
|
import "node:path";
|
|
14684
|
-
import
|
|
15237
|
+
import ora6 from "ora";
|
|
14685
15238
|
init_world_paths();
|
|
14686
15239
|
function registerCrystallize(program2, options = {}) {
|
|
14687
15240
|
const cmd = program2.command("crystallize").description("Crystallize thoughts from a world to Pleri Plane").argument("<world>", "World ID").action(async (worldId) => {
|
|
@@ -14717,7 +15270,7 @@ function registerCrystallize(program2, options = {}) {
|
|
|
14717
15270
|
process.exitCode = EXIT_GENERIC_ERROR;
|
|
14718
15271
|
return;
|
|
14719
15272
|
}
|
|
14720
|
-
const spinner =
|
|
15273
|
+
const spinner = ora6("Crystallizing thoughts...").start();
|
|
14721
15274
|
try {
|
|
14722
15275
|
const { ThoughtLocalStore: ThoughtLocalStore2 } = await Promise.resolve().then(() => (init_local_store(), local_store_exports));
|
|
14723
15276
|
const { computeGraphChecksum: computeGraphChecksum2 } = await Promise.resolve().then(() => (init_checksum(), checksum_exports));
|
|
@@ -14782,7 +15335,7 @@ function registerCrystallize(program2, options = {}) {
|
|
|
14782
15335
|
|
|
14783
15336
|
// src/commands/pr.ts
|
|
14784
15337
|
init_registry();
|
|
14785
|
-
import
|
|
15338
|
+
import pc14 from "picocolors";
|
|
14786
15339
|
|
|
14787
15340
|
// ../core/src/pr-gate/client.ts
|
|
14788
15341
|
var HOST_CONTROL_PLANE_BASE2 = 19080;
|
|
@@ -14862,26 +15415,26 @@ async function findGate(id) {
|
|
|
14862
15415
|
return null;
|
|
14863
15416
|
}
|
|
14864
15417
|
function formatStateBadge(state) {
|
|
14865
|
-
if (state === "approve") return
|
|
14866
|
-
if (state === "block") return
|
|
14867
|
-
if (state === "denied") return
|
|
14868
|
-
return
|
|
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");
|
|
14869
15422
|
}
|
|
14870
15423
|
function registerPr(program2) {
|
|
14871
15424
|
const pr = program2.command("pr").description("Review and decide PR-gate requests from running worlds");
|
|
14872
15425
|
pr.command("list").description("List all PR-gate requests across every running world").action(async () => {
|
|
14873
15426
|
const all = await fanoutList();
|
|
14874
15427
|
if (all.length === 0) {
|
|
14875
|
-
console.log(
|
|
15428
|
+
console.log(pc14.dim("No gates open."));
|
|
14876
15429
|
return;
|
|
14877
15430
|
}
|
|
14878
15431
|
printHeader(`${all.length} gate(s)`);
|
|
14879
15432
|
for (const { world, gate } of all) {
|
|
14880
15433
|
const badge = formatStateBadge(gate.state);
|
|
14881
15434
|
console.log(
|
|
14882
|
-
` ${
|
|
15435
|
+
` ${pc14.bold(gate.id.slice(0, 8))} ${badge.padEnd(20)} ${pc14.dim(world.name)} ${pc14.dim(gate.branch)}`
|
|
14883
15436
|
);
|
|
14884
|
-
console.log(` ${
|
|
15437
|
+
console.log(` ${pc14.dim(gate.command.slice(0, 100))}`);
|
|
14885
15438
|
}
|
|
14886
15439
|
});
|
|
14887
15440
|
pr.command("show").description("Show full gate detail (diff, command, commits)").argument("<id>", "Gate id (prefix match ok)").action(async (id) => {
|
|
@@ -14904,9 +15457,9 @@ function registerPr(program2) {
|
|
|
14904
15457
|
if (gate.reason) printInfo("Reason", gate.reason);
|
|
14905
15458
|
}
|
|
14906
15459
|
printHeader("Commits");
|
|
14907
|
-
console.log(gate.commitLog ||
|
|
15460
|
+
console.log(gate.commitLog || pc14.dim(" (empty)"));
|
|
14908
15461
|
printHeader("Diff stat");
|
|
14909
|
-
console.log(gate.diffStat ||
|
|
15462
|
+
console.log(gate.diffStat || pc14.dim(" (empty)"));
|
|
14910
15463
|
});
|
|
14911
15464
|
function decideCommand(verb, decision) {
|
|
14912
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) => {
|
|
@@ -15026,7 +15579,7 @@ function registerLanes(program2) {
|
|
|
15026
15579
|
// src/commands/policy-check.ts
|
|
15027
15580
|
init_loader2();
|
|
15028
15581
|
import { execSync as execSync8 } from "node:child_process";
|
|
15029
|
-
import
|
|
15582
|
+
import pc15 from "picocolors";
|
|
15030
15583
|
|
|
15031
15584
|
// ../../node_modules/balanced-match/dist/esm/index.js
|
|
15032
15585
|
var balanced = (a, b, str) => {
|
|
@@ -16890,7 +17443,7 @@ function registerPolicyCheck(program2) {
|
|
|
16890
17443
|
const workspaceRoot = opts.workspace ?? process.cwd();
|
|
16891
17444
|
const policies = loadPolicies(workspaceRoot);
|
|
16892
17445
|
if (policies.length === 0) {
|
|
16893
|
-
console.log(
|
|
17446
|
+
console.log(pc15.dim("policy-check: no policies found in .olam/policies/ \u2014 nothing to enforce"));
|
|
16894
17447
|
return;
|
|
16895
17448
|
}
|
|
16896
17449
|
const diff = getDiff(opts.base, workspaceRoot);
|
|
@@ -16899,11 +17452,11 @@ function registerPolicyCheck(program2) {
|
|
|
16899
17452
|
for (const result of results) {
|
|
16900
17453
|
if (result.passed) continue;
|
|
16901
17454
|
if (result.severity === "error") {
|
|
16902
|
-
console.error(`${
|
|
17455
|
+
console.error(`${pc15.red("policy error")} [${result.policyId}]`);
|
|
16903
17456
|
console.error(result.message);
|
|
16904
17457
|
hasErrorFailure = true;
|
|
16905
17458
|
} else {
|
|
16906
|
-
console.warn(`${
|
|
17459
|
+
console.warn(`${pc15.yellow("policy warn")} [${result.policyId}]`);
|
|
16907
17460
|
console.warn(result.message);
|
|
16908
17461
|
}
|
|
16909
17462
|
}
|
|
@@ -16916,14 +17469,15 @@ function registerPolicyCheck(program2) {
|
|
|
16916
17469
|
// src/commands/upgrade.ts
|
|
16917
17470
|
import * as fs24 from "node:fs";
|
|
16918
17471
|
import * as path28 from "node:path";
|
|
16919
|
-
import { spawnSync as
|
|
16920
|
-
import
|
|
17472
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
17473
|
+
import ora7 from "ora";
|
|
17474
|
+
import pc16 from "picocolors";
|
|
16921
17475
|
|
|
16922
17476
|
// src/commands/upgrade-lock.ts
|
|
16923
17477
|
import * as fs22 from "node:fs";
|
|
16924
17478
|
import * as os13 from "node:os";
|
|
16925
17479
|
import * as path26 from "node:path";
|
|
16926
|
-
import { spawnSync as
|
|
17480
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
16927
17481
|
var LOCK_FILE_PATH = path26.join(os13.homedir(), ".olam", ".upgrade.lock");
|
|
16928
17482
|
var STALE_LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
16929
17483
|
function readLockFile(lockPath) {
|
|
@@ -16948,7 +17502,7 @@ function isPidAlive(pid) {
|
|
|
16948
17502
|
}
|
|
16949
17503
|
var PS_UNAVAILABLE = "__ps_unavailable__";
|
|
16950
17504
|
function getPidCommand(pid) {
|
|
16951
|
-
const result =
|
|
17505
|
+
const result = spawnSync8("ps", ["-p", String(pid), "-o", "comm="], {
|
|
16952
17506
|
encoding: "utf-8",
|
|
16953
17507
|
stdio: ["ignore", "pipe", "ignore"]
|
|
16954
17508
|
});
|
|
@@ -17146,6 +17700,7 @@ function handleHistory(opts) {
|
|
|
17146
17700
|
|
|
17147
17701
|
// src/commands/upgrade.ts
|
|
17148
17702
|
init_auth();
|
|
17703
|
+
init_install_root();
|
|
17149
17704
|
var AUTH_HEALTH_URL2 = "http://127.0.0.1:9999/health";
|
|
17150
17705
|
function isNodeModulesInSync(cwd) {
|
|
17151
17706
|
const lockPath = path28.join(cwd, "package-lock.json");
|
|
@@ -17192,7 +17747,8 @@ function parseUpgradeOpts(raw) {
|
|
|
17192
17747
|
noCache: raw.noCache === true,
|
|
17193
17748
|
history: raw.history === true,
|
|
17194
17749
|
historyN: Number.isFinite(historyN) && historyN > 0 ? historyN : 10,
|
|
17195
|
-
historyJson: raw.json === true
|
|
17750
|
+
historyJson: raw.json === true,
|
|
17751
|
+
fromSource: raw.fromSource === true
|
|
17196
17752
|
};
|
|
17197
17753
|
}
|
|
17198
17754
|
function extractBundleHash(indexHtml) {
|
|
@@ -17201,8 +17757,8 @@ function extractBundleHash(indexHtml) {
|
|
|
17201
17757
|
}
|
|
17202
17758
|
function runStep2(label, cmd, args, opts = {}) {
|
|
17203
17759
|
const start = Date.now();
|
|
17204
|
-
process.stdout.write(` ${
|
|
17205
|
-
const result =
|
|
17760
|
+
process.stdout.write(` ${pc16.dim(label.padEnd(34))}`);
|
|
17761
|
+
const result = spawnSync9(cmd, [...args], {
|
|
17206
17762
|
encoding: "utf-8",
|
|
17207
17763
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17208
17764
|
cwd: opts.cwd ?? process.cwd(),
|
|
@@ -17211,7 +17767,7 @@ function runStep2(label, cmd, args, opts = {}) {
|
|
|
17211
17767
|
const durationMs = Date.now() - start;
|
|
17212
17768
|
const ok = result.status === 0 && result.error === void 0;
|
|
17213
17769
|
const dur = `${(durationMs / 1e3).toFixed(1)}s`;
|
|
17214
|
-
process.stdout.write(`${ok ?
|
|
17770
|
+
process.stdout.write(`${ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${dur}
|
|
17215
17771
|
`);
|
|
17216
17772
|
return {
|
|
17217
17773
|
ok,
|
|
@@ -17221,7 +17777,7 @@ function runStep2(label, cmd, args, opts = {}) {
|
|
|
17221
17777
|
};
|
|
17222
17778
|
}
|
|
17223
17779
|
function isGitDirty(cwd) {
|
|
17224
|
-
const result =
|
|
17780
|
+
const result = spawnSync9("git", ["status", "--porcelain"], {
|
|
17225
17781
|
encoding: "utf-8",
|
|
17226
17782
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17227
17783
|
cwd
|
|
@@ -17229,7 +17785,7 @@ function isGitDirty(cwd) {
|
|
|
17229
17785
|
return (result.stdout ?? "").trim().length > 0;
|
|
17230
17786
|
}
|
|
17231
17787
|
function hasGitUpstream(cwd) {
|
|
17232
|
-
const result =
|
|
17788
|
+
const result = spawnSync9("git", ["rev-parse", "--abbrev-ref", "@{u}"], {
|
|
17233
17789
|
encoding: "utf-8",
|
|
17234
17790
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17235
17791
|
cwd
|
|
@@ -17237,7 +17793,7 @@ function hasGitUpstream(cwd) {
|
|
|
17237
17793
|
return result.status === 0;
|
|
17238
17794
|
}
|
|
17239
17795
|
function captureHeadSha(cwd) {
|
|
17240
|
-
const result =
|
|
17796
|
+
const result = spawnSync9("git", ["rev-parse", "HEAD"], {
|
|
17241
17797
|
encoding: "utf-8",
|
|
17242
17798
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17243
17799
|
cwd
|
|
@@ -17252,7 +17808,7 @@ function abbreviateSha(sha) {
|
|
|
17252
17808
|
}
|
|
17253
17809
|
function imageExists(tag) {
|
|
17254
17810
|
try {
|
|
17255
|
-
const result =
|
|
17811
|
+
const result = spawnSync9("docker", ["image", "inspect", "--format", "{{.Id}}", tag], {
|
|
17256
17812
|
encoding: "utf-8",
|
|
17257
17813
|
stdio: ["ignore", "pipe", "ignore"]
|
|
17258
17814
|
});
|
|
@@ -17267,7 +17823,7 @@ function checkRollbackSetExists(plan) {
|
|
|
17267
17823
|
return missing.join(", ");
|
|
17268
17824
|
}
|
|
17269
17825
|
function smokeImage(image, targetSha) {
|
|
17270
|
-
const createResult =
|
|
17826
|
+
const createResult = spawnSync9("docker", ["create", "--name", `olam-smoke-${Date.now()}`, image], {
|
|
17271
17827
|
encoding: "utf-8",
|
|
17272
17828
|
stdio: ["ignore", "pipe", "pipe"]
|
|
17273
17829
|
});
|
|
@@ -17280,16 +17836,16 @@ function smokeImage(image, targetSha) {
|
|
|
17280
17836
|
};
|
|
17281
17837
|
}
|
|
17282
17838
|
const containerId = (createResult.stdout ?? "").trim();
|
|
17283
|
-
const inspectResult =
|
|
17839
|
+
const inspectResult = spawnSync9(
|
|
17284
17840
|
"docker",
|
|
17285
|
-
["inspect", "--format", '{{index .Config.Labels "
|
|
17841
|
+
["inspect", "--format", '{{index .Config.Labels "olam.build.sha"}}', image],
|
|
17286
17842
|
{
|
|
17287
17843
|
encoding: "utf-8",
|
|
17288
17844
|
stdio: ["ignore", "pipe", "pipe"]
|
|
17289
17845
|
}
|
|
17290
17846
|
);
|
|
17291
17847
|
if (containerId.length > 0) {
|
|
17292
|
-
|
|
17848
|
+
spawnSync9("docker", ["rm", "-f", containerId], {
|
|
17293
17849
|
encoding: "utf-8",
|
|
17294
17850
|
stdio: ["ignore", "ignore", "ignore"]
|
|
17295
17851
|
});
|
|
@@ -17308,7 +17864,7 @@ function smokeImage(image, targetSha) {
|
|
|
17308
17864
|
image,
|
|
17309
17865
|
ok: false,
|
|
17310
17866
|
bakedSha: null,
|
|
17311
|
-
error: "
|
|
17867
|
+
error: "olam.build.sha label is missing or empty"
|
|
17312
17868
|
};
|
|
17313
17869
|
}
|
|
17314
17870
|
if (bakedSha !== targetSha) {
|
|
@@ -17328,7 +17884,7 @@ var PRODUCTION_SWAP_PLAN = [
|
|
|
17328
17884
|
];
|
|
17329
17885
|
function dockerTag(source, dest) {
|
|
17330
17886
|
try {
|
|
17331
|
-
const result =
|
|
17887
|
+
const result = spawnSync9("docker", ["tag", source, dest], {
|
|
17332
17888
|
encoding: "utf-8",
|
|
17333
17889
|
stdio: ["ignore", "ignore", "pipe"]
|
|
17334
17890
|
});
|
|
@@ -17421,10 +17977,10 @@ async function confirm2(message) {
|
|
|
17421
17977
|
if (!process.stdin.isTTY) return true;
|
|
17422
17978
|
const { createInterface: createInterface2 } = await import("node:readline");
|
|
17423
17979
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
17424
|
-
return new Promise((
|
|
17980
|
+
return new Promise((resolve8) => {
|
|
17425
17981
|
rl.question(`${message} [y/N] `, (answer) => {
|
|
17426
17982
|
rl.close();
|
|
17427
|
-
|
|
17983
|
+
resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
17428
17984
|
});
|
|
17429
17985
|
});
|
|
17430
17986
|
}
|
|
@@ -17442,7 +17998,7 @@ async function waitForHealth(timeoutMs = 1e4) {
|
|
|
17442
17998
|
}
|
|
17443
17999
|
return false;
|
|
17444
18000
|
}
|
|
17445
|
-
async function waitForVersionMatch(targetSha, timeoutMs =
|
|
18001
|
+
async function waitForVersionMatch(targetSha, timeoutMs = 9e4, pollIntervalMs = 1e3) {
|
|
17446
18002
|
const deadline = Date.now() + timeoutMs;
|
|
17447
18003
|
let lastSnapshot = null;
|
|
17448
18004
|
while (Date.now() < deadline) {
|
|
@@ -17491,11 +18047,11 @@ async function waitForAuthHealthLocal(timeoutMs = 15e3) {
|
|
|
17491
18047
|
async function recreateAuthService() {
|
|
17492
18048
|
const start = Date.now();
|
|
17493
18049
|
try {
|
|
17494
|
-
|
|
18050
|
+
spawnSync9("docker", ["stop", "olam-auth"], {
|
|
17495
18051
|
encoding: "utf-8",
|
|
17496
18052
|
stdio: ["ignore", "ignore", "ignore"]
|
|
17497
18053
|
});
|
|
17498
|
-
|
|
18054
|
+
spawnSync9("docker", ["rm", "olam-auth"], {
|
|
17499
18055
|
encoding: "utf-8",
|
|
17500
18056
|
stdio: ["ignore", "ignore", "ignore"]
|
|
17501
18057
|
});
|
|
@@ -17524,6 +18080,163 @@ function readBundleHash(cwd) {
|
|
|
17524
18080
|
if (!fs24.existsSync(indexPath)) return null;
|
|
17525
18081
|
return extractBundleHash(fs24.readFileSync(indexPath, "utf-8"));
|
|
17526
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
|
+
}
|
|
17527
18240
|
async function handleUpgrade(opts) {
|
|
17528
18241
|
const cwd = process.cwd();
|
|
17529
18242
|
const rootCheck = validateRepoRoot(cwd);
|
|
@@ -17648,11 +18361,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
|
|
|
17648
18361
|
process.once("SIGINT", releaseOnSignal);
|
|
17649
18362
|
process.once("SIGTERM", releaseOnSignal);
|
|
17650
18363
|
try {
|
|
17651
|
-
process.stdout.write(` ${
|
|
18364
|
+
process.stdout.write(` ${pc16.dim("rollback retag (3 ops)".padEnd(34))}`);
|
|
17652
18365
|
const swapStart = Date.now();
|
|
17653
18366
|
const swapResult = performRollbackSwap(PRODUCTION_SWAP_PLAN);
|
|
17654
18367
|
const swapDur = `${((Date.now() - swapStart) / 1e3).toFixed(1)}s`;
|
|
17655
|
-
process.stdout.write(`${swapResult.ok ?
|
|
18368
|
+
process.stdout.write(`${swapResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${swapDur}
|
|
17656
18369
|
`);
|
|
17657
18370
|
if (!swapResult.ok) {
|
|
17658
18371
|
printError(`Rollback retag failed: ${swapResult.summary}`);
|
|
@@ -17663,11 +18376,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
|
|
|
17663
18376
|
const cwd = process.cwd();
|
|
17664
18377
|
const composeFile = path28.join(cwd, "packages/host-cp/compose.yaml");
|
|
17665
18378
|
const authSecret = readAuthSecret2();
|
|
17666
|
-
process.stdout.write(` ${
|
|
18379
|
+
process.stdout.write(` ${pc16.dim("docker compose recreate host-cp".padEnd(34))}`);
|
|
17667
18380
|
const composeStart = Date.now();
|
|
17668
18381
|
const composeResult = runCompose(["up", "-d", "--force-recreate", "host-cp"], composeFile, buildComposeEnv(authSecret));
|
|
17669
18382
|
const composeDur = `${((Date.now() - composeStart) / 1e3).toFixed(1)}s`;
|
|
17670
|
-
process.stdout.write(`${composeResult.ok ?
|
|
18383
|
+
process.stdout.write(`${composeResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${composeDur}
|
|
17671
18384
|
`);
|
|
17672
18385
|
if (!composeResult.ok) {
|
|
17673
18386
|
printError(
|
|
@@ -17678,10 +18391,10 @@ Canonical tags are at :olam-rollback (good); container restart pending. Manually
|
|
|
17678
18391
|
process.exitCode = 1;
|
|
17679
18392
|
return;
|
|
17680
18393
|
}
|
|
17681
|
-
process.stdout.write(` ${
|
|
18394
|
+
process.stdout.write(` ${pc16.dim("recreate auth-service".padEnd(34))}`);
|
|
17682
18395
|
const authResult = await recreateAuthService();
|
|
17683
18396
|
const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
|
|
17684
|
-
process.stdout.write(`${authResult.ok ?
|
|
18397
|
+
process.stdout.write(`${authResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${authDur}
|
|
17685
18398
|
`);
|
|
17686
18399
|
if (!authResult.ok) {
|
|
17687
18400
|
printError(`Auth-service recreate failed: ${authResult.error ?? "unknown"}`);
|
|
@@ -17833,13 +18546,24 @@ ${spaResult.stderr}`);
|
|
|
17833
18546
|
{ label: "bash build-devbox.sh", relPath: "packages/adapters/src/docker/build-devbox.sh", tee: true },
|
|
17834
18547
|
{ label: "bash build-host-cp.sh", relPath: "packages/adapters/src/docker/build-host-cp.sh", tee: false }
|
|
17835
18548
|
];
|
|
18549
|
+
const { resolveBuildScript: resolveBuildScript2, MissingBuildScriptError: MissingBuildScriptError2 } = await Promise.resolve().then(() => (init_install_root(), install_root_exports));
|
|
17836
18550
|
for (const step of buildScripts) {
|
|
17837
|
-
|
|
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
|
+
}
|
|
17838
18562
|
if (step.tee) {
|
|
17839
|
-
process.stdout.write(` ${
|
|
18563
|
+
process.stdout.write(` ${pc16.dim(step.label.padEnd(34))}
|
|
17840
18564
|
`);
|
|
17841
18565
|
const start = Date.now();
|
|
17842
|
-
const result =
|
|
18566
|
+
const result = spawnSync9("bash", [scriptPath], {
|
|
17843
18567
|
stdio: "inherit",
|
|
17844
18568
|
cwd,
|
|
17845
18569
|
env: { ...process.env, ...olamTagEnv }
|
|
@@ -17847,7 +18571,7 @@ ${spaResult.stderr}`);
|
|
|
17847
18571
|
const durationMs = Date.now() - start;
|
|
17848
18572
|
const ok = result.status === 0 && result.error === void 0;
|
|
17849
18573
|
const dur = `${(durationMs / 1e3).toFixed(1)}s`;
|
|
17850
|
-
process.stdout.write(` ${
|
|
18574
|
+
process.stdout.write(` ${pc16.dim(step.label.padEnd(34))}${ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${dur}
|
|
17851
18575
|
`);
|
|
17852
18576
|
timings.push({ label: step.label, durationMs });
|
|
17853
18577
|
if (!ok) {
|
|
@@ -17873,7 +18597,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
|
|
|
17873
18597
|
}
|
|
17874
18598
|
for (const t of timings) logRow.durations_ms[t.label] = t.durationMs;
|
|
17875
18599
|
const smokeStart = Date.now();
|
|
17876
|
-
process.stdout.write(` ${
|
|
18600
|
+
process.stdout.write(` ${pc16.dim("smoke (docker create + inspect)".padEnd(34))}`);
|
|
17877
18601
|
const smokeImages = [
|
|
17878
18602
|
"olam-auth:olam-next",
|
|
17879
18603
|
"olam-devbox:olam-next",
|
|
@@ -17883,7 +18607,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
|
|
|
17883
18607
|
const smokeFailures = smokeResults.filter((r) => !r.ok);
|
|
17884
18608
|
const smokeDurationMs = Date.now() - smokeStart;
|
|
17885
18609
|
const smokeDur = `${(smokeDurationMs / 1e3).toFixed(1)}s`;
|
|
17886
|
-
process.stdout.write(`${smokeFailures.length === 0 ?
|
|
18610
|
+
process.stdout.write(`${smokeFailures.length === 0 ? pc16.green("\u2713") : pc16.red("\u2717")} ${smokeDur}
|
|
17887
18611
|
`);
|
|
17888
18612
|
timings.push({ label: "smoke", durationMs: smokeDurationMs });
|
|
17889
18613
|
if (smokeFailures.length > 0) {
|
|
@@ -17910,12 +18634,12 @@ Recovery options:
|
|
|
17910
18634
|
process.exitCode = 1;
|
|
17911
18635
|
return;
|
|
17912
18636
|
}
|
|
17913
|
-
process.stdout.write(` ${
|
|
18637
|
+
process.stdout.write(` ${pc16.dim("atomic 6-tag swap".padEnd(34))}`);
|
|
17914
18638
|
const swapStart = Date.now();
|
|
17915
18639
|
const swapResult = performAtomicSwap(PRODUCTION_SWAP_PLAN);
|
|
17916
18640
|
const swapDurationMs = Date.now() - swapStart;
|
|
17917
18641
|
const swapDur = `${(swapDurationMs / 1e3).toFixed(1)}s`;
|
|
17918
|
-
process.stdout.write(`${swapResult.ok ?
|
|
18642
|
+
process.stdout.write(`${swapResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${swapDur}
|
|
17919
18643
|
`);
|
|
17920
18644
|
timings.push({ label: "atomic swap", durationMs: swapDurationMs });
|
|
17921
18645
|
if (!swapResult.ok) {
|
|
@@ -17925,7 +18649,7 @@ Recovery options:
|
|
|
17925
18649
|
}
|
|
17926
18650
|
printInfo("Swap", swapResult.summary);
|
|
17927
18651
|
const composeFile = path28.join(cwd, "packages/host-cp/compose.yaml");
|
|
17928
|
-
process.stdout.write(` ${
|
|
18652
|
+
process.stdout.write(` ${pc16.dim("docker compose recreate".padEnd(34))}`);
|
|
17929
18653
|
const composeStart = Date.now();
|
|
17930
18654
|
const composeResult = runCompose(
|
|
17931
18655
|
["up", "-d", "--force-recreate"],
|
|
@@ -17935,7 +18659,7 @@ Recovery options:
|
|
|
17935
18659
|
const composeDurationMs = Date.now() - composeStart;
|
|
17936
18660
|
const composeOk = composeResult.ok;
|
|
17937
18661
|
const composeDur = `${(composeDurationMs / 1e3).toFixed(1)}s`;
|
|
17938
|
-
process.stdout.write(`${composeOk ?
|
|
18662
|
+
process.stdout.write(`${composeOk ? pc16.green("\u2713") : pc16.red("\u2717")} ${composeDur}
|
|
17939
18663
|
`);
|
|
17940
18664
|
timings.push({ label: "container recreate", durationMs: composeDurationMs });
|
|
17941
18665
|
if (!composeOk) {
|
|
@@ -17951,10 +18675,10 @@ Recovery options:
|
|
|
17951
18675
|
process.exitCode = 1;
|
|
17952
18676
|
return;
|
|
17953
18677
|
}
|
|
17954
|
-
process.stdout.write(` ${
|
|
18678
|
+
process.stdout.write(` ${pc16.dim("recreate auth-service".padEnd(34))}`);
|
|
17955
18679
|
const authResult = await recreateAuthService();
|
|
17956
18680
|
const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
|
|
17957
|
-
process.stdout.write(`${authResult.ok ?
|
|
18681
|
+
process.stdout.write(`${authResult.ok ? pc16.green("\u2713") : pc16.red("\u2717")} ${authDur}
|
|
17958
18682
|
`);
|
|
17959
18683
|
timings.push({ label: "auth recreate", durationMs: authResult.durationMs });
|
|
17960
18684
|
if (!authResult.ok) {
|
|
@@ -17969,12 +18693,12 @@ Recovery options:
|
|
|
17969
18693
|
process.exitCode = 1;
|
|
17970
18694
|
return;
|
|
17971
18695
|
}
|
|
17972
|
-
process.stdout.write(` ${
|
|
18696
|
+
process.stdout.write(` ${pc16.dim("waiting for /health".padEnd(34))}`);
|
|
17973
18697
|
const healthStart = Date.now();
|
|
17974
18698
|
const healthy = await waitForHealth(1e4);
|
|
17975
18699
|
const healthDurationMs = Date.now() - healthStart;
|
|
17976
18700
|
const healthDur = `${(healthDurationMs / 1e3).toFixed(1)}s`;
|
|
17977
|
-
process.stdout.write(`${healthy ?
|
|
18701
|
+
process.stdout.write(`${healthy ? pc16.green("\u2713") : pc16.yellow("?")} ${healthDur}
|
|
17978
18702
|
`);
|
|
17979
18703
|
timings.push({ label: "/health", durationMs: healthDurationMs });
|
|
17980
18704
|
if (!healthy) {
|
|
@@ -17982,12 +18706,12 @@ Recovery options:
|
|
|
17982
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."
|
|
17983
18707
|
);
|
|
17984
18708
|
}
|
|
17985
|
-
process.stdout.write(` ${
|
|
18709
|
+
process.stdout.write(` ${pc16.dim("verify /version/status round-trip".padEnd(34))}`);
|
|
17986
18710
|
const versionStart = Date.now();
|
|
17987
|
-
const versionMatch = await waitForVersionMatch(_targetSha,
|
|
18711
|
+
const versionMatch = await waitForVersionMatch(_targetSha, 9e4);
|
|
17988
18712
|
const versionDurationMs = Date.now() - versionStart;
|
|
17989
18713
|
const versionDur = `${(versionDurationMs / 1e3).toFixed(1)}s`;
|
|
17990
|
-
process.stdout.write(`${versionMatch.matched ?
|
|
18714
|
+
process.stdout.write(`${versionMatch.matched ? pc16.green("\u2713") : pc16.yellow("?")} ${versionDur}
|
|
17991
18715
|
`);
|
|
17992
18716
|
timings.push({ label: "/version/status round-trip", durationMs: versionDurationMs });
|
|
17993
18717
|
if (!versionMatch.matched) {
|
|
@@ -18011,7 +18735,9 @@ function printTimings2(timings) {
|
|
|
18011
18735
|
printInfo("total", `${(total / 1e3).toFixed(1)}s`);
|
|
18012
18736
|
}
|
|
18013
18737
|
function registerUpgrade(program2) {
|
|
18014
|
-
program2.command("upgrade").description(
|
|
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(
|
|
18015
18741
|
"--skip-install",
|
|
18016
18742
|
"Skip npm install entirely (use existing node_modules as-is). Useful when a native-module build failure blocks the normal upgrade path."
|
|
18017
18743
|
).option("--branch <name>", "Switch to this branch before pulling (refuses if working tree is dirty)").option(
|
|
@@ -18026,24 +18752,47 @@ function registerUpgrade(program2) {
|
|
|
18026
18752
|
).option(
|
|
18027
18753
|
"--history",
|
|
18028
18754
|
"Print the upgrade history (~/.olam/upgrade.log) and exit.\n No upgrade is performed."
|
|
18029
|
-
).option("-n <count>", "Number of history rows to print (default 10)", "10").option("--json", "Emit history as JSONL instead of a table").
|
|
18030
|
-
|
|
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
|
+
}
|
|
18031
18780
|
});
|
|
18032
18781
|
}
|
|
18033
18782
|
|
|
18034
18783
|
// src/commands/logs.ts
|
|
18035
18784
|
import * as http3 from "node:http";
|
|
18036
|
-
import
|
|
18785
|
+
import pc17 from "picocolors";
|
|
18037
18786
|
init_context();
|
|
18038
18787
|
var HOST_CP_PORT2 = 19e3;
|
|
18039
18788
|
function colorLine(line) {
|
|
18040
|
-
if (/\bERROR\b/.test(line)) return
|
|
18041
|
-
if (/\bWARN\b/.test(line)) return
|
|
18042
|
-
if (/\bINFO\b/.test(line)) return
|
|
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);
|
|
18043
18792
|
return line;
|
|
18044
18793
|
}
|
|
18045
18794
|
function formatLine(line, service, showService) {
|
|
18046
|
-
const prefix = showService && service ? `${
|
|
18795
|
+
const prefix = showService && service ? `${pc17.cyan(`[${service}]`)} ` : "";
|
|
18047
18796
|
return prefix + colorLine(line);
|
|
18048
18797
|
}
|
|
18049
18798
|
function parseSseEvent(raw) {
|
|
@@ -18159,8 +18908,8 @@ function registerLogs(program2) {
|
|
|
18159
18908
|
|
|
18160
18909
|
// src/commands/ps.ts
|
|
18161
18910
|
init_context();
|
|
18162
|
-
import
|
|
18163
|
-
import { spawnSync as
|
|
18911
|
+
import pc18 from "picocolors";
|
|
18912
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
18164
18913
|
var SAFE_IDENT4 = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
18165
18914
|
function parseDockerTop(stdout) {
|
|
18166
18915
|
const trimmed = stdout.trim();
|
|
@@ -18220,18 +18969,18 @@ function printTable(rows) {
|
|
|
18220
18969
|
const fixedWidth = 5 + 1 + 8 + 1 + 6 + 1 + 6 + 1 + 10 + 1 + 6 + 1;
|
|
18221
18970
|
const cmdWidth = Math.max(20, cols - fixedWidth);
|
|
18222
18971
|
const header = [
|
|
18223
|
-
|
|
18224
|
-
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18229
|
-
|
|
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"))
|
|
18230
18979
|
].join(" ");
|
|
18231
18980
|
console.log(header);
|
|
18232
18981
|
for (const row of rows) {
|
|
18233
18982
|
const cmd = row.command.length > cmdWidth ? row.command.slice(0, cmdWidth - 1) + "\u2026" : row.command;
|
|
18234
|
-
const stateFn = row.state.startsWith("R") ?
|
|
18983
|
+
const stateFn = row.state.startsWith("R") ? pc18.green : row.state.startsWith("Z") ? pc18.red : pc18.dim;
|
|
18235
18984
|
console.log([
|
|
18236
18985
|
row.pid.padEnd(5),
|
|
18237
18986
|
row.user.slice(0, 8).padEnd(8),
|
|
@@ -18260,7 +19009,7 @@ function registerPs(program2) {
|
|
|
18260
19009
|
const containerName = `olam-${worldId}-devbox`;
|
|
18261
19010
|
let watchInterval;
|
|
18262
19011
|
function fetchAndPrint() {
|
|
18263
|
-
const result =
|
|
19012
|
+
const result = spawnSync10(
|
|
18264
19013
|
"docker",
|
|
18265
19014
|
["top", containerName, "pid", "user", "pcpu", "pmem", "stime", "stat", "cmd"],
|
|
18266
19015
|
{ encoding: "utf-8", timeout: 3e3 }
|
|
@@ -18280,7 +19029,7 @@ function registerPs(program2) {
|
|
|
18280
19029
|
printTable(rows);
|
|
18281
19030
|
if (opts.watch) {
|
|
18282
19031
|
process.stdout.write(`
|
|
18283
|
-
${
|
|
19032
|
+
${pc18.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
|
|
18284
19033
|
`);
|
|
18285
19034
|
}
|
|
18286
19035
|
}
|
|
@@ -18400,7 +19149,7 @@ function registerKeys(program2) {
|
|
|
18400
19149
|
import * as fs27 from "node:fs";
|
|
18401
19150
|
import * as path31 from "node:path";
|
|
18402
19151
|
import { execSync as execSync9 } from "node:child_process";
|
|
18403
|
-
import
|
|
19152
|
+
import pc19 from "picocolors";
|
|
18404
19153
|
|
|
18405
19154
|
// ../core/src/world/snapshot.ts
|
|
18406
19155
|
import * as crypto6 from "node:crypto";
|
|
@@ -18569,7 +19318,7 @@ async function handleCreate2(worldId, kindArg) {
|
|
|
18569
19318
|
const label = r.repo ? `${r.kind}/${r.repo}` : r.kind;
|
|
18570
19319
|
if (r.ok) {
|
|
18571
19320
|
printSuccess(`${label}`);
|
|
18572
|
-
console.log(` ${
|
|
19321
|
+
console.log(` ${pc19.dim(r.tarPath)}`);
|
|
18573
19322
|
} else {
|
|
18574
19323
|
printWarning(`${label}: ${r.msg ?? "skipped"}`);
|
|
18575
19324
|
}
|
|
@@ -18719,7 +19468,7 @@ function handleList2(worldIdFilter) {
|
|
|
18719
19468
|
const entries = listSnapshots(worldIdFilter);
|
|
18720
19469
|
if (entries.length === 0) {
|
|
18721
19470
|
const where = worldIdFilter ? ` for world "${worldIdFilter}"` : "";
|
|
18722
|
-
console.log(
|
|
19471
|
+
console.log(pc19.dim(`No snapshots found${where}. Run \`olam world snapshot create <worldId>\` first.`));
|
|
18723
19472
|
return;
|
|
18724
19473
|
}
|
|
18725
19474
|
printHeader(`${entries.length} snapshot(s)`);
|
|
@@ -18728,15 +19477,15 @@ function handleList2(worldIdFilter) {
|
|
|
18728
19477
|
for (const entry of entries) {
|
|
18729
19478
|
const { manifest } = entry;
|
|
18730
19479
|
if (manifest.worldId !== lastWorldId) {
|
|
18731
|
-
console.log(
|
|
19480
|
+
console.log(pc19.bold(manifest.worldId));
|
|
18732
19481
|
lastWorldId = manifest.worldId;
|
|
18733
19482
|
}
|
|
18734
19483
|
const repo = manifest.repo ?? "(all repos)";
|
|
18735
19484
|
const size = formatBytes(manifest.sizeBytes);
|
|
18736
19485
|
const age = formatAge2(entry.ageMs);
|
|
18737
|
-
const kindColor = manifest.kind === "gems" ?
|
|
19486
|
+
const kindColor = manifest.kind === "gems" ? pc19.magenta(manifest.kind) : manifest.kind === "node" ? pc19.cyan(manifest.kind) : pc19.yellow(manifest.kind);
|
|
18738
19487
|
console.log(
|
|
18739
|
-
` ${kindColor.padEnd(6)} ${
|
|
19488
|
+
` ${kindColor.padEnd(6)} ${pc19.dim(repo.padEnd(24))} ${manifest.fingerprint} ${size.padStart(8)} ${age}`
|
|
18740
19489
|
);
|
|
18741
19490
|
}
|
|
18742
19491
|
console.log();
|
|
@@ -18773,8 +19522,8 @@ init_context();
|
|
|
18773
19522
|
import * as fs29 from "node:fs";
|
|
18774
19523
|
import * as os17 from "node:os";
|
|
18775
19524
|
import * as path33 from "node:path";
|
|
18776
|
-
import { spawnSync as
|
|
18777
|
-
import
|
|
19525
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
19526
|
+
import ora8 from "ora";
|
|
18778
19527
|
|
|
18779
19528
|
// src/commands/refresh-helpers.ts
|
|
18780
19529
|
import * as fs28 from "node:fs";
|
|
@@ -18817,7 +19566,7 @@ var RESTART_TIMEOUT_S = 30;
|
|
|
18817
19566
|
var HEALTH_POLL_MS = 500;
|
|
18818
19567
|
var HEALTH_TIMEOUT_MS = 3e4;
|
|
18819
19568
|
function docker(args) {
|
|
18820
|
-
const result =
|
|
19569
|
+
const result = spawnSync11("docker", args, {
|
|
18821
19570
|
encoding: "utf-8",
|
|
18822
19571
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18823
19572
|
});
|
|
@@ -18957,7 +19706,7 @@ Run \`olam refresh\` from the olam repo root.`
|
|
|
18957
19706
|
process.stdout.write("\n");
|
|
18958
19707
|
const results = [];
|
|
18959
19708
|
for (const world of worldsToRefresh) {
|
|
18960
|
-
const spinner =
|
|
19709
|
+
const spinner = ora8(`${world.name}: copying CP source...`).start();
|
|
18961
19710
|
try {
|
|
18962
19711
|
const result = await refreshWorld(
|
|
18963
19712
|
world.id,
|
|
@@ -19022,7 +19771,7 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
|
19022
19771
|
var program = new Command();
|
|
19023
19772
|
function readCliVersion() {
|
|
19024
19773
|
try {
|
|
19025
|
-
const here = path35.dirname(
|
|
19774
|
+
const here = path35.dirname(fileURLToPath4(import.meta.url));
|
|
19026
19775
|
for (const candidate of [
|
|
19027
19776
|
path35.join(here, "package.json"),
|
|
19028
19777
|
path35.join(here, "..", "package.json"),
|
|
@@ -19060,4 +19809,5 @@ registerPs(program);
|
|
|
19060
19809
|
registerKeys(program);
|
|
19061
19810
|
registerWorldSnapshot(program);
|
|
19062
19811
|
registerRefresh(program);
|
|
19812
|
+
registerBootstrap(program);
|
|
19063
19813
|
program.parse();
|