@pleri/olam-cli 0.1.50 → 0.1.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
4
10
  var __esm = (fn, res) => function __init() {
5
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
12
  };
@@ -454,8 +460,8 @@ var init_parseUtil = __esm({
454
460
  init_errors();
455
461
  init_en();
456
462
  makeIssue = (params) => {
457
- const { data, path: path44, errorMaps, issueData } = params;
458
- const fullPath = [...path44, ...issueData.path || []];
463
+ const { data, path: path45, errorMaps, issueData } = params;
464
+ const fullPath = [...path45, ...issueData.path || []];
459
465
  const fullIssue = {
460
466
  ...issueData,
461
467
  path: fullPath
@@ -763,11 +769,11 @@ var init_types = __esm({
763
769
  init_parseUtil();
764
770
  init_util();
765
771
  ParseInputLazyPath = class {
766
- constructor(parent, value, path44, key) {
772
+ constructor(parent, value, path45, key) {
767
773
  this._cachedPath = [];
768
774
  this.parent = parent;
769
775
  this.data = value;
770
- this._path = path44;
776
+ this._path = path45;
771
777
  this._key = key;
772
778
  }
773
779
  get path() {
@@ -4248,7 +4254,7 @@ import YAML from "yaml";
4248
4254
  function bootstrapStepCmd(entry) {
4249
4255
  return typeof entry === "string" ? entry : entry.cmd;
4250
4256
  }
4251
- function refineForbiddenKeys(value, path44, ctx, rejectSource) {
4257
+ function refineForbiddenKeys(value, path45, ctx, rejectSource) {
4252
4258
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4253
4259
  return;
4254
4260
  }
@@ -4256,12 +4262,12 @@ function refineForbiddenKeys(value, path44, ctx, rejectSource) {
4256
4262
  if (FORBIDDEN_KEYS.has(key)) {
4257
4263
  ctx.addIssue({
4258
4264
  code: external_exports.ZodIssueCode.custom,
4259
- path: [...path44, key],
4265
+ path: [...path45, key],
4260
4266
  message: `forbidden key "${key}" (prototype-pollution surface)`
4261
4267
  });
4262
4268
  continue;
4263
4269
  }
4264
- if (rejectSource && path44.length === 0 && key === "source") {
4270
+ if (rejectSource && path45.length === 0 && key === "source") {
4265
4271
  ctx.addIssue({
4266
4272
  code: external_exports.ZodIssueCode.custom,
4267
4273
  path: ["source"],
@@ -4269,21 +4275,21 @@ function refineForbiddenKeys(value, path44, ctx, rejectSource) {
4269
4275
  });
4270
4276
  continue;
4271
4277
  }
4272
- refineForbiddenKeys(value[key], [...path44, key], ctx, false);
4278
+ refineForbiddenKeys(value[key], [...path45, key], ctx, false);
4273
4279
  }
4274
4280
  }
4275
- function rejectForbiddenKeys(value, path44, rejectSource) {
4281
+ function rejectForbiddenKeys(value, path45, rejectSource) {
4276
4282
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4277
4283
  return;
4278
4284
  }
4279
4285
  for (const key of Object.keys(value)) {
4280
4286
  if (FORBIDDEN_KEYS.has(key)) {
4281
- throw new Error(`[manifest] ${path44}: forbidden key "${key}" (prototype-pollution surface)`);
4287
+ throw new Error(`[manifest] ${path45}: forbidden key "${key}" (prototype-pollution surface)`);
4282
4288
  }
4283
4289
  if (rejectSource && key === "source") {
4284
- throw new Error(`[manifest] ${path44}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4290
+ throw new Error(`[manifest] ${path45}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4285
4291
  }
4286
- rejectForbiddenKeys(value[key], `${path44}.${key}`, false);
4292
+ rejectForbiddenKeys(value[key], `${path45}.${key}`, false);
4287
4293
  }
4288
4294
  }
4289
4295
  function unknownTopLevelKeys(parsed) {
@@ -5224,8 +5230,8 @@ var init_client = __esm({
5224
5230
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
5225
5231
  }
5226
5232
  }
5227
- async request(method, path44, body, attempt = 0) {
5228
- const url = `${this.baseUrl}${path44}`;
5233
+ async request(method, path45, body, attempt = 0) {
5234
+ const url = `${this.baseUrl}${path45}`;
5229
5235
  const controller = new AbortController();
5230
5236
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
5231
5237
  const headers = {};
@@ -5243,7 +5249,7 @@ var init_client = __esm({
5243
5249
  } catch (err) {
5244
5250
  if (attempt < RETRY_COUNT && isTransient(err)) {
5245
5251
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
5246
- return this.request(method, path44, body, attempt + 1);
5252
+ return this.request(method, path45, body, attempt + 1);
5247
5253
  }
5248
5254
  throw err;
5249
5255
  } finally {
@@ -6722,8 +6728,8 @@ var init_provider3 = __esm({
6722
6728
  // -----------------------------------------------------------------------
6723
6729
  // Internal fetch helper
6724
6730
  // -----------------------------------------------------------------------
6725
- async request(path44, method, body) {
6726
- const url = `${this.config.workerUrl}${path44}`;
6731
+ async request(path45, method, body) {
6732
+ const url = `${this.config.workerUrl}${path45}`;
6727
6733
  const bearer = await this.config.mintToken();
6728
6734
  const headers = {
6729
6735
  Authorization: `Bearer ${bearer}`
@@ -7437,14 +7443,25 @@ function generateHooksConfig(hookServerUrl = DEFAULT_HOOK_SERVER_URL) {
7437
7443
  hooks: {
7438
7444
  // Codex adversarial review — fires on every user prompt in-world
7439
7445
  // Uses $HOME so sanitizeContainerClaudeHooks does not strip it
7440
- UserPromptSubmit: [{
7441
- matcher: "",
7442
- hooks: [{
7443
- type: "command",
7444
- command: "$HOME/.claude/scripts/codex-on-prompt.sh",
7445
- timeout: 15
7446
- }]
7447
- }],
7446
+ UserPromptSubmit: [
7447
+ {
7448
+ matcher: "",
7449
+ hooks: [{
7450
+ type: "command",
7451
+ command: "$HOME/.claude/scripts/codex-on-prompt.sh",
7452
+ timeout: 15
7453
+ }]
7454
+ },
7455
+ // C02: surface first-call hint when MCP creds are missing
7456
+ {
7457
+ matcher: "",
7458
+ hooks: [{
7459
+ type: "command",
7460
+ command: "node /opt/olam/scripts/mcp-status-hint.mjs",
7461
+ timeout: 2
7462
+ }]
7463
+ }
7464
+ ],
7448
7465
  // Tool execution events → high-frequency thought capture
7449
7466
  // Layer 1: Upstream Codex review (PreToolUse) — runs after thought capture
7450
7467
  PreToolUse: [
@@ -7502,87 +7519,192 @@ var init_hooks_config = __esm({
7502
7519
  }
7503
7520
  });
7504
7521
 
7505
- // ../core/dist/world/env-setup.js
7506
- import * as crypto2 from "node:crypto";
7522
+ // ../core/dist/world/merge-settings.js
7507
7523
  import * as fs12 from "node:fs";
7508
- import * as os8 from "node:os";
7509
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
+ // ../core/dist/world/env-setup.js
7623
+ import * as crypto3 from "node:crypto";
7624
+ import * as fs13 from "node:fs";
7625
+ import * as os8 from "node:os";
7626
+ import * as path14 from "node:path";
7510
7627
  import { globSync } from "node:fs";
7511
7628
  function copyClaudeConfig(workspacePath, homeDir, configCtx) {
7512
- const sourceClaudeDir = path13.join(homeDir ?? os8.homedir(), ".claude");
7513
- const destClaudeDir = path13.join(workspacePath, ".claude-host-config");
7629
+ const sourceClaudeDir = path14.join(homeDir ?? os8.homedir(), ".claude");
7630
+ const destClaudeDir = path14.join(workspacePath, ".claude-host-config");
7514
7631
  void configCtx;
7515
- if (!fs12.existsSync(sourceClaudeDir))
7632
+ if (!fs13.existsSync(sourceClaudeDir))
7516
7633
  return;
7517
- fs12.mkdirSync(destClaudeDir, { recursive: true });
7518
- const settingsPath = path13.join(sourceClaudeDir, "settings.json");
7634
+ fs13.mkdirSync(destClaudeDir, { recursive: true });
7635
+ const settingsPath = path14.join(sourceClaudeDir, "settings.json");
7519
7636
  let settings = {};
7520
- if (fs12.existsSync(settingsPath)) {
7637
+ if (fs13.existsSync(settingsPath)) {
7521
7638
  try {
7522
- settings = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
7639
+ settings = JSON.parse(fs13.readFileSync(settingsPath, "utf-8"));
7523
7640
  delete settings["hooks"];
7524
7641
  } catch {
7525
7642
  }
7526
7643
  }
7527
7644
  const hooksConfig = generateHooksConfig(DEFAULT_HOOK_SERVER_URL);
7528
7645
  settings["hooks"] = hooksConfig["hooks"];
7529
- fs12.writeFileSync(path13.join(destClaudeDir, "settings.json"), JSON.stringify(settings, null, 2));
7530
- const claudeMdPath = path13.join(sourceClaudeDir, "CLAUDE.md");
7531
- if (fs12.existsSync(claudeMdPath)) {
7532
- fs12.copyFileSync(claudeMdPath, path13.join(destClaudeDir, "CLAUDE.md"));
7646
+ fs13.writeFileSync(path14.join(destClaudeDir, "settings.json"), JSON.stringify(settings, null, 2));
7647
+ const claudeMdPath = path14.join(sourceClaudeDir, "CLAUDE.md");
7648
+ if (fs13.existsSync(claudeMdPath)) {
7649
+ fs13.copyFileSync(claudeMdPath, path14.join(destClaudeDir, "CLAUDE.md"));
7533
7650
  }
7534
- const rulesDir = path13.join(sourceClaudeDir, "rules");
7535
- if (fs12.existsSync(rulesDir)) {
7536
- copyDirRecursive(rulesDir, path13.join(destClaudeDir, "rules"));
7651
+ const rulesDir = path14.join(sourceClaudeDir, "rules");
7652
+ if (fs13.existsSync(rulesDir)) {
7653
+ copyDirRecursive(rulesDir, path14.join(destClaudeDir, "rules"));
7537
7654
  }
7538
- const agentsDir = path13.join(sourceClaudeDir, "agents");
7539
- if (fs12.existsSync(agentsDir)) {
7540
- copyDirRecursive(agentsDir, path13.join(destClaudeDir, "agents"));
7655
+ const agentsDir = path14.join(sourceClaudeDir, "agents");
7656
+ if (fs13.existsSync(agentsDir)) {
7657
+ copyDirRecursive(agentsDir, path14.join(destClaudeDir, "agents"));
7541
7658
  }
7542
- const pluginsDir = path13.join(sourceClaudeDir, "plugins");
7543
- if (fs12.existsSync(pluginsDir)) {
7544
- copyDirRecursive(pluginsDir, path13.join(destClaudeDir, "plugins"), 0, SKIP_FILES);
7659
+ const pluginsDir = path14.join(sourceClaudeDir, "plugins");
7660
+ if (fs13.existsSync(pluginsDir)) {
7661
+ copyDirRecursive(pluginsDir, path14.join(destClaudeDir, "plugins"), 0, SKIP_FILES);
7545
7662
  }
7546
- const skillsDir = path13.join(sourceClaudeDir, "skills");
7547
- if (fs12.existsSync(skillsDir)) {
7548
- copyDirRecursive(skillsDir, path13.join(destClaudeDir, "skills"));
7663
+ const skillsDir = path14.join(sourceClaudeDir, "skills");
7664
+ if (fs13.existsSync(skillsDir)) {
7665
+ copyDirRecursive(skillsDir, path14.join(destClaudeDir, "skills"));
7549
7666
  }
7550
- const scriptsDir = path13.join(sourceClaudeDir, "scripts");
7551
- if (fs12.existsSync(scriptsDir)) {
7552
- copyDirRecursive(scriptsDir, path13.join(destClaudeDir, "scripts"));
7667
+ const scriptsDir = path14.join(sourceClaudeDir, "scripts");
7668
+ if (fs13.existsSync(scriptsDir)) {
7669
+ copyDirRecursive(scriptsDir, path14.join(destClaudeDir, "scripts"));
7553
7670
  }
7554
7671
  applyProjectClaudeOverlay(workspacePath, destClaudeDir);
7555
7672
  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
+ }
7556
7678
  }
7557
7679
  function applyProjectClaudeOverlay(workspacePath, destClaudeDir) {
7558
- const projectClaudeDir = path13.join(workspacePath, ".claude");
7559
- if (!fs12.existsSync(projectClaudeDir))
7680
+ const projectClaudeDir = path14.join(workspacePath, ".claude");
7681
+ if (!fs13.existsSync(projectClaudeDir))
7560
7682
  return;
7561
- const projectSettingsPath = path13.join(projectClaudeDir, "settings.json");
7562
- const destSettingsPath = path13.join(destClaudeDir, "settings.json");
7563
- if (fs12.existsSync(projectSettingsPath)) {
7683
+ const projectSettingsPath = path14.join(projectClaudeDir, "settings.json");
7684
+ const destSettingsPath = path14.join(destClaudeDir, "settings.json");
7685
+ if (fs13.existsSync(projectSettingsPath)) {
7564
7686
  try {
7565
- const projectSettings = JSON.parse(fs12.readFileSync(projectSettingsPath, "utf-8"));
7687
+ const projectSettings = JSON.parse(fs13.readFileSync(projectSettingsPath, "utf-8"));
7566
7688
  delete projectSettings["hooks"];
7567
- const existing = fs12.existsSync(destSettingsPath) ? JSON.parse(fs12.readFileSync(destSettingsPath, "utf-8")) : {};
7689
+ const existing = fs13.existsSync(destSettingsPath) ? JSON.parse(fs13.readFileSync(destSettingsPath, "utf-8")) : {};
7568
7690
  const merged = { ...existing, ...projectSettings, hooks: existing["hooks"] };
7569
- fs12.writeFileSync(destSettingsPath, JSON.stringify(merged, null, 2));
7691
+ fs13.writeFileSync(destSettingsPath, JSON.stringify(merged, null, 2));
7570
7692
  } catch {
7571
7693
  }
7572
7694
  }
7573
- const projectClaudeMd = path13.join(projectClaudeDir, "CLAUDE.md");
7574
- if (fs12.existsSync(projectClaudeMd)) {
7575
- fs12.copyFileSync(projectClaudeMd, path13.join(destClaudeDir, "CLAUDE.md"));
7695
+ const projectClaudeMd = path14.join(projectClaudeDir, "CLAUDE.md");
7696
+ if (fs13.existsSync(projectClaudeMd)) {
7697
+ fs13.copyFileSync(projectClaudeMd, path14.join(destClaudeDir, "CLAUDE.md"));
7576
7698
  }
7577
- const projectAgentsMd = path13.join(projectClaudeDir, "AGENTS.md");
7578
- if (fs12.existsSync(projectAgentsMd)) {
7579
- fs12.copyFileSync(projectAgentsMd, path13.join(destClaudeDir, "AGENTS.md"));
7699
+ const projectAgentsMd = path14.join(projectClaudeDir, "AGENTS.md");
7700
+ if (fs13.existsSync(projectAgentsMd)) {
7701
+ fs13.copyFileSync(projectAgentsMd, path14.join(destClaudeDir, "AGENTS.md"));
7580
7702
  }
7581
7703
  for (const subdir of ["rules", "agents", "plugins", "skills", "scripts"]) {
7582
- const projectSubdir = path13.join(projectClaudeDir, subdir);
7583
- if (fs12.existsSync(projectSubdir)) {
7704
+ const projectSubdir = path14.join(projectClaudeDir, subdir);
7705
+ if (fs13.existsSync(projectSubdir)) {
7584
7706
  const skip = subdir === "plugins" ? SKIP_FILES : /* @__PURE__ */ new Set();
7585
- copyDirRecursive(projectSubdir, path13.join(destClaudeDir, subdir), 0, skip);
7707
+ copyDirRecursive(projectSubdir, path14.join(destClaudeDir, subdir), 0, skip);
7586
7708
  }
7587
7709
  }
7588
7710
  }
@@ -7612,8 +7734,8 @@ function stripMcpServers(mcpServers) {
7612
7734
  return out;
7613
7735
  }
7614
7736
  function writeStrippedMcpServersSnapshot(homeDir, workspacePath, destClaudeDir) {
7615
- const hostMcpServers = readMcpServersFromFile(path13.join(homeDir, ".claude.json"));
7616
- const projectMcpServers = readMcpServersFromFile(path13.join(workspacePath, ".mcp.json"));
7737
+ const hostMcpServers = readMcpServersFromFile(path14.join(homeDir, ".claude.json"));
7738
+ const projectMcpServers = readMcpServersFromFile(path14.join(workspacePath, ".mcp.json"));
7617
7739
  if (Object.keys(hostMcpServers).length === 0 && Object.keys(projectMcpServers).length === 0) {
7618
7740
  return;
7619
7741
  }
@@ -7622,11 +7744,11 @@ function writeStrippedMcpServersSnapshot(homeDir, workspacePath, destClaudeDir)
7622
7744
  ...stripMcpServers(projectMcpServers)
7623
7745
  };
7624
7746
  const output = { mcpServers: stripped };
7625
- fs12.writeFileSync(path13.join(destClaudeDir, ".claude.json"), JSON.stringify(output, null, 2), { mode: 384 });
7747
+ fs13.writeFileSync(path14.join(destClaudeDir, ".claude.json"), JSON.stringify(output, null, 2), { mode: 384 });
7626
7748
  }
7627
7749
  function writeWorldEntitlementsJson(workspacePath, configCtx) {
7628
- const olamDir = path13.join(workspacePath, ".olam");
7629
- fs12.mkdirSync(olamDir, { recursive: true });
7750
+ const olamDir = path14.join(workspacePath, ".olam");
7751
+ fs13.mkdirSync(olamDir, { recursive: true });
7630
7752
  const resolvedRepos = configCtx.repos.map((repo) => {
7631
7753
  const repoEntitlement = repo;
7632
7754
  return {
@@ -7640,14 +7762,14 @@ function writeWorldEntitlementsJson(workspacePath, configCtx) {
7640
7762
  worlds_default: configCtx.worlds_default,
7641
7763
  repos: resolvedRepos
7642
7764
  };
7643
- const filePath = path13.join(olamDir, "world-entitlements.json");
7644
- fs12.writeFileSync(filePath, JSON.stringify(resolved, null, 2), { mode: 420 });
7765
+ const filePath = path14.join(olamDir, "world-entitlements.json");
7766
+ fs13.writeFileSync(filePath, JSON.stringify(resolved, null, 2), { mode: 420 });
7645
7767
  }
7646
7768
  function readMcpServersFromFile(filePath) {
7647
- if (!fs12.existsSync(filePath))
7769
+ if (!fs13.existsSync(filePath))
7648
7770
  return {};
7649
7771
  try {
7650
- const raw = fs12.readFileSync(filePath, "utf-8");
7772
+ const raw = fs13.readFileSync(filePath, "utf-8");
7651
7773
  if (!raw.trim())
7652
7774
  return {};
7653
7775
  const parsed = JSON.parse(raw);
@@ -7662,8 +7784,8 @@ function readMcpServersFromFile(filePath) {
7662
7784
  function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new Set()) {
7663
7785
  if (depth > 10)
7664
7786
  return;
7665
- fs12.mkdirSync(dest, { recursive: true });
7666
- for (const entry of fs12.readdirSync(src, { withFileTypes: true })) {
7787
+ fs13.mkdirSync(dest, { recursive: true });
7788
+ for (const entry of fs13.readdirSync(src, { withFileTypes: true })) {
7667
7789
  const { name } = entry;
7668
7790
  if (skipFiles.has(name))
7669
7791
  continue;
@@ -7673,12 +7795,12 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
7673
7795
  continue;
7674
7796
  if (entry.isDirectory() && SKIP_DIRS.has(name))
7675
7797
  continue;
7676
- const srcPath = path13.join(src, name);
7677
- const destPath = path13.join(dest, name);
7798
+ const srcPath = path14.join(src, name);
7799
+ const destPath = path14.join(dest, name);
7678
7800
  if (entry.isSymbolicLink()) {
7679
7801
  let stat;
7680
7802
  try {
7681
- stat = fs12.statSync(srcPath);
7803
+ stat = fs13.statSync(srcPath);
7682
7804
  } catch {
7683
7805
  continue;
7684
7806
  }
@@ -7687,12 +7809,12 @@ function copyDirRecursive(src, dest, depth = 0, skipFiles = /* @__PURE__ */ new
7687
7809
  continue;
7688
7810
  copyDirRecursive(srcPath, destPath, depth + 1, skipFiles);
7689
7811
  } else if (stat.isFile()) {
7690
- fs12.copyFileSync(srcPath, destPath);
7812
+ fs13.copyFileSync(srcPath, destPath);
7691
7813
  }
7692
7814
  } else if (entry.isDirectory()) {
7693
7815
  copyDirRecursive(srcPath, destPath, depth + 1, skipFiles);
7694
7816
  } else {
7695
- fs12.copyFileSync(srcPath, destPath);
7817
+ fs13.copyFileSync(srcPath, destPath);
7696
7818
  }
7697
7819
  }
7698
7820
  }
@@ -7830,9 +7952,9 @@ function setupWorldEnv(repos, workspacePath, serviceEnv, crossRepoEnv = {}, conf
7830
7952
  }
7831
7953
  }
7832
7954
  for (const repo of repos) {
7833
- const worktreePath = path13.join(workspacePath, repo.name);
7955
+ const worktreePath = path14.join(workspacePath, repo.name);
7834
7956
  const sourcePath = repo.path;
7835
- if (!sourcePath || !fs12.existsSync(worktreePath))
7957
+ if (!sourcePath || !fs13.existsSync(worktreePath))
7836
7958
  continue;
7837
7959
  const envSetup = repo.env_setup;
7838
7960
  if (!envSetup)
@@ -7851,23 +7973,23 @@ function setupWorldEnv(repos, workspacePath, serviceEnv, crossRepoEnv = {}, conf
7851
7973
  }
7852
7974
  }
7853
7975
  function copyMatchingFiles(sourcePath, destPath, pattern) {
7854
- const fullPattern = path13.join(sourcePath, pattern);
7976
+ const fullPattern = path14.join(sourcePath, pattern);
7855
7977
  if (!pattern.includes("*")) {
7856
- const sourceFile = path13.join(sourcePath, pattern);
7857
- const destFile = path13.join(destPath, pattern);
7858
- if (fs12.existsSync(sourceFile)) {
7859
- fs12.mkdirSync(path13.dirname(destFile), { recursive: true });
7860
- fs12.copyFileSync(sourceFile, destFile);
7978
+ const sourceFile = path14.join(sourcePath, pattern);
7979
+ const destFile = path14.join(destPath, pattern);
7980
+ if (fs13.existsSync(sourceFile)) {
7981
+ fs13.mkdirSync(path14.dirname(destFile), { recursive: true });
7982
+ fs13.copyFileSync(sourceFile, destFile);
7861
7983
  }
7862
7984
  return;
7863
7985
  }
7864
7986
  try {
7865
7987
  const matches2 = globSync(fullPattern);
7866
7988
  for (const match2 of matches2) {
7867
- const relative2 = path13.relative(sourcePath, match2);
7868
- const dest = path13.join(destPath, relative2);
7869
- fs12.mkdirSync(path13.dirname(dest), { recursive: true });
7870
- fs12.copyFileSync(match2, dest);
7989
+ const relative2 = path14.relative(sourcePath, match2);
7990
+ const dest = path14.join(destPath, relative2);
7991
+ fs13.mkdirSync(path14.dirname(dest), { recursive: true });
7992
+ fs13.copyFileSync(match2, dest);
7871
7993
  }
7872
7994
  } catch {
7873
7995
  }
@@ -7875,14 +7997,14 @@ function copyMatchingFiles(sourcePath, destPath, pattern) {
7875
7997
  function generateEnvFromExample(repoPath, overrides) {
7876
7998
  const exampleFiles = [".env.example", ".env.sample", ".env.local.example"];
7877
7999
  for (const exampleName of exampleFiles) {
7878
- const examplePath = path13.join(repoPath, exampleName);
7879
- if (!fs12.existsSync(examplePath))
8000
+ const examplePath = path14.join(repoPath, exampleName);
8001
+ if (!fs13.existsSync(examplePath))
7880
8002
  continue;
7881
8003
  const targetName = exampleName.replace(".example", "").replace(".sample", "");
7882
- const targetPath = path13.join(repoPath, targetName);
7883
- if (fs12.existsSync(targetPath))
8004
+ const targetPath = path14.join(repoPath, targetName);
8005
+ if (fs13.existsSync(targetPath))
7884
8006
  continue;
7885
- const template = fs12.readFileSync(examplePath, "utf-8");
8007
+ const template = fs13.readFileSync(examplePath, "utf-8");
7886
8008
  const generated = template.split("\n").map((line) => {
7887
8009
  const match2 = /^([A-Z_][A-Z0-9_]*)=(.*)$/.exec(line);
7888
8010
  if (match2) {
@@ -7893,13 +8015,13 @@ function generateEnvFromExample(repoPath, overrides) {
7893
8015
  }
7894
8016
  return line;
7895
8017
  }).join("\n");
7896
- fs12.writeFileSync(targetPath, generated);
8018
+ fs13.writeFileSync(targetPath, generated);
7897
8019
  }
7898
8020
  }
7899
8021
  function applyEnvOverrides(repoPath, overrides) {
7900
- const envPath = path13.join(repoPath, ".env");
7901
- if (fs12.existsSync(envPath)) {
7902
- const existing = fs12.readFileSync(envPath, "utf-8");
8022
+ const envPath = path14.join(repoPath, ".env");
8023
+ if (fs13.existsSync(envPath)) {
8024
+ const existing = fs13.readFileSync(envPath, "utf-8");
7903
8025
  const existingKeys = new Set(existing.split("\n").map((l) => l.split("=")[0]?.trim()).filter(Boolean));
7904
8026
  const additions = [];
7905
8027
  for (const [key, value] of Object.entries(overrides)) {
@@ -7908,7 +8030,7 @@ function applyEnvOverrides(repoPath, overrides) {
7908
8030
  }
7909
8031
  }
7910
8032
  if (additions.length > 0) {
7911
- fs12.appendFileSync(envPath, "\n# Olam injected\n" + additions.join("\n") + "\n");
8033
+ fs13.appendFileSync(envPath, "\n# Olam injected\n" + additions.join("\n") + "\n");
7912
8034
  }
7913
8035
  }
7914
8036
  }
@@ -7917,6 +8039,7 @@ var init_env_setup = __esm({
7917
8039
  "../core/dist/world/env-setup.js"() {
7918
8040
  "use strict";
7919
8041
  init_hooks_config();
8042
+ init_merge_settings();
7920
8043
  MCP_SERVER_ALLOWLIST = /* @__PURE__ */ new Set([
7921
8044
  "command",
7922
8045
  "args",
@@ -7980,9 +8103,9 @@ var init_env_setup = __esm({
7980
8103
 
7981
8104
  // ../core/dist/world/baseline-diff.js
7982
8105
  import { execFileSync as execFileSync3 } from "node:child_process";
7983
- import * as fs13 from "node:fs";
8106
+ import * as fs14 from "node:fs";
7984
8107
  import * as os9 from "node:os";
7985
- import * as path14 from "node:path";
8108
+ import * as path15 from "node:path";
7986
8109
  function expandHome(p, homedir22) {
7987
8110
  return p.replace(/^~(?=$|\/|\\)/, homedir22());
7988
8111
  }
@@ -8008,9 +8131,9 @@ ${stderr}`;
8008
8131
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8009
8132
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
8010
8133
  const homedir22 = deps.homedir ?? (() => os9.homedir());
8011
- const baselineDir = path14.join(workspacePath, ".olam", "baseline");
8134
+ const baselineDir = path15.join(workspacePath, ".olam", "baseline");
8012
8135
  try {
8013
- fs13.mkdirSync(baselineDir, { recursive: true });
8136
+ fs14.mkdirSync(baselineDir, { recursive: true });
8014
8137
  } catch (err) {
8015
8138
  const msg = err instanceof Error ? err.message : String(err);
8016
8139
  console.warn(`[baseline-diff] mkdir ${baselineDir} failed: ${msg}; reaper will see no baseline at all`);
@@ -8022,9 +8145,9 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8022
8145
  if (!repo.path)
8023
8146
  continue;
8024
8147
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
8025
- const outPath = path14.join(baselineDir, filename);
8148
+ const outPath = path15.join(baselineDir, filename);
8026
8149
  const repoPath = expandHome(repo.path, homedir22);
8027
- if (!fs13.existsSync(repoPath)) {
8150
+ if (!fs14.existsSync(repoPath)) {
8028
8151
  writeBaselineFile(outPath, `# repo: ${repo.name}
8029
8152
  # (skipped: path ${repoPath} does not exist)
8030
8153
  `);
@@ -8091,7 +8214,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8091
8214
  }
8092
8215
  function writeBaselineFile(outPath, content) {
8093
8216
  try {
8094
- fs13.writeFileSync(outPath, content);
8217
+ fs14.writeFileSync(outPath, content);
8095
8218
  } catch (err) {
8096
8219
  const msg = err instanceof Error ? err.message : String(err);
8097
8220
  console.warn(`[baseline-diff] write to ${outPath} failed: ${msg}`);
@@ -8099,8 +8222,8 @@ function writeBaselineFile(outPath, content) {
8099
8222
  }
8100
8223
  function stripWorktreeEdits(repos, workspacePath) {
8101
8224
  for (const repo of repos) {
8102
- const worktreePath = path14.join(workspacePath, repo.name);
8103
- if (!fs13.existsSync(worktreePath))
8225
+ const worktreePath = path15.join(workspacePath, repo.name);
8226
+ if (!fs14.existsSync(worktreePath))
8104
8227
  continue;
8105
8228
  try {
8106
8229
  execFileSync3("git", ["checkout", "--", "."], {
@@ -8137,12 +8260,12 @@ var init_baseline_diff = __esm({
8137
8260
  });
8138
8261
 
8139
8262
  // ../core/dist/world/context-injection.js
8140
- import * as fs14 from "node:fs";
8141
- import * as path15 from "node:path";
8263
+ import * as fs15 from "node:fs";
8264
+ import * as path16 from "node:path";
8142
8265
  function injectWorldContext(opts) {
8143
8266
  const { world, task, linearTicketId, claudeMdExtra, taskContext, services, pleriPlaneUrl } = opts;
8144
- const claudeDir = path15.join(world.workspacePath, ".claude");
8145
- fs14.mkdirSync(claudeDir, { recursive: true });
8267
+ const claudeDir = path16.join(world.workspacePath, ".claude");
8268
+ fs15.mkdirSync(claudeDir, { recursive: true });
8146
8269
  const sections = [];
8147
8270
  sections.push(`# Olam World: ${world.name}`);
8148
8271
  sections.push("");
@@ -8303,7 +8426,7 @@ function injectWorldContext(opts) {
8303
8426
  sections.push("");
8304
8427
  }
8305
8428
  const content = sections.join("\n");
8306
- fs14.writeFileSync(path15.join(claudeDir, "CLAUDE.md"), content);
8429
+ fs15.writeFileSync(path16.join(claudeDir, "CLAUDE.md"), content);
8307
8430
  }
8308
8431
  function formatTaskSource(ctx) {
8309
8432
  if (ctx.source === "linear" && ctx.ticketId) {
@@ -8317,9 +8440,9 @@ function formatTaskSource(ctx) {
8317
8440
  function hasPlanFile(world) {
8318
8441
  if (world.repos.length === 0)
8319
8442
  return false;
8320
- const plansDir = path15.join(world.workspacePath, world.repos[0], "docs", "plans");
8443
+ const plansDir = path16.join(world.workspacePath, world.repos[0], "docs", "plans");
8321
8444
  try {
8322
- return fs14.existsSync(plansDir) && fs14.readdirSync(plansDir).length > 0;
8445
+ return fs15.existsSync(plansDir) && fs15.readdirSync(plansDir).length > 0;
8323
8446
  } catch {
8324
8447
  return false;
8325
8448
  }
@@ -8564,7 +8687,7 @@ var init_stack_install = __esm({
8564
8687
 
8565
8688
  // ../core/dist/world/stack-image.js
8566
8689
  import { execSync as execSync2 } from "node:child_process";
8567
- import * as crypto3 from "node:crypto";
8690
+ import * as crypto4 from "node:crypto";
8568
8691
  function computeFingerprint(runtimes) {
8569
8692
  const parts = [];
8570
8693
  for (const [runtime, versions] of runtimes) {
@@ -8614,14 +8737,14 @@ function getBaseImageDigest() {
8614
8737
  cachedBaseDigest = digest.replace("sha256:", "").slice(0, 16);
8615
8738
  return cachedBaseDigest;
8616
8739
  } catch {
8617
- cachedBaseDigest = crypto3.createHash("sha256").update("unknown").digest("hex").slice(0, 16);
8740
+ cachedBaseDigest = crypto4.createHash("sha256").update("unknown").digest("hex").slice(0, 16);
8618
8741
  return cachedBaseDigest;
8619
8742
  }
8620
8743
  }
8621
8744
  function sanitizeTag(raw) {
8622
8745
  let tag = raw.toLowerCase().replace(/[^a-z0-9._-]/g, "-");
8623
8746
  if (tag.length > MAX_TAG_LENGTH) {
8624
- const hash = crypto3.createHash("sha256").update(raw).digest("hex").slice(0, 12);
8747
+ const hash = crypto4.createHash("sha256").update(raw).digest("hex").slice(0, 12);
8625
8748
  tag = tag.slice(0, MAX_TAG_LENGTH - 13) + "_" + hash;
8626
8749
  }
8627
8750
  return tag;
@@ -9023,14 +9146,14 @@ var init_secrets_fetcher = __esm({
9023
9146
  });
9024
9147
 
9025
9148
  // ../core/dist/world/olam-yaml.js
9026
- import * as path16 from "node:path";
9149
+ import * as path17 from "node:path";
9027
9150
  import YAML2 from "yaml";
9028
9151
  function enrichReposWithManifests(repos, workspacePath) {
9029
9152
  return repos.map((repo) => {
9030
9153
  if (repo.manifest !== void 0 && repo.manifest !== null) {
9031
9154
  return repo;
9032
9155
  }
9033
- const repoDir = path16.join(workspacePath, repo.name);
9156
+ const repoDir = path17.join(workspacePath, repo.name);
9034
9157
  let manifest = null;
9035
9158
  try {
9036
9159
  manifest = loadRepoManifest(repoDir);
@@ -9051,8 +9174,8 @@ var init_olam_yaml = __esm({
9051
9174
  });
9052
9175
 
9053
9176
  // ../core/dist/policies/loader.js
9054
- import * as fs15 from "node:fs";
9055
- import * as path17 from "node:path";
9177
+ import * as fs16 from "node:fs";
9178
+ import * as path18 from "node:path";
9056
9179
  import { parse as parseYaml3 } from "yaml";
9057
9180
  function parseFrontmatter(content) {
9058
9181
  const match2 = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/m.exec(content);
@@ -9072,20 +9195,20 @@ function toStringArray(v) {
9072
9195
  return v.filter((x) => typeof x === "string");
9073
9196
  }
9074
9197
  function loadPolicies(workspaceRoot) {
9075
- const policiesDir = path17.join(workspaceRoot, ".olam", "policies");
9076
- if (!fs15.existsSync(policiesDir))
9198
+ const policiesDir = path18.join(workspaceRoot, ".olam", "policies");
9199
+ if (!fs16.existsSync(policiesDir))
9077
9200
  return [];
9078
9201
  let files;
9079
9202
  try {
9080
- files = fs15.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
9203
+ files = fs16.readdirSync(policiesDir).filter((f) => f.endsWith(".md")).sort();
9081
9204
  } catch {
9082
9205
  return [];
9083
9206
  }
9084
9207
  const policies = [];
9085
9208
  for (const file of files) {
9086
- const filePath = path17.join(policiesDir, file);
9209
+ const filePath = path18.join(policiesDir, file);
9087
9210
  try {
9088
- const content = fs15.readFileSync(filePath, "utf8");
9211
+ const content = fs16.readFileSync(filePath, "utf8");
9089
9212
  const parsed = parseFrontmatter(content);
9090
9213
  if (!parsed) {
9091
9214
  console.warn(`[policies] skipping ${file}: no valid frontmatter block`);
@@ -9249,6 +9372,7 @@ var manager_exports = {};
9249
9372
  __export(manager_exports, {
9250
9373
  AuthPreflightError: () => AuthPreflightError,
9251
9374
  BotIdentityError: () => BotIdentityError,
9375
+ RepoSelectionRequiredError: () => RepoSelectionRequiredError,
9252
9376
  WorkspaceNotFoundError: () => WorkspaceNotFoundError,
9253
9377
  WorldManager: () => WorldManager,
9254
9378
  buildManifestRuntimeForTest: () => buildManifestRuntime,
@@ -9257,11 +9381,11 @@ __export(manager_exports, {
9257
9381
  planManifestPipeline: () => planManifestPipeline,
9258
9382
  runManifestRuntime: () => runManifestRuntime
9259
9383
  });
9260
- import * as crypto4 from "node:crypto";
9384
+ import * as crypto5 from "node:crypto";
9261
9385
  import { execSync as execSync4 } from "node:child_process";
9262
- import * as fs16 from "node:fs";
9386
+ import * as fs17 from "node:fs";
9263
9387
  import * as os10 from "node:os";
9264
- import * as path18 from "node:path";
9388
+ import * as path19 from "node:path";
9265
9389
  import YAML3 from "yaml";
9266
9390
  function getTokenScopes(ghToken, _exec = execSync4) {
9267
9391
  try {
@@ -9343,12 +9467,42 @@ ${stderr.split("\n").slice(0, 3).join(" ")}`);
9343
9467
  dockerExec(`cd ${repoDir} && git checkout -b ${branch}`);
9344
9468
  }
9345
9469
  if (ghToken) {
9470
+ const olamUserPresent = (() => {
9471
+ try {
9472
+ dockerExec(`id olam`);
9473
+ return true;
9474
+ } catch {
9475
+ return false;
9476
+ }
9477
+ })();
9478
+ if (!olamUserPresent) {
9479
+ const imageName = (() => {
9480
+ try {
9481
+ const { execSync: execSync12 } = __require("node:child_process");
9482
+ return execSync12(`docker inspect ${containerName} --format '{{.Config.Image}}'`, {
9483
+ encoding: "utf8",
9484
+ timeout: 5e3
9485
+ }).trim() || "(unknown)";
9486
+ } catch {
9487
+ return "(unknown)";
9488
+ }
9489
+ })();
9490
+ console.warn(`[world] gh auth setup skipped for ${containerName}: container has no \`olam\` user.
9491
+ Image: ${imageName}
9492
+ Likely cause: corrupt local image, a custom devbox image without
9493
+ the olam user (line 10 of devbox.Dockerfile), or NSS resolver
9494
+ failure under cross-arch QEMU emulation.
9495
+ Remedy: docker rmi olam-devbox:latest && olam upgrade -y
9496
+ (or set OLAM_DEVBOX_IMAGE to a known-good ref before \`olam create\`).
9497
+ PR push from inside the world will not work until this is resolved.`);
9498
+ return;
9499
+ }
9346
9500
  let tokenWritten = false;
9347
9501
  try {
9348
9502
  dockerExec(`which gh || (sudo apt-get update -qq && sudo apt-get install -y -qq gh) 2>/dev/null`);
9349
9503
  dockerExec(`printf '%s' '${ghToken.replace(/'/g, "'\\''")}' > /tmp/.gh-token && chmod 600 /tmp/.gh-token`);
9350
9504
  tokenWritten = true;
9351
- dockerExec(`sudo chown olam:olam /home/olam/.config`);
9505
+ dockerExec(`sudo chown olam:olam /home/olam/.config 2>/dev/null || true`);
9352
9506
  dockerExec(`gh auth login --with-token < /tmp/.gh-token`);
9353
9507
  dockerExec(`gh auth setup-git`);
9354
9508
  } catch (err) {
@@ -9570,7 +9724,7 @@ function buildManifestRuntime(worldId, repos) {
9570
9724
  }
9571
9725
  return { worldId, repos: runtimeRepos };
9572
9726
  }
9573
- var BotIdentityError, ADJECTIVES, NOUNS, AuthPreflightError, WorkspaceNotFoundError, WorldManager;
9727
+ var BotIdentityError, ADJECTIVES, NOUNS, AuthPreflightError, WorkspaceNotFoundError, RepoSelectionRequiredError, WorldManager;
9574
9728
  var init_manager = __esm({
9575
9729
  "../core/dist/world/manager.js"() {
9576
9730
  "use strict";
@@ -9619,6 +9773,15 @@ var init_manager = __esm({
9619
9773
  this.name = "WorkspaceNotFoundError";
9620
9774
  }
9621
9775
  };
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
+ };
9622
9785
  WorldManager = class {
9623
9786
  config;
9624
9787
  provider;
@@ -9646,7 +9809,7 @@ var init_manager = __esm({
9646
9809
  }
9647
9810
  }
9648
9811
  const worldId = generateWorldId();
9649
- const workspacePath = path18.join(os10.homedir(), ".olam", "worlds", worldId);
9812
+ const workspacePath = path19.join(os10.homedir(), ".olam", "worlds", worldId);
9650
9813
  const portOffset = this.registry.getNextPortOffset();
9651
9814
  const branch = opts.branchName ?? `olam/${worldId}`;
9652
9815
  const repos = this.resolveReposWithWorkspace(opts);
@@ -9703,37 +9866,37 @@ var init_manager = __esm({
9703
9866
  if (!repo.path)
9704
9867
  continue;
9705
9868
  const sourceRoot = repo.path.replace(/^~/, os10.homedir());
9706
- const worktreeRoot = path18.join(workspacePath, repo.name);
9707
- if (!fs16.existsSync(sourceRoot) || !fs16.existsSync(worktreeRoot))
9869
+ const worktreeRoot = path19.join(workspacePath, repo.name);
9870
+ if (!fs17.existsSync(sourceRoot) || !fs17.existsSync(worktreeRoot))
9708
9871
  continue;
9709
9872
  let copied = 0;
9710
9873
  for (const pattern of RUNTIME_FILE_PATTERNS) {
9711
9874
  const matches2 = [];
9712
9875
  if (pattern.includes("*")) {
9713
- const [dir, glob] = [path18.dirname(pattern), path18.basename(pattern)];
9714
- const sourceDir = path18.join(sourceRoot, dir);
9715
- if (fs16.existsSync(sourceDir)) {
9876
+ const [dir, glob] = [path19.dirname(pattern), path19.basename(pattern)];
9877
+ const sourceDir = path19.join(sourceRoot, dir);
9878
+ if (fs17.existsSync(sourceDir)) {
9716
9879
  const ext2 = glob.replace(/^\*+/, "");
9717
9880
  try {
9718
- for (const entry of fs16.readdirSync(sourceDir)) {
9881
+ for (const entry of fs17.readdirSync(sourceDir)) {
9719
9882
  if (ext2 === "" || entry.endsWith(ext2))
9720
- matches2.push(path18.join(dir, entry));
9883
+ matches2.push(path19.join(dir, entry));
9721
9884
  }
9722
9885
  } catch {
9723
9886
  }
9724
9887
  }
9725
- } else if (fs16.existsSync(path18.join(sourceRoot, pattern))) {
9888
+ } else if (fs17.existsSync(path19.join(sourceRoot, pattern))) {
9726
9889
  matches2.push(pattern);
9727
9890
  }
9728
9891
  for (const rel of matches2) {
9729
- const src = path18.join(sourceRoot, rel);
9730
- const dst = path18.join(worktreeRoot, rel);
9892
+ const src = path19.join(sourceRoot, rel);
9893
+ const dst = path19.join(worktreeRoot, rel);
9731
9894
  try {
9732
- const st = fs16.statSync(src);
9895
+ const st = fs17.statSync(src);
9733
9896
  if (!st.isFile())
9734
9897
  continue;
9735
- fs16.mkdirSync(path18.dirname(dst), { recursive: true });
9736
- fs16.copyFileSync(src, dst);
9898
+ fs17.mkdirSync(path19.dirname(dst), { recursive: true });
9899
+ fs17.copyFileSync(src, dst);
9737
9900
  copied++;
9738
9901
  } catch {
9739
9902
  }
@@ -9833,7 +9996,7 @@ var init_manager = __esm({
9833
9996
  try {
9834
9997
  const hostExec = makeHostExecFn();
9835
9998
  for (const repo of repos) {
9836
- const repoDir = path18.join(workspacePath, repo.name);
9999
+ const repoDir = path19.join(workspacePath, repo.name);
9837
10000
  if (repo.stack && Object.keys(repo.stack).length > 0) {
9838
10001
  preDetectedStacks.set(repo.name, { repoName: repo.name, versions: repo.stack });
9839
10002
  } else {
@@ -9890,10 +10053,10 @@ var init_manager = __esm({
9890
10053
  const worldEnv = {};
9891
10054
  if (opts.task)
9892
10055
  worldEnv.OLAM_TASK = opts.task;
9893
- const r2CredsPath = path18.join(os10.homedir(), ".olam", "r2-credentials.json");
9894
- if (fs16.existsSync(r2CredsPath)) {
10056
+ const r2CredsPath = path19.join(os10.homedir(), ".olam", "r2-credentials.json");
10057
+ if (fs17.existsSync(r2CredsPath)) {
9895
10058
  try {
9896
- const r2Raw = fs16.readFileSync(r2CredsPath, "utf-8").trim();
10059
+ const r2Raw = fs17.readFileSync(r2CredsPath, "utf-8").trim();
9897
10060
  if (r2Raw.length > 0) {
9898
10061
  const r2 = JSON.parse(r2Raw);
9899
10062
  if (typeof r2.account_id === "string")
@@ -9910,10 +10073,10 @@ var init_manager = __esm({
9910
10073
  } catch {
9911
10074
  }
9912
10075
  }
9913
- const keysYamlPath = path18.join(os10.homedir(), ".olam", "keys.yaml");
9914
- if (fs16.existsSync(keysYamlPath)) {
10076
+ const keysYamlPath = path19.join(os10.homedir(), ".olam", "keys.yaml");
10077
+ if (fs17.existsSync(keysYamlPath)) {
9915
10078
  try {
9916
- const keysRaw = fs16.readFileSync(keysYamlPath, "utf-8").trim();
10079
+ const keysRaw = fs17.readFileSync(keysYamlPath, "utf-8").trim();
9917
10080
  if (keysRaw.length > 0) {
9918
10081
  const parsed = YAML3.parse(keysRaw);
9919
10082
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
@@ -9960,10 +10123,10 @@ var init_manager = __esm({
9960
10123
  worldEnv[k] = v;
9961
10124
  }
9962
10125
  for (const { repoName, relativePath, content } of fileWrites) {
9963
- const absPath = path18.join(workspacePath, repoName, relativePath);
10126
+ const absPath = path19.join(workspacePath, repoName, relativePath);
9964
10127
  try {
9965
- fs16.mkdirSync(path18.dirname(absPath), { recursive: true });
9966
- fs16.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
10128
+ fs17.mkdirSync(path19.dirname(absPath), { recursive: true });
10129
+ fs17.writeFileSync(absPath, content.endsWith("\n") ? content : content + "\n", {
9967
10130
  mode: 384
9968
10131
  });
9969
10132
  console.log(`[secrets] ${repoName}: materialised ${relativePath} (${content.length} chars, mode 0600)`);
@@ -10115,7 +10278,7 @@ var init_manager = __esm({
10115
10278
  let taskWithPolicies = opts.task;
10116
10279
  try {
10117
10280
  const allPolicies = repos.flatMap((repo) => {
10118
- const repoWorktree = path18.join(workspacePath, repo.name);
10281
+ const repoWorktree = path19.join(workspacePath, repo.name);
10119
10282
  return loadPolicies(repoWorktree);
10120
10283
  });
10121
10284
  const seen = /* @__PURE__ */ new Set();
@@ -10131,8 +10294,8 @@ var init_manager = __esm({
10131
10294
  ${opts.task}`;
10132
10295
  execSync4(`docker exec ${containerName} mkdir -p /home/olam/.olam/policies`, { stdio: "pipe", timeout: 1e4 });
10133
10296
  for (const repo of repos) {
10134
- const policiesDir = path18.join(workspacePath, repo.name, ".olam", "policies");
10135
- if (fs16.existsSync(policiesDir)) {
10297
+ const policiesDir = path19.join(workspacePath, repo.name, ".olam", "policies");
10298
+ if (fs17.existsSync(policiesDir)) {
10136
10299
  execSync4(`docker cp "${policiesDir}/." "${containerName}:/home/olam/.olam/policies/"`, { stdio: "pipe", timeout: 15e3 });
10137
10300
  }
10138
10301
  }
@@ -10211,8 +10374,8 @@ ${opts.task}`;
10211
10374
  } catch {
10212
10375
  }
10213
10376
  try {
10214
- fs16.rmSync(world.workspacePath, { recursive: true, force: true });
10215
- if (fs16.existsSync(world.workspacePath)) {
10377
+ fs17.rmSync(world.workspacePath, { recursive: true, force: true });
10378
+ if (fs17.existsSync(world.workspacePath)) {
10216
10379
  console.warn(`[WorldManager] destroyWorld(${worldId}): workspace dir ${world.workspacePath} still exists after rmSync. Run \`olam clean --apply\` to reap.`);
10217
10380
  }
10218
10381
  } catch (err) {
@@ -10267,7 +10430,10 @@ ${opts.task}`;
10267
10430
  * Resolution precedence (matches the CF worker exactly):
10268
10431
  * 1. inline `opts.repos` names → look up in `config.repos`
10269
10432
  * 2. named `opts.workspace` → load catalog YAML, map via workspaceToRepoConfigs
10270
- * 3. fallbackevery repo declared in the project's config.yaml
10433
+ * 3. neitherthrow `RepoSelectionRequiredError`. The historical
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.
10271
10437
  */
10272
10438
  resolveReposWithWorkspace(opts) {
10273
10439
  if (opts.repos && opts.repos.length > 0) {
@@ -10279,7 +10445,7 @@ ${opts.task}`;
10279
10445
  throw new WorkspaceNotFoundError(opts.workspace);
10280
10446
  return workspaceToRepoConfigs(ws);
10281
10447
  }
10282
- return this.resolveRepos(void 0);
10448
+ throw new RepoSelectionRequiredError(this.config.repos.map((r) => r.name));
10283
10449
  }
10284
10450
  resolveRepos(repoNames) {
10285
10451
  if (!repoNames || repoNames.length === 0) {
@@ -10297,14 +10463,14 @@ ${opts.task}`;
10297
10463
  return names.map((name) => this.config.repos.find((r) => r.name === name)).filter((r) => r !== void 0);
10298
10464
  }
10299
10465
  transportPlanFile(planFilePath, workspacePath, repoNames) {
10300
- const planContent = fs16.readFileSync(planFilePath, "utf-8");
10301
- const planFileName = path18.basename(planFilePath);
10466
+ const planContent = fs17.readFileSync(planFilePath, "utf-8");
10467
+ const planFileName = path19.basename(planFilePath);
10302
10468
  const targetRepo = repoNames[0];
10303
10469
  if (!targetRepo)
10304
10470
  return;
10305
- const plansDir = path18.join(workspacePath, targetRepo, "docs", "plans");
10306
- fs16.mkdirSync(plansDir, { recursive: true });
10307
- fs16.writeFileSync(path18.join(plansDir, planFileName), planContent);
10471
+ const plansDir = path19.join(workspacePath, targetRepo, "docs", "plans");
10472
+ fs17.mkdirSync(plansDir, { recursive: true });
10473
+ fs17.writeFileSync(path19.join(plansDir, planFileName), planContent);
10308
10474
  }
10309
10475
  resolveServices(repos) {
10310
10476
  const services = [];
@@ -10397,9 +10563,9 @@ var init_tracker = __esm({
10397
10563
  });
10398
10564
 
10399
10565
  // ../core/dist/world-paths.js
10400
- import * as path19 from "node:path";
10566
+ import * as path20 from "node:path";
10401
10567
  function getWorldDbPath(workspacePath) {
10402
- return path19.join(workspacePath, WORLD_DB_FILENAME);
10568
+ return path20.join(workspacePath, WORLD_DB_FILENAME);
10403
10569
  }
10404
10570
  var WORLD_DB_FILENAME;
10405
10571
  var init_world_paths = __esm({
@@ -11017,8 +11183,8 @@ var init_session_aggregator = __esm({
11017
11183
 
11018
11184
  // ../core/dist/dashboard/server.js
11019
11185
  import * as http from "node:http";
11020
- import * as fs17 from "node:fs";
11021
- import * as path20 from "node:path";
11186
+ import * as fs18 from "node:fs";
11187
+ import * as path21 from "node:path";
11022
11188
  import { fileURLToPath as fileURLToPath2 } from "node:url";
11023
11189
  function jsonResponse(res, data, status = 200) {
11024
11190
  res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
@@ -11029,7 +11195,7 @@ function notFound(res) {
11029
11195
  }
11030
11196
  function openThoughtStore(workspacePath) {
11031
11197
  const dbPath = getWorldDbPath(workspacePath);
11032
- if (!fs17.existsSync(dbPath))
11198
+ if (!fs18.existsSync(dbPath))
11033
11199
  return null;
11034
11200
  return new ThoughtLocalStore(dbPath);
11035
11201
  }
@@ -11200,13 +11366,13 @@ function findSessionInWorld(registry, sessionId) {
11200
11366
  }
11201
11367
  function createDashboardServer(opts) {
11202
11368
  const { port, registry } = opts;
11203
- const thisDir = path20.dirname(fileURLToPath2(import.meta.url));
11204
- const defaultPublicDir = path20.resolve(thisDir, "../../../control-plane/public");
11369
+ const thisDir = path21.dirname(fileURLToPath2(import.meta.url));
11370
+ const defaultPublicDir = path21.resolve(thisDir, "../../../control-plane/public");
11205
11371
  const publicDir = opts.publicDir ?? defaultPublicDir;
11206
- let hasPublicDir = fs17.existsSync(publicDir);
11372
+ let hasPublicDir = fs18.existsSync(publicDir);
11207
11373
  const server = http.createServer((req, res) => {
11208
11374
  if (!hasPublicDir) {
11209
- hasPublicDir = fs17.existsSync(publicDir);
11375
+ hasPublicDir = fs18.existsSync(publicDir);
11210
11376
  }
11211
11377
  const host = req.headers.host ?? `localhost:${port}`;
11212
11378
  const url = new URL(req.url ?? "/", `http://${host}`);
@@ -11480,22 +11646,22 @@ function createDashboardServer(opts) {
11480
11646
  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>`);
11481
11647
  return;
11482
11648
  }
11483
- let filePath = path20.join(publicDir, pathname === "/" ? "index.html" : pathname);
11649
+ let filePath = path21.join(publicDir, pathname === "/" ? "index.html" : pathname);
11484
11650
  if (!filePath.startsWith(publicDir)) {
11485
11651
  notFound(res);
11486
11652
  return;
11487
11653
  }
11488
- if (fs17.existsSync(filePath) && fs17.statSync(filePath).isFile()) {
11489
- const ext2 = path20.extname(filePath);
11654
+ if (fs18.existsSync(filePath) && fs18.statSync(filePath).isFile()) {
11655
+ const ext2 = path21.extname(filePath);
11490
11656
  const contentType = MIME[ext2] ?? "application/octet-stream";
11491
11657
  res.writeHead(200, { "Content-Type": contentType });
11492
- fs17.createReadStream(filePath).pipe(res);
11658
+ fs18.createReadStream(filePath).pipe(res);
11493
11659
  return;
11494
11660
  }
11495
- filePath = path20.join(publicDir, "index.html");
11496
- if (fs17.existsSync(filePath)) {
11661
+ filePath = path21.join(publicDir, "index.html");
11662
+ if (fs18.existsSync(filePath)) {
11497
11663
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
11498
- fs17.createReadStream(filePath).pipe(res);
11664
+ fs18.createReadStream(filePath).pipe(res);
11499
11665
  return;
11500
11666
  }
11501
11667
  notFound(res);
@@ -11526,16 +11692,16 @@ var init_server = __esm({
11526
11692
  });
11527
11693
 
11528
11694
  // ../core/dist/dashboard/state.js
11529
- import * as fs18 from "node:fs";
11695
+ import * as fs19 from "node:fs";
11530
11696
  import * as os11 from "node:os";
11531
- import * as path21 from "node:path";
11697
+ import * as path22 from "node:path";
11532
11698
  function saveDashboardState(state) {
11533
- fs18.mkdirSync(path21.dirname(STATE_PATH), { recursive: true });
11534
- fs18.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
11699
+ fs19.mkdirSync(path22.dirname(STATE_PATH), { recursive: true });
11700
+ fs19.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
11535
11701
  }
11536
11702
  function loadDashboardState() {
11537
11703
  try {
11538
- const raw = fs18.readFileSync(STATE_PATH, "utf-8");
11704
+ const raw = fs19.readFileSync(STATE_PATH, "utf-8");
11539
11705
  return JSON.parse(raw);
11540
11706
  } catch {
11541
11707
  return null;
@@ -11543,7 +11709,7 @@ function loadDashboardState() {
11543
11709
  }
11544
11710
  function clearDashboardState() {
11545
11711
  try {
11546
- fs18.unlinkSync(STATE_PATH);
11712
+ fs19.unlinkSync(STATE_PATH);
11547
11713
  } catch {
11548
11714
  }
11549
11715
  }
@@ -11563,7 +11729,7 @@ var STATE_PATH;
11563
11729
  var init_state2 = __esm({
11564
11730
  "../core/dist/dashboard/state.js"() {
11565
11731
  "use strict";
11566
- STATE_PATH = path21.join(os11.homedir(), ".olam", "dashboard.json");
11732
+ STATE_PATH = path22.join(os11.homedir(), ".olam", "dashboard.json");
11567
11733
  }
11568
11734
  });
11569
11735
 
@@ -11957,51 +12123,51 @@ __export(host_cp_exports, {
11957
12123
  writePid: () => writePid,
11958
12124
  writeToken: () => writeToken
11959
12125
  });
11960
- import * as crypto5 from "node:crypto";
11961
- import * as fs19 from "node:fs";
12126
+ import * as crypto6 from "node:crypto";
12127
+ import * as fs20 from "node:fs";
11962
12128
  import * as os12 from "node:os";
11963
- import * as path22 from "node:path";
12129
+ import * as path23 from "node:path";
11964
12130
  import { spawnSync as spawnSync4 } from "node:child_process";
11965
12131
  import Dockerode2 from "dockerode";
11966
12132
  function findComposeFile() {
11967
12133
  const candidates = [
11968
12134
  // Bundled path: dist/index.js lives at <pkg>/dist/; host-cp/ is a sibling of dist/
11969
- path22.resolve(path22.dirname(new URL(import.meta.url).pathname), "../host-cp/compose.yaml"),
12135
+ path23.resolve(path23.dirname(new URL(import.meta.url).pathname), "../host-cp/compose.yaml"),
11970
12136
  // Source-mode: cwd is monorepo root
11971
- path22.resolve(process.cwd(), "packages/host-cp/compose.yaml"),
12137
+ path23.resolve(process.cwd(), "packages/host-cp/compose.yaml"),
11972
12138
  // Source-mode: cwd is one level inside the monorepo
11973
- path22.resolve(process.cwd(), "../packages/host-cp/compose.yaml")
12139
+ path23.resolve(process.cwd(), "../packages/host-cp/compose.yaml")
11974
12140
  ];
11975
12141
  for (const c of candidates) {
11976
- if (fs19.existsSync(c)) return c;
12142
+ if (fs20.existsSync(c)) return c;
11977
12143
  }
11978
- return path22.resolve(process.cwd(), "packages/host-cp/compose.yaml");
12144
+ return path23.resolve(process.cwd(), "packages/host-cp/compose.yaml");
11979
12145
  }
11980
12146
  function olamHome() {
11981
- return process.env.OLAM_HOME ?? path22.join(os12.homedir(), ".olam");
12147
+ return process.env.OLAM_HOME ?? path23.join(os12.homedir(), ".olam");
11982
12148
  }
11983
12149
  function tokenPath() {
11984
- return path22.join(olamHome(), "host-cp.token");
12150
+ return path23.join(olamHome(), "host-cp.token");
11985
12151
  }
11986
12152
  function pidPath() {
11987
- return path22.join(olamHome(), "host-cp.pid");
12153
+ return path23.join(olamHome(), "host-cp.pid");
11988
12154
  }
11989
12155
  function authSecretPath() {
11990
- return path22.join(olamHome(), "auth-secret");
12156
+ return path23.join(olamHome(), "auth-secret");
11991
12157
  }
11992
12158
  function readAuthSecret2() {
11993
12159
  const filePath = authSecretPath();
11994
- if (!fs19.existsSync(filePath)) return null;
11995
- const raw = fs19.readFileSync(filePath, "utf-8").trim();
12160
+ if (!fs20.existsSync(filePath)) return null;
12161
+ const raw = fs20.readFileSync(filePath, "utf-8").trim();
11996
12162
  return raw.length > 0 ? raw : null;
11997
12163
  }
11998
12164
  function r2CredentialsPath() {
11999
- return path22.join(olamHome(), "r2-credentials.json");
12165
+ return path23.join(olamHome(), "r2-credentials.json");
12000
12166
  }
12001
12167
  function readR2Credentials() {
12002
12168
  const filePath = r2CredentialsPath();
12003
- if (!fs19.existsSync(filePath)) return null;
12004
- const raw = fs19.readFileSync(filePath, "utf-8").trim();
12169
+ if (!fs20.existsSync(filePath)) return null;
12170
+ const raw = fs20.readFileSync(filePath, "utf-8").trim();
12005
12171
  if (raw.length === 0) return null;
12006
12172
  try {
12007
12173
  const parsed = JSON.parse(raw);
@@ -12022,39 +12188,39 @@ function readR2Credentials() {
12022
12188
  }
12023
12189
  }
12024
12190
  function writeToken() {
12025
- const token = crypto5.randomBytes(32).toString("hex");
12191
+ const token = crypto6.randomBytes(32).toString("hex");
12026
12192
  const filePath = tokenPath();
12027
- fs19.mkdirSync(path22.dirname(filePath), { recursive: true });
12028
- fs19.writeFileSync(filePath, token, { mode: 384 });
12193
+ fs20.mkdirSync(path23.dirname(filePath), { recursive: true });
12194
+ fs20.writeFileSync(filePath, token, { mode: 384 });
12029
12195
  return token;
12030
12196
  }
12031
12197
  function readToken() {
12032
12198
  const filePath = tokenPath();
12033
- if (!fs19.existsSync(filePath)) return null;
12034
- return fs19.readFileSync(filePath, "utf-8").trim();
12199
+ if (!fs20.existsSync(filePath)) return null;
12200
+ return fs20.readFileSync(filePath, "utf-8").trim();
12035
12201
  }
12036
12202
  function removeToken() {
12037
12203
  const filePath = tokenPath();
12038
- if (!fs19.existsSync(filePath)) return false;
12039
- fs19.unlinkSync(filePath);
12204
+ if (!fs20.existsSync(filePath)) return false;
12205
+ fs20.unlinkSync(filePath);
12040
12206
  return true;
12041
12207
  }
12042
12208
  function writePid(pid) {
12043
12209
  const filePath = pidPath();
12044
- fs19.mkdirSync(path22.dirname(filePath), { recursive: true });
12045
- fs19.writeFileSync(filePath, String(pid), { mode: 420 });
12210
+ fs20.mkdirSync(path23.dirname(filePath), { recursive: true });
12211
+ fs20.writeFileSync(filePath, String(pid), { mode: 420 });
12046
12212
  }
12047
12213
  function readPid() {
12048
12214
  const filePath = pidPath();
12049
- if (!fs19.existsSync(filePath)) return null;
12050
- const raw = fs19.readFileSync(filePath, "utf-8").trim();
12215
+ if (!fs20.existsSync(filePath)) return null;
12216
+ const raw = fs20.readFileSync(filePath, "utf-8").trim();
12051
12217
  const n = parseInt(raw, 10);
12052
12218
  return Number.isFinite(n) ? n : null;
12053
12219
  }
12054
12220
  function removePid() {
12055
12221
  const filePath = pidPath();
12056
- if (!fs19.existsSync(filePath)) return false;
12057
- fs19.unlinkSync(filePath);
12222
+ if (!fs20.existsSync(filePath)) return false;
12223
+ fs20.unlinkSync(filePath);
12058
12224
  return true;
12059
12225
  }
12060
12226
  async function findHostCpContainer() {
@@ -12213,7 +12379,7 @@ async function handleStart(opts) {
12213
12379
  }
12214
12380
  const token = writeToken();
12215
12381
  const composeFile = findComposeFile();
12216
- if (!fs19.existsSync(composeFile)) {
12382
+ if (!fs20.existsSync(composeFile)) {
12217
12383
  printError(`compose.yaml not found at ${composeFile}. Run from the olam project root.`);
12218
12384
  removeToken();
12219
12385
  process.exitCode = 1;
@@ -12295,7 +12461,7 @@ async function stopHostCp() {
12295
12461
  }
12296
12462
  async function handleStop() {
12297
12463
  const composeFile = findComposeFile();
12298
- if (!fs19.existsSync(composeFile)) {
12464
+ if (!fs20.existsSync(composeFile)) {
12299
12465
  printWarning(`compose.yaml not found at ${composeFile}. Cleaning up token + PID anyway.`);
12300
12466
  removeToken();
12301
12467
  removePid();
@@ -12323,13 +12489,13 @@ async function buildStatusReport() {
12323
12489
  const container = await findHostCpContainer();
12324
12490
  const health = await probeHealth();
12325
12491
  const tokenFile = tokenPath();
12326
- const tokenPresent = fs19.existsSync(tokenFile);
12492
+ const tokenPresent = fs20.existsSync(tokenFile);
12327
12493
  let tokenModeOk = false;
12328
12494
  if (tokenPresent) {
12329
- const mode = fs19.statSync(tokenFile).mode & 511;
12495
+ const mode = fs20.statSync(tokenFile).mode & 511;
12330
12496
  tokenModeOk = mode === 384;
12331
12497
  }
12332
- const pidPresent = fs19.existsSync(pidPath());
12498
+ const pidPresent = fs20.existsSync(pidPath());
12333
12499
  let stack;
12334
12500
  if (!container) {
12335
12501
  stack = "not_started";
@@ -12417,13 +12583,13 @@ async function discoverWorldPort(worldId) {
12417
12583
  }
12418
12584
  async function readHostCpToken2() {
12419
12585
  const tp = tokenPath();
12420
- if (!fs19.existsSync(tp)) return null;
12421
- return fs19.readFileSync(tp, "utf-8").trim();
12586
+ if (!fs20.existsSync(tp)) return null;
12587
+ return fs20.readFileSync(tp, "utf-8").trim();
12422
12588
  }
12423
- async function callHostCpProxy(method, worldId, path44, body) {
12589
+ async function callHostCpProxy(method, worldId, path45, body) {
12424
12590
  const token = await readHostCpToken2();
12425
12591
  if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
12426
- const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path44}`;
12592
+ const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path45}`;
12427
12593
  try {
12428
12594
  const headers = {
12429
12595
  Authorization: `Bearer ${token}`
@@ -12548,17 +12714,17 @@ __export(install_root_exports, {
12548
12714
  isDevMode: () => isDevMode,
12549
12715
  resolveBuildScript: () => resolveBuildScript
12550
12716
  });
12551
- import { existsSync as existsSync18 } from "node:fs";
12552
- import { dirname as dirname13, join as join24, resolve as resolve6 } from "node:path";
12717
+ import { existsSync as existsSync19 } from "node:fs";
12718
+ import { dirname as dirname14, join as join24, resolve as resolve6 } from "node:path";
12553
12719
  import { fileURLToPath as fileURLToPath3 } from "node:url";
12554
12720
  function installRoot(metaUrl = import.meta.url) {
12555
12721
  const here = fileURLToPath3(metaUrl);
12556
- return resolve6(dirname13(here), "..");
12722
+ return resolve6(dirname14(here), "..");
12557
12723
  }
12558
12724
  function isDevMode(env = process.env, installRootDir = installRoot()) {
12559
12725
  if (env.OLAM_DEV !== "1") return false;
12560
12726
  const repoRoot = resolve6(installRootDir, "..", "..");
12561
- return existsSync18(join24(repoRoot, "packages")) && existsSync18(join24(repoRoot, "package.json"));
12727
+ return existsSync19(join24(repoRoot, "packages")) && existsSync19(join24(repoRoot, "package.json"));
12562
12728
  }
12563
12729
  function resolveBuildScript(input) {
12564
12730
  const { scriptRelPath, env = process.env, installRootDir = installRoot() } = input;
@@ -12737,20 +12903,20 @@ var init_registry_allowlist = __esm({
12737
12903
  });
12738
12904
 
12739
12905
  // ../core/dist/world/world-yaml.js
12740
- import * as fs21 from "node:fs";
12741
- import * as path24 from "node:path";
12906
+ import * as fs22 from "node:fs";
12907
+ import * as path25 from "node:path";
12742
12908
  import { parse as parseYaml4, stringify as stringifyYaml4 } from "yaml";
12743
12909
  function writeWorldYaml(worldPath, data) {
12744
- const olamDir = path24.join(worldPath, ".olam");
12745
- fs21.mkdirSync(olamDir, { recursive: true });
12746
- fs21.writeFileSync(path24.join(olamDir, "world.yaml"), stringifyYaml4(data), "utf-8");
12910
+ const olamDir = path25.join(worldPath, ".olam");
12911
+ fs22.mkdirSync(olamDir, { recursive: true });
12912
+ fs22.writeFileSync(path25.join(olamDir, "world.yaml"), stringifyYaml4(data), "utf-8");
12747
12913
  }
12748
12914
  function readWorldYaml(worldPath) {
12749
- const yamlPath = path24.join(worldPath, ".olam", "world.yaml");
12750
- if (!fs21.existsSync(yamlPath))
12915
+ const yamlPath = path25.join(worldPath, ".olam", "world.yaml");
12916
+ if (!fs22.existsSync(yamlPath))
12751
12917
  return null;
12752
12918
  try {
12753
- const raw = fs21.readFileSync(yamlPath, "utf-8");
12919
+ const raw = fs22.readFileSync(yamlPath, "utf-8");
12754
12920
  const parsed = parseYaml4(raw);
12755
12921
  return WorldYamlSchema.parse(parsed);
12756
12922
  } catch {
@@ -12881,16 +13047,16 @@ __export(machine_schema_exports, {
12881
13047
  readMachineConfig: () => readMachineConfig,
12882
13048
  writeMachineConfig: () => writeMachineConfig
12883
13049
  });
12884
- import * as fs35 from "node:fs";
12885
- import * as path39 from "node:path";
13050
+ import * as fs36 from "node:fs";
13051
+ import * as path40 from "node:path";
12886
13052
  import * as os21 from "node:os";
12887
13053
  import { parse as parseYaml5, stringify as stringifyYaml5 } from "yaml";
12888
13054
  function readMachineConfig(configPath) {
12889
13055
  const p = configPath ?? DEFAULT_CONFIG_PATH;
12890
- if (!fs35.existsSync(p))
13056
+ if (!fs36.existsSync(p))
12891
13057
  return null;
12892
13058
  try {
12893
- const raw = fs35.readFileSync(p, "utf-8");
13059
+ const raw = fs36.readFileSync(p, "utf-8");
12894
13060
  const parsed = parseYaml5(raw);
12895
13061
  return MachineConfigSchema.parse(parsed);
12896
13062
  } catch {
@@ -12899,8 +13065,8 @@ function readMachineConfig(configPath) {
12899
13065
  }
12900
13066
  function writeMachineConfig(config, configPath) {
12901
13067
  const p = configPath ?? DEFAULT_CONFIG_PATH;
12902
- fs35.mkdirSync(path39.dirname(p), { recursive: true });
12903
- fs35.writeFileSync(p, stringifyYaml5({ ...config }), { mode: 420 });
13068
+ fs36.mkdirSync(path40.dirname(p), { recursive: true });
13069
+ fs36.writeFileSync(p, stringifyYaml5({ ...config }), { mode: 420 });
12904
13070
  }
12905
13071
  function initMachineConfig(opts = {}) {
12906
13072
  const configPath = opts.configPath ?? DEFAULT_CONFIG_PATH;
@@ -12923,16 +13089,16 @@ var init_machine_schema = __esm({
12923
13089
  channel: external_exports.enum(["stable", "beta", "edge"]).default("stable"),
12924
13090
  auto_update: external_exports.boolean().default(true),
12925
13091
  telemetry: external_exports.boolean().default(true),
12926
- worlds_dir: external_exports.string().default(() => path39.join(os21.homedir(), ".olam", "worlds"))
13092
+ worlds_dir: external_exports.string().default(() => path40.join(os21.homedir(), ".olam", "worlds"))
12927
13093
  });
12928
- DEFAULT_CONFIG_PATH = path39.join(os21.homedir(), ".olam", "config.yaml");
13094
+ DEFAULT_CONFIG_PATH = path40.join(os21.homedir(), ".olam", "config.yaml");
12929
13095
  }
12930
13096
  });
12931
13097
 
12932
13098
  // src/index.ts
12933
13099
  import { Command } from "commander";
12934
- import * as fs39 from "node:fs";
12935
- import * as path43 from "node:path";
13100
+ import * as fs40 from "node:fs";
13101
+ import * as path44 from "node:path";
12936
13102
  import { fileURLToPath as fileURLToPath4 } from "node:url";
12937
13103
 
12938
13104
  // src/commands/init.ts
@@ -13309,9 +13475,9 @@ var UnknownArchetypeError = class extends Error {
13309
13475
  };
13310
13476
  var ArchetypeCycleError = class extends Error {
13311
13477
  path;
13312
- constructor(path44) {
13313
- super(`Archetype inheritance cycle detected: ${path44.join(" \u2192 ")} \u2192 ${path44[0] ?? "?"}`);
13314
- this.path = path44;
13478
+ constructor(path45) {
13479
+ super(`Archetype inheritance cycle detected: ${path45.join(" \u2192 ")} \u2192 ${path45[0] ?? "?"}`);
13480
+ this.path = path45;
13315
13481
  this.name = "ArchetypeCycleError";
13316
13482
  }
13317
13483
  };
@@ -13680,8 +13846,8 @@ async function runAuthStatus(getStatus) {
13680
13846
  init_output();
13681
13847
  init_host_cp();
13682
13848
  init_auth();
13683
- import * as fs20 from "node:fs";
13684
- import * as path23 from "node:path";
13849
+ import * as fs21 from "node:fs";
13850
+ import * as path24 from "node:path";
13685
13851
  import { spawnSync as spawnSync7 } from "node:child_process";
13686
13852
  import ora2 from "ora";
13687
13853
  import pc7 from "picocolors";
@@ -13692,20 +13858,20 @@ init_exit_codes();
13692
13858
  init_protocol_version();
13693
13859
  init_output();
13694
13860
  import { spawn as spawn2, spawnSync as spawnSync6 } from "node:child_process";
13695
- import { existsSync as existsSync19, readFileSync as readFileSync15 } from "node:fs";
13861
+ import { existsSync as existsSync20, readFileSync as readFileSync16 } from "node:fs";
13696
13862
  import { join as join25 } from "node:path";
13697
13863
  import ora from "ora";
13698
13864
  import pc6 from "picocolors";
13699
13865
  function loadImageDigests(installRootDir = installRoot()) {
13700
13866
  const digestsPath = join25(installRootDir, "dist", "image-digests.json");
13701
- if (!existsSync19(digestsPath)) {
13867
+ if (!existsSync20(digestsPath)) {
13702
13868
  throw new Error(
13703
13869
  `image-digests.json missing at ${digestsPath}. Re-run \`npm install -g @pleri/olam-cli@<version>\` to refresh the tarball.`
13704
13870
  );
13705
13871
  }
13706
13872
  let parsed;
13707
13873
  try {
13708
- parsed = JSON.parse(readFileSync15(digestsPath, "utf8"));
13874
+ parsed = JSON.parse(readFileSync16(digestsPath, "utf8"));
13709
13875
  } catch (err) {
13710
13876
  throw new Error(`image-digests.json is not valid JSON: ${err.message}`);
13711
13877
  }
@@ -14005,8 +14171,8 @@ function parseAuthUpgradeOpts(raw) {
14005
14171
  };
14006
14172
  }
14007
14173
  function validateAuthRepoRoot(cwd) {
14008
- const marker = path23.join(cwd, "packages/auth-service/Dockerfile");
14009
- if (!fs20.existsSync(marker)) {
14174
+ const marker = path24.join(cwd, "packages/auth-service/Dockerfile");
14175
+ if (!fs21.existsSync(marker)) {
14010
14176
  return {
14011
14177
  ok: false,
14012
14178
  error: `Not an olam repo root (expected ${marker}).
@@ -14579,14 +14745,14 @@ ${pc8.dim("Next: olam create --name my-world")}`);
14579
14745
  // src/commands/create.ts
14580
14746
  init_manager();
14581
14747
  import { spawnSync as spawnSync8 } from "node:child_process";
14582
- import { existsSync as existsSync22 } from "node:fs";
14583
- import { dirname as dirname14, resolve as resolve7 } from "node:path";
14748
+ import { existsSync as existsSync23 } from "node:fs";
14749
+ import { dirname as dirname15, resolve as resolve7 } from "node:path";
14584
14750
  import ora3 from "ora";
14585
14751
  import pc9 from "picocolors";
14586
14752
 
14587
14753
  // ../core/dist/world/devbox-freshness.js
14588
14754
  import { execSync as execSync6 } from "node:child_process";
14589
- import { existsSync as existsSync21, statSync as statSync5 } from "node:fs";
14755
+ import { existsSync as existsSync22, statSync as statSync5 } from "node:fs";
14590
14756
  import { join as join27 } from "node:path";
14591
14757
  var DEFAULT_DEVBOX_IMAGE = "olam-devbox:latest";
14592
14758
  var DEVBOX_BAKED_SOURCES = [
@@ -14595,6 +14761,7 @@ var DEVBOX_BAKED_SOURCES = [
14595
14761
  "packages/control-plane/standalone/intelligence-bridge.mjs",
14596
14762
  "packages/control-plane/standalone/d1-sqlite-adapter.mjs",
14597
14763
  "packages/adapters/src/docker/scripts/fetch-creds.mjs",
14764
+ "packages/adapters/src/docker/scripts/mcp-status-hint.mjs",
14598
14765
  "packages/adapters/src/docker/scripts/pr-gate-hook.mjs",
14599
14766
  "packages/adapters/src/docker/scripts/entrypoint.sh"
14600
14767
  ];
@@ -14654,9 +14821,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
14654
14821
  "These source files have changed since the image was built; the",
14655
14822
  "changes will NOT take effect in fresh worlds until you rebuild:"
14656
14823
  ];
14657
- for (const { path: path44, mtimeMs } of result.newerSources) {
14824
+ for (const { path: path45, mtimeMs } of result.newerSources) {
14658
14825
  const when = new Date(mtimeMs).toISOString();
14659
- lines.push(` \u2022 ${path44} (modified ${when})`);
14826
+ lines.push(` \u2022 ${path45} (modified ${when})`);
14660
14827
  }
14661
14828
  lines.push("");
14662
14829
  lines.push("Rebuild with:");
@@ -14675,7 +14842,7 @@ function defaultDockerInspect(image) {
14675
14842
  }
14676
14843
  function defaultStatMtime(absPath) {
14677
14844
  try {
14678
- if (!existsSync21(absPath))
14845
+ if (!existsSync22(absPath))
14679
14846
  return null;
14680
14847
  return statSync5(absPath).mtimeMs;
14681
14848
  } catch {
@@ -14815,15 +14982,15 @@ init_host_cp();
14815
14982
  var HOST_CP_URL = "http://127.0.0.1:19000";
14816
14983
  async function readHostCpTokenForCreate() {
14817
14984
  try {
14818
- const { default: fs40 } = await import("node:fs");
14985
+ const { default: fs41 } = await import("node:fs");
14819
14986
  const { default: os24 } = await import("node:os");
14820
- const { default: path44 } = await import("node:path");
14821
- const tp = path44.join(
14822
- process.env.OLAM_HOME ?? path44.join(os24.homedir(), ".olam"),
14987
+ const { default: path45 } = await import("node:path");
14988
+ const tp = path45.join(
14989
+ process.env.OLAM_HOME ?? path45.join(os24.homedir(), ".olam"),
14823
14990
  "host-cp.token"
14824
14991
  );
14825
- if (!fs40.existsSync(tp)) return null;
14826
- return fs40.readFileSync(tp, "utf-8").trim();
14992
+ if (!fs41.existsSync(tp)) return null;
14993
+ return fs41.readFileSync(tp, "utf-8").trim();
14827
14994
  } catch {
14828
14995
  return null;
14829
14996
  }
@@ -15161,6 +15328,13 @@ ${pc9.cyan("Host CP UI:")} ${worldUrl}`);
15161
15328
  if (err instanceof AuthPreflightError) {
15162
15329
  printError(err.message);
15163
15330
  if (err.remedy) console.log(` ${pc9.dim(err.remedy)}`);
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
+ }
15164
15338
  } else {
15165
15339
  printError(err instanceof Error ? err.message : String(err));
15166
15340
  }
@@ -15171,10 +15345,10 @@ ${pc9.cyan("Host CP UI:")} ${worldUrl}`);
15171
15345
  function resolveRepoRoot(start) {
15172
15346
  let cur = start;
15173
15347
  while (true) {
15174
- if (existsSync22(resolve7(cur, "packages")) && existsSync22(resolve7(cur, "package.json"))) {
15348
+ if (existsSync23(resolve7(cur, "packages")) && existsSync23(resolve7(cur, "package.json"))) {
15175
15349
  return cur;
15176
15350
  }
15177
- const parent = dirname14(cur);
15351
+ const parent = dirname15(cur);
15178
15352
  if (parent === cur) return start;
15179
15353
  cur = parent;
15180
15354
  }
@@ -15185,12 +15359,12 @@ function defaultNameFromPrompt(prompt) {
15185
15359
  }
15186
15360
  async function readHostCpToken3() {
15187
15361
  try {
15188
- const { default: fs40 } = await import("node:fs");
15362
+ const { default: fs41 } = await import("node:fs");
15189
15363
  const { default: os24 } = await import("node:os");
15190
- const { default: path44 } = await import("node:path");
15191
- const tp = path44.join(os24.homedir(), ".olam", "host-cp.token");
15192
- if (!fs40.existsSync(tp)) return null;
15193
- const raw = fs40.readFileSync(tp, "utf-8").trim();
15364
+ const { default: path45 } = await import("node:path");
15365
+ const tp = path45.join(os24.homedir(), ".olam", "host-cp.token");
15366
+ if (!fs41.existsSync(tp)) return null;
15367
+ const raw = fs41.readFileSync(tp, "utf-8").trim();
15194
15368
  return raw.length > 0 ? raw : null;
15195
15369
  } catch {
15196
15370
  return null;
@@ -15402,9 +15576,9 @@ function registerList(program2) {
15402
15576
 
15403
15577
  // src/commands/status.ts
15404
15578
  init_output();
15405
- import * as fs22 from "node:fs";
15579
+ import * as fs23 from "node:fs";
15406
15580
  import * as os13 from "node:os";
15407
- import * as path25 from "node:path";
15581
+ import * as path26 from "node:path";
15408
15582
  var CLI_VERSION2 = process.env["OLAM_CLI_VERSION"] ?? "0.0.0";
15409
15583
  var HOST_CP_PORT2 = 19e3;
15410
15584
  async function getMachineStatus(_probe, _loadCtx, _readToken) {
@@ -15434,14 +15608,14 @@ async function getMachineStatus(_probe, _loadCtx, _readToken) {
15434
15608
  }
15435
15609
  } catch {
15436
15610
  }
15437
- const manifestPath2 = path25.join(os13.homedir(), ".olam", "cache", "manifest.json");
15611
+ const manifestPath2 = path26.join(os13.homedir(), ".olam", "cache", "manifest.json");
15438
15612
  let updateAvailable = null;
15439
15613
  let lastUpdateCheck = null;
15440
- if (fs22.existsSync(manifestPath2)) {
15441
- const mtime = fs22.statSync(manifestPath2).mtime;
15614
+ if (fs23.existsSync(manifestPath2)) {
15615
+ const mtime = fs23.statSync(manifestPath2).mtime;
15442
15616
  lastUpdateCheck = mtime.toISOString();
15443
15617
  try {
15444
- const manifest = JSON.parse(fs22.readFileSync(manifestPath2, "utf-8"));
15618
+ const manifest = JSON.parse(fs23.readFileSync(manifestPath2, "utf-8"));
15445
15619
  const latest = manifest["version"];
15446
15620
  updateAvailable = latest !== void 0 && latest !== CLI_VERSION2;
15447
15621
  } catch {
@@ -15564,9 +15738,9 @@ function registerDestroy(program2) {
15564
15738
  // src/commands/clean.ts
15565
15739
  init_context();
15566
15740
  init_output();
15567
- import fs23 from "node:fs";
15741
+ import fs24 from "node:fs";
15568
15742
  import os14 from "node:os";
15569
- import path26 from "node:path";
15743
+ import path27 from "node:path";
15570
15744
  import { execFileSync as execFileSync4 } from "node:child_process";
15571
15745
  function registerClean(program2) {
15572
15746
  program2.command("clean").description("Reap orphan world filesystem state under ~/.olam/worlds/").option("--apply", "Actually delete the orphans (default is dry-run)", false).option(
@@ -15590,8 +15764,8 @@ async function runClean(opts) {
15590
15764
  printError(error?.message ?? "Olam is not configured. Run `olam init` first.");
15591
15765
  return 1;
15592
15766
  }
15593
- const worldsDir = path26.join(os14.homedir(), ".olam", "worlds");
15594
- if (!fs23.existsSync(worldsDir)) {
15767
+ const worldsDir = path27.join(os14.homedir(), ".olam", "worlds");
15768
+ if (!fs24.existsSync(worldsDir)) {
15595
15769
  if (opts.json) {
15596
15770
  process.stdout.write(`${JSON.stringify({ worldsDir, entries: [] })}
15597
15771
  `);
@@ -15606,8 +15780,8 @@ async function runClean(opts) {
15606
15780
  }
15607
15781
  const worktreeMap = collectWorktrees(worldsDir);
15608
15782
  const entries = [];
15609
- for (const id of fs23.readdirSync(worldsDir).sort()) {
15610
- const fullPath = path26.join(worldsDir, id);
15783
+ for (const id of fs24.readdirSync(worldsDir).sort()) {
15784
+ const fullPath = path27.join(worldsDir, id);
15611
15785
  const stat = safeStat(fullPath);
15612
15786
  if (!stat || !stat.isDirectory()) continue;
15613
15787
  entries.push(classifyWorld({ id, fullPath, liveIds, worktreeMap }));
@@ -15673,7 +15847,7 @@ function classifyWorld(args) {
15673
15847
  if (liveIds.has(id)) {
15674
15848
  return { id, path: fullPath, bytes, category: "active", note: "in registry" };
15675
15849
  }
15676
- const worktreeChild = path26.join(fullPath, "olam");
15850
+ const worktreeChild = path27.join(fullPath, "olam");
15677
15851
  const worktreeInfo = worktreeMap.get(worktreeChild);
15678
15852
  if (worktreeInfo) {
15679
15853
  if (worktreeInfo.dirty > 0 || worktreeInfo.unpushed > 0) {
@@ -15719,20 +15893,20 @@ function reapEntry(entry) {
15719
15893
  }
15720
15894
  }
15721
15895
  try {
15722
- fs23.rmSync(entry.path, { recursive: true, force: true });
15896
+ fs24.rmSync(entry.path, { recursive: true, force: true });
15723
15897
  } catch (err) {
15724
15898
  process.stderr.write(` ! rm ${entry.path}: ${err instanceof Error ? err.message : String(err)}
15725
15899
  `);
15726
15900
  return false;
15727
15901
  }
15728
- return !fs23.existsSync(entry.path);
15902
+ return !fs24.existsSync(entry.path);
15729
15903
  }
15730
15904
  function collectWorktrees(worldsDir) {
15731
15905
  const out = /* @__PURE__ */ new Map();
15732
- for (const id of fs23.readdirSync(worldsDir)) {
15733
- const child = path26.join(worldsDir, id, "olam");
15734
- const gitMarker = path26.join(child, ".git");
15735
- if (!fs23.existsSync(gitMarker)) continue;
15906
+ for (const id of fs24.readdirSync(worldsDir)) {
15907
+ const child = path27.join(worldsDir, id, "olam");
15908
+ const gitMarker = path27.join(child, ".git");
15909
+ if (!fs24.existsSync(gitMarker)) continue;
15736
15910
  const gitDir = resolveGitDirForWorktree(child);
15737
15911
  if (!gitDir) continue;
15738
15912
  const branch = readBranch(child);
@@ -15743,7 +15917,7 @@ function collectWorktrees(worldsDir) {
15743
15917
  return out;
15744
15918
  }
15745
15919
  function resolveGitDirForWorktree(worktreePath) {
15746
- const gitMarker = path26.join(worktreePath, ".git");
15920
+ const gitMarker = path27.join(worktreePath, ".git");
15747
15921
  try {
15748
15922
  const top = execFileSync4("git", ["rev-parse", "--show-toplevel"], {
15749
15923
  cwd: worktreePath,
@@ -15757,7 +15931,7 @@ function resolveGitDirForWorktree(worktreePath) {
15757
15931
  stdio: "pipe"
15758
15932
  }).trim();
15759
15933
  if (!common) return top;
15760
- return path26.dirname(path26.resolve(worktreePath, common));
15934
+ return path27.dirname(path27.resolve(worktreePath, common));
15761
15935
  } catch {
15762
15936
  return null;
15763
15937
  }
@@ -15800,7 +15974,7 @@ function countUnpushed(worktreePath) {
15800
15974
  }
15801
15975
  function safeStat(p) {
15802
15976
  try {
15803
- return fs23.statSync(p);
15977
+ return fs24.statSync(p);
15804
15978
  } catch {
15805
15979
  return null;
15806
15980
  }
@@ -15812,7 +15986,7 @@ function computeBytes(p) {
15812
15986
  const cur = stack.pop();
15813
15987
  let st;
15814
15988
  try {
15815
- st = fs23.lstatSync(cur);
15989
+ st = fs24.lstatSync(cur);
15816
15990
  } catch {
15817
15991
  continue;
15818
15992
  }
@@ -15820,11 +15994,11 @@ function computeBytes(p) {
15820
15994
  if (st.isDirectory()) {
15821
15995
  let entries;
15822
15996
  try {
15823
- entries = fs23.readdirSync(cur);
15997
+ entries = fs24.readdirSync(cur);
15824
15998
  } catch {
15825
15999
  continue;
15826
16000
  }
15827
- for (const name of entries) stack.push(path26.join(cur, name));
16001
+ for (const name of entries) stack.push(path27.join(cur, name));
15828
16002
  } else {
15829
16003
  total += st.size;
15830
16004
  }
@@ -16032,7 +16206,7 @@ init_context();
16032
16206
  init_output();
16033
16207
  init_exit_codes();
16034
16208
  init_world_paths();
16035
- import * as fs24 from "node:fs";
16209
+ import * as fs25 from "node:fs";
16036
16210
  import "node:path";
16037
16211
  import ora6 from "ora";
16038
16212
  function registerCrystallize(program2, options = {}) {
@@ -16064,7 +16238,7 @@ function registerCrystallize(program2, options = {}) {
16064
16238
  return;
16065
16239
  }
16066
16240
  const thoughtDbPath = getWorldDbPath(world.workspacePath);
16067
- if (!fs24.existsSync(thoughtDbPath)) {
16241
+ if (!fs25.existsSync(thoughtDbPath)) {
16068
16242
  printError(`No thoughts captured yet for "${worldId}". Run a dispatch first.`);
16069
16243
  process.exitCode = EXIT_GENERIC_ERROR;
16070
16244
  return;
@@ -17438,11 +17612,11 @@ var qmarksTestNoExtDot = ([$0]) => {
17438
17612
  return (f) => f.length === len && f !== "." && f !== "..";
17439
17613
  };
17440
17614
  var defaultPlatform = typeof process === "object" && process ? typeof process.env === "object" && process.env && process.env.__MINIMATCH_TESTING_PLATFORM__ || process.platform : "posix";
17441
- var path28 = {
17615
+ var path29 = {
17442
17616
  win32: { sep: "\\" },
17443
17617
  posix: { sep: "/" }
17444
17618
  };
17445
- var sep = defaultPlatform === "win32" ? path28.win32.sep : path28.posix.sep;
17619
+ var sep = defaultPlatform === "win32" ? path29.win32.sep : path29.posix.sep;
17446
17620
  minimatch.sep = sep;
17447
17621
  var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
17448
17622
  minimatch.GLOBSTAR = GLOBSTAR;
@@ -18270,23 +18444,23 @@ function registerPolicyCheck(program2) {
18270
18444
  // src/commands/upgrade.ts
18271
18445
  init_output();
18272
18446
  init_host_cp();
18273
- import * as fs27 from "node:fs";
18274
- import * as path31 from "node:path";
18447
+ import * as fs28 from "node:fs";
18448
+ import * as path32 from "node:path";
18275
18449
  import { spawnSync as spawnSync10 } from "node:child_process";
18276
18450
  import ora7 from "ora";
18277
18451
  import pc16 from "picocolors";
18278
18452
 
18279
18453
  // src/commands/upgrade-lock.ts
18280
- import * as fs25 from "node:fs";
18454
+ import * as fs26 from "node:fs";
18281
18455
  import * as os15 from "node:os";
18282
- import * as path29 from "node:path";
18456
+ import * as path30 from "node:path";
18283
18457
  import { spawnSync as spawnSync9 } from "node:child_process";
18284
- var LOCK_FILE_PATH = path29.join(os15.homedir(), ".olam", ".upgrade.lock");
18458
+ var LOCK_FILE_PATH = path30.join(os15.homedir(), ".olam", ".upgrade.lock");
18285
18459
  var STALE_LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
18286
18460
  function readLockFile(lockPath) {
18287
18461
  try {
18288
- if (!fs25.existsSync(lockPath)) return null;
18289
- const raw = fs25.readFileSync(lockPath, "utf-8").trim();
18462
+ if (!fs26.existsSync(lockPath)) return null;
18463
+ const raw = fs26.readFileSync(lockPath, "utf-8").trim();
18290
18464
  if (raw.length === 0) return null;
18291
18465
  const parsed = JSON.parse(raw);
18292
18466
  if (typeof parsed.pid !== "number" || typeof parsed.startTs !== "number") return null;
@@ -18331,16 +18505,16 @@ function isStaleLock(content, nowMs = Date.now()) {
18331
18505
  return false;
18332
18506
  }
18333
18507
  function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
18334
- const dir = path29.dirname(lockPath);
18335
- fs25.mkdirSync(dir, { recursive: true });
18508
+ const dir = path30.dirname(lockPath);
18509
+ fs26.mkdirSync(dir, { recursive: true });
18336
18510
  for (let attempt = 0; attempt < 2; attempt++) {
18337
18511
  try {
18338
- const fd = fs25.openSync(lockPath, "wx", 420);
18512
+ const fd = fs26.openSync(lockPath, "wx", 420);
18339
18513
  try {
18340
18514
  const content = { pid: process.pid, startTs: nowMs };
18341
- fs25.writeSync(fd, JSON.stringify(content));
18515
+ fs26.writeSync(fd, JSON.stringify(content));
18342
18516
  } finally {
18343
- fs25.closeSync(fd);
18517
+ fs26.closeSync(fd);
18344
18518
  }
18345
18519
  return { acquired: true, lockPath };
18346
18520
  } catch (err) {
@@ -18349,7 +18523,7 @@ function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
18349
18523
  const existing2 = readLockFile(lockPath);
18350
18524
  if (isStaleLock(existing2, nowMs)) {
18351
18525
  try {
18352
- fs25.unlinkSync(lockPath);
18526
+ fs26.unlinkSync(lockPath);
18353
18527
  } catch (unlinkErr) {
18354
18528
  const ucode = unlinkErr.code;
18355
18529
  if (ucode !== "ENOENT") throw unlinkErr;
@@ -18374,7 +18548,7 @@ function acquireLock(lockPath = LOCK_FILE_PATH, nowMs = Date.now()) {
18374
18548
  }
18375
18549
  function releaseLock(lockPath = LOCK_FILE_PATH) {
18376
18550
  try {
18377
- fs25.unlinkSync(lockPath);
18551
+ fs26.unlinkSync(lockPath);
18378
18552
  } catch (err) {
18379
18553
  const code = err.code;
18380
18554
  if (code !== "ENOENT") throw err;
@@ -18392,19 +18566,19 @@ function formatRefusalMessage(result, lockPath = LOCK_FILE_PATH) {
18392
18566
  }
18393
18567
 
18394
18568
  // src/commands/upgrade-log.ts
18395
- import * as fs26 from "node:fs";
18569
+ import * as fs27 from "node:fs";
18396
18570
  import * as os16 from "node:os";
18397
- import * as path30 from "node:path";
18571
+ import * as path31 from "node:path";
18398
18572
  function getUpgradeLogPath() {
18399
18573
  const home = process.env["HOME"] ?? os16.homedir();
18400
- return path30.join(home, ".olam", "upgrade.log");
18574
+ return path31.join(home, ".olam", "upgrade.log");
18401
18575
  }
18402
18576
  var UPGRADE_LOG_PATH = getUpgradeLogPath();
18403
18577
  function appendUpgradeLog(row, logPath = getUpgradeLogPath()) {
18404
18578
  try {
18405
- fs26.mkdirSync(path30.dirname(logPath), { recursive: true });
18579
+ fs27.mkdirSync(path31.dirname(logPath), { recursive: true });
18406
18580
  const line = JSON.stringify(row) + "\n";
18407
- fs26.appendFileSync(logPath, line, { mode: 420 });
18581
+ fs27.appendFileSync(logPath, line, { mode: 420 });
18408
18582
  } catch (err) {
18409
18583
  process.stderr.write(
18410
18584
  `[upgrade-log] failed to append: ${err instanceof Error ? err.message : String(err)}
@@ -18413,10 +18587,10 @@ function appendUpgradeLog(row, logPath = getUpgradeLogPath()) {
18413
18587
  }
18414
18588
  }
18415
18589
  function readUpgradeLog(limit = 10, logPath = getUpgradeLogPath()) {
18416
- if (!fs26.existsSync(logPath)) return [];
18590
+ if (!fs27.existsSync(logPath)) return [];
18417
18591
  let raw;
18418
18592
  try {
18419
- raw = fs26.readFileSync(logPath, "utf-8");
18593
+ raw = fs27.readFileSync(logPath, "utf-8");
18420
18594
  } catch (err) {
18421
18595
  process.stderr.write(
18422
18596
  `[upgrade-log] failed to read: ${err instanceof Error ? err.message : String(err)}
@@ -18509,12 +18683,12 @@ init_protocol_version();
18509
18683
  init_install_root();
18510
18684
  var AUTH_HEALTH_URL2 = "http://127.0.0.1:9999/health";
18511
18685
  function isNodeModulesInSync(cwd) {
18512
- const lockPath = path31.join(cwd, "package-lock.json");
18513
- const markerPath = path31.join(cwd, "node_modules", ".package-lock.json");
18514
- if (!fs27.existsSync(lockPath) || !fs27.existsSync(markerPath)) return false;
18686
+ const lockPath = path32.join(cwd, "package-lock.json");
18687
+ const markerPath = path32.join(cwd, "node_modules", ".package-lock.json");
18688
+ if (!fs28.existsSync(lockPath) || !fs28.existsSync(markerPath)) return false;
18515
18689
  try {
18516
- const lockStat = fs27.statSync(lockPath);
18517
- const markerStat = fs27.statSync(markerPath);
18690
+ const lockStat = fs28.statSync(lockPath);
18691
+ const markerStat = fs28.statSync(markerPath);
18518
18692
  return markerStat.mtimeMs >= lockStat.mtimeMs;
18519
18693
  } catch {
18520
18694
  return false;
@@ -18530,8 +18704,8 @@ function shouldSkipInstall(opts, cwd) {
18530
18704
  return { skip: false };
18531
18705
  }
18532
18706
  function validateRepoRoot(cwd) {
18533
- const marker = path31.join(cwd, "packages/host-cp/compose.yaml");
18534
- if (!fs27.existsSync(marker)) {
18707
+ const marker = path32.join(cwd, "packages/host-cp/compose.yaml");
18708
+ if (!fs28.existsSync(marker)) {
18535
18709
  return {
18536
18710
  ok: false,
18537
18711
  error: `Not an olam repo root (expected ${marker}).
@@ -18883,9 +19057,9 @@ async function recreateAuthService() {
18883
19057
  }
18884
19058
  }
18885
19059
  function readBundleHash(cwd) {
18886
- const indexPath = path31.join(cwd, "packages/control-plane/public/index.html");
18887
- if (!fs27.existsSync(indexPath)) return null;
18888
- return extractBundleHash(fs27.readFileSync(indexPath, "utf-8"));
19060
+ const indexPath = path32.join(cwd, "packages/control-plane/public/index.html");
19061
+ if (!fs28.existsSync(indexPath)) return null;
19062
+ return extractBundleHash(fs28.readFileSync(indexPath, "utf-8"));
18889
19063
  }
18890
19064
  async function runUpgradePullByDigest(deps = {}) {
18891
19065
  const docker2 = deps.docker ?? realDocker;
@@ -19324,7 +19498,7 @@ ${buildResult.stderr}`);
19324
19498
  return;
19325
19499
  }
19326
19500
  const authSecret = readAuthSecret2();
19327
- const spaDir = path31.join(cwd, "packages/control-plane/app");
19501
+ const spaDir = path32.join(cwd, "packages/control-plane/app");
19328
19502
  const spaResult = runStep2(
19329
19503
  "vite build (SPA)",
19330
19504
  "npx",
@@ -19858,20 +20032,20 @@ ${pc18.dim(`world: ${worldId} sort: ${sortKey} refresh: 5s Ctrl-C to exit`)}
19858
20032
 
19859
20033
  // src/commands/keys.ts
19860
20034
  init_output();
19861
- import * as fs28 from "node:fs";
20035
+ import * as fs29 from "node:fs";
19862
20036
  import * as os17 from "node:os";
19863
- import * as path32 from "node:path";
20037
+ import * as path33 from "node:path";
19864
20038
  import YAML4 from "yaml";
19865
20039
  function olamHome2() {
19866
- return process.env.OLAM_HOME ?? path32.join(os17.homedir(), ".olam");
20040
+ return process.env.OLAM_HOME ?? path33.join(os17.homedir(), ".olam");
19867
20041
  }
19868
20042
  function keysFilePath() {
19869
- return path32.join(olamHome2(), "keys.yaml");
20043
+ return path33.join(olamHome2(), "keys.yaml");
19870
20044
  }
19871
20045
  function readKeysFile() {
19872
20046
  const filePath = keysFilePath();
19873
- if (!fs28.existsSync(filePath)) return null;
19874
- const raw = fs28.readFileSync(filePath, "utf-8").trim();
20047
+ if (!fs29.existsSync(filePath)) return null;
20048
+ const raw = fs29.readFileSync(filePath, "utf-8").trim();
19875
20049
  if (raw.length === 0) return null;
19876
20050
  try {
19877
20051
  const parsed = YAML4.parse(raw);
@@ -19887,13 +20061,13 @@ function readKeysFile() {
19887
20061
  }
19888
20062
  function writeKeysFile(keys) {
19889
20063
  const dir = olamHome2();
19890
- if (!fs28.existsSync(dir)) {
19891
- fs28.mkdirSync(dir, { recursive: true });
20064
+ if (!fs29.existsSync(dir)) {
20065
+ fs29.mkdirSync(dir, { recursive: true });
19892
20066
  }
19893
20067
  const filePath = keysFilePath();
19894
20068
  const content = YAML4.stringify(keys);
19895
- fs28.writeFileSync(filePath, content, { encoding: "utf-8", mode: 384 });
19896
- fs28.chmodSync(filePath, 384);
20069
+ fs29.writeFileSync(filePath, content, { encoding: "utf-8", mode: 384 });
20070
+ fs29.chmodSync(filePath, 384);
19897
20071
  }
19898
20072
  function redact(value) {
19899
20073
  if (value.length <= 8) return value + "...";
@@ -19936,7 +20110,7 @@ function registerKeys(program2) {
19936
20110
  }
19937
20111
  const { [key]: _removed, ...rest } = existing;
19938
20112
  if (Object.keys(rest).length === 0) {
19939
- fs28.unlinkSync(keysFilePath());
20113
+ fs29.unlinkSync(keysFilePath());
19940
20114
  } else {
19941
20115
  writeKeysFile(rest);
19942
20116
  }
@@ -19959,33 +20133,33 @@ function registerKeys(program2) {
19959
20133
  }
19960
20134
 
19961
20135
  // src/commands/world-snapshot.ts
19962
- import * as fs30 from "node:fs";
19963
- import * as path34 from "node:path";
20136
+ import * as fs31 from "node:fs";
20137
+ import * as path35 from "node:path";
19964
20138
  import { execSync as execSync9 } from "node:child_process";
19965
20139
  import pc19 from "picocolors";
19966
20140
 
19967
20141
  // ../core/dist/world/snapshot.js
19968
- import * as crypto6 from "node:crypto";
19969
- import * as fs29 from "node:fs";
20142
+ import * as crypto7 from "node:crypto";
20143
+ import * as fs30 from "node:fs";
19970
20144
  import * as os18 from "node:os";
19971
- import * as path33 from "node:path";
20145
+ import * as path34 from "node:path";
19972
20146
  import { execFileSync as execFileSync5 } from "node:child_process";
19973
20147
  function snapshotsDir() {
19974
- return process.env["OLAM_SNAPSHOTS_DIR"] ?? path33.join(os18.homedir(), ".olam", "snapshots");
20148
+ return process.env["OLAM_SNAPSHOTS_DIR"] ?? path34.join(os18.homedir(), ".olam", "snapshots");
19975
20149
  }
19976
20150
  function snapshotKindDir(worldId, kind) {
19977
- return path33.join(snapshotsDir(), worldId, kind);
20151
+ return path34.join(snapshotsDir(), worldId, kind);
19978
20152
  }
19979
20153
  function snapshotTarPath(worldId, kind, repoName, hash) {
19980
20154
  const base = repoName ? `${repoName}-${hash}` : hash;
19981
- return path33.join(snapshotKindDir(worldId, kind), `${base}.tar.gz`);
20155
+ return path34.join(snapshotKindDir(worldId, kind), `${base}.tar.gz`);
19982
20156
  }
19983
20157
  function manifestPath(tarPath) {
19984
20158
  return tarPath.replace(/\.tar\.gz$/, ".manifest.json");
19985
20159
  }
19986
20160
  function hashBuffers(entries) {
19987
20161
  const sorted = [...entries].sort((a, b) => a.path.localeCompare(b.path));
19988
- const hash = crypto6.createHash("sha256");
20162
+ const hash = crypto7.createHash("sha256");
19989
20163
  for (const entry of sorted) {
19990
20164
  hash.update(entry.path);
19991
20165
  hash.update("\0");
@@ -19995,17 +20169,17 @@ function hashBuffers(entries) {
19995
20169
  return hash.digest("hex").slice(0, 12);
19996
20170
  }
19997
20171
  function computeGemsFingerprint(repoDir) {
19998
- const lockfile = path33.join(repoDir, "Gemfile.lock");
19999
- if (!fs29.existsSync(lockfile))
20172
+ const lockfile = path34.join(repoDir, "Gemfile.lock");
20173
+ if (!fs30.existsSync(lockfile))
20000
20174
  return null;
20001
- return hashBuffers([{ path: "Gemfile.lock", content: fs29.readFileSync(lockfile) }]);
20175
+ return hashBuffers([{ path: "Gemfile.lock", content: fs30.readFileSync(lockfile) }]);
20002
20176
  }
20003
20177
  function computeNodeFingerprint(repoDir) {
20004
20178
  const candidates = ["yarn.lock", "pnpm-lock.yaml", "package-lock.json"];
20005
20179
  for (const name of candidates) {
20006
- const lockfile = path33.join(repoDir, name);
20007
- if (fs29.existsSync(lockfile)) {
20008
- return hashBuffers([{ path: name, content: fs29.readFileSync(lockfile) }]);
20180
+ const lockfile = path34.join(repoDir, name);
20181
+ if (fs30.existsSync(lockfile)) {
20182
+ return hashBuffers([{ path: name, content: fs30.readFileSync(lockfile) }]);
20009
20183
  }
20010
20184
  }
20011
20185
  return null;
@@ -20015,16 +20189,16 @@ function computePgFingerprint(repoDirs) {
20015
20189
  const entries = [];
20016
20190
  for (const repoDir of repoDirs) {
20017
20191
  for (const pattern of patterns) {
20018
- const filePath = path33.join(repoDir, pattern);
20019
- if (fs29.existsSync(filePath)) {
20020
- entries.push({ path: filePath, content: fs29.readFileSync(filePath) });
20192
+ const filePath = path34.join(repoDir, pattern);
20193
+ if (fs30.existsSync(filePath)) {
20194
+ entries.push({ path: filePath, content: fs30.readFileSync(filePath) });
20021
20195
  }
20022
20196
  }
20023
20197
  }
20024
20198
  return entries.length > 0 ? hashBuffers(entries) : null;
20025
20199
  }
20026
20200
  function packTarball(srcDir, destPath, opts = {}) {
20027
- fs29.mkdirSync(path33.dirname(destPath), { recursive: true });
20201
+ fs30.mkdirSync(path34.dirname(destPath), { recursive: true });
20028
20202
  const tmp = `${destPath}.tmp`;
20029
20203
  const args = [];
20030
20204
  if (opts.followSymlinks)
@@ -20032,47 +20206,47 @@ function packTarball(srcDir, destPath, opts = {}) {
20032
20206
  args.push("-czf", tmp, "-C", srcDir, ".");
20033
20207
  try {
20034
20208
  execFileSync5("tar", args, { stdio: "pipe" });
20035
- fs29.renameSync(tmp, destPath);
20209
+ fs30.renameSync(tmp, destPath);
20036
20210
  } catch (err) {
20037
20211
  try {
20038
- fs29.rmSync(tmp, { force: true });
20212
+ fs30.rmSync(tmp, { force: true });
20039
20213
  } catch {
20040
20214
  }
20041
20215
  throw err;
20042
20216
  }
20043
20217
  }
20044
20218
  function writeManifest(manifest, tarPath) {
20045
- fs29.writeFileSync(manifestPath(tarPath), JSON.stringify(manifest, null, 2), "utf-8");
20219
+ fs30.writeFileSync(manifestPath(tarPath), JSON.stringify(manifest, null, 2), "utf-8");
20046
20220
  }
20047
20221
  function readManifest(tarPath) {
20048
20222
  const mPath = manifestPath(tarPath);
20049
- if (!fs29.existsSync(mPath))
20223
+ if (!fs30.existsSync(mPath))
20050
20224
  return null;
20051
20225
  try {
20052
- return JSON.parse(fs29.readFileSync(mPath, "utf-8"));
20226
+ return JSON.parse(fs30.readFileSync(mPath, "utf-8"));
20053
20227
  } catch {
20054
20228
  return null;
20055
20229
  }
20056
20230
  }
20057
20231
  function listSnapshots(worldIdFilter) {
20058
20232
  const root = snapshotsDir();
20059
- if (!fs29.existsSync(root))
20233
+ if (!fs30.existsSync(root))
20060
20234
  return [];
20061
20235
  const now = Date.now();
20062
20236
  const results = [];
20063
- const worlds = worldIdFilter ? [worldIdFilter] : fs29.readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
20237
+ const worlds = worldIdFilter ? [worldIdFilter] : fs30.readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
20064
20238
  for (const worldId of worlds) {
20065
- const worldDir = path33.join(root, worldId);
20066
- if (!fs29.existsSync(worldDir) || !fs29.statSync(worldDir).isDirectory())
20239
+ const worldDir = path34.join(root, worldId);
20240
+ if (!fs30.existsSync(worldDir) || !fs30.statSync(worldDir).isDirectory())
20067
20241
  continue;
20068
20242
  for (const kind of ["gems", "node", "pg"]) {
20069
- const kindDir = path33.join(worldDir, kind);
20070
- if (!fs29.existsSync(kindDir))
20243
+ const kindDir = path34.join(worldDir, kind);
20244
+ if (!fs30.existsSync(kindDir))
20071
20245
  continue;
20072
- const tarballs = fs29.readdirSync(kindDir).filter((f) => f.endsWith(".tar.gz"));
20246
+ const tarballs = fs30.readdirSync(kindDir).filter((f) => f.endsWith(".tar.gz"));
20073
20247
  for (const tarFile of tarballs) {
20074
- const tarPath = path33.join(kindDir, tarFile);
20075
- const stat = fs29.statSync(tarPath);
20248
+ const tarPath = path34.join(kindDir, tarFile);
20249
+ const stat = fs30.statSync(tarPath);
20076
20250
  const manifest = readManifest(tarPath);
20077
20251
  if (!manifest)
20078
20252
  continue;
@@ -20162,17 +20336,17 @@ function resolveKinds(arg) {
20162
20336
  return [];
20163
20337
  }
20164
20338
  async function captureGems(worldId, workspacePath, repo) {
20165
- const repoDir = path34.join(workspacePath, repo);
20339
+ const repoDir = path35.join(workspacePath, repo);
20166
20340
  const fingerprint = computeGemsFingerprint(repoDir);
20167
20341
  if (!fingerprint) {
20168
20342
  return { ok: false, tarPath: "", msg: "no Gemfile.lock \u2014 layer does not apply" };
20169
20343
  }
20170
20344
  const tarPath = snapshotTarPath(worldId, "gems", repo, fingerprint);
20171
- const vendorBundle = path34.join(repoDir, "vendor", "bundle");
20172
- if (fs30.existsSync(vendorBundle)) {
20345
+ const vendorBundle = path35.join(repoDir, "vendor", "bundle");
20346
+ if (fs31.existsSync(vendorBundle)) {
20173
20347
  try {
20174
20348
  packTarball(vendorBundle, tarPath);
20175
- const stat = fs30.statSync(tarPath);
20349
+ const stat = fs31.statSync(tarPath);
20176
20350
  const manifest = {
20177
20351
  kind: "gems",
20178
20352
  worldId,
@@ -20205,10 +20379,10 @@ async function captureGems(worldId, workspacePath, repo) {
20205
20379
  `docker exec ${containerName} sh -c 'mkdir -p "$(dirname ${tmpTar})" && tar -czf ${tmpTar}.tmp -C ${bundlePath} . && mv ${tmpTar}.tmp ${tmpTar}'`,
20206
20380
  { stdio: "pipe", timeout: 12e4 }
20207
20381
  );
20208
- fs30.mkdirSync(path34.dirname(tarPath), { recursive: true });
20382
+ fs31.mkdirSync(path35.dirname(tarPath), { recursive: true });
20209
20383
  execSync9(`docker cp ${containerName}:${tmpTar} "${tarPath}"`, { stdio: "pipe", timeout: 12e4 });
20210
20384
  execSync9(`docker exec ${containerName} rm -f ${tmpTar}`, { stdio: "pipe" });
20211
- const stat = fs30.statSync(tarPath);
20385
+ const stat = fs31.statSync(tarPath);
20212
20386
  const manifest = {
20213
20387
  kind: "gems",
20214
20388
  worldId,
@@ -20225,19 +20399,19 @@ async function captureGems(worldId, workspacePath, repo) {
20225
20399
  }
20226
20400
  }
20227
20401
  async function captureNode(worldId, workspacePath, repo) {
20228
- const repoDir = path34.join(workspacePath, repo);
20402
+ const repoDir = path35.join(workspacePath, repo);
20229
20403
  const fingerprint = computeNodeFingerprint(repoDir);
20230
20404
  if (!fingerprint) {
20231
20405
  return { ok: false, tarPath: "", msg: "no lockfile \u2014 layer does not apply" };
20232
20406
  }
20233
- const nodeModules = path34.join(repoDir, "node_modules");
20234
- if (!fs30.existsSync(nodeModules)) {
20407
+ const nodeModules = path35.join(repoDir, "node_modules");
20408
+ if (!fs31.existsSync(nodeModules)) {
20235
20409
  return { ok: false, tarPath: "", msg: "node_modules not installed yet" };
20236
20410
  }
20237
20411
  const tarPath = snapshotTarPath(worldId, "node", repo, fingerprint);
20238
20412
  try {
20239
20413
  packTarball(nodeModules, tarPath);
20240
- const stat = fs30.statSync(tarPath);
20414
+ const stat = fs31.statSync(tarPath);
20241
20415
  const manifest = {
20242
20416
  kind: "node",
20243
20417
  worldId,
@@ -20254,7 +20428,7 @@ async function captureNode(worldId, workspacePath, repo) {
20254
20428
  }
20255
20429
  }
20256
20430
  async function capturePg(worldId, workspacePath, repoNames) {
20257
- const repoDirs = repoNames.map((r) => path34.join(workspacePath, r));
20431
+ const repoDirs = repoNames.map((r) => path35.join(workspacePath, r));
20258
20432
  const fingerprint = computePgFingerprint(repoDirs);
20259
20433
  if (!fingerprint) {
20260
20434
  return { ok: false, tarPath: "", msg: "no Gemfile.lock / schema.rb \u2014 layer does not apply" };
@@ -20269,13 +20443,13 @@ async function capturePg(worldId, workspacePath, repoNames) {
20269
20443
  }
20270
20444
  try {
20271
20445
  execSync9(`docker stop ${containerName}`, { stdio: "pipe", timeout: 3e4 });
20272
- fs30.mkdirSync(path34.dirname(tarPath), { recursive: true });
20446
+ fs31.mkdirSync(path35.dirname(tarPath), { recursive: true });
20273
20447
  execSync9(
20274
- `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)}'`,
20448
+ `docker run --rm -v "${volumeName2}:/pgdata:ro" -v "${path35.dirname(tarPath)}:/dest" alpine sh -c 'tar -czf /dest/${path35.basename(tarPath)}.tmp -C /pgdata . && mv /dest/${path35.basename(tarPath)}.tmp /dest/${path35.basename(tarPath)}'`,
20275
20449
  { stdio: "pipe", timeout: 18e4 }
20276
20450
  );
20277
20451
  execSync9(`docker start ${containerName}`, { stdio: "pipe", timeout: 3e4 });
20278
- const stat = fs30.statSync(tarPath);
20452
+ const stat = fs31.statSync(tarPath);
20279
20453
  const manifest = {
20280
20454
  kind: "pg",
20281
20455
  worldId,
@@ -20350,35 +20524,35 @@ function formatAge2(ms) {
20350
20524
  // src/commands/refresh.ts
20351
20525
  init_context();
20352
20526
  init_output();
20353
- import * as fs32 from "node:fs";
20527
+ import * as fs33 from "node:fs";
20354
20528
  import * as os19 from "node:os";
20355
- import * as path36 from "node:path";
20529
+ import * as path37 from "node:path";
20356
20530
  import { spawnSync as spawnSync12 } from "node:child_process";
20357
20531
  import ora8 from "ora";
20358
20532
 
20359
20533
  // src/commands/refresh-helpers.ts
20360
- import * as fs31 from "node:fs";
20361
- import * as path35 from "node:path";
20534
+ import * as fs32 from "node:fs";
20535
+ import * as path36 from "node:path";
20362
20536
  function collectCpSourceFiles(standaloneDir) {
20363
- if (!fs31.existsSync(standaloneDir)) {
20537
+ if (!fs32.existsSync(standaloneDir)) {
20364
20538
  throw new Error(`CP standalone dir not found: ${standaloneDir}`);
20365
20539
  }
20366
20540
  const entries = [];
20367
- const topLevel = fs31.readdirSync(standaloneDir).filter((f) => {
20368
- const stat = fs31.statSync(path35.join(standaloneDir, f));
20541
+ const topLevel = fs32.readdirSync(standaloneDir).filter((f) => {
20542
+ const stat = fs32.statSync(path36.join(standaloneDir, f));
20369
20543
  return stat.isFile() && f.endsWith(".mjs") && !f.endsWith(".test.mjs");
20370
20544
  }).sort();
20371
20545
  for (const f of topLevel) {
20372
- entries.push({ srcPath: path35.join(standaloneDir, f), destRelPath: f });
20546
+ entries.push({ srcPath: path36.join(standaloneDir, f), destRelPath: f });
20373
20547
  }
20374
- const libDir = path35.join(standaloneDir, "lib");
20375
- if (fs31.existsSync(libDir) && fs31.statSync(libDir).isDirectory()) {
20376
- const libFiles = fs31.readdirSync(libDir).filter((f) => {
20377
- const stat = fs31.statSync(path35.join(libDir, f));
20548
+ const libDir = path36.join(standaloneDir, "lib");
20549
+ if (fs32.existsSync(libDir) && fs32.statSync(libDir).isDirectory()) {
20550
+ const libFiles = fs32.readdirSync(libDir).filter((f) => {
20551
+ const stat = fs32.statSync(path36.join(libDir, f));
20378
20552
  return stat.isFile() && f.endsWith(".mjs") && !f.endsWith(".test.mjs");
20379
20553
  }).sort();
20380
20554
  for (const f of libFiles) {
20381
- entries.push({ srcPath: path35.join(libDir, f), destRelPath: `lib/${f}` });
20555
+ entries.push({ srcPath: path36.join(libDir, f), destRelPath: `lib/${f}` });
20382
20556
  }
20383
20557
  }
20384
20558
  return entries;
@@ -20436,16 +20610,16 @@ async function refreshWorld(worldId, portOffset, standaloneDir, opts) {
20436
20610
  error: err instanceof Error ? err.message : String(err)
20437
20611
  };
20438
20612
  }
20439
- const stagingDir = fs32.mkdtempSync(
20440
- path36.join(os19.tmpdir(), `olam-refresh-${worldId}-`)
20613
+ const stagingDir = fs33.mkdtempSync(
20614
+ path37.join(os19.tmpdir(), `olam-refresh-${worldId}-`)
20441
20615
  );
20442
20616
  try {
20443
20617
  const hasLib = entries.some((e) => e.destRelPath.startsWith("lib/"));
20444
20618
  if (hasLib) {
20445
- fs32.mkdirSync(path36.join(stagingDir, "lib"), { recursive: true });
20619
+ fs33.mkdirSync(path37.join(stagingDir, "lib"), { recursive: true });
20446
20620
  }
20447
20621
  for (const { srcPath, destRelPath } of entries) {
20448
- fs32.copyFileSync(srcPath, path36.join(stagingDir, destRelPath));
20622
+ fs33.copyFileSync(srcPath, path37.join(stagingDir, destRelPath));
20449
20623
  }
20450
20624
  const cpResult = docker([
20451
20625
  "cp",
@@ -20460,7 +20634,7 @@ async function refreshWorld(worldId, portOffset, standaloneDir, opts) {
20460
20634
  };
20461
20635
  }
20462
20636
  } finally {
20463
- fs32.rmSync(stagingDir, { recursive: true, force: true });
20637
+ fs33.rmSync(stagingDir, { recursive: true, force: true });
20464
20638
  }
20465
20639
  if (opts.restart) {
20466
20640
  const restartResult = docker([
@@ -20497,11 +20671,11 @@ function registerRefresh(program2) {
20497
20671
  process.exitCode = 1;
20498
20672
  return;
20499
20673
  }
20500
- const standaloneDir = path36.join(
20674
+ const standaloneDir = path37.join(
20501
20675
  process.cwd(),
20502
20676
  "packages/control-plane/standalone"
20503
20677
  );
20504
- if (!fs32.existsSync(standaloneDir)) {
20678
+ if (!fs33.existsSync(standaloneDir)) {
20505
20679
  printError(
20506
20680
  `CP standalone source not found at ${standaloneDir}.
20507
20681
  Run \`olam refresh\` from the olam repo root.`
@@ -20580,9 +20754,9 @@ Run \`olam refresh\` from the olam repo root.`
20580
20754
  }
20581
20755
 
20582
20756
  // src/commands/diagnose.ts
20583
- import * as fs33 from "node:fs";
20757
+ import * as fs34 from "node:fs";
20584
20758
  import * as os20 from "node:os";
20585
- import * as path37 from "node:path";
20759
+ import * as path38 from "node:path";
20586
20760
  import { execFileSync as execFileSync6, execSync as execSync10 } from "node:child_process";
20587
20761
  import pc20 from "picocolors";
20588
20762
 
@@ -20617,9 +20791,9 @@ function stripSecrets(input) {
20617
20791
  }
20618
20792
 
20619
20793
  // src/commands/diagnose.ts
20620
- var DIAGNOSTICS_DIR = path37.join(os20.homedir(), ".olam", "diagnostics");
20621
- var LOG_DIR = path37.join(os20.homedir(), ".olam", "log");
20622
- var CACHE_DIR = path37.join(os20.homedir(), ".olam", "cache");
20794
+ var DIAGNOSTICS_DIR = path38.join(os20.homedir(), ".olam", "diagnostics");
20795
+ var LOG_DIR = path38.join(os20.homedir(), ".olam", "log");
20796
+ var CACHE_DIR = path38.join(os20.homedir(), ".olam", "cache");
20623
20797
  var LOG_TAIL_LINES = 200;
20624
20798
  function safeExec(cmd) {
20625
20799
  try {
@@ -20633,10 +20807,10 @@ function defaultZip(zipPath, files) {
20633
20807
  }
20634
20808
  async function buildDiagnosticsZip(_exec = (cmd) => safeExec(cmd), _outDir = DIAGNOSTICS_DIR, _logDir = LOG_DIR, _zip = defaultZip) {
20635
20809
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 23);
20636
- const zipPath = path37.join(_outDir, `olam-diag-${ts}.zip`);
20637
- const tmpDir = fs33.mkdtempSync(path37.join(os20.tmpdir(), "olam-diag-"));
20810
+ const zipPath = path38.join(_outDir, `olam-diag-${ts}.zip`);
20811
+ const tmpDir = fs34.mkdtempSync(path38.join(os20.tmpdir(), "olam-diag-"));
20638
20812
  try {
20639
- fs33.mkdirSync(_outDir, { recursive: true });
20813
+ fs34.mkdirSync(_outDir, { recursive: true });
20640
20814
  const entries = [];
20641
20815
  const version = process.env["OLAM_CLI_VERSION"] ?? "unknown";
20642
20816
  const nodeVersion = process.version;
@@ -20652,14 +20826,14 @@ platform: ${platform}
20652
20826
  `uptime: ${os20.uptime()}s`
20653
20827
  ].join("\n") + "\n";
20654
20828
  _writeEntry(tmpDir, "os-info.txt", stripSecrets(osContent), entries);
20655
- const depsFile = path37.join(CACHE_DIR, "deps.json");
20656
- if (fs33.existsSync(depsFile)) {
20657
- const deps = fs33.readFileSync(depsFile, "utf-8");
20829
+ const depsFile = path38.join(CACHE_DIR, "deps.json");
20830
+ if (fs34.existsSync(depsFile)) {
20831
+ const deps = fs34.readFileSync(depsFile, "utf-8");
20658
20832
  _writeEntry(tmpDir, "deps.json", stripSecrets(deps), entries);
20659
20833
  }
20660
20834
  const latestLog = _latestLog(_logDir);
20661
20835
  if (latestLog) {
20662
- const lines = fs33.readFileSync(latestLog, "utf-8").split("\n");
20836
+ const lines = fs34.readFileSync(latestLog, "utf-8").split("\n");
20663
20837
  const tail = lines.slice(-LOG_TAIL_LINES).join("\n");
20664
20838
  _writeEntry(tmpDir, "log-tail.txt", stripSecrets(tail), entries);
20665
20839
  }
@@ -20671,38 +20845,38 @@ platform: ${platform}
20671
20845
  if (authAudit) {
20672
20846
  _writeEntry(tmpDir, "audit-auth-callers.txt", stripSecrets(authAudit), entries);
20673
20847
  }
20674
- const fileArgs = entries.map((e) => path37.join(tmpDir, e));
20848
+ const fileArgs = entries.map((e) => path38.join(tmpDir, e));
20675
20849
  try {
20676
20850
  _zip(zipPath, fileArgs);
20677
20851
  } catch (err) {
20678
20852
  throw new Error(`zip command produced no output file. zip stderr: ${err.message}`);
20679
20853
  }
20680
- if (!fs33.existsSync(zipPath)) {
20854
+ if (!fs34.existsSync(zipPath)) {
20681
20855
  throw new Error("zip command produced no output file.");
20682
20856
  }
20683
20857
  return { zipPath, entries };
20684
20858
  } finally {
20685
20859
  try {
20686
- fs33.rmSync(tmpDir, { recursive: true, force: true });
20860
+ fs34.rmSync(tmpDir, { recursive: true, force: true });
20687
20861
  } catch {
20688
20862
  }
20689
20863
  }
20690
20864
  }
20691
20865
  function _writeEntry(dir, name, content, entries) {
20692
- fs33.writeFileSync(path37.join(dir, name), content, { mode: 420 });
20866
+ fs34.writeFileSync(path38.join(dir, name), content, { mode: 420 });
20693
20867
  entries.push(name);
20694
20868
  }
20695
20869
  function _latestLog(logDir) {
20696
- if (!fs33.existsSync(logDir)) return null;
20697
- const files = fs33.readdirSync(logDir).filter((f) => f.startsWith("host-")).sort().reverse();
20698
- return files.length > 0 ? path37.join(logDir, files[0]) : null;
20870
+ if (!fs34.existsSync(logDir)) return null;
20871
+ const files = fs34.readdirSync(logDir).filter((f) => f.startsWith("host-")).sort().reverse();
20872
+ return files.length > 0 ? path38.join(logDir, files[0]) : null;
20699
20873
  }
20700
20874
  async function buildTelemetryPayload() {
20701
20875
  const channel = "stable";
20702
- const manifestFile = path37.join(CACHE_DIR, "manifest.json");
20876
+ const manifestFile = path38.join(CACHE_DIR, "manifest.json");
20703
20877
  let manifestAgeHours = null;
20704
- if (fs33.existsSync(manifestFile)) {
20705
- const mtime = fs33.statSync(manifestFile).mtime.getTime();
20878
+ if (fs34.existsSync(manifestFile)) {
20879
+ const mtime = fs34.statSync(manifestFile).mtime.getTime();
20706
20880
  manifestAgeHours = Math.round((Date.now() - mtime) / 36e5);
20707
20881
  }
20708
20882
  return {
@@ -20745,24 +20919,24 @@ function registerDiagnose(program2) {
20745
20919
  }
20746
20920
 
20747
20921
  // src/commands/update.ts
20748
- import * as fs36 from "node:fs";
20922
+ import * as fs37 from "node:fs";
20749
20923
  import * as os22 from "node:os";
20750
- import * as path40 from "node:path";
20924
+ import * as path41 from "node:path";
20751
20925
  import { execSync as execSync11 } from "node:child_process";
20752
20926
  import pc21 from "picocolors";
20753
20927
 
20754
20928
  // src/lib/symlink-reconcile.ts
20755
- import * as fs34 from "node:fs";
20756
- import * as path38 from "node:path";
20929
+ import * as fs35 from "node:fs";
20930
+ import * as path39 from "node:path";
20757
20931
  var realFs = {
20758
- readdirSync: (p) => fs34.readdirSync(p),
20759
- existsSync: (p) => fs34.existsSync(p),
20760
- lstatSync: (p) => fs34.lstatSync(p),
20761
- readlinkSync: (p) => fs34.readlinkSync(p),
20762
- symlinkSync: (t, l) => fs34.symlinkSync(t, l),
20763
- unlinkSync: (p) => fs34.unlinkSync(p),
20932
+ readdirSync: (p) => fs35.readdirSync(p),
20933
+ existsSync: (p) => fs35.existsSync(p),
20934
+ lstatSync: (p) => fs35.lstatSync(p),
20935
+ readlinkSync: (p) => fs35.readlinkSync(p),
20936
+ symlinkSync: (t, l) => fs35.symlinkSync(t, l),
20937
+ unlinkSync: (p) => fs35.unlinkSync(p),
20764
20938
  mkdirSync: (p, o) => {
20765
- fs34.mkdirSync(p, o);
20939
+ fs35.mkdirSync(p, o);
20766
20940
  }
20767
20941
  };
20768
20942
  function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
@@ -20772,8 +20946,8 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
20772
20946
  _fs.mkdirSync(claudeSkillsDir, { recursive: true });
20773
20947
  const sourceSkills = _fs.existsSync(npmSkillsDir) ? _fs.readdirSync(npmSkillsDir).filter((d) => d.startsWith("olam-")) : [];
20774
20948
  for (const skill of sourceSkills) {
20775
- const linkPath = path38.join(claudeSkillsDir, skill);
20776
- const target = path38.join(npmSkillsDir, skill);
20949
+ const linkPath = path39.join(claudeSkillsDir, skill);
20950
+ const target = path39.join(npmSkillsDir, skill);
20777
20951
  if (!_fs.existsSync(linkPath)) {
20778
20952
  try {
20779
20953
  _fs.symlinkSync(target, linkPath);
@@ -20786,7 +20960,7 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
20786
20960
  }
20787
20961
  const deployedEntries = _fs.existsSync(claudeSkillsDir) ? _fs.readdirSync(claudeSkillsDir).filter((d) => d.startsWith("olam-")) : [];
20788
20962
  for (const entry of deployedEntries) {
20789
- const linkPath = path38.join(claudeSkillsDir, entry);
20963
+ const linkPath = path39.join(claudeSkillsDir, entry);
20790
20964
  let isSymlink = false;
20791
20965
  try {
20792
20966
  isSymlink = _fs.lstatSync(linkPath).isSymbolicLink();
@@ -20811,9 +20985,9 @@ function reconcileSkillSymlinks(npmSkillsDir, claudeSkillsDir, _fs = realFs) {
20811
20985
 
20812
20986
  // src/commands/update.ts
20813
20987
  var PACKAGE_NAME = "@pleri/olam-cli";
20814
- var CACHE_DIR2 = path40.join(os22.homedir(), ".olam", "cache");
20815
- var LOG_DIR2 = path40.join(os22.homedir(), ".olam", "log");
20816
- var LAST_STABLE_FILE = path40.join(CACHE_DIR2, "last-stable.txt");
20988
+ var CACHE_DIR2 = path41.join(os22.homedir(), ".olam", "cache");
20989
+ var LOG_DIR2 = path41.join(os22.homedir(), ".olam", "log");
20990
+ var LAST_STABLE_FILE = path41.join(CACHE_DIR2, "last-stable.txt");
20817
20991
  function defaultExec(cmd) {
20818
20992
  try {
20819
20993
  const stdout = execSync11(cmd, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
@@ -20835,22 +21009,22 @@ function getCurrentVersion(_exec = defaultExec) {
20835
21009
  }
20836
21010
  function readLastStable(file = LAST_STABLE_FILE) {
20837
21011
  try {
20838
- const v = fs36.readFileSync(file, "utf-8").trim();
21012
+ const v = fs37.readFileSync(file, "utf-8").trim();
20839
21013
  return v || null;
20840
21014
  } catch {
20841
21015
  return null;
20842
21016
  }
20843
21017
  }
20844
21018
  function writeLastStable(version, file = LAST_STABLE_FILE) {
20845
- fs36.mkdirSync(path40.dirname(file), { recursive: true });
20846
- fs36.writeFileSync(file, version, { mode: 420 });
21019
+ fs37.mkdirSync(path41.dirname(file), { recursive: true });
21020
+ fs37.writeFileSync(file, version, { mode: 420 });
20847
21021
  }
20848
21022
  function logUpdateFailure(stderr) {
20849
21023
  const ts = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20850
- const logFile = path40.join(LOG_DIR2, `update-${ts}.log`);
21024
+ const logFile = path41.join(LOG_DIR2, `update-${ts}.log`);
20851
21025
  try {
20852
- fs36.mkdirSync(LOG_DIR2, { recursive: true });
20853
- fs36.appendFileSync(logFile, `[update-failure ${(/* @__PURE__ */ new Date()).toISOString()}]
21026
+ fs37.mkdirSync(LOG_DIR2, { recursive: true });
21027
+ fs37.appendFileSync(logFile, `[update-failure ${(/* @__PURE__ */ new Date()).toISOString()}]
20854
21028
  ${stderr}
20855
21029
  `, "utf-8");
20856
21030
  } catch {
@@ -20937,8 +21111,8 @@ async function doUpdate(opts, _exec = defaultExec, _reconcile = reconcileSkillSy
20937
21111
  let symlinkResult = { added: [], removed: [] };
20938
21112
  if (npmRootResult.exitCode === 0) {
20939
21113
  const npmRoot = npmRootResult.stdout.trim();
20940
- const npmSkillsDir = path40.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills");
20941
- const claudeSkillsDir = path40.join(os22.homedir(), ".claude", "skills");
21114
+ const npmSkillsDir = path41.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills");
21115
+ const claudeSkillsDir = path41.join(os22.homedir(), ".claude", "skills");
20942
21116
  const rec = _reconcile(npmSkillsDir, claudeSkillsDir);
20943
21117
  symlinkResult = { added: rec.added, removed: rec.removed };
20944
21118
  if (!quiet && (rec.added.length > 0 || rec.removed.length > 0)) {
@@ -20984,8 +21158,8 @@ async function doRollback(_exec = defaultExec, _reconcile = reconcileSkillSymlin
20984
21158
  if (npmRootResult.exitCode === 0) {
20985
21159
  const npmRoot = npmRootResult.stdout.trim();
20986
21160
  _reconcile(
20987
- path40.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills"),
20988
- path40.join(os22.homedir(), ".claude", "skills")
21161
+ path41.join(npmRoot, "@pleri", "olam-cli", "plugin", "skills"),
21162
+ path41.join(os22.homedir(), ".claude", "skills")
20989
21163
  );
20990
21164
  }
20991
21165
  return { action: "rolled-back", restoredVersion: prev, exitCode: 0 };
@@ -21146,8 +21320,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
21146
21320
  function authHeaders() {
21147
21321
  return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
21148
21322
  }
21149
- async function apiFetch(path44, init = {}) {
21150
- const res = await fetch(`${BASE_URL}${path44}`, {
21323
+ async function apiFetch(path45, init = {}) {
21324
+ const res = await fetch(`${BASE_URL}${path45}`, {
21151
21325
  ...init,
21152
21326
  headers: {
21153
21327
  "Content-Type": "application/json",
@@ -21407,12 +21581,12 @@ import * as readline3 from "node:readline";
21407
21581
  import pc25 from "picocolors";
21408
21582
 
21409
21583
  // src/commands/mcp/import-discovery.ts
21410
- import * as fs37 from "node:fs";
21584
+ import * as fs38 from "node:fs";
21411
21585
  import * as os23 from "node:os";
21412
- import * as path41 from "node:path";
21586
+ import * as path42 from "node:path";
21413
21587
  function readJsonFile(filePath) {
21414
21588
  try {
21415
- const raw = fs37.readFileSync(filePath, "utf-8");
21589
+ const raw = fs38.readFileSync(filePath, "utf-8");
21416
21590
  return JSON.parse(raw);
21417
21591
  } catch {
21418
21592
  return null;
@@ -21441,24 +21615,24 @@ function extractMcpServers(obj, source, sourceLabel) {
21441
21615
  }
21442
21616
  function getClaudeDesktopPath() {
21443
21617
  if (process.platform === "darwin") {
21444
- return path41.join(os23.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
21618
+ return path42.join(os23.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
21445
21619
  }
21446
21620
  if (process.platform === "win32") {
21447
- const appData = process.env["APPDATA"] ?? path41.join(os23.homedir(), "AppData", "Roaming");
21448
- return path41.join(appData, "Claude", "claude_desktop_config.json");
21621
+ const appData = process.env["APPDATA"] ?? path42.join(os23.homedir(), "AppData", "Roaming");
21622
+ return path42.join(appData, "Claude", "claude_desktop_config.json");
21449
21623
  }
21450
- return path41.join(os23.homedir(), ".config", "Claude", "claude_desktop_config.json");
21624
+ return path42.join(os23.homedir(), ".config", "Claude", "claude_desktop_config.json");
21451
21625
  }
21452
21626
  function getOlamRepoPaths() {
21453
21627
  const configPaths = [
21454
- path41.join(os23.homedir(), ".olam", "config.yaml"),
21455
- path41.join(process.cwd(), ".olam", "config.yaml")
21628
+ path42.join(os23.homedir(), ".olam", "config.yaml"),
21629
+ path42.join(process.cwd(), ".olam", "config.yaml")
21456
21630
  ];
21457
21631
  const paths = [];
21458
21632
  for (const configPath of configPaths) {
21459
- if (!fs37.existsSync(configPath)) continue;
21633
+ if (!fs38.existsSync(configPath)) continue;
21460
21634
  try {
21461
- const raw = fs37.readFileSync(configPath, "utf-8");
21635
+ const raw = fs38.readFileSync(configPath, "utf-8");
21462
21636
  const repoMatches = [...raw.matchAll(/path:\s*["']?([^\s"'\n]+)/g)];
21463
21637
  for (const m of repoMatches) {
21464
21638
  if (m[1]) paths.push(m[1]);
@@ -21474,7 +21648,7 @@ async function discoverMcpSources(repoPaths) {
21474
21648
  const sources = [];
21475
21649
  const sourceDefs = [
21476
21650
  {
21477
- path: path41.join(os23.homedir(), ".claude.json"),
21651
+ path: path42.join(os23.homedir(), ".claude.json"),
21478
21652
  label: "Claude Code (~/.claude.json)"
21479
21653
  },
21480
21654
  {
@@ -21482,19 +21656,19 @@ async function discoverMcpSources(repoPaths) {
21482
21656
  label: "Claude Desktop"
21483
21657
  },
21484
21658
  {
21485
- path: path41.join(os23.homedir(), ".cursor", "mcp.json"),
21659
+ path: path42.join(os23.homedir(), ".cursor", "mcp.json"),
21486
21660
  label: "Cursor (~/.cursor/mcp.json)"
21487
21661
  },
21488
21662
  {
21489
- path: path41.join(os23.homedir(), ".codeium", "windsurf", "mcp_config.json"),
21663
+ path: path42.join(os23.homedir(), ".codeium", "windsurf", "mcp_config.json"),
21490
21664
  label: "Windsurf (~/.codeium/windsurf/mcp_config.json)"
21491
21665
  }
21492
21666
  ];
21493
21667
  const resolvedRepoPaths = repoPaths ?? getOlamRepoPaths();
21494
21668
  for (const repoPath of resolvedRepoPaths) {
21495
21669
  sourceDefs.push({
21496
- path: path41.join(repoPath, ".mcp.json"),
21497
- label: `.mcp.json (${path41.basename(repoPath)})`
21670
+ path: path42.join(repoPath, ".mcp.json"),
21671
+ label: `.mcp.json (${path42.basename(repoPath)})`
21498
21672
  });
21499
21673
  }
21500
21674
  const reads = await Promise.all(
@@ -21670,30 +21844,72 @@ Importing ${selected.length} server(s)\u2026`);
21670
21844
  });
21671
21845
  }
21672
21846
 
21847
+ // src/commands/mcp/revoke.ts
21848
+ init_output();
21849
+ import pc26 from "picocolors";
21850
+ function registerMcpRevoke(cmd) {
21851
+ cmd.command("revoke <user> <world> <service>").description("Revoke a user's MCP service entitlement (multi-tenant mode only)").action(async (user, world, service) => {
21852
+ const multiTenant = (process.env["OLAM_MCP_MULTI_TENANT"] ?? "").toLowerCase() === "true";
21853
+ if (!multiTenant) {
21854
+ console.warn(pc26.yellow("\u26A0 revocation only meaningful in multi_tenant mode \u2014 no-op"));
21855
+ return;
21856
+ }
21857
+ const baseUrl = process.env["OLAM_MCP_AUTH_SERVICE_URL"] ?? "http://127.0.0.1:9998";
21858
+ const secret = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
21859
+ const headers = { "Content-Type": "application/json" };
21860
+ if (secret) headers["X-Olam-Mcp-Secret"] = secret;
21861
+ let res;
21862
+ try {
21863
+ res = await fetch(
21864
+ `${baseUrl}/entitlement/${encodeURIComponent(user)}/${encodeURIComponent(world)}/${encodeURIComponent(service)}`,
21865
+ { method: "DELETE", headers }
21866
+ );
21867
+ } catch (err) {
21868
+ printError(`Could not reach mcp-auth service: ${err instanceof Error ? err.message : "unknown"}`);
21869
+ process.exitCode = 1;
21870
+ return;
21871
+ }
21872
+ if (!res.ok) {
21873
+ let msg = `HTTP ${res.status}`;
21874
+ try {
21875
+ const body = await res.json();
21876
+ if (body.error) msg = body.error;
21877
+ } catch {
21878
+ }
21879
+ printError(`Revoke failed: ${msg}`);
21880
+ process.exitCode = 1;
21881
+ return;
21882
+ }
21883
+ console.log(pc26.green(`\u2713 Revoked: ${user} / ${world} / ${service}`));
21884
+ console.log(pc26.dim(" Next fetch-creds call returns 403; token cleared on next merge."));
21885
+ });
21886
+ }
21887
+
21673
21888
  // src/commands/mcp/index.ts
21674
21889
  function registerMcp(program2) {
21675
- const mcp = program2.command("mcp").description("Manage MCP server credentials (login, add, list, remove, import)");
21890
+ const mcp = program2.command("mcp").description("Manage MCP server credentials (login, add, list, remove, import, revoke)");
21676
21891
  registerMcpLogin(mcp);
21677
21892
  registerMcpAdd(mcp);
21678
21893
  registerMcpList(mcp);
21679
21894
  registerMcpRemove(mcp);
21680
21895
  registerMcpStatus(mcp);
21681
21896
  registerMcpImport(mcp);
21897
+ registerMcpRevoke(mcp);
21682
21898
  }
21683
21899
 
21684
21900
  // src/pleri-config.ts
21685
- import * as fs38 from "node:fs";
21686
- import * as path42 from "node:path";
21901
+ import * as fs39 from "node:fs";
21902
+ import * as path43 from "node:path";
21687
21903
  function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
21688
21904
  if (process.env.PLERI_BASE_URL) {
21689
21905
  return true;
21690
21906
  }
21691
- const configPath = path42.join(configDir, "config.yaml");
21692
- if (!fs38.existsSync(configPath)) {
21907
+ const configPath = path43.join(configDir, "config.yaml");
21908
+ if (!fs39.existsSync(configPath)) {
21693
21909
  return false;
21694
21910
  }
21695
21911
  try {
21696
- const contents = fs38.readFileSync(configPath, "utf8");
21912
+ const contents = fs39.readFileSync(configPath, "utf8");
21697
21913
  return /^[^#\n]*\bpleri:/m.test(contents);
21698
21914
  } catch {
21699
21915
  return false;
@@ -21704,14 +21920,14 @@ function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
21704
21920
  var program = new Command();
21705
21921
  function readCliVersion() {
21706
21922
  try {
21707
- const here = path43.dirname(fileURLToPath4(import.meta.url));
21923
+ const here = path44.dirname(fileURLToPath4(import.meta.url));
21708
21924
  for (const candidate of [
21709
- path43.join(here, "package.json"),
21710
- path43.join(here, "..", "package.json"),
21711
- path43.join(here, "..", "..", "package.json")
21925
+ path44.join(here, "package.json"),
21926
+ path44.join(here, "..", "package.json"),
21927
+ path44.join(here, "..", "..", "package.json")
21712
21928
  ]) {
21713
- if (fs39.existsSync(candidate)) {
21714
- const pkg = JSON.parse(fs39.readFileSync(candidate, "utf-8"));
21929
+ if (fs40.existsSync(candidate)) {
21930
+ const pkg = JSON.parse(fs40.readFileSync(candidate, "utf-8"));
21715
21931
  if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
21716
21932
  }
21717
21933
  }