switchroom 0.12.19 → 0.12.21

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.
@@ -13857,7 +13857,11 @@ var init_schema = __esm(() => {
13857
13857
  path: ["approvalAuth"]
13858
13858
  });
13859
13859
  }
13860
- }).describe("Vault-broker daemon configuration. The broker holds the decrypted vault " + "in memory and serves secrets to cron scripts via a Unix socket, so the " + "vault passphrase is entered once at startup rather than per-cron invocation.")
13860
+ }).describe("Vault-broker daemon configuration. The broker holds the decrypted vault " + "in memory and serves secrets to cron scripts via a Unix socket, so the " + "vault passphrase is entered once at startup rather than per-cron invocation."),
13861
+ backup: exports_external.object({
13862
+ destination: exports_external.string().optional().describe("Destination directory for `switchroom vault backup`. " + "When unset, the CLI defaults to " + "`~/.switchroom-config/vault-backups/` if that operator config " + "repo exists, else `~/.switchroom/vault-backups/`. " + "Path is tilde-expanded at read time. " + "MUST NOT be `~/.switchroom/vault/` (the broker bind-mount dir, " + "validated by `switchroom apply` against an artifact allowlist)."),
13863
+ retain: exports_external.number().int().nonnegative().default(30).describe("How many of the most-recent backups to keep in the destination dir. " + "Older ones are pruned after each new backup is written. Default 30 " + "= roughly a month at daily cadence.")
13864
+ }).optional().describe("Configuration for `switchroom vault backup`. Optional \u2014 the CLI works " + "with built-in defaults if this block is absent. The backed-up file is " + "the AES-256-GCM-encrypted vault envelope; the operator passphrase " + "remains the gate, so committing backups to a private git repo extends " + "durability without weakening encryption (provided the auto-unlock " + "blob is NEVER co-located \u2014 `vault backup` refuses to write into a " + "directory that contains an auto-unlock-shaped sibling).")
13861
13865
  });
13862
13866
  QuotaConfigSchema = exports_external.object({
13863
13867
  weekly_budget_usd: exports_external.number().positive().optional().describe("Weekly USD spend budget. If unset, the greeting shows raw usage only."),
@@ -27440,10 +27444,10 @@ var CHAIN_GENESIS = "GENESIS", SEP = "\x00";
27440
27444
  var init_audit_hashchain = () => {};
27441
27445
 
27442
27446
  // src/host-control/audit-reader.ts
27443
- import { homedir as homedir16 } from "node:os";
27444
- import { join as join32 } from "node:path";
27445
- function defaultAuditLogPath2(home2 = homedir16()) {
27446
- return join32(home2, ".switchroom", "host-control-audit.log");
27447
+ import { homedir as homedir17 } from "node:os";
27448
+ import { join as join34 } from "node:path";
27449
+ function defaultAuditLogPath2(home2 = homedir17()) {
27450
+ return join34(home2, ".switchroom", "host-control-audit.log");
27447
27451
  }
27448
27452
  function parseAuditLine2(line) {
27449
27453
  const trimmed = line.trim();
@@ -27598,17 +27602,17 @@ var init_doctor_status = __esm(() => {
27598
27602
 
27599
27603
  // src/manifest.ts
27600
27604
  import {
27601
- existsSync as existsSync44,
27602
- readFileSync as readFileSync41,
27603
- readdirSync as readdirSync15
27605
+ existsSync as existsSync46,
27606
+ readFileSync as readFileSync42,
27607
+ readdirSync as readdirSync16
27604
27608
  } from "node:fs";
27605
- import { dirname as dirname11, join as join36 } from "node:path";
27609
+ import { dirname as dirname11, join as join38 } from "node:path";
27606
27610
  import { execSync as execSync2 } from "node:child_process";
27607
27611
  function locateManifestPath() {
27608
27612
  let dir = import.meta.dirname;
27609
27613
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
27610
- const candidate = join36(dir, "dependencies.json");
27611
- if (existsSync44(candidate))
27614
+ const candidate = join38(dir, "dependencies.json");
27615
+ if (existsSync46(candidate))
27612
27616
  return candidate;
27613
27617
  dir = dirname11(dir);
27614
27618
  }
@@ -27621,7 +27625,7 @@ function loadManifest(manifestPath) {
27621
27625
  }
27622
27626
  let raw;
27623
27627
  try {
27624
- raw = readFileSync41(path4, "utf-8");
27628
+ raw = readFileSync42(path4, "utf-8");
27625
27629
  } catch (err) {
27626
27630
  throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
27627
27631
  }
@@ -27677,16 +27681,16 @@ function probeClaudeVersion() {
27677
27681
  }
27678
27682
  function probePlaywrightMcpVersion() {
27679
27683
  const home2 = process.env.HOME ?? "";
27680
- const npxCache = join36(home2, ".npm/_npx");
27681
- if (!existsSync44(npxCache))
27684
+ const npxCache = join38(home2, ".npm/_npx");
27685
+ if (!existsSync46(npxCache))
27682
27686
  return null;
27683
27687
  try {
27684
- const entries = readdirSync15(npxCache);
27688
+ const entries = readdirSync16(npxCache);
27685
27689
  for (const entry of entries) {
27686
- const pkgPath = join36(npxCache, entry, "node_modules/@playwright/mcp/package.json");
27687
- if (existsSync44(pkgPath)) {
27690
+ const pkgPath = join38(npxCache, entry, "node_modules/@playwright/mcp/package.json");
27691
+ if (existsSync46(pkgPath)) {
27688
27692
  try {
27689
- const pkg = JSON.parse(readFileSync41(pkgPath, "utf-8"));
27693
+ const pkg = JSON.parse(readFileSync42(pkgPath, "utf-8"));
27690
27694
  if (pkg.version)
27691
27695
  return pkg.version;
27692
27696
  } catch {}
@@ -27778,13 +27782,13 @@ var init_manifest = __esm(() => {
27778
27782
  });
27779
27783
 
27780
27784
  // src/cli/doctor-docker.ts
27781
- import { readFileSync as readFileSync42 } from "node:fs";
27785
+ import { readFileSync as readFileSync43 } from "node:fs";
27782
27786
  function isDockerMode(opts) {
27783
27787
  if (process.env.SWITCHROOM_RUNTIME === "docker")
27784
27788
  return true;
27785
27789
  if (opts?.composePath) {
27786
27790
  try {
27787
- readFileSync42(opts.composePath, "utf8");
27791
+ readFileSync43(opts.composePath, "utf8");
27788
27792
  return true;
27789
27793
  } catch {}
27790
27794
  }
@@ -27950,11 +27954,11 @@ var init_doctor_docker = __esm(() => {
27950
27954
  });
27951
27955
 
27952
27956
  // src/cli/doctor-auth-broker.ts
27953
- import { existsSync as existsSync45, readFileSync as readFileSync43 } from "node:fs";
27954
- import { createHash as createHash8 } from "node:crypto";
27957
+ import { existsSync as existsSync47, readFileSync as readFileSync44 } from "node:fs";
27958
+ import { createHash as createHash9 } from "node:crypto";
27955
27959
  import { spawnSync as spawnSync5 } from "node:child_process";
27956
- import { homedir as homedir19 } from "node:os";
27957
- import { join as join37 } from "node:path";
27960
+ import { homedir as homedir20 } from "node:os";
27961
+ import { join as join39 } from "node:path";
27958
27962
  function defaultDockerInspect(container, format) {
27959
27963
  try {
27960
27964
  const r = spawnSync5("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
@@ -27977,7 +27981,7 @@ function resolveStateDir(deps) {
27977
27981
  return deps.stateDir ?? resolveStatePath("state/auth-broker");
27978
27982
  }
27979
27983
  function sha256Hex(content) {
27980
- return createHash8("sha256").update(content).digest("hex");
27984
+ return createHash9("sha256").update(content).digest("hex");
27981
27985
  }
27982
27986
  function checkAuthBrokerServiceHealth(deps = {}) {
27983
27987
  const inspect = deps.dockerInspect ?? defaultDockerInspect;
@@ -28054,8 +28058,8 @@ function checkAuthBrokerPerAgentSockets(config, deps = {}) {
28054
28058
  }
28055
28059
  function checkAuthBrokerDrift(deps = {}) {
28056
28060
  const stateDir = resolveStateDir(deps);
28057
- const indexPath = join37(stateDir, "sha-index.json");
28058
- if (!existsSync45(indexPath)) {
28061
+ const indexPath = join39(stateDir, "sha-index.json");
28062
+ if (!existsSync47(indexPath)) {
28059
28063
  return {
28060
28064
  name: "auth-broker: drift",
28061
28065
  status: "ok",
@@ -28064,7 +28068,7 @@ function checkAuthBrokerDrift(deps = {}) {
28064
28068
  }
28065
28069
  let index;
28066
28070
  try {
28067
- index = JSON.parse(readFileSync43(indexPath, "utf-8"));
28071
+ index = JSON.parse(readFileSync44(indexPath, "utf-8"));
28068
28072
  } catch (err) {
28069
28073
  return {
28070
28074
  name: "auth-broker: drift",
@@ -28073,18 +28077,18 @@ function checkAuthBrokerDrift(deps = {}) {
28073
28077
  fix: "Inspect `~/.switchroom/state/auth-broker/sha-index.json` for corruption."
28074
28078
  };
28075
28079
  }
28076
- const home2 = deps.home ?? homedir19();
28080
+ const home2 = deps.home ?? homedir20();
28077
28081
  const divergent = [];
28078
28082
  const missingOnDisk = [];
28079
28083
  for (const [label, expected] of Object.entries(index)) {
28080
28084
  const credsPath = accountCredentialsPath(label, home2);
28081
- if (!existsSync45(credsPath)) {
28085
+ if (!existsSync47(credsPath)) {
28082
28086
  missingOnDisk.push(label);
28083
28087
  continue;
28084
28088
  }
28085
28089
  let got;
28086
28090
  try {
28087
- got = sha256Hex(readFileSync43(credsPath, "utf-8"));
28091
+ got = sha256Hex(readFileSync44(credsPath, "utf-8"));
28088
28092
  } catch (err) {
28089
28093
  divergent.push(`${label} (read failed: ${err.message})`);
28090
28094
  continue;
@@ -28115,8 +28119,8 @@ function checkAuthBrokerDrift(deps = {}) {
28115
28119
  }
28116
28120
  function checkAuthBrokerThresholdViolations(deps = {}) {
28117
28121
  const stateDir = resolveStateDir(deps);
28118
- const path4 = join37(stateDir, "threshold-violations.json");
28119
- if (!existsSync45(path4)) {
28122
+ const path4 = join39(stateDir, "threshold-violations.json");
28123
+ if (!existsSync47(path4)) {
28120
28124
  return {
28121
28125
  name: "auth-broker: threshold violations",
28122
28126
  status: "ok",
@@ -28125,7 +28129,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
28125
28129
  }
28126
28130
  let violations;
28127
28131
  try {
28128
- violations = JSON.parse(readFileSync43(path4, "utf-8"));
28132
+ violations = JSON.parse(readFileSync44(path4, "utf-8"));
28129
28133
  } catch (err) {
28130
28134
  return {
28131
28135
  name: "auth-broker: threshold violations",
@@ -28159,9 +28163,9 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
28159
28163
  fix: "Run `switchroom auth use <label>` to pin a fleet-wide account, then `switchroom apply`. Without an active account every agent boot fails."
28160
28164
  };
28161
28165
  }
28162
- const home2 = deps.home ?? homedir19();
28166
+ const home2 = deps.home ?? homedir20();
28163
28167
  const dir = accountDir(active, home2);
28164
- if (!existsSync45(dir)) {
28168
+ if (!existsSync47(dir)) {
28165
28169
  return {
28166
28170
  name: "auth-broker: fleet active account",
28167
28171
  status: "fail",
@@ -28170,7 +28174,7 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
28170
28174
  };
28171
28175
  }
28172
28176
  const creds = accountCredentialsPath(active, home2);
28173
- if (!existsSync45(creds)) {
28177
+ if (!existsSync47(creds)) {
28174
28178
  return {
28175
28179
  name: "auth-broker: fleet active account",
28176
28180
  status: "fail",
@@ -28307,17 +28311,17 @@ var init_doctor_hostd = () => {};
28307
28311
  import {
28308
28312
  accessSync,
28309
28313
  constants as fsConstants4,
28310
- existsSync as existsSync46,
28314
+ existsSync as existsSync48,
28311
28315
  realpathSync as realpathSync4,
28312
- statSync as statSync20
28316
+ statSync as statSync21
28313
28317
  } from "node:fs";
28314
- import { userInfo, homedir as homedir20 } from "node:os";
28315
- import { join as join38 } from "node:path";
28318
+ import { userInfo, homedir as homedir21 } from "node:os";
28319
+ import { join as join40 } from "node:path";
28316
28320
  function resolveVaultPath2(config) {
28317
28321
  return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
28318
28322
  }
28319
28323
  function defaultStatVault(path4) {
28320
- if (!existsSync46(path4)) {
28324
+ if (!existsSync48(path4)) {
28321
28325
  return { exists: false, readable: false, uid: -1, mode: 0, realPath: path4 };
28322
28326
  }
28323
28327
  let real = path4;
@@ -28327,7 +28331,7 @@ function defaultStatVault(path4) {
28327
28331
  let uid = -1;
28328
28332
  let mode = 0;
28329
28333
  try {
28330
- const s = statSync20(real);
28334
+ const s = statSync21(real);
28331
28335
  uid = s.uid;
28332
28336
  mode = s.mode & 511;
28333
28337
  } catch {
@@ -28450,7 +28454,7 @@ async function runSecretAccessChecks(config, deps = {}) {
28450
28454
  };
28451
28455
  const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
28452
28456
  if (!passphrase) {
28453
- const sock = deps.brokerOperatorSocket ?? join38(homedir20(), ".switchroom", "broker-operator", "sock");
28457
+ const sock = deps.brokerOperatorSocket ?? join40(homedir21(), ".switchroom", "broker-operator", "sock");
28454
28458
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
28455
28459
  for (const name of Object.keys(config.agents ?? {})) {
28456
28460
  const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
@@ -28535,8 +28539,8 @@ import {
28535
28539
  existsSync as realExistsSync,
28536
28540
  readFileSync as realReadFileSync
28537
28541
  } from "node:fs";
28538
- import { join as join39, resolve as resolve28 } from "node:path";
28539
- import { homedir as homedir21 } from "node:os";
28542
+ import { join as join41, resolve as resolve28 } from "node:path";
28543
+ import { homedir as homedir22 } from "node:os";
28540
28544
  function resolveDeps(config, deps) {
28541
28545
  let agentsDir = deps.agentsDir;
28542
28546
  if (agentsDir === undefined) {
@@ -28659,8 +28663,8 @@ function checkScaffoldWiring(config, driveAgents, d) {
28659
28663
  });
28660
28664
  continue;
28661
28665
  }
28662
- const mcpPath = join39(agentDir, ".mcp.json");
28663
- const claudeJsonPath = join39(agentDir, ".claude", ".claude.json");
28666
+ const mcpPath = join41(agentDir, ".mcp.json");
28667
+ const claudeJsonPath = join41(agentDir, ".claude", ".claude.json");
28664
28668
  const mcpRead = readJson(d, mcpPath);
28665
28669
  const trustRead = readJson(d, claudeJsonPath);
28666
28670
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -28773,7 +28777,7 @@ async function runDriveBrokerReachabilityChecks(config, deps = {}) {
28773
28777
  }
28774
28778
  ];
28775
28779
  }
28776
- const sock = deps.brokerOperatorSocket ?? join39(homedir21(), ".switchroom", "broker-operator", "sock");
28780
+ const sock = deps.brokerOperatorSocket ?? join41(homedir22(), ".switchroom", "broker-operator", "sock");
28777
28781
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
28778
28782
  const results = [];
28779
28783
  for (const agent of driveAgents) {
@@ -28824,12 +28828,12 @@ import {
28824
28828
  readdirSync as realReaddirSync,
28825
28829
  statSync as realStatSync
28826
28830
  } from "node:fs";
28827
- import { homedir as homedir22 } from "node:os";
28828
- import { join as join40 } from "node:path";
28831
+ import { homedir as homedir23 } from "node:os";
28832
+ import { join as join42 } from "node:path";
28829
28833
  function runCredentialsMigrationChecks(config, deps = {}) {
28830
- const credDir = deps.credentialsDir ?? join40(homedir22(), ".switchroom", "credentials");
28831
- const existsSync47 = deps.existsSync ?? ((p) => realExistsSync2(p));
28832
- const readdirSync17 = deps.readdirSync ?? ((p) => realReaddirSync(p));
28834
+ const credDir = deps.credentialsDir ?? join42(homedir23(), ".switchroom", "credentials");
28835
+ const existsSync49 = deps.existsSync ?? ((p) => realExistsSync2(p));
28836
+ const readdirSync18 = deps.readdirSync ?? ((p) => realReaddirSync(p));
28833
28837
  const isDirectory = deps.isDirectory ?? ((p) => {
28834
28838
  try {
28835
28839
  return realStatSync(p).isDirectory();
@@ -28837,12 +28841,12 @@ function runCredentialsMigrationChecks(config, deps = {}) {
28837
28841
  return false;
28838
28842
  }
28839
28843
  });
28840
- if (!existsSync47(credDir))
28844
+ if (!existsSync49(credDir))
28841
28845
  return [];
28842
28846
  const agentNames = new Set(Object.keys(config.agents ?? {}));
28843
28847
  let entries;
28844
28848
  try {
28845
- entries = readdirSync17(credDir);
28849
+ entries = readdirSync18(credDir);
28846
28850
  } catch {
28847
28851
  return [
28848
28852
  {
@@ -28855,7 +28859,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
28855
28859
  const flat = [];
28856
28860
  const perAgentDirs = [];
28857
28861
  for (const e of entries) {
28858
- const full = join40(credDir, e);
28862
+ const full = join42(credDir, e);
28859
28863
  if (isDirectory(full) && agentNames.has(e)) {
28860
28864
  perAgentDirs.push(e);
28861
28865
  } else {
@@ -28978,19 +28982,19 @@ var init_doctor_inlined_secrets = __esm(() => {
28978
28982
 
28979
28983
  // src/cli/doctor-audit-integrity.ts
28980
28984
  import { readFileSync as fsReadFileSync2 } from "node:fs";
28981
- import { homedir as homedir23 } from "node:os";
28982
- import { join as join41 } from "node:path";
28985
+ import { homedir as homedir24 } from "node:os";
28986
+ import { join as join43 } from "node:path";
28983
28987
  function rootWrittenLogs(home2) {
28984
28988
  return [
28985
- { label: "vault-broker", path: join41(home2, ".switchroom", "vault-audit.log") },
28989
+ { label: "vault-broker", path: join43(home2, ".switchroom", "vault-audit.log") },
28986
28990
  {
28987
28991
  label: "hostd",
28988
- path: join41(home2, ".switchroom", "host-control-audit.log")
28992
+ path: join43(home2, ".switchroom", "host-control-audit.log")
28989
28993
  }
28990
28994
  ];
28991
28995
  }
28992
28996
  function runAuditIntegrityChecks(deps = {}) {
28993
- const home2 = deps.homeDir ?? homedir23();
28997
+ const home2 = deps.homeDir ?? homedir24();
28994
28998
  const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
28995
28999
  const results = [];
28996
29000
  for (const { label, path: path4 } of rootWrittenLogs(home2)) {
@@ -29266,16 +29270,16 @@ var init_client4 = __esm(() => {
29266
29270
  });
29267
29271
 
29268
29272
  // src/cli/doctor-agent-smoke.ts
29269
- import { existsSync as existsSync47 } from "node:fs";
29270
- import { homedir as homedir24 } from "node:os";
29271
- import { join as join42 } from "node:path";
29273
+ import { existsSync as existsSync49 } from "node:fs";
29274
+ import { homedir as homedir25 } from "node:os";
29275
+ import { join as join44 } from "node:path";
29272
29276
  import { randomUUID as randomUUID4 } from "node:crypto";
29273
29277
  async function runAgentSmokeChecks(config, deps = {}) {
29274
29278
  if (deps.fast)
29275
29279
  return [];
29276
- const home2 = deps.homeDir ?? homedir24();
29277
- const sock = deps.operatorSockPath ?? join42(home2, ".switchroom", "hostd", "operator", "sock");
29278
- if (!deps.hostdRequestImpl && !existsSync47(sock)) {
29280
+ const home2 = deps.homeDir ?? homedir25();
29281
+ const sock = deps.operatorSockPath ?? join44(home2, ".switchroom", "hostd", "operator", "sock");
29282
+ if (!deps.hostdRequestImpl && !existsSync49(sock)) {
29279
29283
  return [
29280
29284
  {
29281
29285
  name: "agent liveness",
@@ -29394,25 +29398,25 @@ import { execSync as execSync3, spawnSync as spawnSync7 } from "node:child_proce
29394
29398
  import {
29395
29399
  accessSync as accessSync2,
29396
29400
  constants as fsConstants5,
29397
- existsSync as existsSync48,
29401
+ existsSync as existsSync50,
29398
29402
  lstatSync as lstatSync5,
29399
- mkdirSync as mkdirSync26,
29400
- readFileSync as readFileSync44,
29401
- readdirSync as readdirSync17,
29402
- statSync as statSync21
29403
+ mkdirSync as mkdirSync27,
29404
+ readFileSync as readFileSync45,
29405
+ readdirSync as readdirSync18,
29406
+ statSync as statSync22
29403
29407
  } from "node:fs";
29404
- import { dirname as dirname12, join as join43, resolve as resolve29 } from "node:path";
29408
+ import { dirname as dirname12, join as join45, resolve as resolve29 } from "node:path";
29405
29409
  import { createPublicKey, createPrivateKey } from "node:crypto";
29406
29410
  function findInNvm(bin) {
29407
- const nvmRoot = join43(process.env.HOME ?? "", ".nvm", "versions", "node");
29408
- if (!existsSync48(nvmRoot))
29411
+ const nvmRoot = join45(process.env.HOME ?? "", ".nvm", "versions", "node");
29412
+ if (!existsSync50(nvmRoot))
29409
29413
  return null;
29410
29414
  try {
29411
- const versions = readdirSync17(nvmRoot).sort().reverse();
29415
+ const versions = readdirSync18(nvmRoot).sort().reverse();
29412
29416
  for (const v of versions) {
29413
- const candidate = join43(nvmRoot, v, "bin", bin);
29417
+ const candidate = join45(nvmRoot, v, "bin", bin);
29414
29418
  try {
29415
- const s = statSync21(candidate);
29419
+ const s = statSync22(candidate);
29416
29420
  if (s.isFile() || s.isSymbolicLink()) {
29417
29421
  return candidate;
29418
29422
  }
@@ -29575,21 +29579,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
29575
29579
  if (envBrowsersPath && envBrowsersPath.length > 0) {
29576
29580
  cacheLocations.push(envBrowsersPath);
29577
29581
  }
29578
- cacheLocations.push(join43(homeDir, ".cache", "ms-playwright"));
29582
+ cacheLocations.push(join45(homeDir, ".cache", "ms-playwright"));
29579
29583
  for (const cacheDir of cacheLocations) {
29580
- if (!existsSync48(cacheDir))
29584
+ if (!existsSync50(cacheDir))
29581
29585
  continue;
29582
29586
  try {
29583
- const entries = readdirSync17(cacheDir).filter((e) => e.startsWith("chromium"));
29587
+ const entries = readdirSync18(cacheDir).filter((e) => e.startsWith("chromium"));
29584
29588
  for (const entry of entries) {
29585
29589
  const candidates2 = [
29586
- join43(cacheDir, entry, "chrome-linux64", "chrome"),
29587
- join43(cacheDir, entry, "chrome-linux", "chrome"),
29588
- join43(cacheDir, entry, "chrome-linux64", "headless_shell"),
29589
- join43(cacheDir, entry, "chrome-linux", "headless_shell")
29590
+ join45(cacheDir, entry, "chrome-linux64", "chrome"),
29591
+ join45(cacheDir, entry, "chrome-linux", "chrome"),
29592
+ join45(cacheDir, entry, "chrome-linux64", "headless_shell"),
29593
+ join45(cacheDir, entry, "chrome-linux", "headless_shell")
29590
29594
  ];
29591
29595
  for (const path4 of candidates2) {
29592
- if (existsSync48(path4))
29596
+ if (existsSync50(path4))
29593
29597
  return path4;
29594
29598
  }
29595
29599
  }
@@ -29611,7 +29615,7 @@ function checkChromium() {
29611
29615
  }
29612
29616
  function checkDepsCacheWritable(depsRoot = resolvePath("~/.switchroom/deps")) {
29613
29617
  try {
29614
- mkdirSync26(depsRoot, { recursive: true });
29618
+ mkdirSync27(depsRoot, { recursive: true });
29615
29619
  accessSync2(depsRoot, fsConstants5.W_OK);
29616
29620
  return {
29617
29621
  name: "~/.switchroom/deps writable",
@@ -29669,8 +29673,8 @@ function checkConfig(config, configPath) {
29669
29673
  function checkLegacyState() {
29670
29674
  const results = [];
29671
29675
  const h = process.env.HOME ?? "/root";
29672
- const clerkDir = join43(h, LEGACY_STATE_DIR);
29673
- const clerkPresent = existsSync48(clerkDir);
29676
+ const clerkDir = join45(h, LEGACY_STATE_DIR);
29677
+ const clerkPresent = existsSync50(clerkDir);
29674
29678
  results.push({
29675
29679
  name: "legacy ~/.clerk state",
29676
29680
  status: clerkPresent ? "warn" : "ok",
@@ -29679,7 +29683,7 @@ function checkLegacyState() {
29679
29683
  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."
29680
29684
  } : {}
29681
29685
  });
29682
- const legacySock = join43(h, ".switchroom", "vault-broker.sock");
29686
+ const legacySock = join45(h, ".switchroom", "vault-broker.sock");
29683
29687
  let sockStat = null;
29684
29688
  try {
29685
29689
  sockStat = lstatSync5(legacySock);
@@ -29712,7 +29716,7 @@ function checkVault(config) {
29712
29716
  status: "ok",
29713
29717
  detail: "Approval auth: passphrase (two-factor)"
29714
29718
  };
29715
- if (!existsSync48(vaultPath)) {
29719
+ if (!existsSync50(vaultPath)) {
29716
29720
  return [
29717
29721
  postureResult,
29718
29722
  {
@@ -29883,8 +29887,8 @@ async function checkHindsight(config) {
29883
29887
  }
29884
29888
  function checkPendingRetainsQueue(dir) {
29885
29889
  const home2 = process.env.HOME ?? "";
29886
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join43(home2, ".hindsight", "pending-retains");
29887
- if (!existsSync48(pendingDir)) {
29890
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join45(home2, ".hindsight", "pending-retains");
29891
+ if (!existsSync50(pendingDir)) {
29888
29892
  return {
29889
29893
  name: "pending-retains queue",
29890
29894
  status: "ok",
@@ -29893,7 +29897,7 @@ function checkPendingRetainsQueue(dir) {
29893
29897
  }
29894
29898
  let names;
29895
29899
  try {
29896
- names = readdirSync17(pendingDir);
29900
+ names = readdirSync18(pendingDir);
29897
29901
  } catch (err) {
29898
29902
  return {
29899
29903
  name: "pending-retains queue",
@@ -29943,7 +29947,7 @@ function classifyReadError(err) {
29943
29947
  }
29944
29948
  function tryReadHostFile(path4) {
29945
29949
  try {
29946
- return { kind: "ok", content: readFileSync44(path4, "utf-8") };
29950
+ return { kind: "ok", content: readFileSync45(path4, "utf-8") };
29947
29951
  } catch (err) {
29948
29952
  const kind = classifyReadError(err);
29949
29953
  const error = err?.message ?? String(err);
@@ -29955,11 +29959,11 @@ function tryReadHostFile(path4) {
29955
29959
  }
29956
29960
  }
29957
29961
  function parseEnvFile(path4) {
29958
- if (!existsSync48(path4))
29962
+ if (!existsSync50(path4))
29959
29963
  return {};
29960
29964
  let content;
29961
29965
  try {
29962
- content = readFileSync44(path4, "utf-8");
29966
+ content = readFileSync45(path4, "utf-8");
29963
29967
  } catch {
29964
29968
  return {};
29965
29969
  }
@@ -30014,7 +30018,7 @@ async function checkTelegram(config) {
30014
30018
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
30015
30019
  if (plugin !== "switchroom")
30016
30020
  continue;
30017
- const envPath = join43(agentsDir, name, "telegram", ".env");
30021
+ const envPath = join45(agentsDir, name, "telegram", ".env");
30018
30022
  const read = tryReadHostFile(envPath);
30019
30023
  if (read.kind === "eacces") {
30020
30024
  results.push({
@@ -30066,7 +30070,7 @@ async function checkTelegram(config) {
30066
30070
  }
30067
30071
  function checkStartShStale(agentName, startShPath) {
30068
30072
  const label = `${agentName}: start.sh scheduler block`;
30069
- if (!existsSync48(startShPath)) {
30073
+ if (!existsSync50(startShPath)) {
30070
30074
  return {
30071
30075
  name: label,
30072
30076
  status: "warn",
@@ -30076,7 +30080,7 @@ function checkStartShStale(agentName, startShPath) {
30076
30080
  }
30077
30081
  let content;
30078
30082
  try {
30079
- content = readFileSync44(startShPath, "utf-8");
30083
+ content = readFileSync45(startShPath, "utf-8");
30080
30084
  } catch (err) {
30081
30085
  return {
30082
30086
  name: label,
@@ -30097,7 +30101,7 @@ function checkStartShStale(agentName, startShPath) {
30097
30101
  }
30098
30102
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
30099
30103
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
30100
- const path4 = join43(agentDir, "home", ".switchroom");
30104
+ const path4 = join45(agentDir, "home", ".switchroom");
30101
30105
  let stats;
30102
30106
  try {
30103
30107
  stats = lstatSync5(path4);
@@ -30134,8 +30138,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
30134
30138
  }
30135
30139
  function checkRepoHygiene(repoRoot) {
30136
30140
  const results = [];
30137
- const exportDir = join43(repoRoot, "clerk-export");
30138
- if (existsSync48(exportDir)) {
30141
+ const exportDir = join45(repoRoot, "clerk-export");
30142
+ if (existsSync50(exportDir)) {
30139
30143
  results.push({
30140
30144
  name: "repo hygiene: clerk-export/ on disk (#1072)",
30141
30145
  status: "warn",
@@ -30143,8 +30147,8 @@ function checkRepoHygiene(repoRoot) {
30143
30147
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
30144
30148
  });
30145
30149
  }
30146
- const knownTarball = join43(repoRoot, "clerk-export-with-secrets.tar.gz");
30147
- if (existsSync48(knownTarball)) {
30150
+ const knownTarball = join45(repoRoot, "clerk-export-with-secrets.tar.gz");
30151
+ if (existsSync50(knownTarball)) {
30148
30152
  results.push({
30149
30153
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
30150
30154
  status: "warn",
@@ -30153,7 +30157,7 @@ function checkRepoHygiene(repoRoot) {
30153
30157
  });
30154
30158
  }
30155
30159
  try {
30156
- const entries = readdirSync17(repoRoot);
30160
+ const entries = readdirSync18(repoRoot);
30157
30161
  for (const name of entries) {
30158
30162
  if (name === "clerk-export-with-secrets.tar.gz")
30159
30163
  continue;
@@ -30161,7 +30165,7 @@ function checkRepoHygiene(repoRoot) {
30161
30165
  results.push({
30162
30166
  name: `repo hygiene: ${name} on disk (#1072)`,
30163
30167
  status: "warn",
30164
- detail: `${join43(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
30168
+ detail: `${join45(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
30165
30169
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
30166
30170
  });
30167
30171
  }
@@ -30184,12 +30188,12 @@ function checkRepoHygiene(repoRoot) {
30184
30188
  }
30185
30189
  function isSwitchroomCheckout(dir) {
30186
30190
  try {
30187
- if (!existsSync48(join43(dir, ".git")))
30191
+ if (!existsSync50(join45(dir, ".git")))
30188
30192
  return false;
30189
- const pkgPath = join43(dir, "package.json");
30190
- if (!existsSync48(pkgPath))
30193
+ const pkgPath = join45(dir, "package.json");
30194
+ if (!existsSync50(pkgPath))
30191
30195
  return false;
30192
- const pkg = JSON.parse(readFileSync44(pkgPath, "utf-8"));
30196
+ const pkg = JSON.parse(readFileSync45(pkgPath, "utf-8"));
30193
30197
  return pkg.name === "switchroom";
30194
30198
  } catch {
30195
30199
  return false;
@@ -30202,7 +30206,7 @@ function checkAgents(config, configPath) {
30202
30206
  const authStatuses = getAllAuthStatuses(config);
30203
30207
  for (const [name, agentConfig] of Object.entries(config.agents)) {
30204
30208
  const agentDir = resolve29(agentsDir, name);
30205
- if (!existsSync48(agentDir)) {
30209
+ if (!existsSync50(agentDir)) {
30206
30210
  results.push({
30207
30211
  name: `${name}: scaffold`,
30208
30212
  status: "fail",
@@ -30223,7 +30227,7 @@ function checkAgents(config, configPath) {
30223
30227
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
30224
30228
  });
30225
30229
  }
30226
- results.push(checkStartShStale(name, join43(agentDir, "start.sh")));
30230
+ results.push(checkStartShStale(name, join45(agentDir, "start.sh")));
30227
30231
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
30228
30232
  const status = statuses[name];
30229
30233
  const active = status?.active ?? "unknown";
@@ -30300,8 +30304,8 @@ function checkAgents(config, configPath) {
30300
30304
  }
30301
30305
  }
30302
30306
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
30303
- const mcpJsonPath = join43(agentDir, ".mcp.json");
30304
- if (!existsSync48(mcpJsonPath)) {
30307
+ const mcpJsonPath = join45(agentDir, ".mcp.json");
30308
+ if (!existsSync50(mcpJsonPath)) {
30305
30309
  results.push({
30306
30310
  name: `${name}: .mcp.json`,
30307
30311
  status: "fail",
@@ -30310,7 +30314,7 @@ function checkAgents(config, configPath) {
30310
30314
  });
30311
30315
  } else {
30312
30316
  try {
30313
- const mcp = JSON.parse(readFileSync44(mcpJsonPath, "utf-8"));
30317
+ const mcp = JSON.parse(readFileSync45(mcpJsonPath, "utf-8"));
30314
30318
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
30315
30319
  const memoryEnabled = isHindsightEnabled(config);
30316
30320
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -30386,7 +30390,7 @@ function mffEnvPath(config) {
30386
30390
  return agent ? resolve29(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve29(home2, ".switchroom/credentials/my-family-finance/.env");
30387
30391
  }
30388
30392
  function mffEnvState(envPath) {
30389
- if (!existsSync48(envPath))
30393
+ if (!existsSync50(envPath))
30390
30394
  return "absent";
30391
30395
  try {
30392
30396
  accessSync2(envPath, fsConstants5.R_OK);
@@ -30404,7 +30408,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
30404
30408
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
30405
30409
  };
30406
30410
  }
30407
- if (!existsSync48(vaultPath)) {
30411
+ if (!existsSync50(vaultPath)) {
30408
30412
  return {
30409
30413
  name: "mff: vault key present",
30410
30414
  status: "fail",
@@ -30457,7 +30461,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
30457
30461
  }
30458
30462
  }
30459
30463
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
30460
- if (!passphrase || !existsSync48(vaultPath)) {
30464
+ if (!passphrase || !existsSync50(vaultPath)) {
30461
30465
  return {
30462
30466
  name: "mff: vault key format",
30463
30467
  status: "warn",
@@ -30601,8 +30605,8 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
30601
30605
  };
30602
30606
  }
30603
30607
  const credDir = dirname12(envPath);
30604
- const authScript = join43(credDir, "claude-auth.py");
30605
- if (!existsSync48(authScript)) {
30608
+ const authScript = join45(credDir, "claude-auth.py");
30609
+ if (!existsSync50(authScript)) {
30606
30610
  return {
30607
30611
  name: "mff: auth flow",
30608
30612
  status: "warn",
@@ -30804,11 +30808,11 @@ function runDockerSection(config) {
30804
30808
  let composeYaml;
30805
30809
  let dockerfileAgent;
30806
30810
  try {
30807
- composeYaml = readFileSync44(composePath, "utf8");
30811
+ composeYaml = readFileSync45(composePath, "utf8");
30808
30812
  } catch {}
30809
30813
  const dockerfilePath = resolve29(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
30810
30814
  try {
30811
- dockerfileAgent = readFileSync44(dockerfilePath, "utf8");
30815
+ dockerfileAgent = readFileSync45(dockerfilePath, "utf8");
30812
30816
  } catch {}
30813
30817
  return runDockerChecks({
30814
30818
  config,
@@ -46890,13 +46894,13 @@ __export(exports_server2, {
46890
46894
  dispatchTool: () => dispatchTool2,
46891
46895
  TOOLS: () => TOOLS2
46892
46896
  });
46893
- import { randomBytes as randomBytes13 } from "node:crypto";
46894
- import { existsSync as existsSync72, readFileSync as readFileSync59 } from "node:fs";
46897
+ import { randomBytes as randomBytes14 } from "node:crypto";
46898
+ import { existsSync as existsSync74, readFileSync as readFileSync60 } from "node:fs";
46895
46899
  function selfSocketPath() {
46896
46900
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
46897
46901
  }
46898
46902
  function makeRequestId(prefix) {
46899
- return `${prefix}-${Date.now()}-${randomBytes13(4).toString("hex")}`;
46903
+ return `${prefix}-${Date.now()}-${randomBytes14(4).toString("hex")}`;
46900
46904
  }
46901
46905
  async function dispatchTool2(name, args) {
46902
46906
  if (name === "get_status") {
@@ -46906,7 +46910,7 @@ async function dispatchTool2(name, args) {
46906
46910
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
46907
46911
  }
46908
46912
  const sockPath = selfSocketPath();
46909
- if (!existsSync72(sockPath)) {
46913
+ if (!existsSync74(sockPath)) {
46910
46914
  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.`);
46911
46915
  }
46912
46916
  let req;
@@ -47025,18 +47029,18 @@ function resolveAuditLogPath() {
47025
47029
  if (process.env.HOSTD_AUDIT_LOG_PATH)
47026
47030
  return process.env.HOSTD_AUDIT_LOG_PATH;
47027
47031
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
47028
- if (existsSync72(bindMounted))
47032
+ if (existsSync74(bindMounted))
47029
47033
  return bindMounted;
47030
47034
  return defaultAuditLogPath2();
47031
47035
  }
47032
47036
  function getLastUpdateApplyStatus() {
47033
47037
  const path8 = resolveAuditLogPath();
47034
- if (!existsSync72(path8)) {
47038
+ if (!existsSync74(path8)) {
47035
47039
  return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
47036
47040
  }
47037
47041
  let raw;
47038
47042
  try {
47039
- raw = readFileSync59(path8, "utf-8");
47043
+ raw = readFileSync60(path8, "utf-8");
47040
47044
  } catch (err2) {
47041
47045
  return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
47042
47046
  }
@@ -47243,8 +47247,8 @@ var {
47243
47247
  } = import__.default;
47244
47248
 
47245
47249
  // src/build-info.ts
47246
- var VERSION = "0.12.19";
47247
- var COMMIT_SHA = "5f6810ed";
47250
+ var VERSION = "0.12.21";
47251
+ var COMMIT_SHA = "e32c064";
47248
47252
 
47249
47253
  // src/cli/agent.ts
47250
47254
  init_source();
@@ -55349,7 +55353,7 @@ init_loader();
55349
55353
  init_loader();
55350
55354
  init_vault();
55351
55355
  import { createInterface as createInterface3 } from "node:readline";
55352
- import { readFileSync as readFileSync33 } from "node:fs";
55356
+ import { readFileSync as readFileSync34 } from "node:fs";
55353
55357
 
55354
55358
  // src/cli/vault-sweep.ts
55355
55359
  init_source();
@@ -58598,7 +58602,7 @@ class VaultBroker {
58598
58602
  chownSync(abs, operatorUid, operatorUid);
58599
58603
  } catch {}
58600
58604
  const unlockServer = net3.createServer((sock) => {
58601
- this._handleUnlockConnection(sock);
58605
+ this._handleUnlockConnection(sock, true);
58602
58606
  });
58603
58607
  unlockServer.on("error", (err) => rejectP(err));
58604
58608
  unlockServer.listen(unlockAbs, () => {
@@ -59598,9 +59602,9 @@ class VaultBroker {
59598
59602
  socket.write(encodeResponse(errorResponse("INTERNAL", msg)));
59599
59603
  }
59600
59604
  }
59601
- _handleUnlockConnection(socket) {
59605
+ _handleUnlockConnection(socket, isOperator = false) {
59602
59606
  let unlockPeer = null;
59603
- if (process.platform === "linux") {
59607
+ if (!isOperator && process.platform === "linux") {
59604
59608
  unlockPeer = this.testOpts._testIdentify ? this.testOpts._testIdentify(this.unlockSocketPath, socket) : identify(this.unlockSocketPath, socket);
59605
59609
  if (unlockPeer === null) {
59606
59610
  this.auditLogger.write({
@@ -59615,9 +59619,9 @@ class VaultBroker {
59615
59619
  return;
59616
59620
  }
59617
59621
  }
59618
- const auditPid = unlockPeer?.pid ?? process.pid;
59619
- const auditCaller = unlockPeer !== null ? callerFromPeer(unlockPeer) : `pid:${process.pid}`;
59620
- const auditCgroup = unlockPeer?.systemdUnit ?? undefined;
59622
+ const auditPid = isOperator ? process.pid : unlockPeer?.pid ?? process.pid;
59623
+ const auditCaller = isOperator ? "operator" : unlockPeer !== null ? callerFromPeer(unlockPeer) : `pid:${process.pid}`;
59624
+ const auditCgroup = isOperator ? undefined : unlockPeer?.systemdUnit ?? undefined;
59621
59625
  let buffer = "";
59622
59626
  socket.on("data", (chunk) => {
59623
59627
  buffer += chunk.toString("utf8");
@@ -60758,6 +60762,289 @@ The token file was written to the agent directory (mode 0600).`));
60758
60762
  });
60759
60763
  }
60760
60764
 
60765
+ // src/cli/vault-backup.ts
60766
+ init_source();
60767
+ init_loader();
60768
+ import { existsSync as existsSync38 } from "node:fs";
60769
+ import { join as join32 } from "node:path";
60770
+
60771
+ // src/vault/backup.ts
60772
+ import {
60773
+ createHash as createHash7,
60774
+ randomBytes as randomBytes9
60775
+ } from "node:crypto";
60776
+ import {
60777
+ appendFileSync as appendFileSync2,
60778
+ closeSync as closeSync9,
60779
+ existsSync as existsSync37,
60780
+ fsyncSync as fsyncSync5,
60781
+ mkdirSync as mkdirSync21,
60782
+ openSync as openSync9,
60783
+ readdirSync as readdirSync15,
60784
+ readFileSync as readFileSync33,
60785
+ renameSync as renameSync10,
60786
+ statSync as statSync20,
60787
+ symlinkSync as symlinkSync4,
60788
+ unlinkSync as unlinkSync10,
60789
+ writeSync as writeSync5
60790
+ } from "node:fs";
60791
+ import { homedir as homedir16 } from "node:os";
60792
+ import { join as join31, resolve as resolvePath2 } from "node:path";
60793
+ var LATEST_SYMLINK = "vault.enc.latest.bak";
60794
+ var MANIFEST_FILE = "manifest.jsonl";
60795
+ var DEFAULT_RETAIN = 30;
60796
+ var FILENAME_PREFIX = "vault.enc.";
60797
+ var FILENAME_SUFFIX = ".bak";
60798
+ var AUTO_UNLOCK_FILENAME_PATTERNS = [
60799
+ /auto-unlock/i,
60800
+ /^\.?vault-auto/i
60801
+ ];
60802
+ var KNOWN_BACKUP_DIR_ARTIFACTS = new Set([
60803
+ LATEST_SYMLINK,
60804
+ MANIFEST_FILE,
60805
+ ".git",
60806
+ ".gitignore",
60807
+ "README.md"
60808
+ ]);
60809
+ function computeBackupFilename(now) {
60810
+ const Y = now.getUTCFullYear().toString().padStart(4, "0");
60811
+ const M = (now.getUTCMonth() + 1).toString().padStart(2, "0");
60812
+ const D = now.getUTCDate().toString().padStart(2, "0");
60813
+ const h = now.getUTCHours().toString().padStart(2, "0");
60814
+ const m = now.getUTCMinutes().toString().padStart(2, "0");
60815
+ const s = now.getUTCSeconds().toString().padStart(2, "0");
60816
+ return `${FILENAME_PREFIX}${Y}${M}${D}-${h}${m}${s}${FILENAME_SUFFIX}`;
60817
+ }
60818
+ function parseBackupFilename(name) {
60819
+ if (!name.startsWith(FILENAME_PREFIX))
60820
+ return null;
60821
+ if (!name.endsWith(FILENAME_SUFFIX))
60822
+ return null;
60823
+ const inner = name.slice(FILENAME_PREFIX.length, -FILENAME_SUFFIX.length);
60824
+ if (!/^\d{8}-\d{6}$/.test(inner))
60825
+ return null;
60826
+ return inner;
60827
+ }
60828
+ function validateVaultEnvelopeFile(path4) {
60829
+ let buf;
60830
+ try {
60831
+ buf = readFileSync33(path4);
60832
+ } catch (err) {
60833
+ return `cannot read ${path4}: ${err.message}`;
60834
+ }
60835
+ if (buf.length === 0) {
60836
+ return `${path4} is empty`;
60837
+ }
60838
+ let parsed;
60839
+ try {
60840
+ parsed = JSON.parse(buf.toString("utf8"));
60841
+ } catch {
60842
+ return `${path4} is not valid JSON (vault envelope is JSON)`;
60843
+ }
60844
+ if (!parsed || typeof parsed !== "object") {
60845
+ return `${path4} is not a JSON object`;
60846
+ }
60847
+ const obj = parsed;
60848
+ for (const required of ["salt", "iv", "data", "tag"]) {
60849
+ if (typeof obj[required] !== "string" || obj[required].length === 0) {
60850
+ return `${path4} is missing or empty required field '${required}'`;
60851
+ }
60852
+ }
60853
+ if (obj.kdf !== undefined) {
60854
+ const k = obj.kdf;
60855
+ if (typeof k.N !== "number" || typeof k.r !== "number" || typeof k.p !== "number") {
60856
+ return `${path4} has malformed kdf field`;
60857
+ }
60858
+ }
60859
+ return null;
60860
+ }
60861
+ function resolveBackupDestination(input) {
60862
+ if (input.cliToFlag)
60863
+ return resolvePath2(input.cliToFlag);
60864
+ if (input.configDestination)
60865
+ return resolvePath2(input.configDestination);
60866
+ if (input.hasSwitchroomConfigRepo) {
60867
+ return join31(input.home, ".switchroom-config", "vault-backups");
60868
+ }
60869
+ return join31(input.home, ".switchroom", "vault-backups");
60870
+ }
60871
+ function findAutoUnlockSibling(entries) {
60872
+ for (const name of entries) {
60873
+ if (KNOWN_BACKUP_DIR_ARTIFACTS.has(name))
60874
+ continue;
60875
+ if (AUTO_UNLOCK_FILENAME_PATTERNS.some((re) => re.test(name))) {
60876
+ return name;
60877
+ }
60878
+ }
60879
+ return null;
60880
+ }
60881
+ function selectBackupsToPrune(sortedNewestFirst, retain) {
60882
+ if (retain < 0)
60883
+ throw new Error("retain must be >= 0");
60884
+ return sortedNewestFirst.slice(retain);
60885
+ }
60886
+ function listBackupFiles(dir) {
60887
+ if (!existsSync37(dir))
60888
+ return [];
60889
+ let entries;
60890
+ try {
60891
+ entries = readdirSync15(dir);
60892
+ } catch {
60893
+ return [];
60894
+ }
60895
+ return entries.filter((n) => parseBackupFilename(n) !== null).sort((a, b) => {
60896
+ const ka = parseBackupFilename(a);
60897
+ const kb = parseBackupFilename(b);
60898
+ return kb.localeCompare(ka);
60899
+ });
60900
+ }
60901
+ function sha256OfFile(path4) {
60902
+ const h = createHash7("sha256");
60903
+ h.update(readFileSync33(path4));
60904
+ return h.digest("hex");
60905
+ }
60906
+ function backupVault(opts) {
60907
+ const now = opts.now ?? new Date;
60908
+ const retain = opts.retain ?? DEFAULT_RETAIN;
60909
+ const validationError = validateVaultEnvelopeFile(opts.vaultPath);
60910
+ if (validationError) {
60911
+ throw new Error(`vault backup refused: source is not a valid vault envelope: ${validationError}`);
60912
+ }
60913
+ mkdirSync21(opts.destDir, { recursive: true, mode: 448 });
60914
+ const dirEntries = readdirSync15(opts.destDir);
60915
+ const offender = findAutoUnlockSibling(dirEntries);
60916
+ if (offender) {
60917
+ throw new Error(`vault backup refused: destination '${opts.destDir}' contains a file ` + `that looks like an auto-unlock credential ('${offender}'). The ` + `machine-bound auto-unlock blob MUST NOT be co-located with the ` + `encrypted vault \u2014 if they're together in version control, the ` + `passphrase gate is bypassed. Move/remove that file and retry.`);
60918
+ }
60919
+ const filename = computeBackupFilename(now);
60920
+ const fullPath = join31(opts.destDir, filename);
60921
+ const tmpName = `.tmp.${process.pid}.${randomBytes9(4).toString("hex")}`;
60922
+ const tmpPath = join31(opts.destDir, tmpName);
60923
+ const src = readFileSync33(opts.vaultPath);
60924
+ const fd = openSync9(tmpPath, "wx", 384);
60925
+ try {
60926
+ writeSync5(fd, src);
60927
+ fsyncSync5(fd);
60928
+ } finally {
60929
+ closeSync9(fd);
60930
+ }
60931
+ if (existsSync37(fullPath)) {
60932
+ try {
60933
+ unlinkSync10(tmpPath);
60934
+ } catch {}
60935
+ throw new Error(`vault backup refused: '${fullPath}' already exists ` + `(sub-second collision with another backup). Retry in 1 second, ` + `or check for a concurrent backup process.`);
60936
+ }
60937
+ renameSync10(tmpPath, fullPath);
60938
+ const stat = statSync20(fullPath);
60939
+ const sha256 = sha256OfFile(fullPath);
60940
+ const row = {
60941
+ ts: now.toISOString(),
60942
+ file: filename,
60943
+ size_bytes: stat.size,
60944
+ sha256
60945
+ };
60946
+ appendFileSync2(join31(opts.destDir, MANIFEST_FILE), JSON.stringify(row) + `
60947
+ `, { mode: 384 });
60948
+ const latestPath = join31(opts.destDir, LATEST_SYMLINK);
60949
+ try {
60950
+ unlinkSync10(latestPath);
60951
+ } catch {}
60952
+ try {
60953
+ symlinkSync4(filename, latestPath);
60954
+ } catch {}
60955
+ try {
60956
+ const dirFd = openSync9(opts.destDir, "r");
60957
+ try {
60958
+ fsyncSync5(dirFd);
60959
+ } finally {
60960
+ closeSync9(dirFd);
60961
+ }
60962
+ } catch {}
60963
+ const sorted = listBackupFiles(opts.destDir);
60964
+ const pruneNames = selectBackupsToPrune(sorted, retain);
60965
+ for (const old of pruneNames) {
60966
+ try {
60967
+ unlinkSync10(join31(opts.destDir, old));
60968
+ } catch {}
60969
+ }
60970
+ if (pruneNames.length > 0) {
60971
+ try {
60972
+ const dirFd = openSync9(opts.destDir, "r");
60973
+ try {
60974
+ fsyncSync5(dirFd);
60975
+ } finally {
60976
+ closeSync9(dirFd);
60977
+ }
60978
+ } catch {}
60979
+ }
60980
+ return {
60981
+ destDir: opts.destDir,
60982
+ filename,
60983
+ fullPath,
60984
+ bytes: stat.size,
60985
+ sha256,
60986
+ pruned: pruneNames
60987
+ };
60988
+ }
60989
+ function defaultHome() {
60990
+ return process.env.HOME ?? homedir16();
60991
+ }
60992
+
60993
+ // src/cli/vault-backup.ts
60994
+ function getLiveVaultPath(configPath) {
60995
+ try {
60996
+ const cfg = loadConfig(configPath);
60997
+ if (cfg.vault?.path)
60998
+ return resolvePath(cfg.vault.path);
60999
+ } catch {}
61000
+ return resolvePath("~/.switchroom/vault/vault.enc");
61001
+ }
61002
+ function registerVaultBackupCommand(vault, program3) {
61003
+ vault.command("backup").description("Write a dated, encrypted backup of the vault to a configured directory. " + "Default destination: ~/.switchroom-config/vault-backups/ if that operator " + "config repo exists, else ~/.switchroom/vault-backups/. Rotated to the last " + "30 backups by default.").option("--to <path>", "Destination directory for the backup. Overrides config and default. " + "MUST NOT be ~/.switchroom/vault/ (the broker's bind-mounted dir, " + "validated by `switchroom apply` against an allowlist). Created with mode 0700.").option("--retain <n>", `Keep only the N most recent backups in the destination dir (default ${DEFAULT_RETAIN}). ` + `Older ones are pruned after the new backup is written.`).action(async (opts) => {
61004
+ try {
61005
+ const parentOpts = program3.opts();
61006
+ const vaultPath = getLiveVaultPath(parentOpts.config);
61007
+ let configDestination;
61008
+ try {
61009
+ const cfg = loadConfig(parentOpts.config);
61010
+ const backupCfg = cfg.vault?.backup;
61011
+ if (backupCfg?.destination) {
61012
+ configDestination = resolvePath(backupCfg.destination);
61013
+ }
61014
+ } catch {}
61015
+ const home2 = defaultHome();
61016
+ const hasSwitchroomConfigRepo = existsSync38(join32(home2, ".switchroom-config"));
61017
+ const destDir = resolveBackupDestination({
61018
+ cliToFlag: opts.to ? resolvePath(opts.to) : undefined,
61019
+ configDestination,
61020
+ home: home2,
61021
+ hasSwitchroomConfigRepo
61022
+ });
61023
+ let retain = DEFAULT_RETAIN;
61024
+ if (opts.retain !== undefined) {
61025
+ const n = Number(opts.retain);
61026
+ if (!Number.isInteger(n) || n < 0) {
61027
+ console.error(source_default.red(`Error: --retain must be a non-negative integer (got ${JSON.stringify(opts.retain)})`));
61028
+ process.exit(1);
61029
+ }
61030
+ retain = n;
61031
+ }
61032
+ const result = backupVault({ vaultPath, destDir, retain });
61033
+ console.log(`${source_default.green("\u2713")} vault backup written`);
61034
+ console.log(` ${source_default.dim("path:")} ${result.fullPath}`);
61035
+ console.log(` ${source_default.dim("size:")} ${result.bytes} bytes`);
61036
+ console.log(` ${source_default.dim("sha256:")} ${result.sha256}`);
61037
+ if (result.pruned.length > 0) {
61038
+ console.log(` ${source_default.dim("pruned:")} ${result.pruned.length} older backup(s)`);
61039
+ }
61040
+ } catch (err) {
61041
+ const msg = err instanceof Error ? err.message : String(err);
61042
+ console.error(source_default.red(`Error: ${msg}`));
61043
+ process.exit(1);
61044
+ }
61045
+ });
61046
+ }
61047
+
60761
61048
  // src/cli/vault.ts
60762
61049
  function isSandboxContext() {
60763
61050
  return process.env.SWITCHROOM_RUNTIME === "docker";
@@ -60992,7 +61279,7 @@ function registerVaultCommand(program3) {
60992
61279
  let value;
60993
61280
  if (opts.file) {
60994
61281
  try {
60995
- value = readFileSync33(resolvePath(opts.file), "utf8");
61282
+ value = readFileSync34(resolvePath(opts.file), "utf8");
60996
61283
  } catch (err) {
60997
61284
  const msg = err instanceof Error ? err.message : String(err);
60998
61285
  console.error(source_default.red(`Error reading file: ${msg}`));
@@ -61298,11 +61585,12 @@ Push passphrase to broker for future requests? [Y/n]: `);
61298
61585
  registerVaultDoctorCommand(vault, program3);
61299
61586
  registerVaultAuditCommand(vault, program3);
61300
61587
  registerVaultGrantCommands(vault, program3);
61588
+ registerVaultBackupCommand(vault, program3);
61301
61589
  }
61302
61590
 
61303
61591
  // src/cli/telegram.ts
61304
61592
  init_source();
61305
- import { existsSync as existsSync37, readFileSync as readFileSync34, writeFileSync as writeFileSync20 } from "node:fs";
61593
+ import { existsSync as existsSync39, readFileSync as readFileSync35, writeFileSync as writeFileSync20 } from "node:fs";
61306
61594
 
61307
61595
  // src/web/webhook-dispatch.ts
61308
61596
  function renderTemplate2(template, ctx) {
@@ -61601,7 +61889,7 @@ function registerDisableVerb(tg, program3) {
61601
61889
  }
61602
61890
  async function applyYamlEdit(program3, agent, feature, value, dryRun) {
61603
61891
  const path4 = getConfigPath(program3);
61604
- const before = readFileSync34(path4, "utf-8");
61892
+ const before = readFileSync35(path4, "utf-8");
61605
61893
  let after;
61606
61894
  try {
61607
61895
  after = setTelegramFeature(before, agent, feature, value);
@@ -61616,7 +61904,7 @@ async function applyYamlEdit(program3, agent, feature, value, dryRun) {
61616
61904
  }
61617
61905
  async function applyYamlRemove(program3, agent, feature, dryRun) {
61618
61906
  const path4 = getConfigPath(program3);
61619
- const before = readFileSync34(path4, "utf-8");
61907
+ const before = readFileSync35(path4, "utf-8");
61620
61908
  const after = removeTelegramFeature(before, agent, feature);
61621
61909
  if (before === after) {
61622
61910
  console.log(source_default.yellow(`No change \u2014 ${feature.replace("_", "-")} is not set for agent '${agent}'.`));
@@ -61665,7 +61953,7 @@ function fail(msg) {
61665
61953
  }
61666
61954
  async function applyYamlAddWebhook(program3, agent, source, dryRun) {
61667
61955
  const path4 = getConfigPath(program3);
61668
- const before = readFileSync34(path4, "utf-8");
61956
+ const before = readFileSync35(path4, "utf-8");
61669
61957
  let after;
61670
61958
  try {
61671
61959
  after = addWebhookSource(before, agent, source);
@@ -61685,7 +61973,7 @@ async function applyYamlAddWebhook(program3, agent, source, dryRun) {
61685
61973
  }
61686
61974
  async function applyYamlRemoveWebhook(program3, agent, source, dryRun) {
61687
61975
  const path4 = getConfigPath(program3);
61688
- const before = readFileSync34(path4, "utf-8");
61976
+ const before = readFileSync35(path4, "utf-8");
61689
61977
  const after = removeWebhookSource(before, agent, source);
61690
61978
  if (before === after) {
61691
61979
  console.log(source_default.yellow(`No change \u2014 webhook source '${source}' is not currently enabled for agent '${agent}'.`));
@@ -61702,7 +61990,7 @@ async function vaultPut(program3, key, value) {
61702
61990
  const configPath = program3.optsWithGlobals().config ?? undefined;
61703
61991
  const vaultPath = resolveVaultPath(configPath);
61704
61992
  const passphrase = await getVaultPassphrase();
61705
- if (!existsSync37(vaultPath)) {
61993
+ if (!existsSync39(vaultPath)) {
61706
61994
  createVault(passphrase, vaultPath);
61707
61995
  console.log(source_default.gray(` Created new vault at ${vaultPath}`));
61708
61996
  }
@@ -61742,7 +62030,7 @@ function registerDispatchVerb(tg, _program) {
61742
62030
  }
61743
62031
  let payload;
61744
62032
  try {
61745
- payload = JSON.parse(readFileSync34(opts.payload, "utf-8"));
62033
+ payload = JSON.parse(readFileSync35(opts.payload, "utf-8"));
61746
62034
  } catch (err) {
61747
62035
  fail(`Could not read payload file '${opts.payload}': ${err.message}`);
61748
62036
  }
@@ -62071,15 +62359,15 @@ async function ensureHindsightConsumer(configPath, account, uid = HINDSIGHT_DEFA
62071
62359
  // src/cli/memory.ts
62072
62360
  init_loader();
62073
62361
  var import_yaml10 = __toESM(require_dist(), 1);
62074
- import { existsSync as existsSync38, readFileSync as readFileSync35, writeFileSync as writeFileSync21 } from "node:fs";
62075
- import { join as join31 } from "node:path";
62362
+ import { existsSync as existsSync40, readFileSync as readFileSync36, writeFileSync as writeFileSync21 } from "node:fs";
62363
+ import { join as join33 } from "node:path";
62076
62364
  function readRecallLog(agentDir, limit) {
62077
- const path4 = join31(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
62078
- if (!existsSync38(path4))
62365
+ const path4 = join33(agentDir, ".claude", "plugins", "data", "hindsight-memory-inline", "state", "recall_log.jsonl");
62366
+ if (!existsSync40(path4))
62079
62367
  return [];
62080
62368
  let raw;
62081
62369
  try {
62082
- raw = readFileSync35(path4, "utf-8");
62370
+ raw = readFileSync36(path4, "utf-8");
62083
62371
  } catch {
62084
62372
  return [];
62085
62373
  }
@@ -62273,8 +62561,8 @@ Cross-agent reflection plan
62273
62561
  const url = `http://127.0.0.1:${ports.apiPort}/mcp/`;
62274
62562
  const configPath = getConfigPath(program3);
62275
62563
  try {
62276
- if (existsSync38(configPath)) {
62277
- const raw = readFileSync35(configPath, "utf-8");
62564
+ if (existsSync40(configPath)) {
62565
+ const raw = readFileSync36(configPath, "utf-8");
62278
62566
  const doc = import_yaml10.default.parseDocument(raw);
62279
62567
  if (!doc.has("memory")) {
62280
62568
  doc.set("memory", { backend: "hindsight", shared_collection: "shared", config: { provider, url } });
@@ -62315,7 +62603,7 @@ Cross-agent reflection plan
62315
62603
  process.exit(1);
62316
62604
  })() : Object.keys(config.agents);
62317
62605
  for (const name of targets) {
62318
- const agentDir = join31(agentsDir, name);
62606
+ const agentDir = join33(agentsDir, name);
62319
62607
  const entries = readRecallLog(agentDir, limit);
62320
62608
  if (opts.json) {
62321
62609
  for (const e of entries) {
@@ -62381,32 +62669,32 @@ init_source();
62381
62669
  init_merge();
62382
62670
  init_lifecycle();
62383
62671
  import {
62384
- readFileSync as readFileSync39,
62385
- existsSync as existsSync42,
62672
+ readFileSync as readFileSync40,
62673
+ existsSync as existsSync44,
62386
62674
  realpathSync as realpathSync3,
62387
- mkdirSync as mkdirSync24,
62388
- openSync as openSync9,
62389
- closeSync as closeSync9,
62390
- writeSync as writeSync5,
62675
+ mkdirSync as mkdirSync25,
62676
+ openSync as openSync10,
62677
+ closeSync as closeSync10,
62678
+ writeSync as writeSync6,
62391
62679
  constants as fsConstants3
62392
62680
  } from "node:fs";
62393
- import { resolve as resolve26, extname, join as join35, relative, dirname as dirname9 } from "node:path";
62394
- import { homedir as homedir18 } from "node:os";
62681
+ import { resolve as resolve26, extname, join as join37, relative, dirname as dirname9 } from "node:path";
62682
+ import { homedir as homedir19 } from "node:os";
62395
62683
  import { spawn as spawn5 } from "node:child_process";
62396
- import { timingSafeEqual as timingSafeEqual2, randomBytes as randomBytes9 } from "node:crypto";
62684
+ import { timingSafeEqual as timingSafeEqual2, randomBytes as randomBytes10 } from "node:crypto";
62397
62685
 
62398
62686
  // src/web/api.ts
62399
62687
  init_lifecycle();
62400
62688
  init_manager();
62401
62689
  init_hindsight();
62402
62690
  import { spawnSync as spawnSync4 } from "node:child_process";
62403
- import { existsSync as existsSync40, readFileSync as readFileSync37 } from "node:fs";
62691
+ import { existsSync as existsSync42, readFileSync as readFileSync38 } from "node:fs";
62404
62692
  import { resolve as resolve25 } from "node:path";
62405
62693
  init_audit_reader();
62406
62694
 
62407
62695
  // src/scheduler/dispatch.ts
62408
62696
  init_merge();
62409
- import { createHash as createHash7 } from "node:crypto";
62697
+ import { createHash as createHash8 } from "node:crypto";
62410
62698
  function collectScheduleEntries(config) {
62411
62699
  const out = [];
62412
62700
  const agentNames = Object.keys(config.agents).sort();
@@ -62423,7 +62711,7 @@ function collectScheduleEntries(config) {
62423
62711
  scheduleIndex: i,
62424
62712
  cron: entry.cron,
62425
62713
  prompt: entry.prompt,
62426
- promptKey: createHash7("sha256").update(entry.prompt).digest("hex").slice(0, 12)
62714
+ promptKey: createHash8("sha256").update(entry.prompt).digest("hex").slice(0, 12)
62427
62715
  });
62428
62716
  }
62429
62717
  }
@@ -66901,9 +67189,9 @@ class PostHog extends PostHogBackendClient {
66901
67189
  // src/analytics/posthog.ts
66902
67190
  init_paths();
66903
67191
  import {
66904
- existsSync as existsSync39,
66905
- mkdirSync as mkdirSync21,
66906
- readFileSync as readFileSync36,
67192
+ existsSync as existsSync41,
67193
+ mkdirSync as mkdirSync22,
67194
+ readFileSync as readFileSync37,
66907
67195
  writeFileSync as writeFileSync22
66908
67196
  } from "node:fs";
66909
67197
  import { dirname as dirname8 } from "node:path";
@@ -66923,8 +67211,8 @@ function getDistinctId() {
66923
67211
  return cachedDistinctId;
66924
67212
  const path4 = resolveStatePath("analytics-id");
66925
67213
  try {
66926
- if (existsSync39(path4)) {
66927
- const existing = readFileSync36(path4, "utf-8").trim();
67214
+ if (existsSync41(path4)) {
67215
+ const existing = readFileSync37(path4, "utf-8").trim();
66928
67216
  if (existing) {
66929
67217
  cachedDistinctId = existing;
66930
67218
  return existing;
@@ -66934,7 +67222,7 @@ function getDistinctId() {
66934
67222
  const id = randomUUID3();
66935
67223
  cachedDistinctId = id;
66936
67224
  try {
66937
- mkdirSync21(dirname8(path4), { recursive: true });
67225
+ mkdirSync22(dirname8(path4), { recursive: true });
66938
67226
  writeFileSync22(path4, id, "utf-8");
66939
67227
  } catch {}
66940
67228
  return id;
@@ -67012,8 +67300,8 @@ init_account_store();
67012
67300
  init_client2();
67013
67301
 
67014
67302
  // telegram-plugin/registry/turns-schema.ts
67015
- import { chmodSync as chmodSync8, mkdirSync as mkdirSync22 } from "fs";
67016
- import { join as join33 } from "path";
67303
+ import { chmodSync as chmodSync8, mkdirSync as mkdirSync23 } from "fs";
67304
+ import { join as join35 } from "path";
67017
67305
  var DatabaseClass = null;
67018
67306
  function loadDatabaseClass() {
67019
67307
  if (DatabaseClass != null)
@@ -67072,9 +67360,9 @@ function applySchema(db) {
67072
67360
  }
67073
67361
  function openTurnsDb(agentDir) {
67074
67362
  const Database2 = loadDatabaseClass();
67075
- const dir = join33(agentDir, "telegram");
67076
- mkdirSync22(dir, { recursive: true, mode: 448 });
67077
- const path4 = join33(dir, "registry.db");
67363
+ const dir = join35(agentDir, "telegram");
67364
+ mkdirSync23(dir, { recursive: true, mode: 448 });
67365
+ const path4 = join35(dir, "registry.db");
67078
67366
  const db = new Database2(path4, { create: true });
67079
67367
  applySchema(db);
67080
67368
  try {
@@ -67487,9 +67775,9 @@ async function handleGetSystemHealth(home2) {
67487
67775
  };
67488
67776
  try {
67489
67777
  const logPath = defaultAuditLogPath2(home2);
67490
- if (existsSync40(logPath)) {
67778
+ if (existsSync42(logPath)) {
67491
67779
  hostd.auditLogPresent = true;
67492
- const raw = readFileSync37(logPath, "utf-8");
67780
+ const raw = readFileSync38(logPath, "utf-8");
67493
67781
  hostd.recent = readAndFilter(raw, {}, 10);
67494
67782
  }
67495
67783
  } catch (err) {
@@ -67568,9 +67856,9 @@ async function handleGetApprovals() {
67568
67856
  }
67569
67857
 
67570
67858
  // src/web/webhook-handler.ts
67571
- import { appendFileSync as appendFileSync2, existsSync as existsSync41, mkdirSync as mkdirSync23, readFileSync as readFileSync38, writeFileSync as writeFileSync23 } from "fs";
67572
- import { join as join34 } from "path";
67573
- import { homedir as homedir17 } from "os";
67859
+ import { appendFileSync as appendFileSync3, existsSync as existsSync43, mkdirSync as mkdirSync24, readFileSync as readFileSync39, writeFileSync as writeFileSync23 } from "fs";
67860
+ import { join as join36 } from "path";
67861
+ import { homedir as homedir18 } from "os";
67574
67862
 
67575
67863
  // src/web/webhook-verify.ts
67576
67864
  import { createHmac as createHmac2, timingSafeEqual } from "crypto";
@@ -67689,9 +67977,9 @@ var DEDUP_MAX = 1000;
67689
67977
  var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
67690
67978
  function loadDedupFile(path4) {
67691
67979
  try {
67692
- if (!existsSync41(path4))
67980
+ if (!existsSync43(path4))
67693
67981
  return {};
67694
- const raw = JSON.parse(readFileSync38(path4, "utf-8"));
67982
+ const raw = JSON.parse(readFileSync39(path4, "utf-8"));
67695
67983
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
67696
67984
  } catch {
67697
67985
  return {};
@@ -67713,8 +68001,8 @@ var agentDedupCache = new Map;
67713
68001
  function createFileDedupStore(resolveAgentDir) {
67714
68002
  return {
67715
68003
  check(agent, deliveryId, now) {
67716
- const telegramDir = join34(resolveAgentDir(agent), "telegram");
67717
- const filePath = join34(telegramDir, "webhook-dedup.json");
68004
+ const telegramDir = join36(resolveAgentDir(agent), "telegram");
68005
+ const filePath = join36(telegramDir, "webhook-dedup.json");
67718
68006
  if (!agentDedupCache.has(agent)) {
67719
68007
  agentDedupCache.set(agent, loadDedupFile(filePath));
67720
68008
  }
@@ -67724,7 +68012,7 @@ function createFileDedupStore(resolveAgentDir) {
67724
68012
  }
67725
68013
  deliveries[deliveryId] = now;
67726
68014
  try {
67727
- mkdirSync23(telegramDir, { recursive: true });
68015
+ mkdirSync24(telegramDir, { recursive: true });
67728
68016
  saveDedupFile(filePath, deliveries, now);
67729
68017
  } catch {}
67730
68018
  return;
@@ -67766,9 +68054,9 @@ function shouldWriteThrottleIssue(agent, source, now, windowMap) {
67766
68054
  return true;
67767
68055
  }
67768
68056
  function writeThrottleIssue(agent, source, now, telegramDir, log) {
67769
- const issuesPath = join34(telegramDir, "issues.jsonl");
68057
+ const issuesPath = join36(telegramDir, "issues.jsonl");
67770
68058
  try {
67771
- mkdirSync23(telegramDir, { recursive: true });
68059
+ mkdirSync24(telegramDir, { recursive: true });
67772
68060
  const record = {
67773
68061
  ts: now,
67774
68062
  agent,
@@ -67781,7 +68069,7 @@ function writeThrottleIssue(agent, source, now, telegramDir, log) {
67781
68069
  first_seen: now,
67782
68070
  last_seen: now
67783
68071
  };
67784
- appendFileSync2(issuesPath, JSON.stringify(record) + `
68072
+ appendFileSync3(issuesPath, JSON.stringify(record) + `
67785
68073
  `, { mode: 384 });
67786
68074
  } catch (err) {
67787
68075
  log(`webhook-ingest: agent='${agent}' source='${source}' issues.jsonl write failed: ${err.message}
@@ -67791,7 +68079,7 @@ function writeThrottleIssue(agent, source, now, telegramDir, log) {
67791
68079
  async function handleWebhookIngest(args, deps = {}) {
67792
68080
  const log = deps.log ?? ((s) => process.stderr.write(s));
67793
68081
  const now = (deps.now ?? Date.now)();
67794
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join34(homedir17(), ".switchroom", "agents", a));
68082
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join36(homedir18(), ".switchroom", "agents", a));
67795
68083
  const rateLimiter = deps.rateLimiter ?? defaultRateLimiter;
67796
68084
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
67797
68085
  if (!args.agentExists) {
@@ -67845,7 +68133,7 @@ async function handleWebhookIngest(args, deps = {}) {
67845
68133
  const retryAfter = rpm !== undefined ? rateLimiter.check(args.agent, source, rpm, now) : null;
67846
68134
  if (retryAfter !== null) {
67847
68135
  const agentDir2 = resolveAgentDir(args.agent);
67848
- const telegramDir2 = join34(agentDir2, "telegram");
68136
+ const telegramDir2 = join36(agentDir2, "telegram");
67849
68137
  if (shouldWriteThrottleIssue(args.agent, source, now)) {
67850
68138
  writeThrottleIssue(args.agent, source, now, telegramDir2, log);
67851
68139
  }
@@ -67864,10 +68152,10 @@ async function handleWebhookIngest(args, deps = {}) {
67864
68152
  const eventType = source === "github" ? args.headers.get("x-github-event") ?? "unknown" : args.source;
67865
68153
  const rendered = source === "github" ? renderGithubEvent(eventType, payload) : renderGenericEvent(args.source, payload);
67866
68154
  const agentDir = resolveAgentDir(args.agent);
67867
- const telegramDir = join34(agentDir, "telegram");
67868
- const logPath = join34(telegramDir, "webhook-events.jsonl");
68155
+ const telegramDir = join36(agentDir, "telegram");
68156
+ const logPath = join36(telegramDir, "webhook-events.jsonl");
67869
68157
  try {
67870
- mkdirSync23(telegramDir, { recursive: true });
68158
+ mkdirSync24(telegramDir, { recursive: true });
67871
68159
  const record = {
67872
68160
  ts: now,
67873
68161
  source,
@@ -67875,7 +68163,7 @@ async function handleWebhookIngest(args, deps = {}) {
67875
68163
  rendered_text: rendered.text,
67876
68164
  payload
67877
68165
  };
67878
- appendFileSync2(logPath, JSON.stringify(record) + `
68166
+ appendFileSync3(logPath, JSON.stringify(record) + `
67879
68167
  `, { mode: 384 });
67880
68168
  } catch (err) {
67881
68169
  log(`webhook-ingest: agent='${args.agent}' source='${source}' write failed: ${err.message}
@@ -67918,27 +68206,27 @@ function resolveWebToken() {
67918
68206
  const fromEnv = process.env.SWITCHROOM_WEB_TOKEN;
67919
68207
  if (fromEnv && fromEnv.length > 0)
67920
68208
  return fromEnv;
67921
- const home2 = process.env.HOME ?? homedir18();
67922
- const tokenPath = join35(home2, ".switchroom", "web-token");
67923
- if (existsSync42(tokenPath)) {
67924
- const existing = readFileSync39(tokenPath, "utf8").trim();
68209
+ const home2 = process.env.HOME ?? homedir19();
68210
+ const tokenPath = join37(home2, ".switchroom", "web-token");
68211
+ if (existsSync44(tokenPath)) {
68212
+ const existing = readFileSync40(tokenPath, "utf8").trim();
67925
68213
  if (existing.length > 0)
67926
68214
  return existing;
67927
68215
  }
67928
- const token = randomBytes9(32).toString("hex");
67929
- mkdirSync24(dirname9(tokenPath), { recursive: true, mode: 448 });
68216
+ const token = randomBytes10(32).toString("hex");
68217
+ mkdirSync25(dirname9(tokenPath), { recursive: true, mode: 448 });
67930
68218
  try {
67931
- const fd = openSync9(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
68219
+ const fd = openSync10(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
67932
68220
  try {
67933
- writeSync5(fd, token + `
68221
+ writeSync6(fd, token + `
67934
68222
  `);
67935
68223
  } finally {
67936
- closeSync9(fd);
68224
+ closeSync10(fd);
67937
68225
  }
67938
68226
  return token;
67939
68227
  } catch (err) {
67940
68228
  if (err.code === "EEXIST") {
67941
- const existing = readFileSync39(tokenPath, "utf8").trim();
68229
+ const existing = readFileSync40(tokenPath, "utf8").trim();
67942
68230
  if (existing.length > 0)
67943
68231
  return existing;
67944
68232
  }
@@ -68004,11 +68292,11 @@ function checkWsAuth(req, token, server) {
68004
68292
  return presented !== null && constantTimeEqual(presented, token);
68005
68293
  }
68006
68294
  function loadWebhookSecrets() {
68007
- const path4 = join35(homedir18(), ".switchroom", "webhook-secrets.json");
68008
- if (!existsSync42(path4))
68295
+ const path4 = join37(homedir19(), ".switchroom", "webhook-secrets.json");
68296
+ if (!existsSync44(path4))
68009
68297
  return {};
68010
68298
  try {
68011
- const parsed = JSON.parse(readFileSync39(path4, "utf-8"));
68299
+ const parsed = JSON.parse(readFileSync40(path4, "utf-8"));
68012
68300
  return parsed && typeof parsed === "object" ? parsed : {};
68013
68301
  } catch (err) {
68014
68302
  process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
@@ -68107,7 +68395,7 @@ function parseRoute(pathname, method) {
68107
68395
  }
68108
68396
  function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
68109
68397
  const uiDirRaw = resolve26(import.meta.dirname, "ui");
68110
- const uiDir = existsSync42(uiDirRaw) ? realpathSync3(uiDirRaw) : uiDirRaw;
68398
+ const uiDir = existsSync44(uiDirRaw) ? realpathSync3(uiDirRaw) : uiDirRaw;
68111
68399
  const token = resolveWebToken();
68112
68400
  const localhostOnly = hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
68113
68401
  const server = Bun.serve({
@@ -68262,8 +68550,8 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
68262
68550
  }
68263
68551
  }
68264
68552
  let filePath = pathname === "/" ? "/index.html" : pathname;
68265
- const fullPath = join35(uiDir, filePath);
68266
- if (!existsSync42(fullPath)) {
68553
+ const fullPath = join37(uiDir, filePath);
68554
+ if (!existsSync44(fullPath)) {
68267
68555
  return new Response("Not Found", { status: 404 });
68268
68556
  }
68269
68557
  let realFullPath;
@@ -68278,7 +68566,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
68278
68566
  }
68279
68567
  const ext = extname(realFullPath);
68280
68568
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
68281
- const content = readFileSync39(realFullPath);
68569
+ const content = readFileSync40(realFullPath);
68282
68570
  return new Response(content, {
68283
68571
  headers: { "Content-Type": contentType }
68284
68572
  });
@@ -68412,7 +68700,7 @@ Starting Switchroom dashboard...
68412
68700
  // src/cli/setup.ts
68413
68701
  init_source();
68414
68702
  init_loader();
68415
- import { existsSync as existsSync43, copyFileSync as copyFileSync8, readFileSync as readFileSync40, writeFileSync as writeFileSync24, mkdirSync as mkdirSync25 } from "node:fs";
68703
+ import { existsSync as existsSync45, copyFileSync as copyFileSync8, readFileSync as readFileSync41, writeFileSync as writeFileSync24, mkdirSync as mkdirSync26 } from "node:fs";
68416
68704
  import { resolve as resolve27, dirname as dirname10 } from "node:path";
68417
68705
  init_vault();
68418
68706
  init_manager();
@@ -68552,7 +68840,7 @@ async function stepConfigFile(configPath, nonInteractive) {
68552
68840
  existingConfig = null;
68553
68841
  }
68554
68842
  }
68555
- if (existingConfig && existsSync43(existingConfig)) {
68843
+ if (existingConfig && existsSync45(existingConfig)) {
68556
68844
  if (!nonInteractive) {
68557
68845
  const useExisting = await askYesNo(` Found ${source_default.cyan(existingConfig)}. Use it?`, true);
68558
68846
  if (!useExisting) {
@@ -68580,10 +68868,10 @@ async function copyExampleConfig(nonInteractive) {
68580
68868
  }
68581
68869
  const srcFile = resolve27(examplesDir, `${choice}.yaml`);
68582
68870
  const destFile = resolvePath("~/.switchroom/switchroom.yaml");
68583
- if (!existsSync43(srcFile)) {
68871
+ if (!existsSync45(srcFile)) {
68584
68872
  throw new ConfigError(`Example config not found: ${choice}.yaml`);
68585
68873
  }
68586
- mkdirSync25(dirname10(destFile), { recursive: true });
68874
+ mkdirSync26(dirname10(destFile), { recursive: true });
68587
68875
  copyFileSync8(srcFile, destFile);
68588
68876
  console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
68589
68877
  console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
@@ -68664,7 +68952,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
68664
68952
  try {
68665
68953
  const { openVault: openVault2 } = await Promise.resolve().then(() => (init_vault(), exports_vault));
68666
68954
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
68667
- if (existsSync43(vaultPath)) {
68955
+ if (existsSync45(vaultPath)) {
68668
68956
  const secrets = openVault2(passphrase, vaultPath);
68669
68957
  const key = rawToken.replace("vault:", "");
68670
68958
  const entry = secrets[key];
@@ -68692,7 +68980,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
68692
68980
  async function storeTokenInVault(config, vaultRef, token) {
68693
68981
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
68694
68982
  const key = vaultRef.replace("vault:", "");
68695
- if (!existsSync43(vaultPath)) {
68983
+ if (!existsSync45(vaultPath)) {
68696
68984
  console.log(source_default.gray(" Creating encrypted vault..."));
68697
68985
  let passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
68698
68986
  if (!passphrase) {
@@ -68812,7 +69100,7 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
68812
69100
  try {
68813
69101
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
68814
69102
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
68815
- if (passphrase && existsSync43(vaultPath)) {
69103
+ if (passphrase && existsSync45(vaultPath)) {
68816
69104
  const existing = getStringSecret(passphrase, vaultPath, "hindsight-api-key");
68817
69105
  if (existing) {
68818
69106
  console.log(source_default.gray(" Note: legacy 'hindsight-api-key' is in your vault but is no longer used. You can remove it with `switchroom vault rm hindsight-api-key`."));
@@ -68972,13 +69260,13 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
68972
69260
  return;
68973
69261
  }
68974
69262
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
68975
- if (!existsSync43(vaultPath)) {
69263
+ if (!existsSync45(vaultPath)) {
68976
69264
  console.log(source_default.gray(" Skipping (vault not created yet)."));
68977
69265
  return;
68978
69266
  }
68979
69267
  const credPathRaw = config.vault?.broker?.autoUnlockCredentialPath ?? "~/.switchroom/vault-auto-unlock";
68980
69268
  const credPath = resolvePath(credPathRaw);
68981
- if (config.vault?.broker?.autoUnlock === true && existsSync43(credPath)) {
69269
+ if (config.vault?.broker?.autoUnlock === true && existsSync45(credPath)) {
68982
69270
  console.log(source_default.green(` ${STEP_DONE} Already configured (${credPath})`));
68983
69271
  return;
68984
69272
  }
@@ -69044,9 +69332,9 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
69044
69332
  const choice = await askChoice(" Approval posture", [PASSPHRASE_CHOICE, TELEGRAM_ID_CHOICE]);
69045
69333
  if (choice === TELEGRAM_ID_CHOICE) {
69046
69334
  try {
69047
- const yamlPath = existsSync43(resolve27(process.cwd(), "switchroom.yaml")) ? resolve27(process.cwd(), "switchroom.yaml") : resolve27(process.cwd(), "switchroom.yml");
69048
- if (existsSync43(yamlPath)) {
69049
- const content = readFileSync40(yamlPath, "utf-8");
69335
+ const yamlPath = existsSync45(resolve27(process.cwd(), "switchroom.yaml")) ? resolve27(process.cwd(), "switchroom.yaml") : resolve27(process.cwd(), "switchroom.yml");
69336
+ if (existsSync45(yamlPath)) {
69337
+ const content = readFileSync41(yamlPath, "utf-8");
69050
69338
  const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
69051
69339
  if (result.kind === "rewritten") {
69052
69340
  writeFileSync24(yamlPath, result.content, "utf-8");
@@ -69080,8 +69368,8 @@ async function stepDangerousMode(config, nonInteractive) {
69080
69368
  resolve27(process.cwd(), "switchroom.yml")
69081
69369
  ];
69082
69370
  for (const configPath of configPaths) {
69083
- if (existsSync43(configPath)) {
69084
- let content = readFileSync40(configPath, "utf-8");
69371
+ if (existsSync45(configPath)) {
69372
+ let content = readFileSync41(configPath, "utf-8");
69085
69373
  const agentNames = Object.keys(config.agents);
69086
69374
  for (const name of agentNames) {
69087
69375
  const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
@@ -69210,17 +69498,17 @@ init_doctor();
69210
69498
  init_source();
69211
69499
  init_loader();
69212
69500
  init_lifecycle();
69213
- import { cpSync as cpSync2, existsSync as existsSync49, mkdirSync as mkdirSync27, readFileSync as readFileSync45, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync22 } from "node:fs";
69501
+ 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";
69214
69502
  import { spawnSync as spawnSync8 } from "node:child_process";
69215
- import { join as join44, dirname as dirname13, resolve as resolve30 } from "node:path";
69216
- import { homedir as homedir25 } from "node:os";
69217
- var DEFAULT_COMPOSE_PATH = join44(homedir25(), ".switchroom", "compose", "docker-compose.yml");
69503
+ import { join as join46, dirname as dirname13, resolve as resolve30 } from "node:path";
69504
+ import { homedir as homedir26 } from "node:os";
69505
+ var DEFAULT_COMPOSE_PATH = join46(homedir26(), ".switchroom", "compose", "docker-compose.yml");
69218
69506
  function runningFromSwitchroomCheckout(scriptPath) {
69219
69507
  let dir = dirname13(scriptPath);
69220
69508
  for (let i = 0;i < 12; i++) {
69221
- if (existsSync49(join44(dir, ".git"))) {
69509
+ if (existsSync51(join46(dir, ".git"))) {
69222
69510
  try {
69223
- const pkg = JSON.parse(readFileSync45(join44(dir, "package.json"), "utf-8"));
69511
+ const pkg = JSON.parse(readFileSync46(join46(dir, "package.json"), "utf-8"));
69224
69512
  if (pkg.name === "switchroom")
69225
69513
  return true;
69226
69514
  } catch {}
@@ -69272,7 +69560,7 @@ function planUpdate(opts) {
69272
69560
  steps.push({
69273
69561
  name: "pull-images",
69274
69562
  description: "Pull broker / kernel / agent images from GHCR",
69275
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync49(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
69563
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync51(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
69276
69564
  run: () => {
69277
69565
  const r = runner("docker", [
69278
69566
  "compose",
@@ -69351,17 +69639,17 @@ function planUpdate(opts) {
69351
69639
  return;
69352
69640
  }
69353
69641
  const source = resolve30(import.meta.dirname, "../../skills");
69354
- const dest = join44(homedir25(), ".switchroom", "skills", "_bundled");
69355
- if (!existsSync49(source)) {
69642
+ const dest = join46(homedir26(), ".switchroom", "skills", "_bundled");
69643
+ if (!existsSync51(source)) {
69356
69644
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
69357
69645
  `);
69358
69646
  return;
69359
69647
  }
69360
69648
  try {
69361
- if (existsSync49(dest)) {
69649
+ if (existsSync51(dest)) {
69362
69650
  rmSync12(dest, { recursive: true, force: true });
69363
69651
  }
69364
- mkdirSync27(dirname13(dest), { recursive: true });
69652
+ mkdirSync28(dirname13(dest), { recursive: true });
69365
69653
  cpSync2(source, dest, { recursive: true, dereference: false });
69366
69654
  } catch (err) {
69367
69655
  throw new Error(`sync-bundled-skills failed: ${err.message}`);
@@ -69461,14 +69749,14 @@ function defaultStatusProbe(composePath) {
69461
69749
  } catch {}
69462
69750
  if (scriptPath) {
69463
69751
  try {
69464
- cliBuiltAt = new Date(statSync22(scriptPath).mtimeMs).toISOString();
69752
+ cliBuiltAt = new Date(statSync23(scriptPath).mtimeMs).toISOString();
69465
69753
  } catch {}
69466
69754
  let dir = dirname13(scriptPath);
69467
69755
  for (let i = 0;i < 8; i++) {
69468
- const pkgPath = join44(dir, "package.json");
69469
- if (existsSync49(pkgPath)) {
69756
+ const pkgPath = join46(dir, "package.json");
69757
+ if (existsSync51(pkgPath)) {
69470
69758
  try {
69471
- const pkg = JSON.parse(readFileSync45(pkgPath, "utf-8"));
69759
+ const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
69472
69760
  if (typeof pkg.version === "string")
69473
69761
  cliVersion = pkg.version;
69474
69762
  } catch (err) {
@@ -69489,7 +69777,7 @@ function defaultStatusProbe(composePath) {
69489
69777
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
69490
69778
  }
69491
69779
  const services = [];
69492
- if (!existsSync49(composePath)) {
69780
+ if (!existsSync51(composePath)) {
69493
69781
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
69494
69782
  return { cliVersion, cliBuiltAt, services, warnings };
69495
69783
  }
@@ -69684,8 +69972,8 @@ init_source();
69684
69972
  init_helpers();
69685
69973
  init_lifecycle();
69686
69974
  import { execSync as execSync4 } from "node:child_process";
69687
- import { existsSync as existsSync50, readFileSync as readFileSync46 } from "node:fs";
69688
- import { dirname as dirname14, join as join45 } from "node:path";
69975
+ import { existsSync as existsSync52, readFileSync as readFileSync47 } from "node:fs";
69976
+ import { dirname as dirname14, join as join47 } from "node:path";
69689
69977
  function getClaudeCodeVersion() {
69690
69978
  try {
69691
69979
  const out = execSync4("claude --version 2>/dev/null", {
@@ -69735,11 +70023,11 @@ function formatUptime3(timestamp) {
69735
70023
  function locateSwitchroomInstallDir() {
69736
70024
  let dir = import.meta.dirname;
69737
70025
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
69738
- const pkgPath = join45(dir, "package.json");
69739
- if (existsSync50(pkgPath)) {
70026
+ const pkgPath = join47(dir, "package.json");
70027
+ if (existsSync52(pkgPath)) {
69740
70028
  try {
69741
- const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
69742
- if (pkg.name === "switchroom" && existsSync50(join45(dir, ".git"))) {
70029
+ const pkg = JSON.parse(readFileSync47(pkgPath, "utf-8"));
70030
+ if (pkg.name === "switchroom" && existsSync52(join47(dir, ".git"))) {
69743
70031
  return dir;
69744
70032
  }
69745
70033
  } catch {}
@@ -69957,20 +70245,20 @@ function registerHandoffCommand(program3) {
69957
70245
 
69958
70246
  // src/issues/store.ts
69959
70247
  import {
69960
- closeSync as closeSync10,
69961
- existsSync as existsSync51,
69962
- mkdirSync as mkdirSync28,
69963
- openSync as openSync10,
69964
- readdirSync as readdirSync18,
69965
- readFileSync as readFileSync47,
69966
- renameSync as renameSync10,
69967
- statSync as statSync23,
69968
- unlinkSync as unlinkSync10,
70248
+ closeSync as closeSync11,
70249
+ existsSync as existsSync53,
70250
+ mkdirSync as mkdirSync29,
70251
+ openSync as openSync11,
70252
+ readdirSync as readdirSync19,
70253
+ readFileSync as readFileSync48,
70254
+ renameSync as renameSync11,
70255
+ statSync as statSync24,
70256
+ unlinkSync as unlinkSync11,
69969
70257
  writeFileSync as writeFileSync25,
69970
- writeSync as writeSync6
70258
+ writeSync as writeSync7
69971
70259
  } from "node:fs";
69972
- import { join as join46 } from "node:path";
69973
- import { randomBytes as randomBytes10 } from "node:crypto";
70260
+ import { join as join48 } from "node:path";
70261
+ import { randomBytes as randomBytes11 } from "node:crypto";
69974
70262
  import { execSync as execSync5 } from "node:child_process";
69975
70263
 
69976
70264
  // src/issues/types.ts
@@ -70289,12 +70577,12 @@ function redactedMarker(ruleId) {
70289
70577
  var ISSUES_FILE = "issues.jsonl";
70290
70578
  var ISSUES_LOCK = "issues.lock";
70291
70579
  function readAll(stateDir) {
70292
- const path4 = join46(stateDir, ISSUES_FILE);
70293
- if (!existsSync51(path4))
70580
+ const path4 = join48(stateDir, ISSUES_FILE);
70581
+ if (!existsSync53(path4))
70294
70582
  return [];
70295
70583
  let raw;
70296
70584
  try {
70297
- raw = readFileSync47(path4, "utf-8");
70585
+ raw = readFileSync48(path4, "utf-8");
70298
70586
  } catch {
70299
70587
  return [];
70300
70588
  }
@@ -70367,7 +70655,7 @@ function record(stateDir, input, nowFn = Date.now) {
70367
70655
  });
70368
70656
  }
70369
70657
  function resolve33(stateDir, fingerprint, nowFn = Date.now) {
70370
- if (!existsSync51(join46(stateDir, ISSUES_FILE)))
70658
+ if (!existsSync53(join48(stateDir, ISSUES_FILE)))
70371
70659
  return 0;
70372
70660
  return withLock(stateDir, () => {
70373
70661
  const all = readAll(stateDir);
@@ -70385,7 +70673,7 @@ function resolve33(stateDir, fingerprint, nowFn = Date.now) {
70385
70673
  });
70386
70674
  }
70387
70675
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
70388
- if (!existsSync51(join46(stateDir, ISSUES_FILE)))
70676
+ if (!existsSync53(join48(stateDir, ISSUES_FILE)))
70389
70677
  return 0;
70390
70678
  return withLock(stateDir, () => {
70391
70679
  const all = readAll(stateDir);
@@ -70403,7 +70691,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
70403
70691
  });
70404
70692
  }
70405
70693
  function prune(stateDir, opts = {}) {
70406
- if (!existsSync51(join46(stateDir, ISSUES_FILE)))
70694
+ if (!existsSync53(join48(stateDir, ISSUES_FILE)))
70407
70695
  return 0;
70408
70696
  return withLock(stateDir, () => {
70409
70697
  const all = readAll(stateDir);
@@ -70433,24 +70721,24 @@ function prune(stateDir, opts = {}) {
70433
70721
  });
70434
70722
  }
70435
70723
  function ensureDir(stateDir) {
70436
- mkdirSync28(stateDir, { recursive: true });
70724
+ mkdirSync29(stateDir, { recursive: true });
70437
70725
  }
70438
70726
  function writeAll(stateDir, events) {
70439
- const path4 = join46(stateDir, ISSUES_FILE);
70727
+ const path4 = join48(stateDir, ISSUES_FILE);
70440
70728
  sweepOrphanTmpFiles(stateDir);
70441
- const tmp = `${path4}.tmp-${process.pid}-${randomBytes10(4).toString("hex")}`;
70729
+ const tmp = `${path4}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
70442
70730
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
70443
70731
  `) + `
70444
70732
  `;
70445
70733
  writeFileSync25(tmp, body, "utf-8");
70446
- renameSync10(tmp, path4);
70734
+ renameSync11(tmp, path4);
70447
70735
  }
70448
70736
  var ORPHAN_TMP_TTL_MS = 60000;
70449
70737
  var TMP_PREFIX = `${ISSUES_FILE}.tmp-`;
70450
70738
  function sweepOrphanTmpFiles(stateDir) {
70451
70739
  let entries;
70452
70740
  try {
70453
- entries = readdirSync18(stateDir);
70741
+ entries = readdirSync19(stateDir);
70454
70742
  } catch {
70455
70743
  return;
70456
70744
  }
@@ -70458,11 +70746,11 @@ function sweepOrphanTmpFiles(stateDir) {
70458
70746
  for (const entry of entries) {
70459
70747
  if (!entry.startsWith(TMP_PREFIX))
70460
70748
  continue;
70461
- const tmpPath = join46(stateDir, entry);
70749
+ const tmpPath = join48(stateDir, entry);
70462
70750
  try {
70463
- const stat = statSync23(tmpPath);
70751
+ const stat = statSync24(tmpPath);
70464
70752
  if (stat.mtimeMs < cutoff) {
70465
- unlinkSync10(tmpPath);
70753
+ unlinkSync11(tmpPath);
70466
70754
  }
70467
70755
  } catch {}
70468
70756
  }
@@ -70470,14 +70758,14 @@ function sweepOrphanTmpFiles(stateDir) {
70470
70758
  var LOCK_RETRY_MS = 25;
70471
70759
  var LOCK_TIMEOUT_MS = 1e4;
70472
70760
  function withLock(stateDir, fn) {
70473
- const lockPath = join46(stateDir, ISSUES_LOCK);
70761
+ const lockPath = join48(stateDir, ISSUES_LOCK);
70474
70762
  const startedAt = Date.now();
70475
70763
  let fd = null;
70476
70764
  while (fd === null) {
70477
70765
  try {
70478
- fd = openSync10(lockPath, "wx");
70766
+ fd = openSync11(lockPath, "wx");
70479
70767
  try {
70480
- writeSync6(fd, String(process.pid));
70768
+ writeSync7(fd, String(process.pid));
70481
70769
  } catch {}
70482
70770
  } catch (err) {
70483
70771
  const e = err;
@@ -70495,30 +70783,30 @@ function withLock(stateDir, fn) {
70495
70783
  return fn();
70496
70784
  } finally {
70497
70785
  try {
70498
- closeSync10(fd);
70786
+ closeSync11(fd);
70499
70787
  } catch {}
70500
70788
  try {
70501
- unlinkSync10(lockPath);
70789
+ unlinkSync11(lockPath);
70502
70790
  } catch {}
70503
70791
  }
70504
70792
  }
70505
70793
  function tryStealStaleLock(lockPath) {
70506
70794
  let pidStr;
70507
70795
  try {
70508
- pidStr = readFileSync47(lockPath, "utf-8").trim();
70796
+ pidStr = readFileSync48(lockPath, "utf-8").trim();
70509
70797
  } catch {
70510
70798
  return true;
70511
70799
  }
70512
70800
  const pid = Number(pidStr);
70513
70801
  if (!Number.isFinite(pid) || pid <= 0) {
70514
70802
  try {
70515
- unlinkSync10(lockPath);
70803
+ unlinkSync11(lockPath);
70516
70804
  } catch {}
70517
70805
  return true;
70518
70806
  }
70519
70807
  if (pid === process.pid) {
70520
70808
  try {
70521
- unlinkSync10(lockPath);
70809
+ unlinkSync11(lockPath);
70522
70810
  } catch {}
70523
70811
  return true;
70524
70812
  }
@@ -70533,7 +70821,7 @@ function tryStealStaleLock(lockPath) {
70533
70821
  return false;
70534
70822
  }
70535
70823
  try {
70536
- unlinkSync10(lockPath);
70824
+ unlinkSync11(lockPath);
70537
70825
  } catch {}
70538
70826
  return true;
70539
70827
  }
@@ -70753,21 +71041,21 @@ function relTime(deltaMs) {
70753
71041
 
70754
71042
  // src/cli/deps.ts
70755
71043
  init_source();
70756
- import { existsSync as existsSync54 } from "node:fs";
70757
- import { homedir as homedir28 } from "node:os";
70758
- import { join as join49, resolve as resolve34 } from "node:path";
71044
+ import { existsSync as existsSync56 } from "node:fs";
71045
+ import { homedir as homedir29 } from "node:os";
71046
+ import { join as join51, resolve as resolve34 } from "node:path";
70759
71047
 
70760
71048
  // src/deps/python.ts
70761
- import { createHash as createHash9 } from "node:crypto";
71049
+ import { createHash as createHash10 } from "node:crypto";
70762
71050
  import {
70763
- existsSync as existsSync52,
70764
- mkdirSync as mkdirSync29,
70765
- readFileSync as readFileSync48,
71051
+ existsSync as existsSync54,
71052
+ mkdirSync as mkdirSync30,
71053
+ readFileSync as readFileSync49,
70766
71054
  rmSync as rmSync13,
70767
71055
  writeFileSync as writeFileSync26
70768
71056
  } from "node:fs";
70769
- import { dirname as dirname15, join as join47 } from "node:path";
70770
- import { homedir as homedir26 } from "node:os";
71057
+ import { dirname as dirname15, join as join49 } from "node:path";
71058
+ import { homedir as homedir27 } from "node:os";
70771
71059
  import { execFileSync as execFileSync14 } from "node:child_process";
70772
71060
 
70773
71061
  class PythonEnvError extends Error {
@@ -70779,26 +71067,26 @@ class PythonEnvError extends Error {
70779
71067
  }
70780
71068
  }
70781
71069
  function defaultPythonCacheRoot() {
70782
- return join47(homedir26(), ".switchroom", "deps", "python");
71070
+ return join49(homedir27(), ".switchroom", "deps", "python");
70783
71071
  }
70784
71072
  function hashFile(path4) {
70785
- return createHash9("sha256").update(readFileSync48(path4)).digest("hex");
71073
+ return createHash10("sha256").update(readFileSync49(path4)).digest("hex");
70786
71074
  }
70787
71075
  function ensurePythonEnv(opts) {
70788
71076
  const { skillName, requirementsPath, force = false } = opts;
70789
71077
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
70790
71078
  const hostPython = opts.pythonBin ?? "python3";
70791
- if (!existsSync52(requirementsPath)) {
71079
+ if (!existsSync54(requirementsPath)) {
70792
71080
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
70793
71081
  }
70794
- const venvDir = join47(cacheRoot, skillName);
70795
- const stampPath = join47(venvDir, ".requirements.sha256");
70796
- const binDir = join47(venvDir, "bin");
70797
- const pythonBin = join47(binDir, "python");
70798
- const pipBin = join47(binDir, "pip");
71082
+ const venvDir = join49(cacheRoot, skillName);
71083
+ const stampPath = join49(venvDir, ".requirements.sha256");
71084
+ const binDir = join49(venvDir, "bin");
71085
+ const pythonBin = join49(binDir, "python");
71086
+ const pipBin = join49(binDir, "pip");
70799
71087
  const targetHash = hashFile(requirementsPath);
70800
- if (!force && existsSync52(stampPath) && existsSync52(pythonBin)) {
70801
- const existingHash = readFileSync48(stampPath, "utf8").trim();
71088
+ if (!force && existsSync54(stampPath) && existsSync54(pythonBin)) {
71089
+ const existingHash = readFileSync49(stampPath, "utf8").trim();
70802
71090
  if (existingHash === targetHash) {
70803
71091
  return {
70804
71092
  skillName,
@@ -70810,10 +71098,10 @@ function ensurePythonEnv(opts) {
70810
71098
  };
70811
71099
  }
70812
71100
  }
70813
- if (existsSync52(venvDir)) {
71101
+ if (existsSync54(venvDir)) {
70814
71102
  rmSync13(venvDir, { recursive: true, force: true });
70815
71103
  }
70816
- mkdirSync29(dirname15(venvDir), { recursive: true });
71104
+ mkdirSync30(dirname15(venvDir), { recursive: true });
70817
71105
  try {
70818
71106
  execFileSync14(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
70819
71107
  } catch (err) {
@@ -70845,17 +71133,17 @@ function ensurePythonEnv(opts) {
70845
71133
  }
70846
71134
 
70847
71135
  // src/deps/node.ts
70848
- import { createHash as createHash10 } from "node:crypto";
71136
+ import { createHash as createHash11 } from "node:crypto";
70849
71137
  import {
70850
71138
  copyFileSync as copyFileSync9,
70851
- existsSync as existsSync53,
70852
- mkdirSync as mkdirSync30,
70853
- readFileSync as readFileSync49,
71139
+ existsSync as existsSync55,
71140
+ mkdirSync as mkdirSync31,
71141
+ readFileSync as readFileSync50,
70854
71142
  rmSync as rmSync14,
70855
71143
  writeFileSync as writeFileSync27
70856
71144
  } from "node:fs";
70857
- import { dirname as dirname16, join as join48 } from "node:path";
70858
- import { homedir as homedir27 } from "node:os";
71145
+ import { dirname as dirname16, join as join50 } from "node:path";
71146
+ import { homedir as homedir28 } from "node:os";
70859
71147
  import { execFileSync as execFileSync15 } from "node:child_process";
70860
71148
 
70861
71149
  class NodeEnvError extends Error {
@@ -70878,23 +71166,23 @@ var LOCKFILES_FOR = {
70878
71166
  npm: ["package-lock.json"]
70879
71167
  };
70880
71168
  function defaultNodeCacheRoot() {
70881
- return join48(homedir27(), ".switchroom", "deps", "node");
71169
+ return join50(homedir28(), ".switchroom", "deps", "node");
70882
71170
  }
70883
71171
  function hashDepInputs(packageJsonPath) {
70884
71172
  const sourceDir = dirname16(packageJsonPath);
70885
- const hasher = createHash10("sha256");
71173
+ const hasher = createHash11("sha256");
70886
71174
  hasher.update(`package.json
70887
71175
  `);
70888
- hasher.update(readFileSync49(packageJsonPath));
71176
+ hasher.update(readFileSync50(packageJsonPath));
70889
71177
  for (const lockName of ALL_LOCKFILES) {
70890
- const lockPath = join48(sourceDir, lockName);
70891
- if (existsSync53(lockPath)) {
71178
+ const lockPath = join50(sourceDir, lockName);
71179
+ if (existsSync55(lockPath)) {
70892
71180
  hasher.update(`
70893
71181
  `);
70894
71182
  hasher.update(lockName);
70895
71183
  hasher.update(`
70896
71184
  `);
70897
- hasher.update(readFileSync49(lockPath));
71185
+ hasher.update(readFileSync50(lockPath));
70898
71186
  }
70899
71187
  }
70900
71188
  return hasher.digest("hex");
@@ -70903,17 +71191,17 @@ function ensureNodeEnv(opts) {
70903
71191
  const { skillName, packageJsonPath, force = false } = opts;
70904
71192
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
70905
71193
  const installer = opts.installer ?? "bun";
70906
- if (!existsSync53(packageJsonPath)) {
71194
+ if (!existsSync55(packageJsonPath)) {
70907
71195
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
70908
71196
  }
70909
71197
  const sourceDir = dirname16(packageJsonPath);
70910
- const envDir = join48(cacheRoot, skillName);
70911
- const stampPath = join48(envDir, ".package.sha256");
70912
- const nodeModulesDir = join48(envDir, "node_modules");
70913
- const binDir = join48(nodeModulesDir, ".bin");
71198
+ const envDir = join50(cacheRoot, skillName);
71199
+ const stampPath = join50(envDir, ".package.sha256");
71200
+ const nodeModulesDir = join50(envDir, "node_modules");
71201
+ const binDir = join50(nodeModulesDir, ".bin");
70914
71202
  const targetHash = hashDepInputs(packageJsonPath);
70915
- if (!force && existsSync53(stampPath) && existsSync53(nodeModulesDir)) {
70916
- const existingHash = readFileSync49(stampPath, "utf8").trim();
71203
+ if (!force && existsSync55(stampPath) && existsSync55(nodeModulesDir)) {
71204
+ const existingHash = readFileSync50(stampPath, "utf8").trim();
70917
71205
  if (existingHash === targetHash) {
70918
71206
  return {
70919
71207
  skillName,
@@ -70924,16 +71212,16 @@ function ensureNodeEnv(opts) {
70924
71212
  };
70925
71213
  }
70926
71214
  }
70927
- if (existsSync53(envDir)) {
71215
+ if (existsSync55(envDir)) {
70928
71216
  rmSync14(envDir, { recursive: true, force: true });
70929
71217
  }
70930
- mkdirSync30(envDir, { recursive: true });
70931
- copyFileSync9(packageJsonPath, join48(envDir, "package.json"));
71218
+ mkdirSync31(envDir, { recursive: true });
71219
+ copyFileSync9(packageJsonPath, join50(envDir, "package.json"));
70932
71220
  let copiedLockfile = false;
70933
71221
  for (const lockName of LOCKFILES_FOR[installer]) {
70934
- const lockPath = join48(sourceDir, lockName);
70935
- if (existsSync53(lockPath)) {
70936
- copyFileSync9(lockPath, join48(envDir, lockName));
71222
+ const lockPath = join50(sourceDir, lockName);
71223
+ if (existsSync55(lockPath)) {
71224
+ copyFileSync9(lockPath, join50(envDir, lockName));
70937
71225
  copiedLockfile = true;
70938
71226
  }
70939
71227
  }
@@ -70962,28 +71250,28 @@ function ensureNodeEnv(opts) {
70962
71250
 
70963
71251
  // src/cli/deps.ts
70964
71252
  function builtinSkillsRoot() {
70965
- return resolve34(homedir28(), ".switchroom/skills/_bundled");
71253
+ return resolve34(homedir29(), ".switchroom/skills/_bundled");
70966
71254
  }
70967
71255
  function registerDepsCommand(program3) {
70968
71256
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
70969
71257
  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) => {
70970
71258
  const skillsRoot = builtinSkillsRoot();
70971
- if (!existsSync54(skillsRoot)) {
71259
+ if (!existsSync56(skillsRoot)) {
70972
71260
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
70973
71261
  process.exit(1);
70974
71262
  }
70975
- const skillDir = join49(skillsRoot, skill);
70976
- if (!existsSync54(skillDir)) {
71263
+ const skillDir = join51(skillsRoot, skill);
71264
+ if (!existsSync56(skillDir)) {
70977
71265
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
70978
71266
  process.exit(1);
70979
71267
  }
70980
- const requirementsPath = join49(skillDir, "requirements.txt");
70981
- const packageJsonPath = join49(skillDir, "package.json");
70982
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync54(requirementsPath));
70983
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync54(packageJsonPath));
71268
+ const requirementsPath = join51(skillDir, "requirements.txt");
71269
+ const packageJsonPath = join51(skillDir, "package.json");
71270
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync56(requirementsPath));
71271
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync56(packageJsonPath));
70984
71272
  let did = 0;
70985
71273
  if (wantPython) {
70986
- if (!existsSync54(requirementsPath)) {
71274
+ if (!existsSync56(requirementsPath)) {
70987
71275
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
70988
71276
  process.exit(1);
70989
71277
  }
@@ -71007,7 +71295,7 @@ function registerDepsCommand(program3) {
71007
71295
  }
71008
71296
  }
71009
71297
  if (wantNode) {
71010
- if (!existsSync54(packageJsonPath)) {
71298
+ if (!existsSync56(packageJsonPath)) {
71011
71299
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
71012
71300
  process.exit(1);
71013
71301
  }
@@ -71040,7 +71328,7 @@ function registerDepsCommand(program3) {
71040
71328
  // src/cli/workspace.ts
71041
71329
  init_helpers();
71042
71330
  init_loader();
71043
- import { existsSync as existsSync55 } from "node:fs";
71331
+ import { existsSync as existsSync57 } from "node:fs";
71044
71332
  import { resolve as resolve35, sep as sep2 } from "node:path";
71045
71333
  import { spawnSync as spawnSync9 } from "node:child_process";
71046
71334
 
@@ -71817,7 +72105,7 @@ function registerWorkspaceCommand(program3) {
71817
72105
  if (!dir)
71818
72106
  return;
71819
72107
  const gitDir = resolve35(dir, ".git");
71820
- if (!existsSync55(gitDir)) {
72108
+ if (!existsSync57(gitDir)) {
71821
72109
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
71822
72110
  `);
71823
72111
  return;
@@ -71871,7 +72159,7 @@ function registerWorkspaceCommand(program3) {
71871
72159
  if (!dir)
71872
72160
  return;
71873
72161
  const gitDir = resolve35(dir, ".git");
71874
- if (!existsSync55(gitDir)) {
72162
+ if (!existsSync57(gitDir)) {
71875
72163
  process.stdout.write(`Workspace is not a git repository.
71876
72164
  `);
71877
72165
  return;
@@ -71896,7 +72184,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
71896
72184
  const agentsDir = resolveAgentsDir(config);
71897
72185
  const agentDir = resolve35(agentsDir, agentName);
71898
72186
  const dir = resolveAgentWorkspaceDir(agentDir);
71899
- if (!existsSync55(dir)) {
72187
+ if (!existsSync57(dir)) {
71900
72188
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
71901
72189
  `);
71902
72190
  return;
@@ -71932,8 +72220,8 @@ function safeParseInt(value, fallback) {
71932
72220
  init_helpers();
71933
72221
  init_loader();
71934
72222
  init_merge();
71935
- import { copyFileSync as copyFileSync10, existsSync as existsSync56, readFileSync as readFileSync50, writeFileSync as writeFileSync28 } from "node:fs";
71936
- import { join as join50, resolve as resolve36 } from "node:path";
72223
+ import { copyFileSync as copyFileSync10, existsSync as existsSync58, readFileSync as readFileSync51, writeFileSync as writeFileSync28 } from "node:fs";
72224
+ import { join as join52, resolve as resolve36 } from "node:path";
71937
72225
  init_schema();
71938
72226
  function resolveSoulTargetOrExit(program3, agentName) {
71939
72227
  const config = getConfig(program3);
@@ -71948,7 +72236,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
71948
72236
  const agentsDir = resolveAgentsDir(config);
71949
72237
  const agentDir = resolve36(agentsDir, agentName);
71950
72238
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
71951
- if (!existsSync56(workspaceDir)) {
72239
+ if (!existsSync58(workspaceDir)) {
71952
72240
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
71953
72241
  process.exit(1);
71954
72242
  }
@@ -71957,7 +72245,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
71957
72245
  profileName,
71958
72246
  profilePath,
71959
72247
  workspaceDir,
71960
- soulPath: join50(workspaceDir, "SOUL.md"),
72248
+ soulPath: join52(workspaceDir, "SOUL.md"),
71961
72249
  soul: merged.soul
71962
72250
  };
71963
72251
  }
@@ -71974,11 +72262,11 @@ function registerSoulCommand(program3) {
71974
72262
  const t = resolveSoulTargetOrExit(program3, agentName);
71975
72263
  if (!t)
71976
72264
  return;
71977
- if (!existsSync56(t.soulPath)) {
72265
+ if (!existsSync58(t.soulPath)) {
71978
72266
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
71979
72267
  process.exit(1);
71980
72268
  }
71981
- process.stdout.write(readFileSync50(t.soulPath, "utf-8"));
72269
+ process.stdout.write(readFileSync51(t.soulPath, "utf-8"));
71982
72270
  }));
71983
72271
  cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
71984
72272
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -71989,7 +72277,7 @@ function registerSoulCommand(program3) {
71989
72277
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
71990
72278
  process.exit(1);
71991
72279
  }
71992
- const exists = existsSync56(t.soulPath);
72280
+ const exists = existsSync58(t.soulPath);
71993
72281
  if (exists && !opts.yes) {
71994
72282
  if (!isInteractive()) {
71995
72283
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -72004,7 +72292,7 @@ function registerSoulCommand(program3) {
72004
72292
  let backupPath;
72005
72293
  if (exists) {
72006
72294
  backupPath = `${t.soulPath}.bak`;
72007
- if (existsSync56(backupPath)) {
72295
+ if (existsSync58(backupPath)) {
72008
72296
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
72009
72297
  }
72010
72298
  copyFileSync10(t.soulPath, backupPath);
@@ -72023,9 +72311,9 @@ function registerSoulCommand(program3) {
72023
72311
  // src/cli/debug.ts
72024
72312
  init_helpers();
72025
72313
  init_loader();
72026
- import { existsSync as existsSync57, readFileSync as readFileSync51, readdirSync as readdirSync19, statSync as statSync24 } from "node:fs";
72027
- import { resolve as resolve37, join as join51 } from "node:path";
72028
- import { createHash as createHash11 } from "node:crypto";
72314
+ import { existsSync as existsSync59, readFileSync as readFileSync52, readdirSync as readdirSync20, statSync as statSync25 } from "node:fs";
72315
+ import { resolve as resolve37, join as join53 } from "node:path";
72316
+ import { createHash as createHash12 } from "node:crypto";
72029
72317
  init_merge();
72030
72318
  init_hindsight();
72031
72319
  function formatBytes(bytes) {
@@ -72035,23 +72323,23 @@ function estimateTokens(bytes) {
72035
72323
  return Math.round(bytes / 4);
72036
72324
  }
72037
72325
  function sha256(content) {
72038
- return createHash11("sha256").update(content).digest("hex").slice(0, 16);
72326
+ return createHash12("sha256").update(content).digest("hex").slice(0, 16);
72039
72327
  }
72040
72328
  function findLatestTranscriptJsonl(claudeConfigDir) {
72041
- const projectsDir = join51(claudeConfigDir, "projects");
72042
- if (!existsSync57(projectsDir))
72329
+ const projectsDir = join53(claudeConfigDir, "projects");
72330
+ if (!existsSync59(projectsDir))
72043
72331
  return;
72044
72332
  try {
72045
- const entries = readdirSync19(projectsDir, { withFileTypes: true });
72333
+ const entries = readdirSync20(projectsDir, { withFileTypes: true });
72046
72334
  let latest;
72047
72335
  for (const entry of entries) {
72048
72336
  if (!entry.isDirectory())
72049
72337
  continue;
72050
- const projectPath = join51(projectsDir, entry.name);
72051
- const transcriptPath = join51(projectPath, "transcript.jsonl");
72052
- if (!existsSync57(transcriptPath))
72338
+ const projectPath = join53(projectsDir, entry.name);
72339
+ const transcriptPath = join53(projectPath, "transcript.jsonl");
72340
+ if (!existsSync59(transcriptPath))
72053
72341
  continue;
72054
- const stat3 = statSync24(transcriptPath);
72342
+ const stat3 = statSync25(transcriptPath);
72055
72343
  if (!latest || stat3.mtimeMs > latest.mtime) {
72056
72344
  latest = { path: transcriptPath, mtime: stat3.mtimeMs };
72057
72345
  }
@@ -72063,7 +72351,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
72063
72351
  }
72064
72352
  function extractLatestUserMessage(transcriptPath) {
72065
72353
  try {
72066
- const content = readFileSync51(transcriptPath, "utf-8");
72354
+ const content = readFileSync52(transcriptPath, "utf-8");
72067
72355
  const lines = content.trim().split(`
72068
72356
  `).filter(Boolean);
72069
72357
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -72112,16 +72400,16 @@ function registerDebugCommand(program3) {
72112
72400
  }
72113
72401
  const agentsDir = resolveAgentsDir(config);
72114
72402
  const agentDir = resolve37(agentsDir, agentName);
72115
- if (!existsSync57(agentDir)) {
72403
+ if (!existsSync59(agentDir)) {
72116
72404
  console.error(`Agent directory not found: ${agentDir}`);
72117
72405
  process.exit(1);
72118
72406
  }
72119
72407
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
72120
- const claudeConfigDir = join51(agentDir, ".claude");
72121
- const claudeMdPath = join51(agentDir, "CLAUDE.md");
72122
- const soulMdPath = join51(agentDir, "SOUL.md");
72123
- const workspaceSoulMdPath = join51(workspaceDir, "SOUL.md");
72124
- const handoffPath = join51(agentDir, ".handoff.md");
72408
+ const claudeConfigDir = join53(agentDir, ".claude");
72409
+ const claudeMdPath = join53(agentDir, "CLAUDE.md");
72410
+ const soulMdPath = join53(agentDir, "SOUL.md");
72411
+ const workspaceSoulMdPath = join53(workspaceDir, "SOUL.md");
72412
+ const handoffPath = join53(agentDir, ".handoff.md");
72125
72413
  const lastN = parseInt(opts.last, 10);
72126
72414
  if (isNaN(lastN) || lastN < 1) {
72127
72415
  console.error("--last must be a positive integer");
@@ -72167,7 +72455,7 @@ function registerDebugCommand(program3) {
72167
72455
  }
72168
72456
  console.log(`=== Append System Prompt (per-session) ===
72169
72457
  `);
72170
- const handoffContent = existsSync57(handoffPath) ? readFileSync51(handoffPath, "utf-8") : "";
72458
+ const handoffContent = existsSync59(handoffPath) ? readFileSync52(handoffPath, "utf-8") : "";
72171
72459
  if (handoffContent.trim().length > 0) {
72172
72460
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
72173
72461
  console.log(handoffContent);
@@ -72178,7 +72466,7 @@ function registerDebugCommand(program3) {
72178
72466
  }
72179
72467
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
72180
72468
  `);
72181
- const claudeMdContent = existsSync57(claudeMdPath) ? readFileSync51(claudeMdPath, "utf-8") : "";
72469
+ const claudeMdContent = existsSync59(claudeMdPath) ? readFileSync52(claudeMdPath, "utf-8") : "";
72182
72470
  if (claudeMdContent.trim().length > 0) {
72183
72471
  console.log(`(${formatBytes(claudeMdContent.length)})`);
72184
72472
  console.log(claudeMdContent);
@@ -72189,7 +72477,7 @@ function registerDebugCommand(program3) {
72189
72477
  }
72190
72478
  console.log(`=== Persona (SOUL.md) ===
72191
72479
  `);
72192
- const soulMdContent = existsSync57(soulMdPath) ? readFileSync51(soulMdPath, "utf-8") : existsSync57(workspaceSoulMdPath) ? readFileSync51(workspaceSoulMdPath, "utf-8") : "";
72480
+ const soulMdContent = existsSync59(soulMdPath) ? readFileSync52(soulMdPath, "utf-8") : existsSync59(workspaceSoulMdPath) ? readFileSync52(workspaceSoulMdPath, "utf-8") : "";
72193
72481
  if (soulMdContent.trim().length > 0) {
72194
72482
  console.log(`(${formatBytes(soulMdContent.length)})`);
72195
72483
  console.log(soulMdContent);
@@ -72270,31 +72558,31 @@ init_source();
72270
72558
 
72271
72559
  // src/worktree/claim.ts
72272
72560
  import { execFileSync as execFileSync16 } from "node:child_process";
72273
- import { closeSync as closeSync11, mkdirSync as mkdirSync32, openSync as openSync11, existsSync as existsSync59, unlinkSync as unlinkSync12 } from "node:fs";
72274
- import { join as join53, resolve as resolve39 } from "node:path";
72275
- import { homedir as homedir30 } from "node:os";
72276
- import { randomBytes as randomBytes11 } from "node:crypto";
72561
+ import { closeSync as closeSync12, mkdirSync as mkdirSync33, openSync as openSync12, existsSync as existsSync61, unlinkSync as unlinkSync13 } from "node:fs";
72562
+ import { join as join55, resolve as resolve39 } from "node:path";
72563
+ import { homedir as homedir31 } from "node:os";
72564
+ import { randomBytes as randomBytes12 } from "node:crypto";
72277
72565
 
72278
72566
  // src/worktree/registry.ts
72279
72567
  import {
72280
- mkdirSync as mkdirSync31,
72568
+ mkdirSync as mkdirSync32,
72281
72569
  writeFileSync as writeFileSync29,
72282
- readFileSync as readFileSync52,
72283
- readdirSync as readdirSync20,
72284
- unlinkSync as unlinkSync11,
72285
- existsSync as existsSync58,
72286
- renameSync as renameSync11
72570
+ readFileSync as readFileSync53,
72571
+ readdirSync as readdirSync21,
72572
+ unlinkSync as unlinkSync12,
72573
+ existsSync as existsSync60,
72574
+ renameSync as renameSync12
72287
72575
  } from "node:fs";
72288
- import { join as join52, resolve as resolve38 } from "node:path";
72289
- import { homedir as homedir29 } from "node:os";
72576
+ import { join as join54, resolve as resolve38 } from "node:path";
72577
+ import { homedir as homedir30 } from "node:os";
72290
72578
  function registryDir() {
72291
- return resolve38(process.env.SWITCHROOM_WORKTREE_DIR ?? join52(homedir29(), ".switchroom", "worktrees"));
72579
+ return resolve38(process.env.SWITCHROOM_WORKTREE_DIR ?? join54(homedir30(), ".switchroom", "worktrees"));
72292
72580
  }
72293
72581
  function recordPath(id) {
72294
- return join52(registryDir(), `${id}.json`);
72582
+ return join54(registryDir(), `${id}.json`);
72295
72583
  }
72296
72584
  function ensureDir2() {
72297
- mkdirSync31(registryDir(), { recursive: true });
72585
+ mkdirSync32(registryDir(), { recursive: true });
72298
72586
  }
72299
72587
  function writeRecord(record2) {
72300
72588
  ensureDir2();
@@ -72302,12 +72590,12 @@ function writeRecord(record2) {
72302
72590
  const tmp = `${target}.tmp${process.pid}`;
72303
72591
  writeFileSync29(tmp, JSON.stringify(record2, null, 2) + `
72304
72592
  `, { mode: 384 });
72305
- renameSync11(tmp, target);
72593
+ renameSync12(tmp, target);
72306
72594
  }
72307
72595
  function readRecord(id) {
72308
72596
  const path7 = recordPath(id);
72309
72597
  try {
72310
- const raw = readFileSync52(path7, "utf8");
72598
+ const raw = readFileSync53(path7, "utf8");
72311
72599
  return JSON.parse(raw);
72312
72600
  } catch {
72313
72601
  return null;
@@ -72316,14 +72604,14 @@ function readRecord(id) {
72316
72604
  function deleteRecord(id) {
72317
72605
  const path7 = recordPath(id);
72318
72606
  try {
72319
- unlinkSync11(path7);
72607
+ unlinkSync12(path7);
72320
72608
  } catch {}
72321
72609
  }
72322
72610
  function listRecords() {
72323
72611
  ensureDir2();
72324
72612
  const dir = registryDir();
72325
72613
  const records = [];
72326
- for (const entry of readdirSync20(dir)) {
72614
+ for (const entry of readdirSync21(dir)) {
72327
72615
  if (!entry.endsWith(".json"))
72328
72616
  continue;
72329
72617
  const id = entry.slice(0, -5);
@@ -72340,14 +72628,14 @@ function countByRepo(repoPath) {
72340
72628
  // src/worktree/claim.ts
72341
72629
  function acquireRepoLock(repoPath) {
72342
72630
  const lockDir = registryDir();
72343
- mkdirSync32(lockDir, { recursive: true });
72631
+ mkdirSync33(lockDir, { recursive: true });
72344
72632
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
72345
- const lockPath = join53(lockDir, `.lock-${lockName}`);
72633
+ const lockPath = join55(lockDir, `.lock-${lockName}`);
72346
72634
  const deadline = Date.now() + 5000;
72347
72635
  let fd = null;
72348
72636
  while (fd === null) {
72349
72637
  try {
72350
- fd = openSync11(lockPath, "wx");
72638
+ fd = openSync12(lockPath, "wx");
72351
72639
  } catch (err) {
72352
72640
  if (err.code !== "EEXIST")
72353
72641
  throw err;
@@ -72360,19 +72648,19 @@ function acquireRepoLock(repoPath) {
72360
72648
  }
72361
72649
  return () => {
72362
72650
  try {
72363
- closeSync11(fd);
72651
+ closeSync12(fd);
72364
72652
  } catch {}
72365
72653
  try {
72366
- unlinkSync12(lockPath);
72654
+ unlinkSync13(lockPath);
72367
72655
  } catch {}
72368
72656
  };
72369
72657
  }
72370
72658
  var DEFAULT_CONCURRENCY = 5;
72371
72659
  function worktreesBaseDir() {
72372
- return resolve39(process.env.SWITCHROOM_WORKTREE_BASE ?? join53(homedir30(), ".switchroom", "worktree-checkouts"));
72660
+ return resolve39(process.env.SWITCHROOM_WORKTREE_BASE ?? join55(homedir31(), ".switchroom", "worktree-checkouts"));
72373
72661
  }
72374
72662
  function shortId() {
72375
- return randomBytes11(4).toString("hex");
72663
+ return randomBytes12(4).toString("hex");
72376
72664
  }
72377
72665
  function sanitizeTaskName(name) {
72378
72666
  return name.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -72391,12 +72679,12 @@ function resolveRepoPath(repo, codeRepos) {
72391
72679
  }
72392
72680
  function expandHome(p) {
72393
72681
  if (p.startsWith("~/"))
72394
- return join53(homedir30(), p.slice(2));
72682
+ return join55(homedir31(), p.slice(2));
72395
72683
  return p;
72396
72684
  }
72397
72685
  async function claimWorktree(input, codeRepos) {
72398
72686
  const repoPath = resolveRepoPath(input.repo, codeRepos);
72399
- if (!existsSync59(repoPath)) {
72687
+ if (!existsSync61(repoPath)) {
72400
72688
  throw new Error(`Repository path does not exist: ${repoPath}`);
72401
72689
  }
72402
72690
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -72418,8 +72706,8 @@ async function claimWorktree(input, codeRepos) {
72418
72706
  const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
72419
72707
  branch = `task/${taskSuffix}-${id}`;
72420
72708
  const baseDir = worktreesBaseDir();
72421
- mkdirSync32(baseDir, { recursive: true });
72422
- worktreePath = join53(baseDir, `${id}-${taskSuffix}`);
72709
+ mkdirSync33(baseDir, { recursive: true });
72710
+ worktreePath = join55(baseDir, `${id}-${taskSuffix}`);
72423
72711
  const now = new Date().toISOString();
72424
72712
  const record2 = {
72425
72713
  id,
@@ -72450,7 +72738,7 @@ async function claimWorktree(input, codeRepos) {
72450
72738
 
72451
72739
  // src/worktree/release.ts
72452
72740
  import { execFileSync as execFileSync17 } from "node:child_process";
72453
- import { existsSync as existsSync60 } from "node:fs";
72741
+ import { existsSync as existsSync62 } from "node:fs";
72454
72742
  function releaseWorktree(input) {
72455
72743
  const { id } = input;
72456
72744
  const record2 = readRecord(id);
@@ -72458,7 +72746,7 @@ function releaseWorktree(input) {
72458
72746
  return { released: true };
72459
72747
  }
72460
72748
  let gitSuccess = true;
72461
- if (existsSync60(record2.path)) {
72749
+ if (existsSync62(record2.path)) {
72462
72750
  try {
72463
72751
  execFileSync17("git", ["worktree", "remove", "--force", record2.path], {
72464
72752
  cwd: record2.repo,
@@ -72497,7 +72785,7 @@ function listWorktrees() {
72497
72785
 
72498
72786
  // src/worktree/reaper.ts
72499
72787
  import { execFileSync as execFileSync18 } from "node:child_process";
72500
- import { existsSync as existsSync61 } from "node:fs";
72788
+ import { existsSync as existsSync63 } from "node:fs";
72501
72789
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
72502
72790
  function isPathInUse(path7) {
72503
72791
  try {
@@ -72524,7 +72812,7 @@ function hasUncommittedChanges(repoPath, worktreePath) {
72524
72812
  function reapRecord(record2) {
72525
72813
  const { id, path: path7, repo, branch, ownerAgent } = record2;
72526
72814
  let warning = null;
72527
- if (existsSync61(path7)) {
72815
+ if (existsSync63(path7)) {
72528
72816
  if (hasUncommittedChanges(repo, path7)) {
72529
72817
  warning = `[worktree-reaper] Reaped worktree with uncommitted changes: ` + `id=${id} branch=${branch} agent=${ownerAgent ?? "unknown"} path=${path7}`;
72530
72818
  }
@@ -72545,7 +72833,7 @@ function runReaper(nowMs) {
72545
72833
  const warnings = [];
72546
72834
  for (const record2 of records) {
72547
72835
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
72548
- const worktreeExists = existsSync61(record2.path);
72836
+ const worktreeExists = existsSync63(record2.path);
72549
72837
  if (!worktreeExists) {
72550
72838
  deleteRecord(record2.id);
72551
72839
  reaped.push(record2.id);
@@ -72669,12 +72957,12 @@ init_drive();
72669
72957
  init_scaffold_integration();
72670
72958
  import {
72671
72959
  chmodSync as chmodSync9,
72672
- mkdirSync as mkdirSync33,
72673
- readdirSync as readdirSync21,
72960
+ mkdirSync as mkdirSync34,
72961
+ readdirSync as readdirSync22,
72674
72962
  rmSync as rmSync15,
72675
72963
  writeFileSync as writeFileSync30
72676
72964
  } from "node:fs";
72677
- import { join as join54 } from "node:path";
72965
+ import { join as join56 } from "node:path";
72678
72966
  function encodeCredentialsFilename(email) {
72679
72967
  const SAFE = new Set([
72680
72968
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -72838,16 +73126,16 @@ function resolveCredentialsDir(env2) {
72838
73126
  if (explicit && explicit.length > 0)
72839
73127
  return explicit;
72840
73128
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
72841
- return join54(stateBase, "google-workspace-mcp", "credentials");
73129
+ return join56(stateBase, "google-workspace-mcp", "credentials");
72842
73130
  }
72843
73131
  function writeSeedFile(dir, email, seed) {
72844
- mkdirSync33(dir, { recursive: true, mode: 448 });
73132
+ mkdirSync34(dir, { recursive: true, mode: 448 });
72845
73133
  chmodSync9(dir, 448);
72846
- for (const name of readdirSync21(dir)) {
72847
- rmSync15(join54(dir, name), { force: true, recursive: true });
73134
+ for (const name of readdirSync22(dir)) {
73135
+ rmSync15(join56(dir, name), { force: true, recursive: true });
72848
73136
  }
72849
73137
  const filename = encodeCredentialsFilename(email);
72850
- const filePath = join54(dir, filename);
73138
+ const filePath = join56(dir, filename);
72851
73139
  writeFileSync30(filePath, JSON.stringify(seed), { mode: 384 });
72852
73140
  chmodSync9(filePath, 384);
72853
73141
  return filePath;
@@ -73000,7 +73288,7 @@ function registerDriveMcpLauncherCommand(program3) {
73000
73288
 
73001
73289
  // src/cli/apply.ts
73002
73290
  init_source();
73003
- import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync65, mkdirSync as mkdirSync35, readdirSync as readdirSync23, renameSync as renameSync12, writeFileSync as writeFileSync32 } from "node:fs";
73291
+ import { accessSync as accessSync3, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync67, mkdirSync as mkdirSync36, readdirSync as readdirSync24, renameSync as renameSync13, writeFileSync as writeFileSync32 } from "node:fs";
73004
73292
  import { mkdir, writeFile } from "node:fs/promises";
73005
73293
  import { spawnSync as childSpawnSync } from "node:child_process";
73006
73294
  import readline from "node:readline";
@@ -73345,16 +73633,16 @@ agents:
73345
73633
 
73346
73634
  // src/cli/apply.ts
73347
73635
  init_resolver();
73348
- import { dirname as dirname19, join as join58, resolve as resolve41 } from "node:path";
73349
- import { homedir as homedir32 } from "node:os";
73636
+ import { dirname as dirname19, join as join60, resolve as resolve41 } from "node:path";
73637
+ import { homedir as homedir33 } from "node:os";
73350
73638
  import { execFileSync as execFileSync19 } from "node:child_process";
73351
73639
  init_vault();
73352
73640
  init_loader();
73353
73641
  init_loader();
73354
73642
 
73355
73643
  // src/cli/update-prompt-hook.ts
73356
- import { existsSync as existsSync62, readFileSync as readFileSync53, writeFileSync as writeFileSync31, chmodSync as chmodSync10, mkdirSync as mkdirSync34 } from "node:fs";
73357
- import { join as join55 } from "node:path";
73644
+ import { existsSync as existsSync64, readFileSync as readFileSync54, writeFileSync as writeFileSync31, chmodSync as chmodSync10, mkdirSync as mkdirSync35 } from "node:fs";
73645
+ import { join as join57 } from "node:path";
73358
73646
  var HOOK_FILENAME = "update-card-on-prompt.sh";
73359
73647
  function updatePromptHookScript() {
73360
73648
  return `#!/bin/bash
@@ -73420,12 +73708,12 @@ exit 0
73420
73708
  `;
73421
73709
  }
73422
73710
  function installUpdatePromptHook(agentDir) {
73423
- const hooksDir = join55(agentDir, ".claude", "hooks");
73424
- mkdirSync34(hooksDir, { recursive: true });
73425
- const scriptPath = join55(hooksDir, HOOK_FILENAME);
73711
+ const hooksDir = join57(agentDir, ".claude", "hooks");
73712
+ mkdirSync35(hooksDir, { recursive: true });
73713
+ const scriptPath = join57(hooksDir, HOOK_FILENAME);
73426
73714
  const desired = updatePromptHookScript();
73427
73715
  let installed = false;
73428
- const existing = existsSync62(scriptPath) ? readFileSync53(scriptPath, "utf-8") : "";
73716
+ const existing = existsSync64(scriptPath) ? readFileSync54(scriptPath, "utf-8") : "";
73429
73717
  if (existing !== desired) {
73430
73718
  writeFileSync31(scriptPath, desired, { mode: 493 });
73431
73719
  chmodSync10(scriptPath, 493);
@@ -73435,11 +73723,11 @@ function installUpdatePromptHook(agentDir) {
73435
73723
  chmodSync10(scriptPath, 493);
73436
73724
  } catch {}
73437
73725
  }
73438
- const settingsPath = join55(agentDir, ".claude", "settings.json");
73439
- if (!existsSync62(settingsPath)) {
73726
+ const settingsPath = join57(agentDir, ".claude", "settings.json");
73727
+ if (!existsSync64(settingsPath)) {
73440
73728
  return { scriptPath, settingsPath, installed };
73441
73729
  }
73442
- const raw = readFileSync53(settingsPath, "utf-8");
73730
+ const raw = readFileSync54(settingsPath, "utf-8");
73443
73731
  let parsed;
73444
73732
  try {
73445
73733
  parsed = JSON.parse(raw);
@@ -73555,13 +73843,13 @@ function detectInstallType() {
73555
73843
  // src/cli/operator-uid.ts
73556
73844
  import {
73557
73845
  chownSync as chownSync2,
73558
- existsSync as existsSync64,
73846
+ existsSync as existsSync66,
73559
73847
  lstatSync as lstatSync7,
73560
- readdirSync as readdirSync22,
73848
+ readdirSync as readdirSync23,
73561
73849
  realpathSync as realpathSync6,
73562
- statSync as statSync25
73850
+ statSync as statSync26
73563
73851
  } from "node:fs";
73564
- import { join as join57 } from "node:path";
73852
+ import { join as join59 } from "node:path";
73565
73853
  function resolveOperatorUid() {
73566
73854
  const sudoUid = process.env.SUDO_UID;
73567
73855
  if (sudoUid !== undefined) {
@@ -73577,19 +73865,19 @@ function resolveOperatorUid() {
73577
73865
  return;
73578
73866
  }
73579
73867
  function operatorOwnedPaths(home2) {
73580
- const root = join57(home2, ".switchroom");
73868
+ const root = join59(home2, ".switchroom");
73581
73869
  return [
73582
- join57(root, "vault"),
73583
- join57(root, "vault-auto-unlock"),
73584
- join57(root, "vault-audit.log"),
73585
- join57(root, "host-control-audit.log"),
73586
- join57(root, "accounts"),
73587
- join57(root, "compose")
73870
+ join59(root, "vault"),
73871
+ join59(root, "vault-auto-unlock"),
73872
+ join59(root, "vault-audit.log"),
73873
+ join59(root, "host-control-audit.log"),
73874
+ join59(root, "accounts"),
73875
+ join59(root, "compose")
73588
73876
  ];
73589
73877
  }
73590
73878
  function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
73591
73879
  const chown = deps.chown ?? ((p, u, g) => chownSync2(p, u, g));
73592
- const exists = deps.exists ?? ((p) => existsSync64(p));
73880
+ const exists = deps.exists ?? ((p) => existsSync66(p));
73593
73881
  const isSymlink = deps.isSymlink ?? ((p) => {
73594
73882
  try {
73595
73883
  return lstatSync7(p).isSymbolicLink();
@@ -73599,7 +73887,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
73599
73887
  });
73600
73888
  const isDir = deps.isDir ?? ((p) => {
73601
73889
  try {
73602
- return statSync25(p).isDirectory();
73890
+ return statSync26(p).isDirectory();
73603
73891
  } catch {
73604
73892
  return false;
73605
73893
  }
@@ -73613,7 +73901,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
73613
73901
  });
73614
73902
  const readdir2 = deps.readdir ?? ((p) => {
73615
73903
  try {
73616
- return readdirSync22(p);
73904
+ return readdirSync23(p);
73617
73905
  } catch {
73618
73906
  return [];
73619
73907
  }
@@ -73633,7 +73921,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
73633
73921
  } catch {}
73634
73922
  if (isDir(target)) {
73635
73923
  for (const entry of readdir2(target)) {
73636
- visit(join57(target, entry));
73924
+ visit(join59(target, entry));
73637
73925
  }
73638
73926
  }
73639
73927
  };
@@ -73647,19 +73935,19 @@ var EMBEDDED_EXAMPLES = {
73647
73935
  switchroom: switchroom_default,
73648
73936
  minimal: minimal_default
73649
73937
  };
73650
- var DEFAULT_COMPOSE_PATH2 = join58(homedir32(), ".switchroom", "compose", "docker-compose.yml");
73938
+ var DEFAULT_COMPOSE_PATH2 = join60(homedir33(), ".switchroom", "compose", "docker-compose.yml");
73651
73939
  var COMPOSE_PROJECT2 = "switchroom";
73652
73940
  function resolveVaultBindMountDir(homeDir, ctx) {
73653
73941
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
73654
73942
  if (isCustomPath && ctx.customVaultPath) {
73655
73943
  return dirname19(ctx.customVaultPath);
73656
73944
  }
73657
- return join58(homeDir, ".switchroom", "vault");
73945
+ return join60(homeDir, ".switchroom", "vault");
73658
73946
  }
73659
73947
  function inspectVaultBindMountDir(vaultDir) {
73660
- if (!existsSync65(vaultDir))
73948
+ if (!existsSync67(vaultDir))
73661
73949
  return { kind: "missing" };
73662
- const entries = readdirSync23(vaultDir);
73950
+ const entries = readdirSync24(vaultDir);
73663
73951
  const unknown = [];
73664
73952
  for (const name of entries) {
73665
73953
  if (KNOWN_VAULT_ARTIFACT_NAMES.has(name))
@@ -73683,32 +73971,32 @@ function hasVaultRefs(value) {
73683
73971
  return false;
73684
73972
  }
73685
73973
  async function ensureHostMountSources(config) {
73686
- const home2 = homedir32();
73974
+ const home2 = homedir33();
73687
73975
  const dirs = [
73688
- join58(home2, ".switchroom", "approvals"),
73689
- join58(home2, ".switchroom", "scheduler"),
73690
- join58(home2, ".switchroom", "logs"),
73691
- join58(home2, ".switchroom", "compose"),
73692
- join58(home2, ".switchroom", "broker-operator")
73976
+ join60(home2, ".switchroom", "approvals"),
73977
+ join60(home2, ".switchroom", "scheduler"),
73978
+ join60(home2, ".switchroom", "logs"),
73979
+ join60(home2, ".switchroom", "compose"),
73980
+ join60(home2, ".switchroom", "broker-operator")
73693
73981
  ];
73694
73982
  for (const name of Object.keys(config.agents)) {
73695
- dirs.push(join58(home2, ".switchroom", "agents", name));
73696
- dirs.push(join58(home2, ".switchroom", "logs", name));
73697
- dirs.push(join58(home2, ".claude", "projects", name));
73983
+ dirs.push(join60(home2, ".switchroom", "agents", name));
73984
+ dirs.push(join60(home2, ".switchroom", "logs", name));
73985
+ dirs.push(join60(home2, ".claude", "projects", name));
73698
73986
  }
73699
73987
  for (const dir of dirs) {
73700
73988
  await mkdir(dir, { recursive: true });
73701
73989
  }
73702
- const autoUnlockPath = join58(home2, ".switchroom", "vault-auto-unlock");
73703
- if (!existsSync65(autoUnlockPath)) {
73990
+ const autoUnlockPath = join60(home2, ".switchroom", "vault-auto-unlock");
73991
+ if (!existsSync67(autoUnlockPath)) {
73704
73992
  writeFileSync32(autoUnlockPath, "", { mode: 384 });
73705
73993
  }
73706
- const auditLogPath = join58(home2, ".switchroom", "vault-audit.log");
73707
- if (!existsSync65(auditLogPath)) {
73994
+ const auditLogPath = join60(home2, ".switchroom", "vault-audit.log");
73995
+ if (!existsSync67(auditLogPath)) {
73708
73996
  writeFileSync32(auditLogPath, "", { mode: 420 });
73709
73997
  }
73710
- const hostdAuditLogPath = join58(home2, ".switchroom", "host-control-audit.log");
73711
- if (!existsSync65(hostdAuditLogPath)) {
73998
+ const hostdAuditLogPath = join60(home2, ".switchroom", "host-control-audit.log");
73999
+ if (!existsSync67(hostdAuditLogPath)) {
73712
74000
  writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
73713
74001
  }
73714
74002
  }
@@ -73729,7 +74017,7 @@ ${out.trim()}`;
73729
74017
  }
73730
74018
  function runApplyPreflight(config, opts = {}) {
73731
74019
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
73732
- if (hasVaultRefs(config) && !existsSync65(vaultPath)) {
74020
+ if (hasVaultRefs(config) && !existsSync67(vaultPath)) {
73733
74021
  throw new Error(`Config references vault keys (vault:<name>) but ${vaultPath} is missing. ` + `Run \`switchroom setup\` first to initialise the vault.`);
73734
74022
  }
73735
74023
  const detect = opts.detectComposeV2 ?? detectComposeV2;
@@ -73740,7 +74028,7 @@ function runApplyPreflight(config, opts = {}) {
73740
74028
  detectAndReportLegacyGdriveSlots(vaultPath);
73741
74029
  }
73742
74030
  function detectAndReportLegacyGdriveSlots(vaultPath) {
73743
- if (!existsSync65(vaultPath))
74031
+ if (!existsSync67(vaultPath))
73744
74032
  return;
73745
74033
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
73746
74034
  if (!passphrase)
@@ -73779,19 +74067,19 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
73779
74067
  `));
73780
74068
  }
73781
74069
  }
73782
- function writeInstallTypeCache(homeDir = homedir32()) {
74070
+ function writeInstallTypeCache(homeDir = homedir33()) {
73783
74071
  const ctx = detectInstallType();
73784
- const dir = join58(homeDir, ".switchroom");
73785
- const out = join58(dir, "install-type.json");
74072
+ const dir = join60(homeDir, ".switchroom");
74073
+ const out = join60(dir, "install-type.json");
73786
74074
  const tmp = `${out}.tmp`;
73787
- mkdirSync35(dir, { recursive: true });
74075
+ mkdirSync36(dir, { recursive: true });
73788
74076
  const payload = {
73789
74077
  install_type: ctx.install_type,
73790
74078
  detected_at: new Date().toISOString(),
73791
74079
  source_paths: ctx.source_paths
73792
74080
  };
73793
74081
  writeFileSync32(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
73794
- renameSync12(tmp, out);
74082
+ renameSync13(tmp, out);
73795
74083
  return out;
73796
74084
  }
73797
74085
  async function runApply(config, options, deps = {}, switchroomConfigPath) {
@@ -73831,14 +74119,14 @@ Applying switchroom config...
73831
74119
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
73832
74120
  `));
73833
74121
  try {
73834
- installUpdatePromptHook(join58(agentsDir, name));
74122
+ installUpdatePromptHook(join60(agentsDir, name));
73835
74123
  } catch (hookErr) {
73836
74124
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
73837
74125
  `));
73838
74126
  }
73839
74127
  try {
73840
74128
  const uid = allocateAgentUid(name);
73841
- alignAgentUid(name, join58(agentsDir, name), uid, {
74129
+ alignAgentUid(name, join60(agentsDir, name), uid, {
73842
74130
  confirm: !options.nonInteractive,
73843
74131
  writeOut
73844
74132
  });
@@ -73875,7 +74163,7 @@ Applying switchroom config...
73875
74163
  for (const name of agentNames) {
73876
74164
  try {
73877
74165
  const uid = allocateAgentUid(name);
73878
- alignAgentUid(name, join58(agentsDir, name), uid, {
74166
+ alignAgentUid(name, join60(agentsDir, name), uid, {
73879
74167
  confirm: !options.nonInteractive,
73880
74168
  writeOut
73881
74169
  });
@@ -73889,7 +74177,7 @@ Applying switchroom config...
73889
74177
  }
73890
74178
  const vaultPathConfigured = config.vault?.path;
73891
74179
  const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
73892
- const migrationResult = migrateVaultLayout(homedir32(), {
74180
+ const migrationResult = migrateVaultLayout(homedir33(), {
73893
74181
  customVaultPath
73894
74182
  });
73895
74183
  switch (migrationResult.kind) {
@@ -73915,7 +74203,7 @@ Applying switchroom config...
73915
74203
  writeErr(formatDivergentRecoveryMessage(migrationResult.details));
73916
74204
  process.exit(4);
73917
74205
  }
73918
- const postMigrationInspect = inspectVaultLayout(homedir32());
74206
+ const postMigrationInspect = inspectVaultLayout(homedir33());
73919
74207
  const acceptable = [
73920
74208
  "no-vault",
73921
74209
  "already-migrated",
@@ -73930,7 +74218,7 @@ Applying switchroom config...
73930
74218
  `));
73931
74219
  process.exit(5);
73932
74220
  }
73933
- const vaultDir = resolveVaultBindMountDir(homedir32(), {
74221
+ const vaultDir = resolveVaultBindMountDir(homedir33(), {
73934
74222
  migrationKind: migrationResult.kind,
73935
74223
  customVaultPath
73936
74224
  });
@@ -73960,7 +74248,7 @@ Applying switchroom config...
73960
74248
  imageTag: composeImageTag,
73961
74249
  buildMode: options.buildLocal ? "local" : "pull",
73962
74250
  buildContext: options.buildContext,
73963
- homeDir: homedir32(),
74251
+ homeDir: homedir33(),
73964
74252
  switchroomConfigPath,
73965
74253
  operatorUid
73966
74254
  });
@@ -73980,7 +74268,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
73980
74268
  writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
73981
74269
  `));
73982
74270
  if (process.geteuid?.() === 0 && operatorUid !== undefined) {
73983
- const restored = restoreOperatorOwnership(homedir32(), operatorUid);
74271
+ const restored = restoreOperatorOwnership(homedir33(), operatorUid);
73984
74272
  if (restored.length > 0) {
73985
74273
  writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
73986
74274
  `));
@@ -74028,7 +74316,7 @@ function copyExampleConfig2(name) {
74028
74316
  throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
74029
74317
  }
74030
74318
  const dest = resolve41(process.cwd(), "switchroom.yaml");
74031
- if (existsSync65(dest)) {
74319
+ if (existsSync67(dest)) {
74032
74320
  console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
74033
74321
  return;
74034
74322
  }
@@ -74039,7 +74327,7 @@ function copyExampleConfig2(name) {
74039
74327
  return;
74040
74328
  }
74041
74329
  const exampleFile = resolve41(import.meta.dirname, `../../examples/${name}.yaml`);
74042
- if (!existsSync65(exampleFile)) {
74330
+ if (!existsSync67(exampleFile)) {
74043
74331
  throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
74044
74332
  }
74045
74333
  copyFileSync11(exampleFile, dest);
@@ -74050,8 +74338,8 @@ function findUnwritableAgentDirs(config, opts) {
74050
74338
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
74051
74339
  const unwritable = [];
74052
74340
  for (const name of targets) {
74053
- const startSh = join58(agentsDir, name, "start.sh");
74054
- if (!existsSync65(startSh))
74341
+ const startSh = join60(agentsDir, name, "start.sh");
74342
+ if (!existsSync67(startSh))
74055
74343
  continue;
74056
74344
  try {
74057
74345
  accessSync3(startSh, fsConstants6.W_OK);
@@ -74229,9 +74517,9 @@ function runRedactStdin() {
74229
74517
  }
74230
74518
 
74231
74519
  // src/cli/status-ask.ts
74232
- import { readFileSync as readFileSync54, existsSync as existsSync66, readdirSync as readdirSync24 } from "node:fs";
74233
- import { join as join59 } from "node:path";
74234
- import { homedir as homedir33 } from "node:os";
74520
+ import { readFileSync as readFileSync55, existsSync as existsSync68, readdirSync as readdirSync25 } from "node:fs";
74521
+ import { join as join61 } from "node:path";
74522
+ import { homedir as homedir34 } from "node:os";
74235
74523
 
74236
74524
  // src/status-ask/report.ts
74237
74525
  function parseJsonl(content) {
@@ -74505,7 +74793,7 @@ function runReport(opts) {
74505
74793
  for (const src of sources) {
74506
74794
  let content;
74507
74795
  try {
74508
- content = readFileSync54(src.path, "utf-8");
74796
+ content = readFileSync55(src.path, "utf-8");
74509
74797
  } catch (err) {
74510
74798
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
74511
74799
  `);
@@ -74552,7 +74840,7 @@ function runReport(opts) {
74552
74840
  function resolveSources(explicitPath) {
74553
74841
  if (explicitPath != null && explicitPath.trim() !== "") {
74554
74842
  const trimmed = explicitPath.trim();
74555
- if (!existsSync66(trimmed)) {
74843
+ if (!existsSync68(trimmed)) {
74556
74844
  process.stderr.write(`status-ask report: ${trimmed}: file not found
74557
74845
  `);
74558
74846
  process.exit(1);
@@ -74566,20 +74854,20 @@ function resolveSources(explicitPath) {
74566
74854
  const config = loadConfig();
74567
74855
  agentsDir = resolveAgentsDir(config);
74568
74856
  } catch {
74569
- agentsDir = join59(homedir33(), ".switchroom", "agents");
74857
+ agentsDir = join61(homedir34(), ".switchroom", "agents");
74570
74858
  }
74571
- if (!existsSync66(agentsDir))
74859
+ if (!existsSync68(agentsDir))
74572
74860
  return [];
74573
74861
  const sources = [];
74574
74862
  let entries;
74575
74863
  try {
74576
- entries = readdirSync24(agentsDir);
74864
+ entries = readdirSync25(agentsDir);
74577
74865
  } catch {
74578
74866
  return [];
74579
74867
  }
74580
74868
  for (const name of entries) {
74581
- const path8 = join59(agentsDir, name, "runtime-metrics.jsonl");
74582
- if (existsSync66(path8)) {
74869
+ const path8 = join61(agentsDir, name, "runtime-metrics.jsonl");
74870
+ if (existsSync68(path8)) {
74583
74871
  sources.push({ path: path8, agent: name });
74584
74872
  }
74585
74873
  }
@@ -74600,17 +74888,17 @@ function inferAgentFromPath(p) {
74600
74888
 
74601
74889
  // src/cli/agent-config.ts
74602
74890
  init_helpers();
74603
- import { join as join60 } from "node:path";
74604
- import { homedir as homedir34 } from "node:os";
74891
+ import { join as join62 } from "node:path";
74892
+ import { homedir as homedir35 } from "node:os";
74605
74893
  import {
74606
- existsSync as existsSync67,
74607
- mkdirSync as mkdirSync36,
74608
- appendFileSync as appendFileSync3,
74609
- readFileSync as readFileSync55
74894
+ existsSync as existsSync69,
74895
+ mkdirSync as mkdirSync37,
74896
+ appendFileSync as appendFileSync4,
74897
+ readFileSync as readFileSync56
74610
74898
  } from "node:fs";
74611
- var AUDIT_ROOT = join60(homedir34(), ".switchroom", "audit");
74899
+ var AUDIT_ROOT = join62(homedir35(), ".switchroom", "audit");
74612
74900
  function auditPathFor(agent) {
74613
- return join60(AUDIT_ROOT, agent, "agent-config.jsonl");
74901
+ return join62(AUDIT_ROOT, agent, "agent-config.jsonl");
74614
74902
  }
74615
74903
  function appendAudit(agent, cmd, args, exit, opts = {}) {
74616
74904
  const row = {
@@ -74624,10 +74912,10 @@ function appendAudit(agent, cmd, args, exit, opts = {}) {
74624
74912
  const path8 = opts.auditPath ?? auditPathFor(agent);
74625
74913
  const dir = path8.slice(0, path8.lastIndexOf("/"));
74626
74914
  try {
74627
- if (!existsSync67(dir)) {
74628
- mkdirSync36(dir, { recursive: true });
74915
+ if (!existsSync69(dir)) {
74916
+ mkdirSync37(dir, { recursive: true });
74629
74917
  }
74630
- appendFileSync3(path8, JSON.stringify(row) + `
74918
+ appendFileSync4(path8, JSON.stringify(row) + `
74631
74919
  `, { flag: "a" });
74632
74920
  } catch {}
74633
74921
  }
@@ -74636,7 +74924,7 @@ function isContainerContext(env2 = process.env, opts = {}) {
74636
74924
  return true;
74637
74925
  const probe2 = opts.dockerEnvPath ?? "/.dockerenv";
74638
74926
  try {
74639
- if (existsSync67(probe2))
74927
+ if (existsSync69(probe2))
74640
74928
  return true;
74641
74929
  } catch {}
74642
74930
  return false;
@@ -74697,11 +74985,11 @@ function getAgentSlice(config, agent) {
74697
74985
  }
74698
74986
  function readAuditTail(agent, limit, opts = {}) {
74699
74987
  const path8 = opts.auditPath ?? auditPathFor(agent);
74700
- if (!existsSync67(path8))
74988
+ if (!existsSync69(path8))
74701
74989
  return [];
74702
74990
  let raw;
74703
74991
  try {
74704
- raw = readFileSync55(path8, "utf-8");
74992
+ raw = readFileSync56(path8, "utf-8");
74705
74993
  } catch {
74706
74994
  return [];
74707
74995
  }
@@ -74856,61 +75144,61 @@ var import_yaml14 = __toESM(require_dist(), 1);
74856
75144
  // src/config/overlay-writer.ts
74857
75145
  init_paths();
74858
75146
  import {
74859
- closeSync as closeSync12,
74860
- existsSync as existsSync68,
74861
- fsyncSync as fsyncSync5,
74862
- mkdirSync as mkdirSync37,
74863
- openSync as openSync12,
74864
- readdirSync as readdirSync25,
74865
- readFileSync as readFileSync56,
74866
- renameSync as renameSync13,
74867
- statSync as statSync26,
74868
- unlinkSync as unlinkSync13,
74869
- writeSync as writeSync7
75147
+ closeSync as closeSync13,
75148
+ existsSync as existsSync70,
75149
+ fsyncSync as fsyncSync6,
75150
+ mkdirSync as mkdirSync38,
75151
+ openSync as openSync13,
75152
+ readdirSync as readdirSync26,
75153
+ readFileSync as readFileSync57,
75154
+ renameSync as renameSync14,
75155
+ statSync as statSync27,
75156
+ unlinkSync as unlinkSync14,
75157
+ writeSync as writeSync8
74870
75158
  } from "node:fs";
74871
- import { join as join61, resolve as resolve42 } from "node:path";
75159
+ import { join as join63, resolve as resolve42 } from "node:path";
74872
75160
  var STAGING_SUBDIR = ".staging";
74873
75161
  function overlayPathsFor(agent, opts = {}) {
74874
75162
  const base = opts.root ? resolve42(opts.root, agent) : resolve42(resolveDualPath(`~/.switchroom/agents/${agent}`));
74875
- const scheduleDir = join61(base, "schedule.d");
74876
- const scheduleStagingDir = join61(scheduleDir, STAGING_SUBDIR);
74877
- const skillsDir = join61(base, "skills.d");
74878
- const skillsStagingDir = join61(skillsDir, STAGING_SUBDIR);
75163
+ const scheduleDir = join63(base, "schedule.d");
75164
+ const scheduleStagingDir = join63(scheduleDir, STAGING_SUBDIR);
75165
+ const skillsDir = join63(base, "skills.d");
75166
+ const skillsStagingDir = join63(skillsDir, STAGING_SUBDIR);
74879
75167
  return {
74880
75168
  agentRoot: base,
74881
75169
  scheduleDir,
74882
75170
  scheduleStagingDir,
74883
75171
  skillsDir,
74884
75172
  skillsStagingDir,
74885
- lockPath: join61(base, ".lock"),
75173
+ lockPath: join63(base, ".lock"),
74886
75174
  stagingDir: scheduleStagingDir
74887
75175
  };
74888
75176
  }
74889
75177
  function ensureDirs(paths) {
74890
- mkdirSync37(paths.scheduleDir, { recursive: true });
74891
- mkdirSync37(paths.scheduleStagingDir, { recursive: true });
75178
+ mkdirSync38(paths.scheduleDir, { recursive: true });
75179
+ mkdirSync38(paths.scheduleStagingDir, { recursive: true });
74892
75180
  }
74893
75181
  function ensureSkillsDirs(paths) {
74894
- mkdirSync37(paths.skillsDir, { recursive: true });
74895
- mkdirSync37(paths.skillsStagingDir, { recursive: true });
75182
+ mkdirSync38(paths.skillsDir, { recursive: true });
75183
+ mkdirSync38(paths.skillsStagingDir, { recursive: true });
74896
75184
  }
74897
75185
  function withAgentLock(paths, fn) {
74898
- mkdirSync37(paths.agentRoot, { recursive: true });
75186
+ mkdirSync38(paths.agentRoot, { recursive: true });
74899
75187
  const start = Date.now();
74900
75188
  const TIMEOUT_MS = 5000;
74901
75189
  let fd = null;
74902
75190
  while (Date.now() - start < TIMEOUT_MS) {
74903
75191
  try {
74904
- fd = openSync12(paths.lockPath, "wx");
75192
+ fd = openSync13(paths.lockPath, "wx");
74905
75193
  break;
74906
75194
  } catch (err) {
74907
75195
  const e = err;
74908
75196
  if (e.code !== "EEXIST")
74909
75197
  throw err;
74910
75198
  try {
74911
- const age = Date.now() - statSync26(paths.lockPath).mtimeMs;
75199
+ const age = Date.now() - statSync27(paths.lockPath).mtimeMs;
74912
75200
  if (age > 30000) {
74913
- unlinkSync13(paths.lockPath);
75201
+ unlinkSync14(paths.lockPath);
74914
75202
  continue;
74915
75203
  }
74916
75204
  } catch {}
@@ -74925,10 +75213,10 @@ function withAgentLock(paths, fn) {
74925
75213
  return fn();
74926
75214
  } finally {
74927
75215
  try {
74928
- closeSync12(fd);
75216
+ closeSync13(fd);
74929
75217
  } catch {}
74930
75218
  try {
74931
- unlinkSync13(paths.lockPath);
75219
+ unlinkSync14(paths.lockPath);
74932
75220
  } catch {}
74933
75221
  }
74934
75222
  }
@@ -74936,16 +75224,16 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
74936
75224
  const paths = overlayPathsFor(agent, opts);
74937
75225
  return withAgentLock(paths, () => {
74938
75226
  ensureDirs(paths);
74939
- const stagingPath = join61(paths.scheduleStagingDir, `${slug}.yaml`);
74940
- const finalPath = join61(paths.scheduleDir, `${slug}.yaml`);
74941
- const fd = openSync12(stagingPath, "w", 384);
75227
+ const stagingPath = join63(paths.scheduleStagingDir, `${slug}.yaml`);
75228
+ const finalPath = join63(paths.scheduleDir, `${slug}.yaml`);
75229
+ const fd = openSync13(stagingPath, "w", 384);
74942
75230
  try {
74943
- writeSync7(fd, yamlText);
74944
- fsyncSync5(fd);
75231
+ writeSync8(fd, yamlText);
75232
+ fsyncSync6(fd);
74945
75233
  } finally {
74946
- closeSync12(fd);
75234
+ closeSync13(fd);
74947
75235
  }
74948
- renameSync13(stagingPath, finalPath);
75236
+ renameSync14(stagingPath, finalPath);
74949
75237
  return finalPath;
74950
75238
  });
74951
75239
  }
@@ -74953,40 +75241,40 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
74953
75241
  const paths = overlayPathsFor(agent, opts);
74954
75242
  return withAgentLock(paths, () => {
74955
75243
  ensureSkillsDirs(paths);
74956
- const stagingPath = join61(paths.skillsStagingDir, `${slug}.yaml`);
74957
- const finalPath = join61(paths.skillsDir, `${slug}.yaml`);
74958
- const fd = openSync12(stagingPath, "w", 384);
75244
+ const stagingPath = join63(paths.skillsStagingDir, `${slug}.yaml`);
75245
+ const finalPath = join63(paths.skillsDir, `${slug}.yaml`);
75246
+ const fd = openSync13(stagingPath, "w", 384);
74959
75247
  try {
74960
- writeSync7(fd, yamlText);
74961
- fsyncSync5(fd);
75248
+ writeSync8(fd, yamlText);
75249
+ fsyncSync6(fd);
74962
75250
  } finally {
74963
- closeSync12(fd);
75251
+ closeSync13(fd);
74964
75252
  }
74965
- renameSync13(stagingPath, finalPath);
75253
+ renameSync14(stagingPath, finalPath);
74966
75254
  return finalPath;
74967
75255
  });
74968
75256
  }
74969
75257
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
74970
75258
  const paths = overlayPathsFor(agent, opts);
74971
75259
  return withAgentLock(paths, () => {
74972
- const finalPath = join61(paths.skillsDir, `${slug}.yaml`);
74973
- if (!existsSync68(finalPath))
75260
+ const finalPath = join63(paths.skillsDir, `${slug}.yaml`);
75261
+ if (!existsSync70(finalPath))
74974
75262
  return false;
74975
- unlinkSync13(finalPath);
75263
+ unlinkSync14(finalPath);
74976
75264
  return true;
74977
75265
  });
74978
75266
  }
74979
75267
  function listSkillsOverlayEntries(agent, opts = {}) {
74980
75268
  const paths = overlayPathsFor(agent, opts);
74981
- if (!existsSync68(paths.skillsDir))
75269
+ if (!existsSync70(paths.skillsDir))
74982
75270
  return [];
74983
75271
  const out = [];
74984
- for (const name of readdirSync25(paths.skillsDir)) {
75272
+ for (const name of readdirSync26(paths.skillsDir)) {
74985
75273
  if (!/\.ya?ml$/i.test(name))
74986
75274
  continue;
74987
- const full = join61(paths.skillsDir, name);
75275
+ const full = join63(paths.skillsDir, name);
74988
75276
  try {
74989
- const raw = readFileSync56(full, "utf-8");
75277
+ const raw = readFileSync57(full, "utf-8");
74990
75278
  const slug = name.replace(/\.ya?ml$/i, "");
74991
75279
  out.push({ slug, path: full, raw });
74992
75280
  } catch {}
@@ -74996,24 +75284,24 @@ function listSkillsOverlayEntries(agent, opts = {}) {
74996
75284
  function deleteOverlayEntry(agent, slug, opts = {}) {
74997
75285
  const paths = overlayPathsFor(agent, opts);
74998
75286
  return withAgentLock(paths, () => {
74999
- const finalPath = join61(paths.scheduleDir, `${slug}.yaml`);
75000
- if (!existsSync68(finalPath))
75287
+ const finalPath = join63(paths.scheduleDir, `${slug}.yaml`);
75288
+ if (!existsSync70(finalPath))
75001
75289
  return false;
75002
- unlinkSync13(finalPath);
75290
+ unlinkSync14(finalPath);
75003
75291
  return true;
75004
75292
  });
75005
75293
  }
75006
75294
  function listOverlayEntries(agent, opts = {}) {
75007
75295
  const paths = overlayPathsFor(agent, opts);
75008
- if (!existsSync68(paths.scheduleDir))
75296
+ if (!existsSync70(paths.scheduleDir))
75009
75297
  return [];
75010
75298
  const out = [];
75011
- for (const name of readdirSync25(paths.scheduleDir)) {
75299
+ for (const name of readdirSync26(paths.scheduleDir)) {
75012
75300
  if (!/\.ya?ml$/i.test(name))
75013
75301
  continue;
75014
- const full = join61(paths.scheduleDir, name);
75302
+ const full = join63(paths.scheduleDir, name);
75015
75303
  try {
75016
- const raw = readFileSync56(full, "utf-8");
75304
+ const raw = readFileSync57(full, "utf-8");
75017
75305
  const slug = name.replace(/\.ya?ml$/i, "");
75018
75306
  out.push({ slug, path: full, raw });
75019
75307
  } catch {}
@@ -75155,38 +75443,38 @@ function reconcileAgentCronOnly(agent) {
75155
75443
 
75156
75444
  // src/cli/agent-config-pending.ts
75157
75445
  import {
75158
- closeSync as closeSync13,
75159
- existsSync as existsSync69,
75160
- fsyncSync as fsyncSync6,
75161
- mkdirSync as mkdirSync38,
75162
- openSync as openSync13,
75163
- readdirSync as readdirSync26,
75164
- readFileSync as readFileSync57,
75165
- renameSync as renameSync14,
75166
- unlinkSync as unlinkSync14,
75446
+ closeSync as closeSync14,
75447
+ existsSync as existsSync71,
75448
+ fsyncSync as fsyncSync7,
75449
+ mkdirSync as mkdirSync39,
75450
+ openSync as openSync14,
75451
+ readdirSync as readdirSync27,
75452
+ readFileSync as readFileSync58,
75453
+ renameSync as renameSync15,
75454
+ unlinkSync as unlinkSync15,
75167
75455
  writeFileSync as writeFileSync33,
75168
- writeSync as writeSync8
75456
+ writeSync as writeSync9
75169
75457
  } from "node:fs";
75170
- import { join as join62 } from "node:path";
75171
- import { randomBytes as randomBytes12 } from "node:crypto";
75458
+ import { join as join64 } from "node:path";
75459
+ import { randomBytes as randomBytes13 } from "node:crypto";
75172
75460
  var STAGE_ID_PREFIX = "cap_";
75173
75461
  function pendingDir(agent, opts = {}) {
75174
75462
  const paths = overlayPathsFor(agent, opts);
75175
- return join62(paths.scheduleDir, ".pending");
75463
+ return join64(paths.scheduleDir, ".pending");
75176
75464
  }
75177
75465
  function ensurePendingDir(agent, opts = {}) {
75178
75466
  const dir = pendingDir(agent, opts);
75179
- mkdirSync38(dir, { recursive: true });
75467
+ mkdirSync39(dir, { recursive: true });
75180
75468
  return dir;
75181
75469
  }
75182
75470
  function newStageId() {
75183
- return `${STAGE_ID_PREFIX}${randomBytes12(4).toString("hex")}`;
75471
+ return `${STAGE_ID_PREFIX}${randomBytes13(4).toString("hex")}`;
75184
75472
  }
75185
75473
  function stagePendingScheduleEntry(opts) {
75186
75474
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
75187
75475
  const stageId = opts.stageId ?? newStageId();
75188
- const yamlPath = join62(dir, `${stageId}.yaml`);
75189
- const metaPath = join62(dir, `${stageId}.meta.json`);
75476
+ const yamlPath = join64(dir, `${stageId}.yaml`);
75477
+ const metaPath = join64(dir, `${stageId}.meta.json`);
75190
75478
  const meta = {
75191
75479
  v: 1,
75192
75480
  stage_id: stageId,
@@ -75198,14 +75486,14 @@ function stagePendingScheduleEntry(opts) {
75198
75486
  };
75199
75487
  const yamlTmp = `${yamlPath}.tmp-${process.pid}`;
75200
75488
  {
75201
- const fd = openSync13(yamlTmp, "w", 384);
75489
+ const fd = openSync14(yamlTmp, "w", 384);
75202
75490
  try {
75203
- writeSync8(fd, opts.yamlText);
75204
- fsyncSync6(fd);
75491
+ writeSync9(fd, opts.yamlText);
75492
+ fsyncSync7(fd);
75205
75493
  } finally {
75206
- closeSync13(fd);
75494
+ closeSync14(fd);
75207
75495
  }
75208
- renameSync14(yamlTmp, yamlPath);
75496
+ renameSync15(yamlTmp, yamlPath);
75209
75497
  }
75210
75498
  writeFileSync33(metaPath, JSON.stringify(meta, null, 2) + `
75211
75499
  `, { mode: 384 });
@@ -75213,19 +75501,19 @@ function stagePendingScheduleEntry(opts) {
75213
75501
  }
75214
75502
  function listPendingScheduleEntries(agent, opts = {}) {
75215
75503
  const dir = pendingDir(agent, opts);
75216
- if (!existsSync69(dir))
75504
+ if (!existsSync71(dir))
75217
75505
  return [];
75218
75506
  const out = [];
75219
- for (const name of readdirSync26(dir).sort()) {
75507
+ for (const name of readdirSync27(dir).sort()) {
75220
75508
  if (!name.endsWith(".meta.json"))
75221
75509
  continue;
75222
75510
  const stageId = name.slice(0, -".meta.json".length);
75223
- const metaPath = join62(dir, name);
75224
- const yamlPath = join62(dir, `${stageId}.yaml`);
75225
- if (!existsSync69(yamlPath))
75511
+ const metaPath = join64(dir, name);
75512
+ const yamlPath = join64(dir, `${stageId}.yaml`);
75513
+ if (!existsSync71(yamlPath))
75226
75514
  continue;
75227
75515
  try {
75228
- const meta = JSON.parse(readFileSync57(metaPath, "utf-8"));
75516
+ const meta = JSON.parse(readFileSync58(metaPath, "utf-8"));
75229
75517
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
75230
75518
  continue;
75231
75519
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -75240,12 +75528,12 @@ function commitPendingScheduleEntry(opts) {
75240
75528
  return { committed: false, reason: "not_found" };
75241
75529
  const slug = match.meta.entry.name ?? match.stageId;
75242
75530
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
75243
- const finalPath = join62(paths.scheduleDir, `${slug}.yaml`);
75244
- if (existsSync69(finalPath)) {
75531
+ const finalPath = join64(paths.scheduleDir, `${slug}.yaml`);
75532
+ if (existsSync71(finalPath)) {
75245
75533
  return { committed: false, reason: "slug_collision" };
75246
75534
  }
75247
- renameSync14(match.yamlPath, finalPath);
75248
- unlinkSync14(match.metaPath);
75535
+ renameSync15(match.yamlPath, finalPath);
75536
+ unlinkSync15(match.metaPath);
75249
75537
  return { committed: true, path: finalPath, slug };
75250
75538
  }
75251
75539
  function denyPendingScheduleEntry(opts) {
@@ -75254,16 +75542,16 @@ function denyPendingScheduleEntry(opts) {
75254
75542
  if (!match)
75255
75543
  return { denied: false, reason: "not_found" };
75256
75544
  try {
75257
- unlinkSync14(match.yamlPath);
75545
+ unlinkSync15(match.yamlPath);
75258
75546
  } catch {}
75259
75547
  try {
75260
- unlinkSync14(match.metaPath);
75548
+ unlinkSync15(match.metaPath);
75261
75549
  } catch {}
75262
75550
  return { denied: true };
75263
75551
  }
75264
75552
 
75265
75553
  // src/cli/agent-config-write.ts
75266
- import { existsSync as existsSync70, readFileSync as readFileSync58 } from "node:fs";
75554
+ import { existsSync as existsSync72, readFileSync as readFileSync59 } from "node:fs";
75267
75555
  var MAX_ENTRIES_PER_AGENT = 20;
75268
75556
  function checkOperatorContext(verb, env2 = process.env) {
75269
75557
  if (env2.SWITCHROOM_OPERATOR === "1")
@@ -75497,8 +75785,8 @@ function scheduleRemove(opts) {
75497
75785
  }
75498
75786
  let priorContent = null;
75499
75787
  try {
75500
- if (existsSync70(match.path))
75501
- priorContent = readFileSync58(match.path, "utf-8");
75788
+ if (existsSync72(match.path))
75789
+ priorContent = readFileSync59(match.path, "utf-8");
75502
75790
  } catch {}
75503
75791
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
75504
75792
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -75690,10 +75978,10 @@ function registerAgentConfigWriteCommands(program3) {
75690
75978
 
75691
75979
  // src/cli/agent-config-skill-write.ts
75692
75980
  var import_yaml15 = __toESM(require_dist(), 1);
75693
- import { existsSync as existsSync71 } from "node:fs";
75981
+ import { existsSync as existsSync73 } from "node:fs";
75694
75982
  init_reconcile_default_skills();
75695
75983
  var import_yaml16 = __toESM(require_dist(), 1);
75696
- import { join as join63 } from "node:path";
75984
+ import { join as join65 } from "node:path";
75697
75985
  var MAX_SKILLS_PER_AGENT = 20;
75698
75986
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
75699
75987
  function exitCodeFor2(code) {
@@ -75768,8 +76056,8 @@ function skillInstall(opts) {
75768
76056
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
75769
76057
  }
75770
76058
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
75771
- const skillPath = join63(poolDir, skillName);
75772
- if (!existsSync71(skillPath)) {
76059
+ const skillPath = join65(poolDir, skillName);
76060
+ if (!existsSync73(skillPath)) {
75773
76061
  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.`);
75774
76062
  }
75775
76063
  const yamlText = import_yaml15.stringify({ skills: [skillName] });
@@ -75953,27 +76241,27 @@ init_source();
75953
76241
  init_helpers();
75954
76242
  init_loader();
75955
76243
  import {
75956
- existsSync as existsSync73,
75957
- readdirSync as readdirSync27,
75958
- readFileSync as readFileSync60,
75959
- renameSync as renameSync15,
75960
- statSync as statSync27,
75961
- unlinkSync as unlinkSync15
76244
+ existsSync as existsSync75,
76245
+ readdirSync as readdirSync28,
76246
+ readFileSync as readFileSync61,
76247
+ renameSync as renameSync16,
76248
+ statSync as statSync28,
76249
+ unlinkSync as unlinkSync16
75962
76250
  } from "node:fs";
75963
- import { createHash as createHash12 } from "node:crypto";
75964
- import { join as join64 } from "node:path";
76251
+ import { createHash as createHash13 } from "node:crypto";
76252
+ import { join as join66 } from "node:path";
75965
76253
  function planCronUnitRenames(agentsDir, agents) {
75966
76254
  const plans = [];
75967
76255
  for (const [agentName, agentConfig] of Object.entries(agents)) {
75968
76256
  const schedule = agentConfig.schedule ?? [];
75969
76257
  if (schedule.length === 0)
75970
76258
  continue;
75971
- const telegramDir = join64(agentsDir, agentName, "telegram");
75972
- if (!existsSync73(telegramDir))
76259
+ const telegramDir = join66(agentsDir, agentName, "telegram");
76260
+ if (!existsSync75(telegramDir))
75973
76261
  continue;
75974
76262
  let entries;
75975
76263
  try {
75976
- entries = readdirSync27(telegramDir);
76264
+ entries = readdirSync28(telegramDir);
75977
76265
  } catch {
75978
76266
  continue;
75979
76267
  }
@@ -75990,8 +76278,8 @@ function planCronUnitRenames(agentsDir, agents) {
75990
76278
  continue;
75991
76279
  plans.push({
75992
76280
  agent: agentName,
75993
- from: join64(telegramDir, file),
75994
- to: join64(telegramDir, canonical),
76281
+ from: join66(telegramDir, file),
76282
+ to: join66(telegramDir, canonical),
75995
76283
  scheduleIdx: idx,
75996
76284
  entry
75997
76285
  });
@@ -76000,10 +76288,10 @@ function planCronUnitRenames(agentsDir, agents) {
76000
76288
  return plans;
76001
76289
  }
76002
76290
  function sha256File2(path8) {
76003
- return createHash12("sha256").update(readFileSync60(path8)).digest("hex");
76291
+ return createHash13("sha256").update(readFileSync61(path8)).digest("hex");
76004
76292
  }
76005
76293
  function renamePair(from, to, opts = {}) {
76006
- if (existsSync73(to)) {
76294
+ if (existsSync75(to)) {
76007
76295
  let identical = false;
76008
76296
  try {
76009
76297
  identical = sha256File2(from) === sha256File2(to);
@@ -76013,7 +76301,7 @@ function renamePair(from, to, opts = {}) {
76013
76301
  if (identical) {
76014
76302
  if (!opts.dryRun) {
76015
76303
  try {
76016
- unlinkSync15(from);
76304
+ unlinkSync16(from);
76017
76305
  } catch {}
76018
76306
  }
76019
76307
  return { kind: "deduped", legacy: from };
@@ -76021,13 +76309,13 @@ function renamePair(from, to, opts = {}) {
76021
76309
  return { kind: "skipped", reason: "target exists, legacy preserved", legacy: from };
76022
76310
  }
76023
76311
  if (!opts.dryRun)
76024
- renameSync15(from, to);
76312
+ renameSync16(from, to);
76025
76313
  return { kind: "renamed" };
76026
76314
  }
76027
76315
  function extractPromptFromLegacyScript(path8) {
76028
76316
  let body;
76029
76317
  try {
76030
- body = readFileSync60(path8, "utf-8");
76318
+ body = readFileSync61(path8, "utf-8");
76031
76319
  } catch {
76032
76320
  return null;
76033
76321
  }
@@ -76096,7 +76384,7 @@ function registerMigrateCommand(program3) {
76096
76384
  const fromSidecar = p.from.replace(/\.sh$/, ".source");
76097
76385
  const toSidecar = p.to.replace(/\.sh$/, ".source");
76098
76386
  let sidecarStatus = null;
76099
- if (existsSync73(fromSidecar) && statSync27(fromSidecar).isFile()) {
76387
+ if (existsSync75(fromSidecar) && statSync28(fromSidecar).isFile()) {
76100
76388
  sidecarStatus = renamePair(fromSidecar, toSidecar);
76101
76389
  }
76102
76390
  switch (status.kind) {
@@ -76128,9 +76416,9 @@ function registerMigrateCommand(program3) {
76128
76416
  // src/cli/hostd.ts
76129
76417
  init_source();
76130
76418
  init_helpers();
76131
- import { existsSync as existsSync74, mkdirSync as mkdirSync39, readdirSync as readdirSync28, readFileSync as readFileSync61, writeFileSync as writeFileSync34, statSync as statSync28, copyFileSync as copyFileSync12 } from "node:fs";
76132
- import { homedir as homedir35 } from "node:os";
76133
- import { join as join65 } from "node:path";
76419
+ import { existsSync as existsSync76, mkdirSync as mkdirSync40, readdirSync as readdirSync29, readFileSync as readFileSync62, writeFileSync as writeFileSync34, statSync as statSync29, copyFileSync as copyFileSync12 } from "node:fs";
76420
+ import { homedir as homedir36 } from "node:os";
76421
+ import { join as join67 } from "node:path";
76134
76422
  import { spawnSync as spawnSync11 } from "node:child_process";
76135
76423
  init_audit_reader();
76136
76424
  var DEFAULT_IMAGE_TAG = "latest";
@@ -76221,14 +76509,14 @@ networks:
76221
76509
  `;
76222
76510
  }
76223
76511
  function hostdDir() {
76224
- return join65(homedir35(), ".switchroom", "hostd");
76512
+ return join67(homedir36(), ".switchroom", "hostd");
76225
76513
  }
76226
76514
  function hostdComposePath() {
76227
- return join65(hostdDir(), "docker-compose.yml");
76515
+ return join67(hostdDir(), "docker-compose.yml");
76228
76516
  }
76229
76517
  function backupExistingCompose() {
76230
76518
  const p = hostdComposePath();
76231
- if (!existsSync74(p))
76519
+ if (!existsSync76(p))
76232
76520
  return null;
76233
76521
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
76234
76522
  const bak = `${p}.bak-${ts}`;
@@ -76261,9 +76549,9 @@ async function doInstall(opts, program3) {
76261
76549
  }
76262
76550
  const dir = hostdDir();
76263
76551
  const composePath = hostdComposePath();
76264
- mkdirSync39(dir, { recursive: true });
76552
+ mkdirSync40(dir, { recursive: true });
76265
76553
  const yaml = renderHostdComposeFile({
76266
- hostHome: homedir35(),
76554
+ hostHome: homedir36(),
76267
76555
  imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
76268
76556
  operatorUid: resolveOperatorUid()
76269
76557
  });
@@ -76305,7 +76593,7 @@ function doStatus() {
76305
76593
  const composeYml = hostdComposePath();
76306
76594
  console.log(source_default.bold("switchroom-hostd"));
76307
76595
  console.log("");
76308
- if (!existsSync74(composeYml)) {
76596
+ if (!existsSync76(composeYml)) {
76309
76597
  console.log(source_default.yellow(" compose: not installed"));
76310
76598
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
76311
76599
  return;
@@ -76326,15 +76614,15 @@ function doStatus() {
76326
76614
  } else {
76327
76615
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
76328
76616
  }
76329
- if (existsSync74(dir)) {
76617
+ if (existsSync76(dir)) {
76330
76618
  const entries = [];
76331
76619
  try {
76332
- for (const name of readdirSync28(dir)) {
76620
+ for (const name of readdirSync29(dir)) {
76333
76621
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
76334
76622
  continue;
76335
- const sockPath = join65(dir, name, "sock");
76336
- if (existsSync74(sockPath)) {
76337
- const st = statSync28(sockPath);
76623
+ const sockPath = join67(dir, name, "sock");
76624
+ if (existsSync76(sockPath)) {
76625
+ const st = statSync29(sockPath);
76338
76626
  if ((st.mode & 61440) === 49152) {
76339
76627
  entries.push(`${name} \u2192 ${sockPath}`);
76340
76628
  }
@@ -76352,7 +76640,7 @@ function doStatus() {
76352
76640
  }
76353
76641
  function doUninstall() {
76354
76642
  const composeYml = hostdComposePath();
76355
- if (!existsSync74(composeYml)) {
76643
+ if (!existsSync76(composeYml)) {
76356
76644
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
76357
76645
  return;
76358
76646
  }
@@ -76376,12 +76664,12 @@ function registerHostdCommand(program3) {
76376
76664
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
76377
76665
  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) => {
76378
76666
  const logPath = opts.path ?? defaultAuditLogPath2();
76379
- if (!existsSync74(logPath)) {
76667
+ if (!existsSync76(logPath)) {
76380
76668
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
76381
76669
  The log is created when hostd handles its first privileged-verb request.`));
76382
76670
  return;
76383
76671
  }
76384
- const raw = readFileSync61(logPath, "utf-8");
76672
+ const raw = readFileSync62(logPath, "utf-8");
76385
76673
  const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
76386
76674
  const filters = {
76387
76675
  agent: opts.agent,