@pleri/olam-cli 0.1.104 → 0.1.106

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
@@ -487,8 +487,8 @@ var init_parseUtil = __esm({
487
487
  init_errors();
488
488
  init_en();
489
489
  makeIssue = (params) => {
490
- const { data, path: path50, errorMaps, issueData } = params;
491
- const fullPath = [...path50, ...issueData.path || []];
490
+ const { data, path: path52, errorMaps, issueData } = params;
491
+ const fullPath = [...path52, ...issueData.path || []];
492
492
  const fullIssue = {
493
493
  ...issueData,
494
494
  path: fullPath
@@ -796,11 +796,11 @@ var init_types = __esm({
796
796
  init_parseUtil();
797
797
  init_util();
798
798
  ParseInputLazyPath = class {
799
- constructor(parent, value, path50, key) {
799
+ constructor(parent, value, path52, key) {
800
800
  this._cachedPath = [];
801
801
  this.parent = parent;
802
802
  this.data = value;
803
- this._path = path50;
803
+ this._path = path52;
804
804
  this._key = key;
805
805
  }
806
806
  get path() {
@@ -4281,7 +4281,7 @@ import YAML from "yaml";
4281
4281
  function bootstrapStepCmd(entry) {
4282
4282
  return typeof entry === "string" ? entry : entry.cmd;
4283
4283
  }
4284
- function refineForbiddenKeys(value, path50, ctx, rejectSource) {
4284
+ function refineForbiddenKeys(value, path52, ctx, rejectSource) {
4285
4285
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4286
4286
  return;
4287
4287
  }
@@ -4289,12 +4289,12 @@ function refineForbiddenKeys(value, path50, ctx, rejectSource) {
4289
4289
  if (FORBIDDEN_KEYS.has(key)) {
4290
4290
  ctx.addIssue({
4291
4291
  code: external_exports.ZodIssueCode.custom,
4292
- path: [...path50, key],
4292
+ path: [...path52, key],
4293
4293
  message: `forbidden key "${key}" (prototype-pollution surface)`
4294
4294
  });
4295
4295
  continue;
4296
4296
  }
4297
- if (rejectSource && path50.length === 0 && key === "source") {
4297
+ if (rejectSource && path52.length === 0 && key === "source") {
4298
4298
  ctx.addIssue({
4299
4299
  code: external_exports.ZodIssueCode.custom,
4300
4300
  path: ["source"],
@@ -4302,21 +4302,21 @@ function refineForbiddenKeys(value, path50, ctx, rejectSource) {
4302
4302
  });
4303
4303
  continue;
4304
4304
  }
4305
- refineForbiddenKeys(value[key], [...path50, key], ctx, false);
4305
+ refineForbiddenKeys(value[key], [...path52, key], ctx, false);
4306
4306
  }
4307
4307
  }
4308
- function rejectForbiddenKeys(value, path50, rejectSource) {
4308
+ function rejectForbiddenKeys(value, path52, rejectSource) {
4309
4309
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
4310
4310
  return;
4311
4311
  }
4312
4312
  for (const key of Object.keys(value)) {
4313
4313
  if (FORBIDDEN_KEYS.has(key)) {
4314
- throw new Error(`[manifest] ${path50}: forbidden key "${key}" (prototype-pollution surface)`);
4314
+ throw new Error(`[manifest] ${path52}: forbidden key "${key}" (prototype-pollution surface)`);
4315
4315
  }
4316
4316
  if (rejectSource && key === "source") {
4317
- throw new Error(`[manifest] ${path50}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4317
+ throw new Error(`[manifest] ${path52}: top-level "source" is loader-stamped \u2014 manifests must not author it`);
4318
4318
  }
4319
- rejectForbiddenKeys(value[key], `${path50}.${key}`, false);
4319
+ rejectForbiddenKeys(value[key], `${path52}.${key}`, false);
4320
4320
  }
4321
4321
  }
4322
4322
  function unknownTopLevelKeys(parsed) {
@@ -5309,8 +5309,8 @@ var init_client = __esm({
5309
5309
  throw new Error(`failed to report rate-limit for ${accountId} (HTTP ${res.status})`);
5310
5310
  }
5311
5311
  }
5312
- async request(method, path50, body, attempt = 0) {
5313
- const url = `${this.baseUrl}${path50}`;
5312
+ async request(method, path52, body, attempt = 0) {
5313
+ const url = `${this.baseUrl}${path52}`;
5314
5314
  const controller = new AbortController();
5315
5315
  const timer = setTimeout(() => controller.abort(), this.timeoutMs);
5316
5316
  const headers = {};
@@ -5328,7 +5328,7 @@ var init_client = __esm({
5328
5328
  } catch (err) {
5329
5329
  if (attempt < RETRY_COUNT && isTransient(err)) {
5330
5330
  await sleep(RETRY_BACKOFF_MS * (attempt + 1));
5331
- return this.request(method, path50, body, attempt + 1);
5331
+ return this.request(method, path52, body, attempt + 1);
5332
5332
  }
5333
5333
  throw err;
5334
5334
  } finally {
@@ -6877,8 +6877,8 @@ var init_provider3 = __esm({
6877
6877
  // -----------------------------------------------------------------------
6878
6878
  // Internal fetch helper
6879
6879
  // -----------------------------------------------------------------------
6880
- async request(path50, method, body) {
6881
- const url = `${this.config.workerUrl}${path50}`;
6880
+ async request(path52, method, body) {
6881
+ const url = `${this.config.workerUrl}${path52}`;
6882
6882
  const bearer = await this.config.mintToken();
6883
6883
  const headers = {
6884
6884
  Authorization: `Bearer ${bearer}`
@@ -8170,8 +8170,8 @@ import { execFileSync as execFileSync3 } from "node:child_process";
8170
8170
  import * as fs14 from "node:fs";
8171
8171
  import * as os9 from "node:os";
8172
8172
  import * as path15 from "node:path";
8173
- function expandHome(p, homedir27) {
8174
- return p.replace(/^~(?=$|\/|\\)/, homedir27());
8173
+ function expandHome(p, homedir28) {
8174
+ return p.replace(/^~(?=$|\/|\\)/, homedir28());
8175
8175
  }
8176
8176
  function sanitizeRepoFilename(name) {
8177
8177
  const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_");
@@ -8194,7 +8194,7 @@ ${stderr}`;
8194
8194
  }
8195
8195
  function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8196
8196
  const exec = deps.exec ?? ((cmd, args, opts) => execFileSync3(cmd, args, opts));
8197
- const homedir27 = deps.homedir ?? (() => os9.homedir());
8197
+ const homedir28 = deps.homedir ?? (() => os9.homedir());
8198
8198
  const baselineDir = path15.join(workspacePath, ".olam", "baseline");
8199
8199
  try {
8200
8200
  fs14.mkdirSync(baselineDir, { recursive: true });
@@ -8210,7 +8210,7 @@ function snapshotBaselineDiff(repos, workspacePath, deps = {}) {
8210
8210
  continue;
8211
8211
  const filename = `${sanitizeRepoFilename(repo.name)}.diff`;
8212
8212
  const outPath = path15.join(baselineDir, filename);
8213
- const repoPath = expandHome(repo.path, homedir27);
8213
+ const repoPath = expandHome(repo.path, homedir28);
8214
8214
  if (!fs14.existsSync(repoPath)) {
8215
8215
  writeBaselineFile(outPath, `# repo: ${repo.name}
8216
8216
  # (skipped: path ${repoPath} does not exist)
@@ -13822,10 +13822,10 @@ async function readHostCpToken2() {
13822
13822
  if (!fs25.existsSync(tp)) return null;
13823
13823
  return fs25.readFileSync(tp, "utf-8").trim();
13824
13824
  }
13825
- async function callHostCpProxy(method, worldId, path50, body) {
13825
+ async function callHostCpProxy(method, worldId, path52, body) {
13826
13826
  const token = await readHostCpToken2();
13827
13827
  if (!token) return { ok: false, status: 0, error: "no token (host CP not started)" };
13828
- const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path50}`;
13828
+ const url = `http://127.0.0.1:${HOST_CP_PORT}/api/world/${encodeURIComponent(worldId)}${path52}`;
13829
13829
  try {
13830
13830
  const headers = {
13831
13831
  Authorization: `Bearer ${token}`
@@ -14710,9 +14710,9 @@ var UnknownArchetypeError = class extends Error {
14710
14710
  };
14711
14711
  var ArchetypeCycleError = class extends Error {
14712
14712
  path;
14713
- constructor(path50) {
14714
- super(`Archetype inheritance cycle detected: ${path50.join(" \u2192 ")} \u2192 ${path50[0] ?? "?"}`);
14715
- this.path = path50;
14713
+ constructor(path52) {
14714
+ super(`Archetype inheritance cycle detected: ${path52.join(" \u2192 ")} \u2192 ${path52[0] ?? "?"}`);
14715
+ this.path = path52;
14716
14716
  this.name = "ArchetypeCycleError";
14717
14717
  }
14718
14718
  };
@@ -15082,6 +15082,12 @@ async function runBootstrap2(opts, deps = {}) {
15082
15082
  if (digests["mcp-auth"]) {
15083
15083
  imageRefs.push({ name: "mcp-auth", ref: `${registry}/olam-mcp-auth@${digests["mcp-auth"]}` });
15084
15084
  }
15085
+ if (digests["devbox-base"]) {
15086
+ imageRefs.push({
15087
+ name: "devbox-base",
15088
+ ref: `${registry}/olam-devbox@${digests["devbox-base"]}`
15089
+ });
15090
+ }
15085
15091
  const pullSpinner = ora(`Pulling images (${imageRefs.length} parallel)`).start();
15086
15092
  const pullStart = Date.now();
15087
15093
  const pullResults = await Promise.all(
@@ -15111,7 +15117,7 @@ async function runBootstrap2(opts, deps = {}) {
15111
15117
  summary: `pull failed: ${failed.map((r) => r.name).join(", ")}`
15112
15118
  };
15113
15119
  }
15114
- pullSpinner.succeed(`Pulled 3 images in ${pullElapsed}s`);
15120
+ pullSpinner.succeed(`Pulled ${imageRefs.length} images in ${pullElapsed}s`);
15115
15121
  const handshakeSpinner = ora("Verifying olam.protocol.versions handshake").start();
15116
15122
  for (const { name, ref } of imageRefs) {
15117
15123
  const inspect = await docker2.inspectLabel(ref, "olam.protocol.versions");
@@ -15149,6 +15155,15 @@ async function runBootstrap2(opts, deps = {}) {
15149
15155
  if (mcpAuthRef) {
15150
15156
  tagPlan.push({ name: "mcp-auth", from: mcpAuthRef.ref, to: "olam-mcp-auth:local" });
15151
15157
  }
15158
+ const devboxBaseRef = imageRefs.find((r) => r.name === "devbox-base");
15159
+ if (devboxBaseRef) {
15160
+ tagPlan.push({ name: "devbox-base (bare)", from: devboxBaseRef.ref, to: "olam-devbox:base" });
15161
+ tagPlan.push({
15162
+ name: "devbox-base (registry)",
15163
+ from: devboxBaseRef.ref,
15164
+ to: `${registry}/olam-devbox:base`
15165
+ });
15166
+ }
15152
15167
  for (const { name, from, to } of tagPlan) {
15153
15168
  const result = await docker2.tag(from, to);
15154
15169
  if (result.exitCode !== 0) {
@@ -16078,9 +16093,9 @@ function formatFreshnessWarning(result, image = DEFAULT_DEVBOX_IMAGE) {
16078
16093
  "These source files have changed since the image was built; the",
16079
16094
  "changes will NOT take effect in fresh worlds until you rebuild:"
16080
16095
  ];
16081
- for (const { path: path50, mtimeMs } of result.newerSources) {
16096
+ for (const { path: path52, mtimeMs } of result.newerSources) {
16082
16097
  const when = new Date(mtimeMs).toISOString();
16083
- lines.push(` \u2022 ${path50} (modified ${when})`);
16098
+ lines.push(` \u2022 ${path52} (modified ${when})`);
16084
16099
  }
16085
16100
  lines.push("");
16086
16101
  lines.push("Rebuild with:");
@@ -16239,15 +16254,15 @@ init_host_cp();
16239
16254
  var HOST_CP_URL = "http://127.0.0.1:19000";
16240
16255
  async function readHostCpTokenForCreate() {
16241
16256
  try {
16242
- const { default: fs48 } = await import("node:fs");
16257
+ const { default: fs50 } = await import("node:fs");
16243
16258
  const { default: os28 } = await import("node:os");
16244
- const { default: path50 } = await import("node:path");
16245
- const tp = path50.join(
16246
- process.env.OLAM_HOME ?? path50.join(os28.homedir(), ".olam"),
16259
+ const { default: path52 } = await import("node:path");
16260
+ const tp = path52.join(
16261
+ process.env.OLAM_HOME ?? path52.join(os28.homedir(), ".olam"),
16247
16262
  "host-cp.token"
16248
16263
  );
16249
- if (!fs48.existsSync(tp)) return null;
16250
- return fs48.readFileSync(tp, "utf-8").trim();
16264
+ if (!fs50.existsSync(tp)) return null;
16265
+ return fs50.readFileSync(tp, "utf-8").trim();
16251
16266
  } catch {
16252
16267
  return null;
16253
16268
  }
@@ -16610,12 +16625,12 @@ function defaultNameFromPrompt(prompt) {
16610
16625
  }
16611
16626
  async function readHostCpToken3() {
16612
16627
  try {
16613
- const { default: fs48 } = await import("node:fs");
16628
+ const { default: fs50 } = await import("node:fs");
16614
16629
  const { default: os28 } = await import("node:os");
16615
- const { default: path50 } = await import("node:path");
16616
- const tp = path50.join(os28.homedir(), ".olam", "host-cp.token");
16617
- if (!fs48.existsSync(tp)) return null;
16618
- const raw = fs48.readFileSync(tp, "utf-8").trim();
16630
+ const { default: path52 } = await import("node:path");
16631
+ const tp = path52.join(os28.homedir(), ".olam", "host-cp.token");
16632
+ if (!fs50.existsSync(tp)) return null;
16633
+ const raw = fs50.readFileSync(tp, "utf-8").trim();
16619
16634
  return raw.length > 0 ? raw : null;
16620
16635
  } catch {
16621
16636
  return null;
@@ -20346,11 +20361,11 @@ function zodIssueToError(issue, doc, lineCounter) {
20346
20361
  suggestion: deriveSuggestion(issue)
20347
20362
  };
20348
20363
  }
20349
- function formatJsonPath(path50) {
20350
- if (path50.length === 0)
20364
+ function formatJsonPath(path52) {
20365
+ if (path52.length === 0)
20351
20366
  return "<root>";
20352
20367
  let out = "";
20353
- for (const seg of path50) {
20368
+ for (const seg of path52) {
20354
20369
  if (typeof seg === "number") {
20355
20370
  out += `[${seg}]`;
20356
20371
  } else {
@@ -20359,11 +20374,11 @@ function formatJsonPath(path50) {
20359
20374
  }
20360
20375
  return out;
20361
20376
  }
20362
- function resolveYamlLocation(path50, doc, lineCounter) {
20377
+ function resolveYamlLocation(path52, doc, lineCounter) {
20363
20378
  let bestLine = 0;
20364
20379
  let bestColumn = 0;
20365
- for (let depth = path50.length; depth >= 0; depth -= 1) {
20366
- const segment = path50.slice(0, depth);
20380
+ for (let depth = path52.length; depth >= 0; depth -= 1) {
20381
+ const segment = path52.slice(0, depth);
20367
20382
  try {
20368
20383
  const node = doc.getIn(segment, true);
20369
20384
  if (node && typeof node === "object" && "range" in node) {
@@ -20581,11 +20596,11 @@ function topoSort(nodes) {
20581
20596
  }
20582
20597
  function traceCycle(start, byId) {
20583
20598
  const seen = /* @__PURE__ */ new Set();
20584
- const path50 = [];
20599
+ const path52 = [];
20585
20600
  let current = start;
20586
20601
  while (current && !seen.has(current)) {
20587
20602
  seen.add(current);
20588
- path50.push(current);
20603
+ path52.push(current);
20589
20604
  const node = byId.get(current);
20590
20605
  const next = node?.dependsOn[0];
20591
20606
  if (next === void 0)
@@ -20593,10 +20608,10 @@ function traceCycle(start, byId) {
20593
20608
  current = next;
20594
20609
  }
20595
20610
  if (current && seen.has(current)) {
20596
- const idx = path50.indexOf(current);
20597
- return [...path50.slice(idx), current];
20611
+ const idx = path52.indexOf(current);
20612
+ return [...path52.slice(idx), current];
20598
20613
  }
20599
- return path50;
20614
+ return path52;
20600
20615
  }
20601
20616
 
20602
20617
  // ../core/dist/executor/types.js
@@ -25767,8 +25782,8 @@ var SECRET = process.env["OLAM_MCP_AUTH_SECRET"] ?? "";
25767
25782
  function authHeaders() {
25768
25783
  return SECRET ? { "X-Olam-Mcp-Secret": SECRET } : {};
25769
25784
  }
25770
- async function apiFetch(path50, init = {}) {
25771
- const res = await fetch(`${BASE_URL}${path50}`, {
25785
+ async function apiFetch(path52, init = {}) {
25786
+ const res = await fetch(`${BASE_URL}${path52}`, {
25772
25787
  ...init,
25773
25788
  headers: {
25774
25789
  "Content-Type": "application/json",
@@ -26346,8 +26361,8 @@ function registerMcp(program2) {
26346
26361
 
26347
26362
  // src/commands/kg-build.ts
26348
26363
  import { spawnSync as spawnSync14 } from "node:child_process";
26349
- import fs46 from "node:fs";
26350
- import path48 from "node:path";
26364
+ import fs48 from "node:fs";
26365
+ import path50 from "node:path";
26351
26366
 
26352
26367
  // ../core/dist/kg/storage-paths.js
26353
26368
  import { homedir as homedir26 } from "node:os";
@@ -26380,17 +26395,17 @@ function kgRoot() {
26380
26395
  function worldsRoot() {
26381
26396
  return join48(olamHome3(), "worlds");
26382
26397
  }
26383
- function assertWithinPrefix(path50, prefix, label) {
26384
- if (!path50.startsWith(prefix + "/")) {
26385
- throw new Error(`${label} escape: ${path50} not under ${prefix}/`);
26398
+ function assertWithinPrefix(path52, prefix, label) {
26399
+ if (!path52.startsWith(prefix + "/")) {
26400
+ throw new Error(`${label} escape: ${path52} not under ${prefix}/`);
26386
26401
  }
26387
26402
  }
26388
26403
  function kgPristinePath(workspace) {
26389
26404
  validateWorkspaceName(workspace);
26390
26405
  const root = kgRoot();
26391
- const path50 = resolve10(join48(root, workspace));
26392
- assertWithinPrefix(path50, root, "kgPristinePath");
26393
- return path50;
26406
+ const path52 = resolve10(join48(root, workspace));
26407
+ assertWithinPrefix(path52, root, "kgPristinePath");
26408
+ return path52;
26394
26409
  }
26395
26410
  var KG_PATHS_INTERNALS = Object.freeze({
26396
26411
  olamHome: olamHome3,
@@ -26400,10 +26415,394 @@ var KG_PATHS_INTERNALS = Object.freeze({
26400
26415
 
26401
26416
  // src/commands/kg-build.ts
26402
26417
  init_output();
26418
+
26419
+ // src/commands/kg-status.ts
26420
+ import fs46 from "node:fs";
26421
+ import { homedir as homedir27 } from "node:os";
26422
+ import path48 from "node:path";
26423
+ init_output();
26424
+ var SOFT_CAP_BYTES = 15e8;
26425
+ var HARD_CAP_BYTES = 5e9;
26426
+ function olamHome4() {
26427
+ return process.env.OLAM_HOME ?? path48.join(homedir27(), ".olam");
26428
+ }
26429
+ function kgRoot2() {
26430
+ return path48.join(olamHome4(), "kg");
26431
+ }
26432
+ function worldsRoot2() {
26433
+ return path48.join(olamHome4(), "worlds");
26434
+ }
26435
+ function dirSizeBytes(dir) {
26436
+ if (!fs46.existsSync(dir)) return 0;
26437
+ let total = 0;
26438
+ const stack = [dir];
26439
+ while (stack.length > 0) {
26440
+ const cur = stack.pop();
26441
+ let entries;
26442
+ try {
26443
+ entries = fs46.readdirSync(cur, { withFileTypes: true });
26444
+ } catch {
26445
+ continue;
26446
+ }
26447
+ for (const entry of entries) {
26448
+ const full = path48.join(cur, entry.name);
26449
+ if (entry.isSymbolicLink()) continue;
26450
+ if (entry.isDirectory()) {
26451
+ stack.push(full);
26452
+ continue;
26453
+ }
26454
+ try {
26455
+ total += fs46.statSync(full).size;
26456
+ } catch {
26457
+ }
26458
+ }
26459
+ }
26460
+ return total;
26461
+ }
26462
+ function formatBytes4(n) {
26463
+ if (n < 1024) return `${n} B`;
26464
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
26465
+ if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
26466
+ return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
26467
+ }
26468
+ function readFreshness(workspace) {
26469
+ const file = path48.join(kgPristinePath(workspace), "freshness.json");
26470
+ if (!fs46.existsSync(file)) return null;
26471
+ try {
26472
+ const raw = JSON.parse(fs46.readFileSync(file, "utf-8"));
26473
+ if (raw && typeof raw === "object") return raw;
26474
+ return null;
26475
+ } catch {
26476
+ return null;
26477
+ }
26478
+ }
26479
+ function readOverlayNodeCount(graphifyOutDir) {
26480
+ const graphPath = path48.join(graphifyOutDir, "graph.json");
26481
+ if (!fs46.existsSync(graphPath)) return null;
26482
+ try {
26483
+ const raw = JSON.parse(fs46.readFileSync(graphPath, "utf-8"));
26484
+ if (raw && typeof raw === "object") {
26485
+ const nodes = raw.nodes;
26486
+ if (Array.isArray(nodes)) return nodes.length;
26487
+ }
26488
+ return null;
26489
+ } catch {
26490
+ return null;
26491
+ }
26492
+ }
26493
+ function listOverlays() {
26494
+ const root = worldsRoot2();
26495
+ if (!fs46.existsSync(root)) return [];
26496
+ const records = [];
26497
+ let worldDirs;
26498
+ try {
26499
+ worldDirs = fs46.readdirSync(root, { withFileTypes: true });
26500
+ } catch {
26501
+ return [];
26502
+ }
26503
+ for (const worldEntry of worldDirs) {
26504
+ if (!worldEntry.isDirectory()) continue;
26505
+ const worldId = worldEntry.name;
26506
+ const worldDir = path48.join(root, worldId);
26507
+ let cloneDirs;
26508
+ try {
26509
+ cloneDirs = fs46.readdirSync(worldDir, { withFileTypes: true });
26510
+ } catch {
26511
+ continue;
26512
+ }
26513
+ for (const cloneEntry of cloneDirs) {
26514
+ if (!cloneEntry.isDirectory()) continue;
26515
+ const graphifyOut = path48.join(worldDir, cloneEntry.name, "graphify-out");
26516
+ if (!fs46.existsSync(graphifyOut)) continue;
26517
+ records.push({
26518
+ world_id: worldId,
26519
+ clone_dir: cloneEntry.name,
26520
+ graphify_out_path: graphifyOut,
26521
+ graphify_out_size_bytes: dirSizeBytes(graphifyOut),
26522
+ node_count: readOverlayNodeCount(graphifyOut)
26523
+ });
26524
+ }
26525
+ }
26526
+ return records;
26527
+ }
26528
+ function listPristines(overlays) {
26529
+ const root = kgRoot2();
26530
+ if (!fs46.existsSync(root)) return [];
26531
+ const records = [];
26532
+ let entries;
26533
+ try {
26534
+ entries = fs46.readdirSync(root, { withFileTypes: true });
26535
+ } catch {
26536
+ return [];
26537
+ }
26538
+ for (const entry of entries) {
26539
+ if (!entry.isDirectory()) continue;
26540
+ const workspace = entry.name;
26541
+ try {
26542
+ validateWorkspaceName(workspace);
26543
+ } catch {
26544
+ continue;
26545
+ }
26546
+ const fresh = readFreshness(workspace);
26547
+ const graphifyOut = path48.join(kgPristinePath(workspace), "graphify-out");
26548
+ const size = dirSizeBytes(graphifyOut);
26549
+ const worldCount = overlays.filter((o) => o.clone_dir === workspace).length;
26550
+ records.push({
26551
+ workspace,
26552
+ built_at: fresh?.built_at ?? null,
26553
+ node_count: fresh?.node_count ?? null,
26554
+ graphify_version: fresh?.graphify_version ?? null,
26555
+ graphify_out_size_bytes: size,
26556
+ world_count: worldCount
26557
+ });
26558
+ }
26559
+ return records.sort((a, b) => a.workspace.localeCompare(b.workspace));
26560
+ }
26561
+ function classifyCap(totalBytes) {
26562
+ if (totalBytes >= HARD_CAP_BYTES) return "hard-critical";
26563
+ if (totalBytes >= SOFT_CAP_BYTES) return "soft-warn";
26564
+ return "ok";
26565
+ }
26566
+ function buildReport(workspaceFilter) {
26567
+ if (workspaceFilter) validateWorkspaceName(workspaceFilter);
26568
+ const allOverlays = listOverlays();
26569
+ const allPristines = listPristines(allOverlays);
26570
+ const pristines = workspaceFilter ? allPristines.filter((p) => p.workspace === workspaceFilter) : allPristines;
26571
+ const overlays = workspaceFilter ? allOverlays.filter((o) => o.clone_dir === workspaceFilter) : allOverlays;
26572
+ const totalBytes = pristines.reduce((acc, p) => acc + p.graphify_out_size_bytes, 0) + overlays.reduce((acc, o) => acc + o.graphify_out_size_bytes, 0);
26573
+ return {
26574
+ pristines,
26575
+ overlays,
26576
+ total_bytes: totalBytes,
26577
+ cap_status: classifyCap(totalBytes)
26578
+ };
26579
+ }
26580
+ function renderHuman(report, workspaceFilter) {
26581
+ const lines = [];
26582
+ if (report.pristines.length === 0 && report.overlays.length === 0) {
26583
+ if (workspaceFilter) {
26584
+ lines.push(`No KG state for workspace "${workspaceFilter}".`);
26585
+ } else {
26586
+ lines.push("No KG state on this host yet. Run `olam kg build` from a workspace clone.");
26587
+ }
26588
+ return lines.join("\n");
26589
+ }
26590
+ if (report.pristines.length > 0) {
26591
+ lines.push("Pristines:");
26592
+ lines.push(
26593
+ [
26594
+ "workspace".padEnd(24),
26595
+ "built_at".padEnd(22),
26596
+ "nodes".padStart(8),
26597
+ "size".padStart(10),
26598
+ "worlds".padStart(7)
26599
+ ].join(" ")
26600
+ );
26601
+ lines.push("-".repeat(74));
26602
+ for (const p of report.pristines) {
26603
+ lines.push(
26604
+ [
26605
+ p.workspace.padEnd(24),
26606
+ (p.built_at ?? "\u2014").padEnd(22),
26607
+ String(p.node_count ?? "\u2014").padStart(8),
26608
+ formatBytes4(p.graphify_out_size_bytes).padStart(10),
26609
+ String(p.world_count).padStart(7)
26610
+ ].join(" ")
26611
+ );
26612
+ }
26613
+ lines.push("");
26614
+ }
26615
+ if (report.overlays.length > 0) {
26616
+ lines.push("Overlays:");
26617
+ lines.push(
26618
+ [
26619
+ "world_id".padEnd(24),
26620
+ "clone".padEnd(20),
26621
+ "nodes".padStart(8),
26622
+ "size".padStart(10)
26623
+ ].join(" ")
26624
+ );
26625
+ lines.push("-".repeat(66));
26626
+ for (const o of report.overlays) {
26627
+ lines.push(
26628
+ [
26629
+ o.world_id.padEnd(24),
26630
+ o.clone_dir.padEnd(20),
26631
+ String(o.node_count ?? "\u2014").padStart(8),
26632
+ formatBytes4(o.graphify_out_size_bytes).padStart(10)
26633
+ ].join(" ")
26634
+ );
26635
+ }
26636
+ lines.push("");
26637
+ }
26638
+ lines.push(`Total: ${formatBytes4(report.total_bytes)} (cap: ${report.cap_status})`);
26639
+ return lines.join("\n");
26640
+ }
26641
+ async function runKgStatus(workspaceArg, opts) {
26642
+ let report;
26643
+ try {
26644
+ report = buildReport(workspaceArg);
26645
+ } catch (err) {
26646
+ printError(err.message);
26647
+ return {
26648
+ exitCode: 1,
26649
+ report: { pristines: [], overlays: [], total_bytes: 0, cap_status: "ok" }
26650
+ };
26651
+ }
26652
+ if (opts.json) {
26653
+ process.stdout.write(JSON.stringify(report, null, 2) + "\n");
26654
+ } else {
26655
+ process.stdout.write(renderHuman(report, workspaceArg) + "\n");
26656
+ }
26657
+ if (report.cap_status === "soft-warn") {
26658
+ printWarning(
26659
+ `KG storage at ${formatBytes4(report.total_bytes)} \u2014 exceeds 1.5GB soft cap. Consider pruning stale pristines via \`rm -rf ~/.olam/kg/<workspace>\` after operator review.`
26660
+ );
26661
+ } else if (report.cap_status === "hard-critical") {
26662
+ printError(
26663
+ `KG storage at ${formatBytes4(report.total_bytes)} \u2014 exceeds 5GB hard cap. Prune stale pristines or overlays before running \`olam kg build\` again.`
26664
+ );
26665
+ return { exitCode: 1, report };
26666
+ }
26667
+ return { exitCode: 0, report };
26668
+ }
26669
+ function registerKgStatusCommand(kg) {
26670
+ kg.command("status").description(
26671
+ "Report pristine + overlay KG state for every workspace (or one named workspace). Emits 1.5GB soft-cap warning + 5GB hard-cap critical error on stderr."
26672
+ ).argument("[workspace]", "workspace name (lowercase alphanumeric + hyphens/underscores)").option("--json", "emit the report as JSON instead of a compact table").action(async (workspaceArg, opts) => {
26673
+ const r = await runKgStatus(workspaceArg, opts);
26674
+ if (r.exitCode !== 0) process.exitCode = r.exitCode;
26675
+ });
26676
+ }
26677
+
26678
+ // src/commands/kg-watch.ts
26679
+ import { spawn as spawn7 } from "node:child_process";
26680
+ import fs47 from "node:fs";
26681
+ import path49 from "node:path";
26682
+ init_output();
26683
+ function pidFilePath(workspace) {
26684
+ return path49.join(kgPristinePath(workspace), ".watch.pid");
26685
+ }
26686
+ function isPidAlive3(pid) {
26687
+ if (!Number.isInteger(pid) || pid <= 0) return false;
26688
+ try {
26689
+ process.kill(pid, 0);
26690
+ return true;
26691
+ } catch (err) {
26692
+ const code = err.code;
26693
+ if (code === "EPERM") return true;
26694
+ return false;
26695
+ }
26696
+ }
26697
+ function readAndClassifyPid(workspace) {
26698
+ const file = pidFilePath(workspace);
26699
+ if (!fs47.existsSync(file)) return { status: "no-pidfile", pid: null };
26700
+ let pid;
26701
+ try {
26702
+ const raw = fs47.readFileSync(file, "utf-8").trim();
26703
+ pid = Number.parseInt(raw, 10);
26704
+ } catch {
26705
+ fs47.rmSync(file, { force: true });
26706
+ return { status: "stale-reclaimed", pid: null };
26707
+ }
26708
+ if (!Number.isInteger(pid) || pid <= 0) {
26709
+ fs47.rmSync(file, { force: true });
26710
+ return { status: "stale-reclaimed", pid: null };
26711
+ }
26712
+ if (isPidAlive3(pid)) return { status: "active", pid };
26713
+ fs47.rmSync(file, { force: true });
26714
+ return { status: "stale-reclaimed", pid: null };
26715
+ }
26716
+ function writePidFile(workspace, pid) {
26717
+ const file = pidFilePath(workspace);
26718
+ const dir = path49.dirname(file);
26719
+ fs47.mkdirSync(dir, { recursive: true });
26720
+ fs47.writeFileSync(file, String(pid), { encoding: "utf-8" });
26721
+ }
26722
+ function removePidFile(workspace) {
26723
+ const file = pidFilePath(workspace);
26724
+ try {
26725
+ fs47.rmSync(file, { force: true });
26726
+ } catch {
26727
+ }
26728
+ }
26729
+ async function runKgWatch(workspaceArg, opts, deps = {}) {
26730
+ const cwd = deps.cwd ?? opts.cwd ?? process.cwd();
26731
+ const name = workspaceArg ?? path49.basename(cwd).toLowerCase();
26732
+ try {
26733
+ validateWorkspaceName(name);
26734
+ } catch (err) {
26735
+ printError(err.message);
26736
+ return { exitCode: 1, pidWritten: false };
26737
+ }
26738
+ const pristinePath = kgPristinePath(name);
26739
+ const graphPath = path49.join(pristinePath, "graphify-out", "graph.json");
26740
+ const pidState = readAndClassifyPid(name);
26741
+ if (pidState.status === "active") {
26742
+ printError(
26743
+ `olam kg watch ${name} \u2014 already running (PID ${pidState.pid}). Send SIGINT to that process to stop the existing watcher first.`
26744
+ );
26745
+ return { exitCode: 1, pidWritten: false };
26746
+ }
26747
+ if (pidState.status === "stale-reclaimed") {
26748
+ printInfo("stale-pid", `reclaimed dead PID file at ${pidFilePath(name)}`);
26749
+ }
26750
+ const spawnFn = deps.spawnImpl ?? spawn7;
26751
+ const child = spawnFn(
26752
+ "graphify",
26753
+ [cwd, "--watch", "--update", "--graph", graphPath],
26754
+ {
26755
+ cwd,
26756
+ env: process.env,
26757
+ detached: false,
26758
+ stdio: "inherit"
26759
+ }
26760
+ );
26761
+ if (typeof child.pid !== "number") {
26762
+ printError("graphify failed to launch (no PID assigned)");
26763
+ return { exitCode: 1, pidWritten: false };
26764
+ }
26765
+ writePidFile(name, child.pid);
26766
+ printSuccess(`olam kg watch ${name} \u2014 graphify PID ${child.pid} (Ctrl-C to stop)`);
26767
+ if (deps.registerSignalHandlers !== false) {
26768
+ const forward = (sig) => {
26769
+ if (child.pid && !child.killed) {
26770
+ try {
26771
+ process.kill(child.pid, sig);
26772
+ } catch {
26773
+ }
26774
+ }
26775
+ };
26776
+ process.on("SIGINT", () => forward("SIGINT"));
26777
+ process.on("SIGTERM", () => forward("SIGTERM"));
26778
+ }
26779
+ return new Promise((resolve11) => {
26780
+ child.on("exit", (code, signal) => {
26781
+ removePidFile(name);
26782
+ const exitCode = typeof code === "number" ? code : signal === "SIGINT" || signal === "SIGTERM" ? 0 : 1;
26783
+ resolve11({ exitCode, pidWritten: true });
26784
+ });
26785
+ child.on("error", (err) => {
26786
+ removePidFile(name);
26787
+ printError(`graphify subprocess error: ${err.message}`);
26788
+ resolve11({ exitCode: 1, pidWritten: true });
26789
+ });
26790
+ });
26791
+ }
26792
+ function registerKgWatchCommand(kg) {
26793
+ kg.command("watch").description(
26794
+ "Run graphify --watch against a workspace, keeping its pristine KG fresh as host-side commits land. Single-instance via PID file; Ctrl-C cleanly stops."
26795
+ ).argument("[workspace]", "workspace name (lowercase alphanumeric + hyphens/underscores)").action(async (workspaceArg) => {
26796
+ const r = await runKgWatch(workspaceArg, {});
26797
+ if (r.exitCode !== 0) process.exitCode = r.exitCode;
26798
+ });
26799
+ }
26800
+
26801
+ // src/commands/kg-build.ts
26403
26802
  var DEFAULT_DEVBOX_IMAGE2 = "olam-devbox:latest";
26404
26803
  function resolveWorkspace(arg) {
26405
26804
  const cwd = process.cwd();
26406
- const name = arg ?? path48.basename(cwd).toLowerCase();
26805
+ const name = arg ?? path50.basename(cwd).toLowerCase();
26407
26806
  validateWorkspaceName(name);
26408
26807
  return { name, sourcePath: cwd };
26409
26808
  }
@@ -26425,10 +26824,10 @@ function copyWorkspaceToScratch(source, scratch) {
26425
26824
  return "cp-r";
26426
26825
  }
26427
26826
  function parseNodeCount(graphifyOutDir) {
26428
- const graphPath = path48.join(graphifyOutDir, "graph.json");
26429
- if (!fs46.existsSync(graphPath)) return null;
26827
+ const graphPath = path50.join(graphifyOutDir, "graph.json");
26828
+ if (!fs48.existsSync(graphPath)) return null;
26430
26829
  try {
26431
- const content = fs46.readFileSync(graphPath, "utf-8");
26830
+ const content = fs48.readFileSync(graphPath, "utf-8");
26432
26831
  const data = JSON.parse(content);
26433
26832
  return Array.isArray(data.nodes) ? data.nodes.length : null;
26434
26833
  } catch {
@@ -26462,10 +26861,10 @@ async function runKgBuild(workspaceArg, options = {}) {
26462
26861
  return { exitCode: 2 };
26463
26862
  }
26464
26863
  const outDir = kgPristinePath(workspace.name);
26465
- const scratchDir = path48.join(outDir, "scratch");
26466
- fs46.mkdirSync(outDir, { recursive: true });
26467
- if (fs46.existsSync(scratchDir)) fs46.rmSync(scratchDir, { recursive: true, force: true });
26468
- fs46.mkdirSync(scratchDir);
26864
+ const scratchDir = path50.join(outDir, "scratch");
26865
+ fs48.mkdirSync(outDir, { recursive: true });
26866
+ if (fs48.existsSync(scratchDir)) fs48.rmSync(scratchDir, { recursive: true, force: true });
26867
+ fs48.mkdirSync(scratchDir);
26469
26868
  const human = !options.json;
26470
26869
  if (human) {
26471
26870
  printInfo("kg build", `workspace=${workspace.name} source=${workspace.sourcePath}`);
@@ -26493,14 +26892,14 @@ async function runKgBuild(workspaceArg, options = {}) {
26493
26892
  printError(`graphify update failed (exit ${r.status})`);
26494
26893
  return { exitCode: r.status ?? 1 };
26495
26894
  }
26496
- const scratchOut = path48.join(scratchDir, "graphify-out");
26497
- const finalOut = path48.join(outDir, "graphify-out");
26498
- if (!fs46.existsSync(scratchOut)) {
26895
+ const scratchOut = path50.join(scratchDir, "graphify-out");
26896
+ const finalOut = path50.join(outDir, "graphify-out");
26897
+ if (!fs48.existsSync(scratchOut)) {
26499
26898
  printError(`graphify produced no graphify-out/ in scratch (${scratchOut})`);
26500
26899
  return { exitCode: 1 };
26501
26900
  }
26502
- if (fs46.existsSync(finalOut)) fs46.rmSync(finalOut, { recursive: true, force: true });
26503
- fs46.renameSync(scratchOut, finalOut);
26901
+ if (fs48.existsSync(finalOut)) fs48.rmSync(finalOut, { recursive: true, force: true });
26902
+ fs48.renameSync(scratchOut, finalOut);
26504
26903
  const durationMs = Date.now() - started;
26505
26904
  const nodeCount = parseNodeCount(finalOut);
26506
26905
  const version = readGraphifyVersion(image);
@@ -26512,8 +26911,8 @@ async function runKgBuild(workspaceArg, options = {}) {
26512
26911
  workspace: workspace.name,
26513
26912
  scratch_strategy: scratchStrategy
26514
26913
  };
26515
- fs46.writeFileSync(
26516
- path48.join(outDir, "freshness.json"),
26914
+ fs48.writeFileSync(
26915
+ path50.join(outDir, "freshness.json"),
26517
26916
  JSON.stringify(freshness, null, 2) + "\n",
26518
26917
  "utf-8"
26519
26918
  );
@@ -26527,8 +26926,8 @@ async function runKgBuild(workspaceArg, options = {}) {
26527
26926
  }
26528
26927
  return { exitCode: 0, freshness, outputDir: finalOut };
26529
26928
  } finally {
26530
- if (fs46.existsSync(scratchDir)) {
26531
- fs46.rmSync(scratchDir, { recursive: true, force: true });
26929
+ if (fs48.existsSync(scratchDir)) {
26930
+ fs48.rmSync(scratchDir, { recursive: true, force: true });
26532
26931
  }
26533
26932
  }
26534
26933
  }
@@ -26540,21 +26939,23 @@ function registerKg(program2) {
26540
26939
  const r = await runKgBuild(workspaceArg, opts);
26541
26940
  if (r.exitCode !== 0) process.exitCode = r.exitCode;
26542
26941
  });
26942
+ registerKgStatusCommand(kg);
26943
+ registerKgWatchCommand(kg);
26543
26944
  }
26544
26945
 
26545
26946
  // src/pleri-config.ts
26546
- import * as fs47 from "node:fs";
26547
- import * as path49 from "node:path";
26947
+ import * as fs49 from "node:fs";
26948
+ import * as path51 from "node:path";
26548
26949
  function isPleriConfigured(configDir = process.env.OLAM_CONFIG_DIR ?? ".olam") {
26549
26950
  if (process.env.PLERI_BASE_URL) {
26550
26951
  return true;
26551
26952
  }
26552
- const configPath = path49.join(configDir, "config.yaml");
26553
- if (!fs47.existsSync(configPath)) {
26953
+ const configPath = path51.join(configDir, "config.yaml");
26954
+ if (!fs49.existsSync(configPath)) {
26554
26955
  return false;
26555
26956
  }
26556
26957
  try {
26557
- const contents = fs47.readFileSync(configPath, "utf8");
26958
+ const contents = fs49.readFileSync(configPath, "utf8");
26558
26959
  return /^[^#\n]*\bpleri:/m.test(contents);
26559
26960
  } catch {
26560
26961
  return false;