@pleri/olam-cli 0.1.55 → 0.1.57
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__/services.test.d.ts +8 -0
- package/dist/__tests__/services.test.d.ts.map +1 -0
- package/dist/__tests__/services.test.js +185 -0
- package/dist/__tests__/services.test.js.map +1 -0
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts +6 -0
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.d.ts.map +1 -0
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js +26 -0
- package/dist/commands/__tests__/__fixtures__/upgrade-helpers.js.map +1 -0
- package/dist/commands/__tests__/upgrade.all-three.test.js +1 -13
- package/dist/commands/__tests__/upgrade.all-three.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.compose-path.test.js +1 -13
- package/dist/commands/__tests__/upgrade.compose-path.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.olam-tag.test.js +1 -14
- package/dist/commands/__tests__/upgrade.olam-tag.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.recreate.test.js +1 -13
- package/dist/commands/__tests__/upgrade.recreate.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.rollback.test.js +1 -21
- package/dist/commands/__tests__/upgrade.rollback.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.smoke.test.js +9 -23
- package/dist/commands/__tests__/upgrade.smoke.test.js.map +1 -1
- package/dist/commands/__tests__/upgrade.swap.test.js +1 -22
- package/dist/commands/__tests__/upgrade.swap.test.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +15 -38
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +2 -0
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +3 -3
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -9
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/services.d.ts +39 -0
- package/dist/commands/services.d.ts.map +1 -0
- package/dist/commands/services.js +220 -0
- package/dist/commands/services.js.map +1 -0
- package/dist/commands/upgrade.d.ts +11 -12
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +81 -6
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/image-digests.json +4 -3
- package/dist/index.js +1013 -1029
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +310 -372
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -460,8 +460,8 @@ var init_parseUtil = __esm({
|
|
|
460
460
|
init_errors();
|
|
461
461
|
init_en();
|
|
462
462
|
makeIssue = (params) => {
|
|
463
|
-
const { data, path:
|
|
464
|
-
const fullPath = [...
|
|
463
|
+
const { data, path: path44, errorMaps, issueData } = params;
|
|
464
|
+
const fullPath = [...path44, ...issueData.path || []];
|
|
465
465
|
const fullIssue = {
|
|
466
466
|
...issueData,
|
|
467
467
|
path: fullPath
|
|
@@ -769,11 +769,11 @@ var init_types = __esm({
|
|
|
769
769
|
init_parseUtil();
|
|
770
770
|
init_util();
|
|
771
771
|
ParseInputLazyPath = class {
|
|
772
|
-
constructor(parent, value,
|
|
772
|
+
constructor(parent, value, path44, key) {
|
|
773
773
|
this._cachedPath = [];
|
|
774
774
|
this.parent = parent;
|
|
775
775
|
this.data = value;
|
|
776
|
-
this._path =
|
|
776
|
+
this._path = path44;
|
|
777
777
|
this._key = key;
|
|
778
778
|
}
|
|
779
779
|
get path() {
|
|
@@ -4254,7 +4254,7 @@ import YAML from "yaml";
|
|
|
4254
4254
|
function bootstrapStepCmd(entry) {
|
|
4255
4255
|
return typeof entry === "string" ? entry : entry.cmd;
|
|
4256
4256
|
}
|
|
4257
|
-
function refineForbiddenKeys(value,
|
|
4257
|
+
function refineForbiddenKeys(value, path44, ctx, rejectSource) {
|
|
4258
4258
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4259
4259
|
return;
|
|
4260
4260
|
}
|
|
@@ -4262,12 +4262,12 @@ function refineForbiddenKeys(value, path45, ctx, rejectSource) {
|
|
|
4262
4262
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4263
4263
|
ctx.addIssue({
|
|
4264
4264
|
code: external_exports.ZodIssueCode.custom,
|
|
4265
|
-
path: [...
|
|
4265
|
+
path: [...path44, key],
|
|
4266
4266
|
message: `forbidden key "${key}" (prototype-pollution surface)`
|
|
4267
4267
|
});
|
|
4268
4268
|
continue;
|
|
4269
4269
|
}
|
|
4270
|
-
if (rejectSource &&
|
|
4270
|
+
if (rejectSource && path44.length === 0 && key === "source") {
|
|
4271
4271
|
ctx.addIssue({
|
|
4272
4272
|
code: external_exports.ZodIssueCode.custom,
|
|
4273
4273
|
path: ["source"],
|
|
@@ -4275,21 +4275,21 @@ function refineForbiddenKeys(value, path45, ctx, rejectSource) {
|
|
|
4275
4275
|
});
|
|
4276
4276
|
continue;
|
|
4277
4277
|
}
|
|
4278
|
-
refineForbiddenKeys(value[key], [...
|
|
4278
|
+
refineForbiddenKeys(value[key], [...path44, key], ctx, false);
|
|
4279
4279
|
}
|
|
4280
4280
|
}
|
|
4281
|
-
function rejectForbiddenKeys(value,
|
|
4281
|
+
function rejectForbiddenKeys(value, path44, rejectSource) {
|
|
4282
4282
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4283
4283
|
return;
|
|
4284
4284
|
}
|
|
4285
4285
|
for (const key of Object.keys(value)) {
|
|
4286
4286
|
if (FORBIDDEN_KEYS.has(key)) {
|
|
4287
|
-
throw new Error(`[manifest] ${
|
|
4287
|
+
throw new Error(`[manifest] ${path44}: forbidden key "${key}" (prototype-pollution surface)`);
|
|
4288
4288
|
}
|
|
4289
4289
|
if (rejectSource && key === "source") {
|
|
4290
|
-
throw new Error(`[manifest] ${
|
|
4290
|
+
throw new Error(`[manifest] ${path44}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
|
|
4291
4291
|
}
|
|
4292
|
-
rejectForbiddenKeys(value[key], `${
|
|
4292
|
+
rejectForbiddenKeys(value[key], `${path44}.${key}`, false);
|
|
4293
4293
|
}
|
|
4294
4294
|
}
|
|
4295
4295
|
function unknownTopLevelKeys(parsed) {
|
|
@@ -4604,7 +4604,22 @@ function isSecureOrLocalhost(value) {
|
|
|
4604
4604
|
return false;
|
|
4605
4605
|
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]" || parsed.hostname === "::1";
|
|
4606
4606
|
}
|
|
4607
|
-
|
|
4607
|
+
function inferRegistryProvider(prefix) {
|
|
4608
|
+
if (!prefix)
|
|
4609
|
+
return void 0;
|
|
4610
|
+
const head = prefix.split("/")[0] ?? "";
|
|
4611
|
+
if (!head.includes(".") && !head.includes(":"))
|
|
4612
|
+
return "dockerhub";
|
|
4613
|
+
if (head === "ghcr.io")
|
|
4614
|
+
return "ghcr";
|
|
4615
|
+
if (head === "docker.io")
|
|
4616
|
+
return "dockerhub";
|
|
4617
|
+
if (head === "gcr.io" || head.endsWith("-docker.pkg.dev") || head.endsWith(".pkg.dev")) {
|
|
4618
|
+
return "gar";
|
|
4619
|
+
}
|
|
4620
|
+
return void 0;
|
|
4621
|
+
}
|
|
4622
|
+
var repoTypeSchema, envSetupSchema, EntitledMcpsSchema, RepoConfigSchema, ServiceConfigSchema, sshHostSchema, computeProviderType, stackImageSchema, computeProvidersSchema, ComputeConfigSchema, CostConfigSchema, DashboardConfigSchema, PleriConfigSchema, authModeSchema, AuthConfigSchema, DevboxRegistryProviderSchema, DevboxRegistrySchema, DevboxImageSelectorSchema, DevboxConfigSchema, WorldsDefaultSchema, OlamConfigSchema;
|
|
4608
4623
|
var init_schema2 = __esm({
|
|
4609
4624
|
"../core/dist/config/schema.js"() {
|
|
4610
4625
|
"use strict";
|
|
@@ -4740,19 +4755,54 @@ var init_schema2 = __esm({
|
|
|
4740
4755
|
mode: authModeSchema.default("oauth"),
|
|
4741
4756
|
autoInject: external_exports.boolean().default(true).describe("Copy host credentials into world at creation. Set false to use browser OAuth instead.")
|
|
4742
4757
|
});
|
|
4758
|
+
DevboxRegistryProviderSchema = external_exports.enum(["ghcr", "gar", "dockerhub"]);
|
|
4743
4759
|
DevboxRegistrySchema = external_exports.object({
|
|
4744
4760
|
/**
|
|
4745
4761
|
* Fully-qualified registry prefix (no trailing slash), e.g.
|
|
4746
|
-
* "ghcr.io/<org>/olam-devbox"
|
|
4747
|
-
*
|
|
4762
|
+
* "ghcr.io/<org>/olam-devbox" or
|
|
4763
|
+
* "us-central1-docker.pkg.dev/<gcp-project>/<repo>/olam-devbox".
|
|
4764
|
+
* When set without per-tag overrides, ALL `olam-devbox:*` lookups are
|
|
4765
|
+
* rewritten to `<prefix>:<tag>`.
|
|
4748
4766
|
*/
|
|
4749
4767
|
prefix: external_exports.string().optional(),
|
|
4768
|
+
/**
|
|
4769
|
+
* Registry provider. Optional — inferred from `prefix` hostname when
|
|
4770
|
+
* omitted. Set explicitly to assert which provider the consumer intends
|
|
4771
|
+
* (a mismatch with `prefix` is a config error, not silent acceptance).
|
|
4772
|
+
*/
|
|
4773
|
+
provider: DevboxRegistryProviderSchema.optional(),
|
|
4750
4774
|
/**
|
|
4751
4775
|
* Per-tag overrides. Wins over `prefix` when both are set. Use this when
|
|
4752
4776
|
* different image tags live in different registries, or when a few tags
|
|
4753
4777
|
* are local-only.
|
|
4754
4778
|
*/
|
|
4755
4779
|
tags: external_exports.record(external_exports.string(), external_exports.string()).optional().default({})
|
|
4780
|
+
}).superRefine((reg, ctx) => {
|
|
4781
|
+
if (reg.provider && !reg.prefix) {
|
|
4782
|
+
ctx.addIssue({
|
|
4783
|
+
code: external_exports.ZodIssueCode.custom,
|
|
4784
|
+
path: ["prefix"],
|
|
4785
|
+
message: `devbox.registry.provider was set to "${reg.provider}" but no prefix was declared. Either drop the provider field or add a prefix like "<host>/<owner>/olam-devbox".`
|
|
4786
|
+
});
|
|
4787
|
+
return;
|
|
4788
|
+
}
|
|
4789
|
+
if (!reg.prefix)
|
|
4790
|
+
return;
|
|
4791
|
+
const inferred = inferRegistryProvider(reg.prefix);
|
|
4792
|
+
if (reg.provider && inferred && reg.provider !== inferred) {
|
|
4793
|
+
ctx.addIssue({
|
|
4794
|
+
code: external_exports.ZodIssueCode.custom,
|
|
4795
|
+
path: ["provider"],
|
|
4796
|
+
message: `devbox.registry.provider "${reg.provider}" does not match prefix "${reg.prefix}" (inferred "${inferred}" from hostname). Fix the prefix host or drop the provider field.`
|
|
4797
|
+
});
|
|
4798
|
+
}
|
|
4799
|
+
if (reg.provider && !inferred) {
|
|
4800
|
+
ctx.addIssue({
|
|
4801
|
+
code: external_exports.ZodIssueCode.custom,
|
|
4802
|
+
path: ["prefix"],
|
|
4803
|
+
message: `devbox.registry.prefix "${reg.prefix}" hostname is not recognised \u2014 provider "${reg.provider}" cannot be cross-checked. Recognised hosts: ghcr.io, *-docker.pkg.dev, gcr.io, docker.io, or a bare "owner/repo" (Docker Hub default).`
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4756
4806
|
});
|
|
4757
4807
|
DevboxImageSelectorSchema = external_exports.object({
|
|
4758
4808
|
/** Bare tag the resolver feeds to the registry (e.g. "rails-monorepo"). */
|
|
@@ -5230,8 +5280,8 @@ var init_client = __esm({
|
|
|
5230
5280
|
throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
|
|
5231
5281
|
}
|
|
5232
5282
|
}
|
|
5233
|
-
async request(method,
|
|
5234
|
-
const url = `${this.baseUrl}${
|
|
5283
|
+
async request(method, path44, body, attempt = 0) {
|
|
5284
|
+
const url = `${this.baseUrl}${path44}`;
|
|
5235
5285
|
const controller = new AbortController();
|
|
5236
5286
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
5237
5287
|
const headers = {};
|
|
@@ -5249,7 +5299,7 @@ var init_client = __esm({
|
|
|
5249
5299
|
} catch (err) {
|
|
5250
5300
|
if (attempt < RETRY_COUNT && isTransient(err)) {
|
|
5251
5301
|
await sleep(RETRY_BACKOFF_MS * (attempt + 1));
|
|
5252
|
-
return this.request(method,
|
|
5302
|
+
return this.request(method, path44, body, attempt + 1);
|
|
5253
5303
|
}
|
|
5254
5304
|
throw err;
|
|
5255
5305
|
} finally {
|
|
@@ -6728,8 +6778,8 @@ var init_provider3 = __esm({
|
|
|
6728
6778
|
// -----------------------------------------------------------------------
|
|
6729
6779
|
// Internal fetch helper
|
|
6730
6780
|
// -----------------------------------------------------------------------
|
|
6731
|
-
async request(
|
|
6732
|
-
const url = `${this.config.workerUrl}${
|
|
6781
|
+
async request(path44, method, body) {
|
|
6782
|
+
const url = `${this.config.workerUrl}${path44}`;
|
|
6733
6783
|
const bearer = await this.config.mintToken();
|
|
6734
6784
|
const headers = {
|
|
6735
6785
|
Authorization: `Bearer ${bearer}`
|
|
@@ -7519,192 +7569,87 @@ var init_hooks_config = __esm({
|
|
|
7519
7569
|
}
|
|
7520
7570
|
});
|
|
7521
7571
|
|
|
7522
|
-
// ../core/dist/world/merge-settings.js
|
|
7523
|
-
import * as fs12 from "node:fs";
|
|
7524
|
-
import * as path13 from "node:path";
|
|
7525
|
-
import * as crypto2 from "node:crypto";
|
|
7526
|
-
function mergeHomeSettingsJson(filePath, options) {
|
|
7527
|
-
let settings;
|
|
7528
|
-
try {
|
|
7529
|
-
settings = readSettings(filePath);
|
|
7530
|
-
} catch (err) {
|
|
7531
|
-
throw new Error(`merge-settings: failed to parse existing settings.json: ${err?.message ?? err}`);
|
|
7532
|
-
}
|
|
7533
|
-
let changed = false;
|
|
7534
|
-
if (options.ensureHook) {
|
|
7535
|
-
const { stage, sentinel, entry } = options.ensureHook;
|
|
7536
|
-
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
7537
|
-
settings = { ...settings, hooks: {} };
|
|
7538
|
-
changed = true;
|
|
7539
|
-
}
|
|
7540
|
-
const hooks = settings.hooks;
|
|
7541
|
-
if (!Array.isArray(hooks[stage])) {
|
|
7542
|
-
settings = {
|
|
7543
|
-
...settings,
|
|
7544
|
-
hooks: { ...hooks, [stage]: [] }
|
|
7545
|
-
};
|
|
7546
|
-
changed = true;
|
|
7547
|
-
}
|
|
7548
|
-
const stageArr = settings.hooks[stage];
|
|
7549
|
-
if (isHookSentinelPresent(stageArr, sentinel)) {
|
|
7550
|
-
if (!changed) {
|
|
7551
|
-
return { status: "already-present", message: `hook already present at ${filePath}` };
|
|
7552
|
-
}
|
|
7553
|
-
} else {
|
|
7554
|
-
settings = {
|
|
7555
|
-
...settings,
|
|
7556
|
-
hooks: {
|
|
7557
|
-
...settings.hooks,
|
|
7558
|
-
[stage]: [...stageArr, entry]
|
|
7559
|
-
}
|
|
7560
|
-
};
|
|
7561
|
-
changed = true;
|
|
7562
|
-
}
|
|
7563
|
-
}
|
|
7564
|
-
if (options.env) {
|
|
7565
|
-
const existingEnv = settings.env && typeof settings.env === "object" ? settings.env : {};
|
|
7566
|
-
const mergedEnv = { ...existingEnv, ...options.env };
|
|
7567
|
-
const sameKeys = Object.keys(mergedEnv).length === Object.keys(existingEnv).length && Object.keys(mergedEnv).every((k) => existingEnv[k] === mergedEnv[k]);
|
|
7568
|
-
if (!sameKeys) {
|
|
7569
|
-
settings = { ...settings, env: mergedEnv };
|
|
7570
|
-
changed = true;
|
|
7571
|
-
}
|
|
7572
|
-
}
|
|
7573
|
-
if (!changed) {
|
|
7574
|
-
return { status: "no-op", message: `no change needed at ${filePath}` };
|
|
7575
|
-
}
|
|
7576
|
-
try {
|
|
7577
|
-
atomicWriteJson(filePath, settings);
|
|
7578
|
-
} catch (err) {
|
|
7579
|
-
throw new Error(`merge-settings: failed to write settings.json: ${err?.message ?? err}`);
|
|
7580
|
-
}
|
|
7581
|
-
return { status: "installed", message: `settings.json updated at ${filePath}` };
|
|
7582
|
-
}
|
|
7583
|
-
function readSettings(filePath) {
|
|
7584
|
-
if (!fs12.existsSync(filePath)) {
|
|
7585
|
-
return {};
|
|
7586
|
-
}
|
|
7587
|
-
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
7588
|
-
if (!raw.trim())
|
|
7589
|
-
return {};
|
|
7590
|
-
return JSON.parse(raw);
|
|
7591
|
-
}
|
|
7592
|
-
function isHookSentinelPresent(matchers, sentinel) {
|
|
7593
|
-
for (const matcher of matchers) {
|
|
7594
|
-
if (typeof matcher?.command === "string" && matcher.command.includes(sentinel)) {
|
|
7595
|
-
return true;
|
|
7596
|
-
}
|
|
7597
|
-
if (Array.isArray(matcher?.hooks)) {
|
|
7598
|
-
for (const h of matcher.hooks) {
|
|
7599
|
-
if (typeof h?.command === "string" && h.command.includes(sentinel)) {
|
|
7600
|
-
return true;
|
|
7601
|
-
}
|
|
7602
|
-
}
|
|
7603
|
-
}
|
|
7604
|
-
}
|
|
7605
|
-
return false;
|
|
7606
|
-
}
|
|
7607
|
-
function atomicWriteJson(filePath, data) {
|
|
7608
|
-
const dir = path13.dirname(filePath);
|
|
7609
|
-
fs12.mkdirSync(dir, { recursive: true });
|
|
7610
|
-
const rand = crypto2.randomBytes(6).toString("hex");
|
|
7611
|
-
const tmp = `${filePath}.tmp.${process.pid}.${rand}`;
|
|
7612
|
-
const json = JSON.stringify(data, null, 2) + "\n";
|
|
7613
|
-
fs12.writeFileSync(tmp, json, { mode: 420 });
|
|
7614
|
-
fs12.renameSync(tmp, filePath);
|
|
7615
|
-
}
|
|
7616
|
-
var init_merge_settings = __esm({
|
|
7617
|
-
"../core/dist/world/merge-settings.js"() {
|
|
7618
|
-
"use strict";
|
|
7619
|
-
}
|
|
7620
|
-
});
|
|
7621
|
-
|
|
7622
7572
|
// ../core/dist/world/env-setup.js
|
|
7623
|
-
import * as
|
|
7624
|
-
import * as
|
|
7573
|
+
import * as crypto2 from "node:crypto";
|
|
7574
|
+
import * as fs12 from "node:fs";
|
|
7625
7575
|
import * as os8 from "node:os";
|
|
7626
|
-
import * as
|
|
7576
|
+
import * as path13 from "node:path";
|
|
7627
7577
|
import { globSync } from "node:fs";
|
|
7628
7578
|
function copyClaudeConfig(workspacePath, homeDir, configCtx) {
|
|
7629
|
-
const sourceClaudeDir =
|
|
7630
|
-
const destClaudeDir =
|
|
7579
|
+
const sourceClaudeDir = path13.join(homeDir ?? os8.homedir(), ".claude");
|
|
7580
|
+
const destClaudeDir = path13.join(workspacePath, ".claude-host-config");
|
|
7631
7581
|
void configCtx;
|
|
7632
|
-
if (!
|
|
7582
|
+
if (!fs12.existsSync(sourceClaudeDir))
|
|
7633
7583
|
return;
|
|
7634
|
-
|
|
7635
|
-
const settingsPath =
|
|
7584
|
+
fs12.mkdirSync(destClaudeDir, { recursive: true });
|
|
7585
|
+
const settingsPath = path13.join(sourceClaudeDir, "settings.json");
|
|
7636
7586
|
let settings = {};
|
|
7637
|
-
if (
|
|
7587
|
+
if (fs12.existsSync(settingsPath)) {
|
|
7638
7588
|
try {
|
|
7639
|
-
settings = JSON.parse(
|
|
7589
|
+
settings = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
|
|
7640
7590
|
delete settings["hooks"];
|
|
7641
7591
|
} catch {
|
|
7642
7592
|
}
|
|
7643
7593
|
}
|
|
7644
7594
|
const hooksConfig = generateHooksConfig(DEFAULT_HOOK_SERVER_URL);
|
|
7645
7595
|
settings["hooks"] = hooksConfig["hooks"];
|
|
7646
|
-
|
|
7647
|
-
const claudeMdPath =
|
|
7648
|
-
if (
|
|
7649
|
-
|
|
7596
|
+
fs12.writeFileSync(path13.join(destClaudeDir, "settings.json"), JSON.stringify(settings, null, 2));
|
|
7597
|
+
const claudeMdPath = path13.join(sourceClaudeDir, "CLAUDE.md");
|
|
7598
|
+
if (fs12.existsSync(claudeMdPath)) {
|
|
7599
|
+
fs12.copyFileSync(claudeMdPath, path13.join(destClaudeDir, "CLAUDE.md"));
|
|
7650
7600
|
}
|
|
7651
|
-
const rulesDir =
|
|
7652
|
-
if (
|
|
7653
|
-
copyDirRecursive(rulesDir,
|
|
7601
|
+
const rulesDir = path13.join(sourceClaudeDir, "rules");
|
|
7602
|
+
if (fs12.existsSync(rulesDir)) {
|
|
7603
|
+
copyDirRecursive(rulesDir, path13.join(destClaudeDir, "rules"));
|
|
7654
7604
|
}
|
|
7655
|
-
const agentsDir =
|
|
7656
|
-
if (
|
|
7657
|
-
copyDirRecursive(agentsDir,
|
|
7605
|
+
const agentsDir = path13.join(sourceClaudeDir, "agents");
|
|
7606
|
+
if (fs12.existsSync(agentsDir)) {
|
|
7607
|
+
copyDirRecursive(agentsDir, path13.join(destClaudeDir, "agents"));
|
|
7658
7608
|
}
|
|
7659
|
-
const pluginsDir =
|
|
7660
|
-
if (
|
|
7661
|
-
copyDirRecursive(pluginsDir,
|
|
7609
|
+
const pluginsDir = path13.join(sourceClaudeDir, "plugins");
|
|
7610
|
+
if (fs12.existsSync(pluginsDir)) {
|
|
7611
|
+
copyDirRecursive(pluginsDir, path13.join(destClaudeDir, "plugins"), 0, SKIP_FILES);
|
|
7662
7612
|
}
|
|
7663
|
-
const skillsDir =
|
|
7664
|
-
if (
|
|
7665
|
-
copyDirRecursive(skillsDir,
|
|
7613
|
+
const skillsDir = path13.join(sourceClaudeDir, "skills");
|
|
7614
|
+
if (fs12.existsSync(skillsDir)) {
|
|
7615
|
+
copyDirRecursive(skillsDir, path13.join(destClaudeDir, "skills"));
|
|
7666
7616
|
}
|
|
7667
|
-
const scriptsDir =
|
|
7668
|
-
if (
|
|
7669
|
-
copyDirRecursive(scriptsDir,
|
|
7617
|
+
const scriptsDir = path13.join(sourceClaudeDir, "scripts");
|
|
7618
|
+
if (fs12.existsSync(scriptsDir)) {
|
|
7619
|
+
copyDirRecursive(scriptsDir, path13.join(destClaudeDir, "scripts"));
|
|
7670
7620
|
}
|
|
7671
7621
|
applyProjectClaudeOverlay(workspacePath, destClaudeDir);
|
|
7672
7622
|
writeStrippedMcpServersSnapshot(homeDir ?? os8.homedir(), workspacePath, destClaudeDir);
|
|
7673
|
-
if (configCtx != null && configCtx.worlds_default?.agent_teams_enabled !== false) {
|
|
7674
|
-
mergeHomeSettingsJson(path14.join(destClaudeDir, "settings.json"), {
|
|
7675
|
-
env: { CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" }
|
|
7676
|
-
});
|
|
7677
|
-
}
|
|
7678
7623
|
}
|
|
7679
7624
|
function applyProjectClaudeOverlay(workspacePath, destClaudeDir) {
|
|
7680
|
-
const projectClaudeDir =
|
|
7681
|
-
if (!
|
|
7625
|
+
const projectClaudeDir = path13.join(workspacePath, ".claude");
|
|
7626
|
+
if (!fs12.existsSync(projectClaudeDir))
|
|
7682
7627
|
return;
|
|
7683
|
-
const projectSettingsPath =
|
|
7684
|
-
const destSettingsPath =
|
|
7685
|
-
if (
|
|
7628
|
+
const projectSettingsPath = path13.join(projectClaudeDir, "settings.json");
|
|
7629
|
+
const destSettingsPath = path13.join(destClaudeDir, "settings.json");
|
|
7630
|
+
if (fs12.existsSync(projectSettingsPath)) {
|
|
7686
7631
|
try {
|
|
7687
|
-
const projectSettings = JSON.parse(
|
|
7632
|
+
const projectSettings = JSON.parse(fs12.readFileSync(projectSettingsPath, "utf-8"));
|
|
7688
7633
|
delete projectSettings["hooks"];
|
|
7689
|
-
const existing =
|
|
7634
|
+
const existing = fs12.existsSync(destSettingsPath) ? JSON.parse(fs12.readFileSync(destSettingsPath, "utf-8")) : {};
|
|
7690
7635
|
const merged = { ...existing, ...projectSettings, hooks: existing["hooks"] };
|
|
7691
|
-
|
|
7636
|
+
fs12.writeFileSync(destSettingsPath, JSON.stringify(merged, null, 2));
|
|
7692
7637
|
} catch {
|
|
7693
7638
|
}
|
|
7694
7639
|
}
|
|
7695
|
-
const projectClaudeMd =
|
|
7696
|
-
if (
|
|
7697
|
-
|
|
7640
|
+
const projectClaudeMd = path13.join(projectClaudeDir, "CLAUDE.md");
|
|
7641
|
+
if (fs12.existsSync(projectClaudeMd)) {
|
|
7642
|
+
fs12.copyFileSync(projectClaudeMd, path13.join(destClaudeDir, "CLAUDE.md"));
|
|
7698
7643
|
}
|
|
7699
|
-
const projectAgentsMd =
|
|
7700
|
-
if (
|
|
7701
|
-
|
|
7644
|
+
const projectAgentsMd = path13.join(projectClaudeDir, "AGENTS.md");
|
|
7645
|
+
if (fs12.existsSync(projectAgentsMd)) {
|
|
7646
|
+
fs12.copyFileSync(projectAgentsMd, path13.join(destClaudeDir, "AGENTS.md"));
|
|
7702
7647
|
}
|
|
7703
7648
|
for (const subdir of ["rules", "agents", "plugins", "skills", "scripts"]) {
|
|
7704
|
-
const projectSubdir =
|
|
7705
|
-
if (
|
|
7649
|
+
const projectSubdir = path13.join(projectClaudeDir, subdir);
|
|
7650
|
+
if (fs12.existsSync(projectSubdir)) {
|
|
7706
7651
|
const skip = subdir === "plugins" ? SKIP_FILES : /* @__PURE__ */ new Set();
|
|
7707
|
-
copyDirRecursive(projectSubdir,
|
|
7652
|
+
copyDirRecursive(projectSubdir, path13.join(destClaudeDir, subdir), 0, skip);
|
|
7708
7653
|
}
|
|
7709
7654
|
}
|
|
7710
7655
|
}
|
|
@@ -7734,8 +7679,8 @@ function stripMcpServers(mcpServers) {
|
|
|
7734
7679
|
return out;
|
|
7735
7680
|
}
|
|
7736
7681
|
function writeStrippedMcpServersSnapshot(homeDir, workspacePath, destClaudeDir) {
|
|
7737
|
-
const hostMcpServers = readMcpServersFromFile(
|
|
7738
|
-
const projectMcpServers = readMcpServersFromFile(
|
|
7682
|
+
const hostMcpServers = readMcpServersFromFile(path13.join(homeDir, ".claude.json"));
|
|
7683
|
+
const projectMcpServers = readMcpServersFromFile(path13.join(workspacePath, ".mcp.json"));
|
|
7739
7684
|
if (Object.keys(hostMcpServers).length === 0 && Object.keys(projectMcpServers).length === 0) {
|
|
7740
7685
|
return;
|
|
7741
7686
|
}
|
|
@@ -7744,11 +7689,11 @@ function writeStrippedMcpServersSnapshot(homeDir, workspacePath, destClaudeDir)
|
|
|
7744
7689
|
...stripMcpServers(projectMcpServers)
|
|
7745
7690
|
};
|
|
7746
7691
|
const output = { mcpServers: stripped };
|
|
7747
|
-
|
|
7692
|
+
fs12.writeFileSync(path13.join(destClaudeDir, ".claude.json"), JSON.stringify(output, null, 2), { mode: 384 });
|
|
7748
7693
|
}
|
|
7749
7694
|
function writeWorldEntitlementsJson(workspacePath, configCtx) {
|
|
7750
|
-
const olamDir =
|
|
7751
|
-
|
|
7695
|
+
const olamDir = path13.join(workspacePath, ".olam");
|
|
7696
|
+
fs12.mkdirSync(olamDir, { recursive: true });
|
|
7752
7697
|
const resolvedRepos = configCtx.repos.map((repo) => {
|
|
7753
7698
|
const repoEntitlement = repo;
|
|
7754
7699
|
return {
|
|
@@ -7762,14 +7707,14 @@ function writeWorldEntitlementsJson(workspacePath, configCtx) {
|
|
|
7762
7707
|
worlds_default: configCtx.worlds_default,
|
|
7763
7708
|
repos: resolvedRepos
|
|
7764
7709
|
};
|
|
7765
|
-
const filePath =
|
|
7766
|
-
|
|
7710
|
+
const filePath = path13.join(olamDir, "world-entitlements.json");
|
|
7711
|
+
fs12.writeFileSync(filePath, JSON.stringify(resolved, null, 2), { mode: 420 });
|
|
7767
7712
|
}
|
|
7768
7713
|
function readMcpServersFromFile(filePath) {
|
|
7769
|
-
if (!
|
|
7714
|
+
if (!fs12.existsSync(filePath))
|
|
7770
7715
|
return {};
|
|
7771
7716
|
try {
|
|
7772
|
-
const raw =
|
|
7717
|
+
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
7773
7718
|
if (!raw.trim())
|
|
7774
7719
|
return {};
|
|
7775
7720
|
const parsed = JSON.parse(raw);
|
|
@@ -7784,8 +7729,8 @@ function readMcpServersFromFile(filePath) {
|
|
|
7784
7729
|
function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new Set()) {
|
|
7785
7730
|
if (depth > 10)
|
|
7786
7731
|
return;
|
|
7787
|
-
|
|
7788
|
-
for (const entry of
|
|
7732
|
+
fs12.mkdirSync(dest, { recursive: true });
|
|
7733
|
+
for (const entry of fs12.readdirSync(src, { withFileTypes: true })) {
|
|
7789
7734
|
const { name } = entry;
|
|
7790
7735
|
if (skipFiles.has(name))
|
|
7791
7736
|
continue;
|
|
@@ -7795,12 +7740,12 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
|
|
|
7795
7740
|
continue;
|
|
7796
7741
|
if (entry.isDirectory() && SKIP_DIRS.has(name))
|
|
7797
7742
|
continue;
|
|
7798
|
-
const srcPath =
|
|
7799
|
-
const destPath =
|
|
7743
|
+
const srcPath = path13.join(src, name);
|
|
7744
|
+
const destPath = path13.join(dest, name);
|
|
7800
7745
|
if (entry.isSymbolicLink()) {
|
|
7801
7746
|
let stat;
|
|
7802
7747
|
try {
|
|
7803
|
-
stat =
|
|
7748
|
+
stat = fs12.statSync(srcPath);
|
|
7804
7749
|
} catch {
|
|
7805
7750
|
continue;
|
|
7806
7751
|
}
|
|
@@ -7809,12 +7754,12 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
|
|
|
7809
7754
|
continue;
|
|
7810
7755
|
copyDirRecursive(srcPath, destPath, depth + 1, skipFiles);
|
|
7811
7756
|
} else if (stat.isFile()) {
|
|
7812
|
-
|
|
7757
|
+
fs12.copyFileSync(srcPath, destPath);
|
|
7813
7758
|
}
|
|
7814
7759
|
} else if (entry.isDirectory()) {
|
|
7815
7760
|
copyDirRecursive(srcPath, destPath, depth + 1, skipFiles);
|
|
7816
7761
|
} else {
|
|
7817
|
-
|
|
7762
|
+
fs12.copyFileSync(srcPath, destPath);
|
|
7818
7763
|
}
|
|
7819
7764
|
}
|
|
7820
7765
|
}
|
|
@@ -7952,9 +7897,9 @@ function setupWorldEnv(repos, workspacePath, serviceEnv, crossRepoEnv = {}, conf
|
|
|
7952
7897
|
}
|
|
7953
7898
|
}
|
|
7954
7899
|
for (const repo of repos) {
|
|
7955
|
-
const worktreePath =
|
|
7900
|
+
const worktreePath = path13.join(workspacePath, repo.name);
|
|
7956
7901
|
const sourcePath = repo.path;
|
|
7957
|
-
if (!sourcePath || !
|
|
7902
|
+
if (!sourcePath || !fs12.existsSync(worktreePath))
|
|
7958
7903
|
continue;
|
|
7959
7904
|
const envSetup = repo.env_setup;
|
|
7960
7905
|
if (!envSetup)
|
|
@@ -7973,23 +7918,23 @@ function setupWorldEnv(repos, workspacePath, serviceEnv, crossRepoEnv = {}, conf
|
|
|
7973
7918
|
}
|
|
7974
7919
|
}
|
|
7975
7920
|
function copyMatchingFiles(sourcePath, destPath, pattern) {
|
|
7976
|
-
const fullPattern =
|
|
7921
|
+
const fullPattern = path13.join(sourcePath, pattern);
|
|
7977
7922
|
if (!pattern.includes("*")) {
|
|
7978
|
-
const sourceFile =
|
|
7979
|
-
const destFile =
|
|
7980
|
-
if (
|
|
7981
|
-
|
|
7982
|
-
|
|
7923
|
+
const sourceFile = path13.join(sourcePath, pattern);
|
|
7924
|
+
const destFile = path13.join(destPath, pattern);
|
|
7925
|
+
if (fs12.existsSync(sourceFile)) {
|
|
7926
|
+
fs12.mkdirSync(path13.dirname(destFile), { recursive: true });
|
|
7927
|
+
fs12.copyFileSync(sourceFile, destFile);
|
|
7983
7928
|
}
|
|
7984
7929
|
return;
|
|
7985
7930
|
}
|
|
7986
7931
|
try {
|
|
7987
7932
|
const matches2 = globSync(fullPattern);
|
|
7988
7933
|
for (const match2 of matches2) {
|
|
7989
|
-
const relative2 =
|
|
7990
|
-
const dest =
|
|
7991
|
-
|
|
7992
|
-
|
|
7934
|
+
const relative2 = path13.relative(sourcePath, match2);
|
|
7935
|
+
const dest = path13.join(destPath, relative2);
|
|
7936
|
+
fs12.mkdirSync(path13.dirname(dest), { recursive: true });
|
|
7937
|
+
fs12.copyFileSync(match2, dest);
|
|
7993
7938
|
}
|
|
7994
7939
|
} catch {
|
|
7995
7940
|
}
|
|
@@ -7997,14 +7942,14 @@ function copyMatchingFiles(sourcePath, destPath, pattern) {
|
|
|
7997
7942
|
function generateEnvFromExample(repoPath, overrides) {
|
|
7998
7943
|
const exampleFiles = [".env.example", ".env.sample", ".env.local.example"];
|
|
7999
7944
|
for (const exampleName of exampleFiles) {
|
|
8000
|
-
const examplePath =
|
|
8001
|
-
if (!
|
|
7945
|
+
const examplePath = path13.join(repoPath, exampleName);
|
|
7946
|
+
if (!fs12.existsSync(examplePath))
|
|
8002
7947
|
continue;
|
|
8003
7948
|
const targetName = exampleName.replace(".example", "").replace(".sample", "");
|
|
8004
|
-
const targetPath =
|
|
8005
|
-
if (
|
|
7949
|
+
const targetPath = path13.join(repoPath, targetName);
|
|
7950
|
+
if (fs12.existsSync(targetPath))
|
|
8006
7951
|
continue;
|
|
8007
|
-
const template =
|
|
7952
|
+
const template = fs12.readFileSync(examplePath, "utf-8");
|
|
8008
7953
|
const generated = template.split("\n").map((line) => {
|
|
8009
7954
|
const match2 = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
|
|
8010
7955
|
if (match2) {
|
|
@@ -8015,13 +7960,13 @@ function generateEnvFromExample(repoPath, overrides) {
|
|
|
8015
7960
|
}
|
|
8016
7961
|
return line;
|
|
8017
7962
|
}).join("\n");
|
|
8018
|
-
|
|
7963
|
+
fs12.writeFileSync(targetPath, generated);
|
|
8019
7964
|
}
|
|
8020
7965
|
}
|
|
8021
7966
|
function applyEnvOverrides(repoPath, overrides) {
|
|
8022
|
-
const envPath =
|
|
8023
|
-
if (
|
|
8024
|
-
const existing =
|
|
7967
|
+
const envPath = path13.join(repoPath, ".env");
|
|
7968
|
+
if (fs12.existsSync(envPath)) {
|
|
7969
|
+
const existing = fs12.readFileSync(envPath, "utf-8");
|
|
8025
7970
|
const existingKeys = new Set(existing.split("\n").map((l) => l.split("=")[0]?.trim()).filter(Boolean));
|
|
8026
7971
|
const additions = [];
|
|
8027
7972
|
for (const [key, value] of Object.entries(overrides)) {
|
|
@@ -8030,7 +7975,7 @@ function applyEnvOverrides(repoPath, overrides) {
|
|
|
8030
7975
|
}
|
|
8031
7976
|
}
|
|
8032
7977
|
if (additions.length > 0) {
|
|
8033
|
-
|
|
7978
|
+
fs12.appendFileSync(envPath, "\n# Olam injected\n" + additions.join("\n") + "\n");
|
|
8034
7979
|
}
|
|
8035
7980
|
}
|
|
8036
7981
|
}
|
|
@@ -8039,7 +7984,6 @@ var init_env_setup = __esm({
|
|
|
8039
7984
|
"../core/dist/world/env-setup.js"() {
|
|
8040
7985
|
"use strict";
|
|
8041
7986
|
init_hooks_config();
|
|
8042
|
-
init_merge_settings();
|
|
8043
7987
|
MCP_SERVER_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
8044
7988
|
"command",
|
|
8045
7989
|
"args",
|
|
@@ -8103,9 +8047,9 @@ var init_env_setup = __esm({
|
|
|
8103
8047
|
|
|
8104
8048
|
// ../core/dist/world/baseline-diff.js
|
|
8105
8049
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
8106
|
-
import * as
|
|
8050
|
+
import * as fs13 from "node:fs";
|
|
8107
8051
|
import * as os9 from "node:os";
|
|
8108
|
-
import * as
|
|
8052
|
+
import * as path14 from "node:path";
|
|
8109
8053
|
function expandHome(p, homedir22) {
|
|
8110
8054
|
return p.replace(/^~(?=$|\/|\\)/, homedir22());
|
|
8111
8055
|
}
|
|
@@ -8131,9 +8075,9 @@ ${stderr}`;
|
|
|
8131
8075
|
function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
8132
8076
|
const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
|
|
8133
8077
|
const homedir22 = deps.homedir ?? (() => os9.homedir());
|
|
8134
|
-
const baselineDir =
|
|
8078
|
+
const baselineDir = path14.join(workspacePath, ".olam", "baseline");
|
|
8135
8079
|
try {
|
|
8136
|
-
|
|
8080
|
+
fs13.mkdirSync(baselineDir, { recursive: true });
|
|
8137
8081
|
} catch (err) {
|
|
8138
8082
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8139
8083
|
console.warn(`[baseline-diff] mkdir ${baselineDir} failed: ${msg}; reaper will see no baseline at all`);
|
|
@@ -8145,9 +8089,9 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
8145
8089
|
if (!repo.path)
|
|
8146
8090
|
continue;
|
|
8147
8091
|
const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
|
|
8148
|
-
const outPath =
|
|
8092
|
+
const outPath = path14.join(baselineDir, filename);
|
|
8149
8093
|
const repoPath = expandHome(repo.path, homedir22);
|
|
8150
|
-
if (!
|
|
8094
|
+
if (!fs13.existsSync(repoPath)) {
|
|
8151
8095
|
writeBaselineFile(outPath, `# repo: ${repo.name}
|
|
8152
8096
|
# (skipped: path ${repoPath} does not exist)
|
|
8153
8097
|
`);
|
|
@@ -8214,7 +8158,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
|
|
|
8214
8158
|
}
|
|
8215
8159
|
function writeBaselineFile(outPath, content) {
|
|
8216
8160
|
try {
|
|
8217
|
-
|
|
8161
|
+
fs13.writeFileSync(outPath, content);
|
|
8218
8162
|
} catch (err) {
|
|
8219
8163
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8220
8164
|
console.warn(`[baseline-diff] write to ${outPath} failed: ${msg}`);
|
|
@@ -8222,8 +8166,8 @@ function writeBaselineFile(outPath, content) {
|
|
|
8222
8166
|
}
|
|
8223
8167
|
function stripWorktreeEdits(repos, workspacePath) {
|
|
8224
8168
|
for (const repo of repos) {
|
|
8225
|
-
const worktreePath =
|
|
8226
|
-
if (!
|
|
8169
|
+
const worktreePath = path14.join(workspacePath, repo.name);
|
|
8170
|
+
if (!fs13.existsSync(worktreePath))
|
|
8227
8171
|
continue;
|
|
8228
8172
|
try {
|
|
8229
8173
|
execFileSync3("git", ["checkout", "--", "."], {
|
|
@@ -8260,12 +8204,12 @@ var init_baseline_diff = __esm({
|
|
|
8260
8204
|
});
|
|
8261
8205
|
|
|
8262
8206
|
// ../core/dist/world/context-injection.js
|
|
8263
|
-
import * as
|
|
8264
|
-
import * as
|
|
8207
|
+
import * as fs14 from "node:fs";
|
|
8208
|
+
import * as path15 from "node:path";
|
|
8265
8209
|
function injectWorldContext(opts) {
|
|
8266
8210
|
const { world, task, linearTicketId, claudeMdExtra, taskContext, services, pleriPlaneUrl } = opts;
|
|
8267
|
-
const claudeDir =
|
|
8268
|
-
|
|
8211
|
+
const claudeDir = path15.join(world.workspacePath, ".claude");
|
|
8212
|
+
fs14.mkdirSync(claudeDir, { recursive: true });
|
|
8269
8213
|
const sections = [];
|
|
8270
8214
|
sections.push(`# Olam World: ${world.name}`);
|
|
8271
8215
|
sections.push("");
|
|
@@ -8426,7 +8370,7 @@ function injectWorldContext(opts) {
|
|
|
8426
8370
|
sections.push("");
|
|
8427
8371
|
}
|
|
8428
8372
|
const content = sections.join("\n");
|
|
8429
|
-
|
|
8373
|
+
fs14.writeFileSync(path15.join(claudeDir, "CLAUDE.md"), content);
|
|
8430
8374
|
}
|
|
8431
8375
|
function formatTaskSource(ctx) {
|
|
8432
8376
|
if (ctx.source === "linear" && ctx.ticketId) {
|
|
@@ -8440,9 +8384,9 @@ function formatTaskSource(ctx) {
|
|
|
8440
8384
|
function hasPlanFile(world) {
|
|
8441
8385
|
if (world.repos.length === 0)
|
|
8442
8386
|
return false;
|
|
8443
|
-
const plansDir =
|
|
8387
|
+
const plansDir = path15.join(world.workspacePath, world.repos[0], "docs", "plans");
|
|
8444
8388
|
try {
|
|
8445
|
-
return
|
|
8389
|
+
return fs14.existsSync(plansDir) && fs14.readdirSync(plansDir).length > 0;
|
|
8446
8390
|
} catch {
|
|
8447
8391
|
return false;
|
|
8448
8392
|
}
|
|
@@ -8687,7 +8631,7 @@ var init_stack_install = __esm({
|
|
|
8687
8631
|
|
|
8688
8632
|
// ../core/dist/world/stack-image.js
|
|
8689
8633
|
import { execSync as execSync2 } from "node:child_process";
|
|
8690
|
-
import * as
|
|
8634
|
+
import * as crypto3 from "node:crypto";
|
|
8691
8635
|
function computeFingerprint(runtimes) {
|
|
8692
8636
|
const parts = [];
|
|
8693
8637
|
for (const [runtime, versions] of runtimes) {
|
|
@@ -8737,14 +8681,14 @@ function getBaseImageDigest() {
|
|
|
8737
8681
|
cachedBaseDigest = digest.replace("sha256:", "").slice(0, 16);
|
|
8738
8682
|
return cachedBaseDigest;
|
|
8739
8683
|
} catch {
|
|
8740
|
-
cachedBaseDigest =
|
|
8684
|
+
cachedBaseDigest = crypto3.createHash("sha256").update("unknown").digest("hex").slice(0, 16);
|
|
8741
8685
|
return cachedBaseDigest;
|
|
8742
8686
|
}
|
|
8743
8687
|
}
|
|
8744
8688
|
function sanitizeTag(raw) {
|
|
8745
8689
|
let tag = raw.toLowerCase().replace(/[^a-z0-9._-]/g, "-");
|
|
8746
8690
|
if (tag.length > MAX_TAG_LENGTH) {
|
|
8747
|
-
const hash =
|
|
8691
|
+
const hash = crypto3.createHash("sha256").update(raw).digest("hex").slice(0, 12);
|
|
8748
8692
|
tag = tag.slice(0, MAX_TAG_LENGTH - 13) + "_" + hash;
|
|
8749
8693
|
}
|
|
8750
8694
|
return tag;
|
|
@@ -9146,14 +9090,14 @@ var init_secrets_fetcher = __esm({
|
|
|
9146
9090
|
});
|
|
9147
9091
|
|
|
9148
9092
|
// ../core/dist/world/olam-yaml.js
|
|
9149
|
-
import * as
|
|
9093
|
+
import * as path16 from "node:path";
|
|
9150
9094
|
import YAML2 from "yaml";
|
|
9151
9095
|
function enrichReposWithManifests(repos, workspacePath) {
|
|
9152
9096
|
return repos.map((repo) => {
|
|
9153
9097
|
if (repo.manifest !== void 0 && repo.manifest !== null) {
|
|
9154
9098
|
return repo;
|
|
9155
9099
|
}
|
|
9156
|
-
const repoDir =
|
|
9100
|
+
const repoDir = path16.join(workspacePath, repo.name);
|
|
9157
9101
|
let manifest = null;
|
|
9158
9102
|
try {
|
|
9159
9103
|
manifest = loadRepoManifest(repoDir);
|
|
@@ -9174,8 +9118,8 @@ var init_olam_yaml = __esm({
|
|
|
9174
9118
|
});
|
|
9175
9119
|
|
|
9176
9120
|
// ../core/dist/policies/loader.js
|
|
9177
|
-
import * as
|
|
9178
|
-
import * as
|
|
9121
|
+
import * as fs15 from "node:fs";
|
|
9122
|
+
import * as path17 from "node:path";
|
|
9179
9123
|
import { parse as parseYaml3 } from "yaml";
|
|
9180
9124
|
function parseFrontmatter(content) {
|
|
9181
9125
|
const match2 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
|
|
@@ -9195,20 +9139,20 @@ function toStringArray(v) {
|
|
|
9195
9139
|
return v.filter((x) => typeof x === "string");
|
|
9196
9140
|
}
|
|
9197
9141
|
function loadPolicies(workspaceRoot) {
|
|
9198
|
-
const policiesDir =
|
|
9199
|
-
if (!
|
|
9142
|
+
const policiesDir = path17.join(workspaceRoot, ".olam", "policies");
|
|
9143
|
+
if (!fs15.existsSync(policiesDir))
|
|
9200
9144
|
return [];
|
|
9201
9145
|
let files;
|
|
9202
9146
|
try {
|
|
9203
|
-
files =
|
|
9147
|
+
files = fs15.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
|
|
9204
9148
|
} catch {
|
|
9205
9149
|
return [];
|
|
9206
9150
|
}
|
|
9207
9151
|
const policies = [];
|
|
9208
9152
|
for (const file of files) {
|
|
9209
|
-
const filePath =
|
|
9153
|
+
const filePath = path17.join(policiesDir, file);
|
|
9210
9154
|
try {
|
|
9211
|
-
const content =
|
|
9155
|
+
const content = fs15.readFileSync(filePath, "utf8");
|
|
9212
9156
|
const parsed = parseFrontmatter(content);
|
|
9213
9157
|
if (!parsed) {
|
|
9214
9158
|
console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
|
|
@@ -9283,7 +9227,7 @@ async function startSupervisedApps(containerName, worldId, repos, exec, options
|
|
|
9283
9227
|
const probeTimeoutMs = options.probeTimeoutMs ?? 3e4;
|
|
9284
9228
|
const probeIntervalMs = options.probeIntervalMs ?? 1e3;
|
|
9285
9229
|
const clock = options.clock ?? Date.now;
|
|
9286
|
-
const
|
|
9230
|
+
const sleep4 = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
9287
9231
|
for (const repo of repos) {
|
|
9288
9232
|
if (isPortBound(exec, containerName, repo.hostPort)) {
|
|
9289
9233
|
throw new PortInUseError(repo.hostPort, repo.name, repo.manifestPath);
|
|
@@ -9308,7 +9252,7 @@ async function startSupervisedApps(containerName, worldId, repos, exec, options
|
|
|
9308
9252
|
probeTimeoutMs,
|
|
9309
9253
|
probeIntervalMs,
|
|
9310
9254
|
clock,
|
|
9311
|
-
sleep:
|
|
9255
|
+
sleep: sleep4
|
|
9312
9256
|
})));
|
|
9313
9257
|
return {
|
|
9314
9258
|
sessionName,
|
|
@@ -9372,7 +9316,6 @@ var manager_exports = {};
|
|
|
9372
9316
|
__export(manager_exports, {
|
|
9373
9317
|
AuthPreflightError: () => AuthPreflightError,
|
|
9374
9318
|
BotIdentityError: () => BotIdentityError,
|
|
9375
|
-
RepoSelectionRequiredError: () => RepoSelectionRequiredError,
|
|
9376
9319
|
WorkspaceNotFoundError: () => WorkspaceNotFoundError,
|
|
9377
9320
|
WorldManager: () => WorldManager,
|
|
9378
9321
|
buildManifestRuntimeForTest: () => buildManifestRuntime,
|
|
@@ -9381,11 +9324,11 @@ __export(manager_exports, {
|
|
|
9381
9324
|
planManifestPipeline: () => planManifestPipeline,
|
|
9382
9325
|
runManifestRuntime: () => runManifestRuntime
|
|
9383
9326
|
});
|
|
9384
|
-
import * as
|
|
9327
|
+
import * as crypto4 from "node:crypto";
|
|
9385
9328
|
import { execSync as execSync4 } from "node:child_process";
|
|
9386
|
-
import * as
|
|
9329
|
+
import * as fs16 from "node:fs";
|
|
9387
9330
|
import * as os10 from "node:os";
|
|
9388
|
-
import * as
|
|
9331
|
+
import * as path18 from "node:path";
|
|
9389
9332
|
import YAML3 from "yaml";
|
|
9390
9333
|
function getTokenScopes(ghToken, _exec = execSync4) {
|
|
9391
9334
|
try {
|
|
@@ -9724,7 +9667,7 @@ function buildManifestRuntime(worldId, repos) {
|
|
|
9724
9667
|
}
|
|
9725
9668
|
return { worldId, repos: runtimeRepos };
|
|
9726
9669
|
}
|
|
9727
|
-
var BotIdentityError, ADJECTIVES, NOUNS, AuthPreflightError, WorkspaceNotFoundError,
|
|
9670
|
+
var BotIdentityError, ADJECTIVES, NOUNS, AuthPreflightError, WorkspaceNotFoundError, WorldManager;
|
|
9728
9671
|
var init_manager = __esm({
|
|
9729
9672
|
"../core/dist/world/manager.js"() {
|
|
9730
9673
|
"use strict";
|
|
@@ -9773,15 +9716,6 @@ var init_manager = __esm({
|
|
|
9773
9716
|
this.name = "WorkspaceNotFoundError";
|
|
9774
9717
|
}
|
|
9775
9718
|
};
|
|
9776
|
-
RepoSelectionRequiredError = class extends Error {
|
|
9777
|
-
availableRepos;
|
|
9778
|
-
constructor(availableRepos) {
|
|
9779
|
-
const list = availableRepos.length > 0 ? availableRepos.join(", ") : "(none configured)";
|
|
9780
|
-
super(`--repos or --workspace is required. Available repos: ${list}. Pick the subset you actually want \u2014 implicit "all repos" was removed to prevent accidental multi-repo bootstraps.`);
|
|
9781
|
-
this.availableRepos = availableRepos;
|
|
9782
|
-
this.name = "RepoSelectionRequiredError";
|
|
9783
|
-
}
|
|
9784
|
-
};
|
|
9785
9719
|
WorldManager = class {
|
|
9786
9720
|
config;
|
|
9787
9721
|
provider;
|
|
@@ -9809,7 +9743,7 @@ var init_manager = __esm({
|
|
|
9809
9743
|
}
|
|
9810
9744
|
}
|
|
9811
9745
|
const worldId = generateWorldId();
|
|
9812
|
-
const workspacePath =
|
|
9746
|
+
const workspacePath = path18.join(os10.homedir(), ".olam", "worlds", worldId);
|
|
9813
9747
|
const portOffset = this.registry.getNextPortOffset();
|
|
9814
9748
|
const branch = opts.branchName ?? `olam/${worldId}`;
|
|
9815
9749
|
const repos = this.resolveReposWithWorkspace(opts);
|
|
@@ -9866,37 +9800,37 @@ var init_manager = __esm({
|
|
|
9866
9800
|
if (!repo.path)
|
|
9867
9801
|
continue;
|
|
9868
9802
|
const sourceRoot = repo.path.replace(/^~/, os10.homedir());
|
|
9869
|
-
const worktreeRoot =
|
|
9870
|
-
if (!
|
|
9803
|
+
const worktreeRoot = path18.join(workspacePath, repo.name);
|
|
9804
|
+
if (!fs16.existsSync(sourceRoot) || !fs16.existsSync(worktreeRoot))
|
|
9871
9805
|
continue;
|
|
9872
9806
|
let copied = 0;
|
|
9873
9807
|
for (const pattern of RUNTIME_FILE_PATTERNS) {
|
|
9874
9808
|
const matches2 = [];
|
|
9875
9809
|
if (pattern.includes("*")) {
|
|
9876
|
-
const [dir, glob] = [
|
|
9877
|
-
const sourceDir =
|
|
9878
|
-
if (
|
|
9810
|
+
const [dir, glob] = [path18.dirname(pattern), path18.basename(pattern)];
|
|
9811
|
+
const sourceDir = path18.join(sourceRoot, dir);
|
|
9812
|
+
if (fs16.existsSync(sourceDir)) {
|
|
9879
9813
|
const ext2 = glob.replace(/^\*+/, "");
|
|
9880
9814
|
try {
|
|
9881
|
-
for (const entry of
|
|
9815
|
+
for (const entry of fs16.readdirSync(sourceDir)) {
|
|
9882
9816
|
if (ext2 === "" || entry.endsWith(ext2))
|
|
9883
|
-
matches2.push(
|
|
9817
|
+
matches2.push(path18.join(dir, entry));
|
|
9884
9818
|
}
|
|
9885
9819
|
} catch {
|
|
9886
9820
|
}
|
|
9887
9821
|
}
|
|
9888
|
-
} else if (
|
|
9822
|
+
} else if (fs16.existsSync(path18.join(sourceRoot, pattern))) {
|
|
9889
9823
|
matches2.push(pattern);
|
|
9890
9824
|
}
|
|
9891
9825
|
for (const rel of matches2) {
|
|
9892
|
-
const src =
|
|
9893
|
-
const dst =
|
|
9826
|
+
const src = path18.join(sourceRoot, rel);
|
|
9827
|
+
const dst = path18.join(worktreeRoot, rel);
|
|
9894
9828
|
try {
|
|
9895
|
-
const st =
|
|
9829
|
+
const st = fs16.statSync(src);
|
|
9896
9830
|
if (!st.isFile())
|
|
9897
9831
|
continue;
|
|
9898
|
-
|
|
9899
|
-
|
|
9832
|
+
fs16.mkdirSync(path18.dirname(dst), { recursive: true });
|
|
9833
|
+
fs16.copyFileSync(src, dst);
|
|
9900
9834
|
copied++;
|
|
9901
9835
|
} catch {
|
|
9902
9836
|
}
|
|
@@ -9996,7 +9930,7 @@ var init_manager = __esm({
|
|
|
9996
9930
|
try {
|
|
9997
9931
|
const hostExec = makeHostExecFn();
|
|
9998
9932
|
for (const repo of repos) {
|
|
9999
|
-
const repoDir =
|
|
9933
|
+
const repoDir = path18.join(workspacePath, repo.name);
|
|
10000
9934
|
if (repo.stack && Object.keys(repo.stack).length > 0) {
|
|
10001
9935
|
preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
|
|
10002
9936
|
} else {
|
|
@@ -10053,10 +9987,10 @@ var init_manager = __esm({
|
|
|
10053
9987
|
const worldEnv = {};
|
|
10054
9988
|
if (opts.task)
|
|
10055
9989
|
worldEnv.OLAM_TASK = opts.task;
|
|
10056
|
-
const r2CredsPath =
|
|
10057
|
-
if (
|
|
9990
|
+
const r2CredsPath = path18.join(os10.homedir(), ".olam", "r2-credentials.json");
|
|
9991
|
+
if (fs16.existsSync(r2CredsPath)) {
|
|
10058
9992
|
try {
|
|
10059
|
-
const r2Raw =
|
|
9993
|
+
const r2Raw = fs16.readFileSync(r2CredsPath, "utf-8").trim();
|
|
10060
9994
|
if (r2Raw.length > 0) {
|
|
10061
9995
|
const r2 = JSON.parse(r2Raw);
|
|
10062
9996
|
if (typeof r2.account_id === "string")
|
|
@@ -10073,10 +10007,10 @@ var init_manager = __esm({
|
|
|
10073
10007
|
} catch {
|
|
10074
10008
|
}
|
|
10075
10009
|
}
|
|
10076
|
-
const keysYamlPath =
|
|
10077
|
-
if (
|
|
10010
|
+
const keysYamlPath = path18.join(os10.homedir(), ".olam", "keys.yaml");
|
|
10011
|
+
if (fs16.existsSync(keysYamlPath)) {
|
|
10078
10012
|
try {
|
|
10079
|
-
const keysRaw =
|
|
10013
|
+
const keysRaw = fs16.readFileSync(keysYamlPath, "utf-8").trim();
|
|
10080
10014
|
if (keysRaw.length > 0) {
|
|
10081
10015
|
const parsed = YAML3.parse(keysRaw);
|
|
10082
10016
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
@@ -10123,10 +10057,10 @@ var init_manager = __esm({
|
|
|
10123
10057
|
worldEnv[k] = v;
|
|
10124
10058
|
}
|
|
10125
10059
|
for (const { repoName, relativePath, content } of fileWrites) {
|
|
10126
|
-
const absPath =
|
|
10060
|
+
const absPath = path18.join(workspacePath, repoName, relativePath);
|
|
10127
10061
|
try {
|
|
10128
|
-
|
|
10129
|
-
|
|
10062
|
+
fs16.mkdirSync(path18.dirname(absPath), { recursive: true });
|
|
10063
|
+
fs16.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
|
|
10130
10064
|
mode: 384
|
|
10131
10065
|
});
|
|
10132
10066
|
console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
|
|
@@ -10278,7 +10212,7 @@ var init_manager = __esm({
|
|
|
10278
10212
|
let taskWithPolicies = opts.task;
|
|
10279
10213
|
try {
|
|
10280
10214
|
const allPolicies = repos.flatMap((repo) => {
|
|
10281
|
-
const repoWorktree =
|
|
10215
|
+
const repoWorktree = path18.join(workspacePath, repo.name);
|
|
10282
10216
|
return loadPolicies(repoWorktree);
|
|
10283
10217
|
});
|
|
10284
10218
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -10294,8 +10228,8 @@ var init_manager = __esm({
|
|
|
10294
10228
|
${opts.task}`;
|
|
10295
10229
|
execSync4(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
|
|
10296
10230
|
for (const repo of repos) {
|
|
10297
|
-
const policiesDir =
|
|
10298
|
-
if (
|
|
10231
|
+
const policiesDir = path18.join(workspacePath, repo.name, ".olam", "policies");
|
|
10232
|
+
if (fs16.existsSync(policiesDir)) {
|
|
10299
10233
|
execSync4(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
|
|
10300
10234
|
}
|
|
10301
10235
|
}
|
|
@@ -10374,8 +10308,8 @@ ${opts.task}`;
|
|
|
10374
10308
|
} catch {
|
|
10375
10309
|
}
|
|
10376
10310
|
try {
|
|
10377
|
-
|
|
10378
|
-
if (
|
|
10311
|
+
fs16.rmSync(world.workspacePath, { recursive: true, force: true });
|
|
10312
|
+
if (fs16.existsSync(world.workspacePath)) {
|
|
10379
10313
|
console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
|
|
10380
10314
|
}
|
|
10381
10315
|
} catch (err) {
|
|
@@ -10430,10 +10364,7 @@ ${opts.task}`;
|
|
|
10430
10364
|
* Resolution precedence (matches the CF worker exactly):
|
|
10431
10365
|
* 1. inline `opts.repos` names → look up in `config.repos`
|
|
10432
10366
|
* 2. named `opts.workspace` → load catalog YAML, map via workspaceToRepoConfigs
|
|
10433
|
-
* 3.
|
|
10434
|
-
* "include every repo in config.yaml" fallback silently fanned a
|
|
10435
|
-
* single-repo audit into 7-repo bootstraps and steered
|
|
10436
|
-
* `image_selectors` to a wider tag than intended.
|
|
10367
|
+
* 3. fallback → every repo declared in the project's config.yaml
|
|
10437
10368
|
*/
|
|
10438
10369
|
resolveReposWithWorkspace(opts) {
|
|
10439
10370
|
if (opts.repos && opts.repos.length > 0) {
|
|
@@ -10445,7 +10376,7 @@ ${opts.task}`;
|
|
|
10445
10376
|
throw new WorkspaceNotFoundError(opts.workspace);
|
|
10446
10377
|
return workspaceToRepoConfigs(ws);
|
|
10447
10378
|
}
|
|
10448
|
-
|
|
10379
|
+
return this.resolveRepos(void 0);
|
|
10449
10380
|
}
|
|
10450
10381
|
resolveRepos(repoNames) {
|
|
10451
10382
|
if (!repoNames || repoNames.length === 0) {
|
|
@@ -10463,14 +10394,14 @@ ${opts.task}`;
|
|
|
10463
10394
|
return names.map((name) => this.config.repos.find((r) => r.name === name)).filter((r) => r !== void 0);
|
|
10464
10395
|
}
|
|
10465
10396
|
transportPlanFile(planFilePath, workspacePath, repoNames) {
|
|
10466
|
-
const planContent =
|
|
10467
|
-
const planFileName =
|
|
10397
|
+
const planContent = fs16.readFileSync(planFilePath, "utf-8");
|
|
10398
|
+
const planFileName = path18.basename(planFilePath);
|
|
10468
10399
|
const targetRepo = repoNames[0];
|
|
10469
10400
|
if (!targetRepo)
|
|
10470
10401
|
return;
|
|
10471
|
-
const plansDir =
|
|
10472
|
-
|
|
10473
|
-
|
|
10402
|
+
const plansDir = path18.join(workspacePath, targetRepo, "docs", "plans");
|
|
10403
|
+
fs16.mkdirSync(plansDir, { recursive: true });
|
|
10404
|
+
fs16.writeFileSync(path18.join(plansDir, planFileName), planContent);
|
|
10474
10405
|
}
|
|
10475
10406
|
resolveServices(repos) {
|
|
10476
10407
|
const services = [];
|
|
@@ -10563,9 +10494,9 @@ var init_tracker = __esm({
|
|
|
10563
10494
|
});
|
|
10564
10495
|
|
|
10565
10496
|
// ../core/dist/world-paths.js
|
|
10566
|
-
import * as
|
|
10497
|
+
import * as path19 from "node:path";
|
|
10567
10498
|
function getWorldDbPath(workspacePath) {
|
|
10568
|
-
return
|
|
10499
|
+
return path19.join(workspacePath, WORLD_DB_FILENAME);
|
|
10569
10500
|
}
|
|
10570
10501
|
var WORLD_DB_FILENAME;
|
|
10571
10502
|
var init_world_paths = __esm({
|
|
@@ -11183,8 +11114,8 @@ var init_session_aggregator = __esm({
|
|
|
11183
11114
|
|
|
11184
11115
|
// ../core/dist/dashboard/server.js
|
|
11185
11116
|
import * as http from "node:http";
|
|
11186
|
-
import * as
|
|
11187
|
-
import * as
|
|
11117
|
+
import * as fs17 from "node:fs";
|
|
11118
|
+
import * as path20 from "node:path";
|
|
11188
11119
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
11189
11120
|
function jsonResponse(res, data, status = 200) {
|
|
11190
11121
|
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
|
|
@@ -11195,7 +11126,7 @@ function notFound(res) {
|
|
|
11195
11126
|
}
|
|
11196
11127
|
function openThoughtStore(workspacePath) {
|
|
11197
11128
|
const dbPath = getWorldDbPath(workspacePath);
|
|
11198
|
-
if (!
|
|
11129
|
+
if (!fs17.existsSync(dbPath))
|
|
11199
11130
|
return null;
|
|
11200
11131
|
return new ThoughtLocalStore(dbPath);
|
|
11201
11132
|
}
|
|
@@ -11366,13 +11297,13 @@ function findSessionInWorld(registry, sessionId) {
|
|
|
11366
11297
|
}
|
|
11367
11298
|
function createDashboardServer(opts) {
|
|
11368
11299
|
const { port, registry } = opts;
|
|
11369
|
-
const thisDir =
|
|
11370
|
-
const defaultPublicDir =
|
|
11300
|
+
const thisDir = path20.dirname(fileURLToPath2(import.meta.url));
|
|
11301
|
+
const defaultPublicDir = path20.resolve(thisDir, "../../../control-plane/public");
|
|
11371
11302
|
const publicDir = opts.publicDir ?? defaultPublicDir;
|
|
11372
|
-
let hasPublicDir =
|
|
11303
|
+
let hasPublicDir = fs17.existsSync(publicDir);
|
|
11373
11304
|
const server = http.createServer((req, res) => {
|
|
11374
11305
|
if (!hasPublicDir) {
|
|
11375
|
-
hasPublicDir =
|
|
11306
|
+
hasPublicDir = fs17.existsSync(publicDir);
|
|
11376
11307
|
}
|
|
11377
11308
|
const host = req.headers.host ?? `localhost:${port}`;
|
|
11378
11309
|
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
@@ -11646,22 +11577,22 @@ function createDashboardServer(opts) {
|
|
|
11646
11577
|
res.end(`<html><body style="font-family:system-ui;padding:2rem"><h1>Olam Dashboard</h1><p>The React app has not been built yet.</p><p>Run <code>npm run build:app</code> in <code>packages/control-plane</code> to build it.</p><p>API routes are available at <code>/api/*</code>.</p></body></html>`);
|
|
11647
11578
|
return;
|
|
11648
11579
|
}
|
|
11649
|
-
let filePath =
|
|
11580
|
+
let filePath = path20.join(publicDir, pathname === "/" ? "index.html" : pathname);
|
|
11650
11581
|
if (!filePath.startsWith(publicDir)) {
|
|
11651
11582
|
notFound(res);
|
|
11652
11583
|
return;
|
|
11653
11584
|
}
|
|
11654
|
-
if (
|
|
11655
|
-
const ext2 =
|
|
11585
|
+
if (fs17.existsSync(filePath) && fs17.statSync(filePath).isFile()) {
|
|
11586
|
+
const ext2 = path20.extname(filePath);
|
|
11656
11587
|
const contentType = MIME[ext2] ?? "application/octet-stream";
|
|
11657
11588
|
res.writeHead(200, { "Content-Type": contentType });
|
|
11658
|
-
|
|
11589
|
+
fs17.createReadStream(filePath).pipe(res);
|
|
11659
11590
|
return;
|
|
11660
11591
|
}
|
|
11661
|
-
filePath =
|
|
11662
|
-
if (
|
|
11592
|
+
filePath = path20.join(publicDir, "index.html");
|
|
11593
|
+
if (fs17.existsSync(filePath)) {
|
|
11663
11594
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
11664
|
-
|
|
11595
|
+
fs17.createReadStream(filePath).pipe(res);
|
|
11665
11596
|
return;
|
|
11666
11597
|
}
|
|
11667
11598
|
notFound(res);
|
|
@@ -11692,16 +11623,16 @@ var init_server = __esm({
|
|
|
11692
11623
|
});
|
|
11693
11624
|
|
|
11694
11625
|
// ../core/dist/dashboard/state.js
|
|
11695
|
-
import * as
|
|
11626
|
+
import * as fs18 from "node:fs";
|
|
11696
11627
|
import * as os11 from "node:os";
|
|
11697
|
-
import * as
|
|
11628
|
+
import * as path21 from "node:path";
|
|
11698
11629
|
function saveDashboardState(state) {
|
|
11699
|
-
|
|
11700
|
-
|
|
11630
|
+
fs18.mkdirSync(path21.dirname(STATE_PATH), { recursive: true });
|
|
11631
|
+
fs18.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
11701
11632
|
}
|
|
11702
11633
|
function loadDashboardState() {
|
|
11703
11634
|
try {
|
|
11704
|
-
const raw =
|
|
11635
|
+
const raw = fs18.readFileSync(STATE_PATH, "utf-8");
|
|
11705
11636
|
return JSON.parse(raw);
|
|
11706
11637
|
} catch {
|
|
11707
11638
|
return null;
|
|
@@ -11709,7 +11640,7 @@ function loadDashboardState() {
|
|
|
11709
11640
|
}
|
|
11710
11641
|
function clearDashboardState() {
|
|
11711
11642
|
try {
|
|
11712
|
-
|
|
11643
|
+
fs18.unlinkSync(STATE_PATH);
|
|
11713
11644
|
} catch {
|
|
11714
11645
|
}
|
|
11715
11646
|
}
|
|
@@ -11729,7 +11660,7 @@ var STATE_PATH;
|
|
|
11729
11660
|
var init_state2 = __esm({
|
|
11730
11661
|
"../core/dist/dashboard/state.js"() {
|
|
11731
11662
|
"use strict";
|
|
11732
|
-
STATE_PATH =
|
|
11663
|
+
STATE_PATH = path21.join(os11.homedir(), ".olam", "dashboard.json");
|
|
11733
11664
|
}
|
|
11734
11665
|
});
|
|
11735
11666
|
|
|
@@ -12123,51 +12054,51 @@ __export(host_cp_exports, {
|
|
|
12123
12054
|
writePid: () => writePid,
|
|
12124
12055
|
writeToken: () => writeToken
|
|
12125
12056
|
});
|
|
12126
|
-
import * as
|
|
12127
|
-
import * as
|
|
12057
|
+
import * as crypto5 from "node:crypto";
|
|
12058
|
+
import * as fs19 from "node:fs";
|
|
12128
12059
|
import * as os12 from "node:os";
|
|
12129
|
-
import * as
|
|
12060
|
+
import * as path22 from "node:path";
|
|
12130
12061
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12131
12062
|
import Dockerode2 from "dockerode";
|
|
12132
12063
|
function findComposeFile() {
|
|
12133
12064
|
const candidates = [
|
|
12134
12065
|
// Bundled path: dist/index.js lives at <pkg>/dist/; host-cp/ is a sibling of dist/
|
|
12135
|
-
|
|
12066
|
+
path22.resolve(path22.dirname(new URL(import.meta.url).pathname), "../host-cp/compose.yaml"),
|
|
12136
12067
|
// Source-mode: cwd is monorepo root
|
|
12137
|
-
|
|
12068
|
+
path22.resolve(process.cwd(), "packages/host-cp/compose.yaml"),
|
|
12138
12069
|
// Source-mode: cwd is one level inside the monorepo
|
|
12139
|
-
|
|
12070
|
+
path22.resolve(process.cwd(), "../packages/host-cp/compose.yaml")
|
|
12140
12071
|
];
|
|
12141
12072
|
for (const c of candidates) {
|
|
12142
|
-
if (
|
|
12073
|
+
if (fs19.existsSync(c)) return c;
|
|
12143
12074
|
}
|
|
12144
|
-
return
|
|
12075
|
+
return path22.resolve(process.cwd(), "packages/host-cp/compose.yaml");
|
|
12145
12076
|
}
|
|
12146
12077
|
function olamHome() {
|
|
12147
|
-
return process.env.OLAM_HOME ??
|
|
12078
|
+
return process.env.OLAM_HOME ?? path22.join(os12.homedir(), ".olam");
|
|
12148
12079
|
}
|
|
12149
12080
|
function tokenPath() {
|
|
12150
|
-
return
|
|
12081
|
+
return path22.join(olamHome(), "host-cp.token");
|
|
12151
12082
|
}
|
|
12152
12083
|
function pidPath() {
|
|
12153
|
-
return
|
|
12084
|
+
return path22.join(olamHome(), "host-cp.pid");
|
|
12154
12085
|
}
|
|
12155
12086
|
function authSecretPath() {
|
|
12156
|
-
return
|
|
12087
|
+
return path22.join(olamHome(), "auth-secret");
|
|
12157
12088
|
}
|
|
12158
12089
|
function readAuthSecret2() {
|
|
12159
12090
|
const filePath = authSecretPath();
|
|
12160
|
-
if (!
|
|
12161
|
-
const raw =
|
|
12091
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
12092
|
+
const raw = fs19.readFileSync(filePath, "utf-8").trim();
|
|
12162
12093
|
return raw.length > 0 ? raw : null;
|
|
12163
12094
|
}
|
|
12164
12095
|
function r2CredentialsPath() {
|
|
12165
|
-
return
|
|
12096
|
+
return path22.join(olamHome(), "r2-credentials.json");
|
|
12166
12097
|
}
|
|
12167
12098
|
function readR2Credentials() {
|
|
12168
12099
|
const filePath = r2CredentialsPath();
|
|
12169
|
-
if (!
|
|
12170
|
-
const raw =
|
|
12100
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
12101
|
+
const raw = fs19.readFileSync(filePath, "utf-8").trim();
|
|
12171
12102
|
if (raw.length === 0) return null;
|
|
12172
12103
|
try {
|
|
12173
12104
|
const parsed = JSON.parse(raw);
|
|
@@ -12188,39 +12119,39 @@ function readR2Credentials() {
|
|
|
12188
12119
|
}
|
|
12189
12120
|
}
|
|
12190
12121
|
function writeToken() {
|
|
12191
|
-
const token =
|
|
12122
|
+
const token = crypto5.randomBytes(32).toString("hex");
|
|
12192
12123
|
const filePath = tokenPath();
|
|
12193
|
-
|
|
12194
|
-
|
|
12124
|
+
fs19.mkdirSync(path22.dirname(filePath), { recursive: true });
|
|
12125
|
+
fs19.writeFileSync(filePath, token, { mode: 384 });
|
|
12195
12126
|
return token;
|
|
12196
12127
|
}
|
|
12197
12128
|
function readToken() {
|
|
12198
12129
|
const filePath = tokenPath();
|
|
12199
|
-
if (!
|
|
12200
|
-
return
|
|
12130
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
12131
|
+
return fs19.readFileSync(filePath, "utf-8").trim();
|
|
12201
12132
|
}
|
|
12202
12133
|
function removeToken() {
|
|
12203
12134
|
const filePath = tokenPath();
|
|
12204
|
-
if (!
|
|
12205
|
-
|
|
12135
|
+
if (!fs19.existsSync(filePath)) return false;
|
|
12136
|
+
fs19.unlinkSync(filePath);
|
|
12206
12137
|
return true;
|
|
12207
12138
|
}
|
|
12208
12139
|
function writePid(pid) {
|
|
12209
12140
|
const filePath = pidPath();
|
|
12210
|
-
|
|
12211
|
-
|
|
12141
|
+
fs19.mkdirSync(path22.dirname(filePath), { recursive: true });
|
|
12142
|
+
fs19.writeFileSync(filePath, String(pid), { mode: 420 });
|
|
12212
12143
|
}
|
|
12213
12144
|
function readPid() {
|
|
12214
12145
|
const filePath = pidPath();
|
|
12215
|
-
if (!
|
|
12216
|
-
const raw =
|
|
12146
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
12147
|
+
const raw = fs19.readFileSync(filePath, "utf-8").trim();
|
|
12217
12148
|
const n = parseInt(raw, 10);
|
|
12218
12149
|
return Number.isFinite(n) ? n : null;
|
|
12219
12150
|
}
|
|
12220
12151
|
function removePid() {
|
|
12221
12152
|
const filePath = pidPath();
|
|
12222
|
-
if (!
|
|
12223
|
-
|
|
12153
|
+
if (!fs19.existsSync(filePath)) return false;
|
|
12154
|
+
fs19.unlinkSync(filePath);
|
|
12224
12155
|
return true;
|
|
12225
12156
|
}
|
|
12226
12157
|
async function findHostCpContainer() {
|
|
@@ -12379,7 +12310,7 @@ async function handleStart(opts) {
|
|
|
12379
12310
|
}
|
|
12380
12311
|
const token = writeToken();
|
|
12381
12312
|
const composeFile = findComposeFile();
|
|
12382
|
-
if (!
|
|
12313
|
+
if (!fs19.existsSync(composeFile)) {
|
|
12383
12314
|
printError(`compose.yaml not found at ${composeFile}. Run from the olam project root.`);
|
|
12384
12315
|
removeToken();
|
|
12385
12316
|
process.exitCode = 1;
|
|
@@ -12461,7 +12392,7 @@ async function stopHostCp() {
|
|
|
12461
12392
|
}
|
|
12462
12393
|
async function handleStop() {
|
|
12463
12394
|
const composeFile = findComposeFile();
|
|
12464
|
-
if (!
|
|
12395
|
+
if (!fs19.existsSync(composeFile)) {
|
|
12465
12396
|
printWarning(`compose.yaml not found at ${composeFile}. Cleaning up token + PID anyway.`);
|
|
12466
12397
|
removeToken();
|
|
12467
12398
|
removePid();
|
|
@@ -12489,13 +12420,13 @@ async function buildStatusReport() {
|
|
|
12489
12420
|
const container = await findHostCpContainer();
|
|
12490
12421
|
const health = await probeHealth();
|
|
12491
12422
|
const tokenFile = tokenPath();
|
|
12492
|
-
const tokenPresent =
|
|
12423
|
+
const tokenPresent = fs19.existsSync(tokenFile);
|
|
12493
12424
|
let tokenModeOk = false;
|
|
12494
12425
|
if (tokenPresent) {
|
|
12495
|
-
const mode =
|
|
12426
|
+
const mode = fs19.statSync(tokenFile).mode & 511;
|
|
12496
12427
|
tokenModeOk = mode === 384;
|
|
12497
12428
|
}
|
|
12498
|
-
const pidPresent =
|
|
12429
|
+
const pidPresent = fs19.existsSync(pidPath());
|
|
12499
12430
|
let stack;
|
|
12500
12431
|
if (!container) {
|
|
12501
12432
|
stack = "not_started";
|
|
@@ -12583,13 +12514,13 @@ async function discoverWorldPort(worldId) {
|
|
|
12583
12514
|
}
|
|
12584
12515
|
async function readHostCpToken2() {
|
|
12585
12516
|
const tp = tokenPath();
|
|
12586
|
-
if (!
|
|
12587
|
-
return
|
|
12517
|
+
if (!fs19.existsSync(tp)) return null;
|
|
12518
|
+
return fs19.readFileSync(tp, "utf-8").trim();
|
|
12588
12519
|
}
|
|
12589
|
-
async function callHostCpProxy(method, worldId,
|
|
12520
|
+
async function callHostCpProxy(method, worldId, path44, body) {
|
|
12590
12521
|
const token = await readHostCpToken2();
|
|
12591
12522
|
if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
|
|
12592
|
-
const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${
|
|
12523
|
+
const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path44}`;
|
|
12593
12524
|
try {
|
|
12594
12525
|
const headers = {
|
|
12595
12526
|
Authorization: `Bearer ${token}`
|
|
@@ -12714,17 +12645,17 @@ __export(install_root_exports, {
|
|
|
12714
12645
|
isDevMode: () => isDevMode,
|
|
12715
12646
|
resolveBuildScript: () => resolveBuildScript
|
|
12716
12647
|
});
|
|
12717
|
-
import { existsSync as
|
|
12718
|
-
import { dirname as
|
|
12648
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
12649
|
+
import { dirname as dirname13, join as join24, resolve as resolve6 } from "node:path";
|
|
12719
12650
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
12720
12651
|
function installRoot(metaUrl = import.meta.url) {
|
|
12721
12652
|
const here = fileURLToPath3(metaUrl);
|
|
12722
|
-
return resolve6(
|
|
12653
|
+
return resolve6(dirname13(here), "..");
|
|
12723
12654
|
}
|
|
12724
12655
|
function isDevMode(env = process.env, installRootDir = installRoot()) {
|
|
12725
12656
|
if (env.OLAM_DEV !== "1") return false;
|
|
12726
12657
|
const repoRoot = resolve6(installRootDir, "..", "..");
|
|
12727
|
-
return
|
|
12658
|
+
return existsSync18(join24(repoRoot, "packages")) && existsSync18(join24(repoRoot, "package.json"));
|
|
12728
12659
|
}
|
|
12729
12660
|
function resolveBuildScript(input) {
|
|
12730
12661
|
const { scriptRelPath, env = process.env, installRootDir = installRoot() } = input;
|
|
@@ -12903,20 +12834,20 @@ var init_registry_allowlist = __esm({
|
|
|
12903
12834
|
});
|
|
12904
12835
|
|
|
12905
12836
|
// ../core/dist/world/world-yaml.js
|
|
12906
|
-
import * as
|
|
12907
|
-
import * as
|
|
12837
|
+
import * as fs21 from "node:fs";
|
|
12838
|
+
import * as path24 from "node:path";
|
|
12908
12839
|
import { parse as parseYaml4, stringify as stringifyYaml4 } from "yaml";
|
|
12909
12840
|
function writeWorldYaml(worldPath, data) {
|
|
12910
|
-
const olamDir =
|
|
12911
|
-
|
|
12912
|
-
|
|
12841
|
+
const olamDir = path24.join(worldPath, ".olam");
|
|
12842
|
+
fs21.mkdirSync(olamDir, { recursive: true });
|
|
12843
|
+
fs21.writeFileSync(path24.join(olamDir, "world.yaml"), stringifyYaml4(data), "utf-8");
|
|
12913
12844
|
}
|
|
12914
12845
|
function readWorldYaml(worldPath) {
|
|
12915
|
-
const yamlPath =
|
|
12916
|
-
if (!
|
|
12846
|
+
const yamlPath = path24.join(worldPath, ".olam", "world.yaml");
|
|
12847
|
+
if (!fs21.existsSync(yamlPath))
|
|
12917
12848
|
return null;
|
|
12918
12849
|
try {
|
|
12919
|
-
const raw =
|
|
12850
|
+
const raw = fs21.readFileSync(yamlPath, "utf-8");
|
|
12920
12851
|
const parsed = parseYaml4(raw);
|
|
12921
12852
|
return WorldYamlSchema.parse(parsed);
|
|
12922
12853
|
} catch {
|
|
@@ -13047,16 +12978,16 @@ __export(machine_schema_exports, {
|
|
|
13047
12978
|
readMachineConfig: () => readMachineConfig,
|
|
13048
12979
|
writeMachineConfig: () => writeMachineConfig
|
|
13049
12980
|
});
|
|
13050
|
-
import * as
|
|
13051
|
-
import * as
|
|
12981
|
+
import * as fs35 from "node:fs";
|
|
12982
|
+
import * as path39 from "node:path";
|
|
13052
12983
|
import * as os21 from "node:os";
|
|
13053
12984
|
import { parse as parseYaml5, stringify as stringifyYaml5 } from "yaml";
|
|
13054
12985
|
function readMachineConfig(configPath) {
|
|
13055
12986
|
const p = configPath ?? DEFAULT_CONFIG_PATH;
|
|
13056
|
-
if (!
|
|
12987
|
+
if (!fs35.existsSync(p))
|
|
13057
12988
|
return null;
|
|
13058
12989
|
try {
|
|
13059
|
-
const raw =
|
|
12990
|
+
const raw = fs35.readFileSync(p, "utf-8");
|
|
13060
12991
|
const parsed = parseYaml5(raw);
|
|
13061
12992
|
return MachineConfigSchema.parse(parsed);
|
|
13062
12993
|
} catch {
|
|
@@ -13065,8 +12996,8 @@ function readMachineConfig(configPath) {
|
|
|
13065
12996
|
}
|
|
13066
12997
|
function writeMachineConfig(config, configPath) {
|
|
13067
12998
|
const p = configPath ?? DEFAULT_CONFIG_PATH;
|
|
13068
|
-
|
|
13069
|
-
|
|
12999
|
+
fs35.mkdirSync(path39.dirname(p), { recursive: true });
|
|
13000
|
+
fs35.writeFileSync(p, stringifyYaml5({ ...config }), { mode: 420 });
|
|
13070
13001
|
}
|
|
13071
13002
|
function initMachineConfig(opts = {}) {
|
|
13072
13003
|
const configPath = opts.configPath ?? DEFAULT_CONFIG_PATH;
|
|
@@ -13089,16 +13020,16 @@ var init_machine_schema = __esm({
|
|
|
13089
13020
|
channel: external_exports.enum(["stable", "beta", "edge"]).default("stable"),
|
|
13090
13021
|
auto_update: external_exports.boolean().default(true),
|
|
13091
13022
|
telemetry: external_exports.boolean().default(true),
|
|
13092
|
-
worlds_dir: external_exports.string().default(() =>
|
|
13023
|
+
worlds_dir: external_exports.string().default(() => path39.join(os21.homedir(), ".olam", "worlds"))
|
|
13093
13024
|
});
|
|
13094
|
-
DEFAULT_CONFIG_PATH =
|
|
13025
|
+
DEFAULT_CONFIG_PATH = path39.join(os21.homedir(), ".olam", "config.yaml");
|
|
13095
13026
|
}
|
|
13096
13027
|
});
|
|
13097
13028
|
|
|
13098
13029
|
// src/index.ts
|
|
13099
13030
|
import { Command } from "commander";
|
|
13100
|
-
import * as
|
|
13101
|
-
import * as
|
|
13031
|
+
import * as fs39 from "node:fs";
|
|
13032
|
+
import * as path43 from "node:path";
|
|
13102
13033
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
13103
13034
|
|
|
13104
13035
|
// src/commands/init.ts
|
|
@@ -13475,9 +13406,9 @@ var UnknownArchetypeError = class extends Error {
|
|
|
13475
13406
|
};
|
|
13476
13407
|
var ArchetypeCycleError = class extends Error {
|
|
13477
13408
|
path;
|
|
13478
|
-
constructor(
|
|
13479
|
-
super(`Archetype inheritance cycle detected: ${
|
|
13480
|
-
this.path =
|
|
13409
|
+
constructor(path44) {
|
|
13410
|
+
super(`Archetype inheritance cycle detected: ${path44.join(" \u2192 ")} \u2192 ${path44[0] ?? "?"}`);
|
|
13411
|
+
this.path = path44;
|
|
13481
13412
|
this.name = "ArchetypeCycleError";
|
|
13482
13413
|
}
|
|
13483
13414
|
};
|
|
@@ -13671,7 +13602,7 @@ function registerInstall(program2) {
|
|
|
13671
13602
|
// src/commands/auth.ts
|
|
13672
13603
|
init_auth();
|
|
13673
13604
|
init_output();
|
|
13674
|
-
import
|
|
13605
|
+
import pc9 from "picocolors";
|
|
13675
13606
|
import * as readline from "node:readline/promises";
|
|
13676
13607
|
import { spawn as spawn3 } from "node:child_process";
|
|
13677
13608
|
|
|
@@ -13680,174 +13611,17 @@ import * as fs8 from "node:fs";
|
|
|
13680
13611
|
import * as os5 from "node:os";
|
|
13681
13612
|
import * as path9 from "node:path";
|
|
13682
13613
|
import pc5 from "picocolors";
|
|
13683
|
-
|
|
13684
|
-
// ../auth-logic/dist/effective-state.js
|
|
13685
|
-
function effectiveState(account, now = Date.now()) {
|
|
13686
|
-
const persisted = account.state ?? (account.rateLimited ? "cooldown" : "active");
|
|
13687
|
-
if (persisted === "disabled")
|
|
13688
|
-
return "disabled";
|
|
13689
|
-
if (account.expiresAt != null && account.expiresAt <= now)
|
|
13690
|
-
return "expired";
|
|
13691
|
-
if (persisted === "cooldown" || persisted === "usage-capped") {
|
|
13692
|
-
const reset = account.rateLimitResetsAt ? new Date(account.rateLimitResetsAt).getTime() : 0;
|
|
13693
|
-
if (reset > 0 && reset <= now)
|
|
13694
|
-
return "active";
|
|
13695
|
-
return persisted;
|
|
13696
|
-
}
|
|
13697
|
-
return "active";
|
|
13698
|
-
}
|
|
13699
|
-
|
|
13700
|
-
// ../auth-logic/dist/pick-credential.js
|
|
13701
|
-
function pickCredential(accounts, now = Date.now()) {
|
|
13702
|
-
const active = accounts.filter((a) => effectiveState(a, now) === "active");
|
|
13703
|
-
if (active.length === 0)
|
|
13704
|
-
return null;
|
|
13705
|
-
return [...active].sort((a, b) => {
|
|
13706
|
-
const aCount = a.usage?.requestCount5h ?? 0;
|
|
13707
|
-
const bCount = b.usage?.requestCount5h ?? 0;
|
|
13708
|
-
if (aCount !== bCount)
|
|
13709
|
-
return aCount - bCount;
|
|
13710
|
-
const aLast = a.lastUsed ? new Date(a.lastUsed).getTime() : 0;
|
|
13711
|
-
const bLast = b.lastUsed ? new Date(b.lastUsed).getTime() : 0;
|
|
13712
|
-
return aLast - bLast;
|
|
13713
|
-
})[0] ?? null;
|
|
13714
|
-
}
|
|
13715
|
-
|
|
13716
|
-
// ../auth-logic/dist/next-cooldown-reset.js
|
|
13717
|
-
function nextCooldownReset(accounts, now = Date.now()) {
|
|
13718
|
-
const upcoming = accounts.filter((a) => effectiveState(a, now) === "cooldown").map((a) => a.rateLimitResetsAt ? new Date(a.rateLimitResetsAt).getTime() : 0).filter((ts) => ts > now).sort((a, b) => a - b);
|
|
13719
|
-
return upcoming.length > 0 ? new Date(upcoming[0]).toISOString() : null;
|
|
13720
|
-
}
|
|
13721
|
-
|
|
13722
|
-
// src/commands/auth-status.ts
|
|
13723
13614
|
init_auth();
|
|
13724
13615
|
init_output();
|
|
13725
13616
|
init_exit_codes();
|
|
13726
13617
|
var LOCAL_DATA_DIR = path9.join(os5.homedir(), ".olam", "auth-data");
|
|
13727
|
-
function localHHMM(isoStr) {
|
|
13728
|
-
const d = new Date(isoStr);
|
|
13729
|
-
return d.toLocaleTimeString(void 0, {
|
|
13730
|
-
hour: "2-digit",
|
|
13731
|
-
minute: "2-digit",
|
|
13732
|
-
hour12: false
|
|
13733
|
-
});
|
|
13734
|
-
}
|
|
13735
|
-
function daysAgoStr(expiresAt, now) {
|
|
13736
|
-
const diffDays = Math.floor((now - expiresAt) / (1e3 * 60 * 60 * 24));
|
|
13737
|
-
if (diffDays <= 0) return "expired today";
|
|
13738
|
-
return `expired ${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
|
|
13739
|
-
}
|
|
13740
|
-
function trunc(s, maxLen) {
|
|
13741
|
-
return s.length > maxLen ? s.slice(0, maxLen) : s;
|
|
13742
|
-
}
|
|
13743
|
-
var STATE_PRIORITY = {
|
|
13744
|
-
active: 0,
|
|
13745
|
-
cooldown: 1,
|
|
13746
|
-
"usage-capped": 2,
|
|
13747
|
-
disabled: 3,
|
|
13748
|
-
expired: 4
|
|
13749
|
-
};
|
|
13750
|
-
function formatAuthStatus(accounts, now = Date.now()) {
|
|
13751
|
-
const picked = pickCredential(accounts, now);
|
|
13752
|
-
const rows = accounts.map((account) => {
|
|
13753
|
-
const state = effectiveState(account, now);
|
|
13754
|
-
const isPicked = picked != null && account.id === picked.id;
|
|
13755
|
-
const req5h = account.usage?.requestCount5h ?? 0;
|
|
13756
|
-
const last429 = account.usage?.last429At ? localHHMM(account.usage.last429At) : "never";
|
|
13757
|
-
let reason;
|
|
13758
|
-
if (isPicked) {
|
|
13759
|
-
reason = "\u2190 selected";
|
|
13760
|
-
} else if (state === "active") {
|
|
13761
|
-
reason = `req5h=${req5h} (higher than candidate)`;
|
|
13762
|
-
} else if (state === "cooldown") {
|
|
13763
|
-
const resetTime = account.rateLimitResetsAt ? localHHMM(account.rateLimitResetsAt) : "?";
|
|
13764
|
-
reason = `cooldown until ${resetTime}`;
|
|
13765
|
-
} else if (state === "expired") {
|
|
13766
|
-
reason = daysAgoStr(account.expiresAt ?? 0, now);
|
|
13767
|
-
} else {
|
|
13768
|
-
reason = "disabled";
|
|
13769
|
-
}
|
|
13770
|
-
return { id: account.id, label: account.accountLabel ?? account.id, state, reason, req5h, last429, isPicked };
|
|
13771
|
-
});
|
|
13772
|
-
rows.sort((a, b) => {
|
|
13773
|
-
if (a.isPicked !== b.isPicked) return a.isPicked ? -1 : 1;
|
|
13774
|
-
return STATE_PRIORITY[a.state] - STATE_PRIORITY[b.state];
|
|
13775
|
-
});
|
|
13776
|
-
const COL = { id: 17, label: 17, state: 11, reason: 28, req5h: 7 };
|
|
13777
|
-
const lines = [];
|
|
13778
|
-
const hdr = "id".padEnd(COL.id) + "label".padEnd(COL.label) + "state".padEnd(COL.state) + "reason".padEnd(COL.reason) + "req5h".padEnd(COL.req5h) + "last429";
|
|
13779
|
-
lines.push(pc5.dim(hdr));
|
|
13780
|
-
lines.push(pc5.dim("-".repeat(hdr.length)));
|
|
13781
|
-
for (const row of rows) {
|
|
13782
|
-
const id = trunc(row.id, 16).padEnd(COL.id);
|
|
13783
|
-
const label = trunc(row.label, 16).padEnd(COL.label);
|
|
13784
|
-
const stateRaw = row.state.padEnd(COL.state);
|
|
13785
|
-
const stateColored = row.state === "active" ? pc5.green(stateRaw) : row.state === "cooldown" ? pc5.yellow(stateRaw) : row.state === "expired" ? pc5.red(stateRaw) : pc5.dim(stateRaw);
|
|
13786
|
-
const reason = row.reason.padEnd(COL.reason);
|
|
13787
|
-
const req5h = String(row.req5h).padEnd(COL.req5h);
|
|
13788
|
-
if (row.isPicked) {
|
|
13789
|
-
lines.push(
|
|
13790
|
-
pc5.bold(id) + pc5.bold(label) + stateColored + pc5.green(reason) + pc5.dim(req5h) + pc5.dim(row.last429)
|
|
13791
|
-
);
|
|
13792
|
-
} else {
|
|
13793
|
-
lines.push(id + label + stateColored + reason + pc5.dim(req5h) + pc5.dim(row.last429));
|
|
13794
|
-
}
|
|
13795
|
-
}
|
|
13796
|
-
if (picked == null) {
|
|
13797
|
-
const resetIso = nextCooldownReset(accounts, now);
|
|
13798
|
-
lines.push("");
|
|
13799
|
-
lines.push(
|
|
13800
|
-
resetIso ? pc5.yellow(`Next reset: ${localHHMM(resetIso)}`) : pc5.dim("No reset scheduled")
|
|
13801
|
-
);
|
|
13802
|
-
return { output: lines.join("\n"), exitCode: EXIT_AUTH_NEEDS_ATTENTION };
|
|
13803
|
-
}
|
|
13804
|
-
return { output: lines.join("\n"), exitCode: 0 };
|
|
13805
|
-
}
|
|
13806
|
-
function toSafeAccount(a) {
|
|
13807
|
-
return {
|
|
13808
|
-
id: a.id,
|
|
13809
|
-
accountLabel: a.accountLabel,
|
|
13810
|
-
// expiresAt not exposed in summary — state is pre-computed server-side
|
|
13811
|
-
rateLimited: a.rateLimited,
|
|
13812
|
-
rateLimitResetsAt: a.rateLimitResetsAt,
|
|
13813
|
-
lastUsed: a.lastUsed,
|
|
13814
|
-
state: a.state,
|
|
13815
|
-
usage: a.usage ? { requestCount5h: a.usage.requestCount5h, last429At: a.usage.last429At } : void 0
|
|
13816
|
-
};
|
|
13817
|
-
}
|
|
13818
|
-
async function runAuthStatus(getStatus) {
|
|
13819
|
-
const fetchStatus = getStatus ?? (() => new AuthClient().status());
|
|
13820
|
-
let status;
|
|
13821
|
-
try {
|
|
13822
|
-
status = await fetchStatus();
|
|
13823
|
-
} catch {
|
|
13824
|
-
printError("Failed to contact auth service. Run `olam auth up` first.");
|
|
13825
|
-
process.exitCode = 1;
|
|
13826
|
-
return;
|
|
13827
|
-
}
|
|
13828
|
-
if (!status.reachable) {
|
|
13829
|
-
printError("Auth container is not reachable. Run `olam auth up` first.");
|
|
13830
|
-
process.exitCode = 1;
|
|
13831
|
-
return;
|
|
13832
|
-
}
|
|
13833
|
-
if (status.accounts.length === 0) {
|
|
13834
|
-
console.log(pc5.dim("No credentials found. Run: olam auth login"));
|
|
13835
|
-
return;
|
|
13836
|
-
}
|
|
13837
|
-
const accounts = status.accounts.map(toSafeAccount);
|
|
13838
|
-
const result = formatAuthStatus(accounts);
|
|
13839
|
-
console.log(result.output);
|
|
13840
|
-
if (result.exitCode !== 0) {
|
|
13841
|
-
process.exitCode = result.exitCode;
|
|
13842
|
-
}
|
|
13843
|
-
}
|
|
13844
13618
|
|
|
13845
13619
|
// src/commands/auth-upgrade.ts
|
|
13846
13620
|
init_output();
|
|
13847
13621
|
init_host_cp();
|
|
13848
13622
|
init_auth();
|
|
13849
|
-
import * as
|
|
13850
|
-
import * as
|
|
13623
|
+
import * as fs20 from "node:fs";
|
|
13624
|
+
import * as path23 from "node:path";
|
|
13851
13625
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
13852
13626
|
import ora2 from "ora";
|
|
13853
13627
|
import pc7 from "picocolors";
|
|
@@ -13858,20 +13632,20 @@ init_exit_codes();
|
|
|
13858
13632
|
init_protocol_version();
|
|
13859
13633
|
init_output();
|
|
13860
13634
|
import { spawn as spawn2, spawnSync as spawnSync6 } from "node:child_process";
|
|
13861
|
-
import { existsSync as
|
|
13635
|
+
import { existsSync as existsSync19, readFileSync as readFileSync15 } from "node:fs";
|
|
13862
13636
|
import { join as join25 } from "node:path";
|
|
13863
13637
|
import ora from "ora";
|
|
13864
13638
|
import pc6 from "picocolors";
|
|
13865
13639
|
function loadImageDigests(installRootDir = installRoot()) {
|
|
13866
13640
|
const digestsPath = join25(installRootDir, "dist", "image-digests.json");
|
|
13867
|
-
if (!
|
|
13641
|
+
if (!existsSync19(digestsPath)) {
|
|
13868
13642
|
throw new Error(
|
|
13869
13643
|
`image-digests.json missing at ${digestsPath}. Re-run \`npm install -g @pleri/olam-cli@<version>\` to refresh the tarball.`
|
|
13870
13644
|
);
|
|
13871
13645
|
}
|
|
13872
13646
|
let parsed;
|
|
13873
13647
|
try {
|
|
13874
|
-
parsed = JSON.parse(
|
|
13648
|
+
parsed = JSON.parse(readFileSync15(digestsPath, "utf8"));
|
|
13875
13649
|
} catch (err) {
|
|
13876
13650
|
throw new Error(`image-digests.json is not valid JSON: ${err.message}`);
|
|
13877
13651
|
}
|
|
@@ -14129,9 +13903,9 @@ async function runBootstrap2(opts, deps = {}) {
|
|
|
14129
13903
|
printSuccess("Smoke world created");
|
|
14130
13904
|
}
|
|
14131
13905
|
printHeader("Stack ready");
|
|
14132
|
-
printInfo("host-cp", `running (${digests["host-cp"].slice(0, 19)}\u2026)`);
|
|
14133
|
-
printInfo("auth", `running (${digests.auth.slice(0, 19)}\u2026)`);
|
|
14134
|
-
printInfo("devbox", `pulled (${digests.devbox.slice(0, 19)}\u2026)`);
|
|
13906
|
+
printInfo("olam-host-cp", `running (${digests["host-cp"].slice(0, 19)}\u2026)`);
|
|
13907
|
+
printInfo("olam-auth", `running (${digests.auth.slice(0, 19)}\u2026)`);
|
|
13908
|
+
printInfo("olam-devbox", `pulled (${digests.devbox.slice(0, 19)}\u2026)`);
|
|
14135
13909
|
printInfo("next", '`olam create --task "your task"` to spawn a world');
|
|
14136
13910
|
return { exitCode: 0, summary: "stack ready" };
|
|
14137
13911
|
}
|
|
@@ -14171,8 +13945,8 @@ function parseAuthUpgradeOpts(raw) {
|
|
|
14171
13945
|
};
|
|
14172
13946
|
}
|
|
14173
13947
|
function validateAuthRepoRoot(cwd) {
|
|
14174
|
-
const marker =
|
|
14175
|
-
if (!
|
|
13948
|
+
const marker = path23.join(cwd, "packages/auth-service/Dockerfile");
|
|
13949
|
+
if (!fs20.existsSync(marker)) {
|
|
14176
13950
|
return {
|
|
14177
13951
|
ok: false,
|
|
14178
13952
|
error: `Not an olam repo root (expected ${marker}).
|
|
@@ -14539,6 +14313,185 @@ function registerAuthUpgrade(auth) {
|
|
|
14539
14313
|
});
|
|
14540
14314
|
}
|
|
14541
14315
|
|
|
14316
|
+
// src/commands/services.ts
|
|
14317
|
+
init_auth();
|
|
14318
|
+
init_output();
|
|
14319
|
+
import { execFileSync as execFileSync4, spawnSync as spawnSync8 } from "node:child_process";
|
|
14320
|
+
import pc8 from "picocolors";
|
|
14321
|
+
var MCP_AUTH_PORT = 9998;
|
|
14322
|
+
var MCP_AUTH_VOLUME = "olam-mcp-auth-data";
|
|
14323
|
+
var MCP_AUTH_CONTAINER = "olam-mcp-auth";
|
|
14324
|
+
var MCP_AUTH_LOCAL_TAG = "olam-mcp-auth:local";
|
|
14325
|
+
var MCP_AUTH_PUBLISHED_TAG = "ghcr.io/pleri/olam-mcp-auth:latest";
|
|
14326
|
+
var MCP_AUTH_HEALTH_URL = `http://127.0.0.1:${MCP_AUTH_PORT}/health`;
|
|
14327
|
+
var MCP_AUTH_HEALTH_TIMEOUT_MS = 15e3;
|
|
14328
|
+
var McpAuthContainerController = class {
|
|
14329
|
+
imageTag = MCP_AUTH_LOCAL_TAG;
|
|
14330
|
+
status() {
|
|
14331
|
+
const r = spawnSync8(
|
|
14332
|
+
"docker",
|
|
14333
|
+
["inspect", "--format", "{{.State.Status}}|{{.Id}}", MCP_AUTH_CONTAINER],
|
|
14334
|
+
{ encoding: "utf-8" }
|
|
14335
|
+
);
|
|
14336
|
+
if (r.status === 0) {
|
|
14337
|
+
const [stateRaw, id] = r.stdout.trim().split("|");
|
|
14338
|
+
return {
|
|
14339
|
+
state: stateRaw === "running" ? "running" : "stopped",
|
|
14340
|
+
port: MCP_AUTH_PORT,
|
|
14341
|
+
containerId: id
|
|
14342
|
+
};
|
|
14343
|
+
}
|
|
14344
|
+
return { state: "missing", port: MCP_AUTH_PORT };
|
|
14345
|
+
}
|
|
14346
|
+
imageExists(tag = this.imageTag) {
|
|
14347
|
+
return spawnSync8("docker", ["image", "inspect", tag], { encoding: "utf-8" }).status === 0;
|
|
14348
|
+
}
|
|
14349
|
+
start() {
|
|
14350
|
+
const current = this.status();
|
|
14351
|
+
if (current.state === "running") return;
|
|
14352
|
+
if (current.state === "stopped") {
|
|
14353
|
+
execFileSync4("docker", ["start", MCP_AUTH_CONTAINER], { stdio: "pipe" });
|
|
14354
|
+
return;
|
|
14355
|
+
}
|
|
14356
|
+
if (!this.imageExists()) {
|
|
14357
|
+
if (this.imageTag !== MCP_AUTH_PUBLISHED_TAG && this.imageExists(MCP_AUTH_PUBLISHED_TAG)) {
|
|
14358
|
+
this.imageTag = MCP_AUTH_PUBLISHED_TAG;
|
|
14359
|
+
} else {
|
|
14360
|
+
throw new Error(
|
|
14361
|
+
`mcp-auth image '${this.imageTag}' not found locally. Run \`olam bootstrap\` to pull the published image.`
|
|
14362
|
+
);
|
|
14363
|
+
}
|
|
14364
|
+
}
|
|
14365
|
+
execFileSync4(
|
|
14366
|
+
"docker",
|
|
14367
|
+
[
|
|
14368
|
+
"run",
|
|
14369
|
+
"--detach",
|
|
14370
|
+
"--name",
|
|
14371
|
+
MCP_AUTH_CONTAINER,
|
|
14372
|
+
"--restart",
|
|
14373
|
+
"unless-stopped",
|
|
14374
|
+
"--publish",
|
|
14375
|
+
`${MCP_AUTH_PORT}:${MCP_AUTH_PORT}`,
|
|
14376
|
+
"--volume",
|
|
14377
|
+
`${MCP_AUTH_VOLUME}:/mcp-auth-data`,
|
|
14378
|
+
this.imageTag
|
|
14379
|
+
],
|
|
14380
|
+
{ stdio: "pipe" }
|
|
14381
|
+
);
|
|
14382
|
+
}
|
|
14383
|
+
stop() {
|
|
14384
|
+
const current = this.status();
|
|
14385
|
+
if (current.state !== "running") return;
|
|
14386
|
+
execFileSync4("docker", ["stop", MCP_AUTH_CONTAINER], { stdio: "pipe" });
|
|
14387
|
+
}
|
|
14388
|
+
remove() {
|
|
14389
|
+
spawnSync8("docker", ["rm", "-f", MCP_AUTH_CONTAINER], { stdio: "pipe" });
|
|
14390
|
+
}
|
|
14391
|
+
async waitForReady(timeoutMs = MCP_AUTH_HEALTH_TIMEOUT_MS) {
|
|
14392
|
+
const deadline = Date.now() + timeoutMs;
|
|
14393
|
+
while (Date.now() < deadline) {
|
|
14394
|
+
try {
|
|
14395
|
+
const res = await fetch(MCP_AUTH_HEALTH_URL, { signal: AbortSignal.timeout(1500) });
|
|
14396
|
+
if (res.ok) return true;
|
|
14397
|
+
} catch {
|
|
14398
|
+
}
|
|
14399
|
+
await sleep3(500);
|
|
14400
|
+
}
|
|
14401
|
+
return false;
|
|
14402
|
+
}
|
|
14403
|
+
};
|
|
14404
|
+
function sleep3(ms) {
|
|
14405
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
14406
|
+
}
|
|
14407
|
+
async function servicesUp() {
|
|
14408
|
+
const auth = new AuthContainerController();
|
|
14409
|
+
const mcpAuth = new McpAuthContainerController();
|
|
14410
|
+
const authStatus = auth.status();
|
|
14411
|
+
if (authStatus.state === "running") {
|
|
14412
|
+
printSuccess(`olam-auth already running on :${authStatus.port}`);
|
|
14413
|
+
} else {
|
|
14414
|
+
try {
|
|
14415
|
+
auth.start();
|
|
14416
|
+
} catch (err) {
|
|
14417
|
+
printError(`olam-auth failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
14418
|
+
return { exitCode: 1 };
|
|
14419
|
+
}
|
|
14420
|
+
const ready = await auth.waitForReady(15e3);
|
|
14421
|
+
if (!ready) {
|
|
14422
|
+
printWarning("olam-auth started but /health did not respond within 15s.");
|
|
14423
|
+
return { exitCode: 1 };
|
|
14424
|
+
}
|
|
14425
|
+
printSuccess("olam-auth started");
|
|
14426
|
+
}
|
|
14427
|
+
const mcpStatus = mcpAuth.status();
|
|
14428
|
+
if (mcpStatus.state === "running") {
|
|
14429
|
+
printSuccess(`olam-mcp-auth already running on :${mcpStatus.port}`);
|
|
14430
|
+
} else {
|
|
14431
|
+
try {
|
|
14432
|
+
mcpAuth.start();
|
|
14433
|
+
} catch (err) {
|
|
14434
|
+
printError(`olam-mcp-auth failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
14435
|
+
return { exitCode: 1 };
|
|
14436
|
+
}
|
|
14437
|
+
const ready = await mcpAuth.waitForReady();
|
|
14438
|
+
if (!ready) {
|
|
14439
|
+
printWarning("olam-mcp-auth started but /health did not respond within 15s.");
|
|
14440
|
+
return { exitCode: 1 };
|
|
14441
|
+
}
|
|
14442
|
+
printSuccess("olam-mcp-auth started");
|
|
14443
|
+
}
|
|
14444
|
+
printHeader("Services up");
|
|
14445
|
+
printInfo("olam-auth", ":9999");
|
|
14446
|
+
printInfo("olam-mcp-auth", ":9998");
|
|
14447
|
+
return { exitCode: 0 };
|
|
14448
|
+
}
|
|
14449
|
+
function servicesDown() {
|
|
14450
|
+
const auth = new AuthContainerController();
|
|
14451
|
+
const mcpAuth = new McpAuthContainerController();
|
|
14452
|
+
let exitCode = 0;
|
|
14453
|
+
try {
|
|
14454
|
+
auth.stop();
|
|
14455
|
+
printSuccess("olam-auth stopped.");
|
|
14456
|
+
} catch (err) {
|
|
14457
|
+
printError(`olam-auth: ${err instanceof Error ? err.message : String(err)}`);
|
|
14458
|
+
exitCode = 1;
|
|
14459
|
+
}
|
|
14460
|
+
try {
|
|
14461
|
+
mcpAuth.stop();
|
|
14462
|
+
printSuccess("olam-mcp-auth stopped.");
|
|
14463
|
+
} catch (err) {
|
|
14464
|
+
printError(`olam-mcp-auth: ${err instanceof Error ? err.message : String(err)}`);
|
|
14465
|
+
exitCode = 1;
|
|
14466
|
+
}
|
|
14467
|
+
return { exitCode };
|
|
14468
|
+
}
|
|
14469
|
+
function servicesStatus() {
|
|
14470
|
+
const auth = new AuthContainerController();
|
|
14471
|
+
const mcpAuth = new McpAuthContainerController();
|
|
14472
|
+
printHeader("Services status");
|
|
14473
|
+
const authState = auth.status();
|
|
14474
|
+
const authStateStr = authState.state === "running" ? pc8.green("running") : authState.state === "stopped" ? pc8.yellow("stopped") : pc8.red("missing");
|
|
14475
|
+
printInfo("olam-auth", `${authStateStr} :${authState.port}`);
|
|
14476
|
+
const mcpState = mcpAuth.status();
|
|
14477
|
+
const mcpStateStr = mcpState.state === "running" ? pc8.green("running") : mcpState.state === "stopped" ? pc8.yellow("stopped") : pc8.red("missing");
|
|
14478
|
+
printInfo("olam-mcp-auth", `${mcpStateStr} :${mcpState.port}`);
|
|
14479
|
+
}
|
|
14480
|
+
function registerServices(program2) {
|
|
14481
|
+
const services = program2.command("services").description("Manage Olam service containers (olam-auth, olam-mcp-auth)");
|
|
14482
|
+
services.command("up").description("Start all service containers (idempotent)").action(async () => {
|
|
14483
|
+
const result = await servicesUp();
|
|
14484
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
14485
|
+
});
|
|
14486
|
+
services.command("down").description("Stop all service containers").action(() => {
|
|
14487
|
+
const result = servicesDown();
|
|
14488
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
14489
|
+
});
|
|
14490
|
+
services.command("status").description("Show state of all service containers").action(() => {
|
|
14491
|
+
servicesStatus();
|
|
14492
|
+
});
|
|
14493
|
+
}
|
|
14494
|
+
|
|
14542
14495
|
// src/commands/auth.ts
|
|
14543
14496
|
function openBrowser(url) {
|
|
14544
14497
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -14559,44 +14512,19 @@ async function promptLine(question) {
|
|
|
14559
14512
|
}
|
|
14560
14513
|
function registerAuth(program2) {
|
|
14561
14514
|
const auth = program2.command("auth").description("Manage the local Claude auth container");
|
|
14562
|
-
auth.command("up").description("Start the auth container
|
|
14563
|
-
|
|
14564
|
-
const
|
|
14565
|
-
if (
|
|
14566
|
-
printSuccess(`Auth container already running on :${initial.port}`);
|
|
14567
|
-
return;
|
|
14568
|
-
}
|
|
14569
|
-
try {
|
|
14570
|
-
controller.start();
|
|
14571
|
-
} catch (err) {
|
|
14572
|
-
printError(err instanceof Error ? err.message : "failed to start");
|
|
14573
|
-
process.exitCode = 1;
|
|
14574
|
-
return;
|
|
14575
|
-
}
|
|
14576
|
-
const ready = await controller.waitForReady(15e3);
|
|
14577
|
-
if (!ready) {
|
|
14578
|
-
printWarning("Container started but /health did not respond within 15s.");
|
|
14579
|
-
process.exitCode = 1;
|
|
14580
|
-
return;
|
|
14581
|
-
}
|
|
14582
|
-
printHeader("Auth container up");
|
|
14583
|
-
printInfo("Port", String(initial.port));
|
|
14584
|
-
printInfo("Volume", "olam-auth-data");
|
|
14585
|
-
console.log(`
|
|
14586
|
-
${pc8.dim("Next: olam auth login")}`);
|
|
14515
|
+
auth.command("up").description("[deprecated] Start the auth container \u2014 use `olam services up` instead").action(async () => {
|
|
14516
|
+
printWarning("`olam auth up` is deprecated. Use `olam services up` instead.");
|
|
14517
|
+
const result = await servicesUp();
|
|
14518
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
14587
14519
|
});
|
|
14588
|
-
auth.command("down").description("Stop the auth container
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
printSuccess("Auth container stopped.");
|
|
14593
|
-
} catch (err) {
|
|
14594
|
-
printError(err instanceof Error ? err.message : "failed to stop");
|
|
14595
|
-
process.exitCode = 1;
|
|
14596
|
-
}
|
|
14520
|
+
auth.command("down").description("[deprecated] Stop the auth container \u2014 use `olam services down` instead").action(() => {
|
|
14521
|
+
printWarning("`olam auth down` is deprecated. Use `olam services down` instead.");
|
|
14522
|
+
const result = servicesDown();
|
|
14523
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
14597
14524
|
});
|
|
14598
|
-
auth.command("status").description("Show
|
|
14599
|
-
|
|
14525
|
+
auth.command("status").description("[deprecated] Show container state \u2014 use `olam services status` instead").action(() => {
|
|
14526
|
+
printWarning("`olam auth status` is deprecated. Use `olam services status` instead.");
|
|
14527
|
+
servicesStatus();
|
|
14600
14528
|
});
|
|
14601
14529
|
auth.command("list").description("List all stored credentials with state, usage, and rate-limit status").action(async () => {
|
|
14602
14530
|
const client = new AuthClient();
|
|
@@ -14608,15 +14536,15 @@ ${pc8.dim("Next: olam auth login")}`);
|
|
|
14608
14536
|
}
|
|
14609
14537
|
printHeader(`Credentials (${status.accounts.length})`);
|
|
14610
14538
|
if (status.accounts.length === 0) {
|
|
14611
|
-
console.log(` ${
|
|
14539
|
+
console.log(` ${pc9.dim("No credentials \u2014 run: olam auth login --label primary")}`);
|
|
14612
14540
|
return;
|
|
14613
14541
|
}
|
|
14614
14542
|
const stateColor = (s) => {
|
|
14615
|
-
if (s === "active") return
|
|
14616
|
-
if (s === "cooldown") return
|
|
14617
|
-
if (s === "expired") return
|
|
14618
|
-
if (s === "disabled") return
|
|
14619
|
-
return
|
|
14543
|
+
if (s === "active") return pc9.green("active");
|
|
14544
|
+
if (s === "cooldown") return pc9.yellow("cooldown");
|
|
14545
|
+
if (s === "expired") return pc9.red("expired");
|
|
14546
|
+
if (s === "disabled") return pc9.dim("disabled");
|
|
14547
|
+
return pc9.dim(s ?? "unknown");
|
|
14620
14548
|
};
|
|
14621
14549
|
for (const a of status.accounts) {
|
|
14622
14550
|
const label = a.accountLabel ?? a.id;
|
|
@@ -14624,7 +14552,7 @@ ${pc8.dim("Next: olam auth login")}`);
|
|
|
14624
14552
|
const last429 = a.usage?.last429At ? `last429=${a.usage.last429At}` : "last429=never";
|
|
14625
14553
|
const reset = a.rateLimitResetsAt ? `resets=${a.rateLimitResetsAt}` : "";
|
|
14626
14554
|
console.log(
|
|
14627
|
-
` ${
|
|
14555
|
+
` ${pc9.bold(label.padEnd(18))} ${stateColor(a.state).padEnd(18)} ${pc9.dim(`req5h=${reqs}`)} ${pc9.dim(`exp=${a.expiresIn}`)} ${pc9.dim(last429)} ${pc9.yellow(reset)}`
|
|
14628
14556
|
);
|
|
14629
14557
|
}
|
|
14630
14558
|
});
|
|
@@ -14662,7 +14590,7 @@ ${pc8.dim("Next: olam auth login")}`);
|
|
|
14662
14590
|
const preflight = await runAuthPreflight({ autoStart: true });
|
|
14663
14591
|
if (preflight.verdict !== "ok" && preflight.verdict !== "no-accounts") {
|
|
14664
14592
|
printError(preflight.message);
|
|
14665
|
-
console.log(` ${
|
|
14593
|
+
console.log(` ${pc9.dim(preflight.remedy)}`);
|
|
14666
14594
|
process.exitCode = 1;
|
|
14667
14595
|
return;
|
|
14668
14596
|
}
|
|
@@ -14695,14 +14623,14 @@ ${pc8.dim("Next: olam auth login")}`);
|
|
|
14695
14623
|
}
|
|
14696
14624
|
printHeader("Claude OAuth \u2014 PKCE flow");
|
|
14697
14625
|
console.log(`
|
|
14698
|
-
${
|
|
14699
|
-
console.log(` ${
|
|
14626
|
+
${pc9.bold("1.")} Opening Claude in your default browser\u2026`);
|
|
14627
|
+
console.log(` ${pc9.dim(pending.loginUrl)}`);
|
|
14700
14628
|
openBrowser(pending.loginUrl);
|
|
14701
14629
|
console.log(`
|
|
14702
|
-
${
|
|
14703
|
-
console.log(` ${
|
|
14630
|
+
${pc9.bold("2.")} After approving, paste the authorization code from the redirect page.`);
|
|
14631
|
+
console.log(` ${pc9.dim("(Format: <code>#<state> or just <code>.)")}
|
|
14704
14632
|
`);
|
|
14705
|
-
const raw = await promptLine(`${
|
|
14633
|
+
const raw = await promptLine(`${pc9.dim("code:")} `);
|
|
14706
14634
|
if (!raw) {
|
|
14707
14635
|
printError("No code provided.");
|
|
14708
14636
|
process.exitCode = 1;
|
|
@@ -14713,7 +14641,7 @@ ${pc8.dim("Next: olam auth login")}`);
|
|
|
14713
14641
|
const result = await client.completeLogin(statePart, codePart);
|
|
14714
14642
|
printSuccess(`Account stored: ${result.account} (${result.expiresIn})`);
|
|
14715
14643
|
console.log(`
|
|
14716
|
-
${
|
|
14644
|
+
${pc9.dim("Next: olam create --name my-world")}`);
|
|
14717
14645
|
} catch (err) {
|
|
14718
14646
|
printError(err instanceof Error ? err.message : "token exchange failed");
|
|
14719
14647
|
process.exitCode = 1;
|
|
@@ -14744,15 +14672,15 @@ ${pc8.dim("Next: olam create --name my-world")}`);
|
|
|
14744
14672
|
|
|
14745
14673
|
// src/commands/create.ts
|
|
14746
14674
|
init_manager();
|
|
14747
|
-
import { spawnSync as
|
|
14748
|
-
import { existsSync as
|
|
14749
|
-
import { dirname as
|
|
14675
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14676
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
14677
|
+
import { dirname as dirname14, resolve as resolve7 } from "node:path";
|
|
14750
14678
|
import ora3 from "ora";
|
|
14751
|
-
import
|
|
14679
|
+
import pc10 from "picocolors";
|
|
14752
14680
|
|
|
14753
14681
|
// ../core/dist/world/devbox-freshness.js
|
|
14754
14682
|
import { execSync as execSync6 } from "node:child_process";
|
|
14755
|
-
import { existsSync as
|
|
14683
|
+
import { existsSync as existsSync21, statSync as statSync5 } from "node:fs";
|
|
14756
14684
|
import { join as join27 } from "node:path";
|
|
14757
14685
|
var DEFAULT_DEVBOX_IMAGE = "olam-devbox:latest";
|
|
14758
14686
|
var DEVBOX_BAKED_SOURCES = [
|
|
@@ -14821,9 +14749,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
|
|
|
14821
14749
|
"These source files have changed since the image was built; the",
|
|
14822
14750
|
"changes will NOT take effect in fresh worlds until you rebuild:"
|
|
14823
14751
|
];
|
|
14824
|
-
for (const { path:
|
|
14752
|
+
for (const { path: path44, mtimeMs } of result.newerSources) {
|
|
14825
14753
|
const when = new Date(mtimeMs).toISOString();
|
|
14826
|
-
lines.push(` \u2022 ${
|
|
14754
|
+
lines.push(` \u2022 ${path44} (modified ${when})`);
|
|
14827
14755
|
}
|
|
14828
14756
|
lines.push("");
|
|
14829
14757
|
lines.push("Rebuild with:");
|
|
@@ -14842,7 +14770,7 @@ function defaultDockerInspect(image) {
|
|
|
14842
14770
|
}
|
|
14843
14771
|
function defaultStatMtime(absPath) {
|
|
14844
14772
|
try {
|
|
14845
|
-
if (!
|
|
14773
|
+
if (!existsSync21(absPath))
|
|
14846
14774
|
return null;
|
|
14847
14775
|
return statSync5(absPath).mtimeMs;
|
|
14848
14776
|
} catch {
|
|
@@ -14982,15 +14910,15 @@ init_host_cp();
|
|
|
14982
14910
|
var HOST_CP_URL = "http://127.0.0.1:19000";
|
|
14983
14911
|
async function readHostCpTokenForCreate() {
|
|
14984
14912
|
try {
|
|
14985
|
-
const { default:
|
|
14913
|
+
const { default: fs40 } = await import("node:fs");
|
|
14986
14914
|
const { default: os24 } = await import("node:os");
|
|
14987
|
-
const { default:
|
|
14988
|
-
const tp =
|
|
14989
|
-
process.env.OLAM_HOME ??
|
|
14915
|
+
const { default: path44 } = await import("node:path");
|
|
14916
|
+
const tp = path44.join(
|
|
14917
|
+
process.env.OLAM_HOME ?? path44.join(os24.homedir(), ".olam"),
|
|
14990
14918
|
"host-cp.token"
|
|
14991
14919
|
);
|
|
14992
|
-
if (!
|
|
14993
|
-
return
|
|
14920
|
+
if (!fs40.existsSync(tp)) return null;
|
|
14921
|
+
return fs40.readFileSync(tp, "utf-8").trim();
|
|
14994
14922
|
} catch {
|
|
14995
14923
|
return null;
|
|
14996
14924
|
}
|
|
@@ -15015,7 +14943,7 @@ function registerCreate(program2) {
|
|
|
15015
14943
|
if (decision.stderrLine) {
|
|
15016
14944
|
process.stderr.write(decision.stderrLine + "\n");
|
|
15017
14945
|
}
|
|
15018
|
-
|
|
14946
|
+
spawnSync9("docker", ["pull", overrideRef], { stdio: "pipe" });
|
|
15019
14947
|
const { inspectImageProtocolVersions: inspectImageProtocolVersions2, checkProtocolOverlap: checkProtocolOverlap2 } = await Promise.resolve().then(() => (init_protocol_version(), protocol_version_exports));
|
|
15020
14948
|
const inspect = inspectImageProtocolVersions2(overrideRef);
|
|
15021
14949
|
if (inspect.inspectFailed) {
|
|
@@ -15064,9 +14992,9 @@ function registerCreate(program2) {
|
|
|
15064
14992
|
const reason = inferred.repos.length === 0 ? "no repo names extracted from prompt" : `inference confidence ${inferred.confidence.toFixed(2)} below ${PICKER_CONFIDENCE_THRESHOLD}`;
|
|
15065
14993
|
printError(`Picker needed: ${reason}`);
|
|
15066
14994
|
if (catalogRepos.length > 0) {
|
|
15067
|
-
console.log(
|
|
14995
|
+
console.log(pc10.dim(` available repos: ${catalogRepos.join(", ")}`));
|
|
15068
14996
|
}
|
|
15069
|
-
console.log(
|
|
14997
|
+
console.log(pc10.dim(" rerun with explicit --workspace <name> or --repos <a> <b> <c>"));
|
|
15070
14998
|
process.exitCode = 1;
|
|
15071
14999
|
return;
|
|
15072
15000
|
}
|
|
@@ -15084,7 +15012,7 @@ function registerCreate(program2) {
|
|
|
15084
15012
|
inferenceSpinner.fail(`Multiple workspaces match (${decision.result})`);
|
|
15085
15013
|
const cands = decision.result === "exact-N" ? decision.candidates.map((w) => w.name) : decision.candidates.map((c) => c.workspace.name);
|
|
15086
15014
|
printError(`Picker needed: ${cands.join(", ")}`);
|
|
15087
|
-
console.log(
|
|
15015
|
+
console.log(pc10.dim(" rerun with explicit --workspace <name>"));
|
|
15088
15016
|
process.exitCode = 1;
|
|
15089
15017
|
return;
|
|
15090
15018
|
} else {
|
|
@@ -15132,7 +15060,7 @@ function registerCreate(program2) {
|
|
|
15132
15060
|
throw err;
|
|
15133
15061
|
}
|
|
15134
15062
|
const spinner2 = ora3("Rebuilding olam-devbox:latest\u2026").start();
|
|
15135
|
-
const rebuild =
|
|
15063
|
+
const rebuild = spawnSync9(
|
|
15136
15064
|
"bash",
|
|
15137
15065
|
[buildScript],
|
|
15138
15066
|
{ cwd: repoRoot, stdio: "inherit" }
|
|
@@ -15198,10 +15126,10 @@ function registerCreate(program2) {
|
|
|
15198
15126
|
}
|
|
15199
15127
|
if (world.credentialsInjected?.claude) {
|
|
15200
15128
|
console.log(`
|
|
15201
|
-
${
|
|
15129
|
+
${pc10.green("Credentials injected. World is ready for dispatch.")}`);
|
|
15202
15130
|
} else if (world.dashboardUrl) {
|
|
15203
15131
|
console.log(`
|
|
15204
|
-
${
|
|
15132
|
+
${pc10.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
15205
15133
|
}
|
|
15206
15134
|
if (opts.hostCp !== false) {
|
|
15207
15135
|
const probeResult = await probeHostCp().catch(() => null);
|
|
@@ -15213,14 +15141,14 @@ ${pc9.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
15213
15141
|
process.stderr.write(
|
|
15214
15142
|
[
|
|
15215
15143
|
"",
|
|
15216
|
-
|
|
15144
|
+
pc10.red("Host CP probe failed."),
|
|
15217
15145
|
` tried: http://127.0.0.1:19000/api/bootstrap \u2192 ${diag.bootstrapStatus}`,
|
|
15218
15146
|
` tried: docker container "olam-host-cp" \u2192 ${diag.containerStatus}`,
|
|
15219
15147
|
"",
|
|
15220
|
-
|
|
15221
|
-
` ${
|
|
15222
|
-
` ${
|
|
15223
|
-
` OR pass ${
|
|
15148
|
+
pc10.yellow("World was created but the SPA inbox will not show it until you:"),
|
|
15149
|
+
` ${pc10.cyan("olam host-cp start")} (start the host CP)`,
|
|
15150
|
+
` ${pc10.cyan(`olam host-cp register --world ${world.id}`)} (register manually)`,
|
|
15151
|
+
` OR pass ${pc10.dim("--no-host-cp")} on next create to suppress this check.`,
|
|
15224
15152
|
""
|
|
15225
15153
|
].join("\n")
|
|
15226
15154
|
);
|
|
@@ -15252,13 +15180,13 @@ ${pc9.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
15252
15180
|
process.stderr.write(
|
|
15253
15181
|
[
|
|
15254
15182
|
"",
|
|
15255
|
-
|
|
15183
|
+
pc10.red("Host CP registry POST failed."),
|
|
15256
15184
|
` url: ${hostCpUrl}/api/admin/registry`,
|
|
15257
15185
|
` status: ${reg.status}`,
|
|
15258
15186
|
` error: ${reg.error}`,
|
|
15259
15187
|
"",
|
|
15260
|
-
|
|
15261
|
-
` ${
|
|
15188
|
+
pc10.yellow("World was created but not registered. Run manually:"),
|
|
15189
|
+
` ${pc10.cyan(`olam host-cp register --world ${world.id}`)}`,
|
|
15262
15190
|
""
|
|
15263
15191
|
].join("\n")
|
|
15264
15192
|
);
|
|
@@ -15313,7 +15241,7 @@ ${pc9.yellow("Authenticate at")} ${world.dashboardUrl}`);
|
|
|
15313
15241
|
}
|
|
15314
15242
|
const worldUrl = `${hostCpUrl}/world/${encodeURIComponent(world.id)}`;
|
|
15315
15243
|
console.log(`
|
|
15316
|
-
${
|
|
15244
|
+
${pc10.cyan("Host CP UI:")} ${worldUrl}`);
|
|
15317
15245
|
if (opts.open !== false && reg.ok) {
|
|
15318
15246
|
const openResult = openUrl(worldUrl);
|
|
15319
15247
|
if (openResult.opened) {
|
|
@@ -15327,14 +15255,7 @@ ${pc9.cyan("Host CP UI:")} ${worldUrl}`);
|
|
|
15327
15255
|
spinner.fail("Failed to create world");
|
|
15328
15256
|
if (err instanceof AuthPreflightError) {
|
|
15329
15257
|
printError(err.message);
|
|
15330
|
-
if (err.remedy) console.log(` ${
|
|
15331
|
-
} else if (err instanceof RepoSelectionRequiredError) {
|
|
15332
|
-
printError(err.message);
|
|
15333
|
-
if (err.availableRepos.length > 0) {
|
|
15334
|
-
const example = err.availableRepos[0];
|
|
15335
|
-
console.log(` ${pc9.dim(`Example: olam create --name <name> --repos ${example} --task "<task>"`)}`);
|
|
15336
|
-
console.log(` ${pc9.dim('Or: olam create --name <name> --workspace <workspace> --task "<task>"')}`);
|
|
15337
|
-
}
|
|
15258
|
+
if (err.remedy) console.log(` ${pc10.dim(err.remedy)}`);
|
|
15338
15259
|
} else {
|
|
15339
15260
|
printError(err instanceof Error ? err.message : String(err));
|
|
15340
15261
|
}
|
|
@@ -15345,10 +15266,10 @@ ${pc9.cyan("Host CP UI:")} ${worldUrl}`);
|
|
|
15345
15266
|
function resolveRepoRoot(start) {
|
|
15346
15267
|
let cur = start;
|
|
15347
15268
|
while (true) {
|
|
15348
|
-
if (
|
|
15269
|
+
if (existsSync22(resolve7(cur, "packages")) && existsSync22(resolve7(cur, "package.json"))) {
|
|
15349
15270
|
return cur;
|
|
15350
15271
|
}
|
|
15351
|
-
const parent =
|
|
15272
|
+
const parent = dirname14(cur);
|
|
15352
15273
|
if (parent === cur) return start;
|
|
15353
15274
|
cur = parent;
|
|
15354
15275
|
}
|
|
@@ -15359,12 +15280,12 @@ function defaultNameFromPrompt(prompt) {
|
|
|
15359
15280
|
}
|
|
15360
15281
|
async function readHostCpToken3() {
|
|
15361
15282
|
try {
|
|
15362
|
-
const { default:
|
|
15283
|
+
const { default: fs40 } = await import("node:fs");
|
|
15363
15284
|
const { default: os24 } = await import("node:os");
|
|
15364
|
-
const { default:
|
|
15365
|
-
const tp =
|
|
15366
|
-
if (!
|
|
15367
|
-
const raw =
|
|
15285
|
+
const { default: path44 } = await import("node:path");
|
|
15286
|
+
const tp = path44.join(os24.homedir(), ".olam", "host-cp.token");
|
|
15287
|
+
if (!fs40.existsSync(tp)) return null;
|
|
15288
|
+
const raw = fs40.readFileSync(tp, "utf-8").trim();
|
|
15368
15289
|
return raw.length > 0 ? raw : null;
|
|
15369
15290
|
} catch {
|
|
15370
15291
|
return null;
|
|
@@ -15409,7 +15330,7 @@ async function fetchHostCpExactMatches(projects) {
|
|
|
15409
15330
|
init_context();
|
|
15410
15331
|
init_output();
|
|
15411
15332
|
import ora4 from "ora";
|
|
15412
|
-
import
|
|
15333
|
+
import pc11 from "picocolors";
|
|
15413
15334
|
|
|
15414
15335
|
// ../core/dist/orchestrator/dispatch.js
|
|
15415
15336
|
var DEEP_MODE_SUFFIX = "\n\nUltrathink and use sub-agents (the Agent tool) to parallelize independent work. Break complex tasks into focused sub-tasks.";
|
|
@@ -15496,7 +15417,7 @@ OLAM_EOF`
|
|
|
15496
15417
|
const containerName = `olam-${worldId}-devbox`;
|
|
15497
15418
|
console.log(
|
|
15498
15419
|
`
|
|
15499
|
-
${
|
|
15420
|
+
${pc11.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
|
|
15500
15421
|
);
|
|
15501
15422
|
} catch (err) {
|
|
15502
15423
|
spinner.fail("Dispatch failed");
|
|
@@ -15509,7 +15430,7 @@ ${pc10.dim(`Watch live: docker exec -it ${containerName} tmux attach -t claude-m
|
|
|
15509
15430
|
// src/commands/observe.ts
|
|
15510
15431
|
init_context();
|
|
15511
15432
|
init_output();
|
|
15512
|
-
import
|
|
15433
|
+
import pc12 from "picocolors";
|
|
15513
15434
|
function registerObserve(program2) {
|
|
15514
15435
|
program2.command("observe").description("Stream thoughts from a world (coming soon)").argument("<world>", "World ID").action(async (worldId) => {
|
|
15515
15436
|
const { ctx, error } = await loadContext();
|
|
@@ -15529,15 +15450,15 @@ function registerObserve(program2) {
|
|
|
15529
15450
|
checkVersionPin2(worldId, world.workspacePath);
|
|
15530
15451
|
}
|
|
15531
15452
|
console.log(
|
|
15532
|
-
|
|
15453
|
+
pc12.yellow("Observation is coming in a future release.")
|
|
15533
15454
|
);
|
|
15534
15455
|
console.log(
|
|
15535
|
-
|
|
15456
|
+
pc12.dim("This will stream the world's reasoning in real-time.")
|
|
15536
15457
|
);
|
|
15537
15458
|
const containerName = `olam-${worldId}-devbox`;
|
|
15538
15459
|
console.log(
|
|
15539
15460
|
`
|
|
15540
|
-
${
|
|
15461
|
+
${pc12.dim(`For now: docker exec -it ${containerName} tmux attach -t claude-main -r`)}`
|
|
15541
15462
|
);
|
|
15542
15463
|
});
|
|
15543
15464
|
}
|
|
@@ -15545,7 +15466,7 @@ ${pc11.dim(`For now: docker exec -it ${containerName} tmux attach -t claude-main
|
|
|
15545
15466
|
// src/commands/list.ts
|
|
15546
15467
|
init_context();
|
|
15547
15468
|
init_output();
|
|
15548
|
-
import
|
|
15469
|
+
import pc13 from "picocolors";
|
|
15549
15470
|
function registerList(program2) {
|
|
15550
15471
|
program2.command("list").alias("ls").description("List active worlds").action(async () => {
|
|
15551
15472
|
const { ctx, error } = await loadContext();
|
|
@@ -15556,18 +15477,18 @@ function registerList(program2) {
|
|
|
15556
15477
|
}
|
|
15557
15478
|
const worlds = ctx.worldManager.listWorlds();
|
|
15558
15479
|
if (worlds.length === 0) {
|
|
15559
|
-
console.log(
|
|
15480
|
+
console.log(pc13.dim("No worlds. Create one with `olam create --name my-world`."));
|
|
15560
15481
|
return;
|
|
15561
15482
|
}
|
|
15562
|
-
console.log(`${
|
|
15483
|
+
console.log(`${pc13.bold(String(worlds.length))} world(s)
|
|
15563
15484
|
`);
|
|
15564
15485
|
for (const w of worlds) {
|
|
15565
|
-
const statusColor = w.status === "running" ?
|
|
15486
|
+
const statusColor = w.status === "running" ? pc13.green(w.status) : w.status === "error" ? pc13.red(w.status) : pc13.yellow(w.status);
|
|
15566
15487
|
const age = formatAge(w.createdAt);
|
|
15567
15488
|
const cost = `$${w.totalCostUsd.toFixed(2)}`;
|
|
15568
|
-
console.log(` ${
|
|
15489
|
+
console.log(` ${pc13.bold(w.name)} ${pc13.dim(`(${w.id})`)}`);
|
|
15569
15490
|
console.log(
|
|
15570
|
-
` ${statusColor} ${
|
|
15491
|
+
` ${statusColor} ${pc13.dim("|")} ${w.repos.join(", ")} ${pc13.dim("|")} ${cost} ${pc13.dim("|")} ${age}`
|
|
15571
15492
|
);
|
|
15572
15493
|
console.log();
|
|
15573
15494
|
}
|
|
@@ -15576,9 +15497,9 @@ function registerList(program2) {
|
|
|
15576
15497
|
|
|
15577
15498
|
// src/commands/status.ts
|
|
15578
15499
|
init_output();
|
|
15579
|
-
import * as
|
|
15500
|
+
import * as fs22 from "node:fs";
|
|
15580
15501
|
import * as os13 from "node:os";
|
|
15581
|
-
import * as
|
|
15502
|
+
import * as path25 from "node:path";
|
|
15582
15503
|
var CLI_VERSION2 = process.env["OLAM_CLI_VERSION"] ?? "0.0.0";
|
|
15583
15504
|
var HOST_CP_PORT2 = 19e3;
|
|
15584
15505
|
async function getMachineStatus(_probe, _loadCtx, _readToken) {
|
|
@@ -15608,14 +15529,14 @@ async function getMachineStatus(_probe, _loadCtx, _readToken) {
|
|
|
15608
15529
|
}
|
|
15609
15530
|
} catch {
|
|
15610
15531
|
}
|
|
15611
|
-
const manifestPath2 =
|
|
15532
|
+
const manifestPath2 = path25.join(os13.homedir(), ".olam", "cache", "manifest.json");
|
|
15612
15533
|
let updateAvailable = null;
|
|
15613
15534
|
let lastUpdateCheck = null;
|
|
15614
|
-
if (
|
|
15615
|
-
const mtime =
|
|
15535
|
+
if (fs22.existsSync(manifestPath2)) {
|
|
15536
|
+
const mtime = fs22.statSync(manifestPath2).mtime;
|
|
15616
15537
|
lastUpdateCheck = mtime.toISOString();
|
|
15617
15538
|
try {
|
|
15618
|
-
const manifest = JSON.parse(
|
|
15539
|
+
const manifest = JSON.parse(fs22.readFileSync(manifestPath2, "utf-8"));
|
|
15619
15540
|
const latest = manifest["version"];
|
|
15620
15541
|
updateAvailable = latest !== void 0 && latest !== CLI_VERSION2;
|
|
15621
15542
|
} catch {
|
|
@@ -15738,10 +15659,10 @@ function registerDestroy(program2) {
|
|
|
15738
15659
|
// src/commands/clean.ts
|
|
15739
15660
|
init_context();
|
|
15740
15661
|
init_output();
|
|
15741
|
-
import
|
|
15662
|
+
import fs23 from "node:fs";
|
|
15742
15663
|
import os14 from "node:os";
|
|
15743
|
-
import
|
|
15744
|
-
import { execFileSync as
|
|
15664
|
+
import path26 from "node:path";
|
|
15665
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
15745
15666
|
function registerClean(program2) {
|
|
15746
15667
|
program2.command("clean").description("Reap orphan world filesystem state under ~/.olam/worlds/").option("--apply", "Actually delete the orphans (default is dry-run)", false).option(
|
|
15747
15668
|
"--include-dirty",
|
|
@@ -15764,8 +15685,8 @@ async function runClean(opts) {
|
|
|
15764
15685
|
printError(error?.message ?? "Olam is not configured. Run `olam init` first.");
|
|
15765
15686
|
return 1;
|
|
15766
15687
|
}
|
|
15767
|
-
const worldsDir =
|
|
15768
|
-
if (!
|
|
15688
|
+
const worldsDir = path26.join(os14.homedir(), ".olam", "worlds");
|
|
15689
|
+
if (!fs23.existsSync(worldsDir)) {
|
|
15769
15690
|
if (opts.json) {
|
|
15770
15691
|
process.stdout.write(`${JSON.stringify({ worldsDir, entries: [] })}
|
|
15771
15692
|
`);
|
|
@@ -15780,8 +15701,8 @@ async function runClean(opts) {
|
|
|
15780
15701
|
}
|
|
15781
15702
|
const worktreeMap = collectWorktrees(worldsDir);
|
|
15782
15703
|
const entries = [];
|
|
15783
|
-
for (const id of
|
|
15784
|
-
const fullPath =
|
|
15704
|
+
for (const id of fs23.readdirSync(worldsDir).sort()) {
|
|
15705
|
+
const fullPath = path26.join(worldsDir, id);
|
|
15785
15706
|
const stat = safeStat(fullPath);
|
|
15786
15707
|
if (!stat || !stat.isDirectory()) continue;
|
|
15787
15708
|
entries.push(classifyWorld({ id, fullPath, liveIds, worktreeMap }));
|
|
@@ -15847,7 +15768,7 @@ function classifyWorld(args) {
|
|
|
15847
15768
|
if (liveIds.has(id)) {
|
|
15848
15769
|
return { id, path: fullPath, bytes, category: "active", note: "in registry" };
|
|
15849
15770
|
}
|
|
15850
|
-
const worktreeChild =
|
|
15771
|
+
const worktreeChild = path26.join(fullPath, "olam");
|
|
15851
15772
|
const worktreeInfo = worktreeMap.get(worktreeChild);
|
|
15852
15773
|
if (worktreeInfo) {
|
|
15853
15774
|
if (worktreeInfo.dirty > 0 || worktreeInfo.unpushed > 0) {
|
|
@@ -15884,7 +15805,7 @@ function reapEntry(entry) {
|
|
|
15884
15805
|
try {
|
|
15885
15806
|
const gitDir = resolveGitDirForWorktree(entry.worktreePath);
|
|
15886
15807
|
if (gitDir) {
|
|
15887
|
-
|
|
15808
|
+
execFileSync5("git", ["worktree", "remove", "--force", entry.worktreePath], {
|
|
15888
15809
|
cwd: gitDir,
|
|
15889
15810
|
stdio: "pipe"
|
|
15890
15811
|
});
|
|
@@ -15893,20 +15814,20 @@ function reapEntry(entry) {
|
|
|
15893
15814
|
}
|
|
15894
15815
|
}
|
|
15895
15816
|
try {
|
|
15896
|
-
|
|
15817
|
+
fs23.rmSync(entry.path, { recursive: true, force: true });
|
|
15897
15818
|
} catch (err) {
|
|
15898
15819
|
process.stderr.write(` ! rm ${entry.path}: ${err instanceof Error ? err.message : String(err)}
|
|
15899
15820
|
`);
|
|
15900
15821
|
return false;
|
|
15901
15822
|
}
|
|
15902
|
-
return !
|
|
15823
|
+
return !fs23.existsSync(entry.path);
|
|
15903
15824
|
}
|
|
15904
15825
|
function collectWorktrees(worldsDir) {
|
|
15905
15826
|
const out = /* @__PURE__ */ new Map();
|
|
15906
|
-
for (const id of
|
|
15907
|
-
const child =
|
|
15908
|
-
const gitMarker =
|
|
15909
|
-
if (!
|
|
15827
|
+
for (const id of fs23.readdirSync(worldsDir)) {
|
|
15828
|
+
const child = path26.join(worldsDir, id, "olam");
|
|
15829
|
+
const gitMarker = path26.join(child, ".git");
|
|
15830
|
+
if (!fs23.existsSync(gitMarker)) continue;
|
|
15910
15831
|
const gitDir = resolveGitDirForWorktree(child);
|
|
15911
15832
|
if (!gitDir) continue;
|
|
15912
15833
|
const branch = readBranch(child);
|
|
@@ -15917,21 +15838,21 @@ function collectWorktrees(worldsDir) {
|
|
|
15917
15838
|
return out;
|
|
15918
15839
|
}
|
|
15919
15840
|
function resolveGitDirForWorktree(worktreePath) {
|
|
15920
|
-
const gitMarker =
|
|
15841
|
+
const gitMarker = path26.join(worktreePath, ".git");
|
|
15921
15842
|
try {
|
|
15922
|
-
const top =
|
|
15843
|
+
const top = execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
15923
15844
|
cwd: worktreePath,
|
|
15924
15845
|
encoding: "utf-8",
|
|
15925
15846
|
stdio: "pipe"
|
|
15926
15847
|
}).trim();
|
|
15927
15848
|
if (!top) return null;
|
|
15928
|
-
const common =
|
|
15849
|
+
const common = execFileSync5("git", ["rev-parse", "--git-common-dir"], {
|
|
15929
15850
|
cwd: worktreePath,
|
|
15930
15851
|
encoding: "utf-8",
|
|
15931
15852
|
stdio: "pipe"
|
|
15932
15853
|
}).trim();
|
|
15933
15854
|
if (!common) return top;
|
|
15934
|
-
return
|
|
15855
|
+
return path26.dirname(path26.resolve(worktreePath, common));
|
|
15935
15856
|
} catch {
|
|
15936
15857
|
return null;
|
|
15937
15858
|
}
|
|
@@ -15939,7 +15860,7 @@ function resolveGitDirForWorktree(worktreePath) {
|
|
|
15939
15860
|
}
|
|
15940
15861
|
function readBranch(worktreePath) {
|
|
15941
15862
|
try {
|
|
15942
|
-
return
|
|
15863
|
+
return execFileSync5("git", ["branch", "--show-current"], {
|
|
15943
15864
|
cwd: worktreePath,
|
|
15944
15865
|
encoding: "utf-8",
|
|
15945
15866
|
stdio: "pipe"
|
|
@@ -15950,7 +15871,7 @@ function readBranch(worktreePath) {
|
|
|
15950
15871
|
}
|
|
15951
15872
|
function countDirty(worktreePath) {
|
|
15952
15873
|
try {
|
|
15953
|
-
const out =
|
|
15874
|
+
const out = execFileSync5("git", ["status", "--porcelain"], {
|
|
15954
15875
|
cwd: worktreePath,
|
|
15955
15876
|
encoding: "utf-8",
|
|
15956
15877
|
stdio: "pipe"
|
|
@@ -15962,7 +15883,7 @@ function countDirty(worktreePath) {
|
|
|
15962
15883
|
}
|
|
15963
15884
|
function countUnpushed(worktreePath) {
|
|
15964
15885
|
try {
|
|
15965
|
-
const out =
|
|
15886
|
+
const out = execFileSync5("git", ["log", "@{u}..HEAD", "--oneline"], {
|
|
15966
15887
|
cwd: worktreePath,
|
|
15967
15888
|
encoding: "utf-8",
|
|
15968
15889
|
stdio: "pipe"
|
|
@@ -15974,7 +15895,7 @@ function countUnpushed(worktreePath) {
|
|
|
15974
15895
|
}
|
|
15975
15896
|
function safeStat(p) {
|
|
15976
15897
|
try {
|
|
15977
|
-
return
|
|
15898
|
+
return fs23.statSync(p);
|
|
15978
15899
|
} catch {
|
|
15979
15900
|
return null;
|
|
15980
15901
|
}
|
|
@@ -15986,7 +15907,7 @@ function computeBytes(p) {
|
|
|
15986
15907
|
const cur = stack.pop();
|
|
15987
15908
|
let st;
|
|
15988
15909
|
try {
|
|
15989
|
-
st =
|
|
15910
|
+
st = fs23.lstatSync(cur);
|
|
15990
15911
|
} catch {
|
|
15991
15912
|
continue;
|
|
15992
15913
|
}
|
|
@@ -15994,11 +15915,11 @@ function computeBytes(p) {
|
|
|
15994
15915
|
if (st.isDirectory()) {
|
|
15995
15916
|
let entries;
|
|
15996
15917
|
try {
|
|
15997
|
-
entries =
|
|
15918
|
+
entries = fs23.readdirSync(cur);
|
|
15998
15919
|
} catch {
|
|
15999
15920
|
continue;
|
|
16000
15921
|
}
|
|
16001
|
-
for (const name of entries) stack.push(
|
|
15922
|
+
for (const name of entries) stack.push(path26.join(cur, name));
|
|
16002
15923
|
} else {
|
|
16003
15924
|
total += st.size;
|
|
16004
15925
|
}
|
|
@@ -16051,7 +15972,7 @@ async function confirmInteractive() {
|
|
|
16051
15972
|
init_context();
|
|
16052
15973
|
init_output();
|
|
16053
15974
|
import { execSync as execSync7 } from "node:child_process";
|
|
16054
|
-
import
|
|
15975
|
+
import pc14 from "picocolors";
|
|
16055
15976
|
var SAFE_IDENT3 = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
16056
15977
|
function buildStartClaudeCommands(containerName, sessionName, task) {
|
|
16057
15978
|
if (!SAFE_IDENT3.test(containerName)) {
|
|
@@ -16158,7 +16079,7 @@ function registerEnter(program2) {
|
|
|
16158
16079
|
}
|
|
16159
16080
|
console.log("Run these commands in order to enter the world:\n");
|
|
16160
16081
|
for (const step of steps) {
|
|
16161
|
-
console.log(` ${
|
|
16082
|
+
console.log(` ${pc14.dim(`# ${step.description}`)}`);
|
|
16162
16083
|
if (step.stdin !== void 0) {
|
|
16163
16084
|
const escaped = step.stdin.replace(/'/g, "'\\''");
|
|
16164
16085
|
console.log(` printf '%s' '${escaped}' | ${step.command}`);
|
|
@@ -16192,11 +16113,11 @@ function registerEnter(program2) {
|
|
|
16192
16113
|
return;
|
|
16193
16114
|
}
|
|
16194
16115
|
console.log("Run this command to enter the world:\n");
|
|
16195
|
-
console.log(` ${
|
|
16116
|
+
console.log(` ${pc14.bold(result.command)}`);
|
|
16196
16117
|
const containerName = `olam-${worldId}-devbox`;
|
|
16197
16118
|
console.log(
|
|
16198
16119
|
`
|
|
16199
|
-
${
|
|
16120
|
+
${pc14.dim(`Observe dispatch: docker exec -it ${containerName} tmux attach -t olam-dispatch -r`)}`
|
|
16200
16121
|
);
|
|
16201
16122
|
});
|
|
16202
16123
|
}
|
|
@@ -16206,7 +16127,7 @@ init_context();
|
|
|
16206
16127
|
init_output();
|
|
16207
16128
|
init_exit_codes();
|
|
16208
16129
|
init_world_paths();
|
|
16209
|
-
import * as
|
|
16130
|
+
import * as fs24 from "node:fs";
|
|
16210
16131
|
import "node:path";
|
|
16211
16132
|
import ora6 from "ora";
|
|
16212
16133
|
function registerCrystallize(program2, options = {}) {
|
|
@@ -16238,7 +16159,7 @@ function registerCrystallize(program2, options = {}) {
|
|
|
16238
16159
|
return;
|
|
16239
16160
|
}
|
|
16240
16161
|
const thoughtDbPath = getWorldDbPath(world.workspacePath);
|
|
16241
|
-
if (!
|
|
16162
|
+
if (!fs24.existsSync(thoughtDbPath)) {
|
|
16242
16163
|
printError(`No thoughts captured yet for "${worldId}". Run a dispatch first.`);
|
|
16243
16164
|
process.exitCode = EXIT_GENERIC_ERROR;
|
|
16244
16165
|
return;
|
|
@@ -16308,7 +16229,7 @@ function registerCrystallize(program2, options = {}) {
|
|
|
16308
16229
|
|
|
16309
16230
|
// src/commands/pr.ts
|
|
16310
16231
|
init_registry();
|
|
16311
|
-
import
|
|
16232
|
+
import pc15 from "picocolors";
|
|
16312
16233
|
|
|
16313
16234
|
// ../core/dist/pr-gate/client.js
|
|
16314
16235
|
var HOST_CONTROL_PLANE_BASE2 = 19080;
|
|
@@ -16389,26 +16310,26 @@ async function findGate(id) {
|
|
|
16389
16310
|
return null;
|
|
16390
16311
|
}
|
|
16391
16312
|
function formatStateBadge(state) {
|
|
16392
|
-
if (state === "approve") return
|
|
16393
|
-
if (state === "block") return
|
|
16394
|
-
if (state === "denied") return
|
|
16395
|
-
return
|
|
16313
|
+
if (state === "approve") return pc15.green("approve");
|
|
16314
|
+
if (state === "block") return pc15.red("block");
|
|
16315
|
+
if (state === "denied") return pc15.gray("denied");
|
|
16316
|
+
return pc15.yellow("pending");
|
|
16396
16317
|
}
|
|
16397
16318
|
function registerPr(program2) {
|
|
16398
16319
|
const pr = program2.command("pr").description("Review and decide PR-gate requests from running worlds");
|
|
16399
16320
|
pr.command("list").description("List all PR-gate requests across every running world").action(async () => {
|
|
16400
16321
|
const all = await fanoutList();
|
|
16401
16322
|
if (all.length === 0) {
|
|
16402
|
-
console.log(
|
|
16323
|
+
console.log(pc15.dim("No gates open."));
|
|
16403
16324
|
return;
|
|
16404
16325
|
}
|
|
16405
16326
|
printHeader(`${all.length} gate(s)`);
|
|
16406
16327
|
for (const { world, gate } of all) {
|
|
16407
16328
|
const badge = formatStateBadge(gate.state);
|
|
16408
16329
|
console.log(
|
|
16409
|
-
` ${
|
|
16330
|
+
` ${pc15.bold(gate.id.slice(0, 8))} ${badge.padEnd(20)} ${pc15.dim(world.name)} ${pc15.dim(gate.branch)}`
|
|
16410
16331
|
);
|
|
16411
|
-
console.log(` ${
|
|
16332
|
+
console.log(` ${pc15.dim(gate.command.slice(0, 100))}`);
|
|
16412
16333
|
}
|
|
16413
16334
|
});
|
|
16414
16335
|
pr.command("show").description("Show full gate detail (diff, command, commits)").argument("<id>", "Gate id (prefix match ok)").action(async (id) => {
|
|
@@ -16431,9 +16352,9 @@ function registerPr(program2) {
|
|
|
16431
16352
|
if (gate.reason) printInfo("Reason", gate.reason);
|
|
16432
16353
|
}
|
|
16433
16354
|
printHeader("Commits");
|
|
16434
|
-
console.log(gate.commitLog ||
|
|
16355
|
+
console.log(gate.commitLog || pc15.dim(" (empty)"));
|
|
16435
16356
|
printHeader("Diff stat");
|
|
16436
|
-
console.log(gate.diffStat ||
|
|
16357
|
+
console.log(gate.diffStat || pc15.dim(" (empty)"));
|
|
16437
16358
|
});
|
|
16438
16359
|
function decideCommand(verb, decision) {
|
|
16439
16360
|
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) => {
|
|
@@ -16558,7 +16479,7 @@ function registerLanes(program2) {
|
|
|
16558
16479
|
// src/commands/policy-check.ts
|
|
16559
16480
|
init_loader2();
|
|
16560
16481
|
import { execSync as execSync8 } from "node:child_process";
|
|
16561
|
-
import
|
|
16482
|
+
import pc16 from "picocolors";
|
|
16562
16483
|
|
|
16563
16484
|
// ../../node_modules/balanced-match/dist/esm/index.js
|
|
16564
16485
|
var balanced = (a, b, str) => {
|
|
@@ -17612,11 +17533,11 @@ var qmarksTestNoExtDot = ([$0]) => {
|
|
|
17612
17533
|
return (f) => f.length === len && f !== "." && f !== "..";
|
|
17613
17534
|
};
|
|
17614
17535
|
var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
|
|
17615
|
-
var
|
|
17536
|
+
var path28 = {
|
|
17616
17537
|
win32: { sep: "\\" },
|
|
17617
17538
|
posix: { sep: "/" }
|
|
17618
17539
|
};
|
|
17619
|
-
var sep = defaultPlatform === "win32" ?
|
|
17540
|
+
var sep = defaultPlatform === "win32" ? path28.win32.sep : path28.posix.sep;
|
|
17620
17541
|
minimatch.sep = sep;
|
|
17621
17542
|
var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
|
|
17622
17543
|
minimatch.GLOBSTAR = GLOBSTAR;
|
|
@@ -18418,7 +18339,7 @@ function registerPolicyCheck(program2) {
|
|
|
18418
18339
|
const workspaceRoot = opts.workspace ?? process.cwd();
|
|
18419
18340
|
const policies = loadPolicies(workspaceRoot);
|
|
18420
18341
|
if (policies.length === 0) {
|
|
18421
|
-
console.log(
|
|
18342
|
+
console.log(pc16.dim("policy-check: no policies found in .olam/policies/ \u2014 nothing to enforce"));
|
|
18422
18343
|
return;
|
|
18423
18344
|
}
|
|
18424
18345
|
const diff = getDiff(opts.base, workspaceRoot);
|
|
@@ -18427,11 +18348,11 @@ function registerPolicyCheck(program2) {
|
|
|
18427
18348
|
for (const result of results) {
|
|
18428
18349
|
if (result.passed) continue;
|
|
18429
18350
|
if (result.severity === "error") {
|
|
18430
|
-
console.error(`${
|
|
18351
|
+
console.error(`${pc16.red("policy error")} [${result.policyId}]`);
|
|
18431
18352
|
console.error(result.message);
|
|
18432
18353
|
hasErrorFailure = true;
|
|
18433
18354
|
} else {
|
|
18434
|
-
console.warn(`${
|
|
18355
|
+
console.warn(`${pc16.yellow("policy warn")} [${result.policyId}]`);
|
|
18435
18356
|
console.warn(result.message);
|
|
18436
18357
|
}
|
|
18437
18358
|
}
|
|
@@ -18444,23 +18365,23 @@ function registerPolicyCheck(program2) {
|
|
|
18444
18365
|
// src/commands/upgrade.ts
|
|
18445
18366
|
init_output();
|
|
18446
18367
|
init_host_cp();
|
|
18447
|
-
import * as
|
|
18448
|
-
import * as
|
|
18449
|
-
import { spawnSync as
|
|
18368
|
+
import * as fs27 from "node:fs";
|
|
18369
|
+
import * as path31 from "node:path";
|
|
18370
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
18450
18371
|
import ora7 from "ora";
|
|
18451
|
-
import
|
|
18372
|
+
import pc17 from "picocolors";
|
|
18452
18373
|
|
|
18453
18374
|
// src/commands/upgrade-lock.ts
|
|
18454
|
-
import * as
|
|
18375
|
+
import * as fs25 from "node:fs";
|
|
18455
18376
|
import * as os15 from "node:os";
|
|
18456
|
-
import * as
|
|
18457
|
-
import { spawnSync as
|
|
18458
|
-
var LOCK_FILE_PATH =
|
|
18377
|
+
import * as path29 from "node:path";
|
|
18378
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
18379
|
+
var LOCK_FILE_PATH = path29.join(os15.homedir(), ".olam", ".upgrade.lock");
|
|
18459
18380
|
var STALE_LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
18460
18381
|
function readLockFile(lockPath) {
|
|
18461
18382
|
try {
|
|
18462
|
-
if (!
|
|
18463
|
-
const raw =
|
|
18383
|
+
if (!fs25.existsSync(lockPath)) return null;
|
|
18384
|
+
const raw = fs25.readFileSync(lockPath, "utf-8").trim();
|
|
18464
18385
|
if (raw.length === 0) return null;
|
|
18465
18386
|
const parsed = JSON.parse(raw);
|
|
18466
18387
|
if (typeof parsed.pid !== "number" || typeof parsed.startTs !== "number") return null;
|
|
@@ -18479,7 +18400,7 @@ function isPidAlive(pid) {
|
|
|
18479
18400
|
}
|
|
18480
18401
|
var PS_UNAVAILABLE = "__ps_unavailable__";
|
|
18481
18402
|
function getPidCommand(pid) {
|
|
18482
|
-
const result =
|
|
18403
|
+
const result = spawnSync10("ps", ["-p", String(pid), "-o", "comm="], {
|
|
18483
18404
|
encoding: "utf-8",
|
|
18484
18405
|
stdio: ["ignore", "pipe", "ignore"]
|
|
18485
18406
|
});
|
|
@@ -18505,16 +18426,16 @@ function isStaleLock(content, nowMs = Date.now()) {
|
|
|
18505
18426
|
return false;
|
|
18506
18427
|
}
|
|
18507
18428
|
function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
|
|
18508
|
-
const dir =
|
|
18509
|
-
|
|
18429
|
+
const dir = path29.dirname(lockPath);
|
|
18430
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
18510
18431
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
18511
18432
|
try {
|
|
18512
|
-
const fd =
|
|
18433
|
+
const fd = fs25.openSync(lockPath, "wx", 420);
|
|
18513
18434
|
try {
|
|
18514
18435
|
const content = { pid: process.pid, startTs: nowMs };
|
|
18515
|
-
|
|
18436
|
+
fs25.writeSync(fd, JSON.stringify(content));
|
|
18516
18437
|
} finally {
|
|
18517
|
-
|
|
18438
|
+
fs25.closeSync(fd);
|
|
18518
18439
|
}
|
|
18519
18440
|
return { acquired: true, lockPath };
|
|
18520
18441
|
} catch (err) {
|
|
@@ -18523,7 +18444,7 @@ function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
|
|
|
18523
18444
|
const existing2 = readLockFile(lockPath);
|
|
18524
18445
|
if (isStaleLock(existing2, nowMs)) {
|
|
18525
18446
|
try {
|
|
18526
|
-
|
|
18447
|
+
fs25.unlinkSync(lockPath);
|
|
18527
18448
|
} catch (unlinkErr) {
|
|
18528
18449
|
const ucode = unlinkErr.code;
|
|
18529
18450
|
if (ucode !== "ENOENT") throw unlinkErr;
|
|
@@ -18548,7 +18469,7 @@ function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
|
|
|
18548
18469
|
}
|
|
18549
18470
|
function releaseLock(lockPath = LOCK_FILE_PATH) {
|
|
18550
18471
|
try {
|
|
18551
|
-
|
|
18472
|
+
fs25.unlinkSync(lockPath);
|
|
18552
18473
|
} catch (err) {
|
|
18553
18474
|
const code = err.code;
|
|
18554
18475
|
if (code !== "ENOENT") throw err;
|
|
@@ -18566,19 +18487,19 @@ function formatRefusalMessage(result, lockPath = LOCK_FILE_PATH) {
|
|
|
18566
18487
|
}
|
|
18567
18488
|
|
|
18568
18489
|
// src/commands/upgrade-log.ts
|
|
18569
|
-
import * as
|
|
18490
|
+
import * as fs26 from "node:fs";
|
|
18570
18491
|
import * as os16 from "node:os";
|
|
18571
|
-
import * as
|
|
18492
|
+
import * as path30 from "node:path";
|
|
18572
18493
|
function getUpgradeLogPath() {
|
|
18573
18494
|
const home = process.env["HOME"] ?? os16.homedir();
|
|
18574
|
-
return
|
|
18495
|
+
return path30.join(home, ".olam", "upgrade.log");
|
|
18575
18496
|
}
|
|
18576
18497
|
var UPGRADE_LOG_PATH = getUpgradeLogPath();
|
|
18577
18498
|
function appendUpgradeLog(row, logPath = getUpgradeLogPath()) {
|
|
18578
18499
|
try {
|
|
18579
|
-
|
|
18500
|
+
fs26.mkdirSync(path30.dirname(logPath), { recursive: true });
|
|
18580
18501
|
const line = JSON.stringify(row) + "\n";
|
|
18581
|
-
|
|
18502
|
+
fs26.appendFileSync(logPath, line, { mode: 420 });
|
|
18582
18503
|
} catch (err) {
|
|
18583
18504
|
process.stderr.write(
|
|
18584
18505
|
`[upgrade-log] failed to append: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -18587,10 +18508,10 @@ function appendUpgradeLog(row, logPath = getUpgradeLogPath()) {
|
|
|
18587
18508
|
}
|
|
18588
18509
|
}
|
|
18589
18510
|
function readUpgradeLog(limit = 10, logPath = getUpgradeLogPath()) {
|
|
18590
|
-
if (!
|
|
18511
|
+
if (!fs26.existsSync(logPath)) return [];
|
|
18591
18512
|
let raw;
|
|
18592
18513
|
try {
|
|
18593
|
-
raw =
|
|
18514
|
+
raw = fs26.readFileSync(logPath, "utf-8");
|
|
18594
18515
|
} catch (err) {
|
|
18595
18516
|
process.stderr.write(
|
|
18596
18517
|
`[upgrade-log] failed to read: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -18683,12 +18604,12 @@ init_protocol_version();
|
|
|
18683
18604
|
init_install_root();
|
|
18684
18605
|
var AUTH_HEALTH_URL2 = "http://127.0.0.1:9999/health";
|
|
18685
18606
|
function isNodeModulesInSync(cwd) {
|
|
18686
|
-
const lockPath =
|
|
18687
|
-
const markerPath =
|
|
18688
|
-
if (!
|
|
18607
|
+
const lockPath = path31.join(cwd, "package-lock.json");
|
|
18608
|
+
const markerPath = path31.join(cwd, "node_modules", ".package-lock.json");
|
|
18609
|
+
if (!fs27.existsSync(lockPath) || !fs27.existsSync(markerPath)) return false;
|
|
18689
18610
|
try {
|
|
18690
|
-
const lockStat =
|
|
18691
|
-
const markerStat =
|
|
18611
|
+
const lockStat = fs27.statSync(lockPath);
|
|
18612
|
+
const markerStat = fs27.statSync(markerPath);
|
|
18692
18613
|
return markerStat.mtimeMs >= lockStat.mtimeMs;
|
|
18693
18614
|
} catch {
|
|
18694
18615
|
return false;
|
|
@@ -18704,8 +18625,8 @@ function shouldSkipInstall(opts, cwd) {
|
|
|
18704
18625
|
return { skip: false };
|
|
18705
18626
|
}
|
|
18706
18627
|
function validateRepoRoot(cwd) {
|
|
18707
|
-
const marker =
|
|
18708
|
-
if (!
|
|
18628
|
+
const marker = path31.join(cwd, "packages/host-cp/compose.yaml");
|
|
18629
|
+
if (!fs27.existsSync(marker)) {
|
|
18709
18630
|
return {
|
|
18710
18631
|
ok: false,
|
|
18711
18632
|
error: `Not an olam repo root (expected ${marker}).
|
|
@@ -18737,8 +18658,8 @@ function extractBundleHash(indexHtml) {
|
|
|
18737
18658
|
}
|
|
18738
18659
|
function runStep2(label, cmd, args, opts = {}) {
|
|
18739
18660
|
const start = Date.now();
|
|
18740
|
-
process.stdout.write(` ${
|
|
18741
|
-
const result =
|
|
18661
|
+
process.stdout.write(` ${pc17.dim(label.padEnd(34))}`);
|
|
18662
|
+
const result = spawnSync11(cmd, [...args], {
|
|
18742
18663
|
encoding: "utf-8",
|
|
18743
18664
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18744
18665
|
cwd: opts.cwd ?? process.cwd(),
|
|
@@ -18747,7 +18668,7 @@ function runStep2(label, cmd, args, opts = {}) {
|
|
|
18747
18668
|
const durationMs = Date.now() - start;
|
|
18748
18669
|
const ok = result.status === 0 && result.error === void 0;
|
|
18749
18670
|
const dur = `${(durationMs / 1e3).toFixed(1)}s`;
|
|
18750
|
-
process.stdout.write(`${ok ?
|
|
18671
|
+
process.stdout.write(`${ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${dur}
|
|
18751
18672
|
`);
|
|
18752
18673
|
return {
|
|
18753
18674
|
ok,
|
|
@@ -18757,7 +18678,7 @@ function runStep2(label, cmd, args, opts = {}) {
|
|
|
18757
18678
|
};
|
|
18758
18679
|
}
|
|
18759
18680
|
function isGitDirty(cwd) {
|
|
18760
|
-
const result =
|
|
18681
|
+
const result = spawnSync11("git", ["status", "--porcelain"], {
|
|
18761
18682
|
encoding: "utf-8",
|
|
18762
18683
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18763
18684
|
cwd
|
|
@@ -18765,7 +18686,7 @@ function isGitDirty(cwd) {
|
|
|
18765
18686
|
return (result.stdout ?? "").trim().length > 0;
|
|
18766
18687
|
}
|
|
18767
18688
|
function hasGitUpstream(cwd) {
|
|
18768
|
-
const result =
|
|
18689
|
+
const result = spawnSync11("git", ["rev-parse", "--abbrev-ref", "@{u}"], {
|
|
18769
18690
|
encoding: "utf-8",
|
|
18770
18691
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18771
18692
|
cwd
|
|
@@ -18773,7 +18694,7 @@ function hasGitUpstream(cwd) {
|
|
|
18773
18694
|
return result.status === 0;
|
|
18774
18695
|
}
|
|
18775
18696
|
function captureHeadSha(cwd) {
|
|
18776
|
-
const result =
|
|
18697
|
+
const result = spawnSync11("git", ["rev-parse", "HEAD"], {
|
|
18777
18698
|
encoding: "utf-8",
|
|
18778
18699
|
stdio: ["ignore", "pipe", "pipe"],
|
|
18779
18700
|
cwd
|
|
@@ -18788,7 +18709,7 @@ function abbreviateSha(sha) {
|
|
|
18788
18709
|
}
|
|
18789
18710
|
function imageExists(tag) {
|
|
18790
18711
|
try {
|
|
18791
|
-
const result =
|
|
18712
|
+
const result = spawnSync11("docker", ["image", "inspect", "--format", "{{.Id}}", tag], {
|
|
18792
18713
|
encoding: "utf-8",
|
|
18793
18714
|
stdio: ["ignore", "pipe", "ignore"]
|
|
18794
18715
|
});
|
|
@@ -18802,10 +18723,12 @@ function checkRollbackSetExists(plan) {
|
|
|
18802
18723
|
if (missing.length === 0) return null;
|
|
18803
18724
|
return missing.join(", ");
|
|
18804
18725
|
}
|
|
18726
|
+
var SMOKE_DOCKER_TIMEOUT_MS = 3e4;
|
|
18805
18727
|
function smokeImage(image, targetSha) {
|
|
18806
|
-
const createResult =
|
|
18728
|
+
const createResult = spawnSync11("docker", ["create", "--name", `olam-smoke-${Date.now()}`, image], {
|
|
18807
18729
|
encoding: "utf-8",
|
|
18808
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
18730
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18731
|
+
timeout: SMOKE_DOCKER_TIMEOUT_MS
|
|
18809
18732
|
});
|
|
18810
18733
|
if (createResult.status !== 0) {
|
|
18811
18734
|
return {
|
|
@@ -18816,18 +18739,20 @@ function smokeImage(image, targetSha) {
|
|
|
18816
18739
|
};
|
|
18817
18740
|
}
|
|
18818
18741
|
const containerId = (createResult.stdout ?? "").trim();
|
|
18819
|
-
const inspectResult =
|
|
18742
|
+
const inspectResult = spawnSync11(
|
|
18820
18743
|
"docker",
|
|
18821
18744
|
["inspect", "--format", '{{index .Config.Labels "olam.build.sha"}}', image],
|
|
18822
18745
|
{
|
|
18823
18746
|
encoding: "utf-8",
|
|
18824
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
18747
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
18748
|
+
timeout: SMOKE_DOCKER_TIMEOUT_MS
|
|
18825
18749
|
}
|
|
18826
18750
|
);
|
|
18827
18751
|
if (containerId.length > 0) {
|
|
18828
|
-
|
|
18752
|
+
spawnSync11("docker", ["rm", "-f", containerId], {
|
|
18829
18753
|
encoding: "utf-8",
|
|
18830
|
-
stdio: ["ignore", "ignore", "ignore"]
|
|
18754
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
18755
|
+
timeout: SMOKE_DOCKER_TIMEOUT_MS
|
|
18831
18756
|
});
|
|
18832
18757
|
}
|
|
18833
18758
|
if (inspectResult.status !== 0) {
|
|
@@ -18864,7 +18789,7 @@ var PRODUCTION_SWAP_PLAN = [
|
|
|
18864
18789
|
];
|
|
18865
18790
|
function dockerTag(source, dest) {
|
|
18866
18791
|
try {
|
|
18867
|
-
const result =
|
|
18792
|
+
const result = spawnSync11("docker", ["tag", source, dest], {
|
|
18868
18793
|
encoding: "utf-8",
|
|
18869
18794
|
stdio: ["ignore", "ignore", "pipe"]
|
|
18870
18795
|
});
|
|
@@ -19028,11 +18953,11 @@ async function waitForAuthHealthLocal(timeoutMs = AUTH_HEALTH_TIMEOUT_MS) {
|
|
|
19028
18953
|
async function recreateAuthService() {
|
|
19029
18954
|
const start = Date.now();
|
|
19030
18955
|
try {
|
|
19031
|
-
|
|
18956
|
+
spawnSync11("docker", ["stop", "olam-auth"], {
|
|
19032
18957
|
encoding: "utf-8",
|
|
19033
18958
|
stdio: ["ignore", "ignore", "ignore"]
|
|
19034
18959
|
});
|
|
19035
|
-
|
|
18960
|
+
spawnSync11("docker", ["rm", "olam-auth"], {
|
|
19036
18961
|
encoding: "utf-8",
|
|
19037
18962
|
stdio: ["ignore", "ignore", "ignore"]
|
|
19038
18963
|
});
|
|
@@ -19057,9 +18982,9 @@ async function recreateAuthService() {
|
|
|
19057
18982
|
}
|
|
19058
18983
|
}
|
|
19059
18984
|
function readBundleHash(cwd) {
|
|
19060
|
-
const indexPath =
|
|
19061
|
-
if (!
|
|
19062
|
-
return extractBundleHash(
|
|
18985
|
+
const indexPath = path31.join(cwd, "packages/control-plane/public/index.html");
|
|
18986
|
+
if (!fs27.existsSync(indexPath)) return null;
|
|
18987
|
+
return extractBundleHash(fs27.readFileSync(indexPath, "utf-8"));
|
|
19063
18988
|
}
|
|
19064
18989
|
async function runUpgradePullByDigest(deps = {}) {
|
|
19065
18990
|
const docker2 = deps.docker ?? realDocker;
|
|
@@ -19071,19 +18996,27 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19071
18996
|
if (info.exitCode !== 0) {
|
|
19072
18997
|
infoSpinner.fail("docker daemon not reachable");
|
|
19073
18998
|
process.stderr.write(
|
|
19074
|
-
`${
|
|
18999
|
+
`${pc17.red("error")} docker info exited with ${info.exitCode}.
|
|
19075
19000
|
Ensure Docker Desktop / Colima / Rancher is running, then retry.
|
|
19076
19001
|
`
|
|
19077
19002
|
);
|
|
19078
19003
|
return { exitCode: EXIT_GENERIC_ERROR, summary: "docker daemon not reachable" };
|
|
19079
19004
|
}
|
|
19080
19005
|
infoSpinner.succeed("docker daemon reachable");
|
|
19081
|
-
const
|
|
19006
|
+
const hasMcpAuthDigest = typeof digests["mcp-auth"] === "string" && digests["mcp-auth"].length > 0;
|
|
19007
|
+
const baseRefs = [
|
|
19082
19008
|
{ name: "host-cp", ref: `${registry}/olam-host-cp@${digests["host-cp"]}` },
|
|
19083
19009
|
{ name: "auth", ref: `${registry}/olam-auth@${digests.auth}` },
|
|
19084
19010
|
{ name: "devbox", ref: `${registry}/olam-devbox@${digests.devbox}` }
|
|
19085
19011
|
];
|
|
19086
|
-
|
|
19012
|
+
if (hasMcpAuthDigest) {
|
|
19013
|
+
baseRefs.push({
|
|
19014
|
+
name: "mcp-auth",
|
|
19015
|
+
ref: `${registry}/olam-mcp-auth@${digests["mcp-auth"]}`
|
|
19016
|
+
});
|
|
19017
|
+
}
|
|
19018
|
+
const imageRefs = baseRefs;
|
|
19019
|
+
const pullSpinner = ora7(`Pulling images (${imageRefs.length} parallel)`).start();
|
|
19087
19020
|
const pullStart = Date.now();
|
|
19088
19021
|
const pullResults = await Promise.all(
|
|
19089
19022
|
imageRefs.map(async ({ name, ref }) => ({
|
|
@@ -19098,7 +19031,7 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19098
19031
|
pullSpinner.fail(`Pull failed for ${failed.map((r) => r.name).join(", ")}`);
|
|
19099
19032
|
for (const f of failed) {
|
|
19100
19033
|
process.stderr.write(
|
|
19101
|
-
` ${
|
|
19034
|
+
` ${pc17.red(f.name)} (${f.ref}):
|
|
19102
19035
|
exit=${f.result.exitCode}
|
|
19103
19036
|
stderr: ${f.result.stderr.split("\n")[0] ?? "(empty)"}
|
|
19104
19037
|
`
|
|
@@ -19112,14 +19045,14 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19112
19045
|
summary: `pull failed: ${failed.map((r) => r.name).join(", ")}`
|
|
19113
19046
|
};
|
|
19114
19047
|
}
|
|
19115
|
-
pullSpinner.succeed(`Pulled
|
|
19048
|
+
pullSpinner.succeed(`Pulled ${imageRefs.length} images in ${pullElapsed}s`);
|
|
19116
19049
|
const handshakeSpinner = ora7("Verifying olam.protocol.versions handshake").start();
|
|
19117
19050
|
for (const { name, ref } of imageRefs) {
|
|
19118
19051
|
const inspect = await docker2.inspectLabel(ref, "olam.protocol.versions");
|
|
19119
19052
|
if (inspect.exitCode !== 0) {
|
|
19120
19053
|
handshakeSpinner.fail(`Could not inspect ${name}`);
|
|
19121
19054
|
process.stderr.write(
|
|
19122
|
-
`${
|
|
19055
|
+
`${pc17.red("error")} docker inspect ${ref} failed: ${inspect.stderr}
|
|
19123
19056
|
`
|
|
19124
19057
|
);
|
|
19125
19058
|
return { exitCode: EXIT_GENERIC_ERROR, summary: `inspect failed: ${name}` };
|
|
@@ -19131,7 +19064,7 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19131
19064
|
const decision = checkProtocolOverlap(versions);
|
|
19132
19065
|
if (!decision.compatible) {
|
|
19133
19066
|
handshakeSpinner.fail(`Protocol mismatch on ${name}`);
|
|
19134
|
-
process.stderr.write(`${
|
|
19067
|
+
process.stderr.write(`${pc17.red("error")} ${decision.remedy}
|
|
19135
19068
|
`);
|
|
19136
19069
|
return {
|
|
19137
19070
|
exitCode: EXIT_PROTOCOL_MISMATCH,
|
|
@@ -19139,8 +19072,8 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19139
19072
|
};
|
|
19140
19073
|
}
|
|
19141
19074
|
}
|
|
19142
|
-
handshakeSpinner.succeed(
|
|
19143
|
-
const
|
|
19075
|
+
handshakeSpinner.succeed(`Protocol handshake passed (all ${imageRefs.length} images)`);
|
|
19076
|
+
const tagPlanBase = [
|
|
19144
19077
|
{ from: imageRefs[0].ref, to: "olam-host-cp:latest", name: "host-cp (bare)" },
|
|
19145
19078
|
{ from: imageRefs[0].ref, to: `${registry}/olam-host-cp:latest`, name: "host-cp (registry)" },
|
|
19146
19079
|
{ from: imageRefs[1].ref, to: "olam-auth:local", name: "auth (bare)" },
|
|
@@ -19148,13 +19081,20 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19148
19081
|
{ from: imageRefs[2].ref, to: "olam-devbox:latest", name: "devbox (bare)" },
|
|
19149
19082
|
{ from: imageRefs[2].ref, to: `${registry}/olam-devbox:latest`, name: "devbox (registry)" }
|
|
19150
19083
|
];
|
|
19084
|
+
if (hasMcpAuthDigest && imageRefs[3]) {
|
|
19085
|
+
tagPlanBase.push(
|
|
19086
|
+
{ from: imageRefs[3].ref, to: "olam-mcp-auth:local", name: "mcp-auth (bare)" },
|
|
19087
|
+
{ from: imageRefs[3].ref, to: `${registry}/olam-mcp-auth:latest`, name: "mcp-auth (registry)" }
|
|
19088
|
+
);
|
|
19089
|
+
}
|
|
19090
|
+
const tagPlan = tagPlanBase;
|
|
19151
19091
|
const tagSpinner = ora7("Tagging digests \u2192 canonical local refs").start();
|
|
19152
19092
|
const tagger = deps.tagImpl ?? dockerTag;
|
|
19153
19093
|
for (const t of tagPlan) {
|
|
19154
19094
|
const r = tagger(t.from, t.to);
|
|
19155
19095
|
if (!r.ok) {
|
|
19156
19096
|
tagSpinner.fail(`docker tag failed for ${t.name}`);
|
|
19157
|
-
process.stderr.write(`${
|
|
19097
|
+
process.stderr.write(`${pc17.red("error")} ${r.error ?? "docker tag failed"}
|
|
19158
19098
|
`);
|
|
19159
19099
|
return { exitCode: EXIT_GENERIC_ERROR, summary: `tag failed: ${t.name}` };
|
|
19160
19100
|
}
|
|
@@ -19172,7 +19112,7 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19172
19112
|
if (!composeResult.ok) {
|
|
19173
19113
|
composeSpinner.fail("compose recreate failed");
|
|
19174
19114
|
process.stderr.write(
|
|
19175
|
-
`${
|
|
19115
|
+
`${pc17.red("error")} docker compose up --force-recreate host-cp failed:
|
|
19176
19116
|
${composeResult.stderr.split("\n").slice(0, 3).join("\n ")}
|
|
19177
19117
|
`
|
|
19178
19118
|
);
|
|
@@ -19185,25 +19125,68 @@ async function runUpgradePullByDigest(deps = {}) {
|
|
|
19185
19125
|
if (!authResult.ok) {
|
|
19186
19126
|
authSpinner.fail("auth-service recreate failed");
|
|
19187
19127
|
process.stderr.write(
|
|
19188
|
-
`${
|
|
19128
|
+
`${pc17.red("error")} ${authResult.error ?? "unknown failure"}
|
|
19189
19129
|
`
|
|
19190
19130
|
);
|
|
19191
19131
|
return { exitCode: EXIT_GENERIC_ERROR, summary: "auth recreate failed" };
|
|
19192
19132
|
}
|
|
19193
19133
|
authSpinner.succeed("auth-service running on new image");
|
|
19134
|
+
if (hasMcpAuthDigest) {
|
|
19135
|
+
const mcpSpinner = ora7("recreate mcp-auth-service").start();
|
|
19136
|
+
const mcpRecreate = deps.recreateMcpAuth ?? defaultRecreateMcpAuthForUpgrade;
|
|
19137
|
+
const mcpResult = await mcpRecreate();
|
|
19138
|
+
if (!mcpResult.ok) {
|
|
19139
|
+
mcpSpinner.fail("mcp-auth-service recreate failed");
|
|
19140
|
+
process.stderr.write(
|
|
19141
|
+
`${pc17.red("error")} ${mcpResult.error ?? "unknown failure"}
|
|
19142
|
+
`
|
|
19143
|
+
);
|
|
19144
|
+
return { exitCode: EXIT_GENERIC_ERROR, summary: "mcp-auth recreate failed" };
|
|
19145
|
+
}
|
|
19146
|
+
mcpSpinner.succeed("mcp-auth-service running on new image");
|
|
19147
|
+
}
|
|
19194
19148
|
printSuccess("Upgrade complete (pull-by-digest)");
|
|
19195
19149
|
printInfo("host-cp", `running (${digests["host-cp"].slice(0, 19)}\u2026)`);
|
|
19196
19150
|
printInfo("auth", `running (${digests.auth.slice(0, 19)}\u2026)`);
|
|
19197
19151
|
printInfo("devbox", `pulled (${digests.devbox.slice(0, 19)}\u2026)`);
|
|
19152
|
+
if (hasMcpAuthDigest) {
|
|
19153
|
+
printInfo("mcp-auth", `running (${digests["mcp-auth"].slice(0, 19)}\u2026)`);
|
|
19154
|
+
}
|
|
19198
19155
|
return { exitCode: 0, summary: "stack upgraded" };
|
|
19199
19156
|
}
|
|
19157
|
+
async function defaultRecreateMcpAuthForUpgrade() {
|
|
19158
|
+
try {
|
|
19159
|
+
const { spawnSync: ss } = await import("node:child_process");
|
|
19160
|
+
ss("docker", ["stop", "olam-mcp-auth"], { stdio: "ignore" });
|
|
19161
|
+
ss("docker", ["rm", "-f", "olam-mcp-auth"], { stdio: "ignore" });
|
|
19162
|
+
const startResult = ss("docker", [
|
|
19163
|
+
"run",
|
|
19164
|
+
"-d",
|
|
19165
|
+
"--name",
|
|
19166
|
+
"olam-mcp-auth",
|
|
19167
|
+
"-p",
|
|
19168
|
+
"9998:9998",
|
|
19169
|
+
"-v",
|
|
19170
|
+
"olam-mcp-auth-data:/mcp-auth-data",
|
|
19171
|
+
"--restart",
|
|
19172
|
+
"unless-stopped",
|
|
19173
|
+
"olam-mcp-auth:local"
|
|
19174
|
+
], { stdio: "pipe" });
|
|
19175
|
+
if (startResult.status !== 0) {
|
|
19176
|
+
return { ok: false, error: `docker run failed: ${startResult.stderr?.toString() ?? "unknown"}` };
|
|
19177
|
+
}
|
|
19178
|
+
return { ok: true };
|
|
19179
|
+
} catch (err) {
|
|
19180
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
19181
|
+
}
|
|
19182
|
+
}
|
|
19200
19183
|
async function defaultRecreateAuthForUpgrade() {
|
|
19201
19184
|
try {
|
|
19202
|
-
|
|
19185
|
+
spawnSync11("docker", ["stop", "olam-auth"], {
|
|
19203
19186
|
encoding: "utf-8",
|
|
19204
19187
|
stdio: ["ignore", "ignore", "ignore"]
|
|
19205
19188
|
});
|
|
19206
|
-
|
|
19189
|
+
spawnSync11("docker", ["rm", "olam-auth"], {
|
|
19207
19190
|
encoding: "utf-8",
|
|
19208
19191
|
stdio: ["ignore", "ignore", "ignore"]
|
|
19209
19192
|
});
|
|
@@ -19348,11 +19331,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
|
|
|
19348
19331
|
process.once("SIGINT", releaseOnSignal);
|
|
19349
19332
|
process.once("SIGTERM", releaseOnSignal);
|
|
19350
19333
|
try {
|
|
19351
|
-
process.stdout.write(` ${
|
|
19334
|
+
process.stdout.write(` ${pc17.dim("rollback retag (3 ops)".padEnd(34))}`);
|
|
19352
19335
|
const swapStart = Date.now();
|
|
19353
19336
|
const swapResult = performRollbackSwap(PRODUCTION_SWAP_PLAN);
|
|
19354
19337
|
const swapDur = `${((Date.now() - swapStart) / 1e3).toFixed(1)}s`;
|
|
19355
|
-
process.stdout.write(`${swapResult.ok ?
|
|
19338
|
+
process.stdout.write(`${swapResult.ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${swapDur}
|
|
19356
19339
|
`);
|
|
19357
19340
|
if (!swapResult.ok) {
|
|
19358
19341
|
printError(`Rollback retag failed: ${swapResult.summary}`);
|
|
@@ -19362,11 +19345,11 @@ manually inspect images with \`docker images olam-*:olam-rollback\`.`
|
|
|
19362
19345
|
printInfo("Rollback", swapResult.summary);
|
|
19363
19346
|
const composeFile = findComposeFile();
|
|
19364
19347
|
const authSecret = readAuthSecret2();
|
|
19365
|
-
process.stdout.write(` ${
|
|
19348
|
+
process.stdout.write(` ${pc17.dim("docker compose recreate host-cp".padEnd(34))}`);
|
|
19366
19349
|
const composeStart = Date.now();
|
|
19367
19350
|
const composeResult = runCompose(["up", "-d", "--force-recreate", "--no-deps", "host-cp"], composeFile, buildComposeEnv(authSecret));
|
|
19368
19351
|
const composeDur = `${((Date.now() - composeStart) / 1e3).toFixed(1)}s`;
|
|
19369
|
-
process.stdout.write(`${composeResult.ok ?
|
|
19352
|
+
process.stdout.write(`${composeResult.ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${composeDur}
|
|
19370
19353
|
`);
|
|
19371
19354
|
if (!composeResult.ok) {
|
|
19372
19355
|
printError(
|
|
@@ -19377,10 +19360,10 @@ Canonical tags are at :olam-rollback (good); container restart pending. Manually
|
|
|
19377
19360
|
process.exitCode = 1;
|
|
19378
19361
|
return;
|
|
19379
19362
|
}
|
|
19380
|
-
process.stdout.write(` ${
|
|
19363
|
+
process.stdout.write(` ${pc17.dim("recreate auth-service".padEnd(34))}`);
|
|
19381
19364
|
const authResult = await recreateAuthService();
|
|
19382
19365
|
const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
|
|
19383
|
-
process.stdout.write(`${authResult.ok ?
|
|
19366
|
+
process.stdout.write(`${authResult.ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${authDur}
|
|
19384
19367
|
`);
|
|
19385
19368
|
if (!authResult.ok) {
|
|
19386
19369
|
printError(`Auth-service recreate failed: ${authResult.error ?? "unknown"}`);
|
|
@@ -19501,7 +19484,7 @@ ${buildResult.stderr}`);
|
|
|
19501
19484
|
return;
|
|
19502
19485
|
}
|
|
19503
19486
|
const authSecret = readAuthSecret2();
|
|
19504
|
-
const spaDir =
|
|
19487
|
+
const spaDir = path31.join(cwd, "packages/control-plane/app");
|
|
19505
19488
|
const spaResult = runStep2(
|
|
19506
19489
|
"vite build (SPA)",
|
|
19507
19490
|
"npx",
|
|
@@ -19546,10 +19529,10 @@ ${spaResult.stderr}`);
|
|
|
19546
19529
|
throw err;
|
|
19547
19530
|
}
|
|
19548
19531
|
if (step.tee) {
|
|
19549
|
-
process.stdout.write(` ${
|
|
19532
|
+
process.stdout.write(` ${pc17.dim(step.label.padEnd(34))}
|
|
19550
19533
|
`);
|
|
19551
19534
|
const start = Date.now();
|
|
19552
|
-
const result =
|
|
19535
|
+
const result = spawnSync11("bash", [scriptPath], {
|
|
19553
19536
|
stdio: "inherit",
|
|
19554
19537
|
cwd,
|
|
19555
19538
|
env: { ...process.env, ...olamTagEnv }
|
|
@@ -19557,7 +19540,7 @@ ${spaResult.stderr}`);
|
|
|
19557
19540
|
const durationMs = Date.now() - start;
|
|
19558
19541
|
const ok = result.status === 0 && result.error === void 0;
|
|
19559
19542
|
const dur = `${(durationMs / 1e3).toFixed(1)}s`;
|
|
19560
|
-
process.stdout.write(` ${
|
|
19543
|
+
process.stdout.write(` ${pc17.dim(step.label.padEnd(34))}${ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${dur}
|
|
19561
19544
|
`);
|
|
19562
19545
|
timings.push({ label: step.label, durationMs });
|
|
19563
19546
|
if (!ok) {
|
|
@@ -19583,7 +19566,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
|
|
|
19583
19566
|
}
|
|
19584
19567
|
for (const t of timings) logRow.durations_ms[t.label] = t.durationMs;
|
|
19585
19568
|
const smokeStart = Date.now();
|
|
19586
|
-
process.stdout.write(` ${
|
|
19569
|
+
process.stdout.write(` ${pc17.dim("smoke (docker create + inspect)".padEnd(34))}`);
|
|
19587
19570
|
const smokeImages = [
|
|
19588
19571
|
"olam-auth:olam-next",
|
|
19589
19572
|
"olam-devbox:olam-next",
|
|
@@ -19593,7 +19576,7 @@ ${result.stderr.split("\n").slice(-3).join("\n")}`);
|
|
|
19593
19576
|
const smokeFailures = smokeResults.filter((r) => !r.ok);
|
|
19594
19577
|
const smokeDurationMs = Date.now() - smokeStart;
|
|
19595
19578
|
const smokeDur = `${(smokeDurationMs / 1e3).toFixed(1)}s`;
|
|
19596
|
-
process.stdout.write(`${smokeFailures.length === 0 ?
|
|
19579
|
+
process.stdout.write(`${smokeFailures.length === 0 ? pc17.green("\u2713") : pc17.red("\u2717")} ${smokeDur}
|
|
19597
19580
|
`);
|
|
19598
19581
|
timings.push({ label: "smoke", durationMs: smokeDurationMs });
|
|
19599
19582
|
if (smokeFailures.length > 0) {
|
|
@@ -19620,12 +19603,12 @@ Recovery options:
|
|
|
19620
19603
|
process.exitCode = 1;
|
|
19621
19604
|
return;
|
|
19622
19605
|
}
|
|
19623
|
-
process.stdout.write(` ${
|
|
19606
|
+
process.stdout.write(` ${pc17.dim("atomic 6-tag swap".padEnd(34))}`);
|
|
19624
19607
|
const swapStart = Date.now();
|
|
19625
19608
|
const swapResult = performAtomicSwap(PRODUCTION_SWAP_PLAN);
|
|
19626
19609
|
const swapDurationMs = Date.now() - swapStart;
|
|
19627
19610
|
const swapDur = `${(swapDurationMs / 1e3).toFixed(1)}s`;
|
|
19628
|
-
process.stdout.write(`${swapResult.ok ?
|
|
19611
|
+
process.stdout.write(`${swapResult.ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${swapDur}
|
|
19629
19612
|
`);
|
|
19630
19613
|
timings.push({ label: "atomic swap", durationMs: swapDurationMs });
|
|
19631
19614
|
if (!swapResult.ok) {
|
|
@@ -19635,7 +19618,7 @@ Recovery options:
|
|
|
19635
19618
|
}
|
|
19636
19619
|
printInfo("Swap", swapResult.summary);
|
|
19637
19620
|
const composeFile = findComposeFile();
|
|
19638
|
-
process.stdout.write(` ${
|
|
19621
|
+
process.stdout.write(` ${pc17.dim("docker compose recreate".padEnd(34))}`);
|
|
19639
19622
|
const composeStart = Date.now();
|
|
19640
19623
|
const composeResult = runCompose(
|
|
19641
19624
|
["up", "-d", "--force-recreate"],
|
|
@@ -19645,7 +19628,7 @@ Recovery options:
|
|
|
19645
19628
|
const composeDurationMs = Date.now() - composeStart;
|
|
19646
19629
|
const composeOk = composeResult.ok;
|
|
19647
19630
|
const composeDur = `${(composeDurationMs / 1e3).toFixed(1)}s`;
|
|
19648
|
-
process.stdout.write(`${composeOk ?
|
|
19631
|
+
process.stdout.write(`${composeOk ? pc17.green("\u2713") : pc17.red("\u2717")} ${composeDur}
|
|
19649
19632
|
`);
|
|
19650
19633
|
timings.push({ label: "container recreate", durationMs: composeDurationMs });
|
|
19651
19634
|
if (!composeOk) {
|
|
@@ -19661,10 +19644,10 @@ Recovery options:
|
|
|
19661
19644
|
process.exitCode = 1;
|
|
19662
19645
|
return;
|
|
19663
19646
|
}
|
|
19664
|
-
process.stdout.write(` ${
|
|
19647
|
+
process.stdout.write(` ${pc17.dim("recreate auth-service".padEnd(34))}`);
|
|
19665
19648
|
const authResult = await recreateAuthService();
|
|
19666
19649
|
const authDur = `${(authResult.durationMs / 1e3).toFixed(1)}s`;
|
|
19667
|
-
process.stdout.write(`${authResult.ok ?
|
|
19650
|
+
process.stdout.write(`${authResult.ok ? pc17.green("\u2713") : pc17.red("\u2717")} ${authDur}
|
|
19668
19651
|
`);
|
|
19669
19652
|
timings.push({ label: "auth recreate", durationMs: authResult.durationMs });
|
|
19670
19653
|
if (!authResult.ok) {
|
|
@@ -19679,12 +19662,12 @@ Recovery options:
|
|
|
19679
19662
|
process.exitCode = 1;
|
|
19680
19663
|
return;
|
|
19681
19664
|
}
|
|
19682
|
-
process.stdout.write(` ${
|
|
19665
|
+
process.stdout.write(` ${pc17.dim("waiting for /health".padEnd(34))}`);
|
|
19683
19666
|
const healthStart = Date.now();
|
|
19684
19667
|
const healthy = await waitForHealth(1e4);
|
|
19685
19668
|
const healthDurationMs = Date.now() - healthStart;
|
|
19686
19669
|
const healthDur = `${(healthDurationMs / 1e3).toFixed(1)}s`;
|
|
19687
|
-
process.stdout.write(`${healthy ?
|
|
19670
|
+
process.stdout.write(`${healthy ? pc17.green("\u2713") : pc17.yellow("?")} ${healthDur}
|
|
19688
19671
|
`);
|
|
19689
19672
|
timings.push({ label: "/health", durationMs: healthDurationMs });
|
|
19690
19673
|
if (!healthy) {
|
|
@@ -19692,12 +19675,12 @@ Recovery options:
|
|
|
19692
19675
|
"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."
|
|
19693
19676
|
);
|
|
19694
19677
|
}
|
|
19695
|
-
process.stdout.write(` ${
|
|
19678
|
+
process.stdout.write(` ${pc17.dim("verify /version/status round-trip".padEnd(34))}`);
|
|
19696
19679
|
const versionStart = Date.now();
|
|
19697
19680
|
const versionMatch = await waitForVersionMatch(_targetSha, 9e4);
|
|
19698
19681
|
const versionDurationMs = Date.now() - versionStart;
|
|
19699
19682
|
const versionDur = `${(versionDurationMs / 1e3).toFixed(1)}s`;
|
|
19700
|
-
process.stdout.write(`${versionMatch.matched ?
|
|
19683
|
+
process.stdout.write(`${versionMatch.matched ? pc17.green("\u2713") : pc17.yellow("?")} ${versionDur}
|
|
19701
19684
|
`);
|
|
19702
19685
|
timings.push({ label: "/version/status round-trip", durationMs: versionDurationMs });
|
|
19703
19686
|
if (!versionMatch.matched) {
|
|
@@ -19771,16 +19754,16 @@ init_host_cp();
|
|
|
19771
19754
|
init_context();
|
|
19772
19755
|
init_output();
|
|
19773
19756
|
import * as http3 from "node:http";
|
|
19774
|
-
import
|
|
19757
|
+
import pc18 from "picocolors";
|
|
19775
19758
|
var HOST_CP_PORT3 = 19e3;
|
|
19776
19759
|
function colorLine(line) {
|
|
19777
|
-
if (/\bERROR\b/.test(line)) return
|
|
19778
|
-
if (/\bWARN\b/.test(line)) return
|
|
19779
|
-
if (/\bINFO\b/.test(line)) return
|
|
19760
|
+
if (/\bERROR\b/.test(line)) return pc18.red(line);
|
|
19761
|
+
if (/\bWARN\b/.test(line)) return pc18.yellow(line);
|
|
19762
|
+
if (/\bINFO\b/.test(line)) return pc18.dim(line);
|
|
19780
19763
|
return line;
|
|
19781
19764
|
}
|
|
19782
19765
|
function formatLine(line, service, showService) {
|
|
19783
|
-
const prefix = showService && service ? `${
|
|
19766
|
+
const prefix = showService && service ? `${pc18.cyan(`[${service}]`)} ` : "";
|
|
19784
19767
|
return prefix + colorLine(line);
|
|
19785
19768
|
}
|
|
19786
19769
|
function parseSseEvent(raw) {
|
|
@@ -19897,8 +19880,8 @@ function registerLogs(program2) {
|
|
|
19897
19880
|
// src/commands/ps.ts
|
|
19898
19881
|
init_context();
|
|
19899
19882
|
init_output();
|
|
19900
|
-
import
|
|
19901
|
-
import { spawnSync as
|
|
19883
|
+
import pc19 from "picocolors";
|
|
19884
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
19902
19885
|
var SAFE_IDENT4 = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
19903
19886
|
function parseDockerTop(stdout) {
|
|
19904
19887
|
const trimmed = stdout.trim();
|
|
@@ -19958,18 +19941,18 @@ function printTable2(rows) {
|
|
|
19958
19941
|
const fixedWidth = 5 + 1 + 8 + 1 + 6 + 1 + 6 + 1 + 10 + 1 + 6 + 1;
|
|
19959
19942
|
const cmdWidth = Math.max(20, cols - fixedWidth);
|
|
19960
19943
|
const header = [
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
19966
|
-
|
|
19967
|
-
|
|
19944
|
+
pc19.bold(pc19.dim("PID".padEnd(5))),
|
|
19945
|
+
pc19.bold(pc19.dim("USER".padEnd(8))),
|
|
19946
|
+
pc19.bold(pc19.dim("%CPU".padEnd(6))),
|
|
19947
|
+
pc19.bold(pc19.dim("%MEM".padEnd(6))),
|
|
19948
|
+
pc19.bold(pc19.dim("STARTED".padEnd(10))),
|
|
19949
|
+
pc19.bold(pc19.dim("STATE".padEnd(6))),
|
|
19950
|
+
pc19.bold(pc19.dim("COMMAND"))
|
|
19968
19951
|
].join(" ");
|
|
19969
19952
|
console.log(header);
|
|
19970
19953
|
for (const row of rows) {
|
|
19971
19954
|
const cmd = row.command.length > cmdWidth ? row.command.slice(0, cmdWidth - 1) + "\u2026" : row.command;
|
|
19972
|
-
const stateFn = row.state.startsWith("R") ?
|
|
19955
|
+
const stateFn = row.state.startsWith("R") ? pc19.green : row.state.startsWith("Z") ? pc19.red : pc19.dim;
|
|
19973
19956
|
console.log([
|
|
19974
19957
|
row.pid.padEnd(5),
|
|
19975
19958
|
row.user.slice(0, 8).padEnd(8),
|
|
@@ -19998,7 +19981,7 @@ function registerPs(program2) {
|
|
|
19998
19981
|
const containerName = `olam-${worldId}-devbox`;
|
|
19999
19982
|
let watchInterval;
|
|
20000
19983
|
function fetchAndPrint() {
|
|
20001
|
-
const result =
|
|
19984
|
+
const result = spawnSync12(
|
|
20002
19985
|
"docker",
|
|
20003
19986
|
["top", containerName, "pid", "user", "pcpu", "pmem", "stime", "stat", "cmd"],
|
|
20004
19987
|
{ encoding: "utf-8", timeout: 3e3 }
|
|
@@ -20018,7 +20001,7 @@ function registerPs(program2) {
|
|
|
20018
20001
|
printTable2(rows);
|
|
20019
20002
|
if (opts.watch) {
|
|
20020
20003
|
process.stdout.write(`
|
|
20021
|
-
${
|
|
20004
|
+
${pc19.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
|
|
20022
20005
|
`);
|
|
20023
20006
|
}
|
|
20024
20007
|
}
|
|
@@ -20035,20 +20018,20 @@ ${pc18.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
|
|
|
20035
20018
|
|
|
20036
20019
|
// src/commands/keys.ts
|
|
20037
20020
|
init_output();
|
|
20038
|
-
import * as
|
|
20021
|
+
import * as fs28 from "node:fs";
|
|
20039
20022
|
import * as os17 from "node:os";
|
|
20040
|
-
import * as
|
|
20023
|
+
import * as path32 from "node:path";
|
|
20041
20024
|
import YAML4 from "yaml";
|
|
20042
20025
|
function olamHome2() {
|
|
20043
|
-
return process.env.OLAM_HOME ??
|
|
20026
|
+
return process.env.OLAM_HOME ?? path32.join(os17.homedir(), ".olam");
|
|
20044
20027
|
}
|
|
20045
20028
|
function keysFilePath() {
|
|
20046
|
-
return
|
|
20029
|
+
return path32.join(olamHome2(), "keys.yaml");
|
|
20047
20030
|
}
|
|
20048
20031
|
function readKeysFile() {
|
|
20049
20032
|
const filePath = keysFilePath();
|
|
20050
|
-
if (!
|
|
20051
|
-
const raw =
|
|
20033
|
+
if (!fs28.existsSync(filePath)) return null;
|
|
20034
|
+
const raw = fs28.readFileSync(filePath, "utf-8").trim();
|
|
20052
20035
|
if (raw.length === 0) return null;
|
|
20053
20036
|
try {
|
|
20054
20037
|
const parsed = YAML4.parse(raw);
|
|
@@ -20064,13 +20047,13 @@ function readKeysFile() {
|
|
|
20064
20047
|
}
|
|
20065
20048
|
function writeKeysFile(keys) {
|
|
20066
20049
|
const dir = olamHome2();
|
|
20067
|
-
if (!
|
|
20068
|
-
|
|
20050
|
+
if (!fs28.existsSync(dir)) {
|
|
20051
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
20069
20052
|
}
|
|
20070
20053
|
const filePath = keysFilePath();
|
|
20071
20054
|
const content = YAML4.stringify(keys);
|
|
20072
|
-
|
|
20073
|
-
|
|
20055
|
+
fs28.writeFileSync(filePath, content, { encoding: "utf-8", mode: 384 });
|
|
20056
|
+
fs28.chmodSync(filePath, 384);
|
|
20074
20057
|
}
|
|
20075
20058
|
function redact(value) {
|
|
20076
20059
|
if (value.length <= 8) return value + "...";
|
|
@@ -20113,7 +20096,7 @@ function registerKeys(program2) {
|
|
|
20113
20096
|
}
|
|
20114
20097
|
const { [key]: _removed, ...rest } = existing;
|
|
20115
20098
|
if (Object.keys(rest).length === 0) {
|
|
20116
|
-
|
|
20099
|
+
fs28.unlinkSync(keysFilePath());
|
|
20117
20100
|
} else {
|
|
20118
20101
|
writeKeysFile(rest);
|
|
20119
20102
|
}
|
|
@@ -20136,33 +20119,33 @@ function registerKeys(program2) {
|
|
|
20136
20119
|
}
|
|
20137
20120
|
|
|
20138
20121
|
// src/commands/world-snapshot.ts
|
|
20139
|
-
import * as
|
|
20140
|
-
import * as
|
|
20122
|
+
import * as fs30 from "node:fs";
|
|
20123
|
+
import * as path34 from "node:path";
|
|
20141
20124
|
import { execSync as execSync9 } from "node:child_process";
|
|
20142
|
-
import
|
|
20125
|
+
import pc20 from "picocolors";
|
|
20143
20126
|
|
|
20144
20127
|
// ../core/dist/world/snapshot.js
|
|
20145
|
-
import * as
|
|
20146
|
-
import * as
|
|
20128
|
+
import * as crypto6 from "node:crypto";
|
|
20129
|
+
import * as fs29 from "node:fs";
|
|
20147
20130
|
import * as os18 from "node:os";
|
|
20148
|
-
import * as
|
|
20149
|
-
import { execFileSync as
|
|
20131
|
+
import * as path33 from "node:path";
|
|
20132
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
20150
20133
|
function snapshotsDir() {
|
|
20151
|
-
return process.env["OLAM_SNAPSHOTS_DIR"] ??
|
|
20134
|
+
return process.env["OLAM_SNAPSHOTS_DIR"] ?? path33.join(os18.homedir(), ".olam", "snapshots");
|
|
20152
20135
|
}
|
|
20153
20136
|
function snapshotKindDir(worldId, kind) {
|
|
20154
|
-
return
|
|
20137
|
+
return path33.join(snapshotsDir(), worldId, kind);
|
|
20155
20138
|
}
|
|
20156
20139
|
function snapshotTarPath(worldId, kind, repoName, hash) {
|
|
20157
20140
|
const base = repoName ? `${repoName}-${hash}` : hash;
|
|
20158
|
-
return
|
|
20141
|
+
return path33.join(snapshotKindDir(worldId, kind), `${base}.tar.gz`);
|
|
20159
20142
|
}
|
|
20160
20143
|
function manifestPath(tarPath) {
|
|
20161
20144
|
return tarPath.replace(/\.tar\.gz$/, ".manifest.json");
|
|
20162
20145
|
}
|
|
20163
20146
|
function hashBuffers(entries) {
|
|
20164
20147
|
const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path));
|
|
20165
|
-
const hash =
|
|
20148
|
+
const hash = crypto6.createHash("sha256");
|
|
20166
20149
|
for (const entry of sorted) {
|
|
20167
20150
|
hash.update(entry.path);
|
|
20168
20151
|
hash.update("\0");
|
|
@@ -20172,17 +20155,17 @@ function hashBuffers(entries) {
|
|
|
20172
20155
|
return hash.digest("hex").slice(0, 12);
|
|
20173
20156
|
}
|
|
20174
20157
|
function computeGemsFingerprint(repoDir) {
|
|
20175
|
-
const lockfile =
|
|
20176
|
-
if (!
|
|
20158
|
+
const lockfile = path33.join(repoDir, "Gemfile.lock");
|
|
20159
|
+
if (!fs29.existsSync(lockfile))
|
|
20177
20160
|
return null;
|
|
20178
|
-
return hashBuffers([{ path: "Gemfile.lock", content:
|
|
20161
|
+
return hashBuffers([{ path: "Gemfile.lock", content: fs29.readFileSync(lockfile) }]);
|
|
20179
20162
|
}
|
|
20180
20163
|
function computeNodeFingerprint(repoDir) {
|
|
20181
20164
|
const candidates = ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"];
|
|
20182
20165
|
for (const name of candidates) {
|
|
20183
|
-
const lockfile =
|
|
20184
|
-
if (
|
|
20185
|
-
return hashBuffers([{ path: name, content:
|
|
20166
|
+
const lockfile = path33.join(repoDir, name);
|
|
20167
|
+
if (fs29.existsSync(lockfile)) {
|
|
20168
|
+
return hashBuffers([{ path: name, content: fs29.readFileSync(lockfile) }]);
|
|
20186
20169
|
}
|
|
20187
20170
|
}
|
|
20188
20171
|
return null;
|
|
@@ -20192,64 +20175,64 @@ function computePgFingerprint(repoDirs) {
|
|
|
20192
20175
|
const entries = [];
|
|
20193
20176
|
for (const repoDir of repoDirs) {
|
|
20194
20177
|
for (const pattern of patterns) {
|
|
20195
|
-
const filePath =
|
|
20196
|
-
if (
|
|
20197
|
-
entries.push({ path: filePath, content:
|
|
20178
|
+
const filePath = path33.join(repoDir, pattern);
|
|
20179
|
+
if (fs29.existsSync(filePath)) {
|
|
20180
|
+
entries.push({ path: filePath, content: fs29.readFileSync(filePath) });
|
|
20198
20181
|
}
|
|
20199
20182
|
}
|
|
20200
20183
|
}
|
|
20201
20184
|
return entries.length > 0 ? hashBuffers(entries) : null;
|
|
20202
20185
|
}
|
|
20203
20186
|
function packTarball(srcDir, destPath, opts = {}) {
|
|
20204
|
-
|
|
20187
|
+
fs29.mkdirSync(path33.dirname(destPath), { recursive: true });
|
|
20205
20188
|
const tmp = `${destPath}.tmp`;
|
|
20206
20189
|
const args = [];
|
|
20207
20190
|
if (opts.followSymlinks)
|
|
20208
20191
|
args.push("-h");
|
|
20209
20192
|
args.push("-czf", tmp, "-C", srcDir, ".");
|
|
20210
20193
|
try {
|
|
20211
|
-
|
|
20212
|
-
|
|
20194
|
+
execFileSync6("tar", args, { stdio: "pipe" });
|
|
20195
|
+
fs29.renameSync(tmp, destPath);
|
|
20213
20196
|
} catch (err) {
|
|
20214
20197
|
try {
|
|
20215
|
-
|
|
20198
|
+
fs29.rmSync(tmp, { force: true });
|
|
20216
20199
|
} catch {
|
|
20217
20200
|
}
|
|
20218
20201
|
throw err;
|
|
20219
20202
|
}
|
|
20220
20203
|
}
|
|
20221
20204
|
function writeManifest(manifest, tarPath) {
|
|
20222
|
-
|
|
20205
|
+
fs29.writeFileSync(manifestPath(tarPath), JSON.stringify(manifest, null, 2), "utf-8");
|
|
20223
20206
|
}
|
|
20224
20207
|
function readManifest(tarPath) {
|
|
20225
20208
|
const mPath = manifestPath(tarPath);
|
|
20226
|
-
if (!
|
|
20209
|
+
if (!fs29.existsSync(mPath))
|
|
20227
20210
|
return null;
|
|
20228
20211
|
try {
|
|
20229
|
-
return JSON.parse(
|
|
20212
|
+
return JSON.parse(fs29.readFileSync(mPath, "utf-8"));
|
|
20230
20213
|
} catch {
|
|
20231
20214
|
return null;
|
|
20232
20215
|
}
|
|
20233
20216
|
}
|
|
20234
20217
|
function listSnapshots(worldIdFilter) {
|
|
20235
20218
|
const root = snapshotsDir();
|
|
20236
|
-
if (!
|
|
20219
|
+
if (!fs29.existsSync(root))
|
|
20237
20220
|
return [];
|
|
20238
20221
|
const now = Date.now();
|
|
20239
20222
|
const results = [];
|
|
20240
|
-
const worlds = worldIdFilter ? [worldIdFilter] :
|
|
20223
|
+
const worlds = worldIdFilter ? [worldIdFilter] : fs29.readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
20241
20224
|
for (const worldId of worlds) {
|
|
20242
|
-
const worldDir =
|
|
20243
|
-
if (!
|
|
20225
|
+
const worldDir = path33.join(root, worldId);
|
|
20226
|
+
if (!fs29.existsSync(worldDir) || !fs29.statSync(worldDir).isDirectory())
|
|
20244
20227
|
continue;
|
|
20245
20228
|
for (const kind of ["gems", "node", "pg"]) {
|
|
20246
|
-
const kindDir =
|
|
20247
|
-
if (!
|
|
20229
|
+
const kindDir = path33.join(worldDir, kind);
|
|
20230
|
+
if (!fs29.existsSync(kindDir))
|
|
20248
20231
|
continue;
|
|
20249
|
-
const tarballs =
|
|
20232
|
+
const tarballs = fs29.readdirSync(kindDir).filter((f) => f.endsWith(".tar.gz"));
|
|
20250
20233
|
for (const tarFile of tarballs) {
|
|
20251
|
-
const tarPath =
|
|
20252
|
-
const stat =
|
|
20234
|
+
const tarPath = path33.join(kindDir, tarFile);
|
|
20235
|
+
const stat = fs29.statSync(tarPath);
|
|
20253
20236
|
const manifest = readManifest(tarPath);
|
|
20254
20237
|
if (!manifest)
|
|
20255
20238
|
continue;
|
|
@@ -20325,7 +20308,7 @@ async function handleCreate2(worldId, kindArg) {
|
|
|
20325
20308
|
const label = r.repo ? `${r.kind}/${r.repo}` : r.kind;
|
|
20326
20309
|
if (r.ok) {
|
|
20327
20310
|
printSuccess(`${label}`);
|
|
20328
|
-
console.log(` ${
|
|
20311
|
+
console.log(` ${pc20.dim(r.tarPath)}`);
|
|
20329
20312
|
} else {
|
|
20330
20313
|
printWarning(`${label}: ${r.msg ?? "skipped"}`);
|
|
20331
20314
|
}
|
|
@@ -20339,17 +20322,17 @@ function resolveKinds(arg) {
|
|
|
20339
20322
|
return [];
|
|
20340
20323
|
}
|
|
20341
20324
|
async function captureGems(worldId, workspacePath, repo) {
|
|
20342
|
-
const repoDir =
|
|
20325
|
+
const repoDir = path34.join(workspacePath, repo);
|
|
20343
20326
|
const fingerprint = computeGemsFingerprint(repoDir);
|
|
20344
20327
|
if (!fingerprint) {
|
|
20345
20328
|
return { ok: false, tarPath: "", msg: "no Gemfile.lock \u2014 layer does not apply" };
|
|
20346
20329
|
}
|
|
20347
20330
|
const tarPath = snapshotTarPath(worldId, "gems", repo, fingerprint);
|
|
20348
|
-
const vendorBundle =
|
|
20349
|
-
if (
|
|
20331
|
+
const vendorBundle = path34.join(repoDir, "vendor", "bundle");
|
|
20332
|
+
if (fs30.existsSync(vendorBundle)) {
|
|
20350
20333
|
try {
|
|
20351
20334
|
packTarball(vendorBundle, tarPath);
|
|
20352
|
-
const stat =
|
|
20335
|
+
const stat = fs30.statSync(tarPath);
|
|
20353
20336
|
const manifest = {
|
|
20354
20337
|
kind: "gems",
|
|
20355
20338
|
worldId,
|
|
@@ -20382,10 +20365,10 @@ async function captureGems(worldId, workspacePath, repo) {
|
|
|
20382
20365
|
`docker exec ${containerName} sh -c 'mkdir -p "$(dirname ${tmpTar})" && tar -czf ${tmpTar}.tmp -C ${bundlePath} . && mv ${tmpTar}.tmp ${tmpTar}'`,
|
|
20383
20366
|
{ stdio: "pipe", timeout: 12e4 }
|
|
20384
20367
|
);
|
|
20385
|
-
|
|
20368
|
+
fs30.mkdirSync(path34.dirname(tarPath), { recursive: true });
|
|
20386
20369
|
execSync9(`docker cp ${containerName}:${tmpTar} "${tarPath}"`, { stdio: "pipe", timeout: 12e4 });
|
|
20387
20370
|
execSync9(`docker exec ${containerName} rm -f ${tmpTar}`, { stdio: "pipe" });
|
|
20388
|
-
const stat =
|
|
20371
|
+
const stat = fs30.statSync(tarPath);
|
|
20389
20372
|
const manifest = {
|
|
20390
20373
|
kind: "gems",
|
|
20391
20374
|
worldId,
|
|
@@ -20402,19 +20385,19 @@ async function captureGems(worldId, workspacePath, repo) {
|
|
|
20402
20385
|
}
|
|
20403
20386
|
}
|
|
20404
20387
|
async function captureNode(worldId, workspacePath, repo) {
|
|
20405
|
-
const repoDir =
|
|
20388
|
+
const repoDir = path34.join(workspacePath, repo);
|
|
20406
20389
|
const fingerprint = computeNodeFingerprint(repoDir);
|
|
20407
20390
|
if (!fingerprint) {
|
|
20408
20391
|
return { ok: false, tarPath: "", msg: "no lockfile \u2014 layer does not apply" };
|
|
20409
20392
|
}
|
|
20410
|
-
const nodeModules =
|
|
20411
|
-
if (!
|
|
20393
|
+
const nodeModules = path34.join(repoDir, "node_modules");
|
|
20394
|
+
if (!fs30.existsSync(nodeModules)) {
|
|
20412
20395
|
return { ok: false, tarPath: "", msg: "node_modules not installed yet" };
|
|
20413
20396
|
}
|
|
20414
20397
|
const tarPath = snapshotTarPath(worldId, "node", repo, fingerprint);
|
|
20415
20398
|
try {
|
|
20416
20399
|
packTarball(nodeModules, tarPath);
|
|
20417
|
-
const stat =
|
|
20400
|
+
const stat = fs30.statSync(tarPath);
|
|
20418
20401
|
const manifest = {
|
|
20419
20402
|
kind: "node",
|
|
20420
20403
|
worldId,
|
|
@@ -20431,7 +20414,7 @@ async function captureNode(worldId, workspacePath, repo) {
|
|
|
20431
20414
|
}
|
|
20432
20415
|
}
|
|
20433
20416
|
async function capturePg(worldId, workspacePath, repoNames) {
|
|
20434
|
-
const repoDirs = repoNames.map((r) =>
|
|
20417
|
+
const repoDirs = repoNames.map((r) => path34.join(workspacePath, r));
|
|
20435
20418
|
const fingerprint = computePgFingerprint(repoDirs);
|
|
20436
20419
|
if (!fingerprint) {
|
|
20437
20420
|
return { ok: false, tarPath: "", msg: "no Gemfile.lock / schema.rb \u2014 layer does not apply" };
|
|
@@ -20446,13 +20429,13 @@ async function capturePg(worldId, workspacePath, repoNames) {
|
|
|
20446
20429
|
}
|
|
20447
20430
|
try {
|
|
20448
20431
|
execSync9(`docker stop ${containerName}`, { stdio: "pipe", timeout: 3e4 });
|
|
20449
|
-
|
|
20432
|
+
fs30.mkdirSync(path34.dirname(tarPath), { recursive: true });
|
|
20450
20433
|
execSync9(
|
|
20451
|
-
`docker run --rm -v "${volumeName2}:/pgdata:ro" -v "${
|
|
20434
|
+
`docker run --rm -v "${volumeName2}:/pgdata:ro" -v "${path34.dirname(tarPath)}:/dest" alpine sh -c 'tar -czf /dest/${path34.basename(tarPath)}.tmp -C /pgdata . && mv /dest/${path34.basename(tarPath)}.tmp /dest/${path34.basename(tarPath)}'`,
|
|
20452
20435
|
{ stdio: "pipe", timeout: 18e4 }
|
|
20453
20436
|
);
|
|
20454
20437
|
execSync9(`docker start ${containerName}`, { stdio: "pipe", timeout: 3e4 });
|
|
20455
|
-
const stat =
|
|
20438
|
+
const stat = fs30.statSync(tarPath);
|
|
20456
20439
|
const manifest = {
|
|
20457
20440
|
kind: "pg",
|
|
20458
20441
|
worldId,
|
|
@@ -20475,7 +20458,7 @@ function handleList2(worldIdFilter) {
|
|
|
20475
20458
|
const entries = listSnapshots(worldIdFilter);
|
|
20476
20459
|
if (entries.length === 0) {
|
|
20477
20460
|
const where = worldIdFilter ? ` for world "${worldIdFilter}"` : "";
|
|
20478
|
-
console.log(
|
|
20461
|
+
console.log(pc20.dim(`No snapshots found${where}. Run \`olam world snapshot create <worldId>\` first.`));
|
|
20479
20462
|
return;
|
|
20480
20463
|
}
|
|
20481
20464
|
printHeader(`${entries.length} snapshot(s)`);
|
|
@@ -20484,15 +20467,15 @@ function handleList2(worldIdFilter) {
|
|
|
20484
20467
|
for (const entry of entries) {
|
|
20485
20468
|
const { manifest } = entry;
|
|
20486
20469
|
if (manifest.worldId !== lastWorldId) {
|
|
20487
|
-
console.log(
|
|
20470
|
+
console.log(pc20.bold(manifest.worldId));
|
|
20488
20471
|
lastWorldId = manifest.worldId;
|
|
20489
20472
|
}
|
|
20490
20473
|
const repo = manifest.repo ?? "(all repos)";
|
|
20491
20474
|
const size = formatBytes2(manifest.sizeBytes);
|
|
20492
20475
|
const age = formatAge2(entry.ageMs);
|
|
20493
|
-
const kindColor = manifest.kind === "gems" ?
|
|
20476
|
+
const kindColor = manifest.kind === "gems" ? pc20.magenta(manifest.kind) : manifest.kind === "node" ? pc20.cyan(manifest.kind) : pc20.yellow(manifest.kind);
|
|
20494
20477
|
console.log(
|
|
20495
|
-
` ${kindColor.padEnd(6)} ${
|
|
20478
|
+
` ${kindColor.padEnd(6)} ${pc20.dim(repo.padEnd(24))} ${manifest.fingerprint} ${size.padStart(8)} ${age}`
|
|
20496
20479
|
);
|
|
20497
20480
|
}
|
|
20498
20481
|
console.log();
|
|
@@ -20527,35 +20510,35 @@ function formatAge2(ms) {
|
|
|
20527
20510
|
// src/commands/refresh.ts
|
|
20528
20511
|
init_context();
|
|
20529
20512
|
init_output();
|
|
20530
|
-
import * as
|
|
20513
|
+
import * as fs32 from "node:fs";
|
|
20531
20514
|
import * as os19 from "node:os";
|
|
20532
|
-
import * as
|
|
20533
|
-
import { spawnSync as
|
|
20515
|
+
import * as path36 from "node:path";
|
|
20516
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20534
20517
|
import ora8 from "ora";
|
|
20535
20518
|
|
|
20536
20519
|
// src/commands/refresh-helpers.ts
|
|
20537
|
-
import * as
|
|
20538
|
-
import * as
|
|
20520
|
+
import * as fs31 from "node:fs";
|
|
20521
|
+
import * as path35 from "node:path";
|
|
20539
20522
|
function collectCpSourceFiles(standaloneDir) {
|
|
20540
|
-
if (!
|
|
20523
|
+
if (!fs31.existsSync(standaloneDir)) {
|
|
20541
20524
|
throw new Error(`CP standalone dir not found: ${standaloneDir}`);
|
|
20542
20525
|
}
|
|
20543
20526
|
const entries = [];
|
|
20544
|
-
const topLevel =
|
|
20545
|
-
const stat =
|
|
20527
|
+
const topLevel = fs31.readdirSync(standaloneDir).filter((f) => {
|
|
20528
|
+
const stat = fs31.statSync(path35.join(standaloneDir, f));
|
|
20546
20529
|
return stat.isFile() && f.endsWith(".mjs") && !f.endsWith(".test.mjs");
|
|
20547
20530
|
}).sort();
|
|
20548
20531
|
for (const f of topLevel) {
|
|
20549
|
-
entries.push({ srcPath:
|
|
20532
|
+
entries.push({ srcPath: path35.join(standaloneDir, f), destRelPath: f });
|
|
20550
20533
|
}
|
|
20551
|
-
const libDir =
|
|
20552
|
-
if (
|
|
20553
|
-
const libFiles =
|
|
20554
|
-
const stat =
|
|
20534
|
+
const libDir = path35.join(standaloneDir, "lib");
|
|
20535
|
+
if (fs31.existsSync(libDir) && fs31.statSync(libDir).isDirectory()) {
|
|
20536
|
+
const libFiles = fs31.readdirSync(libDir).filter((f) => {
|
|
20537
|
+
const stat = fs31.statSync(path35.join(libDir, f));
|
|
20555
20538
|
return stat.isFile() && f.endsWith(".mjs") && !f.endsWith(".test.mjs");
|
|
20556
20539
|
}).sort();
|
|
20557
20540
|
for (const f of libFiles) {
|
|
20558
|
-
entries.push({ srcPath:
|
|
20541
|
+
entries.push({ srcPath: path35.join(libDir, f), destRelPath: `lib/${f}` });
|
|
20559
20542
|
}
|
|
20560
20543
|
}
|
|
20561
20544
|
return entries;
|
|
@@ -20574,7 +20557,7 @@ var RESTART_TIMEOUT_S = 30;
|
|
|
20574
20557
|
var HEALTH_POLL_MS = 500;
|
|
20575
20558
|
var HEALTH_TIMEOUT_MS = 3e4;
|
|
20576
20559
|
function docker(args) {
|
|
20577
|
-
const result =
|
|
20560
|
+
const result = spawnSync13("docker", args, {
|
|
20578
20561
|
encoding: "utf-8",
|
|
20579
20562
|
stdio: ["ignore", "pipe", "pipe"]
|
|
20580
20563
|
});
|
|
@@ -20613,16 +20596,16 @@ async function refreshWorld(worldId, portOffset, standaloneDir, opts) {
|
|
|
20613
20596
|
error: err instanceof Error ? err.message : String(err)
|
|
20614
20597
|
};
|
|
20615
20598
|
}
|
|
20616
|
-
const stagingDir =
|
|
20617
|
-
|
|
20599
|
+
const stagingDir = fs32.mkdtempSync(
|
|
20600
|
+
path36.join(os19.tmpdir(), `olam-refresh-${worldId}-`)
|
|
20618
20601
|
);
|
|
20619
20602
|
try {
|
|
20620
20603
|
const hasLib = entries.some((e) => e.destRelPath.startsWith("lib/"));
|
|
20621
20604
|
if (hasLib) {
|
|
20622
|
-
|
|
20605
|
+
fs32.mkdirSync(path36.join(stagingDir, "lib"), { recursive: true });
|
|
20623
20606
|
}
|
|
20624
20607
|
for (const { srcPath, destRelPath } of entries) {
|
|
20625
|
-
|
|
20608
|
+
fs32.copyFileSync(srcPath, path36.join(stagingDir, destRelPath));
|
|
20626
20609
|
}
|
|
20627
20610
|
const cpResult = docker([
|
|
20628
20611
|
"cp",
|
|
@@ -20637,7 +20620,7 @@ async function refreshWorld(worldId, portOffset, standaloneDir, opts) {
|
|
|
20637
20620
|
};
|
|
20638
20621
|
}
|
|
20639
20622
|
} finally {
|
|
20640
|
-
|
|
20623
|
+
fs32.rmSync(stagingDir, { recursive: true, force: true });
|
|
20641
20624
|
}
|
|
20642
20625
|
if (opts.restart) {
|
|
20643
20626
|
const restartResult = docker([
|
|
@@ -20674,11 +20657,11 @@ function registerRefresh(program2) {
|
|
|
20674
20657
|
process.exitCode = 1;
|
|
20675
20658
|
return;
|
|
20676
20659
|
}
|
|
20677
|
-
const standaloneDir =
|
|
20660
|
+
const standaloneDir = path36.join(
|
|
20678
20661
|
process.cwd(),
|
|
20679
20662
|
"packages/control-plane/standalone"
|
|
20680
20663
|
);
|
|
20681
|
-
if (!
|
|
20664
|
+
if (!fs32.existsSync(standaloneDir)) {
|
|
20682
20665
|
printError(
|
|
20683
20666
|
`CP standalone source not found at ${standaloneDir}.
|
|
20684
20667
|
Run \`olam refresh\` from the olam repo root.`
|
|
@@ -20757,11 +20740,11 @@ Run \`olam refresh\` from the olam repo root.`
|
|
|
20757
20740
|
}
|
|
20758
20741
|
|
|
20759
20742
|
// src/commands/diagnose.ts
|
|
20760
|
-
import * as
|
|
20743
|
+
import * as fs33 from "node:fs";
|
|
20761
20744
|
import * as os20 from "node:os";
|
|
20762
|
-
import * as
|
|
20763
|
-
import { execFileSync as
|
|
20764
|
-
import
|
|
20745
|
+
import * as path37 from "node:path";
|
|
20746
|
+
import { execFileSync as execFileSync7, execSync as execSync10 } from "node:child_process";
|
|
20747
|
+
import pc21 from "picocolors";
|
|
20765
20748
|
|
|
20766
20749
|
// ../core/dist/diagnose/secret-stripper.js
|
|
20767
20750
|
var SECRET_PATTERNS = [
|
|
@@ -20794,9 +20777,9 @@ function stripSecrets(input) {
|
|
|
20794
20777
|
}
|
|
20795
20778
|
|
|
20796
20779
|
// src/commands/diagnose.ts
|
|
20797
|
-
var DIAGNOSTICS_DIR =
|
|
20798
|
-
var LOG_DIR =
|
|
20799
|
-
var CACHE_DIR =
|
|
20780
|
+
var DIAGNOSTICS_DIR = path37.join(os20.homedir(), ".olam", "diagnostics");
|
|
20781
|
+
var LOG_DIR = path37.join(os20.homedir(), ".olam", "log");
|
|
20782
|
+
var CACHE_DIR = path37.join(os20.homedir(), ".olam", "cache");
|
|
20800
20783
|
var LOG_TAIL_LINES = 200;
|
|
20801
20784
|
function safeExec(cmd) {
|
|
20802
20785
|
try {
|
|
@@ -20806,14 +20789,14 @@ function safeExec(cmd) {
|
|
|
20806
20789
|
}
|
|
20807
20790
|
}
|
|
20808
20791
|
function defaultZip(zipPath, files) {
|
|
20809
|
-
|
|
20792
|
+
execFileSync7("zip", ["-j", zipPath, ...files]);
|
|
20810
20793
|
}
|
|
20811
20794
|
async function buildDiagnosticsZip(_exec = (cmd) => safeExec(cmd), _outDir = DIAGNOSTICS_DIR, _logDir = LOG_DIR, _zip = defaultZip) {
|
|
20812
20795
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 23);
|
|
20813
|
-
const zipPath =
|
|
20814
|
-
const tmpDir =
|
|
20796
|
+
const zipPath = path37.join(_outDir, `olam-diag-${ts}.zip`);
|
|
20797
|
+
const tmpDir = fs33.mkdtempSync(path37.join(os20.tmpdir(), "olam-diag-"));
|
|
20815
20798
|
try {
|
|
20816
|
-
|
|
20799
|
+
fs33.mkdirSync(_outDir, { recursive: true });
|
|
20817
20800
|
const entries = [];
|
|
20818
20801
|
const version = process.env["OLAM_CLI_VERSION"] ?? "unknown";
|
|
20819
20802
|
const nodeVersion = process.version;
|
|
@@ -20829,14 +20812,14 @@ platform: ${platform}
|
|
|
20829
20812
|
`uptime: ${os20.uptime()}s`
|
|
20830
20813
|
].join("\n") + "\n";
|
|
20831
20814
|
_writeEntry(tmpDir, "os-info.txt", stripSecrets(osContent), entries);
|
|
20832
|
-
const depsFile =
|
|
20833
|
-
if (
|
|
20834
|
-
const deps =
|
|
20815
|
+
const depsFile = path37.join(CACHE_DIR, "deps.json");
|
|
20816
|
+
if (fs33.existsSync(depsFile)) {
|
|
20817
|
+
const deps = fs33.readFileSync(depsFile, "utf-8");
|
|
20835
20818
|
_writeEntry(tmpDir, "deps.json", stripSecrets(deps), entries);
|
|
20836
20819
|
}
|
|
20837
20820
|
const latestLog = _latestLog(_logDir);
|
|
20838
20821
|
if (latestLog) {
|
|
20839
|
-
const lines =
|
|
20822
|
+
const lines = fs33.readFileSync(latestLog, "utf-8").split("\n");
|
|
20840
20823
|
const tail = lines.slice(-LOG_TAIL_LINES).join("\n");
|
|
20841
20824
|
_writeEntry(tmpDir, "log-tail.txt", stripSecrets(tail), entries);
|
|
20842
20825
|
}
|
|
@@ -20848,38 +20831,38 @@ platform: ${platform}
|
|
|
20848
20831
|
if (authAudit) {
|
|
20849
20832
|
_writeEntry(tmpDir, "audit-auth-callers.txt", stripSecrets(authAudit), entries);
|
|
20850
20833
|
}
|
|
20851
|
-
const fileArgs = entries.map((e) =>
|
|
20834
|
+
const fileArgs = entries.map((e) => path37.join(tmpDir, e));
|
|
20852
20835
|
try {
|
|
20853
20836
|
_zip(zipPath, fileArgs);
|
|
20854
20837
|
} catch (err) {
|
|
20855
20838
|
throw new Error(`zip command produced no output file. zip stderr: ${err.message}`);
|
|
20856
20839
|
}
|
|
20857
|
-
if (!
|
|
20840
|
+
if (!fs33.existsSync(zipPath)) {
|
|
20858
20841
|
throw new Error("zip command produced no output file.");
|
|
20859
20842
|
}
|
|
20860
20843
|
return { zipPath, entries };
|
|
20861
20844
|
} finally {
|
|
20862
20845
|
try {
|
|
20863
|
-
|
|
20846
|
+
fs33.rmSync(tmpDir, { recursive: true, force: true });
|
|
20864
20847
|
} catch {
|
|
20865
20848
|
}
|
|
20866
20849
|
}
|
|
20867
20850
|
}
|
|
20868
20851
|
function _writeEntry(dir, name, content, entries) {
|
|
20869
|
-
|
|
20852
|
+
fs33.writeFileSync(path37.join(dir, name), content, { mode: 420 });
|
|
20870
20853
|
entries.push(name);
|
|
20871
20854
|
}
|
|
20872
20855
|
function _latestLog(logDir) {
|
|
20873
|
-
if (!
|
|
20874
|
-
const files =
|
|
20875
|
-
return files.length > 0 ?
|
|
20856
|
+
if (!fs33.existsSync(logDir)) return null;
|
|
20857
|
+
const files = fs33.readdirSync(logDir).filter((f) => f.startsWith("host-")).sort().reverse();
|
|
20858
|
+
return files.length > 0 ? path37.join(logDir, files[0]) : null;
|
|
20876
20859
|
}
|
|
20877
20860
|
async function buildTelemetryPayload() {
|
|
20878
20861
|
const channel = "stable";
|
|
20879
|
-
const manifestFile =
|
|
20862
|
+
const manifestFile = path37.join(CACHE_DIR, "manifest.json");
|
|
20880
20863
|
let manifestAgeHours = null;
|
|
20881
|
-
if (
|
|
20882
|
-
const mtime =
|
|
20864
|
+
if (fs33.existsSync(manifestFile)) {
|
|
20865
|
+
const mtime = fs33.statSync(manifestFile).mtime.getTime();
|
|
20883
20866
|
manifestAgeHours = Math.round((Date.now() - mtime) / 36e5);
|
|
20884
20867
|
}
|
|
20885
20868
|
return {
|
|
@@ -20899,47 +20882,47 @@ function registerDiagnose(program2) {
|
|
|
20899
20882
|
return;
|
|
20900
20883
|
}
|
|
20901
20884
|
if (!opts.quiet) {
|
|
20902
|
-
console.log(
|
|
20885
|
+
console.log(pc21.cyan("Building diagnostics zip\u2026"));
|
|
20903
20886
|
}
|
|
20904
20887
|
try {
|
|
20905
20888
|
const { zipPath, entries } = await buildDiagnosticsZip();
|
|
20906
20889
|
if (!opts.quiet) {
|
|
20907
|
-
console.log(
|
|
20908
|
-
console.log(
|
|
20890
|
+
console.log(pc21.green(`Done: ${zipPath}`));
|
|
20891
|
+
console.log(pc21.dim(`Contents: ${entries.join(", ")}`));
|
|
20909
20892
|
}
|
|
20910
20893
|
if (opts.upload) {
|
|
20911
|
-
console.log(
|
|
20894
|
+
console.log(pc21.yellow(
|
|
20912
20895
|
`Telemetry endpoint not yet provisioned. Share the zip manually:
|
|
20913
20896
|
File: ${zipPath}
|
|
20914
20897
|
See docs/runbooks/share-diagnostics.md for instructions.`
|
|
20915
20898
|
));
|
|
20916
20899
|
}
|
|
20917
20900
|
} catch (err) {
|
|
20918
|
-
console.error(
|
|
20901
|
+
console.error(pc21.red(`Diagnose failed: ${err.message}`));
|
|
20919
20902
|
process.exitCode = 1;
|
|
20920
20903
|
}
|
|
20921
20904
|
});
|
|
20922
20905
|
}
|
|
20923
20906
|
|
|
20924
20907
|
// src/commands/update.ts
|
|
20925
|
-
import * as
|
|
20908
|
+
import * as fs36 from "node:fs";
|
|
20926
20909
|
import * as os22 from "node:os";
|
|
20927
|
-
import * as
|
|
20910
|
+
import * as path40 from "node:path";
|
|
20928
20911
|
import { execSync as execSync11 } from "node:child_process";
|
|
20929
|
-
import
|
|
20912
|
+
import pc22 from "picocolors";
|
|
20930
20913
|
|
|
20931
20914
|
// src/lib/symlink-reconcile.ts
|
|
20932
|
-
import * as
|
|
20933
|
-
import * as
|
|
20915
|
+
import * as fs34 from "node:fs";
|
|
20916
|
+
import * as path38 from "node:path";
|
|
20934
20917
|
var realFs = {
|
|
20935
|
-
readdirSync: (p) =>
|
|
20936
|
-
existsSync: (p) =>
|
|
20937
|
-
lstatSync: (p) =>
|
|
20938
|
-
readlinkSync: (p) =>
|
|
20939
|
-
symlinkSync: (t, l) =>
|
|
20940
|
-
unlinkSync: (p) =>
|
|
20918
|
+
readdirSync: (p) => fs34.readdirSync(p),
|
|
20919
|
+
existsSync: (p) => fs34.existsSync(p),
|
|
20920
|
+
lstatSync: (p) => fs34.lstatSync(p),
|
|
20921
|
+
readlinkSync: (p) => fs34.readlinkSync(p),
|
|
20922
|
+
symlinkSync: (t, l) => fs34.symlinkSync(t, l),
|
|
20923
|
+
unlinkSync: (p) => fs34.unlinkSync(p),
|
|
20941
20924
|
mkdirSync: (p, o) => {
|
|
20942
|
-
|
|
20925
|
+
fs34.mkdirSync(p, o);
|
|
20943
20926
|
}
|
|
20944
20927
|
};
|
|
20945
20928
|
function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
|
|
@@ -20949,8 +20932,8 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
|
|
|
20949
20932
|
_fs.mkdirSync(claudeSkillsDir, { recursive: true });
|
|
20950
20933
|
const sourceSkills = _fs.existsSync(npmSkillsDir) ? _fs.readdirSync(npmSkillsDir).filter((d) => d.startsWith("olam-")) : [];
|
|
20951
20934
|
for (const skill of sourceSkills) {
|
|
20952
|
-
const linkPath =
|
|
20953
|
-
const target =
|
|
20935
|
+
const linkPath = path38.join(claudeSkillsDir, skill);
|
|
20936
|
+
const target = path38.join(npmSkillsDir, skill);
|
|
20954
20937
|
if (!_fs.existsSync(linkPath)) {
|
|
20955
20938
|
try {
|
|
20956
20939
|
_fs.symlinkSync(target, linkPath);
|
|
@@ -20963,7 +20946,7 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
|
|
|
20963
20946
|
}
|
|
20964
20947
|
const deployedEntries = _fs.existsSync(claudeSkillsDir) ? _fs.readdirSync(claudeSkillsDir).filter((d) => d.startsWith("olam-")) : [];
|
|
20965
20948
|
for (const entry of deployedEntries) {
|
|
20966
|
-
const linkPath =
|
|
20949
|
+
const linkPath = path38.join(claudeSkillsDir, entry);
|
|
20967
20950
|
let isSymlink = false;
|
|
20968
20951
|
try {
|
|
20969
20952
|
isSymlink = _fs.lstatSync(linkPath).isSymbolicLink();
|
|
@@ -20988,9 +20971,9 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
|
|
|
20988
20971
|
|
|
20989
20972
|
// src/commands/update.ts
|
|
20990
20973
|
var PACKAGE_NAME = "@pleri/olam-cli";
|
|
20991
|
-
var CACHE_DIR2 =
|
|
20992
|
-
var LOG_DIR2 =
|
|
20993
|
-
var LAST_STABLE_FILE =
|
|
20974
|
+
var CACHE_DIR2 = path40.join(os22.homedir(), ".olam", "cache");
|
|
20975
|
+
var LOG_DIR2 = path40.join(os22.homedir(), ".olam", "log");
|
|
20976
|
+
var LAST_STABLE_FILE = path40.join(CACHE_DIR2, "last-stable.txt");
|
|
20994
20977
|
function defaultExec(cmd) {
|
|
20995
20978
|
try {
|
|
20996
20979
|
const stdout = execSync11(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -21012,22 +20995,22 @@ function getCurrentVersion(_exec = defaultExec) {
|
|
|
21012
20995
|
}
|
|
21013
20996
|
function readLastStable(file = LAST_STABLE_FILE) {
|
|
21014
20997
|
try {
|
|
21015
|
-
const v =
|
|
20998
|
+
const v = fs36.readFileSync(file, "utf-8").trim();
|
|
21016
20999
|
return v || null;
|
|
21017
21000
|
} catch {
|
|
21018
21001
|
return null;
|
|
21019
21002
|
}
|
|
21020
21003
|
}
|
|
21021
21004
|
function writeLastStable(version, file = LAST_STABLE_FILE) {
|
|
21022
|
-
|
|
21023
|
-
|
|
21005
|
+
fs36.mkdirSync(path40.dirname(file), { recursive: true });
|
|
21006
|
+
fs36.writeFileSync(file, version, { mode: 420 });
|
|
21024
21007
|
}
|
|
21025
21008
|
function logUpdateFailure(stderr) {
|
|
21026
21009
|
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
21027
|
-
const logFile =
|
|
21010
|
+
const logFile = path40.join(LOG_DIR2, `update-${ts}.log`);
|
|
21028
21011
|
try {
|
|
21029
|
-
|
|
21030
|
-
|
|
21012
|
+
fs36.mkdirSync(LOG_DIR2, { recursive: true });
|
|
21013
|
+
fs36.appendFileSync(logFile, `[update-failure ${(/* @__PURE__ */ new Date()).toISOString()}]
|
|
21031
21014
|
${stderr}
|
|
21032
21015
|
`, "utf-8");
|
|
21033
21016
|
} catch {
|
|
@@ -21078,52 +21061,52 @@ async function doUpdate(opts, _exec = defaultExec, _reconcile = reconcileSkillSy
|
|
|
21078
21061
|
writeLastStable(prevVersion);
|
|
21079
21062
|
}
|
|
21080
21063
|
if (!quiet) {
|
|
21081
|
-
console.log(
|
|
21064
|
+
console.log(pc22.cyan(`Installing ${PACKAGE_NAME}@${channel}\u2026`));
|
|
21082
21065
|
}
|
|
21083
21066
|
const installResult = _exec(`npm install -g ${PACKAGE_NAME}@${channel}`);
|
|
21084
21067
|
if (installResult.exitCode !== 0) {
|
|
21085
21068
|
logUpdateFailure(installResult.stderr);
|
|
21086
21069
|
if (!quiet) {
|
|
21087
|
-
console.error(
|
|
21070
|
+
console.error(pc22.red("Update failed."));
|
|
21088
21071
|
}
|
|
21089
21072
|
const prev = readLastStable();
|
|
21090
21073
|
if (prev) {
|
|
21091
21074
|
if (!quiet) {
|
|
21092
|
-
console.log(
|
|
21075
|
+
console.log(pc22.yellow(`Restoring to ${prev}\u2026`));
|
|
21093
21076
|
}
|
|
21094
21077
|
const restoreResult = _exec(`npm install -g ${PACKAGE_NAME}@${prev}`);
|
|
21095
21078
|
if (restoreResult.exitCode !== 0) {
|
|
21096
21079
|
if (!quiet) {
|
|
21097
|
-
console.error(
|
|
21080
|
+
console.error(pc22.red(
|
|
21098
21081
|
`Restore also failed. Run: npm install -g ${PACKAGE_NAME}@${prev} manually.`
|
|
21099
21082
|
));
|
|
21100
21083
|
}
|
|
21101
21084
|
} else if (!quiet) {
|
|
21102
|
-
console.log(
|
|
21085
|
+
console.log(pc22.yellow(`Restored to ${prev}.`));
|
|
21103
21086
|
}
|
|
21104
21087
|
} else if (!quiet) {
|
|
21105
|
-
console.error(
|
|
21088
|
+
console.error(pc22.red("No previous version cached; cannot auto-restore."));
|
|
21106
21089
|
}
|
|
21107
21090
|
return { action: "restored", prevVersion: prev ?? void 0, exitCode: 11 };
|
|
21108
21091
|
}
|
|
21109
21092
|
const newVersion = getCurrentVersion(_exec) ?? void 0;
|
|
21110
21093
|
if (!quiet && newVersion) {
|
|
21111
|
-
console.log(
|
|
21094
|
+
console.log(pc22.green(`Updated to ${newVersion}.`));
|
|
21112
21095
|
}
|
|
21113
21096
|
const npmRootResult = _getNpmRoot("npm root -g");
|
|
21114
21097
|
let symlinkResult = { added: [], removed: [] };
|
|
21115
21098
|
if (npmRootResult.exitCode === 0) {
|
|
21116
21099
|
const npmRoot = npmRootResult.stdout.trim();
|
|
21117
|
-
const npmSkillsDir =
|
|
21118
|
-
const claudeSkillsDir =
|
|
21100
|
+
const npmSkillsDir = path40.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills");
|
|
21101
|
+
const claudeSkillsDir = path40.join(os22.homedir(), ".claude", "skills");
|
|
21119
21102
|
const rec = _reconcile(npmSkillsDir, claudeSkillsDir);
|
|
21120
21103
|
symlinkResult = { added: rec.added, removed: rec.removed };
|
|
21121
21104
|
if (!quiet && (rec.added.length > 0 || rec.removed.length > 0)) {
|
|
21122
21105
|
if (rec.added.length > 0) {
|
|
21123
|
-
console.log(
|
|
21106
|
+
console.log(pc22.dim(`Skills added: ${rec.added.join(", ")}`));
|
|
21124
21107
|
}
|
|
21125
21108
|
if (rec.removed.length > 0) {
|
|
21126
|
-
console.log(
|
|
21109
|
+
console.log(pc22.dim(`Skills removed (dangling): ${rec.removed.join(", ")}`));
|
|
21127
21110
|
}
|
|
21128
21111
|
}
|
|
21129
21112
|
}
|
|
@@ -21161,8 +21144,8 @@ async function doRollback(_exec = defaultExec, _reconcile = reconcileSkillSymlin
|
|
|
21161
21144
|
if (npmRootResult.exitCode === 0) {
|
|
21162
21145
|
const npmRoot = npmRootResult.stdout.trim();
|
|
21163
21146
|
_reconcile(
|
|
21164
|
-
|
|
21165
|
-
|
|
21147
|
+
path40.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills"),
|
|
21148
|
+
path40.join(os22.homedir(), ".claude", "skills")
|
|
21166
21149
|
);
|
|
21167
21150
|
}
|
|
21168
21151
|
return { action: "rolled-back", restoredVersion: prev, exitCode: 0 };
|
|
@@ -21217,7 +21200,7 @@ function registerUpdate(program2) {
|
|
|
21217
21200
|
// src/commands/begin.ts
|
|
21218
21201
|
init_host_cp();
|
|
21219
21202
|
init_open_url();
|
|
21220
|
-
import
|
|
21203
|
+
import pc23 from "picocolors";
|
|
21221
21204
|
async function doBegin(opts, _startFn = startHostCp, _openFn = openUrl) {
|
|
21222
21205
|
const prevExitCode = process.exitCode;
|
|
21223
21206
|
await _startFn({ showToken: opts.showToken });
|
|
@@ -21227,7 +21210,7 @@ async function doBegin(opts, _startFn = startHostCp, _openFn = openUrl) {
|
|
|
21227
21210
|
const token = readToken();
|
|
21228
21211
|
if (token) {
|
|
21229
21212
|
const base = `http://127.0.0.1:${HOST_CP_PORT}/`;
|
|
21230
|
-
console.log(
|
|
21213
|
+
console.log(pc23.dim(`Opening ${base}`));
|
|
21231
21214
|
_openFn(`${base}?token=${token}`);
|
|
21232
21215
|
}
|
|
21233
21216
|
}
|
|
@@ -21323,8 +21306,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
|
|
|
21323
21306
|
function authHeaders() {
|
|
21324
21307
|
return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
|
|
21325
21308
|
}
|
|
21326
|
-
async function apiFetch(
|
|
21327
|
-
const res = await fetch(`${BASE_URL}${
|
|
21309
|
+
async function apiFetch(path44, init = {}) {
|
|
21310
|
+
const res = await fetch(`${BASE_URL}${path44}`, {
|
|
21328
21311
|
...init,
|
|
21329
21312
|
headers: {
|
|
21330
21313
|
"Content-Type": "application/json",
|
|
@@ -21476,7 +21459,7 @@ function registerMcpAdd(cmd) {
|
|
|
21476
21459
|
|
|
21477
21460
|
// src/commands/mcp/list.ts
|
|
21478
21461
|
init_output();
|
|
21479
|
-
import
|
|
21462
|
+
import pc24 from "picocolors";
|
|
21480
21463
|
function registerMcpList(cmd) {
|
|
21481
21464
|
cmd.command("list").description("List all registered MCP credentials").action(async () => {
|
|
21482
21465
|
const client = getMcpAuthClient();
|
|
@@ -21491,8 +21474,8 @@ function registerMcpList(cmd) {
|
|
|
21491
21474
|
}
|
|
21492
21475
|
const mcps = data.mcps ?? [];
|
|
21493
21476
|
if (mcps.length === 0) {
|
|
21494
|
-
console.log(
|
|
21495
|
-
console.log(
|
|
21477
|
+
console.log(pc24.dim("No MCP credentials registered."));
|
|
21478
|
+
console.log(pc24.dim("Add one with: olam mcp add <service>"));
|
|
21496
21479
|
return;
|
|
21497
21480
|
}
|
|
21498
21481
|
const [c0, c1, c2, c3] = [16, 20, 10, 24];
|
|
@@ -21503,12 +21486,12 @@ function registerMcpList(cmd) {
|
|
|
21503
21486
|
"ENV VAR".padEnd(c3),
|
|
21504
21487
|
"STATUS"
|
|
21505
21488
|
].join(" ");
|
|
21506
|
-
console.log(
|
|
21507
|
-
console.log(
|
|
21489
|
+
console.log(pc24.dim(header));
|
|
21490
|
+
console.log(pc24.dim("\u2500".repeat(header.length)));
|
|
21508
21491
|
for (const m of mcps) {
|
|
21509
|
-
const status = !m.validated ?
|
|
21492
|
+
const status = !m.validated ? pc24.yellow("unvalidated") : m.expired ? pc24.red("expired") : pc24.green("active");
|
|
21510
21493
|
const row = [
|
|
21511
|
-
|
|
21494
|
+
pc24.cyan(m.service.padEnd(c0)),
|
|
21512
21495
|
m.label.padEnd(c1).slice(0, c1),
|
|
21513
21496
|
m.type.padEnd(c2),
|
|
21514
21497
|
(m.envName ?? "\u2014").padEnd(c3).slice(0, c3),
|
|
@@ -21536,13 +21519,13 @@ function registerMcpRemove(cmd) {
|
|
|
21536
21519
|
|
|
21537
21520
|
// src/commands/mcp/status.ts
|
|
21538
21521
|
init_output();
|
|
21539
|
-
import
|
|
21522
|
+
import pc25 from "picocolors";
|
|
21540
21523
|
function formatExpiry(expiresAt) {
|
|
21541
21524
|
if (!expiresAt) return "\u2014";
|
|
21542
21525
|
const ms = expiresAt - Date.now();
|
|
21543
|
-
if (ms <= 0) return
|
|
21526
|
+
if (ms <= 0) return pc25.red("expired");
|
|
21544
21527
|
const hours = ms / (1e3 * 60 * 60);
|
|
21545
|
-
if (hours < 1) return
|
|
21528
|
+
if (hours < 1) return pc25.yellow(`${Math.ceil(ms / 6e4)}m`);
|
|
21546
21529
|
return `${hours.toFixed(1)}h`;
|
|
21547
21530
|
}
|
|
21548
21531
|
function registerMcpStatus(cmd) {
|
|
@@ -21559,14 +21542,14 @@ function registerMcpStatus(cmd) {
|
|
|
21559
21542
|
const mcps = data.mcps ?? [];
|
|
21560
21543
|
printHeader(`MCP Credentials (${mcps.length})`);
|
|
21561
21544
|
if (mcps.length === 0) {
|
|
21562
|
-
console.log(
|
|
21545
|
+
console.log(pc25.dim("No credentials registered."));
|
|
21563
21546
|
return;
|
|
21564
21547
|
}
|
|
21565
21548
|
for (const m of mcps) {
|
|
21566
|
-
const stateColor = !m.validated ?
|
|
21549
|
+
const stateColor = !m.validated ? pc25.yellow : m.expired ? pc25.red : pc25.green;
|
|
21567
21550
|
const stateLabel = !m.validated ? "unvalidated" : m.expired ? "expired" : "active";
|
|
21568
21551
|
console.log(`
|
|
21569
|
-
${
|
|
21552
|
+
${pc25.cyan(m.service)} ${stateColor(`[${stateLabel}]`)}`);
|
|
21570
21553
|
console.log(` label: ${m.label}`);
|
|
21571
21554
|
console.log(` type: ${m.type}`);
|
|
21572
21555
|
if (m.envName) console.log(` env var: ${m.envName}`);
|
|
@@ -21581,15 +21564,15 @@ function registerMcpStatus(cmd) {
|
|
|
21581
21564
|
// src/commands/mcp/import.ts
|
|
21582
21565
|
init_output();
|
|
21583
21566
|
import * as readline3 from "node:readline";
|
|
21584
|
-
import
|
|
21567
|
+
import pc26 from "picocolors";
|
|
21585
21568
|
|
|
21586
21569
|
// src/commands/mcp/import-discovery.ts
|
|
21587
|
-
import * as
|
|
21570
|
+
import * as fs37 from "node:fs";
|
|
21588
21571
|
import * as os23 from "node:os";
|
|
21589
|
-
import * as
|
|
21572
|
+
import * as path41 from "node:path";
|
|
21590
21573
|
function readJsonFile(filePath) {
|
|
21591
21574
|
try {
|
|
21592
|
-
const raw =
|
|
21575
|
+
const raw = fs37.readFileSync(filePath, "utf-8");
|
|
21593
21576
|
return JSON.parse(raw);
|
|
21594
21577
|
} catch {
|
|
21595
21578
|
return null;
|
|
@@ -21618,24 +21601,24 @@ function extractMcpServers(obj, source, sourceLabel) {
|
|
|
21618
21601
|
}
|
|
21619
21602
|
function getClaudeDesktopPath() {
|
|
21620
21603
|
if (process.platform === "darwin") {
|
|
21621
|
-
return
|
|
21604
|
+
return path41.join(os23.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
21622
21605
|
}
|
|
21623
21606
|
if (process.platform === "win32") {
|
|
21624
|
-
const appData = process.env["APPDATA"] ??
|
|
21625
|
-
return
|
|
21607
|
+
const appData = process.env["APPDATA"] ?? path41.join(os23.homedir(), "AppData", "Roaming");
|
|
21608
|
+
return path41.join(appData, "Claude", "claude_desktop_config.json");
|
|
21626
21609
|
}
|
|
21627
|
-
return
|
|
21610
|
+
return path41.join(os23.homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
21628
21611
|
}
|
|
21629
21612
|
function getOlamRepoPaths() {
|
|
21630
21613
|
const configPaths = [
|
|
21631
|
-
|
|
21632
|
-
|
|
21614
|
+
path41.join(os23.homedir(), ".olam", "config.yaml"),
|
|
21615
|
+
path41.join(process.cwd(), ".olam", "config.yaml")
|
|
21633
21616
|
];
|
|
21634
21617
|
const paths = [];
|
|
21635
21618
|
for (const configPath of configPaths) {
|
|
21636
|
-
if (!
|
|
21619
|
+
if (!fs37.existsSync(configPath)) continue;
|
|
21637
21620
|
try {
|
|
21638
|
-
const raw =
|
|
21621
|
+
const raw = fs37.readFileSync(configPath, "utf-8");
|
|
21639
21622
|
const repoMatches = [...raw.matchAll(/path:\s*["']?([^\s"'\n]+)/g)];
|
|
21640
21623
|
for (const m of repoMatches) {
|
|
21641
21624
|
if (m[1]) paths.push(m[1]);
|
|
@@ -21651,7 +21634,7 @@ async function discoverMcpSources(repoPaths) {
|
|
|
21651
21634
|
const sources = [];
|
|
21652
21635
|
const sourceDefs = [
|
|
21653
21636
|
{
|
|
21654
|
-
path:
|
|
21637
|
+
path: path41.join(os23.homedir(), ".claude.json"),
|
|
21655
21638
|
label: "Claude Code (~/.claude.json)"
|
|
21656
21639
|
},
|
|
21657
21640
|
{
|
|
@@ -21659,19 +21642,19 @@ async function discoverMcpSources(repoPaths) {
|
|
|
21659
21642
|
label: "Claude Desktop"
|
|
21660
21643
|
},
|
|
21661
21644
|
{
|
|
21662
|
-
path:
|
|
21645
|
+
path: path41.join(os23.homedir(), ".cursor", "mcp.json"),
|
|
21663
21646
|
label: "Cursor (~/.cursor/mcp.json)"
|
|
21664
21647
|
},
|
|
21665
21648
|
{
|
|
21666
|
-
path:
|
|
21649
|
+
path: path41.join(os23.homedir(), ".codeium", "windsurf", "mcp_config.json"),
|
|
21667
21650
|
label: "Windsurf (~/.codeium/windsurf/mcp_config.json)"
|
|
21668
21651
|
}
|
|
21669
21652
|
];
|
|
21670
21653
|
const resolvedRepoPaths = repoPaths ?? getOlamRepoPaths();
|
|
21671
21654
|
for (const repoPath of resolvedRepoPaths) {
|
|
21672
21655
|
sourceDefs.push({
|
|
21673
|
-
path:
|
|
21674
|
-
label: `.mcp.json (${
|
|
21656
|
+
path: path41.join(repoPath, ".mcp.json"),
|
|
21657
|
+
label: `.mcp.json (${path41.basename(repoPath)})`
|
|
21675
21658
|
});
|
|
21676
21659
|
}
|
|
21677
21660
|
const reads = await Promise.all(
|
|
@@ -21751,13 +21734,13 @@ async function validateMcpEntry(entry) {
|
|
|
21751
21734
|
// src/commands/mcp/import.ts
|
|
21752
21735
|
async function multiSelectPicker(entries) {
|
|
21753
21736
|
if (entries.length === 0) return [];
|
|
21754
|
-
console.log("\n" +
|
|
21737
|
+
console.log("\n" + pc26.bold("Discovered MCP servers:"));
|
|
21755
21738
|
entries.forEach((e, i) => {
|
|
21756
21739
|
console.log(
|
|
21757
|
-
` ${
|
|
21740
|
+
` ${pc26.dim(`[${i + 1}]`)} ${pc26.cyan(e.name.padEnd(20))} ${pc26.dim(e.sourceLabel)}`
|
|
21758
21741
|
);
|
|
21759
21742
|
});
|
|
21760
|
-
console.log("\n" +
|
|
21743
|
+
console.log("\n" + pc26.dim('Enter numbers to import (e.g. 1,2,3 or "all" or Enter to skip):'));
|
|
21761
21744
|
const answer = await new Promise((resolve8) => {
|
|
21762
21745
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
21763
21746
|
rl.question("> ", (ans) => {
|
|
@@ -21784,7 +21767,7 @@ function registerMcpImport(cmd) {
|
|
|
21784
21767
|
const repoPaths = opts.repoPaths ? opts.repoPaths.split(",").map((s) => s.trim()) : void 0;
|
|
21785
21768
|
const { entries, sources, durationMs } = await discoverMcpSources(repoPaths);
|
|
21786
21769
|
if (entries.length === 0) {
|
|
21787
|
-
console.log(
|
|
21770
|
+
console.log(pc26.dim("No MCP servers found in any source path."));
|
|
21788
21771
|
return;
|
|
21789
21772
|
}
|
|
21790
21773
|
printInfo("Sources", sources.length > 0 ? sources.join(", ") : "none");
|
|
@@ -21797,15 +21780,15 @@ function registerMcpImport(cmd) {
|
|
|
21797
21780
|
candidates = filtered;
|
|
21798
21781
|
}
|
|
21799
21782
|
if (skippedCount > 0) {
|
|
21800
|
-
console.log(
|
|
21783
|
+
console.log(pc26.dim(`skipped: ${skippedCount} already registered`));
|
|
21801
21784
|
}
|
|
21802
21785
|
if (candidates.length === 0) {
|
|
21803
|
-
console.log(
|
|
21786
|
+
console.log(pc26.dim("Nothing new to import. Use --reimport to force."));
|
|
21804
21787
|
return;
|
|
21805
21788
|
}
|
|
21806
21789
|
const selected = await multiSelectPicker(candidates);
|
|
21807
21790
|
if (selected.length === 0) {
|
|
21808
|
-
console.log(
|
|
21791
|
+
console.log(pc26.dim("No servers selected."));
|
|
21809
21792
|
return;
|
|
21810
21793
|
}
|
|
21811
21794
|
console.log(`
|
|
@@ -21816,16 +21799,16 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
21816
21799
|
let validated = false;
|
|
21817
21800
|
let validationReason = "skipped";
|
|
21818
21801
|
if (opts.validate !== false) {
|
|
21819
|
-
process.stdout.write(` ${
|
|
21802
|
+
process.stdout.write(` ${pc26.dim("\u2192")} ${entry.name} validating\u2026 `);
|
|
21820
21803
|
const vr = await validateMcpEntry(entry);
|
|
21821
21804
|
validated = vr.validated;
|
|
21822
21805
|
validationReason = vr.reason;
|
|
21823
21806
|
process.stdout.write(
|
|
21824
|
-
validated ?
|
|
21807
|
+
validated ? pc26.green("ok\n") : pc26.yellow(`unvalidated (${vr.reason})
|
|
21825
21808
|
`)
|
|
21826
21809
|
);
|
|
21827
21810
|
} else {
|
|
21828
|
-
console.log(` ${
|
|
21811
|
+
console.log(` ${pc26.dim("\u2192")} ${entry.name} ${pc26.dim("(validation skipped)")}`);
|
|
21829
21812
|
}
|
|
21830
21813
|
if (validated) validatedCount++;
|
|
21831
21814
|
const result = await client.staticAdd({
|
|
@@ -21840,21 +21823,21 @@ Importing ${selected.length} server(s)\u2026`);
|
|
|
21840
21823
|
}
|
|
21841
21824
|
}
|
|
21842
21825
|
console.log("");
|
|
21843
|
-
console.log(
|
|
21826
|
+
console.log(pc26.green(`\u2713 Imported ${importedCount}/${selected.length}`));
|
|
21844
21827
|
if (validatedCount > 0) {
|
|
21845
|
-
console.log(
|
|
21828
|
+
console.log(pc26.dim(` ${validatedCount} validated, ${importedCount - validatedCount} unvalidated`));
|
|
21846
21829
|
}
|
|
21847
21830
|
});
|
|
21848
21831
|
}
|
|
21849
21832
|
|
|
21850
21833
|
// src/commands/mcp/revoke.ts
|
|
21851
21834
|
init_output();
|
|
21852
|
-
import
|
|
21835
|
+
import pc27 from "picocolors";
|
|
21853
21836
|
function registerMcpRevoke(cmd) {
|
|
21854
21837
|
cmd.command("revoke <user> <world> <service>").description("Revoke a user's MCP service entitlement (multi-tenant mode only)").action(async (user, world, service) => {
|
|
21855
21838
|
const multiTenant = (process.env["OLAM_MCP_MULTI_TENANT"] ?? "").toLowerCase() === "true";
|
|
21856
21839
|
if (!multiTenant) {
|
|
21857
|
-
console.warn(
|
|
21840
|
+
console.warn(pc27.yellow("\u26A0 revocation only meaningful in multi_tenant mode \u2014 no-op"));
|
|
21858
21841
|
return;
|
|
21859
21842
|
}
|
|
21860
21843
|
const baseUrl = process.env["OLAM_MCP_AUTH_SERVICE_URL"] ?? "http://127.0.0.1:9998";
|
|
@@ -21883,8 +21866,8 @@ function registerMcpRevoke(cmd) {
|
|
|
21883
21866
|
process.exitCode = 1;
|
|
21884
21867
|
return;
|
|
21885
21868
|
}
|
|
21886
|
-
console.log(
|
|
21887
|
-
console.log(
|
|
21869
|
+
console.log(pc27.green(`\u2713 Revoked: ${user} / ${world} / ${service}`));
|
|
21870
|
+
console.log(pc27.dim(" Next fetch-creds call returns 403; token cleared on next merge."));
|
|
21888
21871
|
});
|
|
21889
21872
|
}
|
|
21890
21873
|
|
|
@@ -21901,18 +21884,18 @@ function registerMcp(program2) {
|
|
|
21901
21884
|
}
|
|
21902
21885
|
|
|
21903
21886
|
// src/pleri-config.ts
|
|
21904
|
-
import * as
|
|
21905
|
-
import * as
|
|
21887
|
+
import * as fs38 from "node:fs";
|
|
21888
|
+
import * as path42 from "node:path";
|
|
21906
21889
|
function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
21907
21890
|
if (process.env.PLERI_BASE_URL) {
|
|
21908
21891
|
return true;
|
|
21909
21892
|
}
|
|
21910
|
-
const configPath =
|
|
21911
|
-
if (!
|
|
21893
|
+
const configPath = path42.join(configDir, "config.yaml");
|
|
21894
|
+
if (!fs38.existsSync(configPath)) {
|
|
21912
21895
|
return false;
|
|
21913
21896
|
}
|
|
21914
21897
|
try {
|
|
21915
|
-
const contents =
|
|
21898
|
+
const contents = fs38.readFileSync(configPath, "utf8");
|
|
21916
21899
|
return /^[^#\n]*\bpleri:/m.test(contents);
|
|
21917
21900
|
} catch {
|
|
21918
21901
|
return false;
|
|
@@ -21923,14 +21906,14 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
|
|
|
21923
21906
|
var program = new Command();
|
|
21924
21907
|
function readCliVersion() {
|
|
21925
21908
|
try {
|
|
21926
|
-
const here =
|
|
21909
|
+
const here = path43.dirname(fileURLToPath4(import.meta.url));
|
|
21927
21910
|
for (const candidate of [
|
|
21928
|
-
|
|
21929
|
-
|
|
21930
|
-
|
|
21911
|
+
path43.join(here, "package.json"),
|
|
21912
|
+
path43.join(here, "..", "package.json"),
|
|
21913
|
+
path43.join(here, "..", "..", "package.json")
|
|
21931
21914
|
]) {
|
|
21932
|
-
if (
|
|
21933
|
-
const pkg = JSON.parse(
|
|
21915
|
+
if (fs39.existsSync(candidate)) {
|
|
21916
|
+
const pkg = JSON.parse(fs39.readFileSync(candidate, "utf-8"));
|
|
21934
21917
|
if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
|
|
21935
21918
|
}
|
|
21936
21919
|
}
|
|
@@ -21969,4 +21952,5 @@ registerBegin(program);
|
|
|
21969
21952
|
registerStop(program);
|
|
21970
21953
|
registerWorldUpgrade(program);
|
|
21971
21954
|
registerMcp(program);
|
|
21955
|
+
registerServices(program);
|
|
21972
21956
|
program.parse();
|