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