switchroom 0.13.32 → 0.13.33

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.
@@ -29400,6 +29400,220 @@ var init_doctor_agent_smoke = __esm(() => {
29400
29400
  init_client4();
29401
29401
  });
29402
29402
 
29403
+ // src/cli/doctor-vault-broker-durability.ts
29404
+ import { execFileSync as execFileSync14 } from "node:child_process";
29405
+ import { existsSync as existsSync50, statSync as statSync22 } from "node:fs";
29406
+ import { homedir as homedir27 } from "node:os";
29407
+ import { join as join46 } from "node:path";
29408
+ function probeBindMountInode(hostPath, brokerContainerPath, opts) {
29409
+ const statHost = opts?.statHost ?? defaultStatHost;
29410
+ const statBroker = opts?.statBroker ?? defaultStatBroker;
29411
+ const host = statHost(hostPath);
29412
+ if (host === null)
29413
+ return { kind: "host-missing", hostPath };
29414
+ const broker = statBroker(brokerContainerPath);
29415
+ if (broker.kind !== "ok-with-stat")
29416
+ return broker;
29417
+ if (String(host.ino) === broker.ino && host.size === broker.size) {
29418
+ return { kind: "ok" };
29419
+ }
29420
+ return {
29421
+ kind: "mismatch",
29422
+ hostInode: String(host.ino),
29423
+ brokerInode: broker.ino,
29424
+ hostSize: host.size,
29425
+ brokerSize: broker.size
29426
+ };
29427
+ }
29428
+ function defaultStatHost(p) {
29429
+ if (!existsSync50(p))
29430
+ return null;
29431
+ try {
29432
+ const s = statSync22(p, { bigint: true });
29433
+ return { ino: s.ino, size: Number(s.size) };
29434
+ } catch {
29435
+ return null;
29436
+ }
29437
+ }
29438
+ function defaultStatBroker(p) {
29439
+ const r = spawnDockerStat(p);
29440
+ if (r.error || r.status === null)
29441
+ return { kind: "broker-unreachable" };
29442
+ if (r.status !== 0) {
29443
+ if (r.status >= 125)
29444
+ return { kind: "broker-unreachable" };
29445
+ return {
29446
+ kind: "broker-stat-failed",
29447
+ msg: r.stderr?.trim() || `exit ${r.status}`
29448
+ };
29449
+ }
29450
+ const out = r.stdout.trim();
29451
+ const [inoStr, sizeStr] = out.split(/\s+/);
29452
+ const size = Number(sizeStr);
29453
+ if (!inoStr || !Number.isFinite(size)) {
29454
+ return {
29455
+ kind: "broker-stat-failed",
29456
+ msg: `unparseable stat output: ${out}`
29457
+ };
29458
+ }
29459
+ return { kind: "ok-with-stat", ino: inoStr, size };
29460
+ }
29461
+ function spawnDockerStat(p) {
29462
+ try {
29463
+ const stdout = execFileSync14("docker", ["exec", "switchroom-vault-broker", "stat", "-c", "%i %s", p], { stdio: ["ignore", "pipe", "pipe"], timeout: 3000, encoding: "utf8" });
29464
+ return { status: 0, stdout, stderr: "", error: null };
29465
+ } catch (err) {
29466
+ const e = err;
29467
+ return {
29468
+ status: typeof e.status === "number" ? e.status : null,
29469
+ stdout: e.stdout ?? "",
29470
+ stderr: e.stderr ?? "",
29471
+ error: e.status === undefined ? new Error(e.message ?? "spawn failed") : null
29472
+ };
29473
+ }
29474
+ }
29475
+ function formatBindMountResult(name, hostPath, brokerContainerPath, result) {
29476
+ if (result.kind === "ok") {
29477
+ return {
29478
+ name,
29479
+ status: "ok",
29480
+ detail: `${hostPath} == ${brokerContainerPath} (same inode)`
29481
+ };
29482
+ }
29483
+ if (result.kind === "host-missing") {
29484
+ return {
29485
+ name,
29486
+ status: "warn",
29487
+ detail: `host file ${hostPath} missing \u2014 pre-created by \`switchroom apply\` on greenfield`,
29488
+ fix: "Run `switchroom apply` to pre-create the host file at the correct mode"
29489
+ };
29490
+ }
29491
+ if (result.kind === "broker-unreachable") {
29492
+ return {
29493
+ name,
29494
+ status: "skip",
29495
+ detail: "vault-broker container unreachable \u2014 bind mount unverified"
29496
+ };
29497
+ }
29498
+ if (result.kind === "broker-stat-failed") {
29499
+ return {
29500
+ name,
29501
+ status: "warn",
29502
+ detail: `broker stat failed: ${result.msg}`
29503
+ };
29504
+ }
29505
+ return {
29506
+ name,
29507
+ status: "fail",
29508
+ detail: `inode mismatch \u2014 bind mount is NOT wiring host to broker. ` + `host inode=${result.hostInode} size=${result.hostSize}; ` + `broker inode=${result.brokerInode} size=${result.brokerSize}. ` + `The broker is operating on an ephemeral container-local file; ` + `data written there evaporates on container recreate.`,
29509
+ fix: "Run `switchroom apply` to regenerate compose with the correct " + "bind mount, then `docker compose -p switchroom up -d vault-broker` " + "to recreate the broker container."
29510
+ };
29511
+ }
29512
+ function probeBrokerUnlocked(opts) {
29513
+ const status = (opts?.statusProbe ?? defaultBrokerStatusProbe)();
29514
+ if (status === null) {
29515
+ return {
29516
+ name: "vault-broker unlocked (state)",
29517
+ status: "skip",
29518
+ detail: "vault-broker container unreachable"
29519
+ };
29520
+ }
29521
+ if (!status.unlocked) {
29522
+ return {
29523
+ name: "vault-broker unlocked (state)",
29524
+ status: "fail",
29525
+ detail: `broker reports locked despite config \u2014 auto-unlock failed silently. ` + `Common causes: \`/etc/machine-id\` mount missing or differs from the ` + `host the blob was sealed on; vault-auto-unlock blob corrupted; ` + `vault passphrase was rotated without re-running ` + `\`switchroom vault broker enable-auto-unlock\`.`,
29526
+ fix: "Re-run `switchroom vault broker enable-auto-unlock` on the host " + "to re-seal the blob against the current machine-id + passphrase. " + "Then restart the broker (`docker compose -p switchroom restart vault-broker`)."
29527
+ };
29528
+ }
29529
+ return {
29530
+ name: "vault-broker unlocked (state)",
29531
+ status: "ok",
29532
+ detail: `${status.keyCount} key(s) loaded`
29533
+ };
29534
+ }
29535
+ function defaultBrokerStatusProbe() {
29536
+ try {
29537
+ const out = execFileSync14("switchroom", ["vault", "broker", "status"], { stdio: ["ignore", "pipe", "pipe"], timeout: 3000, encoding: "utf8" });
29538
+ const parsed = JSON.parse(out.trim());
29539
+ if (!parsed.running)
29540
+ return null;
29541
+ return { unlocked: parsed.unlocked, keyCount: parsed.keyCount };
29542
+ } catch {
29543
+ return null;
29544
+ }
29545
+ }
29546
+ function runVaultBrokerDurabilityChecks(_config, opts) {
29547
+ const home2 = homedir27();
29548
+ const probe2 = opts?.inodeProbe ?? probeBindMountInode;
29549
+ return [
29550
+ probeBrokerUnlocked(opts?.statusProbe),
29551
+ probeAutoUnlockBlob(home2),
29552
+ probeMachineIdMount(),
29553
+ formatBindMountResult("vault-broker: vault.enc bind mount", join46(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join46(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
29554
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join46(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join46(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
29555
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join46(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join46(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log"))
29556
+ ];
29557
+ }
29558
+ function probeAutoUnlockBlob(home2) {
29559
+ const blobPath = join46(home2, ".switchroom", "vault-auto-unlock");
29560
+ if (!existsSync50(blobPath)) {
29561
+ return {
29562
+ name: "vault-broker: auto-unlock blob",
29563
+ status: "warn",
29564
+ detail: `${blobPath} not present \u2014 broker will fall back to interactive unlock`,
29565
+ fix: "Run `switchroom vault broker enable-auto-unlock` to seal the blob with the current passphrase + machine-id"
29566
+ };
29567
+ }
29568
+ const sz = statSync22(blobPath).size;
29569
+ if (sz === 0) {
29570
+ return {
29571
+ name: "vault-broker: auto-unlock blob",
29572
+ status: "warn",
29573
+ detail: `${blobPath} is 0 bytes (placeholder) \u2014 broker will fall back to interactive unlock`,
29574
+ fix: "Run `switchroom vault broker enable-auto-unlock` to actually seal the blob"
29575
+ };
29576
+ }
29577
+ return {
29578
+ name: "vault-broker: auto-unlock blob",
29579
+ status: "ok",
29580
+ detail: `${blobPath} present (${sz} bytes, machine-bound)`
29581
+ };
29582
+ }
29583
+ function probeMachineIdMount() {
29584
+ const hostExists = existsSync50("/etc/machine-id");
29585
+ if (!hostExists) {
29586
+ return {
29587
+ name: "vault-broker: machine-id passthrough",
29588
+ status: "fail",
29589
+ detail: "/etc/machine-id missing on host \u2014 auto-unlock key derivation impossible",
29590
+ fix: "Generate a machine-id (`systemd-machine-id-setup`) and re-seal the auto-unlock blob"
29591
+ };
29592
+ }
29593
+ const r = spawnDockerStat("/etc/machine-id");
29594
+ if (r.error || r.status === null || r.status >= 125) {
29595
+ return {
29596
+ name: "vault-broker: machine-id passthrough",
29597
+ status: "skip",
29598
+ detail: "vault-broker container unreachable"
29599
+ };
29600
+ }
29601
+ if (r.status !== 0) {
29602
+ return {
29603
+ name: "vault-broker: machine-id passthrough",
29604
+ status: "fail",
29605
+ detail: "broker container has no /etc/machine-id \u2014 compose `/etc/machine-id:/etc/machine-id:ro` mount is missing",
29606
+ fix: "Run `switchroom apply` to regenerate compose with the machine-id passthrough"
29607
+ };
29608
+ }
29609
+ return {
29610
+ name: "vault-broker: machine-id passthrough",
29611
+ status: "ok",
29612
+ detail: "broker reads the host machine-id"
29613
+ };
29614
+ }
29615
+ var init_doctor_vault_broker_durability = () => {};
29616
+
29403
29617
  // src/cli/doctor.ts
29404
29618
  var exports_doctor = {};
29405
29619
  __export(exports_doctor, {
@@ -29445,25 +29659,25 @@ import { execSync as execSync3, spawnSync as spawnSync7 } from "node:child_proce
29445
29659
  import {
29446
29660
  accessSync as accessSync2,
29447
29661
  constants as fsConstants5,
29448
- existsSync as existsSync50,
29662
+ existsSync as existsSync51,
29449
29663
  lstatSync as lstatSync5,
29450
29664
  mkdirSync as mkdirSync27,
29451
29665
  readFileSync as readFileSync45,
29452
29666
  readdirSync as readdirSync19,
29453
- statSync as statSync22
29667
+ statSync as statSync23
29454
29668
  } from "node:fs";
29455
- import { dirname as dirname12, join as join46, resolve as resolve30 } from "node:path";
29669
+ import { dirname as dirname12, join as join47, resolve as resolve30 } from "node:path";
29456
29670
  import { createPublicKey, createPrivateKey } from "node:crypto";
29457
29671
  function findInNvm(bin) {
29458
- const nvmRoot = join46(process.env.HOME ?? "", ".nvm", "versions", "node");
29459
- if (!existsSync50(nvmRoot))
29672
+ const nvmRoot = join47(process.env.HOME ?? "", ".nvm", "versions", "node");
29673
+ if (!existsSync51(nvmRoot))
29460
29674
  return null;
29461
29675
  try {
29462
29676
  const versions = readdirSync19(nvmRoot).sort().reverse();
29463
29677
  for (const v of versions) {
29464
- const candidate = join46(nvmRoot, v, "bin", bin);
29678
+ const candidate = join47(nvmRoot, v, "bin", bin);
29465
29679
  try {
29466
- const s = statSync22(candidate);
29680
+ const s = statSync23(candidate);
29467
29681
  if (s.isFile() || s.isSymbolicLink()) {
29468
29682
  return candidate;
29469
29683
  }
@@ -29626,21 +29840,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
29626
29840
  if (envBrowsersPath && envBrowsersPath.length > 0) {
29627
29841
  cacheLocations.push(envBrowsersPath);
29628
29842
  }
29629
- cacheLocations.push(join46(homeDir, ".cache", "ms-playwright"));
29843
+ cacheLocations.push(join47(homeDir, ".cache", "ms-playwright"));
29630
29844
  for (const cacheDir of cacheLocations) {
29631
- if (!existsSync50(cacheDir))
29845
+ if (!existsSync51(cacheDir))
29632
29846
  continue;
29633
29847
  try {
29634
29848
  const entries = readdirSync19(cacheDir).filter((e) => e.startsWith("chromium"));
29635
29849
  for (const entry of entries) {
29636
29850
  const candidates2 = [
29637
- join46(cacheDir, entry, "chrome-linux64", "chrome"),
29638
- join46(cacheDir, entry, "chrome-linux", "chrome"),
29639
- join46(cacheDir, entry, "chrome-linux64", "headless_shell"),
29640
- join46(cacheDir, entry, "chrome-linux", "headless_shell")
29851
+ join47(cacheDir, entry, "chrome-linux64", "chrome"),
29852
+ join47(cacheDir, entry, "chrome-linux", "chrome"),
29853
+ join47(cacheDir, entry, "chrome-linux64", "headless_shell"),
29854
+ join47(cacheDir, entry, "chrome-linux", "headless_shell")
29641
29855
  ];
29642
29856
  for (const path4 of candidates2) {
29643
- if (existsSync50(path4))
29857
+ if (existsSync51(path4))
29644
29858
  return path4;
29645
29859
  }
29646
29860
  }
@@ -29720,8 +29934,8 @@ function checkConfig(config, configPath) {
29720
29934
  function checkLegacyState() {
29721
29935
  const results = [];
29722
29936
  const h = process.env.HOME ?? "/root";
29723
- const clerkDir = join46(h, LEGACY_STATE_DIR);
29724
- const clerkPresent = existsSync50(clerkDir);
29937
+ const clerkDir = join47(h, LEGACY_STATE_DIR);
29938
+ const clerkPresent = existsSync51(clerkDir);
29725
29939
  results.push({
29726
29940
  name: "legacy ~/.clerk state",
29727
29941
  status: clerkPresent ? "warn" : "ok",
@@ -29730,7 +29944,7 @@ function checkLegacyState() {
29730
29944
  fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
29731
29945
  } : {}
29732
29946
  });
29733
- const legacySock = join46(h, ".switchroom", "vault-broker.sock");
29947
+ const legacySock = join47(h, ".switchroom", "vault-broker.sock");
29734
29948
  let sockStat = null;
29735
29949
  try {
29736
29950
  sockStat = lstatSync5(legacySock);
@@ -29851,7 +30065,7 @@ function checkVault(config) {
29851
30065
  detail: "Approval auth: passphrase (two-factor)"
29852
30066
  };
29853
30067
  const pairsResult = checkVaultBrokerSocketPairs(config);
29854
- if (!existsSync50(vaultPath)) {
30068
+ if (!existsSync51(vaultPath)) {
29855
30069
  return [
29856
30070
  postureResult,
29857
30071
  {
@@ -30026,8 +30240,8 @@ async function checkHindsight(config) {
30026
30240
  }
30027
30241
  function checkPendingRetainsQueue(dir) {
30028
30242
  const home2 = process.env.HOME ?? "";
30029
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join46(home2, ".hindsight", "pending-retains");
30030
- if (!existsSync50(pendingDir)) {
30243
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join47(home2, ".hindsight", "pending-retains");
30244
+ if (!existsSync51(pendingDir)) {
30031
30245
  return {
30032
30246
  name: "pending-retains queue",
30033
30247
  status: "ok",
@@ -30098,7 +30312,7 @@ function tryReadHostFile(path4) {
30098
30312
  }
30099
30313
  }
30100
30314
  function parseEnvFile(path4) {
30101
- if (!existsSync50(path4))
30315
+ if (!existsSync51(path4))
30102
30316
  return {};
30103
30317
  let content;
30104
30318
  try {
@@ -30157,7 +30371,7 @@ async function checkTelegram(config) {
30157
30371
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
30158
30372
  if (plugin !== "switchroom")
30159
30373
  continue;
30160
- const envPath = join46(agentsDir, name, "telegram", ".env");
30374
+ const envPath = join47(agentsDir, name, "telegram", ".env");
30161
30375
  const read = tryReadHostFile(envPath);
30162
30376
  if (read.kind === "eacces") {
30163
30377
  results.push({
@@ -30209,7 +30423,7 @@ async function checkTelegram(config) {
30209
30423
  }
30210
30424
  function checkStartShStale(agentName, startShPath) {
30211
30425
  const label = `${agentName}: start.sh scheduler block`;
30212
- if (!existsSync50(startShPath)) {
30426
+ if (!existsSync51(startShPath)) {
30213
30427
  return {
30214
30428
  name: label,
30215
30429
  status: "warn",
@@ -30240,7 +30454,7 @@ function checkStartShStale(agentName, startShPath) {
30240
30454
  }
30241
30455
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
30242
30456
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
30243
- const path4 = join46(agentDir, "home", ".switchroom");
30457
+ const path4 = join47(agentDir, "home", ".switchroom");
30244
30458
  let stats;
30245
30459
  try {
30246
30460
  stats = lstatSync5(path4);
@@ -30277,8 +30491,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
30277
30491
  }
30278
30492
  function checkRepoHygiene(repoRoot) {
30279
30493
  const results = [];
30280
- const exportDir = join46(repoRoot, "clerk-export");
30281
- if (existsSync50(exportDir)) {
30494
+ const exportDir = join47(repoRoot, "clerk-export");
30495
+ if (existsSync51(exportDir)) {
30282
30496
  results.push({
30283
30497
  name: "repo hygiene: clerk-export/ on disk (#1072)",
30284
30498
  status: "warn",
@@ -30286,8 +30500,8 @@ function checkRepoHygiene(repoRoot) {
30286
30500
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
30287
30501
  });
30288
30502
  }
30289
- const knownTarball = join46(repoRoot, "clerk-export-with-secrets.tar.gz");
30290
- if (existsSync50(knownTarball)) {
30503
+ const knownTarball = join47(repoRoot, "clerk-export-with-secrets.tar.gz");
30504
+ if (existsSync51(knownTarball)) {
30291
30505
  results.push({
30292
30506
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
30293
30507
  status: "warn",
@@ -30304,7 +30518,7 @@ function checkRepoHygiene(repoRoot) {
30304
30518
  results.push({
30305
30519
  name: `repo hygiene: ${name} on disk (#1072)`,
30306
30520
  status: "warn",
30307
- detail: `${join46(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
30521
+ detail: `${join47(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
30308
30522
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
30309
30523
  });
30310
30524
  }
@@ -30327,10 +30541,10 @@ function checkRepoHygiene(repoRoot) {
30327
30541
  }
30328
30542
  function isSwitchroomCheckout(dir) {
30329
30543
  try {
30330
- if (!existsSync50(join46(dir, ".git")))
30544
+ if (!existsSync51(join47(dir, ".git")))
30331
30545
  return false;
30332
- const pkgPath = join46(dir, "package.json");
30333
- if (!existsSync50(pkgPath))
30546
+ const pkgPath = join47(dir, "package.json");
30547
+ if (!existsSync51(pkgPath))
30334
30548
  return false;
30335
30549
  const pkg = JSON.parse(readFileSync45(pkgPath, "utf-8"));
30336
30550
  return pkg.name === "switchroom";
@@ -30345,7 +30559,7 @@ function checkAgents(config, configPath) {
30345
30559
  const authStatuses = getAllAuthStatuses(config);
30346
30560
  for (const [name, agentConfig] of Object.entries(config.agents)) {
30347
30561
  const agentDir = resolve30(agentsDir, name);
30348
- if (!existsSync50(agentDir)) {
30562
+ if (!existsSync51(agentDir)) {
30349
30563
  results.push({
30350
30564
  name: `${name}: scaffold`,
30351
30565
  status: "fail",
@@ -30366,7 +30580,7 @@ function checkAgents(config, configPath) {
30366
30580
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
30367
30581
  });
30368
30582
  }
30369
- results.push(checkStartShStale(name, join46(agentDir, "start.sh")));
30583
+ results.push(checkStartShStale(name, join47(agentDir, "start.sh")));
30370
30584
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
30371
30585
  const status = statuses[name];
30372
30586
  const active = status?.active ?? "unknown";
@@ -30443,8 +30657,8 @@ function checkAgents(config, configPath) {
30443
30657
  }
30444
30658
  }
30445
30659
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
30446
- const mcpJsonPath = join46(agentDir, ".mcp.json");
30447
- if (!existsSync50(mcpJsonPath)) {
30660
+ const mcpJsonPath = join47(agentDir, ".mcp.json");
30661
+ if (!existsSync51(mcpJsonPath)) {
30448
30662
  results.push({
30449
30663
  name: `${name}: .mcp.json`,
30450
30664
  status: "fail",
@@ -30529,7 +30743,7 @@ function mffEnvPath(config) {
30529
30743
  return agent ? resolve30(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve30(home2, ".switchroom/credentials/my-family-finance/.env");
30530
30744
  }
30531
30745
  function mffEnvState(envPath) {
30532
- if (!existsSync50(envPath))
30746
+ if (!existsSync51(envPath))
30533
30747
  return "absent";
30534
30748
  try {
30535
30749
  accessSync2(envPath, fsConstants5.R_OK);
@@ -30547,7 +30761,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
30547
30761
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
30548
30762
  };
30549
30763
  }
30550
- if (!existsSync50(vaultPath)) {
30764
+ if (!existsSync51(vaultPath)) {
30551
30765
  return {
30552
30766
  name: "mff: vault key present",
30553
30767
  status: "fail",
@@ -30600,7 +30814,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
30600
30814
  }
30601
30815
  }
30602
30816
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
30603
- if (!passphrase || !existsSync50(vaultPath)) {
30817
+ if (!passphrase || !existsSync51(vaultPath)) {
30604
30818
  return {
30605
30819
  name: "mff: vault key format",
30606
30820
  status: "warn",
@@ -30744,8 +30958,8 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
30744
30958
  };
30745
30959
  }
30746
30960
  const credDir = dirname12(envPath);
30747
- const authScript = join46(credDir, "claude-auth.py");
30748
- if (!existsSync50(authScript)) {
30961
+ const authScript = join47(credDir, "claude-auth.py");
30962
+ if (!existsSync51(authScript)) {
30749
30963
  return {
30750
30964
  name: "mff: auth flow",
30751
30965
  status: "warn",
@@ -31023,6 +31237,10 @@ function registerDoctorCommand(program3) {
31023
31237
  },
31024
31238
  { title: "Legacy State", results: checkLegacyState() },
31025
31239
  { title: "Vault", results: checkVault(config) },
31240
+ {
31241
+ title: "Vault-broker durability",
31242
+ results: runVaultBrokerDurabilityChecks(config)
31243
+ },
31026
31244
  { title: "Vault access", results: await runSecretAccessChecks(config) },
31027
31245
  { title: "Memory (Hindsight)", results: await checkHindsight(config) },
31028
31246
  { title: "Telegram", results: await checkTelegram(config) },
@@ -31106,6 +31324,7 @@ var init_doctor = __esm(() => {
31106
31324
  init_doctor_inlined_secrets();
31107
31325
  init_doctor_audit_integrity();
31108
31326
  init_doctor_agent_smoke();
31327
+ init_doctor_vault_broker_durability();
31109
31328
  MANIFEST_WARN_ONLY = new Set([
31110
31329
  "@playwright/mcp",
31111
31330
  "hindsight.backend",
@@ -47034,7 +47253,7 @@ __export(exports_server2, {
47034
47253
  TOOLS: () => TOOLS2
47035
47254
  });
47036
47255
  import { randomBytes as randomBytes14 } from "node:crypto";
47037
- import { existsSync as existsSync74, readFileSync as readFileSync60 } from "node:fs";
47256
+ import { existsSync as existsSync75, readFileSync as readFileSync60 } from "node:fs";
47038
47257
  function selfSocketPath() {
47039
47258
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
47040
47259
  }
@@ -47049,7 +47268,7 @@ async function dispatchTool2(name, args) {
47049
47268
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
47050
47269
  }
47051
47270
  const sockPath = selfSocketPath();
47052
- if (!existsSync74(sockPath)) {
47271
+ if (!existsSync75(sockPath)) {
47053
47272
  return errorText2(`hostd MCP: socket not bound at ${sockPath}. The host-control ` + `daemon is either not installed (run \`switchroom hostd install\`) ` + `or this agent isn't admin-flagged in switchroom.yaml. RFC C ` + `bind-mounts the per-agent socket only when host_control.enabled ` + `is true AND the agent has admin: true.`);
47054
47273
  }
47055
47274
  let req;
@@ -47193,13 +47412,13 @@ function resolveAuditLogPath() {
47193
47412
  if (process.env.HOSTD_AUDIT_LOG_PATH)
47194
47413
  return process.env.HOSTD_AUDIT_LOG_PATH;
47195
47414
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
47196
- if (existsSync74(bindMounted))
47415
+ if (existsSync75(bindMounted))
47197
47416
  return bindMounted;
47198
47417
  return defaultAuditLogPath2();
47199
47418
  }
47200
47419
  function getLastUpdateApplyStatus() {
47201
47420
  const path8 = resolveAuditLogPath();
47202
- if (!existsSync74(path8)) {
47421
+ if (!existsSync75(path8)) {
47203
47422
  return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
47204
47423
  }
47205
47424
  let raw;
@@ -47437,8 +47656,8 @@ var {
47437
47656
  } = import__.default;
47438
47657
 
47439
47658
  // src/build-info.ts
47440
- var VERSION = "0.13.32";
47441
- var COMMIT_SHA = "affa1f03";
47659
+ var VERSION = "0.13.33";
47660
+ var COMMIT_SHA = "a8018071";
47442
47661
 
47443
47662
  // src/cli/agent.ts
47444
47663
  init_source();
@@ -69811,17 +70030,17 @@ init_doctor();
69811
70030
  init_source();
69812
70031
  init_loader();
69813
70032
  init_lifecycle();
69814
- import { cpSync as cpSync2, existsSync as existsSync51, mkdirSync as mkdirSync28, readFileSync as readFileSync46, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync23 } from "node:fs";
70033
+ import { cpSync as cpSync2, existsSync as existsSync52, mkdirSync as mkdirSync28, readFileSync as readFileSync46, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync24 } from "node:fs";
69815
70034
  import { spawnSync as spawnSync8 } from "node:child_process";
69816
- import { join as join47, dirname as dirname13, resolve as resolve31 } from "node:path";
69817
- import { homedir as homedir27 } from "node:os";
69818
- var DEFAULT_COMPOSE_PATH = join47(homedir27(), ".switchroom", "compose", "docker-compose.yml");
70035
+ import { join as join48, dirname as dirname13, resolve as resolve31 } from "node:path";
70036
+ import { homedir as homedir28 } from "node:os";
70037
+ var DEFAULT_COMPOSE_PATH = join48(homedir28(), ".switchroom", "compose", "docker-compose.yml");
69819
70038
  function runningFromSwitchroomCheckout(scriptPath) {
69820
70039
  let dir = dirname13(scriptPath);
69821
70040
  for (let i = 0;i < 12; i++) {
69822
- if (existsSync51(join47(dir, ".git"))) {
70041
+ if (existsSync52(join48(dir, ".git"))) {
69823
70042
  try {
69824
- const pkg = JSON.parse(readFileSync46(join47(dir, "package.json"), "utf-8"));
70043
+ const pkg = JSON.parse(readFileSync46(join48(dir, "package.json"), "utf-8"));
69825
70044
  if (pkg.name === "switchroom")
69826
70045
  return true;
69827
70046
  } catch {}
@@ -69873,7 +70092,7 @@ function planUpdate(opts) {
69873
70092
  steps.push({
69874
70093
  name: "pull-images",
69875
70094
  description: "Pull broker / kernel / agent images from GHCR",
69876
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync51(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
70095
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync52(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
69877
70096
  run: () => {
69878
70097
  const r = runner("docker", [
69879
70098
  "compose",
@@ -69952,14 +70171,14 @@ function planUpdate(opts) {
69952
70171
  return;
69953
70172
  }
69954
70173
  const source = resolve31(import.meta.dirname, "../../skills");
69955
- const dest = join47(homedir27(), ".switchroom", "skills", "_bundled");
69956
- if (!existsSync51(source)) {
70174
+ const dest = join48(homedir28(), ".switchroom", "skills", "_bundled");
70175
+ if (!existsSync52(source)) {
69957
70176
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
69958
70177
  `);
69959
70178
  return;
69960
70179
  }
69961
70180
  try {
69962
- if (existsSync51(dest)) {
70181
+ if (existsSync52(dest)) {
69963
70182
  rmSync12(dest, { recursive: true, force: true });
69964
70183
  }
69965
70184
  mkdirSync28(dirname13(dest), { recursive: true });
@@ -70062,12 +70281,12 @@ function defaultStatusProbe(composePath) {
70062
70281
  } catch {}
70063
70282
  if (scriptPath) {
70064
70283
  try {
70065
- cliBuiltAt = new Date(statSync23(scriptPath).mtimeMs).toISOString();
70284
+ cliBuiltAt = new Date(statSync24(scriptPath).mtimeMs).toISOString();
70066
70285
  } catch {}
70067
70286
  let dir = dirname13(scriptPath);
70068
70287
  for (let i = 0;i < 8; i++) {
70069
- const pkgPath = join47(dir, "package.json");
70070
- if (existsSync51(pkgPath)) {
70288
+ const pkgPath = join48(dir, "package.json");
70289
+ if (existsSync52(pkgPath)) {
70071
70290
  try {
70072
70291
  const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
70073
70292
  if (typeof pkg.version === "string")
@@ -70090,7 +70309,7 @@ function defaultStatusProbe(composePath) {
70090
70309
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
70091
70310
  }
70092
70311
  const services = [];
70093
- if (!existsSync51(composePath)) {
70312
+ if (!existsSync52(composePath)) {
70094
70313
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
70095
70314
  return { cliVersion, cliBuiltAt, services, warnings };
70096
70315
  }
@@ -70285,8 +70504,8 @@ init_source();
70285
70504
  init_helpers();
70286
70505
  init_lifecycle();
70287
70506
  import { execSync as execSync4 } from "node:child_process";
70288
- import { existsSync as existsSync52, readFileSync as readFileSync47 } from "node:fs";
70289
- import { dirname as dirname14, join as join48 } from "node:path";
70507
+ import { existsSync as existsSync53, readFileSync as readFileSync47 } from "node:fs";
70508
+ import { dirname as dirname14, join as join49 } from "node:path";
70290
70509
  function getClaudeCodeVersion() {
70291
70510
  try {
70292
70511
  const out = execSync4("claude --version 2>/dev/null", {
@@ -70336,11 +70555,11 @@ function formatUptime3(timestamp) {
70336
70555
  function locateSwitchroomInstallDir() {
70337
70556
  let dir = import.meta.dirname;
70338
70557
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
70339
- const pkgPath = join48(dir, "package.json");
70340
- if (existsSync52(pkgPath)) {
70558
+ const pkgPath = join49(dir, "package.json");
70559
+ if (existsSync53(pkgPath)) {
70341
70560
  try {
70342
70561
  const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
70343
- if (pkg.name === "switchroom" && existsSync52(join48(dir, ".git"))) {
70562
+ if (pkg.name === "switchroom" && existsSync53(join49(dir, ".git"))) {
70344
70563
  return dir;
70345
70564
  }
70346
70565
  } catch {}
@@ -70555,18 +70774,18 @@ function registerHandoffCommand(program3) {
70555
70774
  // src/issues/store.ts
70556
70775
  import {
70557
70776
  closeSync as closeSync11,
70558
- existsSync as existsSync53,
70777
+ existsSync as existsSync54,
70559
70778
  mkdirSync as mkdirSync29,
70560
70779
  openSync as openSync11,
70561
70780
  readdirSync as readdirSync20,
70562
70781
  readFileSync as readFileSync48,
70563
70782
  renameSync as renameSync11,
70564
- statSync as statSync24,
70783
+ statSync as statSync25,
70565
70784
  unlinkSync as unlinkSync11,
70566
70785
  writeFileSync as writeFileSync25,
70567
70786
  writeSync as writeSync7
70568
70787
  } from "node:fs";
70569
- import { join as join49 } from "node:path";
70788
+ import { join as join50 } from "node:path";
70570
70789
  import { randomBytes as randomBytes11 } from "node:crypto";
70571
70790
  import { execSync as execSync5 } from "node:child_process";
70572
70791
 
@@ -70886,8 +71105,8 @@ function redactedMarker(ruleId) {
70886
71105
  var ISSUES_FILE = "issues.jsonl";
70887
71106
  var ISSUES_LOCK = "issues.lock";
70888
71107
  function readAll(stateDir) {
70889
- const path4 = join49(stateDir, ISSUES_FILE);
70890
- if (!existsSync53(path4))
71108
+ const path4 = join50(stateDir, ISSUES_FILE);
71109
+ if (!existsSync54(path4))
70891
71110
  return [];
70892
71111
  let raw;
70893
71112
  try {
@@ -70964,7 +71183,7 @@ function record(stateDir, input, nowFn = Date.now) {
70964
71183
  });
70965
71184
  }
70966
71185
  function resolve34(stateDir, fingerprint, nowFn = Date.now) {
70967
- if (!existsSync53(join49(stateDir, ISSUES_FILE)))
71186
+ if (!existsSync54(join50(stateDir, ISSUES_FILE)))
70968
71187
  return 0;
70969
71188
  return withLock(stateDir, () => {
70970
71189
  const all = readAll(stateDir);
@@ -70982,7 +71201,7 @@ function resolve34(stateDir, fingerprint, nowFn = Date.now) {
70982
71201
  });
70983
71202
  }
70984
71203
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
70985
- if (!existsSync53(join49(stateDir, ISSUES_FILE)))
71204
+ if (!existsSync54(join50(stateDir, ISSUES_FILE)))
70986
71205
  return 0;
70987
71206
  return withLock(stateDir, () => {
70988
71207
  const all = readAll(stateDir);
@@ -71000,7 +71219,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
71000
71219
  });
71001
71220
  }
71002
71221
  function prune(stateDir, opts = {}) {
71003
- if (!existsSync53(join49(stateDir, ISSUES_FILE)))
71222
+ if (!existsSync54(join50(stateDir, ISSUES_FILE)))
71004
71223
  return 0;
71005
71224
  return withLock(stateDir, () => {
71006
71225
  const all = readAll(stateDir);
@@ -71033,7 +71252,7 @@ function ensureDir(stateDir) {
71033
71252
  mkdirSync29(stateDir, { recursive: true });
71034
71253
  }
71035
71254
  function writeAll(stateDir, events) {
71036
- const path4 = join49(stateDir, ISSUES_FILE);
71255
+ const path4 = join50(stateDir, ISSUES_FILE);
71037
71256
  sweepOrphanTmpFiles(stateDir);
71038
71257
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
71039
71258
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -71055,9 +71274,9 @@ function sweepOrphanTmpFiles(stateDir) {
71055
71274
  for (const entry of entries) {
71056
71275
  if (!entry.startsWith(TMP_PREFIX))
71057
71276
  continue;
71058
- const tmpPath = join49(stateDir, entry);
71277
+ const tmpPath = join50(stateDir, entry);
71059
71278
  try {
71060
- const stat = statSync24(tmpPath);
71279
+ const stat = statSync25(tmpPath);
71061
71280
  if (stat.mtimeMs < cutoff) {
71062
71281
  unlinkSync11(tmpPath);
71063
71282
  }
@@ -71067,7 +71286,7 @@ function sweepOrphanTmpFiles(stateDir) {
71067
71286
  var LOCK_RETRY_MS = 25;
71068
71287
  var LOCK_TIMEOUT_MS = 1e4;
71069
71288
  function withLock(stateDir, fn) {
71070
- const lockPath = join49(stateDir, ISSUES_LOCK);
71289
+ const lockPath = join50(stateDir, ISSUES_LOCK);
71071
71290
  const startedAt = Date.now();
71072
71291
  let fd = null;
71073
71292
  while (fd === null) {
@@ -71350,22 +71569,22 @@ function relTime(deltaMs) {
71350
71569
 
71351
71570
  // src/cli/deps.ts
71352
71571
  init_source();
71353
- import { existsSync as existsSync56 } from "node:fs";
71354
- import { homedir as homedir30 } from "node:os";
71355
- import { join as join52, resolve as resolve35 } from "node:path";
71572
+ import { existsSync as existsSync57 } from "node:fs";
71573
+ import { homedir as homedir31 } from "node:os";
71574
+ import { join as join53, resolve as resolve35 } from "node:path";
71356
71575
 
71357
71576
  // src/deps/python.ts
71358
71577
  import { createHash as createHash10 } from "node:crypto";
71359
71578
  import {
71360
- existsSync as existsSync54,
71579
+ existsSync as existsSync55,
71361
71580
  mkdirSync as mkdirSync30,
71362
71581
  readFileSync as readFileSync49,
71363
71582
  rmSync as rmSync13,
71364
71583
  writeFileSync as writeFileSync26
71365
71584
  } from "node:fs";
71366
- import { dirname as dirname15, join as join50 } from "node:path";
71367
- import { homedir as homedir28 } from "node:os";
71368
- import { execFileSync as execFileSync14 } from "node:child_process";
71585
+ import { dirname as dirname15, join as join51 } from "node:path";
71586
+ import { homedir as homedir29 } from "node:os";
71587
+ import { execFileSync as execFileSync15 } from "node:child_process";
71369
71588
 
71370
71589
  class PythonEnvError extends Error {
71371
71590
  stderr;
@@ -71376,7 +71595,7 @@ class PythonEnvError extends Error {
71376
71595
  }
71377
71596
  }
71378
71597
  function defaultPythonCacheRoot() {
71379
- return join50(homedir28(), ".switchroom", "deps", "python");
71598
+ return join51(homedir29(), ".switchroom", "deps", "python");
71380
71599
  }
71381
71600
  function hashFile(path4) {
71382
71601
  return createHash10("sha256").update(readFileSync49(path4)).digest("hex");
@@ -71385,16 +71604,16 @@ function ensurePythonEnv(opts) {
71385
71604
  const { skillName, requirementsPath, force = false } = opts;
71386
71605
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
71387
71606
  const hostPython = opts.pythonBin ?? "python3";
71388
- if (!existsSync54(requirementsPath)) {
71607
+ if (!existsSync55(requirementsPath)) {
71389
71608
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
71390
71609
  }
71391
- const venvDir = join50(cacheRoot, skillName);
71392
- const stampPath = join50(venvDir, ".requirements.sha256");
71393
- const binDir = join50(venvDir, "bin");
71394
- const pythonBin = join50(binDir, "python");
71395
- const pipBin = join50(binDir, "pip");
71610
+ const venvDir = join51(cacheRoot, skillName);
71611
+ const stampPath = join51(venvDir, ".requirements.sha256");
71612
+ const binDir = join51(venvDir, "bin");
71613
+ const pythonBin = join51(binDir, "python");
71614
+ const pipBin = join51(binDir, "pip");
71396
71615
  const targetHash = hashFile(requirementsPath);
71397
- if (!force && existsSync54(stampPath) && existsSync54(pythonBin)) {
71616
+ if (!force && existsSync55(stampPath) && existsSync55(pythonBin)) {
71398
71617
  const existingHash = readFileSync49(stampPath, "utf8").trim();
71399
71618
  if (existingHash === targetHash) {
71400
71619
  return {
@@ -71407,12 +71626,12 @@ function ensurePythonEnv(opts) {
71407
71626
  };
71408
71627
  }
71409
71628
  }
71410
- if (existsSync54(venvDir)) {
71629
+ if (existsSync55(venvDir)) {
71411
71630
  rmSync13(venvDir, { recursive: true, force: true });
71412
71631
  }
71413
71632
  mkdirSync30(dirname15(venvDir), { recursive: true });
71414
71633
  try {
71415
- execFileSync14(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
71634
+ execFileSync15(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
71416
71635
  } catch (err) {
71417
71636
  const e = err;
71418
71637
  throw new PythonEnvError(`Failed to create venv for skill "${skillName}" with ${hostPython}: ${e.message}`, e.stderr?.toString());
@@ -71424,7 +71643,7 @@ function ensurePythonEnv(opts) {
71424
71643
  delete childEnv.PIP_TARGET;
71425
71644
  delete childEnv.PIP_PREFIX;
71426
71645
  delete childEnv.PYTHONUSERBASE;
71427
- execFileSync14(pipBin, ["install", "--disable-pip-version-check", "-r", requirementsPath], { stdio: "pipe", env: childEnv });
71646
+ execFileSync15(pipBin, ["install", "--disable-pip-version-check", "-r", requirementsPath], { stdio: "pipe", env: childEnv });
71428
71647
  } catch (err) {
71429
71648
  const e = err;
71430
71649
  throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
@@ -71445,15 +71664,15 @@ function ensurePythonEnv(opts) {
71445
71664
  import { createHash as createHash11 } from "node:crypto";
71446
71665
  import {
71447
71666
  copyFileSync as copyFileSync9,
71448
- existsSync as existsSync55,
71667
+ existsSync as existsSync56,
71449
71668
  mkdirSync as mkdirSync31,
71450
71669
  readFileSync as readFileSync50,
71451
71670
  rmSync as rmSync14,
71452
71671
  writeFileSync as writeFileSync27
71453
71672
  } from "node:fs";
71454
- import { dirname as dirname16, join as join51 } from "node:path";
71455
- import { homedir as homedir29 } from "node:os";
71456
- import { execFileSync as execFileSync15 } from "node:child_process";
71673
+ import { dirname as dirname16, join as join52 } from "node:path";
71674
+ import { homedir as homedir30 } from "node:os";
71675
+ import { execFileSync as execFileSync16 } from "node:child_process";
71457
71676
 
71458
71677
  class NodeEnvError extends Error {
71459
71678
  stderr;
@@ -71475,7 +71694,7 @@ var LOCKFILES_FOR = {
71475
71694
  npm: ["package-lock.json"]
71476
71695
  };
71477
71696
  function defaultNodeCacheRoot() {
71478
- return join51(homedir29(), ".switchroom", "deps", "node");
71697
+ return join52(homedir30(), ".switchroom", "deps", "node");
71479
71698
  }
71480
71699
  function hashDepInputs(packageJsonPath) {
71481
71700
  const sourceDir = dirname16(packageJsonPath);
@@ -71484,8 +71703,8 @@ function hashDepInputs(packageJsonPath) {
71484
71703
  `);
71485
71704
  hasher.update(readFileSync50(packageJsonPath));
71486
71705
  for (const lockName of ALL_LOCKFILES) {
71487
- const lockPath = join51(sourceDir, lockName);
71488
- if (existsSync55(lockPath)) {
71706
+ const lockPath = join52(sourceDir, lockName);
71707
+ if (existsSync56(lockPath)) {
71489
71708
  hasher.update(`
71490
71709
  `);
71491
71710
  hasher.update(lockName);
@@ -71500,16 +71719,16 @@ function ensureNodeEnv(opts) {
71500
71719
  const { skillName, packageJsonPath, force = false } = opts;
71501
71720
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
71502
71721
  const installer = opts.installer ?? "bun";
71503
- if (!existsSync55(packageJsonPath)) {
71722
+ if (!existsSync56(packageJsonPath)) {
71504
71723
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
71505
71724
  }
71506
71725
  const sourceDir = dirname16(packageJsonPath);
71507
- const envDir = join51(cacheRoot, skillName);
71508
- const stampPath = join51(envDir, ".package.sha256");
71509
- const nodeModulesDir = join51(envDir, "node_modules");
71510
- const binDir = join51(nodeModulesDir, ".bin");
71726
+ const envDir = join52(cacheRoot, skillName);
71727
+ const stampPath = join52(envDir, ".package.sha256");
71728
+ const nodeModulesDir = join52(envDir, "node_modules");
71729
+ const binDir = join52(nodeModulesDir, ".bin");
71511
71730
  const targetHash = hashDepInputs(packageJsonPath);
71512
- if (!force && existsSync55(stampPath) && existsSync55(nodeModulesDir)) {
71731
+ if (!force && existsSync56(stampPath) && existsSync56(nodeModulesDir)) {
71513
71732
  const existingHash = readFileSync50(stampPath, "utf8").trim();
71514
71733
  if (existingHash === targetHash) {
71515
71734
  return {
@@ -71521,26 +71740,26 @@ function ensureNodeEnv(opts) {
71521
71740
  };
71522
71741
  }
71523
71742
  }
71524
- if (existsSync55(envDir)) {
71743
+ if (existsSync56(envDir)) {
71525
71744
  rmSync14(envDir, { recursive: true, force: true });
71526
71745
  }
71527
71746
  mkdirSync31(envDir, { recursive: true });
71528
- copyFileSync9(packageJsonPath, join51(envDir, "package.json"));
71747
+ copyFileSync9(packageJsonPath, join52(envDir, "package.json"));
71529
71748
  let copiedLockfile = false;
71530
71749
  for (const lockName of LOCKFILES_FOR[installer]) {
71531
- const lockPath = join51(sourceDir, lockName);
71532
- if (existsSync55(lockPath)) {
71533
- copyFileSync9(lockPath, join51(envDir, lockName));
71750
+ const lockPath = join52(sourceDir, lockName);
71751
+ if (existsSync56(lockPath)) {
71752
+ copyFileSync9(lockPath, join52(envDir, lockName));
71534
71753
  copiedLockfile = true;
71535
71754
  }
71536
71755
  }
71537
71756
  try {
71538
71757
  if (installer === "bun") {
71539
71758
  const args = copiedLockfile ? ["install", "--frozen-lockfile"] : ["install"];
71540
- execFileSync15("bun", args, { cwd: envDir, stdio: "pipe" });
71759
+ execFileSync16("bun", args, { cwd: envDir, stdio: "pipe" });
71541
71760
  } else {
71542
71761
  const args = copiedLockfile ? ["ci"] : ["install"];
71543
- execFileSync15("npm", args, { cwd: envDir, stdio: "pipe" });
71762
+ execFileSync16("npm", args, { cwd: envDir, stdio: "pipe" });
71544
71763
  }
71545
71764
  } catch (err) {
71546
71765
  const e = err;
@@ -71559,28 +71778,28 @@ function ensureNodeEnv(opts) {
71559
71778
 
71560
71779
  // src/cli/deps.ts
71561
71780
  function builtinSkillsRoot() {
71562
- return resolve35(homedir30(), ".switchroom/skills/_bundled");
71781
+ return resolve35(homedir31(), ".switchroom/skills/_bundled");
71563
71782
  }
71564
71783
  function registerDepsCommand(program3) {
71565
71784
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
71566
71785
  deps.command("rebuild <skill>").description("Rebuild the Python venv and/or Node node_modules cache for a skill").option("-p, --python", "Rebuild only the Python env").option("-n, --node", "Rebuild only the Node env").action(async (skill, opts) => {
71567
71786
  const skillsRoot = builtinSkillsRoot();
71568
- if (!existsSync56(skillsRoot)) {
71787
+ if (!existsSync57(skillsRoot)) {
71569
71788
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
71570
71789
  process.exit(1);
71571
71790
  }
71572
- const skillDir = join52(skillsRoot, skill);
71573
- if (!existsSync56(skillDir)) {
71791
+ const skillDir = join53(skillsRoot, skill);
71792
+ if (!existsSync57(skillDir)) {
71574
71793
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
71575
71794
  process.exit(1);
71576
71795
  }
71577
- const requirementsPath = join52(skillDir, "requirements.txt");
71578
- const packageJsonPath = join52(skillDir, "package.json");
71579
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync56(requirementsPath));
71580
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync56(packageJsonPath));
71796
+ const requirementsPath = join53(skillDir, "requirements.txt");
71797
+ const packageJsonPath = join53(skillDir, "package.json");
71798
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync57(requirementsPath));
71799
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync57(packageJsonPath));
71581
71800
  let did = 0;
71582
71801
  if (wantPython) {
71583
- if (!existsSync56(requirementsPath)) {
71802
+ if (!existsSync57(requirementsPath)) {
71584
71803
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
71585
71804
  process.exit(1);
71586
71805
  }
@@ -71604,7 +71823,7 @@ function registerDepsCommand(program3) {
71604
71823
  }
71605
71824
  }
71606
71825
  if (wantNode) {
71607
- if (!existsSync56(packageJsonPath)) {
71826
+ if (!existsSync57(packageJsonPath)) {
71608
71827
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
71609
71828
  process.exit(1);
71610
71829
  }
@@ -71637,7 +71856,7 @@ function registerDepsCommand(program3) {
71637
71856
  // src/cli/workspace.ts
71638
71857
  init_helpers();
71639
71858
  init_loader();
71640
- import { existsSync as existsSync57 } from "node:fs";
71859
+ import { existsSync as existsSync58 } from "node:fs";
71641
71860
  import { resolve as resolve36, sep as sep3 } from "node:path";
71642
71861
  import { spawnSync as spawnSync9 } from "node:child_process";
71643
71862
 
@@ -72414,7 +72633,7 @@ function registerWorkspaceCommand(program3) {
72414
72633
  if (!dir)
72415
72634
  return;
72416
72635
  const gitDir = resolve36(dir, ".git");
72417
- if (!existsSync57(gitDir)) {
72636
+ if (!existsSync58(gitDir)) {
72418
72637
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
72419
72638
  `);
72420
72639
  return;
@@ -72468,7 +72687,7 @@ function registerWorkspaceCommand(program3) {
72468
72687
  if (!dir)
72469
72688
  return;
72470
72689
  const gitDir = resolve36(dir, ".git");
72471
- if (!existsSync57(gitDir)) {
72690
+ if (!existsSync58(gitDir)) {
72472
72691
  process.stdout.write(`Workspace is not a git repository.
72473
72692
  `);
72474
72693
  return;
@@ -72493,7 +72712,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
72493
72712
  const agentsDir = resolveAgentsDir(config);
72494
72713
  const agentDir = resolve36(agentsDir, agentName);
72495
72714
  const dir = resolveAgentWorkspaceDir(agentDir);
72496
- if (!existsSync57(dir)) {
72715
+ if (!existsSync58(dir)) {
72497
72716
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
72498
72717
  `);
72499
72718
  return;
@@ -72529,8 +72748,8 @@ function safeParseInt(value, fallback) {
72529
72748
  init_helpers();
72530
72749
  init_loader();
72531
72750
  init_merge();
72532
- import { copyFileSync as copyFileSync10, existsSync as existsSync58, readFileSync as readFileSync51, writeFileSync as writeFileSync28 } from "node:fs";
72533
- import { join as join53, resolve as resolve37 } from "node:path";
72751
+ import { copyFileSync as copyFileSync10, existsSync as existsSync59, readFileSync as readFileSync51, writeFileSync as writeFileSync28 } from "node:fs";
72752
+ import { join as join54, resolve as resolve37 } from "node:path";
72534
72753
  init_schema();
72535
72754
  function resolveSoulTargetOrExit(program3, agentName) {
72536
72755
  const config = getConfig(program3);
@@ -72545,7 +72764,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
72545
72764
  const agentsDir = resolveAgentsDir(config);
72546
72765
  const agentDir = resolve37(agentsDir, agentName);
72547
72766
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
72548
- if (!existsSync58(workspaceDir)) {
72767
+ if (!existsSync59(workspaceDir)) {
72549
72768
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
72550
72769
  process.exit(1);
72551
72770
  }
@@ -72554,7 +72773,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
72554
72773
  profileName,
72555
72774
  profilePath,
72556
72775
  workspaceDir,
72557
- soulPath: join53(workspaceDir, "SOUL.md"),
72776
+ soulPath: join54(workspaceDir, "SOUL.md"),
72558
72777
  soul: merged.soul
72559
72778
  };
72560
72779
  }
@@ -72571,7 +72790,7 @@ function registerSoulCommand(program3) {
72571
72790
  const t = resolveSoulTargetOrExit(program3, agentName);
72572
72791
  if (!t)
72573
72792
  return;
72574
- if (!existsSync58(t.soulPath)) {
72793
+ if (!existsSync59(t.soulPath)) {
72575
72794
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
72576
72795
  process.exit(1);
72577
72796
  }
@@ -72586,7 +72805,7 @@ function registerSoulCommand(program3) {
72586
72805
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
72587
72806
  process.exit(1);
72588
72807
  }
72589
- const exists = existsSync58(t.soulPath);
72808
+ const exists = existsSync59(t.soulPath);
72590
72809
  if (exists && !opts.yes) {
72591
72810
  if (!isInteractive()) {
72592
72811
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -72601,7 +72820,7 @@ function registerSoulCommand(program3) {
72601
72820
  let backupPath;
72602
72821
  if (exists) {
72603
72822
  backupPath = `${t.soulPath}.bak`;
72604
- if (existsSync58(backupPath)) {
72823
+ if (existsSync59(backupPath)) {
72605
72824
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
72606
72825
  }
72607
72826
  copyFileSync10(t.soulPath, backupPath);
@@ -72620,8 +72839,8 @@ function registerSoulCommand(program3) {
72620
72839
  // src/cli/debug.ts
72621
72840
  init_helpers();
72622
72841
  init_loader();
72623
- import { existsSync as existsSync59, readFileSync as readFileSync52, readdirSync as readdirSync21, statSync as statSync25 } from "node:fs";
72624
- import { resolve as resolve38, join as join54 } from "node:path";
72842
+ import { existsSync as existsSync60, readFileSync as readFileSync52, readdirSync as readdirSync21, statSync as statSync26 } from "node:fs";
72843
+ import { resolve as resolve38, join as join55 } from "node:path";
72625
72844
  import { createHash as createHash12 } from "node:crypto";
72626
72845
  init_merge();
72627
72846
  init_hindsight();
@@ -72635,8 +72854,8 @@ function sha256(content) {
72635
72854
  return createHash12("sha256").update(content).digest("hex").slice(0, 16);
72636
72855
  }
72637
72856
  function findLatestTranscriptJsonl(claudeConfigDir) {
72638
- const projectsDir = join54(claudeConfigDir, "projects");
72639
- if (!existsSync59(projectsDir))
72857
+ const projectsDir = join55(claudeConfigDir, "projects");
72858
+ if (!existsSync60(projectsDir))
72640
72859
  return;
72641
72860
  try {
72642
72861
  const entries = readdirSync21(projectsDir, { withFileTypes: true });
@@ -72644,11 +72863,11 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
72644
72863
  for (const entry of entries) {
72645
72864
  if (!entry.isDirectory())
72646
72865
  continue;
72647
- const projectPath = join54(projectsDir, entry.name);
72648
- const transcriptPath = join54(projectPath, "transcript.jsonl");
72649
- if (!existsSync59(transcriptPath))
72866
+ const projectPath = join55(projectsDir, entry.name);
72867
+ const transcriptPath = join55(projectPath, "transcript.jsonl");
72868
+ if (!existsSync60(transcriptPath))
72650
72869
  continue;
72651
- const stat3 = statSync25(transcriptPath);
72870
+ const stat3 = statSync26(transcriptPath);
72652
72871
  if (!latest || stat3.mtimeMs > latest.mtime) {
72653
72872
  latest = { path: transcriptPath, mtime: stat3.mtimeMs };
72654
72873
  }
@@ -72709,16 +72928,16 @@ function registerDebugCommand(program3) {
72709
72928
  }
72710
72929
  const agentsDir = resolveAgentsDir(config);
72711
72930
  const agentDir = resolve38(agentsDir, agentName);
72712
- if (!existsSync59(agentDir)) {
72931
+ if (!existsSync60(agentDir)) {
72713
72932
  console.error(`Agent directory not found: ${agentDir}`);
72714
72933
  process.exit(1);
72715
72934
  }
72716
72935
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
72717
- const claudeConfigDir = join54(agentDir, ".claude");
72718
- const claudeMdPath = join54(agentDir, "CLAUDE.md");
72719
- const soulMdPath = join54(agentDir, "SOUL.md");
72720
- const workspaceSoulMdPath = join54(workspaceDir, "SOUL.md");
72721
- const handoffPath = join54(agentDir, ".handoff.md");
72936
+ const claudeConfigDir = join55(agentDir, ".claude");
72937
+ const claudeMdPath = join55(agentDir, "CLAUDE.md");
72938
+ const soulMdPath = join55(agentDir, "SOUL.md");
72939
+ const workspaceSoulMdPath = join55(workspaceDir, "SOUL.md");
72940
+ const handoffPath = join55(agentDir, ".handoff.md");
72722
72941
  const lastN = parseInt(opts.last, 10);
72723
72942
  if (isNaN(lastN) || lastN < 1) {
72724
72943
  console.error("--last must be a positive integer");
@@ -72764,7 +72983,7 @@ function registerDebugCommand(program3) {
72764
72983
  }
72765
72984
  console.log(`=== Append System Prompt (per-session) ===
72766
72985
  `);
72767
- const handoffContent = existsSync59(handoffPath) ? readFileSync52(handoffPath, "utf-8") : "";
72986
+ const handoffContent = existsSync60(handoffPath) ? readFileSync52(handoffPath, "utf-8") : "";
72768
72987
  if (handoffContent.trim().length > 0) {
72769
72988
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
72770
72989
  console.log(handoffContent);
@@ -72775,7 +72994,7 @@ function registerDebugCommand(program3) {
72775
72994
  }
72776
72995
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
72777
72996
  `);
72778
- const claudeMdContent = existsSync59(claudeMdPath) ? readFileSync52(claudeMdPath, "utf-8") : "";
72997
+ const claudeMdContent = existsSync60(claudeMdPath) ? readFileSync52(claudeMdPath, "utf-8") : "";
72779
72998
  if (claudeMdContent.trim().length > 0) {
72780
72999
  console.log(`(${formatBytes(claudeMdContent.length)})`);
72781
73000
  console.log(claudeMdContent);
@@ -72786,7 +73005,7 @@ function registerDebugCommand(program3) {
72786
73005
  }
72787
73006
  console.log(`=== Persona (SOUL.md) ===
72788
73007
  `);
72789
- const soulMdContent = existsSync59(soulMdPath) ? readFileSync52(soulMdPath, "utf-8") : existsSync59(workspaceSoulMdPath) ? readFileSync52(workspaceSoulMdPath, "utf-8") : "";
73008
+ const soulMdContent = existsSync60(soulMdPath) ? readFileSync52(soulMdPath, "utf-8") : existsSync60(workspaceSoulMdPath) ? readFileSync52(workspaceSoulMdPath, "utf-8") : "";
72790
73009
  if (soulMdContent.trim().length > 0) {
72791
73010
  console.log(`(${formatBytes(soulMdContent.length)})`);
72792
73011
  console.log(soulMdContent);
@@ -72866,10 +73085,10 @@ function registerDebugCommand(program3) {
72866
73085
  init_source();
72867
73086
 
72868
73087
  // src/worktree/claim.ts
72869
- import { execFileSync as execFileSync16 } from "node:child_process";
72870
- import { closeSync as closeSync12, mkdirSync as mkdirSync33, openSync as openSync12, existsSync as existsSync61, unlinkSync as unlinkSync13 } from "node:fs";
72871
- import { join as join56, resolve as resolve40 } from "node:path";
72872
- import { homedir as homedir32 } from "node:os";
73088
+ import { execFileSync as execFileSync17 } from "node:child_process";
73089
+ import { closeSync as closeSync12, mkdirSync as mkdirSync33, openSync as openSync12, existsSync as existsSync62, unlinkSync as unlinkSync13 } from "node:fs";
73090
+ import { join as join57, resolve as resolve40 } from "node:path";
73091
+ import { homedir as homedir33 } from "node:os";
72873
73092
  import { randomBytes as randomBytes12 } from "node:crypto";
72874
73093
 
72875
73094
  // src/worktree/registry.ts
@@ -72879,16 +73098,16 @@ import {
72879
73098
  readFileSync as readFileSync53,
72880
73099
  readdirSync as readdirSync22,
72881
73100
  unlinkSync as unlinkSync12,
72882
- existsSync as existsSync60,
73101
+ existsSync as existsSync61,
72883
73102
  renameSync as renameSync12
72884
73103
  } from "node:fs";
72885
- import { join as join55, resolve as resolve39 } from "node:path";
72886
- import { homedir as homedir31 } from "node:os";
73104
+ import { join as join56, resolve as resolve39 } from "node:path";
73105
+ import { homedir as homedir32 } from "node:os";
72887
73106
  function registryDir() {
72888
- return resolve39(process.env.SWITCHROOM_WORKTREE_DIR ?? join55(homedir31(), ".switchroom", "worktrees"));
73107
+ return resolve39(process.env.SWITCHROOM_WORKTREE_DIR ?? join56(homedir32(), ".switchroom", "worktrees"));
72889
73108
  }
72890
73109
  function recordPath(id) {
72891
- return join55(registryDir(), `${id}.json`);
73110
+ return join56(registryDir(), `${id}.json`);
72892
73111
  }
72893
73112
  function ensureDir2() {
72894
73113
  mkdirSync32(registryDir(), { recursive: true });
@@ -72939,7 +73158,7 @@ function acquireRepoLock(repoPath) {
72939
73158
  const lockDir = registryDir();
72940
73159
  mkdirSync33(lockDir, { recursive: true });
72941
73160
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
72942
- const lockPath = join56(lockDir, `.lock-${lockName}`);
73161
+ const lockPath = join57(lockDir, `.lock-${lockName}`);
72943
73162
  const deadline = Date.now() + 5000;
72944
73163
  let fd = null;
72945
73164
  while (fd === null) {
@@ -72966,7 +73185,7 @@ function acquireRepoLock(repoPath) {
72966
73185
  }
72967
73186
  var DEFAULT_CONCURRENCY = 5;
72968
73187
  function worktreesBaseDir() {
72969
- return resolve40(process.env.SWITCHROOM_WORKTREE_BASE ?? join56(homedir32(), ".switchroom", "worktree-checkouts"));
73188
+ return resolve40(process.env.SWITCHROOM_WORKTREE_BASE ?? join57(homedir33(), ".switchroom", "worktree-checkouts"));
72970
73189
  }
72971
73190
  function shortId() {
72972
73191
  return randomBytes12(4).toString("hex");
@@ -72988,12 +73207,12 @@ function resolveRepoPath(repo, codeRepos) {
72988
73207
  }
72989
73208
  function expandHome(p) {
72990
73209
  if (p.startsWith("~/"))
72991
- return join56(homedir32(), p.slice(2));
73210
+ return join57(homedir33(), p.slice(2));
72992
73211
  return p;
72993
73212
  }
72994
73213
  async function claimWorktree(input, codeRepos) {
72995
73214
  const repoPath = resolveRepoPath(input.repo, codeRepos);
72996
- if (!existsSync61(repoPath)) {
73215
+ if (!existsSync62(repoPath)) {
72997
73216
  throw new Error(`Repository path does not exist: ${repoPath}`);
72998
73217
  }
72999
73218
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -73016,7 +73235,7 @@ async function claimWorktree(input, codeRepos) {
73016
73235
  branch = `task/${taskSuffix}-${id}`;
73017
73236
  const baseDir = worktreesBaseDir();
73018
73237
  mkdirSync33(baseDir, { recursive: true });
73019
- worktreePath = join56(baseDir, `${id}-${taskSuffix}`);
73238
+ worktreePath = join57(baseDir, `${id}-${taskSuffix}`);
73020
73239
  const now = new Date().toISOString();
73021
73240
  const record2 = {
73022
73241
  id,
@@ -73033,7 +73252,7 @@ async function claimWorktree(input, codeRepos) {
73033
73252
  releaseLock();
73034
73253
  }
73035
73254
  try {
73036
- execFileSync16("git", ["worktree", "add", "-b", branch, worktreePath], {
73255
+ execFileSync17("git", ["worktree", "add", "-b", branch, worktreePath], {
73037
73256
  cwd: repoPath,
73038
73257
  stdio: "pipe"
73039
73258
  });
@@ -73046,8 +73265,8 @@ async function claimWorktree(input, codeRepos) {
73046
73265
  }
73047
73266
 
73048
73267
  // src/worktree/release.ts
73049
- import { execFileSync as execFileSync17 } from "node:child_process";
73050
- import { existsSync as existsSync62 } from "node:fs";
73268
+ import { execFileSync as execFileSync18 } from "node:child_process";
73269
+ import { existsSync as existsSync63 } from "node:fs";
73051
73270
  function releaseWorktree(input) {
73052
73271
  const { id } = input;
73053
73272
  const record2 = readRecord(id);
@@ -73055,9 +73274,9 @@ function releaseWorktree(input) {
73055
73274
  return { released: true };
73056
73275
  }
73057
73276
  let gitSuccess = true;
73058
- if (existsSync62(record2.path)) {
73277
+ if (existsSync63(record2.path)) {
73059
73278
  try {
73060
- execFileSync17("git", ["worktree", "remove", "--force", record2.path], {
73279
+ execFileSync18("git", ["worktree", "remove", "--force", record2.path], {
73061
73280
  cwd: record2.repo,
73062
73281
  stdio: "pipe"
73063
73282
  });
@@ -73093,16 +73312,16 @@ function listWorktrees() {
73093
73312
  }
73094
73313
 
73095
73314
  // src/worktree/reaper.ts
73096
- import { execFileSync as execFileSync18 } from "node:child_process";
73097
- import { existsSync as existsSync63 } from "node:fs";
73315
+ import { execFileSync as execFileSync19 } from "node:child_process";
73316
+ import { existsSync as existsSync64 } from "node:fs";
73098
73317
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
73099
73318
  function isPathInUse(path7) {
73100
73319
  try {
73101
- execFileSync18("fuser", [path7], { stdio: "pipe" });
73320
+ execFileSync19("fuser", [path7], { stdio: "pipe" });
73102
73321
  return true;
73103
73322
  } catch {}
73104
73323
  try {
73105
- const out = execFileSync18("lsof", ["-t", path7], {
73324
+ const out = execFileSync19("lsof", ["-t", path7], {
73106
73325
  stdio: ["ignore", "pipe", "ignore"]
73107
73326
  }).toString().trim();
73108
73327
  if (out.length > 0)
@@ -73112,7 +73331,7 @@ function isPathInUse(path7) {
73112
73331
  }
73113
73332
  function hasUncommittedChanges(repoPath, worktreePath) {
73114
73333
  try {
73115
- const out = execFileSync18("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" }).toString();
73334
+ const out = execFileSync19("git", ["-C", worktreePath, "status", "--porcelain"], { stdio: "pipe" }).toString();
73116
73335
  return out.trim().length > 0;
73117
73336
  } catch {
73118
73337
  return false;
@@ -73121,12 +73340,12 @@ function hasUncommittedChanges(repoPath, worktreePath) {
73121
73340
  function reapRecord(record2) {
73122
73341
  const { id, path: path7, repo, branch, ownerAgent } = record2;
73123
73342
  let warning = null;
73124
- if (existsSync63(path7)) {
73343
+ if (existsSync64(path7)) {
73125
73344
  if (hasUncommittedChanges(repo, path7)) {
73126
73345
  warning = `[worktree-reaper] Reaped worktree with uncommitted changes: ` + `id=${id} branch=${branch} agent=${ownerAgent ?? "unknown"} path=${path7}`;
73127
73346
  }
73128
73347
  try {
73129
- execFileSync18("git", ["worktree", "remove", "--force", path7], {
73348
+ execFileSync19("git", ["worktree", "remove", "--force", path7], {
73130
73349
  cwd: repo,
73131
73350
  stdio: "pipe"
73132
73351
  });
@@ -73142,7 +73361,7 @@ function runReaper(nowMs) {
73142
73361
  const warnings = [];
73143
73362
  for (const record2 of records) {
73144
73363
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
73145
- const worktreeExists = existsSync63(record2.path);
73364
+ const worktreeExists = existsSync64(record2.path);
73146
73365
  if (!worktreeExists) {
73147
73366
  deleteRecord(record2.id);
73148
73367
  reaped.push(record2.id);
@@ -73271,7 +73490,7 @@ import {
73271
73490
  rmSync as rmSync15,
73272
73491
  writeFileSync as writeFileSync30
73273
73492
  } from "node:fs";
73274
- import { join as join57 } from "node:path";
73493
+ import { join as join58 } from "node:path";
73275
73494
  function encodeCredentialsFilename(email) {
73276
73495
  const SAFE = new Set([
73277
73496
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -73461,16 +73680,16 @@ function resolveCredentialsDir(env2) {
73461
73680
  if (explicit && explicit.length > 0)
73462
73681
  return explicit;
73463
73682
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
73464
- return join57(stateBase, "google-workspace-mcp", "credentials");
73683
+ return join58(stateBase, "google-workspace-mcp", "credentials");
73465
73684
  }
73466
73685
  function writeSeedFile(dir, email, seed) {
73467
73686
  mkdirSync34(dir, { recursive: true, mode: 448 });
73468
73687
  chmodSync9(dir, 448);
73469
73688
  for (const name of readdirSync23(dir)) {
73470
- rmSync15(join57(dir, name), { force: true, recursive: true });
73689
+ rmSync15(join58(dir, name), { force: true, recursive: true });
73471
73690
  }
73472
73691
  const filename = encodeCredentialsFilename(email);
73473
- const filePath = join57(dir, filename);
73692
+ const filePath = join58(dir, filename);
73474
73693
  writeFileSync30(filePath, JSON.stringify(seed), { mode: 384 });
73475
73694
  chmodSync9(filePath, 384);
73476
73695
  return filePath;
@@ -73628,7 +73847,7 @@ function registerDriveMcpLauncherCommand(program3) {
73628
73847
 
73629
73848
  // src/cli/apply.ts
73630
73849
  init_source();
73631
- import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync67, mkdirSync as mkdirSync36, readdirSync as readdirSync25, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
73850
+ import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync68, mkdirSync as mkdirSync36, readdirSync as readdirSync25, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
73632
73851
  import { mkdir, writeFile } from "node:fs/promises";
73633
73852
  import { spawnSync as childSpawnSync } from "node:child_process";
73634
73853
  import readline from "node:readline";
@@ -73991,16 +74210,16 @@ agents:
73991
74210
 
73992
74211
  // src/cli/apply.ts
73993
74212
  init_resolver();
73994
- import { dirname as dirname19, join as join61, resolve as resolve42 } from "node:path";
73995
- import { homedir as homedir34 } from "node:os";
73996
- import { execFileSync as execFileSync19 } from "node:child_process";
74213
+ import { dirname as dirname19, join as join62, resolve as resolve42 } from "node:path";
74214
+ import { homedir as homedir35 } from "node:os";
74215
+ import { execFileSync as execFileSync20 } from "node:child_process";
73997
74216
  init_vault();
73998
74217
  init_loader();
73999
74218
  init_loader();
74000
74219
 
74001
74220
  // src/cli/update-prompt-hook.ts
74002
- import { existsSync as existsSync64, readFileSync as readFileSync54, writeFileSync as writeFileSync31, chmodSync as chmodSync10, mkdirSync as mkdirSync35 } from "node:fs";
74003
- import { join as join58 } from "node:path";
74221
+ import { existsSync as existsSync65, readFileSync as readFileSync54, writeFileSync as writeFileSync31, chmodSync as chmodSync10, mkdirSync as mkdirSync35 } from "node:fs";
74222
+ import { join as join59 } from "node:path";
74004
74223
  var HOOK_FILENAME = "update-card-on-prompt.sh";
74005
74224
  function updatePromptHookScript() {
74006
74225
  return `#!/bin/bash
@@ -74066,12 +74285,12 @@ exit 0
74066
74285
  `;
74067
74286
  }
74068
74287
  function installUpdatePromptHook(agentDir) {
74069
- const hooksDir = join58(agentDir, ".claude", "hooks");
74288
+ const hooksDir = join59(agentDir, ".claude", "hooks");
74070
74289
  mkdirSync35(hooksDir, { recursive: true });
74071
- const scriptPath = join58(hooksDir, HOOK_FILENAME);
74290
+ const scriptPath = join59(hooksDir, HOOK_FILENAME);
74072
74291
  const desired = updatePromptHookScript();
74073
74292
  let installed = false;
74074
- const existing = existsSync64(scriptPath) ? readFileSync54(scriptPath, "utf-8") : "";
74293
+ const existing = existsSync65(scriptPath) ? readFileSync54(scriptPath, "utf-8") : "";
74075
74294
  if (existing !== desired) {
74076
74295
  writeFileSync31(scriptPath, desired, { mode: 493 });
74077
74296
  chmodSync10(scriptPath, 493);
@@ -74081,8 +74300,8 @@ function installUpdatePromptHook(agentDir) {
74081
74300
  chmodSync10(scriptPath, 493);
74082
74301
  } catch {}
74083
74302
  }
74084
- const settingsPath = join58(agentDir, ".claude", "settings.json");
74085
- if (!existsSync64(settingsPath)) {
74303
+ const settingsPath = join59(agentDir, ".claude", "settings.json");
74304
+ if (!existsSync65(settingsPath)) {
74086
74305
  return { scriptPath, settingsPath, installed };
74087
74306
  }
74088
74307
  const raw = readFileSync54(settingsPath, "utf-8");
@@ -74201,13 +74420,13 @@ function detectInstallType() {
74201
74420
  // src/cli/operator-uid.ts
74202
74421
  import {
74203
74422
  chownSync as chownSync3,
74204
- existsSync as existsSync66,
74423
+ existsSync as existsSync67,
74205
74424
  lstatSync as lstatSync7,
74206
74425
  readdirSync as readdirSync24,
74207
74426
  realpathSync as realpathSync6,
74208
- statSync as statSync26
74427
+ statSync as statSync27
74209
74428
  } from "node:fs";
74210
- import { join as join60 } from "node:path";
74429
+ import { join as join61 } from "node:path";
74211
74430
  function resolveOperatorUid() {
74212
74431
  const sudoUid = process.env.SUDO_UID;
74213
74432
  if (sudoUid !== undefined) {
@@ -74223,19 +74442,19 @@ function resolveOperatorUid() {
74223
74442
  return;
74224
74443
  }
74225
74444
  function operatorOwnedPaths(home2) {
74226
- const root = join60(home2, ".switchroom");
74445
+ const root = join61(home2, ".switchroom");
74227
74446
  return [
74228
- join60(root, "vault"),
74229
- join60(root, "vault-auto-unlock"),
74230
- join60(root, "vault-audit.log"),
74231
- join60(root, "host-control-audit.log"),
74232
- join60(root, "accounts"),
74233
- join60(root, "compose")
74447
+ join61(root, "vault"),
74448
+ join61(root, "vault-auto-unlock"),
74449
+ join61(root, "vault-audit.log"),
74450
+ join61(root, "host-control-audit.log"),
74451
+ join61(root, "accounts"),
74452
+ join61(root, "compose")
74234
74453
  ];
74235
74454
  }
74236
74455
  function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
74237
74456
  const chown = deps.chown ?? ((p, u, g) => chownSync3(p, u, g));
74238
- const exists = deps.exists ?? ((p) => existsSync66(p));
74457
+ const exists = deps.exists ?? ((p) => existsSync67(p));
74239
74458
  const isSymlink = deps.isSymlink ?? ((p) => {
74240
74459
  try {
74241
74460
  return lstatSync7(p).isSymbolicLink();
@@ -74245,7 +74464,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
74245
74464
  });
74246
74465
  const isDir = deps.isDir ?? ((p) => {
74247
74466
  try {
74248
- return statSync26(p).isDirectory();
74467
+ return statSync27(p).isDirectory();
74249
74468
  } catch {
74250
74469
  return false;
74251
74470
  }
@@ -74279,7 +74498,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
74279
74498
  } catch {}
74280
74499
  if (isDir(target)) {
74281
74500
  for (const entry of readdir2(target)) {
74282
- visit(join60(target, entry));
74501
+ visit(join61(target, entry));
74283
74502
  }
74284
74503
  }
74285
74504
  };
@@ -74293,17 +74512,17 @@ var EMBEDDED_EXAMPLES = {
74293
74512
  switchroom: switchroom_default,
74294
74513
  minimal: minimal_default
74295
74514
  };
74296
- var DEFAULT_COMPOSE_PATH2 = join61(homedir34(), ".switchroom", "compose", "docker-compose.yml");
74515
+ var DEFAULT_COMPOSE_PATH2 = join62(homedir35(), ".switchroom", "compose", "docker-compose.yml");
74297
74516
  var COMPOSE_PROJECT2 = "switchroom";
74298
74517
  function resolveVaultBindMountDir(homeDir, ctx) {
74299
74518
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
74300
74519
  if (isCustomPath && ctx.customVaultPath) {
74301
74520
  return dirname19(ctx.customVaultPath);
74302
74521
  }
74303
- return join61(homeDir, ".switchroom", "vault");
74522
+ return join62(homeDir, ".switchroom", "vault");
74304
74523
  }
74305
74524
  function inspectVaultBindMountDir(vaultDir) {
74306
- if (!existsSync67(vaultDir))
74525
+ if (!existsSync68(vaultDir))
74307
74526
  return { kind: "missing" };
74308
74527
  const entries = readdirSync25(vaultDir);
74309
74528
  const unknown = [];
@@ -74329,42 +74548,42 @@ function hasVaultRefs(value) {
74329
74548
  return false;
74330
74549
  }
74331
74550
  async function ensureHostMountSources(config) {
74332
- const home2 = homedir34();
74551
+ const home2 = homedir35();
74333
74552
  const dirs = [
74334
- join61(home2, ".switchroom", "approvals"),
74335
- join61(home2, ".switchroom", "scheduler"),
74336
- join61(home2, ".switchroom", "logs"),
74337
- join61(home2, ".switchroom", "compose"),
74338
- join61(home2, ".switchroom", "broker-operator")
74553
+ join62(home2, ".switchroom", "approvals"),
74554
+ join62(home2, ".switchroom", "scheduler"),
74555
+ join62(home2, ".switchroom", "logs"),
74556
+ join62(home2, ".switchroom", "compose"),
74557
+ join62(home2, ".switchroom", "broker-operator")
74339
74558
  ];
74340
74559
  for (const name of Object.keys(config.agents)) {
74341
- dirs.push(join61(home2, ".switchroom", "agents", name));
74342
- dirs.push(join61(home2, ".switchroom", "logs", name));
74343
- dirs.push(join61(home2, ".claude", "projects", name));
74560
+ dirs.push(join62(home2, ".switchroom", "agents", name));
74561
+ dirs.push(join62(home2, ".switchroom", "logs", name));
74562
+ dirs.push(join62(home2, ".claude", "projects", name));
74344
74563
  }
74345
74564
  for (const dir of dirs) {
74346
74565
  await mkdir(dir, { recursive: true });
74347
74566
  }
74348
- const autoUnlockPath = join61(home2, ".switchroom", "vault-auto-unlock");
74349
- if (!existsSync67(autoUnlockPath)) {
74567
+ const autoUnlockPath = join62(home2, ".switchroom", "vault-auto-unlock");
74568
+ if (!existsSync68(autoUnlockPath)) {
74350
74569
  writeFileSync32(autoUnlockPath, "", { mode: 384 });
74351
74570
  }
74352
- const auditLogPath = join61(home2, ".switchroom", "vault-audit.log");
74353
- if (!existsSync67(auditLogPath)) {
74571
+ const auditLogPath = join62(home2, ".switchroom", "vault-audit.log");
74572
+ if (!existsSync68(auditLogPath)) {
74354
74573
  writeFileSync32(auditLogPath, "", { mode: 420 });
74355
74574
  }
74356
- const grantsDbPath = join61(home2, ".switchroom", "vault-grants.db");
74357
- if (!existsSync67(grantsDbPath)) {
74575
+ const grantsDbPath = join62(home2, ".switchroom", "vault-grants.db");
74576
+ if (!existsSync68(grantsDbPath)) {
74358
74577
  writeFileSync32(grantsDbPath, "", { mode: 384 });
74359
74578
  }
74360
- const hostdAuditLogPath = join61(home2, ".switchroom", "host-control-audit.log");
74361
- if (!existsSync67(hostdAuditLogPath)) {
74579
+ const hostdAuditLogPath = join62(home2, ".switchroom", "host-control-audit.log");
74580
+ if (!existsSync68(hostdAuditLogPath)) {
74362
74581
  writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
74363
74582
  }
74364
74583
  }
74365
74584
  function detectComposeV2() {
74366
74585
  try {
74367
- const out = execFileSync19("docker", ["compose", "version"], {
74586
+ const out = execFileSync20("docker", ["compose", "version"], {
74368
74587
  stdio: ["ignore", "pipe", "pipe"],
74369
74588
  encoding: "utf8"
74370
74589
  });
@@ -74379,7 +74598,7 @@ ${out.trim()}`;
74379
74598
  }
74380
74599
  function runApplyPreflight(config, opts = {}) {
74381
74600
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
74382
- if (hasVaultRefs(config) && !existsSync67(vaultPath)) {
74601
+ if (hasVaultRefs(config) && !existsSync68(vaultPath)) {
74383
74602
  throw new Error(`Config references vault keys (vault:<name>) but ${vaultPath} is missing. ` + `Run \`switchroom setup\` first to initialise the vault.`);
74384
74603
  }
74385
74604
  const detect = opts.detectComposeV2 ?? detectComposeV2;
@@ -74390,7 +74609,7 @@ function runApplyPreflight(config, opts = {}) {
74390
74609
  detectAndReportLegacyGdriveSlots(vaultPath);
74391
74610
  }
74392
74611
  function detectAndReportLegacyGdriveSlots(vaultPath) {
74393
- if (!existsSync67(vaultPath))
74612
+ if (!existsSync68(vaultPath))
74394
74613
  return;
74395
74614
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
74396
74615
  if (!passphrase)
@@ -74429,10 +74648,10 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
74429
74648
  `));
74430
74649
  }
74431
74650
  }
74432
- function writeInstallTypeCache(homeDir = homedir34()) {
74651
+ function writeInstallTypeCache(homeDir = homedir35()) {
74433
74652
  const ctx = detectInstallType();
74434
- const dir = join61(homeDir, ".switchroom");
74435
- const out = join61(dir, "install-type.json");
74653
+ const dir = join62(homeDir, ".switchroom");
74654
+ const out = join62(dir, "install-type.json");
74436
74655
  const tmp = `${out}.tmp`;
74437
74656
  mkdirSync36(dir, { recursive: true });
74438
74657
  const payload = {
@@ -74481,14 +74700,14 @@ Applying switchroom config...
74481
74700
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
74482
74701
  `));
74483
74702
  try {
74484
- installUpdatePromptHook(join61(agentsDir, name));
74703
+ installUpdatePromptHook(join62(agentsDir, name));
74485
74704
  } catch (hookErr) {
74486
74705
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
74487
74706
  `));
74488
74707
  }
74489
74708
  try {
74490
74709
  const uid = allocateAgentUid(name);
74491
- alignAgentUid(name, join61(agentsDir, name), uid, {
74710
+ alignAgentUid(name, join62(agentsDir, name), uid, {
74492
74711
  confirm: !options.nonInteractive,
74493
74712
  writeOut
74494
74713
  });
@@ -74525,7 +74744,7 @@ Applying switchroom config...
74525
74744
  for (const name of agentNames) {
74526
74745
  try {
74527
74746
  const uid = allocateAgentUid(name);
74528
- alignAgentUid(name, join61(agentsDir, name), uid, {
74747
+ alignAgentUid(name, join62(agentsDir, name), uid, {
74529
74748
  confirm: !options.nonInteractive,
74530
74749
  writeOut
74531
74750
  });
@@ -74539,7 +74758,7 @@ Applying switchroom config...
74539
74758
  }
74540
74759
  const vaultPathConfigured = config.vault?.path;
74541
74760
  const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
74542
- const migrationResult = migrateVaultLayout(homedir34(), {
74761
+ const migrationResult = migrateVaultLayout(homedir35(), {
74543
74762
  customVaultPath
74544
74763
  });
74545
74764
  switch (migrationResult.kind) {
@@ -74565,7 +74784,7 @@ Applying switchroom config...
74565
74784
  writeErr(formatDivergentRecoveryMessage(migrationResult.details));
74566
74785
  process.exit(4);
74567
74786
  }
74568
- const postMigrationInspect = inspectVaultLayout(homedir34());
74787
+ const postMigrationInspect = inspectVaultLayout(homedir35());
74569
74788
  const acceptable = [
74570
74789
  "no-vault",
74571
74790
  "already-migrated",
@@ -74580,7 +74799,7 @@ Applying switchroom config...
74580
74799
  `));
74581
74800
  process.exit(5);
74582
74801
  }
74583
- const vaultDir = resolveVaultBindMountDir(homedir34(), {
74802
+ const vaultDir = resolveVaultBindMountDir(homedir35(), {
74584
74803
  migrationKind: migrationResult.kind,
74585
74804
  customVaultPath
74586
74805
  });
@@ -74610,7 +74829,7 @@ Applying switchroom config...
74610
74829
  imageTag: composeImageTag,
74611
74830
  buildMode: options.buildLocal ? "local" : "pull",
74612
74831
  buildContext: options.buildContext,
74613
- homeDir: homedir34(),
74832
+ homeDir: homedir35(),
74614
74833
  switchroomConfigPath,
74615
74834
  operatorUid
74616
74835
  });
@@ -74630,7 +74849,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
74630
74849
  writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
74631
74850
  `));
74632
74851
  if (process.geteuid?.() === 0 && operatorUid !== undefined) {
74633
- const restored = restoreOperatorOwnership(homedir34(), operatorUid);
74852
+ const restored = restoreOperatorOwnership(homedir35(), operatorUid);
74634
74853
  if (restored.length > 0) {
74635
74854
  writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
74636
74855
  `));
@@ -74678,7 +74897,7 @@ function copyExampleConfig2(name) {
74678
74897
  throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
74679
74898
  }
74680
74899
  const dest = resolve42(process.cwd(), "switchroom.yaml");
74681
- if (existsSync67(dest)) {
74900
+ if (existsSync68(dest)) {
74682
74901
  console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
74683
74902
  return;
74684
74903
  }
@@ -74689,7 +74908,7 @@ function copyExampleConfig2(name) {
74689
74908
  return;
74690
74909
  }
74691
74910
  const exampleFile = resolve42(import.meta.dirname, `../../examples/${name}.yaml`);
74692
- if (!existsSync67(exampleFile)) {
74911
+ if (!existsSync68(exampleFile)) {
74693
74912
  throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
74694
74913
  }
74695
74914
  copyFileSync11(exampleFile, dest);
@@ -74700,8 +74919,8 @@ function findUnwritableAgentDirs(config, opts) {
74700
74919
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
74701
74920
  const unwritable = [];
74702
74921
  for (const name of targets) {
74703
- const startSh = join61(agentsDir, name, "start.sh");
74704
- if (!existsSync67(startSh))
74922
+ const startSh = join62(agentsDir, name, "start.sh");
74923
+ if (!existsSync68(startSh))
74705
74924
  continue;
74706
74925
  try {
74707
74926
  accessSync3(startSh, fsConstants6.W_OK);
@@ -74879,9 +75098,9 @@ function runRedactStdin() {
74879
75098
  }
74880
75099
 
74881
75100
  // src/cli/status-ask.ts
74882
- import { readFileSync as readFileSync55, existsSync as existsSync68, readdirSync as readdirSync26 } from "node:fs";
74883
- import { join as join62 } from "node:path";
74884
- import { homedir as homedir35 } from "node:os";
75101
+ import { readFileSync as readFileSync55, existsSync as existsSync69, readdirSync as readdirSync26 } from "node:fs";
75102
+ import { join as join63 } from "node:path";
75103
+ import { homedir as homedir36 } from "node:os";
74885
75104
 
74886
75105
  // src/status-ask/report.ts
74887
75106
  function parseJsonl(content) {
@@ -75202,7 +75421,7 @@ function runReport(opts) {
75202
75421
  function resolveSources(explicitPath) {
75203
75422
  if (explicitPath != null && explicitPath.trim() !== "") {
75204
75423
  const trimmed = explicitPath.trim();
75205
- if (!existsSync68(trimmed)) {
75424
+ if (!existsSync69(trimmed)) {
75206
75425
  process.stderr.write(`status-ask report: ${trimmed}: file not found
75207
75426
  `);
75208
75427
  process.exit(1);
@@ -75216,9 +75435,9 @@ function resolveSources(explicitPath) {
75216
75435
  const config = loadConfig();
75217
75436
  agentsDir = resolveAgentsDir(config);
75218
75437
  } catch {
75219
- agentsDir = join62(homedir35(), ".switchroom", "agents");
75438
+ agentsDir = join63(homedir36(), ".switchroom", "agents");
75220
75439
  }
75221
- if (!existsSync68(agentsDir))
75440
+ if (!existsSync69(agentsDir))
75222
75441
  return [];
75223
75442
  const sources = [];
75224
75443
  let entries;
@@ -75228,8 +75447,8 @@ function resolveSources(explicitPath) {
75228
75447
  return [];
75229
75448
  }
75230
75449
  for (const name of entries) {
75231
- const path8 = join62(agentsDir, name, "runtime-metrics.jsonl");
75232
- if (existsSync68(path8)) {
75450
+ const path8 = join63(agentsDir, name, "runtime-metrics.jsonl");
75451
+ if (existsSync69(path8)) {
75233
75452
  sources.push({ path: path8, agent: name });
75234
75453
  }
75235
75454
  }
@@ -75250,17 +75469,17 @@ function inferAgentFromPath(p) {
75250
75469
 
75251
75470
  // src/cli/agent-config.ts
75252
75471
  init_helpers();
75253
- import { join as join63 } from "node:path";
75254
- import { homedir as homedir36 } from "node:os";
75472
+ import { join as join64 } from "node:path";
75473
+ import { homedir as homedir37 } from "node:os";
75255
75474
  import {
75256
- existsSync as existsSync69,
75475
+ existsSync as existsSync70,
75257
75476
  mkdirSync as mkdirSync37,
75258
75477
  appendFileSync as appendFileSync4,
75259
75478
  readFileSync as readFileSync56
75260
75479
  } from "node:fs";
75261
- var AUDIT_ROOT = join63(homedir36(), ".switchroom", "audit");
75480
+ var AUDIT_ROOT = join64(homedir37(), ".switchroom", "audit");
75262
75481
  function auditPathFor(agent) {
75263
- return join63(AUDIT_ROOT, agent, "agent-config.jsonl");
75482
+ return join64(AUDIT_ROOT, agent, "agent-config.jsonl");
75264
75483
  }
75265
75484
  function appendAudit(agent, cmd, args, exit, opts = {}) {
75266
75485
  const row = {
@@ -75274,7 +75493,7 @@ function appendAudit(agent, cmd, args, exit, opts = {}) {
75274
75493
  const path8 = opts.auditPath ?? auditPathFor(agent);
75275
75494
  const dir = path8.slice(0, path8.lastIndexOf("/"));
75276
75495
  try {
75277
- if (!existsSync69(dir)) {
75496
+ if (!existsSync70(dir)) {
75278
75497
  mkdirSync37(dir, { recursive: true });
75279
75498
  }
75280
75499
  appendFileSync4(path8, JSON.stringify(row) + `
@@ -75286,7 +75505,7 @@ function isContainerContext(env2 = process.env, opts = {}) {
75286
75505
  return true;
75287
75506
  const probe2 = opts.dockerEnvPath ?? "/.dockerenv";
75288
75507
  try {
75289
- if (existsSync69(probe2))
75508
+ if (existsSync70(probe2))
75290
75509
  return true;
75291
75510
  } catch {}
75292
75511
  return false;
@@ -75347,7 +75566,7 @@ function getAgentSlice(config, agent) {
75347
75566
  }
75348
75567
  function readAuditTail(agent, limit, opts = {}) {
75349
75568
  const path8 = opts.auditPath ?? auditPathFor(agent);
75350
- if (!existsSync69(path8))
75569
+ if (!existsSync70(path8))
75351
75570
  return [];
75352
75571
  let raw;
75353
75572
  try {
@@ -75507,32 +75726,32 @@ var import_yaml14 = __toESM(require_dist(), 1);
75507
75726
  init_paths();
75508
75727
  import {
75509
75728
  closeSync as closeSync13,
75510
- existsSync as existsSync70,
75729
+ existsSync as existsSync71,
75511
75730
  fsyncSync as fsyncSync6,
75512
75731
  mkdirSync as mkdirSync38,
75513
75732
  openSync as openSync13,
75514
75733
  readdirSync as readdirSync27,
75515
75734
  readFileSync as readFileSync57,
75516
75735
  renameSync as renameSync14,
75517
- statSync as statSync27,
75736
+ statSync as statSync28,
75518
75737
  unlinkSync as unlinkSync14,
75519
75738
  writeSync as writeSync8
75520
75739
  } from "node:fs";
75521
- import { join as join64, resolve as resolve43 } from "node:path";
75740
+ import { join as join65, resolve as resolve43 } from "node:path";
75522
75741
  var STAGING_SUBDIR = ".staging";
75523
75742
  function overlayPathsFor(agent, opts = {}) {
75524
75743
  const base = opts.root ? resolve43(opts.root, agent) : resolve43(resolveDualPath(`~/.switchroom/agents/${agent}`));
75525
- const scheduleDir = join64(base, "schedule.d");
75526
- const scheduleStagingDir = join64(scheduleDir, STAGING_SUBDIR);
75527
- const skillsDir = join64(base, "skills.d");
75528
- const skillsStagingDir = join64(skillsDir, STAGING_SUBDIR);
75744
+ const scheduleDir = join65(base, "schedule.d");
75745
+ const scheduleStagingDir = join65(scheduleDir, STAGING_SUBDIR);
75746
+ const skillsDir = join65(base, "skills.d");
75747
+ const skillsStagingDir = join65(skillsDir, STAGING_SUBDIR);
75529
75748
  return {
75530
75749
  agentRoot: base,
75531
75750
  scheduleDir,
75532
75751
  scheduleStagingDir,
75533
75752
  skillsDir,
75534
75753
  skillsStagingDir,
75535
- lockPath: join64(base, ".lock"),
75754
+ lockPath: join65(base, ".lock"),
75536
75755
  stagingDir: scheduleStagingDir
75537
75756
  };
75538
75757
  }
@@ -75558,7 +75777,7 @@ function withAgentLock(paths, fn) {
75558
75777
  if (e.code !== "EEXIST")
75559
75778
  throw err;
75560
75779
  try {
75561
- const age = Date.now() - statSync27(paths.lockPath).mtimeMs;
75780
+ const age = Date.now() - statSync28(paths.lockPath).mtimeMs;
75562
75781
  if (age > 30000) {
75563
75782
  unlinkSync14(paths.lockPath);
75564
75783
  continue;
@@ -75586,8 +75805,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
75586
75805
  const paths = overlayPathsFor(agent, opts);
75587
75806
  return withAgentLock(paths, () => {
75588
75807
  ensureDirs(paths);
75589
- const stagingPath = join64(paths.scheduleStagingDir, `${slug}.yaml`);
75590
- const finalPath = join64(paths.scheduleDir, `${slug}.yaml`);
75808
+ const stagingPath = join65(paths.scheduleStagingDir, `${slug}.yaml`);
75809
+ const finalPath = join65(paths.scheduleDir, `${slug}.yaml`);
75591
75810
  const fd = openSync13(stagingPath, "w", 384);
75592
75811
  try {
75593
75812
  writeSync8(fd, yamlText);
@@ -75603,8 +75822,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
75603
75822
  const paths = overlayPathsFor(agent, opts);
75604
75823
  return withAgentLock(paths, () => {
75605
75824
  ensureSkillsDirs(paths);
75606
- const stagingPath = join64(paths.skillsStagingDir, `${slug}.yaml`);
75607
- const finalPath = join64(paths.skillsDir, `${slug}.yaml`);
75825
+ const stagingPath = join65(paths.skillsStagingDir, `${slug}.yaml`);
75826
+ const finalPath = join65(paths.skillsDir, `${slug}.yaml`);
75608
75827
  const fd = openSync13(stagingPath, "w", 384);
75609
75828
  try {
75610
75829
  writeSync8(fd, yamlText);
@@ -75619,8 +75838,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
75619
75838
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
75620
75839
  const paths = overlayPathsFor(agent, opts);
75621
75840
  return withAgentLock(paths, () => {
75622
- const finalPath = join64(paths.skillsDir, `${slug}.yaml`);
75623
- if (!existsSync70(finalPath))
75841
+ const finalPath = join65(paths.skillsDir, `${slug}.yaml`);
75842
+ if (!existsSync71(finalPath))
75624
75843
  return false;
75625
75844
  unlinkSync14(finalPath);
75626
75845
  return true;
@@ -75628,13 +75847,13 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
75628
75847
  }
75629
75848
  function listSkillsOverlayEntries(agent, opts = {}) {
75630
75849
  const paths = overlayPathsFor(agent, opts);
75631
- if (!existsSync70(paths.skillsDir))
75850
+ if (!existsSync71(paths.skillsDir))
75632
75851
  return [];
75633
75852
  const out = [];
75634
75853
  for (const name of readdirSync27(paths.skillsDir)) {
75635
75854
  if (!/\.ya?ml$/i.test(name))
75636
75855
  continue;
75637
- const full = join64(paths.skillsDir, name);
75856
+ const full = join65(paths.skillsDir, name);
75638
75857
  try {
75639
75858
  const raw = readFileSync57(full, "utf-8");
75640
75859
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -75646,8 +75865,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
75646
75865
  function deleteOverlayEntry(agent, slug, opts = {}) {
75647
75866
  const paths = overlayPathsFor(agent, opts);
75648
75867
  return withAgentLock(paths, () => {
75649
- const finalPath = join64(paths.scheduleDir, `${slug}.yaml`);
75650
- if (!existsSync70(finalPath))
75868
+ const finalPath = join65(paths.scheduleDir, `${slug}.yaml`);
75869
+ if (!existsSync71(finalPath))
75651
75870
  return false;
75652
75871
  unlinkSync14(finalPath);
75653
75872
  return true;
@@ -75655,13 +75874,13 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
75655
75874
  }
75656
75875
  function listOverlayEntries(agent, opts = {}) {
75657
75876
  const paths = overlayPathsFor(agent, opts);
75658
- if (!existsSync70(paths.scheduleDir))
75877
+ if (!existsSync71(paths.scheduleDir))
75659
75878
  return [];
75660
75879
  const out = [];
75661
75880
  for (const name of readdirSync27(paths.scheduleDir)) {
75662
75881
  if (!/\.ya?ml$/i.test(name))
75663
75882
  continue;
75664
- const full = join64(paths.scheduleDir, name);
75883
+ const full = join65(paths.scheduleDir, name);
75665
75884
  try {
75666
75885
  const raw = readFileSync57(full, "utf-8");
75667
75886
  const slug = name.replace(/\.ya?ml$/i, "");
@@ -75806,7 +76025,7 @@ function reconcileAgentCronOnly(agent) {
75806
76025
  // src/cli/agent-config-pending.ts
75807
76026
  import {
75808
76027
  closeSync as closeSync14,
75809
- existsSync as existsSync71,
76028
+ existsSync as existsSync72,
75810
76029
  fsyncSync as fsyncSync7,
75811
76030
  mkdirSync as mkdirSync39,
75812
76031
  openSync as openSync14,
@@ -75817,12 +76036,12 @@ import {
75817
76036
  writeFileSync as writeFileSync33,
75818
76037
  writeSync as writeSync9
75819
76038
  } from "node:fs";
75820
- import { join as join65 } from "node:path";
76039
+ import { join as join66 } from "node:path";
75821
76040
  import { randomBytes as randomBytes13 } from "node:crypto";
75822
76041
  var STAGE_ID_PREFIX = "cap_";
75823
76042
  function pendingDir(agent, opts = {}) {
75824
76043
  const paths = overlayPathsFor(agent, opts);
75825
- return join65(paths.scheduleDir, ".pending");
76044
+ return join66(paths.scheduleDir, ".pending");
75826
76045
  }
75827
76046
  function ensurePendingDir(agent, opts = {}) {
75828
76047
  const dir = pendingDir(agent, opts);
@@ -75835,8 +76054,8 @@ function newStageId() {
75835
76054
  function stagePendingScheduleEntry(opts) {
75836
76055
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
75837
76056
  const stageId = opts.stageId ?? newStageId();
75838
- const yamlPath = join65(dir, `${stageId}.yaml`);
75839
- const metaPath = join65(dir, `${stageId}.meta.json`);
76057
+ const yamlPath = join66(dir, `${stageId}.yaml`);
76058
+ const metaPath = join66(dir, `${stageId}.meta.json`);
75840
76059
  const meta = {
75841
76060
  v: 1,
75842
76061
  stage_id: stageId,
@@ -75863,16 +76082,16 @@ function stagePendingScheduleEntry(opts) {
75863
76082
  }
75864
76083
  function listPendingScheduleEntries(agent, opts = {}) {
75865
76084
  const dir = pendingDir(agent, opts);
75866
- if (!existsSync71(dir))
76085
+ if (!existsSync72(dir))
75867
76086
  return [];
75868
76087
  const out = [];
75869
76088
  for (const name of readdirSync28(dir).sort()) {
75870
76089
  if (!name.endsWith(".meta.json"))
75871
76090
  continue;
75872
76091
  const stageId = name.slice(0, -".meta.json".length);
75873
- const metaPath = join65(dir, name);
75874
- const yamlPath = join65(dir, `${stageId}.yaml`);
75875
- if (!existsSync71(yamlPath))
76092
+ const metaPath = join66(dir, name);
76093
+ const yamlPath = join66(dir, `${stageId}.yaml`);
76094
+ if (!existsSync72(yamlPath))
75876
76095
  continue;
75877
76096
  try {
75878
76097
  const meta = JSON.parse(readFileSync58(metaPath, "utf-8"));
@@ -75890,8 +76109,8 @@ function commitPendingScheduleEntry(opts) {
75890
76109
  return { committed: false, reason: "not_found" };
75891
76110
  const slug = match.meta.entry.name ?? match.stageId;
75892
76111
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
75893
- const finalPath = join65(paths.scheduleDir, `${slug}.yaml`);
75894
- if (existsSync71(finalPath)) {
76112
+ const finalPath = join66(paths.scheduleDir, `${slug}.yaml`);
76113
+ if (existsSync72(finalPath)) {
75895
76114
  return { committed: false, reason: "slug_collision" };
75896
76115
  }
75897
76116
  renameSync15(match.yamlPath, finalPath);
@@ -75913,7 +76132,7 @@ function denyPendingScheduleEntry(opts) {
75913
76132
  }
75914
76133
 
75915
76134
  // src/cli/agent-config-write.ts
75916
- import { existsSync as existsSync72, readFileSync as readFileSync59 } from "node:fs";
76135
+ import { existsSync as existsSync73, readFileSync as readFileSync59 } from "node:fs";
75917
76136
  var MAX_ENTRIES_PER_AGENT = 20;
75918
76137
  function checkOperatorContext(verb, env2 = process.env) {
75919
76138
  if (env2.SWITCHROOM_OPERATOR === "1")
@@ -76147,7 +76366,7 @@ function scheduleRemove(opts) {
76147
76366
  }
76148
76367
  let priorContent = null;
76149
76368
  try {
76150
- if (existsSync72(match.path))
76369
+ if (existsSync73(match.path))
76151
76370
  priorContent = readFileSync59(match.path, "utf-8");
76152
76371
  } catch {}
76153
76372
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
@@ -76340,10 +76559,10 @@ function registerAgentConfigWriteCommands(program3) {
76340
76559
 
76341
76560
  // src/cli/agent-config-skill-write.ts
76342
76561
  var import_yaml15 = __toESM(require_dist(), 1);
76343
- import { existsSync as existsSync73 } from "node:fs";
76562
+ import { existsSync as existsSync74 } from "node:fs";
76344
76563
  init_reconcile_default_skills();
76345
76564
  var import_yaml16 = __toESM(require_dist(), 1);
76346
- import { join as join66 } from "node:path";
76565
+ import { join as join67 } from "node:path";
76347
76566
  var MAX_SKILLS_PER_AGENT = 20;
76348
76567
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
76349
76568
  function exitCodeFor2(code) {
@@ -76418,8 +76637,8 @@ function skillInstall(opts) {
76418
76637
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
76419
76638
  }
76420
76639
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
76421
- const skillPath = join66(poolDir, skillName);
76422
- if (!existsSync73(skillPath)) {
76640
+ const skillPath = join67(poolDir, skillName);
76641
+ if (!existsSync74(skillPath)) {
76423
76642
  return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
76424
76643
  }
76425
76644
  const yamlText = import_yaml15.stringify({ skills: [skillName] });
@@ -76603,23 +76822,23 @@ init_source();
76603
76822
  init_helpers();
76604
76823
  init_loader();
76605
76824
  import {
76606
- existsSync as existsSync75,
76825
+ existsSync as existsSync76,
76607
76826
  readdirSync as readdirSync29,
76608
76827
  readFileSync as readFileSync61,
76609
76828
  renameSync as renameSync16,
76610
- statSync as statSync28,
76829
+ statSync as statSync29,
76611
76830
  unlinkSync as unlinkSync16
76612
76831
  } from "node:fs";
76613
76832
  import { createHash as createHash13 } from "node:crypto";
76614
- import { join as join67 } from "node:path";
76833
+ import { join as join68 } from "node:path";
76615
76834
  function planCronUnitRenames(agentsDir, agents) {
76616
76835
  const plans = [];
76617
76836
  for (const [agentName, agentConfig] of Object.entries(agents)) {
76618
76837
  const schedule = agentConfig.schedule ?? [];
76619
76838
  if (schedule.length === 0)
76620
76839
  continue;
76621
- const telegramDir = join67(agentsDir, agentName, "telegram");
76622
- if (!existsSync75(telegramDir))
76840
+ const telegramDir = join68(agentsDir, agentName, "telegram");
76841
+ if (!existsSync76(telegramDir))
76623
76842
  continue;
76624
76843
  let entries;
76625
76844
  try {
@@ -76640,8 +76859,8 @@ function planCronUnitRenames(agentsDir, agents) {
76640
76859
  continue;
76641
76860
  plans.push({
76642
76861
  agent: agentName,
76643
- from: join67(telegramDir, file),
76644
- to: join67(telegramDir, canonical),
76862
+ from: join68(telegramDir, file),
76863
+ to: join68(telegramDir, canonical),
76645
76864
  scheduleIdx: idx,
76646
76865
  entry
76647
76866
  });
@@ -76653,7 +76872,7 @@ function sha256File2(path8) {
76653
76872
  return createHash13("sha256").update(readFileSync61(path8)).digest("hex");
76654
76873
  }
76655
76874
  function renamePair(from, to, opts = {}) {
76656
- if (existsSync75(to)) {
76875
+ if (existsSync76(to)) {
76657
76876
  let identical = false;
76658
76877
  try {
76659
76878
  identical = sha256File2(from) === sha256File2(to);
@@ -76746,7 +76965,7 @@ function registerMigrateCommand(program3) {
76746
76965
  const fromSidecar = p.from.replace(/\.sh$/, ".source");
76747
76966
  const toSidecar = p.to.replace(/\.sh$/, ".source");
76748
76967
  let sidecarStatus = null;
76749
- if (existsSync75(fromSidecar) && statSync28(fromSidecar).isFile()) {
76968
+ if (existsSync76(fromSidecar) && statSync29(fromSidecar).isFile()) {
76750
76969
  sidecarStatus = renamePair(fromSidecar, toSidecar);
76751
76970
  }
76752
76971
  switch (status.kind) {
@@ -76778,9 +76997,9 @@ function registerMigrateCommand(program3) {
76778
76997
  // src/cli/hostd.ts
76779
76998
  init_source();
76780
76999
  init_helpers();
76781
- import { existsSync as existsSync76, mkdirSync as mkdirSync40, readdirSync as readdirSync30, readFileSync as readFileSync62, writeFileSync as writeFileSync34, statSync as statSync29, copyFileSync as copyFileSync12 } from "node:fs";
76782
- import { homedir as homedir37 } from "node:os";
76783
- import { join as join68 } from "node:path";
77000
+ import { existsSync as existsSync77, mkdirSync as mkdirSync40, readdirSync as readdirSync30, readFileSync as readFileSync62, writeFileSync as writeFileSync34, statSync as statSync30, copyFileSync as copyFileSync12 } from "node:fs";
77001
+ import { homedir as homedir38 } from "node:os";
77002
+ import { join as join69 } from "node:path";
76784
77003
  import { spawnSync as spawnSync11 } from "node:child_process";
76785
77004
  init_audit_reader();
76786
77005
  var DEFAULT_IMAGE_TAG = "latest";
@@ -76871,14 +77090,14 @@ networks:
76871
77090
  `;
76872
77091
  }
76873
77092
  function hostdDir() {
76874
- return join68(homedir37(), ".switchroom", "hostd");
77093
+ return join69(homedir38(), ".switchroom", "hostd");
76875
77094
  }
76876
77095
  function hostdComposePath() {
76877
- return join68(hostdDir(), "docker-compose.yml");
77096
+ return join69(hostdDir(), "docker-compose.yml");
76878
77097
  }
76879
77098
  function backupExistingCompose() {
76880
77099
  const p = hostdComposePath();
76881
- if (!existsSync76(p))
77100
+ if (!existsSync77(p))
76882
77101
  return null;
76883
77102
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
76884
77103
  const bak = `${p}.bak-${ts}`;
@@ -76913,7 +77132,7 @@ async function doInstall(opts, program3) {
76913
77132
  const composePath = hostdComposePath();
76914
77133
  mkdirSync40(dir, { recursive: true });
76915
77134
  const yaml = renderHostdComposeFile({
76916
- hostHome: homedir37(),
77135
+ hostHome: homedir38(),
76917
77136
  imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
76918
77137
  operatorUid: resolveOperatorUid()
76919
77138
  });
@@ -76955,7 +77174,7 @@ function doStatus() {
76955
77174
  const composeYml = hostdComposePath();
76956
77175
  console.log(source_default.bold("switchroom-hostd"));
76957
77176
  console.log("");
76958
- if (!existsSync76(composeYml)) {
77177
+ if (!existsSync77(composeYml)) {
76959
77178
  console.log(source_default.yellow(" compose: not installed"));
76960
77179
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
76961
77180
  return;
@@ -76976,15 +77195,15 @@ function doStatus() {
76976
77195
  } else {
76977
77196
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
76978
77197
  }
76979
- if (existsSync76(dir)) {
77198
+ if (existsSync77(dir)) {
76980
77199
  const entries = [];
76981
77200
  try {
76982
77201
  for (const name of readdirSync30(dir)) {
76983
77202
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
76984
77203
  continue;
76985
- const sockPath = join68(dir, name, "sock");
76986
- if (existsSync76(sockPath)) {
76987
- const st = statSync29(sockPath);
77204
+ const sockPath = join69(dir, name, "sock");
77205
+ if (existsSync77(sockPath)) {
77206
+ const st = statSync30(sockPath);
76988
77207
  if ((st.mode & 61440) === 49152) {
76989
77208
  entries.push(`${name} \u2192 ${sockPath}`);
76990
77209
  }
@@ -77002,7 +77221,7 @@ function doStatus() {
77002
77221
  }
77003
77222
  function doUninstall() {
77004
77223
  const composeYml = hostdComposePath();
77005
- if (!existsSync76(composeYml)) {
77224
+ if (!existsSync77(composeYml)) {
77006
77225
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
77007
77226
  return;
77008
77227
  }
@@ -77026,7 +77245,7 @@ function registerHostdCommand(program3) {
77026
77245
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
77027
77246
  hostd.command("audit").description("Tail and filter the hostd audit log (privileged-verb call history)").option("--tail <n>", "Number of matching entries to show (default: 50)", "50").option("--agent <name>", "Filter to a specific caller agent").option("--op <verb>", "Filter to a specific hostd verb (e.g. update_apply, agent_restart)").option("--error", "Show only failed (error/denied) entries").option("--verbose", "Show the captured stderr / error tail under each failed row").option("--path <file>", "Override audit log path (for debugging)").action((opts) => {
77028
77247
  const logPath = opts.path ?? defaultAuditLogPath2();
77029
- if (!existsSync76(logPath)) {
77248
+ if (!existsSync77(logPath)) {
77030
77249
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
77031
77250
  The log is created when hostd handles its first privileged-verb request.`));
77032
77251
  return;