switchroom 0.14.73 → 0.14.74

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.
@@ -28636,6 +28636,86 @@ function formatForCli(entries, opts = {}) {
28636
28636
  }
28637
28637
  var init_audit_reader = () => {};
28638
28638
 
28639
+ // src/host-control/client.ts
28640
+ import { connect as connect2 } from "node:net";
28641
+ async function hostdRequest(opts, req) {
28642
+ const timeoutMs = opts.timeoutMs ?? 5000;
28643
+ return new Promise((resolve27, reject) => {
28644
+ const socket = connect2(opts.socketPath);
28645
+ let buf = "";
28646
+ let settled = false;
28647
+ const timer = setTimeout(() => {
28648
+ if (settled)
28649
+ return;
28650
+ settled = true;
28651
+ socket.destroy();
28652
+ reject(new Error(`hostd: request timed out after ${timeoutMs}ms`));
28653
+ }, timeoutMs);
28654
+ socket.on("connect", () => {
28655
+ try {
28656
+ socket.write(encodeRequest3(req));
28657
+ } catch (err) {
28658
+ if (settled)
28659
+ return;
28660
+ settled = true;
28661
+ clearTimeout(timer);
28662
+ socket.destroy();
28663
+ reject(err);
28664
+ }
28665
+ });
28666
+ socket.on("data", (chunk) => {
28667
+ buf += chunk.toString("utf8");
28668
+ if (Buffer.byteLength(buf, "utf8") > MAX_FRAME_BYTES3 * 2) {
28669
+ if (settled)
28670
+ return;
28671
+ settled = true;
28672
+ clearTimeout(timer);
28673
+ socket.destroy();
28674
+ reject(new Error("hostd: response exceeded frame budget"));
28675
+ return;
28676
+ }
28677
+ const nl = buf.indexOf(`
28678
+ `);
28679
+ if (nl === -1)
28680
+ return;
28681
+ const line = buf.slice(0, nl);
28682
+ try {
28683
+ const resp = decodeResponse3(line);
28684
+ if (settled)
28685
+ return;
28686
+ settled = true;
28687
+ clearTimeout(timer);
28688
+ socket.end();
28689
+ resolve27(resp);
28690
+ } catch (err) {
28691
+ if (settled)
28692
+ return;
28693
+ settled = true;
28694
+ clearTimeout(timer);
28695
+ socket.destroy();
28696
+ reject(new Error(`hostd: bad response frame: ${err.message}`, { cause: err }));
28697
+ }
28698
+ });
28699
+ socket.on("error", (err) => {
28700
+ if (settled)
28701
+ return;
28702
+ settled = true;
28703
+ clearTimeout(timer);
28704
+ reject(err);
28705
+ });
28706
+ socket.on("close", () => {
28707
+ if (settled)
28708
+ return;
28709
+ settled = true;
28710
+ clearTimeout(timer);
28711
+ reject(new Error("hostd: connection closed before response"));
28712
+ });
28713
+ });
28714
+ }
28715
+ var init_client4 = __esm(() => {
28716
+ init_protocol3();
28717
+ });
28718
+
28639
28719
  // src/cli/doctor-status.ts
28640
28720
  function statusGlyph(status) {
28641
28721
  switch (status) {
@@ -28679,17 +28759,17 @@ var init_thinking_effort_risk = __esm(() => {
28679
28759
 
28680
28760
  // src/manifest.ts
28681
28761
  import {
28682
- existsSync as existsSync49,
28683
- readFileSync as readFileSync45,
28762
+ existsSync as existsSync50,
28763
+ readFileSync as readFileSync46,
28684
28764
  readdirSync as readdirSync18
28685
28765
  } from "node:fs";
28686
- import { dirname as dirname11, join as join42 } from "node:path";
28766
+ import { dirname as dirname11, join as join44 } from "node:path";
28687
28767
  import { execSync as execSync2 } from "node:child_process";
28688
28768
  function locateManifestPath() {
28689
28769
  let dir = import.meta.dirname;
28690
28770
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
28691
- const candidate = join42(dir, "dependencies.json");
28692
- if (existsSync49(candidate))
28771
+ const candidate = join44(dir, "dependencies.json");
28772
+ if (existsSync50(candidate))
28693
28773
  return candidate;
28694
28774
  dir = dirname11(dir);
28695
28775
  }
@@ -28702,7 +28782,7 @@ function loadManifest(manifestPath) {
28702
28782
  }
28703
28783
  let raw;
28704
28784
  try {
28705
- raw = readFileSync45(path4, "utf-8");
28785
+ raw = readFileSync46(path4, "utf-8");
28706
28786
  } catch (err) {
28707
28787
  throw new Error(`Failed to read manifest at ${path4}: ${err.message}`);
28708
28788
  }
@@ -28758,16 +28838,16 @@ function probeClaudeVersion() {
28758
28838
  }
28759
28839
  function probePlaywrightMcpVersion() {
28760
28840
  const home2 = process.env.HOME ?? "";
28761
- const npxCache = join42(home2, ".npm/_npx");
28762
- if (!existsSync49(npxCache))
28841
+ const npxCache = join44(home2, ".npm/_npx");
28842
+ if (!existsSync50(npxCache))
28763
28843
  return null;
28764
28844
  try {
28765
28845
  const entries = readdirSync18(npxCache);
28766
28846
  for (const entry of entries) {
28767
- const pkgPath = join42(npxCache, entry, "node_modules/@playwright/mcp/package.json");
28768
- if (existsSync49(pkgPath)) {
28847
+ const pkgPath = join44(npxCache, entry, "node_modules/@playwright/mcp/package.json");
28848
+ if (existsSync50(pkgPath)) {
28769
28849
  try {
28770
- const pkg = JSON.parse(readFileSync45(pkgPath, "utf-8"));
28850
+ const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
28771
28851
  if (pkg.version)
28772
28852
  return pkg.version;
28773
28853
  } catch {}
@@ -28859,7 +28939,7 @@ var init_manifest = __esm(() => {
28859
28939
  });
28860
28940
 
28861
28941
  // src/cli/doctor-docker.ts
28862
- import { readFileSync as readFileSync46 } from "node:fs";
28942
+ import { readFileSync as readFileSync47 } from "node:fs";
28863
28943
  function imageTagOf(ref) {
28864
28944
  if (!ref)
28865
28945
  return "<absent>";
@@ -28874,7 +28954,7 @@ function isDockerMode(opts) {
28874
28954
  return true;
28875
28955
  if (opts?.composePath) {
28876
28956
  try {
28877
- readFileSync46(opts.composePath, "utf8");
28957
+ readFileSync47(opts.composePath, "utf8");
28878
28958
  return true;
28879
28959
  } catch {}
28880
28960
  }
@@ -29063,14 +29143,14 @@ var init_doctor_docker = __esm(() => {
29063
29143
  });
29064
29144
 
29065
29145
  // src/cli/doctor-auth-broker.ts
29066
- import { existsSync as existsSync50, readFileSync as readFileSync47 } from "node:fs";
29146
+ import { existsSync as existsSync51, readFileSync as readFileSync48 } from "node:fs";
29067
29147
  import { createHash as createHash10 } from "node:crypto";
29068
- import { spawnSync as spawnSync5 } from "node:child_process";
29069
- import { homedir as homedir23 } from "node:os";
29070
- import { join as join43 } from "node:path";
29148
+ import { spawnSync as spawnSync6 } from "node:child_process";
29149
+ import { homedir as homedir24 } from "node:os";
29150
+ import { join as join45 } from "node:path";
29071
29151
  function defaultDockerInspect(container, format) {
29072
29152
  try {
29073
- const r = spawnSync5("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
29153
+ const r = spawnSync6("docker", ["inspect", "-f", format, container], { encoding: "utf-8", timeout: 5000 });
29074
29154
  if (r.status !== 0)
29075
29155
  return null;
29076
29156
  return r.stdout.trim();
@@ -29080,7 +29160,7 @@ function defaultDockerInspect(container, format) {
29080
29160
  }
29081
29161
  function defaultDockerExecExists(container, path4) {
29082
29162
  try {
29083
- const r = spawnSync5("docker", ["exec", container, "test", "-S", path4], { encoding: "utf-8", timeout: 5000 });
29163
+ const r = spawnSync6("docker", ["exec", container, "test", "-S", path4], { encoding: "utf-8", timeout: 5000 });
29084
29164
  return r.status === 0;
29085
29165
  } catch {
29086
29166
  return false;
@@ -29167,8 +29247,8 @@ function checkAuthBrokerPerAgentSockets(config, deps = {}) {
29167
29247
  }
29168
29248
  function checkAuthBrokerDrift(deps = {}) {
29169
29249
  const stateDir = resolveStateDir(deps);
29170
- const indexPath = join43(stateDir, "sha-index.json");
29171
- if (!existsSync50(indexPath)) {
29250
+ const indexPath = join45(stateDir, "sha-index.json");
29251
+ if (!existsSync51(indexPath)) {
29172
29252
  return {
29173
29253
  name: "auth-broker: drift",
29174
29254
  status: "ok",
@@ -29177,7 +29257,7 @@ function checkAuthBrokerDrift(deps = {}) {
29177
29257
  }
29178
29258
  let index;
29179
29259
  try {
29180
- index = JSON.parse(readFileSync47(indexPath, "utf-8"));
29260
+ index = JSON.parse(readFileSync48(indexPath, "utf-8"));
29181
29261
  } catch (err) {
29182
29262
  return {
29183
29263
  name: "auth-broker: drift",
@@ -29186,18 +29266,18 @@ function checkAuthBrokerDrift(deps = {}) {
29186
29266
  fix: "Inspect `~/.switchroom/state/auth-broker/sha-index.json` for corruption."
29187
29267
  };
29188
29268
  }
29189
- const home2 = deps.home ?? homedir23();
29269
+ const home2 = deps.home ?? homedir24();
29190
29270
  const divergent = [];
29191
29271
  const missingOnDisk = [];
29192
29272
  for (const [label, expected] of Object.entries(index)) {
29193
29273
  const credsPath = accountCredentialsPath(label, home2);
29194
- if (!existsSync50(credsPath)) {
29274
+ if (!existsSync51(credsPath)) {
29195
29275
  missingOnDisk.push(label);
29196
29276
  continue;
29197
29277
  }
29198
29278
  let got;
29199
29279
  try {
29200
- got = sha256Hex(readFileSync47(credsPath, "utf-8"));
29280
+ got = sha256Hex(readFileSync48(credsPath, "utf-8"));
29201
29281
  } catch (err) {
29202
29282
  divergent.push(`${label} (read failed: ${err.message})`);
29203
29283
  continue;
@@ -29228,8 +29308,8 @@ function checkAuthBrokerDrift(deps = {}) {
29228
29308
  }
29229
29309
  function checkAuthBrokerThresholdViolations(deps = {}) {
29230
29310
  const stateDir = resolveStateDir(deps);
29231
- const path4 = join43(stateDir, "threshold-violations.json");
29232
- if (!existsSync50(path4)) {
29311
+ const path4 = join45(stateDir, "threshold-violations.json");
29312
+ if (!existsSync51(path4)) {
29233
29313
  return {
29234
29314
  name: "auth-broker: threshold violations",
29235
29315
  status: "ok",
@@ -29238,7 +29318,7 @@ function checkAuthBrokerThresholdViolations(deps = {}) {
29238
29318
  }
29239
29319
  let violations;
29240
29320
  try {
29241
- violations = JSON.parse(readFileSync47(path4, "utf-8"));
29321
+ violations = JSON.parse(readFileSync48(path4, "utf-8"));
29242
29322
  } catch (err) {
29243
29323
  return {
29244
29324
  name: "auth-broker: threshold violations",
@@ -29272,9 +29352,9 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
29272
29352
  fix: "Run `switchroom auth use <label>` to pin a fleet-wide account, then `switchroom apply`. Without an active account every agent boot fails."
29273
29353
  };
29274
29354
  }
29275
- const home2 = deps.home ?? homedir23();
29355
+ const home2 = deps.home ?? homedir24();
29276
29356
  const dir = accountDir(active, home2);
29277
- if (!existsSync50(dir)) {
29357
+ if (!existsSync51(dir)) {
29278
29358
  return {
29279
29359
  name: "auth-broker: fleet active account",
29280
29360
  status: "fail",
@@ -29283,7 +29363,7 @@ function checkAuthBrokerActiveAccount(config, deps = {}) {
29283
29363
  };
29284
29364
  }
29285
29365
  const creds = accountCredentialsPath(active, home2);
29286
- if (!existsSync50(creds)) {
29366
+ if (!existsSync51(creds)) {
29287
29367
  return {
29288
29368
  name: "auth-broker: fleet active account",
29289
29369
  status: "fail",
@@ -29315,10 +29395,10 @@ var init_doctor_auth_broker = __esm(() => {
29315
29395
  });
29316
29396
 
29317
29397
  // src/cli/doctor-hostd.ts
29318
- import { spawnSync as spawnSync6 } from "node:child_process";
29398
+ import { spawnSync as spawnSync7 } from "node:child_process";
29319
29399
  function realDockerInspect(ref, format) {
29320
29400
  try {
29321
- const r = spawnSync6("docker", ["inspect", ref, "--format", format], {
29401
+ const r = spawnSync7("docker", ["inspect", ref, "--format", format], {
29322
29402
  encoding: "utf-8",
29323
29403
  timeout: 5000
29324
29404
  });
@@ -29420,17 +29500,17 @@ var init_doctor_hostd = () => {};
29420
29500
  import {
29421
29501
  accessSync,
29422
29502
  constants as fsConstants4,
29423
- existsSync as existsSync51,
29503
+ existsSync as existsSync52,
29424
29504
  realpathSync as realpathSync4,
29425
29505
  statSync as statSync21
29426
29506
  } from "node:fs";
29427
- import { userInfo, homedir as homedir24 } from "node:os";
29428
- import { join as join44 } from "node:path";
29507
+ import { userInfo, homedir as homedir25 } from "node:os";
29508
+ import { join as join46 } from "node:path";
29429
29509
  function resolveVaultPath2(config) {
29430
29510
  return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
29431
29511
  }
29432
29512
  function defaultStatVault(path4) {
29433
- if (!existsSync51(path4)) {
29513
+ if (!existsSync52(path4)) {
29434
29514
  return { exists: false, readable: false, uid: -1, mode: 0, realPath: path4 };
29435
29515
  }
29436
29516
  let real = path4;
@@ -29563,7 +29643,7 @@ async function runSecretAccessChecks(config, deps = {}) {
29563
29643
  };
29564
29644
  const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
29565
29645
  if (!passphrase) {
29566
- const sock = deps.brokerOperatorSocket ?? join44(homedir24(), ".switchroom", "broker-operator", "sock");
29646
+ const sock = deps.brokerOperatorSocket ?? join46(homedir25(), ".switchroom", "broker-operator", "sock");
29567
29647
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
29568
29648
  for (const name of Object.keys(config.agents ?? {})) {
29569
29649
  const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
@@ -29648,8 +29728,8 @@ import {
29648
29728
  existsSync as realExistsSync,
29649
29729
  readFileSync as realReadFileSync
29650
29730
  } from "node:fs";
29651
- import { join as join45, resolve as resolve30 } from "node:path";
29652
- import { homedir as homedir25 } from "node:os";
29731
+ import { join as join47, resolve as resolve30 } from "node:path";
29732
+ import { homedir as homedir26 } from "node:os";
29653
29733
  function resolveDeps(config, deps) {
29654
29734
  let agentsDir = deps.agentsDir;
29655
29735
  if (agentsDir === undefined) {
@@ -29772,8 +29852,8 @@ function checkScaffoldWiring(config, driveAgents, d) {
29772
29852
  });
29773
29853
  continue;
29774
29854
  }
29775
- const mcpPath = join45(agentDir, ".mcp.json");
29776
- const claudeJsonPath = join45(agentDir, ".claude", ".claude.json");
29855
+ const mcpPath = join47(agentDir, ".mcp.json");
29856
+ const claudeJsonPath = join47(agentDir, ".claude", ".claude.json");
29777
29857
  const mcpRead = readJson(d, mcpPath);
29778
29858
  const trustRead = readJson(d, claudeJsonPath);
29779
29859
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -29886,7 +29966,7 @@ async function runDriveBrokerReachabilityChecks(config, deps = {}) {
29886
29966
  }
29887
29967
  ];
29888
29968
  }
29889
- const sock = deps.brokerOperatorSocket ?? join45(homedir25(), ".switchroom", "broker-operator", "sock");
29969
+ const sock = deps.brokerOperatorSocket ?? join47(homedir26(), ".switchroom", "broker-operator", "sock");
29890
29970
  const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
29891
29971
  const results = [];
29892
29972
  for (const agent of driveAgents) {
@@ -29940,8 +30020,8 @@ import {
29940
30020
  readSync as realReadSync,
29941
30021
  closeSync as realCloseSync
29942
30022
  } from "node:fs";
29943
- import { join as join46 } from "node:path";
29944
- import { homedir as homedir26 } from "node:os";
30023
+ import { join as join48 } from "node:path";
30024
+ import { homedir as homedir27 } from "node:os";
29945
30025
  function defaultReadHead(p, n) {
29946
30026
  let fd;
29947
30027
  try {
@@ -29969,7 +30049,7 @@ function resolveDeps2(config, deps) {
29969
30049
  }
29970
30050
  }
29971
30051
  return {
29972
- homeDir: deps.homeDir ?? homedir26(),
30052
+ homeDir: deps.homeDir ?? homedir27(),
29973
30053
  agentsDir,
29974
30054
  existsSync: deps.existsSync ?? ((p) => realExistsSync2(p)),
29975
30055
  readFileSync: deps.readFileSync ?? ((p) => realReadFileSync2(p, "utf-8")),
@@ -29998,7 +30078,7 @@ function runWebkiteChecks(config, deps = {}) {
29998
30078
  return [];
29999
30079
  const d = resolveDeps2(config, deps);
30000
30080
  const results = [];
30001
- const binPath = join46(d.homeDir, ".switchroom", "bin", "webkite");
30081
+ const binPath = join48(d.homeDir, ".switchroom", "bin", "webkite");
30002
30082
  if (!d.existsSync(binPath)) {
30003
30083
  results.push({
30004
30084
  name: "webkite: binary",
@@ -30028,12 +30108,12 @@ function runWebkiteChecks(config, deps = {}) {
30028
30108
  });
30029
30109
  }
30030
30110
  }
30031
- const cloakDir = join46(d.homeDir, ".cloakbrowser");
30111
+ const cloakDir = join48(d.homeDir, ".cloakbrowser");
30032
30112
  let chromeFound = false;
30033
30113
  if (d.existsSync(cloakDir)) {
30034
30114
  try {
30035
30115
  for (const entry of d.readdirSync(cloakDir)) {
30036
- if (entry.startsWith("chromium-") && d.existsSync(join46(cloakDir, entry, "chrome"))) {
30116
+ if (entry.startsWith("chromium-") && d.existsSync(join48(cloakDir, entry, "chrome"))) {
30037
30117
  chromeFound = true;
30038
30118
  break;
30039
30119
  }
@@ -30059,9 +30139,9 @@ function runWebkiteChecks(config, deps = {}) {
30059
30139
  return results;
30060
30140
  }
30061
30141
  for (const agent of enabledAgents) {
30062
- const agentDir = join46(d.agentsDir, agent);
30063
- const settingsPath = join46(agentDir, ".claude", "settings.json");
30064
- const mcpPath = join46(agentDir, ".mcp.json");
30142
+ const agentDir = join48(d.agentsDir, agent);
30143
+ const settingsPath = join48(agentDir, ".claude", "settings.json");
30144
+ const mcpPath = join48(agentDir, ".mcp.json");
30065
30145
  if (!d.existsSync(settingsPath) && !d.existsSync(mcpPath)) {
30066
30146
  continue;
30067
30147
  }
@@ -30125,7 +30205,7 @@ var init_doctor_webkite = __esm(() => {
30125
30205
  });
30126
30206
 
30127
30207
  // src/cli/doctor-scaffold-wiring.ts
30128
- import { join as join47, resolve as resolve31 } from "node:path";
30208
+ import { join as join49, resolve as resolve31 } from "node:path";
30129
30209
  function readJson2(d, path4) {
30130
30210
  if (!d.existsSync(path4))
30131
30211
  return { kind: "absent" };
@@ -30168,8 +30248,8 @@ function checkIntegrationScaffoldWiring(args) {
30168
30248
  });
30169
30249
  continue;
30170
30250
  }
30171
- const mcpPath = join47(agentDir, ".mcp.json");
30172
- const claudeJsonPath = join47(agentDir, ".claude", ".claude.json");
30251
+ const mcpPath = join49(agentDir, ".mcp.json");
30252
+ const claudeJsonPath = join49(agentDir, ".claude", ".claude.json");
30173
30253
  const mcpRead = readJson2(deps, mcpPath);
30174
30254
  const trustRead = readJson2(deps, claudeJsonPath);
30175
30255
  if (mcpRead.kind === "unreadable" || trustRead.kind === "unreadable") {
@@ -30233,14 +30313,14 @@ import {
30233
30313
  existsSync as realExistsSync3,
30234
30314
  readFileSync as realReadFileSync3
30235
30315
  } from "node:fs";
30236
- import { join as join48 } from "node:path";
30237
- import { homedir as homedir27 } from "node:os";
30316
+ import { join as join50 } from "node:path";
30317
+ import { homedir as homedir28 } from "node:os";
30238
30318
  function resolveDeps3(deps) {
30239
- const home2 = deps.homeDir?.() ?? homedir27();
30319
+ const home2 = deps.homeDir?.() ?? homedir28();
30240
30320
  return {
30241
30321
  existsSync: deps.existsSync ?? realExistsSync3,
30242
30322
  readFileSync: deps.readFileSync ?? realReadFileSync3,
30243
- agentsDir: join48(home2, ".switchroom", "agents"),
30323
+ agentsDir: join50(home2, ".switchroom", "agents"),
30244
30324
  now: deps.now ?? Date.now
30245
30325
  };
30246
30326
  }
@@ -30323,7 +30403,7 @@ function checkOAuthClient2(config, anyAgentEnabled) {
30323
30403
  ];
30324
30404
  }
30325
30405
  function readHeartbeat(d, agentName) {
30326
- const path4 = join48(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
30406
+ const path4 = join50(d.agentsDir, agentName, "m365-launcher.heartbeat.json");
30327
30407
  if (!d.existsSync(path4)) {
30328
30408
  return { error: "heartbeat file missing \u2014 launcher has not yet started" };
30329
30409
  }
@@ -30420,15 +30500,15 @@ import {
30420
30500
  readFileSync as realReadFileSync4,
30421
30501
  statSync as realStatSync
30422
30502
  } from "node:fs";
30423
- import { join as join49 } from "node:path";
30424
- import { homedir as homedir28 } from "node:os";
30503
+ import { join as join51 } from "node:path";
30504
+ import { homedir as homedir29 } from "node:os";
30425
30505
  function resolveDeps4(deps) {
30426
- const home2 = deps.homeDir?.() ?? homedir28();
30506
+ const home2 = deps.homeDir?.() ?? homedir29();
30427
30507
  return {
30428
30508
  existsSync: deps.existsSync ?? realExistsSync4,
30429
30509
  readFileSync: deps.readFileSync ?? realReadFileSync4,
30430
30510
  statSync: deps.statSync ?? realStatSync,
30431
- agentsDir: join49(home2, ".switchroom", "agents"),
30511
+ agentsDir: join51(home2, ".switchroom", "agents"),
30432
30512
  now: deps.now ?? Date.now,
30433
30513
  vaultAclReader: deps.vaultAclReader ?? (async () => ({ kind: "unreachable", msg: "no default reader wired" }))
30434
30514
  };
@@ -30553,7 +30633,7 @@ function checkLauncherHeartbeat2(notionAgents, d) {
30553
30633
  return [];
30554
30634
  const results = [];
30555
30635
  for (const name of notionAgents) {
30556
- const heartbeatPath = join49(d.agentsDir, name, "notion-launcher.heartbeat.json");
30636
+ const heartbeatPath = join51(d.agentsDir, name, "notion-launcher.heartbeat.json");
30557
30637
  if (!d.existsSync(heartbeatPath)) {
30558
30638
  results.push({
30559
30639
  name: `notion:launcher-heartbeat:${name}`,
@@ -30628,11 +30708,11 @@ import {
30628
30708
  readdirSync as realReaddirSync2,
30629
30709
  statSync as realStatSync2
30630
30710
  } from "node:fs";
30631
- import { homedir as homedir29 } from "node:os";
30632
- import { join as join50 } from "node:path";
30711
+ import { homedir as homedir30 } from "node:os";
30712
+ import { join as join52 } from "node:path";
30633
30713
  function runCredentialsMigrationChecks(config, deps = {}) {
30634
- const credDir = deps.credentialsDir ?? join50(homedir29(), ".switchroom", "credentials");
30635
- const existsSync52 = deps.existsSync ?? ((p) => realExistsSync5(p));
30714
+ const credDir = deps.credentialsDir ?? join52(homedir30(), ".switchroom", "credentials");
30715
+ const existsSync53 = deps.existsSync ?? ((p) => realExistsSync5(p));
30636
30716
  const readdirSync20 = deps.readdirSync ?? ((p) => realReaddirSync2(p));
30637
30717
  const isDirectory = deps.isDirectory ?? ((p) => {
30638
30718
  try {
@@ -30641,7 +30721,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
30641
30721
  return false;
30642
30722
  }
30643
30723
  });
30644
- if (!existsSync52(credDir))
30724
+ if (!existsSync53(credDir))
30645
30725
  return [];
30646
30726
  const agentNames = new Set(Object.keys(config.agents ?? {}));
30647
30727
  let entries;
@@ -30659,7 +30739,7 @@ function runCredentialsMigrationChecks(config, deps = {}) {
30659
30739
  const flat = [];
30660
30740
  const perAgentDirs = [];
30661
30741
  for (const e of entries) {
30662
- const full = join50(credDir, e);
30742
+ const full = join52(credDir, e);
30663
30743
  if (isDirectory(full) && agentNames.has(e)) {
30664
30744
  perAgentDirs.push(e);
30665
30745
  } else {
@@ -30735,7 +30815,7 @@ function runInlinedSecretChecks(_config, deps = {}) {
30735
30815
  }
30736
30816
  let parsed;
30737
30817
  try {
30738
- parsed = import_yaml15.parse(raw);
30818
+ parsed = import_yaml17.parse(raw);
30739
30819
  } catch {
30740
30820
  return [
30741
30821
  {
@@ -30763,9 +30843,9 @@ function runInlinedSecretChecks(_config, deps = {}) {
30763
30843
  fix: `Move it to the per-agent-ACL'd vault: \`switchroom vault set <name>\` ` + `then replace the literal with \`vault:<name>\`. Rotate the exposed ` + `value if any agent may already have read it.`
30764
30844
  }));
30765
30845
  }
30766
- var import_yaml15, SECRET_KEY_EXACT;
30846
+ var import_yaml17, SECRET_KEY_EXACT;
30767
30847
  var init_doctor_inlined_secrets = __esm(() => {
30768
- import_yaml15 = __toESM(require_dist(), 1);
30848
+ import_yaml17 = __toESM(require_dist(), 1);
30769
30849
  SECRET_KEY_EXACT = new Set([
30770
30850
  "bot_token",
30771
30851
  "client_secret",
@@ -30782,19 +30862,19 @@ var init_doctor_inlined_secrets = __esm(() => {
30782
30862
 
30783
30863
  // src/cli/doctor-audit-integrity.ts
30784
30864
  import { readFileSync as fsReadFileSync2 } from "node:fs";
30785
- import { homedir as homedir30 } from "node:os";
30786
- import { join as join51 } from "node:path";
30865
+ import { homedir as homedir31 } from "node:os";
30866
+ import { join as join53 } from "node:path";
30787
30867
  function rootWrittenLogs(home2) {
30788
30868
  return [
30789
- { label: "vault-broker", path: join51(home2, ".switchroom", "vault-audit.log") },
30869
+ { label: "vault-broker", path: join53(home2, ".switchroom", "vault-audit.log") },
30790
30870
  {
30791
30871
  label: "hostd",
30792
- path: join51(home2, ".switchroom", "host-control-audit.log")
30872
+ path: join53(home2, ".switchroom", "host-control-audit.log")
30793
30873
  }
30794
30874
  ];
30795
30875
  }
30796
30876
  function runAuditIntegrityChecks(deps = {}) {
30797
- const home2 = deps.homeDir ?? homedir30();
30877
+ const home2 = deps.homeDir ?? homedir31();
30798
30878
  const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
30799
30879
  const results = [];
30800
30880
  for (const { label, path: path4 } of rootWrittenLogs(home2)) {
@@ -30850,97 +30930,17 @@ var init_doctor_audit_integrity = __esm(() => {
30850
30930
  init_audit_hashchain();
30851
30931
  });
30852
30932
 
30853
- // src/host-control/client.ts
30854
- import { connect as connect2 } from "node:net";
30855
- async function hostdRequest(opts, req) {
30856
- const timeoutMs = opts.timeoutMs ?? 5000;
30857
- return new Promise((resolve32, reject) => {
30858
- const socket = connect2(opts.socketPath);
30859
- let buf = "";
30860
- let settled = false;
30861
- const timer = setTimeout(() => {
30862
- if (settled)
30863
- return;
30864
- settled = true;
30865
- socket.destroy();
30866
- reject(new Error(`hostd: request timed out after ${timeoutMs}ms`));
30867
- }, timeoutMs);
30868
- socket.on("connect", () => {
30869
- try {
30870
- socket.write(encodeRequest3(req));
30871
- } catch (err) {
30872
- if (settled)
30873
- return;
30874
- settled = true;
30875
- clearTimeout(timer);
30876
- socket.destroy();
30877
- reject(err);
30878
- }
30879
- });
30880
- socket.on("data", (chunk) => {
30881
- buf += chunk.toString("utf8");
30882
- if (Buffer.byteLength(buf, "utf8") > MAX_FRAME_BYTES3 * 2) {
30883
- if (settled)
30884
- return;
30885
- settled = true;
30886
- clearTimeout(timer);
30887
- socket.destroy();
30888
- reject(new Error("hostd: response exceeded frame budget"));
30889
- return;
30890
- }
30891
- const nl = buf.indexOf(`
30892
- `);
30893
- if (nl === -1)
30894
- return;
30895
- const line = buf.slice(0, nl);
30896
- try {
30897
- const resp = decodeResponse3(line);
30898
- if (settled)
30899
- return;
30900
- settled = true;
30901
- clearTimeout(timer);
30902
- socket.end();
30903
- resolve32(resp);
30904
- } catch (err) {
30905
- if (settled)
30906
- return;
30907
- settled = true;
30908
- clearTimeout(timer);
30909
- socket.destroy();
30910
- reject(new Error(`hostd: bad response frame: ${err.message}`, { cause: err }));
30911
- }
30912
- });
30913
- socket.on("error", (err) => {
30914
- if (settled)
30915
- return;
30916
- settled = true;
30917
- clearTimeout(timer);
30918
- reject(err);
30919
- });
30920
- socket.on("close", () => {
30921
- if (settled)
30922
- return;
30923
- settled = true;
30924
- clearTimeout(timer);
30925
- reject(new Error("hostd: connection closed before response"));
30926
- });
30927
- });
30928
- }
30929
- var init_client4 = __esm(() => {
30930
- init_protocol3();
30931
- });
30932
-
30933
30933
  // src/cli/doctor-agent-smoke.ts
30934
- import { existsSync as existsSync52 } from "node:fs";
30935
- import { homedir as homedir31 } from "node:os";
30936
- import { join as join52 } from "node:path";
30937
- import { randomUUID as randomUUID4 } from "node:crypto";
30934
+ import { existsSync as existsSync53 } from "node:fs";
30935
+ import { homedir as homedir32 } from "node:os";
30936
+ import { join as join54 } from "node:path";
30937
+ import { randomUUID as randomUUID5 } from "node:crypto";
30938
30938
  async function runAgentSmokeChecks(config, deps = {}) {
30939
30939
  if (deps.fast)
30940
30940
  return [];
30941
- const home2 = deps.homeDir ?? homedir31();
30942
- const sock = deps.operatorSockPath ?? join52(home2, ".switchroom", "hostd", "operator", "sock");
30943
- if (!deps.hostdRequestImpl && !existsSync52(sock)) {
30941
+ const home2 = deps.homeDir ?? homedir32();
30942
+ const sock = deps.operatorSockPath ?? join54(home2, ".switchroom", "hostd", "operator", "sock");
30943
+ if (!deps.hostdRequestImpl && !existsSync53(sock)) {
30944
30944
  return [
30945
30945
  {
30946
30946
  name: "agent liveness",
@@ -30952,7 +30952,7 @@ async function runAgentSmokeChecks(config, deps = {}) {
30952
30952
  }
30953
30953
  const call = deps.hostdRequestImpl ?? ((s, name) => hostdRequest({ socketPath: s, timeoutMs: 8000 }, {
30954
30954
  v: 1,
30955
- request_id: `doctor-smoke-${randomUUID4()}`,
30955
+ request_id: `doctor-smoke-${randomUUID5()}`,
30956
30956
  op: "agent_smoke",
30957
30957
  args: { name }
30958
30958
  }));
@@ -31018,9 +31018,9 @@ var init_doctor_agent_smoke = __esm(() => {
31018
31018
 
31019
31019
  // src/cli/doctor-vault-broker-durability.ts
31020
31020
  import { execFileSync as execFileSync17 } from "node:child_process";
31021
- import { existsSync as existsSync53, statSync as statSync22 } from "node:fs";
31022
- import { homedir as homedir32 } from "node:os";
31023
- import { join as join53 } from "node:path";
31021
+ import { existsSync as existsSync54, statSync as statSync22 } from "node:fs";
31022
+ import { homedir as homedir33 } from "node:os";
31023
+ import { join as join55 } from "node:path";
31024
31024
  function probeBindMountInode(hostPath, brokerContainerPath, opts) {
31025
31025
  const statHost = opts?.statHost ?? defaultStatHost;
31026
31026
  const statBroker = opts?.statBroker ?? defaultStatBroker;
@@ -31042,7 +31042,7 @@ function probeBindMountInode(hostPath, brokerContainerPath, opts) {
31042
31042
  };
31043
31043
  }
31044
31044
  function defaultStatHost(p) {
31045
- if (!existsSync53(p))
31045
+ if (!existsSync54(p))
31046
31046
  return null;
31047
31047
  try {
31048
31048
  const s = statSync22(p, { bigint: true });
@@ -31163,22 +31163,22 @@ function defaultBrokerStatusProbe() {
31163
31163
  }
31164
31164
  }
31165
31165
  function runVaultBrokerDurabilityChecks(_config, opts) {
31166
- const home2 = homedir32();
31166
+ const home2 = homedir33();
31167
31167
  const probe2 = opts?.inodeProbe ?? probeBindMountInode;
31168
31168
  return [
31169
31169
  probeBrokerUnlocked(opts?.statusProbe),
31170
31170
  probeAutoUnlockBlob(home2),
31171
31171
  probeMachineIdMount(),
31172
- formatBindMountResult("vault-broker: vault.enc bind mount", join53(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join53(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
31173
- formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join53(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join53(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
31174
- formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join53(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join53(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
31172
+ formatBindMountResult("vault-broker: vault.enc bind mount", join55(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc", probe2(join55(home2, ".switchroom", "vault", "vault.enc"), "/state/vault/vault.enc")),
31173
+ formatBindMountResult("vault-broker: vault-grants.db bind mount (#1737)", join55(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db", probe2(join55(home2, ".switchroom", "vault-grants.db"), "/root/.switchroom/vault-grants.db")),
31174
+ formatBindMountResult("vault-broker: vault-audit.log bind mount (#1025)", join55(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log", probe2(join55(home2, ".switchroom", "vault-audit.log"), "/root/.switchroom/vault-audit.log")),
31175
31175
  probeKernelDbDurability(home2, {
31176
31176
  statBroker: opts?.kernelStatBroker
31177
31177
  })
31178
31178
  ];
31179
31179
  }
31180
31180
  function probeKernelDbDurability(home2, opts) {
31181
- const hostDir = join53(home2, ".switchroom", "approvals");
31181
+ const hostDir = join55(home2, ".switchroom", "approvals");
31182
31182
  const containerDir = "/state/approvals";
31183
31183
  const name = "approval-kernel: approvals bind mount (allow_always durability)";
31184
31184
  const kernelStat = opts?.statBroker ?? defaultKernelStatBroker;
@@ -31246,8 +31246,8 @@ function defaultKernelStatBroker(p) {
31246
31246
  return { kind: "ok-with-stat", ino: inoStr, size };
31247
31247
  }
31248
31248
  function probeAutoUnlockBlob(home2) {
31249
- const blobPath = join53(home2, ".switchroom", "vault-auto-unlock");
31250
- if (!existsSync53(blobPath)) {
31249
+ const blobPath = join55(home2, ".switchroom", "vault-auto-unlock");
31250
+ if (!existsSync54(blobPath)) {
31251
31251
  return {
31252
31252
  name: "vault-broker: auto-unlock blob",
31253
31253
  status: "warn",
@@ -31271,7 +31271,7 @@ function probeAutoUnlockBlob(home2) {
31271
31271
  };
31272
31272
  }
31273
31273
  function probeMachineIdMount() {
31274
- const hostExists = existsSync53("/etc/machine-id");
31274
+ const hostExists = existsSync54("/etc/machine-id");
31275
31275
  if (!hostExists) {
31276
31276
  return {
31277
31277
  name: "vault-broker: machine-id passthrough",
@@ -31346,27 +31346,27 @@ __export(exports_doctor, {
31346
31346
  checkAgents: () => checkAgents,
31347
31347
  MFF_VAULT_KEY: () => MFF_VAULT_KEY
31348
31348
  });
31349
- import { execSync as execSync3, spawnSync as spawnSync7 } from "node:child_process";
31349
+ import { execSync as execSync3, spawnSync as spawnSync8 } from "node:child_process";
31350
31350
  import {
31351
31351
  accessSync as accessSync2,
31352
31352
  constants as fsConstants5,
31353
- existsSync as existsSync54,
31353
+ existsSync as existsSync55,
31354
31354
  lstatSync as lstatSync5,
31355
- mkdirSync as mkdirSync29,
31356
- readFileSync as readFileSync48,
31355
+ mkdirSync as mkdirSync30,
31356
+ readFileSync as readFileSync49,
31357
31357
  readdirSync as readdirSync20,
31358
31358
  statSync as statSync23
31359
31359
  } from "node:fs";
31360
- import { dirname as dirname12, join as join54, resolve as resolve32 } from "node:path";
31360
+ import { dirname as dirname12, join as join56, resolve as resolve32 } from "node:path";
31361
31361
  import { createPublicKey, createPrivateKey } from "node:crypto";
31362
31362
  function findInNvm(bin) {
31363
- const nvmRoot = join54(process.env.HOME ?? "", ".nvm", "versions", "node");
31364
- if (!existsSync54(nvmRoot))
31363
+ const nvmRoot = join56(process.env.HOME ?? "", ".nvm", "versions", "node");
31364
+ if (!existsSync55(nvmRoot))
31365
31365
  return null;
31366
31366
  try {
31367
31367
  const versions = readdirSync20(nvmRoot).sort().reverse();
31368
31368
  for (const v of versions) {
31369
- const candidate = join54(nvmRoot, v, "bin", bin);
31369
+ const candidate = join56(nvmRoot, v, "bin", bin);
31370
31370
  try {
31371
31371
  const s = statSync23(candidate);
31372
31372
  if (s.isFile() || s.isSymbolicLink()) {
@@ -31531,21 +31531,21 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
31531
31531
  if (envBrowsersPath && envBrowsersPath.length > 0) {
31532
31532
  cacheLocations.push(envBrowsersPath);
31533
31533
  }
31534
- cacheLocations.push(join54(homeDir, ".cache", "ms-playwright"));
31534
+ cacheLocations.push(join56(homeDir, ".cache", "ms-playwright"));
31535
31535
  for (const cacheDir of cacheLocations) {
31536
- if (!existsSync54(cacheDir))
31536
+ if (!existsSync55(cacheDir))
31537
31537
  continue;
31538
31538
  try {
31539
31539
  const entries = readdirSync20(cacheDir).filter((e) => e.startsWith("chromium"));
31540
31540
  for (const entry of entries) {
31541
31541
  const candidates2 = [
31542
- join54(cacheDir, entry, "chrome-linux64", "chrome"),
31543
- join54(cacheDir, entry, "chrome-linux", "chrome"),
31544
- join54(cacheDir, entry, "chrome-linux64", "headless_shell"),
31545
- join54(cacheDir, entry, "chrome-linux", "headless_shell")
31542
+ join56(cacheDir, entry, "chrome-linux64", "chrome"),
31543
+ join56(cacheDir, entry, "chrome-linux", "chrome"),
31544
+ join56(cacheDir, entry, "chrome-linux64", "headless_shell"),
31545
+ join56(cacheDir, entry, "chrome-linux", "headless_shell")
31546
31546
  ];
31547
31547
  for (const path4 of candidates2) {
31548
- if (existsSync54(path4))
31548
+ if (existsSync55(path4))
31549
31549
  return path4;
31550
31550
  }
31551
31551
  }
@@ -31567,7 +31567,7 @@ function checkChromium() {
31567
31567
  }
31568
31568
  function checkDepsCacheWritable(depsRoot = resolvePath("~/.switchroom/deps")) {
31569
31569
  try {
31570
- mkdirSync29(depsRoot, { recursive: true });
31570
+ mkdirSync30(depsRoot, { recursive: true });
31571
31571
  accessSync2(depsRoot, fsConstants5.W_OK);
31572
31572
  return {
31573
31573
  name: "~/.switchroom/deps writable",
@@ -31665,8 +31665,8 @@ function checkUserDeclaredMcps(name, agentConfig, config, renderedMcpServers) {
31665
31665
  function checkLegacyState() {
31666
31666
  const results = [];
31667
31667
  const h = process.env.HOME ?? "/root";
31668
- const clerkDir = join54(h, LEGACY_STATE_DIR);
31669
- const clerkPresent = existsSync54(clerkDir);
31668
+ const clerkDir = join56(h, LEGACY_STATE_DIR);
31669
+ const clerkPresent = existsSync55(clerkDir);
31670
31670
  results.push({
31671
31671
  name: "legacy ~/.clerk state",
31672
31672
  status: clerkPresent ? "warn" : "ok",
@@ -31675,7 +31675,7 @@ function checkLegacyState() {
31675
31675
  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."
31676
31676
  } : {}
31677
31677
  });
31678
- const legacySock = join54(h, ".switchroom", "vault-broker.sock");
31678
+ const legacySock = join56(h, ".switchroom", "vault-broker.sock");
31679
31679
  let sockStat = null;
31680
31680
  try {
31681
31681
  sockStat = lstatSync5(legacySock);
@@ -31694,7 +31694,7 @@ function probeVaultBrokerSocketPair(agentName) {
31694
31694
  const dataPath = `/run/switchroom/broker/${agentName}/sock`;
31695
31695
  const unlockPath = `/run/switchroom/broker/${agentName}/unlock`;
31696
31696
  const script = `D=0; U=0; ` + `test -S '${dataPath}' && D=1; ` + `test -S '${unlockPath}' && U=1; ` + `echo "D=$D U=$U"`;
31697
- const r = spawnSync7("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
31697
+ const r = spawnSync8("docker", ["exec", "switchroom-vault-broker", "sh", "-c", script], { stdio: "pipe", timeout: 3000 });
31698
31698
  if (r.error || r.status === null)
31699
31699
  return "unreachable";
31700
31700
  if (r.status !== 0) {
@@ -31796,7 +31796,7 @@ function checkVault(config) {
31796
31796
  detail: "Approval auth: passphrase (two-factor)"
31797
31797
  };
31798
31798
  const pairsResult = checkVaultBrokerSocketPairs(config);
31799
- if (!existsSync54(vaultPath)) {
31799
+ if (!existsSync55(vaultPath)) {
31800
31800
  return [
31801
31801
  postureResult,
31802
31802
  {
@@ -31893,7 +31893,7 @@ function checkHindsightConsumer(config, opts) {
31893
31893
  }
31894
31894
  function probeAuthBrokerSocket(consumerName) {
31895
31895
  const containerPath = `/run/switchroom/auth-broker/${consumerName}/sock`;
31896
- const r = spawnSync7("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
31896
+ const r = spawnSync8("docker", ["exec", "switchroom-auth-broker", "test", "-S", containerPath], { stdio: "pipe", timeout: 3000 });
31897
31897
  if (r.error || r.status === null)
31898
31898
  return "unreachable";
31899
31899
  if (r.status === 0)
@@ -31971,8 +31971,8 @@ async function checkHindsight(config) {
31971
31971
  }
31972
31972
  function checkPendingRetainsQueue(dir) {
31973
31973
  const home2 = process.env.HOME ?? "";
31974
- const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join54(home2, ".hindsight", "pending-retains");
31975
- if (!existsSync54(pendingDir)) {
31974
+ const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join56(home2, ".hindsight", "pending-retains");
31975
+ if (!existsSync55(pendingDir)) {
31976
31976
  return {
31977
31977
  name: "pending-retains queue",
31978
31978
  status: "ok",
@@ -32031,7 +32031,7 @@ function classifyReadError(err) {
32031
32031
  }
32032
32032
  function tryReadHostFile(path4) {
32033
32033
  try {
32034
- return { kind: "ok", content: readFileSync48(path4, "utf-8") };
32034
+ return { kind: "ok", content: readFileSync49(path4, "utf-8") };
32035
32035
  } catch (err) {
32036
32036
  const kind = classifyReadError(err);
32037
32037
  const error = err?.message ?? String(err);
@@ -32043,11 +32043,11 @@ function tryReadHostFile(path4) {
32043
32043
  }
32044
32044
  }
32045
32045
  function parseEnvFile(path4) {
32046
- if (!existsSync54(path4))
32046
+ if (!existsSync55(path4))
32047
32047
  return {};
32048
32048
  let content;
32049
32049
  try {
32050
- content = readFileSync48(path4, "utf-8");
32050
+ content = readFileSync49(path4, "utf-8");
32051
32051
  } catch {
32052
32052
  return {};
32053
32053
  }
@@ -32102,7 +32102,7 @@ async function checkTelegram(config) {
32102
32102
  const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
32103
32103
  if (plugin !== "switchroom")
32104
32104
  continue;
32105
- const envPath = join54(agentsDir, name, "telegram", ".env");
32105
+ const envPath = join56(agentsDir, name, "telegram", ".env");
32106
32106
  const read = tryReadHostFile(envPath);
32107
32107
  if (read.kind === "eacces") {
32108
32108
  results.push({
@@ -32154,7 +32154,7 @@ async function checkTelegram(config) {
32154
32154
  }
32155
32155
  function checkStartShStale(agentName, startShPath) {
32156
32156
  const label = `${agentName}: start.sh scheduler block`;
32157
- if (!existsSync54(startShPath)) {
32157
+ if (!existsSync55(startShPath)) {
32158
32158
  return {
32159
32159
  name: label,
32160
32160
  status: "warn",
@@ -32164,7 +32164,7 @@ function checkStartShStale(agentName, startShPath) {
32164
32164
  }
32165
32165
  let content;
32166
32166
  try {
32167
- content = readFileSync48(startShPath, "utf-8");
32167
+ content = readFileSync49(startShPath, "utf-8");
32168
32168
  } catch (err) {
32169
32169
  return {
32170
32170
  name: label,
@@ -32185,7 +32185,7 @@ function checkStartShStale(agentName, startShPath) {
32185
32185
  }
32186
32186
  function checkLeakedHomeSwitchroom(agentName, agentDir) {
32187
32187
  const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
32188
- const path4 = join54(agentDir, "home", ".switchroom");
32188
+ const path4 = join56(agentDir, "home", ".switchroom");
32189
32189
  let stats;
32190
32190
  try {
32191
32191
  stats = lstatSync5(path4);
@@ -32222,8 +32222,8 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
32222
32222
  }
32223
32223
  function checkRepoHygiene(repoRoot) {
32224
32224
  const results = [];
32225
- const exportDir = join54(repoRoot, "clerk-export");
32226
- if (existsSync54(exportDir)) {
32225
+ const exportDir = join56(repoRoot, "clerk-export");
32226
+ if (existsSync55(exportDir)) {
32227
32227
  results.push({
32228
32228
  name: "repo hygiene: clerk-export/ on disk (#1072)",
32229
32229
  status: "warn",
@@ -32231,8 +32231,8 @@ function checkRepoHygiene(repoRoot) {
32231
32231
  fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
32232
32232
  });
32233
32233
  }
32234
- const knownTarball = join54(repoRoot, "clerk-export-with-secrets.tar.gz");
32235
- if (existsSync54(knownTarball)) {
32234
+ const knownTarball = join56(repoRoot, "clerk-export-with-secrets.tar.gz");
32235
+ if (existsSync55(knownTarball)) {
32236
32236
  results.push({
32237
32237
  name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
32238
32238
  status: "warn",
@@ -32249,7 +32249,7 @@ function checkRepoHygiene(repoRoot) {
32249
32249
  results.push({
32250
32250
  name: `repo hygiene: ${name} on disk (#1072)`,
32251
32251
  status: "warn",
32252
- detail: `${join54(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
32252
+ detail: `${join56(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
32253
32253
  fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
32254
32254
  });
32255
32255
  }
@@ -32272,12 +32272,12 @@ function checkRepoHygiene(repoRoot) {
32272
32272
  }
32273
32273
  function isSwitchroomCheckout(dir) {
32274
32274
  try {
32275
- if (!existsSync54(join54(dir, ".git")))
32275
+ if (!existsSync55(join56(dir, ".git")))
32276
32276
  return false;
32277
- const pkgPath = join54(dir, "package.json");
32278
- if (!existsSync54(pkgPath))
32277
+ const pkgPath = join56(dir, "package.json");
32278
+ if (!existsSync55(pkgPath))
32279
32279
  return false;
32280
- const pkg = JSON.parse(readFileSync48(pkgPath, "utf-8"));
32280
+ const pkg = JSON.parse(readFileSync49(pkgPath, "utf-8"));
32281
32281
  return pkg.name === "switchroom";
32282
32282
  } catch {
32283
32283
  return false;
@@ -32290,7 +32290,7 @@ function checkAgents(config, configPath) {
32290
32290
  const authStatuses = getAllAuthStatuses(config);
32291
32291
  for (const [name, agentConfig] of Object.entries(config.agents)) {
32292
32292
  const agentDir = resolve32(agentsDir, name);
32293
- if (!existsSync54(agentDir)) {
32293
+ if (!existsSync55(agentDir)) {
32294
32294
  results.push({
32295
32295
  name: `${name}: scaffold`,
32296
32296
  status: "fail",
@@ -32311,7 +32311,7 @@ function checkAgents(config, configPath) {
32311
32311
  fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
32312
32312
  });
32313
32313
  }
32314
- results.push(checkStartShStale(name, join54(agentDir, "start.sh")));
32314
+ results.push(checkStartShStale(name, join56(agentDir, "start.sh")));
32315
32315
  results.push(checkLeakedHomeSwitchroom(name, agentDir));
32316
32316
  const status = statuses[name];
32317
32317
  const active = status?.active ?? "unknown";
@@ -32388,8 +32388,8 @@ function checkAgents(config, configPath) {
32388
32388
  }
32389
32389
  }
32390
32390
  if (agentConfig.channels?.telegram?.plugin === "switchroom") {
32391
- const mcpJsonPath = join54(agentDir, ".mcp.json");
32392
- if (!existsSync54(mcpJsonPath)) {
32391
+ const mcpJsonPath = join56(agentDir, ".mcp.json");
32392
+ if (!existsSync55(mcpJsonPath)) {
32393
32393
  results.push({
32394
32394
  name: `${name}: .mcp.json`,
32395
32395
  status: "fail",
@@ -32398,7 +32398,7 @@ function checkAgents(config, configPath) {
32398
32398
  });
32399
32399
  } else {
32400
32400
  try {
32401
- const mcp = JSON.parse(readFileSync48(mcpJsonPath, "utf-8"));
32401
+ const mcp = JSON.parse(readFileSync49(mcpJsonPath, "utf-8"));
32402
32402
  const hasSwitchroomTelegram = !!mcp.mcpServers?.["switchroom-telegram"];
32403
32403
  const memoryEnabled = isHindsightEnabled(config);
32404
32404
  const hasHindsight = !!mcp.mcpServers?.hindsight;
@@ -32475,7 +32475,7 @@ function mffEnvPath(config) {
32475
32475
  return agent ? resolve32(home2, ".switchroom/credentials", agent, "my-family-finance/.env") : resolve32(home2, ".switchroom/credentials/my-family-finance/.env");
32476
32476
  }
32477
32477
  function mffEnvState(envPath) {
32478
- if (!existsSync54(envPath))
32478
+ if (!existsSync55(envPath))
32479
32479
  return "absent";
32480
32480
  try {
32481
32481
  accessSync2(envPath, fsConstants5.R_OK);
@@ -32493,7 +32493,7 @@ function checkMffVaultKeyPresent(passphrase, vaultPath) {
32493
32493
  fix: "Export SWITCHROOM_VAULT_PASSPHRASE to enable MFF vault probes"
32494
32494
  };
32495
32495
  }
32496
- if (!existsSync54(vaultPath)) {
32496
+ if (!existsSync55(vaultPath)) {
32497
32497
  return {
32498
32498
  name: "mff: vault key present",
32499
32499
  status: "fail",
@@ -32546,7 +32546,7 @@ function deriveEd25519PublicKeyBytes(keyMaterial) {
32546
32546
  }
32547
32547
  }
32548
32548
  function checkMffVaultKeyFormat(passphrase, vaultPath) {
32549
- if (!passphrase || !existsSync54(vaultPath)) {
32549
+ if (!passphrase || !existsSync55(vaultPath)) {
32550
32550
  return {
32551
32551
  name: "mff: vault key format",
32552
32552
  status: "warn",
@@ -32690,8 +32690,8 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
32690
32690
  };
32691
32691
  }
32692
32692
  const credDir = dirname12(envPath);
32693
- const authScript = join54(credDir, "claude-auth.py");
32694
- if (!existsSync54(authScript)) {
32693
+ const authScript = join56(credDir, "claude-auth.py");
32694
+ if (!existsSync55(authScript)) {
32695
32695
  return {
32696
32696
  name: "mff: auth flow",
32697
32697
  status: "warn",
@@ -32702,7 +32702,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
32702
32702
  const python3 = which("python3") ?? "python3";
32703
32703
  let token;
32704
32704
  try {
32705
- const result = spawnSync7(python3, [authScript, "--quiet"], {
32705
+ const result = spawnSync8(python3, [authScript, "--quiet"], {
32706
32706
  timeout: timeoutMs,
32707
32707
  encoding: "utf-8",
32708
32708
  env: { ...process.env, ...env2 }
@@ -32893,11 +32893,11 @@ function runDockerSection(config) {
32893
32893
  let composeYaml;
32894
32894
  let dockerfileAgent;
32895
32895
  try {
32896
- composeYaml = readFileSync48(composePath, "utf8");
32896
+ composeYaml = readFileSync49(composePath, "utf8");
32897
32897
  } catch {}
32898
32898
  const dockerfilePath = resolve32(process.env.HOME ?? "", ".switchroom", "docker", "Dockerfile.agent");
32899
32899
  try {
32900
- dockerfileAgent = readFileSync48(dockerfilePath, "utf8");
32900
+ dockerfileAgent = readFileSync49(dockerfilePath, "utf8");
32901
32901
  } catch {}
32902
32902
  return runDockerChecks({
32903
32903
  config,
@@ -48726,9 +48726,9 @@ __export(exports_server, {
48726
48726
  dispatchTool: () => dispatchTool,
48727
48727
  TOOLS: () => TOOLS
48728
48728
  });
48729
- import { spawnSync as spawnSync12 } from "node:child_process";
48729
+ import { spawnSync as spawnSync13 } from "node:child_process";
48730
48730
  function execCli(args, stdin) {
48731
- const r = spawnSync12(CLI_BIN, args, {
48731
+ const r = spawnSync13(CLI_BIN, args, {
48732
48732
  encoding: "utf-8",
48733
48733
  env: process.env,
48734
48734
  timeout: 15000,
@@ -49190,7 +49190,7 @@ __export(exports_server2, {
49190
49190
  TOOLS: () => TOOLS2
49191
49191
  });
49192
49192
  import { randomBytes as randomBytes15 } from "node:crypto";
49193
- import { existsSync as existsSync82, readFileSync as readFileSync67 } from "node:fs";
49193
+ import { existsSync as existsSync83, readFileSync as readFileSync68 } from "node:fs";
49194
49194
  function selfSocketPath() {
49195
49195
  return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
49196
49196
  }
@@ -49205,7 +49205,7 @@ async function dispatchTool2(name, args) {
49205
49205
  return errorText2("hostd MCP: SWITCHROOM_AGENT_NAME env var is not set \u2014 cannot " + "determine which per-agent socket to talk to.");
49206
49206
  }
49207
49207
  const sockPath = selfSocketPath();
49208
- if (!existsSync82(sockPath)) {
49208
+ if (!existsSync83(sockPath)) {
49209
49209
  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.`);
49210
49210
  }
49211
49211
  let req;
@@ -49357,18 +49357,18 @@ function resolveAuditLogPath() {
49357
49357
  if (process.env.HOSTD_AUDIT_LOG_PATH)
49358
49358
  return process.env.HOSTD_AUDIT_LOG_PATH;
49359
49359
  const bindMounted = "/host-home/.switchroom/host-control-audit.log";
49360
- if (existsSync82(bindMounted))
49360
+ if (existsSync83(bindMounted))
49361
49361
  return bindMounted;
49362
49362
  return defaultAuditLogPath2();
49363
49363
  }
49364
49364
  function getLastUpdateApplyStatus() {
49365
49365
  const path8 = resolveAuditLogPath();
49366
- if (!existsSync82(path8)) {
49366
+ if (!existsSync83(path8)) {
49367
49367
  return errorText2(`get_status: audit log not found at ${path8}. No update_apply has run yet?`);
49368
49368
  }
49369
49369
  let raw;
49370
49370
  try {
49371
- raw = readFileSync67(path8, "utf-8");
49371
+ raw = readFileSync68(path8, "utf-8");
49372
49372
  } catch (err2) {
49373
49373
  return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
49374
49374
  }
@@ -49601,8 +49601,8 @@ var {
49601
49601
  } = import__.default;
49602
49602
 
49603
49603
  // src/build-info.ts
49604
- var VERSION = "0.14.73";
49605
- var COMMIT_SHA = "bf5d1f94";
49604
+ var VERSION = "0.14.74";
49605
+ var COMMIT_SHA = "7e9ebb67";
49606
49606
 
49607
49607
  // src/cli/agent.ts
49608
49608
  init_source();
@@ -49802,7 +49802,7 @@ import_handlebars.default.registerHelper("isNumber", (value) => {
49802
49802
  return typeof value === "number" && Number.isFinite(value);
49803
49803
  });
49804
49804
  var SHARED_FRAGMENTS_DIR = resolve4(PROFILES_ROOT, "_shared");
49805
- var SHARED_FRAGMENTS = ["telegram-style", "vault-protocol", "agent-self-service"];
49805
+ var SHARED_FRAGMENTS = ["vault-protocol", "agent-self-service"];
49806
49806
  for (const name of SHARED_FRAGMENTS) {
49807
49807
  const fragPath = join2(SHARED_FRAGMENTS_DIR, `${name}.md.hbs`);
49808
49808
  if (existsSync4(fragPath)) {
@@ -51115,6 +51115,18 @@ function seedWorkspaceBootstrapFiles(params) {
51115
51115
  if (entry.endsWith(".hbs")) {
51116
51116
  const destRel = relPath.replace(/\.hbs$/, "");
51117
51117
  const destPath = join8(agentWorkspaceDir, destRel);
51118
+ if (destRel === "SOUL.default.md") {
51119
+ const rendered = renderTemplate(srcPath, params.context);
51120
+ const existed = existsSync13(destPath);
51121
+ const current = existed ? readFileSync11(destPath, "utf8") : null;
51122
+ if (current !== rendered) {
51123
+ writeFileSync5(destPath, rendered);
51124
+ params.created.push(destPath);
51125
+ } else {
51126
+ params.skipped.push(destPath);
51127
+ }
51128
+ continue;
51129
+ }
51118
51130
  const renderFn = () => {
51119
51131
  if (destRel === "SOUL.md") {
51120
51132
  return renderSoulMd(params.profilePath, agentWorkspaceDir, params.context.soul) ?? renderTemplate(srcPath, params.context);
@@ -66576,19 +66588,20 @@ init_source();
66576
66588
 
66577
66589
  // src/web/server.ts
66578
66590
  init_merge();
66591
+ init_loader();
66579
66592
  init_lifecycle();
66580
66593
  import {
66581
- readFileSync as readFileSync43,
66582
- existsSync as existsSync47,
66594
+ readFileSync as readFileSync44,
66595
+ existsSync as existsSync48,
66583
66596
  realpathSync as realpathSync3,
66584
- mkdirSync as mkdirSync27,
66597
+ mkdirSync as mkdirSync28,
66585
66598
  openSync as openSync10,
66586
66599
  closeSync as closeSync10,
66587
66600
  writeSync as writeSync6,
66588
66601
  constants as fsConstants3
66589
66602
  } from "node:fs";
66590
- import { resolve as resolve28, extname, join as join41, relative, dirname as dirname9 } from "node:path";
66591
- import { homedir as homedir22 } from "node:os";
66603
+ import { resolve as resolve28, extname, join as join43, relative, dirname as dirname9 } from "node:path";
66604
+ import { homedir as homedir23 } from "node:os";
66592
66605
  import { spawn as spawn5 } from "node:child_process";
66593
66606
  import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } from "node:crypto";
66594
66607
 
@@ -66596,8 +66609,8 @@ import { timingSafeEqual as timingSafeEqual3, randomBytes as randomBytes11 } fro
66596
66609
  init_lifecycle();
66597
66610
  init_manager();
66598
66611
  init_hindsight();
66599
- import { spawnSync as spawnSync4 } from "node:child_process";
66600
- import { existsSync as existsSync44, readFileSync as readFileSync40 } from "node:fs";
66612
+ import { spawnSync as spawnSync5 } from "node:child_process";
66613
+ import { existsSync as existsSync45, readFileSync as readFileSync41 } from "node:fs";
66601
66614
  import { resolve as resolve27 } from "node:path";
66602
66615
  init_audit_reader();
66603
66616
 
@@ -71206,12 +71219,198 @@ function installGlobalErrorHandlers() {
71206
71219
  // src/web/api.ts
71207
71220
  init_loader();
71208
71221
  init_merge();
71222
+
71223
+ // src/config/agent-workspace-account.ts
71224
+ var import_yaml13 = __toESM(require_dist(), 1);
71225
+ function blockKey(provider) {
71226
+ return `${provider}_workspace`;
71227
+ }
71228
+ function setAgentWorkspaceAccount(yamlText, provider, agent, account) {
71229
+ const doc = import_yaml13.parseDocument(yamlText);
71230
+ const agentsNode = doc.get("agents");
71231
+ if (!import_yaml13.isMap(agentsNode)) {
71232
+ throw new Error("switchroom.yaml has no `agents:` map");
71233
+ }
71234
+ if (!agentsNode.has(agent)) {
71235
+ throw new Error(`agent '${agent}' is not declared in switchroom.yaml`);
71236
+ }
71237
+ const path4 = ["agents", agent, blockKey(provider), "account"];
71238
+ const current = doc.getIn(path4);
71239
+ if (current === account)
71240
+ return yamlText;
71241
+ doc.setIn(path4, account);
71242
+ return String(doc);
71243
+ }
71244
+ function clearAgentWorkspaceAccount(yamlText, provider, agent) {
71245
+ const doc = import_yaml13.parseDocument(yamlText);
71246
+ const agentsNode = doc.get("agents");
71247
+ if (!import_yaml13.isMap(agentsNode))
71248
+ return yamlText;
71249
+ if (!agentsNode.has(agent))
71250
+ return yamlText;
71251
+ const blockPath = ["agents", agent, blockKey(provider)];
71252
+ const block = doc.getIn(blockPath);
71253
+ if (!import_yaml13.isMap(block))
71254
+ return yamlText;
71255
+ const accountPath = [...blockPath, "account"];
71256
+ if (doc.getIn(accountPath) === undefined)
71257
+ return yamlText;
71258
+ doc.deleteIn(accountPath);
71259
+ const after = doc.getIn(blockPath);
71260
+ if (import_yaml13.isMap(after) && after.items.length === 0) {
71261
+ doc.deleteIn(blockPath);
71262
+ }
71263
+ return String(doc);
71264
+ }
71265
+ function getAgentWorkspaceAccount(yamlText, provider, agent) {
71266
+ const doc = import_yaml13.parseDocument(yamlText);
71267
+ const v = doc.getIn(["agents", agent, blockKey(provider), "account"]);
71268
+ return typeof v === "string" ? v : null;
71269
+ }
71270
+
71271
+ // src/web/config-edit-plan.ts
71272
+ init_schema();
71273
+ var import_yaml14 = __toESM(require_dist(), 1);
71274
+ import { readFileSync as readFileSync40 } from "node:fs";
71275
+
71276
+ class ConfigPlanError extends Error {
71277
+ }
71278
+ function composeTransforms(...transforms) {
71279
+ return (text) => transforms.reduce((acc, t) => t(acc), text);
71280
+ }
71281
+ function planConfigEdit(configPath, transform) {
71282
+ let before;
71283
+ try {
71284
+ before = readFileSync40(configPath, "utf-8");
71285
+ } catch (err) {
71286
+ throw new ConfigPlanError(`could not read config: ${err.message}`);
71287
+ }
71288
+ let after;
71289
+ try {
71290
+ after = transform(before);
71291
+ } catch (err) {
71292
+ throw new ConfigPlanError(`config edit failed: ${err.message}`);
71293
+ }
71294
+ if (after === before) {
71295
+ return { before, after, changed: false };
71296
+ }
71297
+ let parsed;
71298
+ try {
71299
+ parsed = import_yaml14.parse(after);
71300
+ } catch (err) {
71301
+ throw new ConfigPlanError(`edit produced unparseable YAML: ${err.message}`);
71302
+ }
71303
+ const check = SwitchroomConfigSchema.safeParse(parsed);
71304
+ if (!check.success) {
71305
+ throw new ConfigPlanError(`edit produced an invalid config: ${check.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`);
71306
+ }
71307
+ return { before, after, changed: true };
71308
+ }
71309
+
71310
+ // src/web/config-diff.ts
71311
+ import {
71312
+ mkdirSync as mkdirSync25,
71313
+ mkdtempSync as mkdtemp,
71314
+ rmSync as rmrf,
71315
+ writeFileSync as writeFileSync24
71316
+ } from "node:fs";
71317
+ import { spawnSync as spawnSync4 } from "node:child_process";
71318
+ import { tmpdir as tmpdir4 } from "node:os";
71319
+ import { join as join38 } from "node:path";
71320
+
71321
+ class ConfigDiffError extends Error {
71322
+ }
71323
+ function generateUnifiedDiff(before, after, name = "switchroom.yaml", gitBin = "git") {
71324
+ if (before === after)
71325
+ return "";
71326
+ const dir = mkdtemp(join38(tmpdir4(), "switchroom-config-diff-"));
71327
+ try {
71328
+ mkdirSync25(join38(dir, "cur"), { recursive: true });
71329
+ mkdirSync25(join38(dir, "new"), { recursive: true });
71330
+ writeFileSync24(join38(dir, "cur", name), before);
71331
+ writeFileSync24(join38(dir, "new", name), after);
71332
+ const r = spawnSync4(gitBin, ["diff", "--no-index", "--no-color", "--", `cur/${name}`, `new/${name}`], { cwd: dir, encoding: "utf-8", timeout: 1e4 });
71333
+ if (r.status === 0)
71334
+ return "";
71335
+ if (r.status !== 1) {
71336
+ throw new ConfigDiffError(`git diff failed (status=${r.status}): ${(r.stderr || r.error?.message || "").toString().trim()}`);
71337
+ }
71338
+ const raw = r.stdout ?? "";
71339
+ if (!raw.includes("@@")) {
71340
+ throw new ConfigDiffError("git diff produced no hunks");
71341
+ }
71342
+ const diff = raw.split(`
71343
+ `).map((line) => {
71344
+ if (line.startsWith("diff --git "))
71345
+ return `diff --git a/${name} b/${name}`;
71346
+ if (line.startsWith("--- "))
71347
+ return `--- a/${name}`;
71348
+ if (line.startsWith("+++ "))
71349
+ return `+++ b/${name}`;
71350
+ return line;
71351
+ }).join(`
71352
+ `);
71353
+ return diff;
71354
+ } finally {
71355
+ try {
71356
+ rmrf(dir, { recursive: true, force: true });
71357
+ } catch {}
71358
+ }
71359
+ }
71360
+
71361
+ // src/web/hostd-config-propose.ts
71362
+ init_client4();
71363
+ import { existsSync as existsSync44 } from "node:fs";
71364
+ import { homedir as homedir20 } from "node:os";
71365
+ import { join as join39 } from "node:path";
71366
+ var PROPOSE_TIMEOUT_MS = 11 * 60 * 1000;
71367
+ function resolveHostdOperatorSocket(env2 = process.env) {
71368
+ const override = env2.SWITCHROOM_HOSTD_OPERATOR_SOCKET;
71369
+ if (override && override.length > 0)
71370
+ return override;
71371
+ const candidates = [
71372
+ "/host-home/.switchroom/hostd/operator/sock",
71373
+ join39(homedir20(), ".switchroom", "hostd", "operator", "sock")
71374
+ ];
71375
+ for (const c of candidates) {
71376
+ if (existsSync44(c))
71377
+ return c;
71378
+ }
71379
+ return null;
71380
+ }
71381
+ async function proposeConfigEditViaHostd(args) {
71382
+ const req = {
71383
+ v: 1,
71384
+ op: "config_propose_edit",
71385
+ request_id: args.requestId,
71386
+ args: {
71387
+ unified_diff: args.unifiedDiff,
71388
+ reason: args.reason,
71389
+ target_path: "/state/config/switchroom.yaml"
71390
+ }
71391
+ };
71392
+ const send = args.send ?? hostdRequest;
71393
+ try {
71394
+ const resp = await send({ socketPath: args.socketPath, timeoutMs: args.timeoutMs ?? PROPOSE_TIMEOUT_MS }, req);
71395
+ if (resp.result === "completed")
71396
+ return { state: "applied" };
71397
+ if (resp.result === "denied") {
71398
+ return { state: "denied", reason: resp.error ?? "operator denied the change" };
71399
+ }
71400
+ return { state: "error", reason: resp.error ?? `hostd returned '${resp.result}'` };
71401
+ } catch (err) {
71402
+ return { state: "error", reason: err.message };
71403
+ }
71404
+ }
71405
+
71406
+ // src/web/api.ts
71209
71407
  init_account_store();
71210
71408
  init_client2();
71409
+ import { randomUUID as randomUUID4 } from "node:crypto";
71211
71410
 
71212
71411
  // telegram-plugin/registry/turns-schema.ts
71213
- import { chmodSync as chmodSync8, mkdirSync as mkdirSync25 } from "fs";
71214
- import { join as join38 } from "path";
71412
+ import { chmodSync as chmodSync8, mkdirSync as mkdirSync26 } from "fs";
71413
+ import { join as join40 } from "path";
71215
71414
  var DatabaseClass = null;
71216
71415
  function loadDatabaseClass() {
71217
71416
  if (DatabaseClass != null)
@@ -71274,9 +71473,9 @@ function applySchema(db) {
71274
71473
  }
71275
71474
  function openTurnsDb(agentDir) {
71276
71475
  const Database2 = loadDatabaseClass();
71277
- const dir = join38(agentDir, "telegram");
71278
- mkdirSync25(dir, { recursive: true, mode: 448 });
71279
- const path4 = join38(dir, "registry.db");
71476
+ const dir = join40(agentDir, "telegram");
71477
+ mkdirSync26(dir, { recursive: true, mode: 448 });
71478
+ const path4 = join40(dir, "registry.db");
71280
71479
  const db = new Database2(path4, { create: true });
71281
71480
  applySchema(db);
71282
71481
  try {
@@ -71441,7 +71640,7 @@ function handleRestartAgent(name) {
71441
71640
  }
71442
71641
  }
71443
71642
  function handleGetLogs(name, lines = 50) {
71444
- const res = spawnSync4("docker", ["logs", "--tail", String(lines), containerName(name)], { encoding: "utf-8", timeout: 5000 });
71643
+ const res = spawnSync5("docker", ["logs", "--tail", String(lines), containerName(name)], { encoding: "utf-8", timeout: 5000 });
71445
71644
  if (res.error) {
71446
71645
  return { ok: false, error: res.error.message };
71447
71646
  }
@@ -71634,7 +71833,7 @@ function inspectEnv(container, keys) {
71634
71833
  const out = {};
71635
71834
  for (const k of keys)
71636
71835
  out[k] = null;
71637
- const res = spawnSync4("docker", ["inspect", "--format", "{{json .Config.Env}}", container], { encoding: "utf-8", timeout: 4000 });
71836
+ const res = spawnSync5("docker", ["inspect", "--format", "{{json .Config.Env}}", container], { encoding: "utf-8", timeout: 4000 });
71638
71837
  if (res.error || res.status !== 0 || !res.stdout)
71639
71838
  return out;
71640
71839
  try {
@@ -71696,9 +71895,9 @@ async function handleGetSystemHealth(home2) {
71696
71895
  };
71697
71896
  try {
71698
71897
  const logPath = defaultAuditLogPath2(home2);
71699
- if (existsSync44(logPath)) {
71898
+ if (existsSync45(logPath)) {
71700
71899
  hostd.auditLogPresent = true;
71701
- const raw = readFileSync40(logPath, "utf-8");
71900
+ const raw = readFileSync41(logPath, "utf-8");
71702
71901
  hostd.recent = readAndFilter(raw, {}, 10);
71703
71902
  }
71704
71903
  } catch (err) {
@@ -71807,6 +72006,98 @@ function handleGetNotionWorkspace(config) {
71807
72006
  fullAccessAgents
71808
72007
  };
71809
72008
  }
72009
+ var connectionAccessStatuses = new Map;
72010
+ function reapConnectionAccessStatuses(now = Date.now()) {
72011
+ for (const [id, s] of connectionAccessStatuses) {
72012
+ if (s.state !== "pending" && now - s.startedAt > 30 * 60000) {
72013
+ connectionAccessStatuses.delete(id);
72014
+ }
72015
+ }
72016
+ }
72017
+ function handleGetConnectionAccessStatus(requestId) {
72018
+ return connectionAccessStatuses.get(requestId) ?? { state: "unknown" };
72019
+ }
72020
+ function handleSetConnectionAccess(configPath, config, args, deps = {}) {
72021
+ const provider = args.provider;
72022
+ if (provider !== "google" && provider !== "microsoft") {
72023
+ return { ok: false, error: `unsupported provider '${args.provider}' (google|microsoft)` };
72024
+ }
72025
+ if (args.action !== "enable" && args.action !== "disable") {
72026
+ return { ok: false, error: `action must be 'enable' or 'disable'` };
72027
+ }
72028
+ const agent = args.agent;
72029
+ if (!agent || !config.agents?.[agent]) {
72030
+ return { ok: false, error: `unknown agent '${agent}'` };
72031
+ }
72032
+ const account = String(args.account ?? "").trim().toLowerCase();
72033
+ if (!/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/.test(account)) {
72034
+ return { ok: false, error: `invalid account email '${args.account}'` };
72035
+ }
72036
+ const enableAcl = provider === "google" ? enableAgentsOnGoogleAccount : enableAgentsOnMicrosoftAccount;
72037
+ const disableAcl = provider === "google" ? disableAgentsOnGoogleAccount : disableAgentsOnMicrosoftAccount;
72038
+ let transform;
72039
+ if (args.action === "enable") {
72040
+ transform = composeTransforms((y) => enableAcl(y, account, [agent]), (y) => setAgentWorkspaceAccount(y, provider, agent, account));
72041
+ } else {
72042
+ transform = composeTransforms((y) => disableAcl(y, account, [agent]), (y) => getAgentWorkspaceAccount(y, provider, agent) === account ? clearAgentWorkspaceAccount(y, provider, agent) : y);
72043
+ }
72044
+ let plan;
72045
+ try {
72046
+ plan = planConfigEdit(configPath, transform);
72047
+ } catch (err) {
72048
+ return {
72049
+ ok: false,
72050
+ error: err instanceof ConfigPlanError ? err.message : err.message
72051
+ };
72052
+ }
72053
+ if (!plan.changed) {
72054
+ return { ok: true, changed: false };
72055
+ }
72056
+ const socketPath = deps.socketPath !== undefined ? deps.socketPath : resolveHostdOperatorSocket();
72057
+ if (!socketPath) {
72058
+ return {
72059
+ ok: false,
72060
+ error: "hostd operator socket not found \u2014 credential grants require hostd (the Telegram approval path). " + "Ensure host_control is enabled and the switchroom-web container mounts ~/.switchroom."
72061
+ };
72062
+ }
72063
+ let diff;
72064
+ try {
72065
+ diff = (deps.generateDiff ?? generateUnifiedDiff)(plan.before, plan.after);
72066
+ } catch (err) {
72067
+ return { ok: false, error: `could not build config diff: ${err.message}` };
72068
+ }
72069
+ const now = deps.now ?? Date.now;
72070
+ const requestId = randomUUID4();
72071
+ const reason = `dashboard: ${args.action} ${agent} on ${account} (${provider})`;
72072
+ connectionAccessStatuses.set(requestId, { state: "pending", startedAt: now() });
72073
+ reapConnectionAccessStatuses(now());
72074
+ const propose = deps.propose ?? ((reqId, d, r) => proposeConfigEditViaHostd({ requestId: reqId, unifiedDiff: d, reason: r, socketPath }));
72075
+ propose(requestId, diff, reason).then((outcome) => {
72076
+ const startedAt = connectionAccessStatuses.get(requestId)?.startedAt ?? now();
72077
+ if (outcome.state === "applied") {
72078
+ connectionAccessStatuses.set(requestId, { state: "applied", startedAt, restartAgent: agent });
72079
+ } else if (outcome.state === "denied") {
72080
+ connectionAccessStatuses.set(requestId, { state: "denied", startedAt, reason: outcome.reason });
72081
+ } else {
72082
+ connectionAccessStatuses.set(requestId, { state: "error", startedAt, reason: outcome.reason });
72083
+ }
72084
+ captureEvent("connection_access", {
72085
+ provider,
72086
+ action: args.action,
72087
+ outcome: outcome.state,
72088
+ source: "web_api"
72089
+ });
72090
+ }).catch((err) => {
72091
+ const startedAt = connectionAccessStatuses.get(requestId)?.startedAt ?? now();
72092
+ connectionAccessStatuses.set(requestId, {
72093
+ state: "error",
72094
+ startedAt,
72095
+ reason: err instanceof Error ? err.message : String(err)
72096
+ });
72097
+ captureException(err, { action: "connection_access", provider });
72098
+ });
72099
+ return { ok: true, changed: true, requestId, pendingApproval: true };
72100
+ }
71810
72101
  function handleGetSchedule(config) {
71811
72102
  const entries = collectScheduleEntries(config);
71812
72103
  const agentsDir = resolveAgentsDir(config);
@@ -71841,9 +72132,9 @@ async function handleGetApprovals() {
71841
72132
  }
71842
72133
 
71843
72134
  // src/web/webhook-handler.ts
71844
- import { appendFileSync as appendFileSync3, existsSync as existsSync46, mkdirSync as mkdirSync26, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
71845
- import { join as join40 } from "path";
71846
- import { homedir as homedir21 } from "os";
72135
+ import { appendFileSync as appendFileSync3, existsSync as existsSync47, mkdirSync as mkdirSync27, readFileSync as readFileSync43, writeFileSync as writeFileSync25 } from "fs";
72136
+ import { join as join42 } from "path";
72137
+ import { homedir as homedir22 } from "os";
71847
72138
 
71848
72139
  // src/web/webhook-verify.ts
71849
72140
  import { createHmac as createHmac2, timingSafeEqual } from "crypto";
@@ -71993,20 +72284,20 @@ function forwardToGateway(socketPath, req, opts = {}) {
71993
72284
  }
71994
72285
 
71995
72286
  // src/web/webhook-edge.ts
71996
- import { existsSync as existsSync45, readFileSync as readFileSync41 } from "fs";
71997
- import { join as join39 } from "path";
71998
- import { homedir as homedir20 } from "os";
72287
+ import { existsSync as existsSync46, readFileSync as readFileSync42 } from "fs";
72288
+ import { join as join41 } from "path";
72289
+ import { homedir as homedir21 } from "os";
71999
72290
  import { timingSafeEqual as timingSafeEqual2 } from "crypto";
72000
72291
  var EDGE_HEADER = "x-switchroom-edge";
72001
72292
  function edgeSecretPath() {
72002
- return join39(homedir20(), ".switchroom", "webhook-edge-secret");
72293
+ return join41(homedir21(), ".switchroom", "webhook-edge-secret");
72003
72294
  }
72004
72295
  function loadEdgeSecret(path4) {
72005
72296
  const p = path4 ?? edgeSecretPath();
72006
72297
  try {
72007
- if (!existsSync45(p))
72298
+ if (!existsSync46(p))
72008
72299
  return null;
72009
- const raw = readFileSync41(p, "utf-8").trim();
72300
+ const raw = readFileSync42(p, "utf-8").trim();
72010
72301
  return raw.length > 0 ? raw : null;
72011
72302
  } catch {
72012
72303
  return null;
@@ -72038,9 +72329,9 @@ var DEDUP_MAX = 1000;
72038
72329
  var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
72039
72330
  function loadDedupFile(path4) {
72040
72331
  try {
72041
- if (!existsSync46(path4))
72332
+ if (!existsSync47(path4))
72042
72333
  return {};
72043
- const raw = JSON.parse(readFileSync42(path4, "utf-8"));
72334
+ const raw = JSON.parse(readFileSync43(path4, "utf-8"));
72044
72335
  return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
72045
72336
  } catch {
72046
72337
  return {};
@@ -72054,7 +72345,7 @@ function saveDedupFile(path4, deliveries, now) {
72054
72345
  }
72055
72346
  const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
72056
72347
  const final = Object.fromEntries(sorted);
72057
- writeFileSync24(path4, JSON.stringify({ deliveries: final }), {
72348
+ writeFileSync25(path4, JSON.stringify({ deliveries: final }), {
72058
72349
  mode: 384
72059
72350
  });
72060
72351
  }
@@ -72062,8 +72353,8 @@ var agentDedupCache = new Map;
72062
72353
  function createFileDedupStore(resolveAgentDir) {
72063
72354
  return {
72064
72355
  check(agent, deliveryId, now) {
72065
- const telegramDir = join40(resolveAgentDir(agent), "telegram");
72066
- const filePath = join40(telegramDir, "webhook-dedup.json");
72356
+ const telegramDir = join42(resolveAgentDir(agent), "telegram");
72357
+ const filePath = join42(telegramDir, "webhook-dedup.json");
72067
72358
  if (!agentDedupCache.has(agent)) {
72068
72359
  agentDedupCache.set(agent, loadDedupFile(filePath));
72069
72360
  }
@@ -72073,7 +72364,7 @@ function createFileDedupStore(resolveAgentDir) {
72073
72364
  }
72074
72365
  deliveries[deliveryId] = now;
72075
72366
  try {
72076
- mkdirSync26(telegramDir, { recursive: true });
72367
+ mkdirSync27(telegramDir, { recursive: true });
72077
72368
  saveDedupFile(filePath, deliveries, now);
72078
72369
  } catch {}
72079
72370
  return;
@@ -72115,9 +72406,9 @@ function shouldWriteThrottleIssue(agent, source, now, windowMap) {
72115
72406
  return true;
72116
72407
  }
72117
72408
  function writeThrottleIssue(agent, source, now, telegramDir, log) {
72118
- const issuesPath = join40(telegramDir, "issues.jsonl");
72409
+ const issuesPath = join42(telegramDir, "issues.jsonl");
72119
72410
  try {
72120
- mkdirSync26(telegramDir, { recursive: true });
72411
+ mkdirSync27(telegramDir, { recursive: true });
72121
72412
  const record = {
72122
72413
  ts: now,
72123
72414
  agent,
@@ -72140,7 +72431,7 @@ function writeThrottleIssue(agent, source, now, telegramDir, log) {
72140
72431
  async function handleWebhookIngest(args, deps = {}) {
72141
72432
  const log = deps.log ?? ((s) => process.stderr.write(s));
72142
72433
  const now = (deps.now ?? Date.now)();
72143
- const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join40(homedir21(), ".switchroom", "agents", a));
72434
+ const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join42(homedir22(), ".switchroom", "agents", a));
72144
72435
  const rateLimiter = deps.rateLimiter ?? defaultRateLimiter;
72145
72436
  const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
72146
72437
  if (!args.agentExists) {
@@ -72204,7 +72495,7 @@ async function handleWebhookIngest(args, deps = {}) {
72204
72495
  if (retryAfter !== null) {
72205
72496
  if (!args.viaGateway) {
72206
72497
  const agentDir2 = resolveAgentDir(args.agent);
72207
- const telegramDir2 = join40(agentDir2, "telegram");
72498
+ const telegramDir2 = join42(agentDir2, "telegram");
72208
72499
  if (shouldWriteThrottleIssue(args.agent, source, now)) {
72209
72500
  writeThrottleIssue(args.agent, source, now, telegramDir2, log);
72210
72501
  }
@@ -72224,7 +72515,7 @@ async function handleWebhookIngest(args, deps = {}) {
72224
72515
  const eventType = source === "github" ? args.headers.get("x-github-event") ?? "unknown" : args.source;
72225
72516
  const rendered = source === "github" ? renderGithubEvent(eventType, payload) : renderGenericEvent(args.source, payload);
72226
72517
  if (args.viaGateway) {
72227
- const socketPath = join40(resolveAgentDir(args.agent), "telegram", "webhook.sock");
72518
+ const socketPath = join42(resolveAgentDir(args.agent), "telegram", "webhook.sock");
72228
72519
  const forward = deps.forwardFn ?? forwardToGateway;
72229
72520
  const deliveryId = source === "github" ? args.headers.get("x-github-delivery") ?? undefined : undefined;
72230
72521
  let resp;
@@ -72263,10 +72554,10 @@ async function handleWebhookIngest(args, deps = {}) {
72263
72554
  return jsonReply(202, { ok: true, recorded: true, ts: resp.ts });
72264
72555
  }
72265
72556
  const agentDir = resolveAgentDir(args.agent);
72266
- const telegramDir = join40(agentDir, "telegram");
72267
- const logPath = join40(telegramDir, "webhook-events.jsonl");
72557
+ const telegramDir = join42(agentDir, "telegram");
72558
+ const logPath = join42(telegramDir, "webhook-events.jsonl");
72268
72559
  try {
72269
- mkdirSync26(telegramDir, { recursive: true });
72560
+ mkdirSync27(telegramDir, { recursive: true });
72270
72561
  const record = {
72271
72562
  ts: now,
72272
72563
  source,
@@ -72317,15 +72608,15 @@ function resolveWebToken() {
72317
72608
  const fromEnv = process.env.SWITCHROOM_WEB_TOKEN;
72318
72609
  if (fromEnv && fromEnv.length > 0)
72319
72610
  return fromEnv;
72320
- const home2 = process.env.HOME ?? homedir22();
72321
- const tokenPath = join41(home2, ".switchroom", "web-token");
72322
- if (existsSync47(tokenPath)) {
72323
- const existing = readFileSync43(tokenPath, "utf8").trim();
72611
+ const home2 = process.env.HOME ?? homedir23();
72612
+ const tokenPath = join43(home2, ".switchroom", "web-token");
72613
+ if (existsSync48(tokenPath)) {
72614
+ const existing = readFileSync44(tokenPath, "utf8").trim();
72324
72615
  if (existing.length > 0)
72325
72616
  return existing;
72326
72617
  }
72327
72618
  const token = randomBytes11(32).toString("hex");
72328
- mkdirSync27(dirname9(tokenPath), { recursive: true, mode: 448 });
72619
+ mkdirSync28(dirname9(tokenPath), { recursive: true, mode: 448 });
72329
72620
  try {
72330
72621
  const fd = openSync10(tokenPath, fsConstants3.O_WRONLY | fsConstants3.O_CREAT | fsConstants3.O_EXCL, 384);
72331
72622
  try {
@@ -72337,7 +72628,7 @@ function resolveWebToken() {
72337
72628
  return token;
72338
72629
  } catch (err) {
72339
72630
  if (err.code === "EEXIST") {
72340
- const existing = readFileSync43(tokenPath, "utf8").trim();
72631
+ const existing = readFileSync44(tokenPath, "utf8").trim();
72341
72632
  if (existing.length > 0)
72342
72633
  return existing;
72343
72634
  }
@@ -72403,11 +72694,11 @@ function checkWsAuth(req, token, server) {
72403
72694
  return presented !== null && constantTimeEqual(presented, token);
72404
72695
  }
72405
72696
  function loadWebhookSecrets() {
72406
- const path4 = join41(homedir22(), ".switchroom", "webhook-secrets.json");
72407
- if (!existsSync47(path4))
72697
+ const path4 = join43(homedir23(), ".switchroom", "webhook-secrets.json");
72698
+ if (!existsSync48(path4))
72408
72699
  return {};
72409
72700
  try {
72410
- const parsed = JSON.parse(readFileSync43(path4, "utf-8"));
72701
+ const parsed = JSON.parse(readFileSync44(path4, "utf-8"));
72411
72702
  return parsed && typeof parsed === "object" ? parsed : {};
72412
72703
  } catch (err) {
72413
72704
  process.stderr.write(`webhook-ingest: failed to parse ${path4}: ${err.message} \u2014 webhooks will return 401 until fixed
@@ -72499,6 +72790,16 @@ function parseRoute(pathname, method) {
72499
72790
  if (method === "GET" && pathname === "/api/accounts") {
72500
72791
  return { handler: "getAccounts", params: {} };
72501
72792
  }
72793
+ if (method === "POST" && pathname === "/api/connections/access") {
72794
+ return { handler: "setConnectionAccess", params: {} };
72795
+ }
72796
+ const accessStatusMatch = pathname.match(/^\/api\/connections\/access\/([^/]+)$/);
72797
+ if (method === "GET" && accessStatusMatch) {
72798
+ return {
72799
+ handler: "getConnectionAccessStatus",
72800
+ params: { requestId: decodeURIComponent(accessStatusMatch[1]) }
72801
+ };
72802
+ }
72502
72803
  if (method === "POST" && pathname === "/api/auth/use") {
72503
72804
  return { handler: "useAccount", params: {} };
72504
72805
  }
@@ -72517,8 +72818,17 @@ function parseRoute(pathname, method) {
72517
72818
  }
72518
72819
  function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
72519
72820
  const uiDirRaw = resolve28(import.meta.dirname, "ui");
72520
- const uiDir = existsSync47(uiDirRaw) ? realpathSync3(uiDirRaw) : uiDirRaw;
72821
+ const uiDir = existsSync48(uiDirRaw) ? realpathSync3(uiDirRaw) : uiDirRaw;
72521
72822
  const token = resolveWebToken();
72823
+ const freshConfig = () => {
72824
+ if (!configPath)
72825
+ return config;
72826
+ try {
72827
+ return loadConfig(configPath);
72828
+ } catch {
72829
+ return config;
72830
+ }
72831
+ };
72522
72832
  const localhostOnly = hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
72523
72833
  const server = Bun.serve({
72524
72834
  port,
@@ -72614,11 +72924,11 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
72614
72924
  case "getSystemHealth":
72615
72925
  return (async () => jsonResponse(await handleGetSystemHealth()))();
72616
72926
  case "getGoogleAccounts":
72617
- return (async () => jsonResponse(await handleGetGoogleAccounts(config)))();
72927
+ return (async () => jsonResponse(await handleGetGoogleAccounts(freshConfig())))();
72618
72928
  case "getMicrosoftAccounts":
72619
- return (async () => jsonResponse(await handleGetMicrosoftAccounts(config)))();
72929
+ return (async () => jsonResponse(await handleGetMicrosoftAccounts(freshConfig())))();
72620
72930
  case "getNotionWorkspace":
72621
- return jsonResponse(handleGetNotionWorkspace(config));
72931
+ return jsonResponse(handleGetNotionWorkspace(freshConfig()));
72622
72932
  case "getSchedule":
72623
72933
  return jsonResponse(handleGetSchedule(config));
72624
72934
  case "getApprovals":
@@ -72644,6 +72954,28 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
72644
72954
  return jsonResponse(result);
72645
72955
  })();
72646
72956
  }
72957
+ case "setConnectionAccess": {
72958
+ return (async () => {
72959
+ if (!configPath) {
72960
+ return jsonResponse({ ok: false, error: "config path not available to the web server" }, 500);
72961
+ }
72962
+ let body;
72963
+ try {
72964
+ body = await req.json();
72965
+ } catch {
72966
+ return jsonResponse({ ok: false, error: "Invalid JSON body" }, 400);
72967
+ }
72968
+ const result = handleSetConnectionAccess(configPath, freshConfig(), {
72969
+ provider: String(body.provider ?? ""),
72970
+ account: String(body.account ?? ""),
72971
+ agent: String(body.agent ?? ""),
72972
+ action: String(body.action ?? "")
72973
+ });
72974
+ return jsonResponse(result, result.ok ? 200 : 400);
72975
+ })();
72976
+ }
72977
+ case "getConnectionAccessStatus":
72978
+ return jsonResponse(handleGetConnectionAccessStatus(route.params.requestId));
72647
72979
  case "refreshQuota": {
72648
72980
  return (async () => {
72649
72981
  let body = {};
@@ -72676,8 +73008,8 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
72676
73008
  }
72677
73009
  }
72678
73010
  let filePath = pathname === "/" ? "/index.html" : pathname;
72679
- const fullPath = join41(uiDir, filePath);
72680
- if (!existsSync47(fullPath)) {
73011
+ const fullPath = join43(uiDir, filePath);
73012
+ if (!existsSync48(fullPath)) {
72681
73013
  return new Response("Not Found", { status: 404 });
72682
73014
  }
72683
73015
  let realFullPath;
@@ -72692,7 +73024,7 @@ function startWebServer(config, port, hostname = "127.0.0.1", configPath) {
72692
73024
  }
72693
73025
  const ext = extname(realFullPath);
72694
73026
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
72695
- const content = readFileSync43(realFullPath);
73027
+ const content = readFileSync44(realFullPath);
72696
73028
  return new Response(content, {
72697
73029
  headers: { "Content-Type": contentType }
72698
73030
  });
@@ -72826,26 +73158,26 @@ Starting Switchroom dashboard...
72826
73158
  // src/cli/setup.ts
72827
73159
  init_source();
72828
73160
  init_loader();
72829
- import { existsSync as existsSync48, copyFileSync as copyFileSync8, readFileSync as readFileSync44, writeFileSync as writeFileSync25, mkdirSync as mkdirSync28 } from "node:fs";
73161
+ import { existsSync as existsSync49, copyFileSync as copyFileSync8, readFileSync as readFileSync45, writeFileSync as writeFileSync26, mkdirSync as mkdirSync29 } from "node:fs";
72830
73162
  import { resolve as resolve29, dirname as dirname10 } from "node:path";
72831
73163
  init_vault();
72832
73164
  init_manager();
72833
73165
 
72834
73166
  // src/cli/setup-posture-rewrite.ts
72835
- var import_yaml13 = __toESM(require_dist(), 1);
73167
+ var import_yaml15 = __toESM(require_dist(), 1);
72836
73168
  function insertVaultBrokerApprovalAuth(source, value = "telegram-id") {
72837
73169
  let doc;
72838
73170
  try {
72839
- doc = import_yaml13.default.parseDocument(source);
73171
+ doc = import_yaml15.default.parseDocument(source);
72840
73172
  } catch {
72841
73173
  return { kind: "not-found" };
72842
73174
  }
72843
73175
  const vault = doc.get("vault");
72844
- if (!vault || !import_yaml13.default.isMap(vault)) {
73176
+ if (!vault || !import_yaml15.default.isMap(vault)) {
72845
73177
  return { kind: "not-found" };
72846
73178
  }
72847
73179
  const broker = vault.get("broker");
72848
- if (!broker || !import_yaml13.default.isMap(broker)) {
73180
+ if (!broker || !import_yaml15.default.isMap(broker)) {
72849
73181
  return { kind: "not-found" };
72850
73182
  }
72851
73183
  if (broker.has("approvalAuth")) {
@@ -72896,7 +73228,7 @@ ${childIndent}approvalAuth: ${value}`;
72896
73228
  }
72897
73229
 
72898
73230
  // src/cli/supergroup-setup-yaml.ts
72899
- var import_yaml14 = __toESM(require_dist(), 1);
73231
+ var import_yaml16 = __toESM(require_dist(), 1);
72900
73232
  function isValidSupergroupChatId(value) {
72901
73233
  return /^-\d+$/.test(value);
72902
73234
  }
@@ -72904,23 +73236,23 @@ function setAgentSupergroupChatId(yamlText, agentName, chatId) {
72904
73236
  if (!isValidSupergroupChatId(chatId)) {
72905
73237
  throw new Error(`setAgentSupergroupChatId: chat_id must be a negative integer string (got "${chatId}")`);
72906
73238
  }
72907
- const doc = import_yaml14.parseDocument(yamlText);
73239
+ const doc = import_yaml16.parseDocument(yamlText);
72908
73240
  const root = doc.contents;
72909
- if (!import_yaml14.isMap(root)) {
73241
+ if (!import_yaml16.isMap(root)) {
72910
73242
  throw new Error("setAgentSupergroupChatId: YAML root is not a map");
72911
73243
  }
72912
73244
  const agents = root.get("agents", true);
72913
- if (!import_yaml14.isMap(agents)) {
73245
+ if (!import_yaml16.isMap(agents)) {
72914
73246
  throw new Error("setAgentSupergroupChatId: config has no `agents:` map");
72915
73247
  }
72916
73248
  const agent = agents.get(agentName, true);
72917
- if (!import_yaml14.isMap(agent)) {
73249
+ if (!import_yaml16.isMap(agent)) {
72918
73250
  throw new Error(`setAgentSupergroupChatId: agent "${agentName}" not found in config`);
72919
73251
  }
72920
73252
  const channels = agent.get("channels", true);
72921
- if (import_yaml14.isMap(channels)) {
73253
+ if (import_yaml16.isMap(channels)) {
72922
73254
  const telegram = channels.get("telegram", true);
72923
- if (import_yaml14.isMap(telegram)) {
73255
+ if (import_yaml16.isMap(telegram)) {
72924
73256
  if (telegram.get("chat_id") === chatId)
72925
73257
  return yamlText;
72926
73258
  telegram.set("chat_id", chatId);
@@ -73008,7 +73340,7 @@ async function stepConfigFile(configPath, nonInteractive) {
73008
73340
  existingConfig = null;
73009
73341
  }
73010
73342
  }
73011
- if (existingConfig && existsSync48(existingConfig)) {
73343
+ if (existingConfig && existsSync49(existingConfig)) {
73012
73344
  if (!nonInteractive) {
73013
73345
  const useExisting = await askYesNo(` Found ${source_default.cyan(existingConfig)}. Use it?`, true);
73014
73346
  if (!useExisting) {
@@ -73036,10 +73368,10 @@ async function copyExampleConfig(nonInteractive) {
73036
73368
  }
73037
73369
  const srcFile = resolve29(examplesDir, `${choice}.yaml`);
73038
73370
  const destFile = resolvePath("~/.switchroom/switchroom.yaml");
73039
- if (!existsSync48(srcFile)) {
73371
+ if (!existsSync49(srcFile)) {
73040
73372
  throw new ConfigError(`Example config not found: ${choice}.yaml`);
73041
73373
  }
73042
- mkdirSync28(dirname10(destFile), { recursive: true });
73374
+ mkdirSync29(dirname10(destFile), { recursive: true });
73043
73375
  copyFileSync8(srcFile, destFile);
73044
73376
  console.log(source_default.green(` Copied ${choice}.yaml -> ${destFile}`));
73045
73377
  console.log(source_default.yellow(` Edit ${destFile} to customize, then re-run switchroom setup.`));
@@ -73120,7 +73452,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
73120
73452
  try {
73121
73453
  const { openVault: openVault2 } = await Promise.resolve().then(() => (init_vault(), exports_vault));
73122
73454
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
73123
- if (existsSync48(vaultPath)) {
73455
+ if (existsSync49(vaultPath)) {
73124
73456
  const secrets = openVault2(passphrase, vaultPath);
73125
73457
  const key = rawToken.replace("vault:", "");
73126
73458
  const entry = secrets[key];
@@ -73148,7 +73480,7 @@ async function resolveOrPromptToken(rawToken, label, config, nonInteractive) {
73148
73480
  async function storeTokenInVault(config, vaultRef, token) {
73149
73481
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
73150
73482
  const key = vaultRef.replace("vault:", "");
73151
- if (!existsSync48(vaultPath)) {
73483
+ if (!existsSync49(vaultPath)) {
73152
73484
  console.log(source_default.gray(" Creating encrypted vault..."));
73153
73485
  let passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
73154
73486
  if (!passphrase) {
@@ -73321,7 +73653,7 @@ async function stepMemoryBackend(config, nonInteractive, switchroomConfigPath) {
73321
73653
  try {
73322
73654
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
73323
73655
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
73324
- if (passphrase && existsSync48(vaultPath)) {
73656
+ if (passphrase && existsSync49(vaultPath)) {
73325
73657
  const existing = getStringSecret(passphrase, vaultPath, "hindsight-api-key");
73326
73658
  if (existing) {
73327
73659
  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`."));
@@ -73449,16 +73781,16 @@ async function stepScaffoldAgents(config, agentBots, userId, nonInteractive, swi
73449
73781
  }
73450
73782
  async function ensureAuthActiveDefault(configPath) {
73451
73783
  const fs4 = await import("node:fs");
73452
- const { parseDocument: parseDocument7, isMap: isMap7 } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
73784
+ const { parseDocument: parseDocument8, isMap: isMap8 } = await Promise.resolve().then(() => __toESM(require_dist(), 1));
73453
73785
  const { atomicWriteFileSync: atomicWriteFileSync3 } = await Promise.resolve().then(() => (init_atomic(), exports_atomic));
73454
73786
  const { setAuthActive: setAuthActive2 } = await Promise.resolve().then(() => (init_auth_active_yaml(), exports_auth_active_yaml));
73455
73787
  const raw = fs4.readFileSync(configPath, "utf-8");
73456
- const doc = parseDocument7(raw);
73788
+ const doc = parseDocument8(raw);
73457
73789
  const root = doc.contents;
73458
- if (!isMap7(root))
73790
+ if (!isMap8(root))
73459
73791
  return;
73460
73792
  const existing = root.get("auth", true);
73461
- if (isMap7(existing) && existing.has("active"))
73793
+ if (isMap8(existing) && existing.has("active"))
73462
73794
  return;
73463
73795
  const after = setAuthActive2(raw, "default");
73464
73796
  if (after === raw)
@@ -73481,13 +73813,13 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
73481
73813
  return;
73482
73814
  }
73483
73815
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
73484
- if (!existsSync48(vaultPath)) {
73816
+ if (!existsSync49(vaultPath)) {
73485
73817
  console.log(source_default.gray(" Skipping (vault not created yet)."));
73486
73818
  return;
73487
73819
  }
73488
73820
  const credPathRaw = config.vault?.broker?.autoUnlockCredentialPath ?? "~/.switchroom/vault-auto-unlock";
73489
73821
  const credPath = resolvePath(credPathRaw);
73490
- if (config.vault?.broker?.autoUnlock === true && existsSync48(credPath)) {
73822
+ if (config.vault?.broker?.autoUnlock === true && existsSync49(credPath)) {
73491
73823
  console.log(source_default.green(` ${STEP_DONE} Already configured (${credPath})`));
73492
73824
  return;
73493
73825
  }
@@ -73553,12 +73885,12 @@ async function stepAutoUnlock(config, switchroomConfigPath, nonInteractive) {
73553
73885
  const choice = await askChoice(" Approval posture", [PASSPHRASE_CHOICE, TELEGRAM_ID_CHOICE]);
73554
73886
  if (choice === TELEGRAM_ID_CHOICE) {
73555
73887
  try {
73556
- const yamlPath = existsSync48(resolve29(process.cwd(), "switchroom.yaml")) ? resolve29(process.cwd(), "switchroom.yaml") : resolve29(process.cwd(), "switchroom.yml");
73557
- if (existsSync48(yamlPath)) {
73558
- const content = readFileSync44(yamlPath, "utf-8");
73888
+ const yamlPath = existsSync49(resolve29(process.cwd(), "switchroom.yaml")) ? resolve29(process.cwd(), "switchroom.yaml") : resolve29(process.cwd(), "switchroom.yml");
73889
+ if (existsSync49(yamlPath)) {
73890
+ const content = readFileSync45(yamlPath, "utf-8");
73559
73891
  const result = insertVaultBrokerApprovalAuth(content, "telegram-id");
73560
73892
  if (result.kind === "rewritten") {
73561
- writeFileSync25(yamlPath, result.content, "utf-8");
73893
+ writeFileSync26(yamlPath, result.content, "utf-8");
73562
73894
  console.log(source_default.green(` ${STEP_DONE} Set vault.broker.approvalAuth: telegram-id in ${yamlPath}`));
73563
73895
  } else if (result.kind === "already-set") {
73564
73896
  console.log(source_default.gray(" approvalAuth already set \u2014 leaving it alone."));
@@ -73589,8 +73921,8 @@ async function stepDangerousMode(config, nonInteractive) {
73589
73921
  resolve29(process.cwd(), "switchroom.yml")
73590
73922
  ];
73591
73923
  for (const configPath of configPaths) {
73592
- if (existsSync48(configPath)) {
73593
- let content = readFileSync44(configPath, "utf-8");
73924
+ if (existsSync49(configPath)) {
73925
+ let content = readFileSync45(configPath, "utf-8");
73594
73926
  const agentNames = Object.keys(config.agents);
73595
73927
  for (const name of agentNames) {
73596
73928
  const agentPattern = new RegExp(`(^ ${name}:\\s*\\n)`, "m");
@@ -73604,7 +73936,7 @@ async function stepDangerousMode(config, nonInteractive) {
73604
73936
  }
73605
73937
  config.agents[name].dangerous_mode = true;
73606
73938
  }
73607
- writeFileSync25(configPath, content, "utf-8");
73939
+ writeFileSync26(configPath, content, "utf-8");
73608
73940
  console.log(source_default.green(` ${STEP_DONE} Enabled dangerous_mode for all agents in ${configPath}`));
73609
73941
  break;
73610
73942
  }
@@ -73719,17 +74051,17 @@ init_doctor();
73719
74051
  init_source();
73720
74052
  init_loader();
73721
74053
  init_lifecycle();
73722
- import { cpSync as cpSync2, existsSync as existsSync55, mkdirSync as mkdirSync30, readFileSync as readFileSync49, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync24 } from "node:fs";
73723
- import { spawnSync as spawnSync8 } from "node:child_process";
73724
- import { join as join55, dirname as dirname13, resolve as resolve33 } from "node:path";
73725
- import { homedir as homedir33 } from "node:os";
73726
- var DEFAULT_COMPOSE_PATH = join55(homedir33(), ".switchroom", "compose", "docker-compose.yml");
74054
+ import { cpSync as cpSync2, existsSync as existsSync56, mkdirSync as mkdirSync31, readFileSync as readFileSync50, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync24 } from "node:fs";
74055
+ import { spawnSync as spawnSync9 } from "node:child_process";
74056
+ import { join as join57, dirname as dirname13, resolve as resolve33 } from "node:path";
74057
+ import { homedir as homedir34 } from "node:os";
74058
+ var DEFAULT_COMPOSE_PATH = join57(homedir34(), ".switchroom", "compose", "docker-compose.yml");
73727
74059
  function runningFromSwitchroomCheckout(scriptPath) {
73728
74060
  let dir = dirname13(scriptPath);
73729
74061
  for (let i = 0;i < 12; i++) {
73730
- if (existsSync55(join55(dir, ".git"))) {
74062
+ if (existsSync56(join57(dir, ".git"))) {
73731
74063
  try {
73732
- const pkg = JSON.parse(readFileSync49(join55(dir, "package.json"), "utf-8"));
74064
+ const pkg = JSON.parse(readFileSync50(join57(dir, "package.json"), "utf-8"));
73733
74065
  if (pkg.name === "switchroom")
73734
74066
  return true;
73735
74067
  } catch {}
@@ -73781,7 +74113,7 @@ function planUpdate(opts) {
73781
74113
  steps.push({
73782
74114
  name: "pull-images",
73783
74115
  description: "Pull broker / kernel / agent images from GHCR",
73784
- skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync55(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
74116
+ skipReason: opts.skipImages ? "--skip-images flag set" : !existsSync56(composePath) ? `compose file not found at ${composePath} (run \`switchroom apply --compose-only\` first)` : undefined,
73785
74117
  run: () => {
73786
74118
  const r = runner("docker", [
73787
74119
  "compose",
@@ -73880,17 +74212,17 @@ function planUpdate(opts) {
73880
74212
  return;
73881
74213
  }
73882
74214
  const source = resolve33(import.meta.dirname, "../../skills");
73883
- const dest = join55(homedir33(), ".switchroom", "skills", "_bundled");
73884
- if (!existsSync55(source)) {
74215
+ const dest = join57(homedir34(), ".switchroom", "skills", "_bundled");
74216
+ if (!existsSync56(source)) {
73885
74217
  process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
73886
74218
  `);
73887
74219
  return;
73888
74220
  }
73889
74221
  try {
73890
- if (existsSync55(dest)) {
74222
+ if (existsSync56(dest)) {
73891
74223
  rmSync12(dest, { recursive: true, force: true });
73892
74224
  }
73893
- mkdirSync30(dirname13(dest), { recursive: true });
74225
+ mkdirSync31(dirname13(dest), { recursive: true });
73894
74226
  cpSync2(source, dest, { recursive: true, dereference: false });
73895
74227
  } catch (err) {
73896
74228
  throw new Error(`sync-bundled-skills failed: ${err.message}`);
@@ -73949,7 +74281,7 @@ function planUpdate(opts) {
73949
74281
  return steps;
73950
74282
  }
73951
74283
  function defaultRunner(cmd, args) {
73952
- const r = spawnSync8(cmd, args, { stdio: "inherit" });
74284
+ const r = spawnSync9(cmd, args, { stdio: "inherit" });
73953
74285
  return { status: r.status ?? 1 };
73954
74286
  }
73955
74287
  function writeMarkerInPreferredLocation(agent, reason, runner) {
@@ -73994,10 +74326,10 @@ function defaultStatusProbe(composePath) {
73994
74326
  } catch {}
73995
74327
  let dir = dirname13(scriptPath);
73996
74328
  for (let i = 0;i < 8; i++) {
73997
- const pkgPath = join55(dir, "package.json");
73998
- if (existsSync55(pkgPath)) {
74329
+ const pkgPath = join57(dir, "package.json");
74330
+ if (existsSync56(pkgPath)) {
73999
74331
  try {
74000
- const pkg = JSON.parse(readFileSync49(pkgPath, "utf-8"));
74332
+ const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
74001
74333
  if (typeof pkg.version === "string")
74002
74334
  cliVersion = pkg.version;
74003
74335
  } catch (err) {
@@ -74018,13 +74350,13 @@ function defaultStatusProbe(composePath) {
74018
74350
  warnings.push("could not resolve CLI version (no package.json found above the resolved script path)");
74019
74351
  }
74020
74352
  const services = [];
74021
- if (!existsSync55(composePath)) {
74353
+ if (!existsSync56(composePath)) {
74022
74354
  warnings.push(`compose file not found at ${composePath}; service status unknown`);
74023
74355
  return { cliVersion, cliBuiltAt, services, warnings };
74024
74356
  }
74025
74357
  let serviceList = [];
74026
74358
  try {
74027
- const r = spawnSync8("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
74359
+ const r = spawnSync9("docker", ["compose", "-p", "switchroom", "-f", composePath, "config", "--services"], { encoding: "utf-8", timeout: 1e4 });
74028
74360
  if (r.status !== 0) {
74029
74361
  warnings.push(`docker compose config --services failed: ${r.stderr?.trim() ?? r.error?.message ?? "unknown"}`);
74030
74362
  return { cliVersion, cliBuiltAt, services, warnings };
@@ -74041,7 +74373,7 @@ function defaultStatusProbe(composePath) {
74041
74373
  let containerCreatedAt = null;
74042
74374
  let status = "<unknown>";
74043
74375
  try {
74044
- const r = spawnSync8("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
74376
+ const r = spawnSync9("docker", ["inspect", "-f", "{{.Config.Image}}|{{.Created}}|{{.State.Status}}", containerName2], { encoding: "utf-8", timeout: 5000 });
74045
74377
  if (r.status === 0) {
74046
74378
  const [img, created, st] = r.stdout.trim().split("|");
74047
74379
  image = img ?? null;
@@ -74057,7 +74389,7 @@ function defaultStatusProbe(composePath) {
74057
74389
  let imagePulledAt = null;
74058
74390
  if (image) {
74059
74391
  try {
74060
- const r = spawnSync8("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
74392
+ const r = spawnSync9("docker", ["image", "inspect", "-f", "{{.Id}}|{{.Created}}|{{.Metadata.LastTagTime}}", image], { encoding: "utf-8", timeout: 5000 });
74061
74393
  if (r.status === 0) {
74062
74394
  const [id, created, lastTag] = r.stdout.trim().split("|");
74063
74395
  imageDigestShort = id?.replace(/^sha256:/, "").slice(0, 12) ?? null;
@@ -74213,8 +74545,8 @@ init_source();
74213
74545
  init_helpers();
74214
74546
  init_lifecycle();
74215
74547
  import { execSync as execSync4 } from "node:child_process";
74216
- import { existsSync as existsSync56, readFileSync as readFileSync50 } from "node:fs";
74217
- import { dirname as dirname14, join as join56 } from "node:path";
74548
+ import { existsSync as existsSync57, readFileSync as readFileSync51 } from "node:fs";
74549
+ import { dirname as dirname14, join as join58 } from "node:path";
74218
74550
  function getClaudeCodeVersion() {
74219
74551
  try {
74220
74552
  const out = execSync4("claude --version 2>/dev/null", {
@@ -74264,11 +74596,11 @@ function formatUptime3(timestamp) {
74264
74596
  function locateSwitchroomInstallDir() {
74265
74597
  let dir = import.meta.dirname;
74266
74598
  for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
74267
- const pkgPath = join56(dir, "package.json");
74268
- if (existsSync56(pkgPath)) {
74599
+ const pkgPath = join58(dir, "package.json");
74600
+ if (existsSync57(pkgPath)) {
74269
74601
  try {
74270
- const pkg = JSON.parse(readFileSync50(pkgPath, "utf-8"));
74271
- if (pkg.name === "switchroom" && existsSync56(join56(dir, ".git"))) {
74602
+ const pkg = JSON.parse(readFileSync51(pkgPath, "utf-8"));
74603
+ if (pkg.name === "switchroom" && existsSync57(join58(dir, ".git"))) {
74272
74604
  return dir;
74273
74605
  }
74274
74606
  } catch {}
@@ -74494,18 +74826,18 @@ function registerHandoffCommand(program3) {
74494
74826
  // src/issues/store.ts
74495
74827
  import {
74496
74828
  closeSync as closeSync11,
74497
- existsSync as existsSync57,
74498
- mkdirSync as mkdirSync31,
74829
+ existsSync as existsSync58,
74830
+ mkdirSync as mkdirSync32,
74499
74831
  openSync as openSync11,
74500
74832
  readdirSync as readdirSync21,
74501
- readFileSync as readFileSync51,
74833
+ readFileSync as readFileSync52,
74502
74834
  renameSync as renameSync12,
74503
74835
  statSync as statSync25,
74504
74836
  unlinkSync as unlinkSync11,
74505
- writeFileSync as writeFileSync26,
74837
+ writeFileSync as writeFileSync27,
74506
74838
  writeSync as writeSync7
74507
74839
  } from "node:fs";
74508
- import { join as join57 } from "node:path";
74840
+ import { join as join59 } from "node:path";
74509
74841
  import { randomBytes as randomBytes12 } from "node:crypto";
74510
74842
  import { execSync as execSync5 } from "node:child_process";
74511
74843
 
@@ -74903,12 +75235,12 @@ function redactedMarker(ruleId) {
74903
75235
  var ISSUES_FILE = "issues.jsonl";
74904
75236
  var ISSUES_LOCK = "issues.lock";
74905
75237
  function readAll(stateDir) {
74906
- const path4 = join57(stateDir, ISSUES_FILE);
74907
- if (!existsSync57(path4))
75238
+ const path4 = join59(stateDir, ISSUES_FILE);
75239
+ if (!existsSync58(path4))
74908
75240
  return [];
74909
75241
  let raw;
74910
75242
  try {
74911
- raw = readFileSync51(path4, "utf-8");
75243
+ raw = readFileSync52(path4, "utf-8");
74912
75244
  } catch {
74913
75245
  return [];
74914
75246
  }
@@ -74981,7 +75313,7 @@ function record(stateDir, input, nowFn = Date.now) {
74981
75313
  });
74982
75314
  }
74983
75315
  function resolve36(stateDir, fingerprint, nowFn = Date.now) {
74984
- if (!existsSync57(join57(stateDir, ISSUES_FILE)))
75316
+ if (!existsSync58(join59(stateDir, ISSUES_FILE)))
74985
75317
  return 0;
74986
75318
  return withLock(stateDir, () => {
74987
75319
  const all = readAll(stateDir);
@@ -74999,7 +75331,7 @@ function resolve36(stateDir, fingerprint, nowFn = Date.now) {
74999
75331
  });
75000
75332
  }
75001
75333
  function resolveAllBySource(stateDir, source, nowFn = Date.now) {
75002
- if (!existsSync57(join57(stateDir, ISSUES_FILE)))
75334
+ if (!existsSync58(join59(stateDir, ISSUES_FILE)))
75003
75335
  return 0;
75004
75336
  return withLock(stateDir, () => {
75005
75337
  const all = readAll(stateDir);
@@ -75017,7 +75349,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
75017
75349
  });
75018
75350
  }
75019
75351
  function prune(stateDir, opts = {}) {
75020
- if (!existsSync57(join57(stateDir, ISSUES_FILE)))
75352
+ if (!existsSync58(join59(stateDir, ISSUES_FILE)))
75021
75353
  return 0;
75022
75354
  return withLock(stateDir, () => {
75023
75355
  const all = readAll(stateDir);
@@ -75047,16 +75379,16 @@ function prune(stateDir, opts = {}) {
75047
75379
  });
75048
75380
  }
75049
75381
  function ensureDir(stateDir) {
75050
- mkdirSync31(stateDir, { recursive: true });
75382
+ mkdirSync32(stateDir, { recursive: true });
75051
75383
  }
75052
75384
  function writeAll(stateDir, events) {
75053
- const path4 = join57(stateDir, ISSUES_FILE);
75385
+ const path4 = join59(stateDir, ISSUES_FILE);
75054
75386
  sweepOrphanTmpFiles(stateDir);
75055
75387
  const tmp = `${path4}.tmp-${process.pid}-${randomBytes12(4).toString("hex")}`;
75056
75388
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
75057
75389
  `) + `
75058
75390
  `;
75059
- writeFileSync26(tmp, body, "utf-8");
75391
+ writeFileSync27(tmp, body, "utf-8");
75060
75392
  renameSync12(tmp, path4);
75061
75393
  }
75062
75394
  var ORPHAN_TMP_TTL_MS = 60000;
@@ -75072,7 +75404,7 @@ function sweepOrphanTmpFiles(stateDir) {
75072
75404
  for (const entry of entries) {
75073
75405
  if (!entry.startsWith(TMP_PREFIX))
75074
75406
  continue;
75075
- const tmpPath = join57(stateDir, entry);
75407
+ const tmpPath = join59(stateDir, entry);
75076
75408
  try {
75077
75409
  const stat = statSync25(tmpPath);
75078
75410
  if (stat.mtimeMs < cutoff) {
@@ -75084,7 +75416,7 @@ function sweepOrphanTmpFiles(stateDir) {
75084
75416
  var LOCK_RETRY_MS = 25;
75085
75417
  var LOCK_TIMEOUT_MS = 1e4;
75086
75418
  function withLock(stateDir, fn) {
75087
- const lockPath = join57(stateDir, ISSUES_LOCK);
75419
+ const lockPath = join59(stateDir, ISSUES_LOCK);
75088
75420
  const startedAt = Date.now();
75089
75421
  let fd = null;
75090
75422
  while (fd === null) {
@@ -75119,7 +75451,7 @@ function withLock(stateDir, fn) {
75119
75451
  function tryStealStaleLock(lockPath) {
75120
75452
  let pidStr;
75121
75453
  try {
75122
- pidStr = readFileSync51(lockPath, "utf-8").trim();
75454
+ pidStr = readFileSync52(lockPath, "utf-8").trim();
75123
75455
  } catch {
75124
75456
  return true;
75125
75457
  }
@@ -75367,21 +75699,21 @@ function relTime(deltaMs) {
75367
75699
 
75368
75700
  // src/cli/deps.ts
75369
75701
  init_source();
75370
- import { existsSync as existsSync60 } from "node:fs";
75371
- import { homedir as homedir36 } from "node:os";
75372
- import { join as join60, resolve as resolve37 } from "node:path";
75702
+ import { existsSync as existsSync61 } from "node:fs";
75703
+ import { homedir as homedir37 } from "node:os";
75704
+ import { join as join62, resolve as resolve37 } from "node:path";
75373
75705
 
75374
75706
  // src/deps/python.ts
75375
75707
  import { createHash as createHash11 } from "node:crypto";
75376
75708
  import {
75377
- existsSync as existsSync58,
75378
- mkdirSync as mkdirSync32,
75379
- readFileSync as readFileSync52,
75709
+ existsSync as existsSync59,
75710
+ mkdirSync as mkdirSync33,
75711
+ readFileSync as readFileSync53,
75380
75712
  rmSync as rmSync13,
75381
- writeFileSync as writeFileSync27
75713
+ writeFileSync as writeFileSync28
75382
75714
  } from "node:fs";
75383
- import { dirname as dirname15, join as join58 } from "node:path";
75384
- import { homedir as homedir34 } from "node:os";
75715
+ import { dirname as dirname15, join as join60 } from "node:path";
75716
+ import { homedir as homedir35 } from "node:os";
75385
75717
  import { execFileSync as execFileSync18 } from "node:child_process";
75386
75718
 
75387
75719
  class PythonEnvError extends Error {
@@ -75393,26 +75725,26 @@ class PythonEnvError extends Error {
75393
75725
  }
75394
75726
  }
75395
75727
  function defaultPythonCacheRoot() {
75396
- return join58(homedir34(), ".switchroom", "deps", "python");
75728
+ return join60(homedir35(), ".switchroom", "deps", "python");
75397
75729
  }
75398
75730
  function hashFile(path4) {
75399
- return createHash11("sha256").update(readFileSync52(path4)).digest("hex");
75731
+ return createHash11("sha256").update(readFileSync53(path4)).digest("hex");
75400
75732
  }
75401
75733
  function ensurePythonEnv(opts) {
75402
75734
  const { skillName, requirementsPath, force = false } = opts;
75403
75735
  const cacheRoot = opts.cacheRoot ?? defaultPythonCacheRoot();
75404
75736
  const hostPython = opts.pythonBin ?? "python3";
75405
- if (!existsSync58(requirementsPath)) {
75737
+ if (!existsSync59(requirementsPath)) {
75406
75738
  throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
75407
75739
  }
75408
- const venvDir = join58(cacheRoot, skillName);
75409
- const stampPath = join58(venvDir, ".requirements.sha256");
75410
- const binDir = join58(venvDir, "bin");
75411
- const pythonBin = join58(binDir, "python");
75412
- const pipBin = join58(binDir, "pip");
75740
+ const venvDir = join60(cacheRoot, skillName);
75741
+ const stampPath = join60(venvDir, ".requirements.sha256");
75742
+ const binDir = join60(venvDir, "bin");
75743
+ const pythonBin = join60(binDir, "python");
75744
+ const pipBin = join60(binDir, "pip");
75413
75745
  const targetHash = hashFile(requirementsPath);
75414
- if (!force && existsSync58(stampPath) && existsSync58(pythonBin)) {
75415
- const existingHash = readFileSync52(stampPath, "utf8").trim();
75746
+ if (!force && existsSync59(stampPath) && existsSync59(pythonBin)) {
75747
+ const existingHash = readFileSync53(stampPath, "utf8").trim();
75416
75748
  if (existingHash === targetHash) {
75417
75749
  return {
75418
75750
  skillName,
@@ -75424,10 +75756,10 @@ function ensurePythonEnv(opts) {
75424
75756
  };
75425
75757
  }
75426
75758
  }
75427
- if (existsSync58(venvDir)) {
75759
+ if (existsSync59(venvDir)) {
75428
75760
  rmSync13(venvDir, { recursive: true, force: true });
75429
75761
  }
75430
- mkdirSync32(dirname15(venvDir), { recursive: true });
75762
+ mkdirSync33(dirname15(venvDir), { recursive: true });
75431
75763
  try {
75432
75764
  execFileSync18(hostPython, ["-m", "venv", venvDir], { stdio: "pipe" });
75433
75765
  } catch (err) {
@@ -75446,7 +75778,7 @@ function ensurePythonEnv(opts) {
75446
75778
  const e = err;
75447
75779
  throw new PythonEnvError(`Failed to install requirements for skill "${skillName}": ${e.message}`, e.stderr?.toString());
75448
75780
  }
75449
- writeFileSync27(stampPath, targetHash + `
75781
+ writeFileSync28(stampPath, targetHash + `
75450
75782
  `);
75451
75783
  return {
75452
75784
  skillName,
@@ -75462,14 +75794,14 @@ function ensurePythonEnv(opts) {
75462
75794
  import { createHash as createHash12 } from "node:crypto";
75463
75795
  import {
75464
75796
  copyFileSync as copyFileSync9,
75465
- existsSync as existsSync59,
75466
- mkdirSync as mkdirSync33,
75467
- readFileSync as readFileSync53,
75797
+ existsSync as existsSync60,
75798
+ mkdirSync as mkdirSync34,
75799
+ readFileSync as readFileSync54,
75468
75800
  rmSync as rmSync14,
75469
- writeFileSync as writeFileSync28
75801
+ writeFileSync as writeFileSync29
75470
75802
  } from "node:fs";
75471
- import { dirname as dirname16, join as join59 } from "node:path";
75472
- import { homedir as homedir35 } from "node:os";
75803
+ import { dirname as dirname16, join as join61 } from "node:path";
75804
+ import { homedir as homedir36 } from "node:os";
75473
75805
  import { execFileSync as execFileSync19 } from "node:child_process";
75474
75806
 
75475
75807
  class NodeEnvError extends Error {
@@ -75492,23 +75824,23 @@ var LOCKFILES_FOR = {
75492
75824
  npm: ["package-lock.json"]
75493
75825
  };
75494
75826
  function defaultNodeCacheRoot() {
75495
- return join59(homedir35(), ".switchroom", "deps", "node");
75827
+ return join61(homedir36(), ".switchroom", "deps", "node");
75496
75828
  }
75497
75829
  function hashDepInputs(packageJsonPath) {
75498
75830
  const sourceDir = dirname16(packageJsonPath);
75499
75831
  const hasher = createHash12("sha256");
75500
75832
  hasher.update(`package.json
75501
75833
  `);
75502
- hasher.update(readFileSync53(packageJsonPath));
75834
+ hasher.update(readFileSync54(packageJsonPath));
75503
75835
  for (const lockName of ALL_LOCKFILES) {
75504
- const lockPath = join59(sourceDir, lockName);
75505
- if (existsSync59(lockPath)) {
75836
+ const lockPath = join61(sourceDir, lockName);
75837
+ if (existsSync60(lockPath)) {
75506
75838
  hasher.update(`
75507
75839
  `);
75508
75840
  hasher.update(lockName);
75509
75841
  hasher.update(`
75510
75842
  `);
75511
- hasher.update(readFileSync53(lockPath));
75843
+ hasher.update(readFileSync54(lockPath));
75512
75844
  }
75513
75845
  }
75514
75846
  return hasher.digest("hex");
@@ -75517,17 +75849,17 @@ function ensureNodeEnv(opts) {
75517
75849
  const { skillName, packageJsonPath, force = false } = opts;
75518
75850
  const cacheRoot = opts.cacheRoot ?? defaultNodeCacheRoot();
75519
75851
  const installer = opts.installer ?? "bun";
75520
- if (!existsSync59(packageJsonPath)) {
75852
+ if (!existsSync60(packageJsonPath)) {
75521
75853
  throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
75522
75854
  }
75523
75855
  const sourceDir = dirname16(packageJsonPath);
75524
- const envDir = join59(cacheRoot, skillName);
75525
- const stampPath = join59(envDir, ".package.sha256");
75526
- const nodeModulesDir = join59(envDir, "node_modules");
75527
- const binDir = join59(nodeModulesDir, ".bin");
75856
+ const envDir = join61(cacheRoot, skillName);
75857
+ const stampPath = join61(envDir, ".package.sha256");
75858
+ const nodeModulesDir = join61(envDir, "node_modules");
75859
+ const binDir = join61(nodeModulesDir, ".bin");
75528
75860
  const targetHash = hashDepInputs(packageJsonPath);
75529
- if (!force && existsSync59(stampPath) && existsSync59(nodeModulesDir)) {
75530
- const existingHash = readFileSync53(stampPath, "utf8").trim();
75861
+ if (!force && existsSync60(stampPath) && existsSync60(nodeModulesDir)) {
75862
+ const existingHash = readFileSync54(stampPath, "utf8").trim();
75531
75863
  if (existingHash === targetHash) {
75532
75864
  return {
75533
75865
  skillName,
@@ -75538,16 +75870,16 @@ function ensureNodeEnv(opts) {
75538
75870
  };
75539
75871
  }
75540
75872
  }
75541
- if (existsSync59(envDir)) {
75873
+ if (existsSync60(envDir)) {
75542
75874
  rmSync14(envDir, { recursive: true, force: true });
75543
75875
  }
75544
- mkdirSync33(envDir, { recursive: true });
75545
- copyFileSync9(packageJsonPath, join59(envDir, "package.json"));
75876
+ mkdirSync34(envDir, { recursive: true });
75877
+ copyFileSync9(packageJsonPath, join61(envDir, "package.json"));
75546
75878
  let copiedLockfile = false;
75547
75879
  for (const lockName of LOCKFILES_FOR[installer]) {
75548
- const lockPath = join59(sourceDir, lockName);
75549
- if (existsSync59(lockPath)) {
75550
- copyFileSync9(lockPath, join59(envDir, lockName));
75880
+ const lockPath = join61(sourceDir, lockName);
75881
+ if (existsSync60(lockPath)) {
75882
+ copyFileSync9(lockPath, join61(envDir, lockName));
75551
75883
  copiedLockfile = true;
75552
75884
  }
75553
75885
  }
@@ -75563,7 +75895,7 @@ function ensureNodeEnv(opts) {
75563
75895
  const e = err;
75564
75896
  throw new NodeEnvError(`Failed to install node deps for skill "${skillName}" with ${installer}: ${e.message}`, e.stderr?.toString());
75565
75897
  }
75566
- writeFileSync28(stampPath, targetHash + `
75898
+ writeFileSync29(stampPath, targetHash + `
75567
75899
  `);
75568
75900
  return {
75569
75901
  skillName,
@@ -75576,28 +75908,28 @@ function ensureNodeEnv(opts) {
75576
75908
 
75577
75909
  // src/cli/deps.ts
75578
75910
  function builtinSkillsRoot() {
75579
- return resolve37(homedir36(), ".switchroom/skills/_bundled");
75911
+ return resolve37(homedir37(), ".switchroom/skills/_bundled");
75580
75912
  }
75581
75913
  function registerDepsCommand(program3) {
75582
75914
  const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
75583
75915
  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) => {
75584
75916
  const skillsRoot = builtinSkillsRoot();
75585
- if (!existsSync60(skillsRoot)) {
75917
+ if (!existsSync61(skillsRoot)) {
75586
75918
  console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
75587
75919
  process.exit(1);
75588
75920
  }
75589
- const skillDir = join60(skillsRoot, skill);
75590
- if (!existsSync60(skillDir)) {
75921
+ const skillDir = join62(skillsRoot, skill);
75922
+ if (!existsSync61(skillDir)) {
75591
75923
  console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
75592
75924
  process.exit(1);
75593
75925
  }
75594
- const requirementsPath = join60(skillDir, "requirements.txt");
75595
- const packageJsonPath = join60(skillDir, "package.json");
75596
- const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync60(requirementsPath));
75597
- const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync60(packageJsonPath));
75926
+ const requirementsPath = join62(skillDir, "requirements.txt");
75927
+ const packageJsonPath = join62(skillDir, "package.json");
75928
+ const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync61(requirementsPath));
75929
+ const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync61(packageJsonPath));
75598
75930
  let did = 0;
75599
75931
  if (wantPython) {
75600
- if (!existsSync60(requirementsPath)) {
75932
+ if (!existsSync61(requirementsPath)) {
75601
75933
  console.error(source_default.red(`Skill "${skill}" has no requirements.txt at ${requirementsPath}`));
75602
75934
  process.exit(1);
75603
75935
  }
@@ -75621,7 +75953,7 @@ function registerDepsCommand(program3) {
75621
75953
  }
75622
75954
  }
75623
75955
  if (wantNode) {
75624
- if (!existsSync60(packageJsonPath)) {
75956
+ if (!existsSync61(packageJsonPath)) {
75625
75957
  console.error(source_default.red(`Skill "${skill}" has no package.json at ${packageJsonPath}`));
75626
75958
  process.exit(1);
75627
75959
  }
@@ -75654,9 +75986,9 @@ function registerDepsCommand(program3) {
75654
75986
  // src/cli/workspace.ts
75655
75987
  init_helpers();
75656
75988
  init_loader();
75657
- import { existsSync as existsSync61 } from "node:fs";
75989
+ import { existsSync as existsSync62 } from "node:fs";
75658
75990
  import { resolve as resolve38, sep as sep3 } from "node:path";
75659
- import { spawnSync as spawnSync9 } from "node:child_process";
75991
+ import { spawnSync as spawnSync10 } from "node:child_process";
75660
75992
 
75661
75993
  // src/agents/workspace.ts
75662
75994
  import { readFile, stat } from "node:fs/promises";
@@ -75864,6 +76196,7 @@ function buildBootstrapPromptWarning(params) {
75864
76196
 
75865
76197
  // src/agents/bootstrap-types.ts
75866
76198
  var DEFAULT_AGENTS_FILENAME = "AGENTS.md";
76199
+ var DEFAULT_SOUL_DEFAULT_FILENAME = "SOUL.default.md";
75867
76200
  var DEFAULT_SOUL_FILENAME = "SOUL.md";
75868
76201
  var DEFAULT_TOOLS_FILENAME = "TOOLS.md";
75869
76202
  var DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
@@ -75877,6 +76210,7 @@ var DEFAULT_WORKSPACE_DIR_NAME = "workspace";
75877
76210
  var DEFAULT_MEMORY_SUBDIR = "memory";
75878
76211
  var STABLE_BOOTSTRAP_FILENAMES = [
75879
76212
  DEFAULT_AGENTS_FILENAME,
76213
+ DEFAULT_SOUL_DEFAULT_FILENAME,
75880
76214
  DEFAULT_SOUL_FILENAME,
75881
76215
  DEFAULT_IDENTITY_FILENAME,
75882
76216
  DEFAULT_USER_FILENAME,
@@ -76369,7 +76703,7 @@ function registerWorkspaceCommand(program3) {
76369
76703
  process.exit(1);
76370
76704
  }
76371
76705
  const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
76372
- const child = spawnSync9(editor, [target], { stdio: "inherit" });
76706
+ const child = spawnSync10(editor, [target], { stdio: "inherit" });
76373
76707
  if (child.status !== 0 && child.status !== null) {
76374
76708
  process.exit(child.status);
76375
76709
  }
@@ -76431,12 +76765,12 @@ function registerWorkspaceCommand(program3) {
76431
76765
  if (!dir)
76432
76766
  return;
76433
76767
  const gitDir = resolve38(dir, ".git");
76434
- if (!existsSync61(gitDir)) {
76768
+ if (!existsSync62(gitDir)) {
76435
76769
  process.stdout.write(`Workspace is not a git repository. Re-run \`switchroom agent create ${agentName}\` ` + `or manually \`git init\` in ${dir} to enable versioning.
76436
76770
  `);
76437
76771
  return;
76438
76772
  }
76439
- const statusResult = spawnSync9("git", ["status", "--short"], {
76773
+ const statusResult = spawnSync10("git", ["status", "--short"], {
76440
76774
  cwd: dir,
76441
76775
  encoding: "utf-8"
76442
76776
  });
@@ -76451,7 +76785,7 @@ function registerWorkspaceCommand(program3) {
76451
76785
  return;
76452
76786
  }
76453
76787
  const message = opts.message || `checkpoint: ${new Date().toISOString()}`;
76454
- const addResult = spawnSync9("git", ["add", "-A"], {
76788
+ const addResult = spawnSync10("git", ["add", "-A"], {
76455
76789
  cwd: dir,
76456
76790
  encoding: "utf-8"
76457
76791
  });
@@ -76460,7 +76794,7 @@ function registerWorkspaceCommand(program3) {
76460
76794
  `);
76461
76795
  process.exit(1);
76462
76796
  }
76463
- const commitResult = spawnSync9("git", ["commit", "-m", message], {
76797
+ const commitResult = spawnSync10("git", ["commit", "-m", message], {
76464
76798
  cwd: dir,
76465
76799
  encoding: "utf-8"
76466
76800
  });
@@ -76469,7 +76803,7 @@ function registerWorkspaceCommand(program3) {
76469
76803
  `);
76470
76804
  process.exit(1);
76471
76805
  }
76472
- const shaResult = spawnSync9("git", ["rev-parse", "--short", "HEAD"], {
76806
+ const shaResult = spawnSync10("git", ["rev-parse", "--short", "HEAD"], {
76473
76807
  cwd: dir,
76474
76808
  encoding: "utf-8"
76475
76809
  });
@@ -76485,12 +76819,12 @@ function registerWorkspaceCommand(program3) {
76485
76819
  if (!dir)
76486
76820
  return;
76487
76821
  const gitDir = resolve38(dir, ".git");
76488
- if (!existsSync61(gitDir)) {
76822
+ if (!existsSync62(gitDir)) {
76489
76823
  process.stdout.write(`Workspace is not a git repository.
76490
76824
  `);
76491
76825
  return;
76492
76826
  }
76493
- const child = spawnSync9("git", ["status", "--short"], {
76827
+ const child = spawnSync10("git", ["status", "--short"], {
76494
76828
  cwd: dir,
76495
76829
  stdio: "inherit"
76496
76830
  });
@@ -76510,7 +76844,7 @@ function resolveAgentWorkspaceDirOrExit(program3, agentName) {
76510
76844
  const agentsDir = resolveAgentsDir(config);
76511
76845
  const agentDir = resolve38(agentsDir, agentName);
76512
76846
  const dir = resolveAgentWorkspaceDir(agentDir);
76513
- if (!existsSync61(dir)) {
76847
+ if (!existsSync62(dir)) {
76514
76848
  process.stderr.write(`workspace: ${dir} does not exist yet. Run \`switchroom setup\` or \`switchroom agent scaffold ${agentName}\` to seed it.
76515
76849
  `);
76516
76850
  return;
@@ -76546,8 +76880,8 @@ function safeParseInt(value, fallback) {
76546
76880
  init_helpers();
76547
76881
  init_loader();
76548
76882
  init_merge();
76549
- import { copyFileSync as copyFileSync10, existsSync as existsSync62, readFileSync as readFileSync54, writeFileSync as writeFileSync29 } from "node:fs";
76550
- import { join as join61, resolve as resolve39 } from "node:path";
76883
+ import { copyFileSync as copyFileSync10, existsSync as existsSync63, readFileSync as readFileSync55, writeFileSync as writeFileSync30 } from "node:fs";
76884
+ import { join as join63, resolve as resolve39 } from "node:path";
76551
76885
  init_schema();
76552
76886
  function resolveSoulTargetOrExit(program3, agentName) {
76553
76887
  const config = getConfig(program3);
@@ -76562,7 +76896,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
76562
76896
  const agentsDir = resolveAgentsDir(config);
76563
76897
  const agentDir = resolve39(agentsDir, agentName);
76564
76898
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
76565
- if (!existsSync62(workspaceDir)) {
76899
+ if (!existsSync63(workspaceDir)) {
76566
76900
  console.error(`soul: ${workspaceDir} does not exist yet. Run \`switchroom setup\` ` + `or \`switchroom agent scaffold ${agentName}\` to seed it.`);
76567
76901
  process.exit(1);
76568
76902
  }
@@ -76571,7 +76905,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
76571
76905
  profileName,
76572
76906
  profilePath,
76573
76907
  workspaceDir,
76574
- soulPath: join61(workspaceDir, "SOUL.md"),
76908
+ soulPath: join63(workspaceDir, "SOUL.md"),
76575
76909
  soul: merged.soul
76576
76910
  };
76577
76911
  }
@@ -76588,11 +76922,11 @@ function registerSoulCommand(program3) {
76588
76922
  const t = resolveSoulTargetOrExit(program3, agentName);
76589
76923
  if (!t)
76590
76924
  return;
76591
- if (!existsSync62(t.soulPath)) {
76925
+ if (!existsSync63(t.soulPath)) {
76592
76926
  console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
76593
76927
  process.exit(1);
76594
76928
  }
76595
- process.stdout.write(readFileSync54(t.soulPath, "utf-8"));
76929
+ process.stdout.write(readFileSync55(t.soulPath, "utf-8"));
76596
76930
  }));
76597
76931
  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) => {
76598
76932
  const t = resolveSoulTargetOrExit(program3, agentName);
@@ -76603,7 +76937,7 @@ function registerSoulCommand(program3) {
76603
76937
  console.error(`soul: profile "${t.profileName}" ships no SOUL.md.hbs \u2014 ` + `nothing to re-seed from.`);
76604
76938
  process.exit(1);
76605
76939
  }
76606
- const exists = existsSync62(t.soulPath);
76940
+ const exists = existsSync63(t.soulPath);
76607
76941
  if (exists && !opts.yes) {
76608
76942
  if (!isInteractive()) {
76609
76943
  console.error(`soul: ${t.soulPath} already exists. Re-run with --yes to ` + `replace it (the current file is backed up to SOUL.md.bak).`);
@@ -76618,12 +76952,12 @@ function registerSoulCommand(program3) {
76618
76952
  let backupPath;
76619
76953
  if (exists) {
76620
76954
  backupPath = `${t.soulPath}.bak`;
76621
- if (existsSync62(backupPath)) {
76955
+ if (existsSync63(backupPath)) {
76622
76956
  backupPath = `${t.soulPath}.bak.${Date.now()}`;
76623
76957
  }
76624
76958
  copyFileSync10(t.soulPath, backupPath);
76625
76959
  }
76626
- writeFileSync29(t.soulPath, content, "utf-8");
76960
+ writeFileSync30(t.soulPath, content, "utf-8");
76627
76961
  if (backupPath) {
76628
76962
  console.log(`soul: re-seeded ${agentName}'s SOUL.md from profile ` + `"${t.profileName}".
76629
76963
  ` + ` Previous version saved to ${backupPath}`);
@@ -76637,8 +76971,8 @@ function registerSoulCommand(program3) {
76637
76971
  // src/cli/debug.ts
76638
76972
  init_helpers();
76639
76973
  init_loader();
76640
- import { existsSync as existsSync63, readFileSync as readFileSync55, readdirSync as readdirSync22, statSync as statSync26 } from "node:fs";
76641
- import { resolve as resolve40, join as join62 } from "node:path";
76974
+ import { existsSync as existsSync64, readFileSync as readFileSync56, readdirSync as readdirSync22, statSync as statSync26 } from "node:fs";
76975
+ import { resolve as resolve40, join as join64 } from "node:path";
76642
76976
  import { createHash as createHash13 } from "node:crypto";
76643
76977
  init_merge();
76644
76978
  init_hindsight();
@@ -76652,8 +76986,8 @@ function sha256(content) {
76652
76986
  return createHash13("sha256").update(content).digest("hex").slice(0, 16);
76653
76987
  }
76654
76988
  function findLatestTranscriptJsonl(claudeConfigDir) {
76655
- const projectsDir = join62(claudeConfigDir, "projects");
76656
- if (!existsSync63(projectsDir))
76989
+ const projectsDir = join64(claudeConfigDir, "projects");
76990
+ if (!existsSync64(projectsDir))
76657
76991
  return;
76658
76992
  try {
76659
76993
  const entries = readdirSync22(projectsDir, { withFileTypes: true });
@@ -76661,9 +76995,9 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
76661
76995
  for (const entry of entries) {
76662
76996
  if (!entry.isDirectory())
76663
76997
  continue;
76664
- const projectPath = join62(projectsDir, entry.name);
76665
- const transcriptPath = join62(projectPath, "transcript.jsonl");
76666
- if (!existsSync63(transcriptPath))
76998
+ const projectPath = join64(projectsDir, entry.name);
76999
+ const transcriptPath = join64(projectPath, "transcript.jsonl");
77000
+ if (!existsSync64(transcriptPath))
76667
77001
  continue;
76668
77002
  const stat3 = statSync26(transcriptPath);
76669
77003
  if (!latest || stat3.mtimeMs > latest.mtime) {
@@ -76677,7 +77011,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
76677
77011
  }
76678
77012
  function extractLatestUserMessage(transcriptPath) {
76679
77013
  try {
76680
- const content = readFileSync55(transcriptPath, "utf-8");
77014
+ const content = readFileSync56(transcriptPath, "utf-8");
76681
77015
  const lines = content.trim().split(`
76682
77016
  `).filter(Boolean);
76683
77017
  for (let i = lines.length - 1;i >= 0; i--) {
@@ -76726,16 +77060,16 @@ function registerDebugCommand(program3) {
76726
77060
  }
76727
77061
  const agentsDir = resolveAgentsDir(config);
76728
77062
  const agentDir = resolve40(agentsDir, agentName);
76729
- if (!existsSync63(agentDir)) {
77063
+ if (!existsSync64(agentDir)) {
76730
77064
  console.error(`Agent directory not found: ${agentDir}`);
76731
77065
  process.exit(1);
76732
77066
  }
76733
77067
  const workspaceDir = resolveAgentWorkspaceDir(agentDir);
76734
- const claudeConfigDir = join62(agentDir, ".claude");
76735
- const claudeMdPath = join62(agentDir, "CLAUDE.md");
76736
- const soulMdPath = join62(agentDir, "SOUL.md");
76737
- const workspaceSoulMdPath = join62(workspaceDir, "SOUL.md");
76738
- const handoffPath = join62(agentDir, ".handoff.md");
77068
+ const claudeConfigDir = join64(agentDir, ".claude");
77069
+ const claudeMdPath = join64(agentDir, "CLAUDE.md");
77070
+ const soulMdPath = join64(agentDir, "SOUL.md");
77071
+ const workspaceSoulMdPath = join64(workspaceDir, "SOUL.md");
77072
+ const handoffPath = join64(agentDir, ".handoff.md");
76739
77073
  const lastN = parseInt(opts.last, 10);
76740
77074
  if (isNaN(lastN) || lastN < 1) {
76741
77075
  console.error("--last must be a positive integer");
@@ -76781,7 +77115,7 @@ function registerDebugCommand(program3) {
76781
77115
  }
76782
77116
  console.log(`=== Append System Prompt (per-session) ===
76783
77117
  `);
76784
- const handoffContent = existsSync63(handoffPath) ? readFileSync55(handoffPath, "utf-8") : "";
77118
+ const handoffContent = existsSync64(handoffPath) ? readFileSync56(handoffPath, "utf-8") : "";
76785
77119
  if (handoffContent.trim().length > 0) {
76786
77120
  console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
76787
77121
  console.log(handoffContent);
@@ -76792,7 +77126,7 @@ function registerDebugCommand(program3) {
76792
77126
  }
76793
77127
  console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
76794
77128
  `);
76795
- const claudeMdContent = existsSync63(claudeMdPath) ? readFileSync55(claudeMdPath, "utf-8") : "";
77129
+ const claudeMdContent = existsSync64(claudeMdPath) ? readFileSync56(claudeMdPath, "utf-8") : "";
76796
77130
  if (claudeMdContent.trim().length > 0) {
76797
77131
  console.log(`(${formatBytes(claudeMdContent.length)})`);
76798
77132
  console.log(claudeMdContent);
@@ -76803,7 +77137,7 @@ function registerDebugCommand(program3) {
76803
77137
  }
76804
77138
  console.log(`=== Persona (SOUL.md) ===
76805
77139
  `);
76806
- const soulMdContent = existsSync63(soulMdPath) ? readFileSync55(soulMdPath, "utf-8") : existsSync63(workspaceSoulMdPath) ? readFileSync55(workspaceSoulMdPath, "utf-8") : "";
77140
+ const soulMdContent = existsSync64(soulMdPath) ? readFileSync56(soulMdPath, "utf-8") : existsSync64(workspaceSoulMdPath) ? readFileSync56(workspaceSoulMdPath, "utf-8") : "";
76807
77141
  if (soulMdContent.trim().length > 0) {
76808
77142
  console.log(`(${formatBytes(soulMdContent.length)})`);
76809
77143
  console.log(soulMdContent);
@@ -76884,44 +77218,44 @@ init_source();
76884
77218
 
76885
77219
  // src/worktree/claim.ts
76886
77220
  import { execFileSync as execFileSync20 } from "node:child_process";
76887
- import { closeSync as closeSync12, mkdirSync as mkdirSync35, openSync as openSync12, existsSync as existsSync65, unlinkSync as unlinkSync13 } from "node:fs";
76888
- import { join as join64, resolve as resolve42 } from "node:path";
76889
- import { homedir as homedir38 } from "node:os";
77221
+ import { closeSync as closeSync12, mkdirSync as mkdirSync36, openSync as openSync12, existsSync as existsSync66, unlinkSync as unlinkSync13 } from "node:fs";
77222
+ import { join as join66, resolve as resolve42 } from "node:path";
77223
+ import { homedir as homedir39 } from "node:os";
76890
77224
  import { randomBytes as randomBytes13 } from "node:crypto";
76891
77225
 
76892
77226
  // src/worktree/registry.ts
76893
77227
  import {
76894
- mkdirSync as mkdirSync34,
76895
- writeFileSync as writeFileSync30,
76896
- readFileSync as readFileSync56,
77228
+ mkdirSync as mkdirSync35,
77229
+ writeFileSync as writeFileSync31,
77230
+ readFileSync as readFileSync57,
76897
77231
  readdirSync as readdirSync23,
76898
77232
  unlinkSync as unlinkSync12,
76899
- existsSync as existsSync64,
77233
+ existsSync as existsSync65,
76900
77234
  renameSync as renameSync13
76901
77235
  } from "node:fs";
76902
- import { join as join63, resolve as resolve41 } from "node:path";
76903
- import { homedir as homedir37 } from "node:os";
77236
+ import { join as join65, resolve as resolve41 } from "node:path";
77237
+ import { homedir as homedir38 } from "node:os";
76904
77238
  function registryDir() {
76905
- return resolve41(process.env.SWITCHROOM_WORKTREE_DIR ?? join63(homedir37(), ".switchroom", "worktrees"));
77239
+ return resolve41(process.env.SWITCHROOM_WORKTREE_DIR ?? join65(homedir38(), ".switchroom", "worktrees"));
76906
77240
  }
76907
77241
  function recordPath(id) {
76908
- return join63(registryDir(), `${id}.json`);
77242
+ return join65(registryDir(), `${id}.json`);
76909
77243
  }
76910
77244
  function ensureDir2() {
76911
- mkdirSync34(registryDir(), { recursive: true });
77245
+ mkdirSync35(registryDir(), { recursive: true });
76912
77246
  }
76913
77247
  function writeRecord(record2) {
76914
77248
  ensureDir2();
76915
77249
  const target = recordPath(record2.id);
76916
77250
  const tmp = `${target}.tmp${process.pid}`;
76917
- writeFileSync30(tmp, JSON.stringify(record2, null, 2) + `
77251
+ writeFileSync31(tmp, JSON.stringify(record2, null, 2) + `
76918
77252
  `, { mode: 384 });
76919
77253
  renameSync13(tmp, target);
76920
77254
  }
76921
77255
  function readRecord(id) {
76922
77256
  const path7 = recordPath(id);
76923
77257
  try {
76924
- const raw = readFileSync56(path7, "utf8");
77258
+ const raw = readFileSync57(path7, "utf8");
76925
77259
  return JSON.parse(raw);
76926
77260
  } catch {
76927
77261
  return null;
@@ -76954,9 +77288,9 @@ function countByRepo(repoPath) {
76954
77288
  // src/worktree/claim.ts
76955
77289
  function acquireRepoLock(repoPath) {
76956
77290
  const lockDir = registryDir();
76957
- mkdirSync35(lockDir, { recursive: true });
77291
+ mkdirSync36(lockDir, { recursive: true });
76958
77292
  const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
76959
- const lockPath = join64(lockDir, `.lock-${lockName}`);
77293
+ const lockPath = join66(lockDir, `.lock-${lockName}`);
76960
77294
  const deadline = Date.now() + 5000;
76961
77295
  let fd = null;
76962
77296
  while (fd === null) {
@@ -76983,7 +77317,7 @@ function acquireRepoLock(repoPath) {
76983
77317
  }
76984
77318
  var DEFAULT_CONCURRENCY = 5;
76985
77319
  function worktreesBaseDir() {
76986
- return resolve42(process.env.SWITCHROOM_WORKTREE_BASE ?? join64(homedir38(), ".switchroom", "worktree-checkouts"));
77320
+ return resolve42(process.env.SWITCHROOM_WORKTREE_BASE ?? join66(homedir39(), ".switchroom", "worktree-checkouts"));
76987
77321
  }
76988
77322
  function shortId() {
76989
77323
  return randomBytes13(4).toString("hex");
@@ -77005,12 +77339,12 @@ function resolveRepoPath(repo, codeRepos) {
77005
77339
  }
77006
77340
  function expandHome(p) {
77007
77341
  if (p.startsWith("~/"))
77008
- return join64(homedir38(), p.slice(2));
77342
+ return join66(homedir39(), p.slice(2));
77009
77343
  return p;
77010
77344
  }
77011
77345
  async function claimWorktree(input, codeRepos) {
77012
77346
  const repoPath = resolveRepoPath(input.repo, codeRepos);
77013
- if (!existsSync65(repoPath)) {
77347
+ if (!existsSync66(repoPath)) {
77014
77348
  throw new Error(`Repository path does not exist: ${repoPath}`);
77015
77349
  }
77016
77350
  let concurrencyCap = DEFAULT_CONCURRENCY;
@@ -77032,8 +77366,8 @@ async function claimWorktree(input, codeRepos) {
77032
77366
  const taskSuffix = input.taskName ? sanitizeTaskName(input.taskName) : "task";
77033
77367
  branch = `task/${taskSuffix}-${id}`;
77034
77368
  const baseDir = worktreesBaseDir();
77035
- mkdirSync35(baseDir, { recursive: true });
77036
- worktreePath = join64(baseDir, `${id}-${taskSuffix}`);
77369
+ mkdirSync36(baseDir, { recursive: true });
77370
+ worktreePath = join66(baseDir, `${id}-${taskSuffix}`);
77037
77371
  const now = new Date().toISOString();
77038
77372
  const record2 = {
77039
77373
  id,
@@ -77064,7 +77398,7 @@ async function claimWorktree(input, codeRepos) {
77064
77398
 
77065
77399
  // src/worktree/release.ts
77066
77400
  import { execFileSync as execFileSync21 } from "node:child_process";
77067
- import { existsSync as existsSync66 } from "node:fs";
77401
+ import { existsSync as existsSync67 } from "node:fs";
77068
77402
  function releaseWorktree(input) {
77069
77403
  const { id } = input;
77070
77404
  const record2 = readRecord(id);
@@ -77072,7 +77406,7 @@ function releaseWorktree(input) {
77072
77406
  return { released: true };
77073
77407
  }
77074
77408
  let gitSuccess = true;
77075
- if (existsSync66(record2.path)) {
77409
+ if (existsSync67(record2.path)) {
77076
77410
  try {
77077
77411
  execFileSync21("git", ["worktree", "remove", "--force", record2.path], {
77078
77412
  cwd: record2.repo,
@@ -77111,7 +77445,7 @@ function listWorktrees() {
77111
77445
 
77112
77446
  // src/worktree/reaper.ts
77113
77447
  import { execFileSync as execFileSync22 } from "node:child_process";
77114
- import { existsSync as existsSync67 } from "node:fs";
77448
+ import { existsSync as existsSync68 } from "node:fs";
77115
77449
  var STALE_THRESHOLD_MS = 10 * 60 * 1000;
77116
77450
  function isPathInUse(path7) {
77117
77451
  try {
@@ -77138,7 +77472,7 @@ function hasUncommittedChanges(repoPath, worktreePath) {
77138
77472
  function reapRecord(record2) {
77139
77473
  const { id, path: path7, repo, branch, ownerAgent } = record2;
77140
77474
  let warning = null;
77141
- if (existsSync67(path7)) {
77475
+ if (existsSync68(path7)) {
77142
77476
  if (hasUncommittedChanges(repo, path7)) {
77143
77477
  warning = `[worktree-reaper] Reaped worktree with uncommitted changes: ` + `id=${id} branch=${branch} agent=${ownerAgent ?? "unknown"} path=${path7}`;
77144
77478
  }
@@ -77159,7 +77493,7 @@ function runReaper(nowMs) {
77159
77493
  const warnings = [];
77160
77494
  for (const record2 of records) {
77161
77495
  const heartbeatAge = now - new Date(record2.heartbeatAt).getTime();
77162
- const worktreeExists = existsSync67(record2.path);
77496
+ const worktreeExists = existsSync68(record2.path);
77163
77497
  if (!worktreeExists) {
77164
77498
  deleteRecord(record2.id);
77165
77499
  reaped.push(record2.id);
@@ -77283,12 +77617,12 @@ init_drive();
77283
77617
  init_scaffold_integration();
77284
77618
  import {
77285
77619
  chmodSync as chmodSync9,
77286
- mkdirSync as mkdirSync36,
77620
+ mkdirSync as mkdirSync37,
77287
77621
  readdirSync as readdirSync24,
77288
77622
  rmSync as rmSync15,
77289
- writeFileSync as writeFileSync31
77623
+ writeFileSync as writeFileSync32
77290
77624
  } from "node:fs";
77291
- import { join as join65 } from "node:path";
77625
+ import { join as join67 } from "node:path";
77292
77626
  function encodeCredentialsFilename(email) {
77293
77627
  const SAFE = new Set([
77294
77628
  ..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
@@ -77478,17 +77812,17 @@ function resolveCredentialsDir(env2) {
77478
77812
  if (explicit && explicit.length > 0)
77479
77813
  return explicit;
77480
77814
  const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
77481
- return join65(stateBase, "google-workspace-mcp", "credentials");
77815
+ return join67(stateBase, "google-workspace-mcp", "credentials");
77482
77816
  }
77483
77817
  function writeSeedFile(dir, email, seed) {
77484
- mkdirSync36(dir, { recursive: true, mode: 448 });
77818
+ mkdirSync37(dir, { recursive: true, mode: 448 });
77485
77819
  chmodSync9(dir, 448);
77486
77820
  for (const name of readdirSync24(dir)) {
77487
- rmSync15(join65(dir, name), { force: true, recursive: true });
77821
+ rmSync15(join67(dir, name), { force: true, recursive: true });
77488
77822
  }
77489
77823
  const filename = encodeCredentialsFilename(email);
77490
- const filePath = join65(dir, filename);
77491
- writeFileSync31(filePath, JSON.stringify(seed), { mode: 384 });
77824
+ const filePath = join67(dir, filename);
77825
+ writeFileSync32(filePath, JSON.stringify(seed), { mode: 384 });
77492
77826
  chmodSync9(filePath, 384);
77493
77827
  return filePath;
77494
77828
  }
@@ -77646,8 +77980,8 @@ function registerDriveMcpLauncherCommand(program3) {
77646
77980
  // src/cli/m365-mcp-launcher.ts
77647
77981
  init_scaffold_integration();
77648
77982
  import { spawn as spawn6 } from "node:child_process";
77649
- import { writeFileSync as writeFileSync32, mkdirSync as mkdirSync37 } from "node:fs";
77650
- import { dirname as dirname17, join as join66 } from "node:path";
77983
+ import { writeFileSync as writeFileSync33, mkdirSync as mkdirSync38 } from "node:fs";
77984
+ import { dirname as dirname17, join as join68 } from "node:path";
77651
77985
  var SOFTERIA_TOKEN_ENV = "MS365_MCP_OAUTH_TOKEN";
77652
77986
  var DEFAULT_REFRESH_LEAD_MS = 5 * 60 * 1000;
77653
77987
  var MAX_REFRESH_INTERVAL_MS = 60 * 60 * 1000;
@@ -77672,14 +78006,14 @@ function computeRefreshDelayMs(expiresAt, now, leadMs = DEFAULT_REFRESH_LEAD_MS)
77672
78006
  function writeRefreshHeartbeat(agentName, data) {
77673
78007
  const path7 = heartbeatPath(agentName);
77674
78008
  try {
77675
- mkdirSync37(dirname17(path7), { recursive: true });
77676
- writeFileSync32(path7, JSON.stringify(data, null, 2), { mode: 420 });
78009
+ mkdirSync38(dirname17(path7), { recursive: true });
78010
+ writeFileSync33(path7, JSON.stringify(data, null, 2), { mode: 420 });
77677
78011
  } catch {}
77678
78012
  }
77679
78013
  function heartbeatPath(agentName) {
77680
78014
  const override = process.env.SWITCHROOM_M365_HEARTBEAT_DIR;
77681
78015
  if (override) {
77682
- return join66(override, `m365-launcher-${agentName}.heartbeat.json`);
78016
+ return join68(override, `m365-launcher-${agentName}.heartbeat.json`);
77683
78017
  }
77684
78018
  return "/state/agent/m365-launcher.heartbeat.json";
77685
78019
  }
@@ -77864,7 +78198,7 @@ function registerM365McpLauncherCommand(program3) {
77864
78198
  // src/cli/notion-mcp-launcher.ts
77865
78199
  init_scaffold_integration();
77866
78200
  import { spawn as spawn7 } from "node:child_process";
77867
- import { existsSync as existsSync68, mkdirSync as mkdirSync38, writeFileSync as writeFileSync33 } from "node:fs";
78201
+ import { existsSync as existsSync69, mkdirSync as mkdirSync39, writeFileSync as writeFileSync34 } from "node:fs";
77868
78202
  import { dirname as dirname18 } from "node:path";
77869
78203
  var HEARTBEAT_WRITE_INTERVAL_MS = 30 * 1000;
77870
78204
  var DEFAULT_HEARTBEAT_PATH = "/state/agent/notion-launcher.heartbeat.json";
@@ -77876,9 +78210,9 @@ function buildNotionMcpArgs(opts) {
77876
78210
  function defaultWriteHeartbeat(path7, contents) {
77877
78211
  try {
77878
78212
  const dir = dirname18(path7);
77879
- if (!existsSync68(dir))
77880
- mkdirSync38(dir, { recursive: true });
77881
- writeFileSync33(path7, contents);
78213
+ if (!existsSync69(dir))
78214
+ mkdirSync39(dir, { recursive: true });
78215
+ writeFileSync34(path7, contents);
77882
78216
  } catch {}
77883
78217
  }
77884
78218
  async function runNotionMcpLauncher(opts, runtime) {
@@ -78235,7 +78569,7 @@ async function fetchToken(vaultKey) {
78235
78569
 
78236
78570
  // src/cli/apply.ts
78237
78571
  init_source();
78238
- import { accessSync as accessSync3, chownSync as chownSync4, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync72, mkdirSync as mkdirSync40, readFileSync as readFileSync58, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync35 } from "node:fs";
78572
+ import { accessSync as accessSync3, chownSync as chownSync4, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync73, mkdirSync as mkdirSync41, readFileSync as readFileSync59, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync36 } from "node:fs";
78239
78573
  import { mkdir, writeFile } from "node:fs/promises";
78240
78574
  import { spawnSync as childSpawnSync } from "node:child_process";
78241
78575
  import readline from "node:readline";
@@ -78624,16 +78958,16 @@ agents:
78624
78958
 
78625
78959
  // src/cli/apply.ts
78626
78960
  init_resolver();
78627
- import { dirname as dirname21, join as join70, resolve as resolve44 } from "node:path";
78628
- import { homedir as homedir40 } from "node:os";
78961
+ import { dirname as dirname21, join as join72, resolve as resolve44 } from "node:path";
78962
+ import { homedir as homedir41 } from "node:os";
78629
78963
  import { execFileSync as execFileSync23 } from "node:child_process";
78630
78964
  init_vault();
78631
78965
  init_loader();
78632
78966
  init_loader();
78633
78967
 
78634
78968
  // src/cli/update-prompt-hook.ts
78635
- import { existsSync as existsSync69, readFileSync as readFileSync57, writeFileSync as writeFileSync34, chmodSync as chmodSync10, mkdirSync as mkdirSync39 } from "node:fs";
78636
- import { join as join67 } from "node:path";
78969
+ import { existsSync as existsSync70, readFileSync as readFileSync58, writeFileSync as writeFileSync35, chmodSync as chmodSync10, mkdirSync as mkdirSync40 } from "node:fs";
78970
+ import { join as join69 } from "node:path";
78637
78971
  var HOOK_FILENAME = "update-card-on-prompt.sh";
78638
78972
  function updatePromptHookScript() {
78639
78973
  return `#!/bin/bash
@@ -78699,14 +79033,14 @@ exit 0
78699
79033
  `;
78700
79034
  }
78701
79035
  function installUpdatePromptHook(agentDir) {
78702
- const hooksDir = join67(agentDir, ".claude", "hooks");
78703
- mkdirSync39(hooksDir, { recursive: true });
78704
- const scriptPath = join67(hooksDir, HOOK_FILENAME);
79036
+ const hooksDir = join69(agentDir, ".claude", "hooks");
79037
+ mkdirSync40(hooksDir, { recursive: true });
79038
+ const scriptPath = join69(hooksDir, HOOK_FILENAME);
78705
79039
  const desired = updatePromptHookScript();
78706
79040
  let installed = false;
78707
- const existing = existsSync69(scriptPath) ? readFileSync57(scriptPath, "utf-8") : "";
79041
+ const existing = existsSync70(scriptPath) ? readFileSync58(scriptPath, "utf-8") : "";
78708
79042
  if (existing !== desired) {
78709
- writeFileSync34(scriptPath, desired, { mode: 493 });
79043
+ writeFileSync35(scriptPath, desired, { mode: 493 });
78710
79044
  chmodSync10(scriptPath, 493);
78711
79045
  installed = true;
78712
79046
  } else {
@@ -78714,11 +79048,11 @@ function installUpdatePromptHook(agentDir) {
78714
79048
  chmodSync10(scriptPath, 493);
78715
79049
  } catch {}
78716
79050
  }
78717
- const settingsPath = join67(agentDir, ".claude", "settings.json");
78718
- if (!existsSync69(settingsPath)) {
79051
+ const settingsPath = join69(agentDir, ".claude", "settings.json");
79052
+ if (!existsSync70(settingsPath)) {
78719
79053
  return { scriptPath, settingsPath, installed };
78720
79054
  }
78721
- const raw = readFileSync57(settingsPath, "utf-8");
79055
+ const raw = readFileSync58(settingsPath, "utf-8");
78722
79056
  let parsed;
78723
79057
  try {
78724
79058
  parsed = JSON.parse(raw);
@@ -78751,7 +79085,7 @@ function installUpdatePromptHook(agentDir) {
78751
79085
  });
78752
79086
  hooks.UserPromptSubmit = list2;
78753
79087
  parsed.hooks = hooks;
78754
- writeFileSync34(settingsPath, JSON.stringify(parsed, null, 2) + `
79088
+ writeFileSync35(settingsPath, JSON.stringify(parsed, null, 2) + `
78755
79089
  `, { mode: 384 });
78756
79090
  installed = true;
78757
79091
  }
@@ -78834,13 +79168,13 @@ function detectInstallType() {
78834
79168
  // src/cli/operator-uid.ts
78835
79169
  import {
78836
79170
  chownSync as chownSync3,
78837
- existsSync as existsSync71,
79171
+ existsSync as existsSync72,
78838
79172
  lstatSync as lstatSync7,
78839
79173
  readdirSync as readdirSync25,
78840
79174
  realpathSync as realpathSync6,
78841
79175
  statSync as statSync27
78842
79176
  } from "node:fs";
78843
- import { join as join69 } from "node:path";
79177
+ import { join as join71 } from "node:path";
78844
79178
  function resolveOperatorUid() {
78845
79179
  const sudoUid = process.env.SUDO_UID;
78846
79180
  if (sudoUid !== undefined) {
@@ -78856,19 +79190,19 @@ function resolveOperatorUid() {
78856
79190
  return;
78857
79191
  }
78858
79192
  function operatorOwnedPaths(home2) {
78859
- const root = join69(home2, ".switchroom");
79193
+ const root = join71(home2, ".switchroom");
78860
79194
  return [
78861
- join69(root, "vault"),
78862
- join69(root, "vault-auto-unlock"),
78863
- join69(root, "vault-audit.log"),
78864
- join69(root, "host-control-audit.log"),
78865
- join69(root, "accounts"),
78866
- join69(root, "compose")
79195
+ join71(root, "vault"),
79196
+ join71(root, "vault-auto-unlock"),
79197
+ join71(root, "vault-audit.log"),
79198
+ join71(root, "host-control-audit.log"),
79199
+ join71(root, "accounts"),
79200
+ join71(root, "compose")
78867
79201
  ];
78868
79202
  }
78869
79203
  function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
78870
79204
  const chown = deps.chown ?? ((p, u, g) => chownSync3(p, u, g));
78871
- const exists = deps.exists ?? ((p) => existsSync71(p));
79205
+ const exists = deps.exists ?? ((p) => existsSync72(p));
78872
79206
  const isSymlink = deps.isSymlink ?? ((p) => {
78873
79207
  try {
78874
79208
  return lstatSync7(p).isSymbolicLink();
@@ -78912,7 +79246,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
78912
79246
  } catch {}
78913
79247
  if (isDir(target)) {
78914
79248
  for (const entry of readdir2(target)) {
78915
- visit(join69(target, entry));
79249
+ visit(join71(target, entry));
78916
79250
  }
78917
79251
  }
78918
79252
  };
@@ -78926,17 +79260,17 @@ var EMBEDDED_EXAMPLES = {
78926
79260
  switchroom: switchroom_default,
78927
79261
  minimal: minimal_default
78928
79262
  };
78929
- var DEFAULT_COMPOSE_PATH2 = join70(homedir40(), ".switchroom", "compose", "docker-compose.yml");
79263
+ var DEFAULT_COMPOSE_PATH2 = join72(homedir41(), ".switchroom", "compose", "docker-compose.yml");
78930
79264
  var COMPOSE_PROJECT2 = "switchroom";
78931
79265
  function resolveVaultBindMountDir(homeDir, ctx) {
78932
79266
  const isCustomPath = ctx.migrationKind === "custom-path-skipped";
78933
79267
  if (isCustomPath && ctx.customVaultPath) {
78934
79268
  return dirname21(ctx.customVaultPath);
78935
79269
  }
78936
- return join70(homeDir, ".switchroom", "vault");
79270
+ return join72(homeDir, ".switchroom", "vault");
78937
79271
  }
78938
79272
  function inspectVaultBindMountDir(vaultDir) {
78939
- if (!existsSync72(vaultDir))
79273
+ if (!existsSync73(vaultDir))
78940
79274
  return { kind: "missing" };
78941
79275
  const entries = readdirSync26(vaultDir);
78942
79276
  const unknown = [];
@@ -78962,63 +79296,63 @@ function hasVaultRefs(value) {
78962
79296
  return false;
78963
79297
  }
78964
79298
  async function ensureHostMountSources(config) {
78965
- const home2 = homedir40();
79299
+ const home2 = homedir41();
78966
79300
  const dirs = [
78967
- join70(home2, ".switchroom", "approvals"),
78968
- join70(home2, ".switchroom", "scheduler"),
78969
- join70(home2, ".switchroom", "logs"),
78970
- join70(home2, ".switchroom", "compose"),
78971
- join70(home2, ".switchroom", "broker-operator")
79301
+ join72(home2, ".switchroom", "approvals"),
79302
+ join72(home2, ".switchroom", "scheduler"),
79303
+ join72(home2, ".switchroom", "logs"),
79304
+ join72(home2, ".switchroom", "compose"),
79305
+ join72(home2, ".switchroom", "broker-operator")
78972
79306
  ];
78973
79307
  for (const name of Object.keys(config.agents)) {
78974
- dirs.push(join70(home2, ".switchroom", "agents", name));
78975
- dirs.push(join70(home2, ".switchroom", "logs", name));
78976
- dirs.push(join70(home2, ".claude", "projects", name));
78977
- dirs.push(join70(home2, ".switchroom", "audit", name));
78978
- if (existsSync72(join70(home2, ".switchroom-config"))) {
78979
- dirs.push(join70(home2, ".switchroom-config", "agents", name, "personal-skills"));
79308
+ dirs.push(join72(home2, ".switchroom", "agents", name));
79309
+ dirs.push(join72(home2, ".switchroom", "logs", name));
79310
+ dirs.push(join72(home2, ".claude", "projects", name));
79311
+ dirs.push(join72(home2, ".switchroom", "audit", name));
79312
+ if (existsSync73(join72(home2, ".switchroom-config"))) {
79313
+ dirs.push(join72(home2, ".switchroom-config", "agents", name, "personal-skills"));
78980
79314
  }
78981
79315
  }
78982
79316
  for (const dir of dirs) {
78983
79317
  await mkdir(dir, { recursive: true });
78984
79318
  }
78985
- const autoUnlockPath = join70(home2, ".switchroom", "vault-auto-unlock");
78986
- if (!existsSync72(autoUnlockPath)) {
78987
- writeFileSync35(autoUnlockPath, "", { mode: 384 });
79319
+ const autoUnlockPath = join72(home2, ".switchroom", "vault-auto-unlock");
79320
+ if (!existsSync73(autoUnlockPath)) {
79321
+ writeFileSync36(autoUnlockPath, "", { mode: 384 });
78988
79322
  }
78989
- const auditLogPath = join70(home2, ".switchroom", "vault-audit.log");
78990
- if (!existsSync72(auditLogPath)) {
78991
- writeFileSync35(auditLogPath, "", { mode: 420 });
79323
+ const auditLogPath = join72(home2, ".switchroom", "vault-audit.log");
79324
+ if (!existsSync73(auditLogPath)) {
79325
+ writeFileSync36(auditLogPath, "", { mode: 420 });
78992
79326
  }
78993
- const grantsDbPath = join70(home2, ".switchroom", "vault-grants.db");
78994
- if (!existsSync72(grantsDbPath)) {
78995
- writeFileSync35(grantsDbPath, "", { mode: 384 });
79327
+ const grantsDbPath = join72(home2, ".switchroom", "vault-grants.db");
79328
+ if (!existsSync73(grantsDbPath)) {
79329
+ writeFileSync36(grantsDbPath, "", { mode: 384 });
78996
79330
  }
78997
- const hostdAuditLogPath = join70(home2, ".switchroom", "host-control-audit.log");
78998
- if (!existsSync72(hostdAuditLogPath)) {
78999
- writeFileSync35(hostdAuditLogPath, "", { mode: 420 });
79331
+ const hostdAuditLogPath = join72(home2, ".switchroom", "host-control-audit.log");
79332
+ if (!existsSync73(hostdAuditLogPath)) {
79333
+ writeFileSync36(hostdAuditLogPath, "", { mode: 420 });
79000
79334
  }
79001
79335
  for (const name of Object.keys(config.agents)) {
79002
- const tokenPath = join70(home2, ".switchroom", "agents", name, ".vault-token");
79003
- if (!existsSync72(tokenPath)) {
79004
- writeFileSync35(tokenPath, "", { mode: 384 });
79336
+ const tokenPath = join72(home2, ".switchroom", "agents", name, ".vault-token");
79337
+ if (!existsSync73(tokenPath)) {
79338
+ writeFileSync36(tokenPath, "", { mode: 384 });
79005
79339
  }
79006
79340
  try {
79007
79341
  const uid = allocateAgentUid(name);
79008
79342
  chownSync4(tokenPath, uid, uid);
79009
79343
  } catch {}
79010
79344
  }
79011
- const fleetDir = join70(home2, ".switchroom", "fleet");
79345
+ const fleetDir = join72(home2, ".switchroom", "fleet");
79012
79346
  await mkdir(fleetDir, { recursive: true });
79013
- const invariantsPath = join70(fleetDir, "switchroom-invariants.md");
79347
+ const invariantsPath = join72(fleetDir, "switchroom-invariants.md");
79014
79348
  const invariantsCanonical = renderFleetInvariants();
79015
- const invariantsCurrent = existsSync72(invariantsPath) ? readFileSync58(invariantsPath, "utf-8") : null;
79349
+ const invariantsCurrent = existsSync73(invariantsPath) ? readFileSync59(invariantsPath, "utf-8") : null;
79016
79350
  if (invariantsCurrent !== invariantsCanonical) {
79017
- writeFileSync35(invariantsPath, invariantsCanonical, { mode: 420 });
79351
+ writeFileSync36(invariantsPath, invariantsCanonical, { mode: 420 });
79018
79352
  }
79019
- const fleetClaudePath = join70(fleetDir, "CLAUDE.md");
79020
- if (!existsSync72(fleetClaudePath)) {
79021
- writeFileSync35(fleetClaudePath, [
79353
+ const fleetClaudePath = join72(fleetDir, "CLAUDE.md");
79354
+ if (!existsSync73(fleetClaudePath)) {
79355
+ writeFileSync36(fleetClaudePath, [
79022
79356
  "# Switchroom fleet defaults",
79023
79357
  "",
79024
79358
  "Operator-owned fleet brain. Every agent reads this via",
@@ -79049,7 +79383,7 @@ ${out.trim()}`;
79049
79383
  }
79050
79384
  function runApplyPreflight(config, opts = {}) {
79051
79385
  const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
79052
- if (hasVaultRefs(config) && !existsSync72(vaultPath)) {
79386
+ if (hasVaultRefs(config) && !existsSync73(vaultPath)) {
79053
79387
  throw new Error(`Config references vault keys (vault:<name>) but ${vaultPath} is missing. ` + `Run \`switchroom setup\` first to initialise the vault.`);
79054
79388
  }
79055
79389
  const detect = opts.detectComposeV2 ?? detectComposeV2;
@@ -79060,7 +79394,7 @@ function runApplyPreflight(config, opts = {}) {
79060
79394
  detectAndReportLegacyGdriveSlots(vaultPath);
79061
79395
  }
79062
79396
  function detectAndReportLegacyGdriveSlots(vaultPath) {
79063
- if (!existsSync72(vaultPath))
79397
+ if (!existsSync73(vaultPath))
79064
79398
  return;
79065
79399
  const passphrase = process.env.SWITCHROOM_VAULT_PASSPHRASE;
79066
79400
  if (!passphrase)
@@ -79099,18 +79433,18 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
79099
79433
  `));
79100
79434
  }
79101
79435
  }
79102
- function writeInstallTypeCache(homeDir = homedir40()) {
79436
+ function writeInstallTypeCache(homeDir = homedir41()) {
79103
79437
  const ctx = detectInstallType();
79104
- const dir = join70(homeDir, ".switchroom");
79105
- const out = join70(dir, "install-type.json");
79438
+ const dir = join72(homeDir, ".switchroom");
79439
+ const out = join72(dir, "install-type.json");
79106
79440
  const tmp = `${out}.tmp`;
79107
- mkdirSync40(dir, { recursive: true });
79441
+ mkdirSync41(dir, { recursive: true });
79108
79442
  const payload = {
79109
79443
  install_type: ctx.install_type,
79110
79444
  detected_at: new Date().toISOString(),
79111
79445
  source_paths: ctx.source_paths
79112
79446
  };
79113
- writeFileSync35(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
79447
+ writeFileSync36(tmp, JSON.stringify(payload, null, 2), { mode: 420 });
79114
79448
  renameSync14(tmp, out);
79115
79449
  return out;
79116
79450
  }
@@ -79151,14 +79485,14 @@ Applying switchroom config...
79151
79485
  writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
79152
79486
  `));
79153
79487
  try {
79154
- installUpdatePromptHook(join70(agentsDir, name));
79488
+ installUpdatePromptHook(join72(agentsDir, name));
79155
79489
  } catch (hookErr) {
79156
79490
  writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
79157
79491
  `));
79158
79492
  }
79159
79493
  try {
79160
79494
  const uid = allocateAgentUid(name);
79161
- alignAgentUid(name, join70(agentsDir, name), uid, {
79495
+ alignAgentUid(name, join72(agentsDir, name), uid, {
79162
79496
  confirm: !options.nonInteractive,
79163
79497
  writeOut
79164
79498
  });
@@ -79195,7 +79529,7 @@ Applying switchroom config...
79195
79529
  for (const name of agentNames) {
79196
79530
  try {
79197
79531
  const uid = allocateAgentUid(name);
79198
- alignAgentUid(name, join70(agentsDir, name), uid, {
79532
+ alignAgentUid(name, join72(agentsDir, name), uid, {
79199
79533
  confirm: !options.nonInteractive,
79200
79534
  writeOut
79201
79535
  });
@@ -79209,7 +79543,7 @@ Applying switchroom config...
79209
79543
  }
79210
79544
  const vaultPathConfigured = config.vault?.path;
79211
79545
  const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
79212
- const migrationResult = migrateVaultLayout(homedir40(), {
79546
+ const migrationResult = migrateVaultLayout(homedir41(), {
79213
79547
  customVaultPath
79214
79548
  });
79215
79549
  switch (migrationResult.kind) {
@@ -79235,7 +79569,7 @@ Applying switchroom config...
79235
79569
  writeErr(formatDivergentRecoveryMessage(migrationResult.details));
79236
79570
  process.exit(4);
79237
79571
  }
79238
- const postMigrationInspect = inspectVaultLayout(homedir40());
79572
+ const postMigrationInspect = inspectVaultLayout(homedir41());
79239
79573
  const acceptable = [
79240
79574
  "no-vault",
79241
79575
  "already-migrated",
@@ -79250,7 +79584,7 @@ Applying switchroom config...
79250
79584
  `));
79251
79585
  process.exit(5);
79252
79586
  }
79253
- const vaultDir = resolveVaultBindMountDir(homedir40(), {
79587
+ const vaultDir = resolveVaultBindMountDir(homedir41(), {
79254
79588
  migrationKind: migrationResult.kind,
79255
79589
  customVaultPath
79256
79590
  });
@@ -79280,7 +79614,7 @@ Applying switchroom config...
79280
79614
  imageTag: composeImageTag,
79281
79615
  buildMode: options.buildLocal ? "local" : "pull",
79282
79616
  buildContext: options.buildContext,
79283
- homeDir: homedir40(),
79617
+ homeDir: homedir41(),
79284
79618
  switchroomConfigPath,
79285
79619
  operatorUid
79286
79620
  });
@@ -79300,7 +79634,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
79300
79634
  writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
79301
79635
  `));
79302
79636
  if (process.geteuid?.() === 0 && operatorUid !== undefined) {
79303
- const restored = restoreOperatorOwnership(homedir40(), operatorUid);
79637
+ const restored = restoreOperatorOwnership(homedir41(), operatorUid);
79304
79638
  if (restored.length > 0) {
79305
79639
  writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
79306
79640
  `));
@@ -79348,18 +79682,18 @@ function copyExampleConfig2(name) {
79348
79682
  throw new Error(`Invalid example name: ${name} (must match /^[a-z0-9_-]+$/)`);
79349
79683
  }
79350
79684
  const dest = resolve44(process.cwd(), "switchroom.yaml");
79351
- if (existsSync72(dest)) {
79685
+ if (existsSync73(dest)) {
79352
79686
  console.error(source_default.yellow("switchroom.yaml already exists \u2014 skipping example copy"));
79353
79687
  return;
79354
79688
  }
79355
79689
  const embedded = EMBEDDED_EXAMPLES[name];
79356
79690
  if (embedded !== undefined) {
79357
- writeFileSync35(dest, embedded, { encoding: "utf8" });
79691
+ writeFileSync36(dest, embedded, { encoding: "utf8" });
79358
79692
  console.log(source_default.green(`Copied ${name}.yaml -> switchroom.yaml`));
79359
79693
  return;
79360
79694
  }
79361
79695
  const exampleFile = resolve44(import.meta.dirname, `../../examples/${name}.yaml`);
79362
- if (!existsSync72(exampleFile)) {
79696
+ if (!existsSync73(exampleFile)) {
79363
79697
  throw new Error(`Example config not found: ${name}.yaml (available: ${Object.keys(EMBEDDED_EXAMPLES).join(", ")})`);
79364
79698
  }
79365
79699
  copyFileSync11(exampleFile, dest);
@@ -79370,8 +79704,8 @@ function findUnwritableAgentDirs(config, opts) {
79370
79704
  const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
79371
79705
  const unwritable = [];
79372
79706
  for (const name of targets) {
79373
- const startSh = join70(agentsDir, name, "start.sh");
79374
- if (!existsSync72(startSh))
79707
+ const startSh = join72(agentsDir, name, "start.sh");
79708
+ if (!existsSync73(startSh))
79375
79709
  continue;
79376
79710
  try {
79377
79711
  accessSync3(startSh, fsConstants6.W_OK);
@@ -79549,9 +79883,9 @@ function runRedactStdin() {
79549
79883
  }
79550
79884
 
79551
79885
  // src/cli/status-ask.ts
79552
- import { readFileSync as readFileSync59, existsSync as existsSync73, readdirSync as readdirSync27 } from "node:fs";
79553
- import { join as join71 } from "node:path";
79554
- import { homedir as homedir41 } from "node:os";
79886
+ import { readFileSync as readFileSync60, existsSync as existsSync74, readdirSync as readdirSync27 } from "node:fs";
79887
+ import { join as join73 } from "node:path";
79888
+ import { homedir as homedir42 } from "node:os";
79555
79889
 
79556
79890
  // src/status-ask/report.ts
79557
79891
  function parseJsonl(content) {
@@ -79825,7 +80159,7 @@ function runReport(opts) {
79825
80159
  for (const src of sources) {
79826
80160
  let content;
79827
80161
  try {
79828
- content = readFileSync59(src.path, "utf-8");
80162
+ content = readFileSync60(src.path, "utf-8");
79829
80163
  } catch (err) {
79830
80164
  process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
79831
80165
  `);
@@ -79872,7 +80206,7 @@ function runReport(opts) {
79872
80206
  function resolveSources(explicitPath) {
79873
80207
  if (explicitPath != null && explicitPath.trim() !== "") {
79874
80208
  const trimmed = explicitPath.trim();
79875
- if (!existsSync73(trimmed)) {
80209
+ if (!existsSync74(trimmed)) {
79876
80210
  process.stderr.write(`status-ask report: ${trimmed}: file not found
79877
80211
  `);
79878
80212
  process.exit(1);
@@ -79886,9 +80220,9 @@ function resolveSources(explicitPath) {
79886
80220
  const config = loadConfig();
79887
80221
  agentsDir = resolveAgentsDir(config);
79888
80222
  } catch {
79889
- agentsDir = join71(homedir41(), ".switchroom", "agents");
80223
+ agentsDir = join73(homedir42(), ".switchroom", "agents");
79890
80224
  }
79891
- if (!existsSync73(agentsDir))
80225
+ if (!existsSync74(agentsDir))
79892
80226
  return [];
79893
80227
  const sources = [];
79894
80228
  let entries;
@@ -79898,8 +80232,8 @@ function resolveSources(explicitPath) {
79898
80232
  return [];
79899
80233
  }
79900
80234
  for (const name of entries) {
79901
- const path8 = join71(agentsDir, name, "runtime-metrics.jsonl");
79902
- if (existsSync73(path8)) {
80235
+ const path8 = join73(agentsDir, name, "runtime-metrics.jsonl");
80236
+ if (existsSync74(path8)) {
79903
80237
  sources.push({ path: path8, agent: name });
79904
80238
  }
79905
80239
  }
@@ -79920,17 +80254,17 @@ function inferAgentFromPath(p) {
79920
80254
 
79921
80255
  // src/cli/agent-config.ts
79922
80256
  init_helpers();
79923
- import { join as join72 } from "node:path";
79924
- import { homedir as homedir42 } from "node:os";
80257
+ import { join as join74 } from "node:path";
80258
+ import { homedir as homedir43 } from "node:os";
79925
80259
  import {
79926
- existsSync as existsSync74,
79927
- mkdirSync as mkdirSync41,
80260
+ existsSync as existsSync75,
80261
+ mkdirSync as mkdirSync42,
79928
80262
  appendFileSync as appendFileSync4,
79929
- readFileSync as readFileSync60
80263
+ readFileSync as readFileSync61
79930
80264
  } from "node:fs";
79931
- var AUDIT_ROOT = join72(homedir42(), ".switchroom", "audit");
80265
+ var AUDIT_ROOT = join74(homedir43(), ".switchroom", "audit");
79932
80266
  function auditPathFor(agent) {
79933
- return join72(AUDIT_ROOT, agent, "agent-config.jsonl");
80267
+ return join74(AUDIT_ROOT, agent, "agent-config.jsonl");
79934
80268
  }
79935
80269
  function appendAudit(agent, cmd, args, exit, opts = {}) {
79936
80270
  const row = {
@@ -79944,8 +80278,8 @@ function appendAudit(agent, cmd, args, exit, opts = {}) {
79944
80278
  const path8 = opts.auditPath ?? auditPathFor(agent);
79945
80279
  const dir = path8.slice(0, path8.lastIndexOf("/"));
79946
80280
  try {
79947
- if (!existsSync74(dir)) {
79948
- mkdirSync41(dir, { recursive: true });
80281
+ if (!existsSync75(dir)) {
80282
+ mkdirSync42(dir, { recursive: true });
79949
80283
  }
79950
80284
  appendFileSync4(path8, JSON.stringify(row) + `
79951
80285
  `, { flag: "a" });
@@ -79956,7 +80290,7 @@ function isContainerContext(env2 = process.env, opts = {}) {
79956
80290
  return true;
79957
80291
  const probe2 = opts.dockerEnvPath ?? "/.dockerenv";
79958
80292
  try {
79959
- if (existsSync74(probe2))
80293
+ if (existsSync75(probe2))
79960
80294
  return true;
79961
80295
  } catch {}
79962
80296
  return false;
@@ -80017,11 +80351,11 @@ function getAgentSlice(config, agent) {
80017
80351
  }
80018
80352
  function readAuditTail(agent, limit, opts = {}) {
80019
80353
  const path8 = opts.auditPath ?? auditPathFor(agent);
80020
- if (!existsSync74(path8))
80354
+ if (!existsSync75(path8))
80021
80355
  return [];
80022
80356
  let raw;
80023
80357
  try {
80024
- raw = readFileSync60(path8, "utf-8");
80358
+ raw = readFileSync61(path8, "utf-8");
80025
80359
  } catch {
80026
80360
  return [];
80027
80361
  }
@@ -80171,51 +80505,51 @@ function registerAgentConfigCommands(program3) {
80171
80505
  }
80172
80506
 
80173
80507
  // src/cli/agent-config-write.ts
80174
- var import_yaml17 = __toESM(require_dist(), 1);
80508
+ var import_yaml19 = __toESM(require_dist(), 1);
80175
80509
 
80176
80510
  // src/config/overlay-writer.ts
80177
80511
  init_paths();
80178
80512
  import {
80179
80513
  closeSync as closeSync13,
80180
- existsSync as existsSync75,
80514
+ existsSync as existsSync76,
80181
80515
  fsyncSync as fsyncSync6,
80182
- mkdirSync as mkdirSync42,
80516
+ mkdirSync as mkdirSync43,
80183
80517
  openSync as openSync13,
80184
80518
  readdirSync as readdirSync28,
80185
- readFileSync as readFileSync61,
80519
+ readFileSync as readFileSync62,
80186
80520
  renameSync as renameSync15,
80187
80521
  statSync as statSync28,
80188
80522
  unlinkSync as unlinkSync14,
80189
80523
  writeSync as writeSync8
80190
80524
  } from "node:fs";
80191
- import { join as join73, resolve as resolve45 } from "node:path";
80525
+ import { join as join75, resolve as resolve45 } from "node:path";
80192
80526
  var STAGING_SUBDIR = ".staging";
80193
80527
  function overlayPathsFor(agent, opts = {}) {
80194
80528
  const base = opts.root ? resolve45(opts.root, agent) : resolve45(resolveDualPath(`~/.switchroom/agents/${agent}`));
80195
- const scheduleDir = join73(base, "schedule.d");
80196
- const scheduleStagingDir = join73(scheduleDir, STAGING_SUBDIR);
80197
- const skillsDir = join73(base, "skills.d");
80198
- const skillsStagingDir = join73(skillsDir, STAGING_SUBDIR);
80529
+ const scheduleDir = join75(base, "schedule.d");
80530
+ const scheduleStagingDir = join75(scheduleDir, STAGING_SUBDIR);
80531
+ const skillsDir = join75(base, "skills.d");
80532
+ const skillsStagingDir = join75(skillsDir, STAGING_SUBDIR);
80199
80533
  return {
80200
80534
  agentRoot: base,
80201
80535
  scheduleDir,
80202
80536
  scheduleStagingDir,
80203
80537
  skillsDir,
80204
80538
  skillsStagingDir,
80205
- lockPath: join73(base, ".lock"),
80539
+ lockPath: join75(base, ".lock"),
80206
80540
  stagingDir: scheduleStagingDir
80207
80541
  };
80208
80542
  }
80209
80543
  function ensureDirs(paths) {
80210
- mkdirSync42(paths.scheduleDir, { recursive: true });
80211
- mkdirSync42(paths.scheduleStagingDir, { recursive: true });
80544
+ mkdirSync43(paths.scheduleDir, { recursive: true });
80545
+ mkdirSync43(paths.scheduleStagingDir, { recursive: true });
80212
80546
  }
80213
80547
  function ensureSkillsDirs(paths) {
80214
- mkdirSync42(paths.skillsDir, { recursive: true });
80215
- mkdirSync42(paths.skillsStagingDir, { recursive: true });
80548
+ mkdirSync43(paths.skillsDir, { recursive: true });
80549
+ mkdirSync43(paths.skillsStagingDir, { recursive: true });
80216
80550
  }
80217
80551
  function withAgentLock(paths, fn) {
80218
- mkdirSync42(paths.agentRoot, { recursive: true });
80552
+ mkdirSync43(paths.agentRoot, { recursive: true });
80219
80553
  const start = Date.now();
80220
80554
  const TIMEOUT_MS = 5000;
80221
80555
  let fd = null;
@@ -80256,8 +80590,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
80256
80590
  const paths = overlayPathsFor(agent, opts);
80257
80591
  return withAgentLock(paths, () => {
80258
80592
  ensureDirs(paths);
80259
- const stagingPath = join73(paths.scheduleStagingDir, `${slug}.yaml`);
80260
- const finalPath = join73(paths.scheduleDir, `${slug}.yaml`);
80593
+ const stagingPath = join75(paths.scheduleStagingDir, `${slug}.yaml`);
80594
+ const finalPath = join75(paths.scheduleDir, `${slug}.yaml`);
80261
80595
  const fd = openSync13(stagingPath, "w", 384);
80262
80596
  try {
80263
80597
  writeSync8(fd, yamlText);
@@ -80273,8 +80607,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
80273
80607
  const paths = overlayPathsFor(agent, opts);
80274
80608
  return withAgentLock(paths, () => {
80275
80609
  ensureSkillsDirs(paths);
80276
- const stagingPath = join73(paths.skillsStagingDir, `${slug}.yaml`);
80277
- const finalPath = join73(paths.skillsDir, `${slug}.yaml`);
80610
+ const stagingPath = join75(paths.skillsStagingDir, `${slug}.yaml`);
80611
+ const finalPath = join75(paths.skillsDir, `${slug}.yaml`);
80278
80612
  const fd = openSync13(stagingPath, "w", 384);
80279
80613
  try {
80280
80614
  writeSync8(fd, yamlText);
@@ -80289,8 +80623,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
80289
80623
  function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
80290
80624
  const paths = overlayPathsFor(agent, opts);
80291
80625
  return withAgentLock(paths, () => {
80292
- const finalPath = join73(paths.skillsDir, `${slug}.yaml`);
80293
- if (!existsSync75(finalPath))
80626
+ const finalPath = join75(paths.skillsDir, `${slug}.yaml`);
80627
+ if (!existsSync76(finalPath))
80294
80628
  return false;
80295
80629
  unlinkSync14(finalPath);
80296
80630
  return true;
@@ -80298,15 +80632,15 @@ function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
80298
80632
  }
80299
80633
  function listSkillsOverlayEntries(agent, opts = {}) {
80300
80634
  const paths = overlayPathsFor(agent, opts);
80301
- if (!existsSync75(paths.skillsDir))
80635
+ if (!existsSync76(paths.skillsDir))
80302
80636
  return [];
80303
80637
  const out = [];
80304
80638
  for (const name of readdirSync28(paths.skillsDir)) {
80305
80639
  if (!/\.ya?ml$/i.test(name))
80306
80640
  continue;
80307
- const full = join73(paths.skillsDir, name);
80641
+ const full = join75(paths.skillsDir, name);
80308
80642
  try {
80309
- const raw = readFileSync61(full, "utf-8");
80643
+ const raw = readFileSync62(full, "utf-8");
80310
80644
  const slug = name.replace(/\.ya?ml$/i, "");
80311
80645
  out.push({ slug, path: full, raw });
80312
80646
  } catch {}
@@ -80316,8 +80650,8 @@ function listSkillsOverlayEntries(agent, opts = {}) {
80316
80650
  function deleteOverlayEntry(agent, slug, opts = {}) {
80317
80651
  const paths = overlayPathsFor(agent, opts);
80318
80652
  return withAgentLock(paths, () => {
80319
- const finalPath = join73(paths.scheduleDir, `${slug}.yaml`);
80320
- if (!existsSync75(finalPath))
80653
+ const finalPath = join75(paths.scheduleDir, `${slug}.yaml`);
80654
+ if (!existsSync76(finalPath))
80321
80655
  return false;
80322
80656
  unlinkSync14(finalPath);
80323
80657
  return true;
@@ -80325,15 +80659,15 @@ function deleteOverlayEntry(agent, slug, opts = {}) {
80325
80659
  }
80326
80660
  function listOverlayEntries(agent, opts = {}) {
80327
80661
  const paths = overlayPathsFor(agent, opts);
80328
- if (!existsSync75(paths.scheduleDir))
80662
+ if (!existsSync76(paths.scheduleDir))
80329
80663
  return [];
80330
80664
  const out = [];
80331
80665
  for (const name of readdirSync28(paths.scheduleDir)) {
80332
80666
  if (!/\.ya?ml$/i.test(name))
80333
80667
  continue;
80334
- const full = join73(paths.scheduleDir, name);
80668
+ const full = join75(paths.scheduleDir, name);
80335
80669
  try {
80336
- const raw = readFileSync61(full, "utf-8");
80670
+ const raw = readFileSync62(full, "utf-8");
80337
80671
  const slug = name.replace(/\.ya?ml$/i, "");
80338
80672
  out.push({ slug, path: full, raw });
80339
80673
  } catch {}
@@ -80364,7 +80698,7 @@ function filterOverlaySecrets(doc, source) {
80364
80698
  // src/agents/reconcile-dry-run.ts
80365
80699
  init_overlay_schema();
80366
80700
  init_schema();
80367
- var import_yaml16 = __toESM(require_dist(), 1);
80701
+ var import_yaml18 = __toESM(require_dist(), 1);
80368
80702
  init_lifecycle();
80369
80703
  var MIN_CRON_INTERVAL_SECS = 5 * 60;
80370
80704
  function violatesMinInterval(cron) {
@@ -80386,7 +80720,7 @@ function violatesMinInterval(cron) {
80386
80720
  function dryRunReconcile(input) {
80387
80721
  let parsed;
80388
80722
  try {
80389
- parsed = import_yaml16.parse(input.yamlText);
80723
+ parsed = import_yaml18.parse(input.yamlText);
80390
80724
  } catch (err) {
80391
80725
  return {
80392
80726
  ok: false,
@@ -80476,27 +80810,27 @@ function reconcileAgentCronOnly(agent) {
80476
80810
  // src/cli/agent-config-pending.ts
80477
80811
  import {
80478
80812
  closeSync as closeSync14,
80479
- existsSync as existsSync76,
80813
+ existsSync as existsSync77,
80480
80814
  fsyncSync as fsyncSync7,
80481
- mkdirSync as mkdirSync43,
80815
+ mkdirSync as mkdirSync44,
80482
80816
  openSync as openSync14,
80483
80817
  readdirSync as readdirSync29,
80484
- readFileSync as readFileSync62,
80818
+ readFileSync as readFileSync63,
80485
80819
  renameSync as renameSync16,
80486
80820
  unlinkSync as unlinkSync15,
80487
- writeFileSync as writeFileSync36,
80821
+ writeFileSync as writeFileSync37,
80488
80822
  writeSync as writeSync9
80489
80823
  } from "node:fs";
80490
- import { join as join74 } from "node:path";
80824
+ import { join as join76 } from "node:path";
80491
80825
  import { randomBytes as randomBytes14 } from "node:crypto";
80492
80826
  var STAGE_ID_PREFIX = "cap_";
80493
80827
  function pendingDir(agent, opts = {}) {
80494
80828
  const paths = overlayPathsFor(agent, opts);
80495
- return join74(paths.scheduleDir, ".pending");
80829
+ return join76(paths.scheduleDir, ".pending");
80496
80830
  }
80497
80831
  function ensurePendingDir(agent, opts = {}) {
80498
80832
  const dir = pendingDir(agent, opts);
80499
- mkdirSync43(dir, { recursive: true });
80833
+ mkdirSync44(dir, { recursive: true });
80500
80834
  return dir;
80501
80835
  }
80502
80836
  function newStageId() {
@@ -80505,8 +80839,8 @@ function newStageId() {
80505
80839
  function stagePendingScheduleEntry(opts) {
80506
80840
  const dir = ensurePendingDir(opts.agent, { root: opts.root });
80507
80841
  const stageId = opts.stageId ?? newStageId();
80508
- const yamlPath = join74(dir, `${stageId}.yaml`);
80509
- const metaPath = join74(dir, `${stageId}.meta.json`);
80842
+ const yamlPath = join76(dir, `${stageId}.yaml`);
80843
+ const metaPath = join76(dir, `${stageId}.meta.json`);
80510
80844
  const meta = {
80511
80845
  v: 1,
80512
80846
  stage_id: stageId,
@@ -80527,25 +80861,25 @@ function stagePendingScheduleEntry(opts) {
80527
80861
  }
80528
80862
  renameSync16(yamlTmp, yamlPath);
80529
80863
  }
80530
- writeFileSync36(metaPath, JSON.stringify(meta, null, 2) + `
80864
+ writeFileSync37(metaPath, JSON.stringify(meta, null, 2) + `
80531
80865
  `, { mode: 384 });
80532
80866
  return { stageId, yamlPath, metaPath };
80533
80867
  }
80534
80868
  function listPendingScheduleEntries(agent, opts = {}) {
80535
80869
  const dir = pendingDir(agent, opts);
80536
- if (!existsSync76(dir))
80870
+ if (!existsSync77(dir))
80537
80871
  return [];
80538
80872
  const out = [];
80539
80873
  for (const name of readdirSync29(dir).sort()) {
80540
80874
  if (!name.endsWith(".meta.json"))
80541
80875
  continue;
80542
80876
  const stageId = name.slice(0, -".meta.json".length);
80543
- const metaPath = join74(dir, name);
80544
- const yamlPath = join74(dir, `${stageId}.yaml`);
80545
- if (!existsSync76(yamlPath))
80877
+ const metaPath = join76(dir, name);
80878
+ const yamlPath = join76(dir, `${stageId}.yaml`);
80879
+ if (!existsSync77(yamlPath))
80546
80880
  continue;
80547
80881
  try {
80548
- const meta = JSON.parse(readFileSync62(metaPath, "utf-8"));
80882
+ const meta = JSON.parse(readFileSync63(metaPath, "utf-8"));
80549
80883
  if (meta?.v !== 1 || typeof meta.stage_id !== "string")
80550
80884
  continue;
80551
80885
  out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
@@ -80560,8 +80894,8 @@ function commitPendingScheduleEntry(opts) {
80560
80894
  return { committed: false, reason: "not_found" };
80561
80895
  const slug = match.meta.entry.name ?? match.stageId;
80562
80896
  const paths = overlayPathsFor(opts.agent, { root: opts.root });
80563
- const finalPath = join74(paths.scheduleDir, `${slug}.yaml`);
80564
- if (existsSync76(finalPath)) {
80897
+ const finalPath = join76(paths.scheduleDir, `${slug}.yaml`);
80898
+ if (existsSync77(finalPath)) {
80565
80899
  return { committed: false, reason: "slug_collision" };
80566
80900
  }
80567
80901
  renameSync16(match.yamlPath, finalPath);
@@ -80584,7 +80918,7 @@ function denyPendingScheduleEntry(opts) {
80584
80918
 
80585
80919
  // src/cli/agent-config-write.ts
80586
80920
  init_protocol3();
80587
- import { existsSync as existsSync77, readFileSync as readFileSync63 } from "node:fs";
80921
+ import { existsSync as existsSync78, readFileSync as readFileSync64 } from "node:fs";
80588
80922
  var MAX_ENTRIES_PER_AGENT = 20;
80589
80923
  var MIN_CRON_INTERVAL_MIN = 5;
80590
80924
  function extractCronSmallestGapMin(expr) {
@@ -80692,7 +81026,7 @@ function scheduleAdd(opts) {
80692
81026
  ]
80693
81027
  };
80694
81028
  const yamlText = (() => {
80695
- const body = import_yaml17.stringify(doc);
81029
+ const body = import_yaml19.stringify(doc);
80696
81030
  const header = opts.name ? `# name: ${opts.name}
80697
81031
  ` : "";
80698
81032
  return header + body;
@@ -80802,7 +81136,7 @@ function scheduleAddOrStage(opts) {
80802
81136
  ]
80803
81137
  };
80804
81138
  const yamlText = (opts.name ? `# name: ${opts.name}
80805
- ` : "") + import_yaml17.stringify(doc);
81139
+ ` : "") + import_yaml19.stringify(doc);
80806
81140
  const summary = (() => {
80807
81141
  const parts = [`cron=${opts.cronExpr}`];
80808
81142
  if (opts.secrets?.length)
@@ -80852,7 +81186,7 @@ function scheduleRemove(opts) {
80852
81186
  break;
80853
81187
  }
80854
81188
  try {
80855
- const parsed = import_yaml17.parse(e.raw);
81189
+ const parsed = import_yaml19.parse(e.raw);
80856
81190
  if (parsed && parsed.name === opts.name) {
80857
81191
  match = e;
80858
81192
  break;
@@ -80870,8 +81204,8 @@ function scheduleRemove(opts) {
80870
81204
  }
80871
81205
  let priorContent = null;
80872
81206
  try {
80873
- if (existsSync77(match.path))
80874
- priorContent = readFileSync63(match.path, "utf-8");
81207
+ if (existsSync78(match.path))
81208
+ priorContent = readFileSync64(match.path, "utf-8");
80875
81209
  } catch {}
80876
81210
  deleteOverlayEntry(agent, match.slug, { root: opts.root });
80877
81211
  const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
@@ -81062,11 +81396,11 @@ function registerAgentConfigWriteCommands(program3) {
81062
81396
  }
81063
81397
 
81064
81398
  // src/cli/agent-config-skill-write.ts
81065
- var import_yaml18 = __toESM(require_dist(), 1);
81066
- import { existsSync as existsSync78 } from "node:fs";
81399
+ var import_yaml20 = __toESM(require_dist(), 1);
81400
+ import { existsSync as existsSync79 } from "node:fs";
81067
81401
  init_reconcile_default_skills();
81068
- var import_yaml19 = __toESM(require_dist(), 1);
81069
- import { join as join75 } from "node:path";
81402
+ var import_yaml21 = __toESM(require_dist(), 1);
81403
+ import { join as join77 } from "node:path";
81070
81404
  var MAX_SKILLS_PER_AGENT = 20;
81071
81405
  var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
81072
81406
  function exitCodeFor2(code) {
@@ -81115,7 +81449,7 @@ function countCurrentSkills(agent, opts) {
81115
81449
  let total = 0;
81116
81450
  for (const e of entries) {
81117
81451
  try {
81118
- const doc = import_yaml19.parse(e.raw);
81452
+ const doc = import_yaml21.parse(e.raw);
81119
81453
  total += (doc?.skills ?? []).length;
81120
81454
  } catch {}
81121
81455
  }
@@ -81141,11 +81475,11 @@ function skillInstall(opts) {
81141
81475
  return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
81142
81476
  }
81143
81477
  const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
81144
- const skillPath = join75(poolDir, skillName);
81145
- if (!existsSync78(skillPath)) {
81478
+ const skillPath = join77(poolDir, skillName);
81479
+ if (!existsSync79(skillPath)) {
81146
81480
  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.`);
81147
81481
  }
81148
- const yamlText = import_yaml18.stringify({ skills: [skillName] });
81482
+ const yamlText = import_yaml20.stringify({ skills: [skillName] });
81149
81483
  let path8;
81150
81484
  try {
81151
81485
  path8 = writeSkillsOverlayEntry(agent, slug, yamlText, { root: opts.root });
@@ -81306,25 +81640,25 @@ function registerAgentConfigSkillWriteCommands(program3) {
81306
81640
  // src/cli/skill.ts
81307
81641
  import {
81308
81642
  closeSync as closeSync15,
81309
- existsSync as existsSync79,
81643
+ existsSync as existsSync80,
81310
81644
  lstatSync as lstatSync8,
81311
- mkdirSync as mkdirSync44,
81645
+ mkdirSync as mkdirSync45,
81312
81646
  mkdtempSync as mkdtempSync5,
81313
81647
  openSync as openSync15,
81314
- readFileSync as readFileSync64,
81648
+ readFileSync as readFileSync65,
81315
81649
  readdirSync as readdirSync30,
81316
81650
  realpathSync as realpathSync7,
81317
81651
  renameSync as renameSync17,
81318
81652
  rmSync as rmSync16,
81319
81653
  statSync as statSync29,
81320
- writeFileSync as writeFileSync37
81654
+ writeFileSync as writeFileSync38
81321
81655
  } from "node:fs";
81322
- import { tmpdir as tmpdir4, homedir as homedir43 } from "node:os";
81323
- import { dirname as dirname22, join as join76, relative as relative2, resolve as resolve46 } from "node:path";
81324
- import { spawnSync as spawnSync10 } from "node:child_process";
81656
+ import { tmpdir as tmpdir5, homedir as homedir44 } from "node:os";
81657
+ import { dirname as dirname22, join as join78, relative as relative2, resolve as resolve46 } from "node:path";
81658
+ import { spawnSync as spawnSync11 } from "node:child_process";
81325
81659
 
81326
81660
  // src/cli/skill-common.ts
81327
- var import_yaml20 = __toESM(require_dist(), 1);
81661
+ var import_yaml22 = __toESM(require_dist(), 1);
81328
81662
  var MAX_FILE_BYTES = 256 * 1024;
81329
81663
  var MAX_SKILL_BYTES = 2 * 1024 * 1024;
81330
81664
  var MAX_FILES_PER_SKILL = 50;
@@ -81428,7 +81762,7 @@ function validateSkillMd(content, expectedName) {
81428
81762
  }
81429
81763
  let parsed;
81430
81764
  try {
81431
- parsed = import_yaml20.parse(fmText);
81765
+ parsed = import_yaml22.parse(fmText);
81432
81766
  } catch (e) {
81433
81767
  return authorErr("E_SKILL_INVALID_FRONTMATTER", `frontmatter is not valid YAML: ${e.message}`);
81434
81768
  }
@@ -81514,10 +81848,10 @@ function scanForClaudeP2(content) {
81514
81848
  function resolveSkillsPoolDir2(override) {
81515
81849
  const raw = override ?? "~/.switchroom/skills";
81516
81850
  if (raw.startsWith("~/")) {
81517
- return join76(homedir43(), raw.slice(2));
81851
+ return join78(homedir44(), raw.slice(2));
81518
81852
  }
81519
81853
  if (raw === "~")
81520
- return homedir43();
81854
+ return homedir44();
81521
81855
  return resolve46(raw);
81522
81856
  }
81523
81857
  function readStdinSync() {
@@ -81553,7 +81887,7 @@ function loadFromDir(dir) {
81553
81887
  const walk2 = (sub) => {
81554
81888
  const entries = readdirSync30(sub, { withFileTypes: true });
81555
81889
  for (const ent of entries) {
81556
- const full = join76(sub, ent.name);
81890
+ const full = join78(sub, ent.name);
81557
81891
  const rel = relative2(abs, full);
81558
81892
  if (ent.isSymbolicLink()) {
81559
81893
  fail2(`refusing to read symlink inside --from dir: ${rel}`);
@@ -81563,7 +81897,7 @@ function loadFromDir(dir) {
81563
81897
  continue;
81564
81898
  }
81565
81899
  if (ent.isFile()) {
81566
- const buf = readFileSync64(full);
81900
+ const buf = readFileSync65(full);
81567
81901
  files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
81568
81902
  }
81569
81903
  }
@@ -81574,7 +81908,7 @@ function loadFromDir(dir) {
81574
81908
  function loadFromTarball(tarPath) {
81575
81909
  const isGz = tarPath.endsWith(".gz") || tarPath.endsWith(".tgz");
81576
81910
  const listFlags = isGz ? ["-tzf"] : ["-tf"];
81577
- const list2 = spawnSync10("tar", [...listFlags, tarPath], {
81911
+ const list2 = spawnSync11("tar", [...listFlags, tarPath], {
81578
81912
  encoding: "utf-8",
81579
81913
  stdio: ["ignore", "pipe", "pipe"]
81580
81914
  });
@@ -81588,10 +81922,10 @@ function loadFromTarball(tarPath) {
81588
81922
  fail2(`tarball contains disallowed path: ${JSON.stringify(entry)} \u2014 ` + `refusing to extract before any file is written`);
81589
81923
  }
81590
81924
  }
81591
- const staging = mkdtempSync5(join76(tmpdir4(), "skill-apply-extract-"));
81925
+ const staging = mkdtempSync5(join78(tmpdir5(), "skill-apply-extract-"));
81592
81926
  try {
81593
81927
  const flags = isGz ? ["-xzf"] : ["-xf"];
81594
- const r = spawnSync10("tar", [
81928
+ const r = spawnSync11("tar", [
81595
81929
  ...flags,
81596
81930
  tarPath,
81597
81931
  "-C",
@@ -81612,7 +81946,7 @@ function loadFromTarball(tarPath) {
81612
81946
  }
81613
81947
  }
81614
81948
  function loadSingleFile(filePath) {
81615
- const content = readFileSync64(filePath, "utf-8");
81949
+ const content = readFileSync65(filePath, "utf-8");
81616
81950
  return { "SKILL.md": content };
81617
81951
  }
81618
81952
  function loadFromStdin() {
@@ -81666,7 +82000,7 @@ function validatePayload(name, files) {
81666
82000
  if (errors2.length === 0) {
81667
82001
  for (const [path8, content] of Object.entries(files)) {
81668
82002
  if (SH_SCRIPT_RE2.test(path8)) {
81669
- const r = spawnSync10("bash", ["-n"], {
82003
+ const r = spawnSync11("bash", ["-n"], {
81670
82004
  input: content,
81671
82005
  encoding: "utf-8"
81672
82006
  });
@@ -81674,11 +82008,11 @@ function validatePayload(name, files) {
81674
82008
  errors2.push(`${path8} fails \`bash -n\` syntax check: ${(r.stderr ?? "").trim()}`);
81675
82009
  }
81676
82010
  } else if (PY_SCRIPT_RE2.test(path8)) {
81677
- const tmp = mkdtempSync5(join76(tmpdir4(), "skill-apply-py-"));
81678
- const tmpPy = join76(tmp, "check.py");
82011
+ const tmp = mkdtempSync5(join78(tmpdir5(), "skill-apply-py-"));
82012
+ const tmpPy = join78(tmp, "check.py");
81679
82013
  try {
81680
- writeFileSync37(tmpPy, content);
81681
- const r = spawnSync10("python3", ["-m", "py_compile", tmpPy], {
82014
+ writeFileSync38(tmpPy, content);
82015
+ const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
81682
82016
  encoding: "utf-8"
81683
82017
  });
81684
82018
  if (r.status !== 0) {
@@ -81695,15 +82029,15 @@ function validatePayload(name, files) {
81695
82029
  function diffSummary(currentDir, files) {
81696
82030
  const lines = [];
81697
82031
  const currentFiles = {};
81698
- if (existsSync79(currentDir)) {
82032
+ if (existsSync80(currentDir)) {
81699
82033
  const walk2 = (sub) => {
81700
82034
  for (const ent of readdirSync30(sub, { withFileTypes: true })) {
81701
- const full = join76(sub, ent.name);
82035
+ const full = join78(sub, ent.name);
81702
82036
  const rel = relative2(currentDir, full);
81703
82037
  if (ent.isDirectory()) {
81704
82038
  walk2(full);
81705
82039
  } else if (ent.isFile()) {
81706
- currentFiles[rel.replace(/\\/g, "/")] = readFileSync64(full, "utf-8");
82040
+ currentFiles[rel.replace(/\\/g, "/")] = readFileSync65(full, "utf-8");
81707
82041
  }
81708
82042
  }
81709
82043
  };
@@ -81731,10 +82065,10 @@ function diffSummary(currentDir, files) {
81731
82065
  `);
81732
82066
  }
81733
82067
  function writePayload(poolDir, name, files) {
81734
- if (!existsSync79(poolDir)) {
81735
- mkdirSync44(poolDir, { recursive: true, mode: 493 });
82068
+ if (!existsSync80(poolDir)) {
82069
+ mkdirSync45(poolDir, { recursive: true, mode: 493 });
81736
82070
  }
81737
- const target = join76(poolDir, name);
82071
+ const target = join78(poolDir, name);
81738
82072
  let targetIsSymlink = false;
81739
82073
  try {
81740
82074
  const st = lstatSync8(target);
@@ -81745,15 +82079,15 @@ function writePayload(poolDir, name, files) {
81745
82079
  if (targetIsSymlink) {
81746
82080
  fail2(`refusing to overwrite symlink at ${target}; investigate manually`);
81747
82081
  }
81748
- const staging = mkdtempSync5(join76(poolDir, `.skill-apply-stage-${name}-`));
82082
+ const staging = mkdtempSync5(join78(poolDir, `.skill-apply-stage-${name}-`));
81749
82083
  let oldRename = null;
81750
82084
  try {
81751
82085
  for (const [path8, content] of Object.entries(files)) {
81752
- const full = join76(staging, path8);
81753
- mkdirSync44(dirname22(full), { recursive: true, mode: 493 });
82086
+ const full = join78(staging, path8);
82087
+ mkdirSync45(dirname22(full), { recursive: true, mode: 493 });
81754
82088
  const fd = openSync15(full, "wx");
81755
82089
  try {
81756
- writeFileSync37(fd, content);
82090
+ writeFileSync38(fd, content);
81757
82091
  } finally {
81758
82092
  closeSync15(fd);
81759
82093
  }
@@ -81780,9 +82114,9 @@ function writePayload(poolDir, name, files) {
81780
82114
  try {
81781
82115
  rmSync16(staging, { recursive: true, force: true });
81782
82116
  } catch {}
81783
- if (oldRename && existsSync79(oldRename)) {
82117
+ if (oldRename && existsSync80(oldRename)) {
81784
82118
  try {
81785
- if (existsSync79(target)) {
82119
+ if (existsSync80(target)) {
81786
82120
  rmSync16(target, { recursive: true, force: true });
81787
82121
  }
81788
82122
  renameSync17(oldRename, target);
@@ -81803,7 +82137,7 @@ function registerSkillCommand(program3) {
81803
82137
  files = loadFromStdin();
81804
82138
  } else {
81805
82139
  const fromPath = resolve46(opts.from);
81806
- if (!existsSync79(fromPath)) {
82140
+ if (!existsSync80(fromPath)) {
81807
82141
  fail2(`--from path does not exist: ${opts.from}`);
81808
82142
  }
81809
82143
  const st = statSync29(fromPath);
@@ -81827,7 +82161,7 @@ function registerSkillCommand(program3) {
81827
82161
  }
81828
82162
  const config = loadConfig();
81829
82163
  const poolDir = resolveSkillsPoolDir2(config.switchroom?.skills_dir);
81830
- const currentDir = join76(poolDir, name);
82164
+ const currentDir = join78(poolDir, name);
81831
82165
  console.log(source_default.bold(`Skill: ${name}`) + source_default.gray(` (${Object.keys(files).length} files, ${sumBytes(files)} bytes)`));
81832
82166
  console.log(source_default.bold("Diff vs current pool content:"));
81833
82167
  console.log(diffSummary(currentDir, files));
@@ -81841,7 +82175,7 @@ function registerSkillCommand(program3) {
81841
82175
  \u2713 Wrote ${name} to ${currentDir}`));
81842
82176
  const applyBin = process.argv[1] ?? "switchroom";
81843
82177
  console.log(source_default.gray(`Running \`switchroom apply --non-interactive\`...`));
81844
- const r = spawnSync10(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
82178
+ const r = spawnSync11(process.argv0, [applyBin, "apply", "--non-interactive"], { stdio: "inherit" });
81845
82179
  if (r.status !== 0) {
81846
82180
  console.error(source_default.yellow(`(warning: \`switchroom apply\` exited ${r.status} \u2014 skill is ` + `in the pool but symlinks may not be refreshed. Re-run manually.)`));
81847
82181
  }
@@ -81858,22 +82192,22 @@ function sumBytes(files) {
81858
82192
  // src/cli/skill-personal.ts
81859
82193
  import {
81860
82194
  closeSync as closeSync16,
81861
- existsSync as existsSync80,
82195
+ existsSync as existsSync81,
81862
82196
  lstatSync as lstatSync9,
81863
- mkdirSync as mkdirSync45,
82197
+ mkdirSync as mkdirSync46,
81864
82198
  mkdtempSync as mkdtempSync6,
81865
82199
  openSync as openSync16,
81866
- readFileSync as readFileSync65,
82200
+ readFileSync as readFileSync66,
81867
82201
  readdirSync as readdirSync31,
81868
82202
  renameSync as renameSync18,
81869
82203
  rmSync as rmSync17,
81870
82204
  statSync as statSync30,
81871
82205
  utimesSync,
81872
- writeFileSync as writeFileSync38
82206
+ writeFileSync as writeFileSync39
81873
82207
  } from "node:fs";
81874
- import { dirname as dirname23, join as join77, relative as relative3, resolve as resolve47 } from "node:path";
81875
- import { homedir as homedir44, tmpdir as tmpdir5 } from "node:os";
81876
- import { spawnSync as spawnSync11 } from "node:child_process";
82208
+ import { dirname as dirname23, join as join79, relative as relative3, resolve as resolve47 } from "node:path";
82209
+ import { homedir as homedir45, tmpdir as tmpdir6 } from "node:os";
82210
+ import { spawnSync as spawnSync12 } from "node:child_process";
81877
82211
  init_helpers();
81878
82212
  init_source();
81879
82213
  var PERSONAL_PREFIX = "personal-";
@@ -81882,15 +82216,15 @@ var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
81882
82216
  var PERSONAL_SKILLS_SUBPATH = "personal-skills";
81883
82217
  function resolveConfigSkillsDir(agent) {
81884
82218
  const override = process.env.SWITCHROOM_CONFIG_DIR;
81885
- const candidate = override ? resolve47(override) : join77(homedir44(), ".switchroom-config");
81886
- if (!existsSync80(candidate))
82219
+ const candidate = override ? resolve47(override) : join79(homedir45(), ".switchroom-config");
82220
+ if (!existsSync81(candidate))
81887
82221
  return null;
81888
- return join77(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
82222
+ return join79(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
81889
82223
  }
81890
82224
  var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
81891
82225
  function sweepMirrorPriors(configSkillsRoot) {
81892
82226
  try {
81893
- if (!existsSync80(configSkillsRoot))
82227
+ if (!existsSync81(configSkillsRoot))
81894
82228
  return;
81895
82229
  const now = Date.now();
81896
82230
  for (const ent of readdirSync31(configSkillsRoot)) {
@@ -81903,7 +82237,7 @@ function sweepMirrorPriors(configSkillsRoot) {
81903
82237
  if (now - ts < MIRROR_PRIOR_TTL_MS)
81904
82238
  continue;
81905
82239
  try {
81906
- rmSync17(join77(configSkillsRoot, ent), { recursive: true, force: true });
82240
+ rmSync17(join79(configSkillsRoot, ent), { recursive: true, force: true });
81907
82241
  } catch {}
81908
82242
  }
81909
82243
  } catch {}
@@ -81912,7 +82246,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
81912
82246
  const configSkillsRoot = resolveConfigSkillsDir(agent);
81913
82247
  if (!configSkillsRoot)
81914
82248
  return;
81915
- const dest = join77(configSkillsRoot, name);
82249
+ const dest = join79(configSkillsRoot, name);
81916
82250
  try {
81917
82251
  if (liveSkillDir !== null) {
81918
82252
  try {
@@ -81926,32 +82260,32 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
81926
82260
  }
81927
82261
  if (liveSkillDir === null) {
81928
82262
  sweepMirrorPriors(configSkillsRoot);
81929
- if (existsSync80(dest)) {
81930
- const trash = join77(configSkillsRoot, `.${name}-trash-${Date.now()}`);
82263
+ if (existsSync81(dest)) {
82264
+ const trash = join79(configSkillsRoot, `.${name}-trash-${Date.now()}`);
81931
82265
  renameSync18(dest, trash);
81932
82266
  }
81933
82267
  return;
81934
82268
  }
81935
- mkdirSync45(configSkillsRoot, { recursive: true, mode: 493 });
82269
+ mkdirSync46(configSkillsRoot, { recursive: true, mode: 493 });
81936
82270
  sweepMirrorPriors(configSkillsRoot);
81937
- const staging = mkdtempSync6(join77(configSkillsRoot, `.${name}-staging-`));
82271
+ const staging = mkdtempSync6(join79(configSkillsRoot, `.${name}-staging-`));
81938
82272
  const walk2 = (src, dst) => {
81939
- mkdirSync45(dst, { recursive: true, mode: 493 });
82273
+ mkdirSync46(dst, { recursive: true, mode: 493 });
81940
82274
  for (const ent of readdirSync31(src, { withFileTypes: true })) {
81941
- const s = join77(src, ent.name);
81942
- const d = join77(dst, ent.name);
82275
+ const s = join79(src, ent.name);
82276
+ const d = join79(dst, ent.name);
81943
82277
  if (ent.isSymbolicLink())
81944
82278
  continue;
81945
82279
  if (ent.isDirectory())
81946
82280
  walk2(s, d);
81947
82281
  else if (ent.isFile()) {
81948
- writeFileSync38(d, readFileSync65(s));
82282
+ writeFileSync39(d, readFileSync66(s));
81949
82283
  }
81950
82284
  }
81951
82285
  };
81952
82286
  walk2(liveSkillDir, staging);
81953
- if (existsSync80(dest)) {
81954
- const prior = join77(configSkillsRoot, `.${name}-prior-${Date.now()}`);
82287
+ if (existsSync81(dest)) {
82288
+ const prior = join79(configSkillsRoot, `.${name}-prior-${Date.now()}`);
81955
82289
  renameSync18(dest, prior);
81956
82290
  }
81957
82291
  renameSync18(staging, dest);
@@ -81978,13 +82312,13 @@ function resolveAgent(opts) {
81978
82312
  function resolveAgentsRoot(opts) {
81979
82313
  if (opts.root)
81980
82314
  return resolve47(opts.root);
81981
- return join77(homedir44(), ".switchroom", "agents");
82315
+ return join79(homedir45(), ".switchroom", "agents");
81982
82316
  }
81983
82317
  function personalSkillDir(agentsRoot, agent, name) {
81984
- return join77(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
82318
+ return join79(agentsRoot, agent, ".claude", "skills", PERSONAL_PREFIX + name);
81985
82319
  }
81986
82320
  function trashDir(agentsRoot, agent) {
81987
- return join77(agentsRoot, agent, ".claude", TRASH_DIRNAME);
82321
+ return join79(agentsRoot, agent, ".claude", TRASH_DIRNAME);
81988
82322
  }
81989
82323
  function readStdinSync2() {
81990
82324
  const chunks = [];
@@ -82014,7 +82348,7 @@ function loadFromDir2(dir) {
82014
82348
  const files = {};
82015
82349
  const walk2 = (sub) => {
82016
82350
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
82017
- const full = join77(sub, ent.name);
82351
+ const full = join79(sub, ent.name);
82018
82352
  if (ent.isSymbolicLink()) {
82019
82353
  fail3(`refusing to read symlink in --from dir: ${relative3(abs, full)}`);
82020
82354
  }
@@ -82024,7 +82358,7 @@ function loadFromDir2(dir) {
82024
82358
  }
82025
82359
  if (ent.isFile()) {
82026
82360
  const rel = relative3(abs, full).replace(/\\/g, "/");
82027
- files[rel] = readFileSync65(full, "utf-8");
82361
+ files[rel] = readFileSync66(full, "utf-8");
82028
82362
  }
82029
82363
  }
82030
82364
  };
@@ -82062,16 +82396,16 @@ function behavioralValidate(files) {
82062
82396
  const errors2 = [];
82063
82397
  for (const [path8, content] of Object.entries(files)) {
82064
82398
  if (SH_SCRIPT_RE.test(path8)) {
82065
- const r = spawnSync11("bash", ["-n"], { input: content, encoding: "utf-8" });
82399
+ const r = spawnSync12("bash", ["-n"], { input: content, encoding: "utf-8" });
82066
82400
  if (r.status !== 0) {
82067
82401
  errors2.push(`${path8} fails \`bash -n\`: ${(r.stderr ?? "").trim()}`);
82068
82402
  }
82069
82403
  } else if (PY_SCRIPT_RE.test(path8)) {
82070
- const tmp = mkdtempSync6(join77(tmpdir5(), "skill-personal-py-"));
82071
- const tmpPy = join77(tmp, "check.py");
82404
+ const tmp = mkdtempSync6(join79(tmpdir6(), "skill-personal-py-"));
82405
+ const tmpPy = join79(tmp, "check.py");
82072
82406
  try {
82073
- writeFileSync38(tmpPy, content);
82074
- const r = spawnSync11("python3", ["-m", "py_compile", tmpPy], {
82407
+ writeFileSync39(tmpPy, content);
82408
+ const r = spawnSync12("python3", ["-m", "py_compile", tmpPy], {
82075
82409
  encoding: "utf-8"
82076
82410
  });
82077
82411
  if (r.status !== 0) {
@@ -82086,13 +82420,13 @@ function behavioralValidate(files) {
82086
82420
  }
82087
82421
  function sweepTrash(agentsRoot, agent) {
82088
82422
  const trash = trashDir(agentsRoot, agent);
82089
- if (!existsSync80(trash))
82423
+ if (!existsSync81(trash))
82090
82424
  return;
82091
82425
  const now = Date.now();
82092
82426
  for (const ent of readdirSync31(trash, { withFileTypes: true })) {
82093
82427
  if (!ent.isDirectory())
82094
82428
  continue;
82095
- const entPath = join77(trash, ent.name);
82429
+ const entPath = join79(trash, ent.name);
82096
82430
  try {
82097
82431
  const st = statSync30(entPath);
82098
82432
  if (now - st.mtimeMs > TRASH_TTL_MS) {
@@ -82112,16 +82446,16 @@ function writePersonalSkill(targetDir, files) {
82112
82446
  if (targetIsSymlink) {
82113
82447
  fail3(`refusing to overwrite symlink at ${targetDir}; investigate manually`);
82114
82448
  }
82115
- mkdirSync45(dirname23(targetDir), { recursive: true, mode: 493 });
82116
- const staging = mkdtempSync6(join77(dirname23(targetDir), `.skill-personal-stage-`));
82449
+ mkdirSync46(dirname23(targetDir), { recursive: true, mode: 493 });
82450
+ const staging = mkdtempSync6(join79(dirname23(targetDir), `.skill-personal-stage-`));
82117
82451
  let oldRename = null;
82118
82452
  try {
82119
82453
  for (const [path8, content] of Object.entries(files)) {
82120
- const full = join77(staging, path8);
82121
- mkdirSync45(dirname23(full), { recursive: true, mode: 493 });
82454
+ const full = join79(staging, path8);
82455
+ mkdirSync46(dirname23(full), { recursive: true, mode: 493 });
82122
82456
  const fd = openSync16(full, "wx");
82123
82457
  try {
82124
- writeFileSync38(fd, content);
82458
+ writeFileSync39(fd, content);
82125
82459
  } finally {
82126
82460
  closeSync16(fd);
82127
82461
  }
@@ -82148,9 +82482,9 @@ function writePersonalSkill(targetDir, files) {
82148
82482
  try {
82149
82483
  rmSync17(staging, { recursive: true, force: true });
82150
82484
  } catch {}
82151
- if (oldRename && existsSync80(oldRename)) {
82485
+ if (oldRename && existsSync81(oldRename)) {
82152
82486
  try {
82153
- if (existsSync80(targetDir)) {
82487
+ if (existsSync81(targetDir)) {
82154
82488
  rmSync17(targetDir, { recursive: true, force: true });
82155
82489
  }
82156
82490
  renameSync18(oldRename, targetDir);
@@ -82202,7 +82536,7 @@ function loadFiles(opts) {
82202
82536
  return loadFromStdin2();
82203
82537
  }
82204
82538
  const p = resolve47(opts.from);
82205
- if (!existsSync80(p)) {
82539
+ if (!existsSync81(p)) {
82206
82540
  fail3(`--from path does not exist: ${opts.from}`);
82207
82541
  }
82208
82542
  const st = statSync30(p);
@@ -82210,7 +82544,7 @@ function loadFiles(opts) {
82210
82544
  return loadFromDir2(p);
82211
82545
  }
82212
82546
  if (p.endsWith(".md")) {
82213
- return { "SKILL.md": readFileSync65(p, "utf-8") };
82547
+ return { "SKILL.md": readFileSync66(p, "utf-8") };
82214
82548
  }
82215
82549
  fail3(`--from must be a directory or a .md file. Got: ${opts.from}`);
82216
82550
  }
@@ -82250,10 +82584,10 @@ function editPersonalAction(name, opts) {
82250
82584
  }
82251
82585
  var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
82252
82586
  function defaultSharedRoot() {
82253
- return join77(homedir44(), ".switchroom", "skills");
82587
+ return join79(homedir45(), ".switchroom", "skills");
82254
82588
  }
82255
82589
  function defaultBundledRoot() {
82256
- return join77(homedir44(), ".switchroom", "skills", "_bundled");
82590
+ return join79(homedir45(), ".switchroom", "skills", "_bundled");
82257
82591
  }
82258
82592
  function resolveCloneSource(source, opts) {
82259
82593
  const m = CLONE_SOURCE_RE.exec(source);
@@ -82263,8 +82597,8 @@ function resolveCloneSource(source, opts) {
82263
82597
  const tier = m[1];
82264
82598
  const slug = m[2];
82265
82599
  const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
82266
- const dir = join77(root, slug);
82267
- if (!existsSync80(dir)) {
82600
+ const dir = join79(root, slug);
82601
+ if (!existsSync81(dir)) {
82268
82602
  fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
82269
82603
  }
82270
82604
  const st = lstatSync9(dir);
@@ -82279,7 +82613,7 @@ function readSourceFiles(dir) {
82279
82613
  const skipped = [];
82280
82614
  const walk2 = (sub) => {
82281
82615
  for (const ent of readdirSync31(sub, { withFileTypes: true })) {
82282
- const full = join77(sub, ent.name);
82616
+ const full = join79(sub, ent.name);
82283
82617
  if (ent.isSymbolicLink()) {
82284
82618
  continue;
82285
82619
  }
@@ -82299,7 +82633,7 @@ function readSourceFiles(dir) {
82299
82633
  fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
82300
82634
  }
82301
82635
  } catch {}
82302
- files[rel] = readFileSync65(full, "utf-8");
82636
+ files[rel] = readFileSync66(full, "utf-8");
82303
82637
  }
82304
82638
  }
82305
82639
  };
@@ -82388,9 +82722,9 @@ function removePersonalAction(name, opts) {
82388
82722
  throw err2;
82389
82723
  }
82390
82724
  const trashRoot = trashDir(agentsRoot, agent);
82391
- mkdirSync45(trashRoot, { recursive: true, mode: 493 });
82725
+ mkdirSync46(trashRoot, { recursive: true, mode: 493 });
82392
82726
  const ts = Date.now();
82393
- const trashTarget = join77(trashRoot, `${name}-${ts}`);
82727
+ const trashTarget = join79(trashRoot, `${name}-${ts}`);
82394
82728
  renameSync18(target, trashTarget);
82395
82729
  const now = new Date(ts);
82396
82730
  utimesSync(trashTarget, now, now);
@@ -82409,16 +82743,16 @@ function listPersonalAction(opts) {
82409
82743
  const agent = resolveAgent(opts);
82410
82744
  const agentsRoot = resolveAgentsRoot(opts);
82411
82745
  sweepTrash(agentsRoot, agent);
82412
- const skillsDir = join77(agentsRoot, agent, ".claude", "skills");
82746
+ const skillsDir = join79(agentsRoot, agent, ".claude", "skills");
82413
82747
  const personal = [];
82414
- if (existsSync80(skillsDir)) {
82748
+ if (existsSync81(skillsDir)) {
82415
82749
  for (const ent of readdirSync31(skillsDir, { withFileTypes: true })) {
82416
82750
  if (!ent.isDirectory())
82417
82751
  continue;
82418
82752
  if (!ent.name.startsWith(PERSONAL_PREFIX))
82419
82753
  continue;
82420
82754
  const skillName = ent.name.slice(PERSONAL_PREFIX.length);
82421
- const skillPath = join77(skillsDir, ent.name);
82755
+ const skillPath = join79(skillsDir, ent.name);
82422
82756
  let fileCount = 0;
82423
82757
  let totalBytes = 0;
82424
82758
  const walk2 = (sub) => {
@@ -82426,10 +82760,10 @@ function listPersonalAction(opts) {
82426
82760
  if (e.isFile()) {
82427
82761
  fileCount += 1;
82428
82762
  try {
82429
- totalBytes += statSync30(join77(sub, e.name)).size;
82763
+ totalBytes += statSync30(join79(sub, e.name)).size;
82430
82764
  } catch {}
82431
82765
  } else if (e.isDirectory()) {
82432
- walk2(join77(sub, e.name));
82766
+ walk2(join79(sub, e.name));
82433
82767
  }
82434
82768
  }
82435
82769
  };
@@ -82467,29 +82801,29 @@ function registerSkillPersonalCommands(program3) {
82467
82801
 
82468
82802
  // src/cli/skill-search.ts
82469
82803
  init_helpers();
82470
- var import_yaml21 = __toESM(require_dist(), 1);
82471
- import { existsSync as existsSync81, readdirSync as readdirSync32, readFileSync as readFileSync66, statSync as statSync31 } from "node:fs";
82472
- import { homedir as homedir45 } from "node:os";
82473
- import { join as join78, resolve as resolve48 } from "node:path";
82804
+ var import_yaml23 = __toESM(require_dist(), 1);
82805
+ import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync67, statSync as statSync31 } from "node:fs";
82806
+ import { homedir as homedir46 } from "node:os";
82807
+ import { join as join80, resolve as resolve48 } from "node:path";
82474
82808
  var PERSONAL_PREFIX2 = "personal-";
82475
82809
  var BUNDLED_SUBDIR = "_bundled";
82476
82810
  var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
82477
82811
  function defaultAgentsRoot() {
82478
- return resolve48(homedir45(), ".switchroom/agents");
82812
+ return resolve48(homedir46(), ".switchroom/agents");
82479
82813
  }
82480
82814
  function defaultSharedRoot2() {
82481
- return resolve48(homedir45(), ".switchroom/skills");
82815
+ return resolve48(homedir46(), ".switchroom/skills");
82482
82816
  }
82483
82817
  function defaultBundledRoot2() {
82484
- return resolve48(homedir45(), ".switchroom/skills/_bundled");
82818
+ return resolve48(homedir46(), ".switchroom/skills/_bundled");
82485
82819
  }
82486
82820
  function readSkillFrontmatter(skillDir) {
82487
- const mdPath = join78(skillDir, "SKILL.md");
82488
- if (!existsSync81(mdPath))
82821
+ const mdPath = join80(skillDir, "SKILL.md");
82822
+ if (!existsSync82(mdPath))
82489
82823
  return null;
82490
82824
  let content;
82491
82825
  try {
82492
- content = readFileSync66(mdPath, "utf-8");
82826
+ content = readFileSync67(mdPath, "utf-8");
82493
82827
  } catch {
82494
82828
  return null;
82495
82829
  }
@@ -82507,7 +82841,7 @@ function readSkillFrontmatter(skillDir) {
82507
82841
  const fmText = rest.slice(0, endIdx);
82508
82842
  let parsed;
82509
82843
  try {
82510
- parsed = import_yaml21.parse(fmText);
82844
+ parsed = import_yaml23.parse(fmText);
82511
82845
  } catch (e) {
82512
82846
  return { error: `yaml parse: ${e.message}` };
82513
82847
  }
@@ -82517,7 +82851,7 @@ function readSkillFrontmatter(skillDir) {
82517
82851
  return { fm: parsed };
82518
82852
  }
82519
82853
  function statSkillMd(skillDir) {
82520
- const mdPath = join78(skillDir, "SKILL.md");
82854
+ const mdPath = join80(skillDir, "SKILL.md");
82521
82855
  try {
82522
82856
  const st = statSync31(mdPath);
82523
82857
  return { size: st.size, mtime: st.mtime.toISOString() };
@@ -82528,8 +82862,8 @@ function statSkillMd(skillDir) {
82528
82862
  function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
82529
82863
  if (!AGENT_NAME_RE3.test(agent))
82530
82864
  return [];
82531
- const skillsDir = join78(agentsRoot, agent, ".claude/skills");
82532
- if (!existsSync81(skillsDir))
82865
+ const skillsDir = join80(agentsRoot, agent, ".claude/skills");
82866
+ if (!existsSync82(skillsDir))
82533
82867
  return [];
82534
82868
  const out = [];
82535
82869
  let entries;
@@ -82541,7 +82875,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
82541
82875
  for (const ent of entries) {
82542
82876
  if (!ent.startsWith(PERSONAL_PREFIX2))
82543
82877
  continue;
82544
- const dirPath = join78(skillsDir, ent);
82878
+ const dirPath = join80(skillsDir, ent);
82545
82879
  try {
82546
82880
  if (!statSync31(dirPath).isDirectory())
82547
82881
  continue;
@@ -82567,7 +82901,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
82567
82901
  return out;
82568
82902
  }
82569
82903
  function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
82570
- if (!existsSync81(sharedRoot))
82904
+ if (!existsSync82(sharedRoot))
82571
82905
  return [];
82572
82906
  const out = [];
82573
82907
  let entries;
@@ -82581,7 +82915,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
82581
82915
  continue;
82582
82916
  if (ent.startsWith("."))
82583
82917
  continue;
82584
- const dirPath = join78(sharedRoot, ent);
82918
+ const dirPath = join80(sharedRoot, ent);
82585
82919
  try {
82586
82920
  if (!statSync31(dirPath).isDirectory())
82587
82921
  continue;
@@ -82605,7 +82939,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
82605
82939
  return out;
82606
82940
  }
82607
82941
  function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
82608
- if (!existsSync81(bundledRoot))
82942
+ if (!existsSync82(bundledRoot))
82609
82943
  return [];
82610
82944
  const out = [];
82611
82945
  let entries;
@@ -82617,7 +82951,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
82617
82951
  for (const ent of entries) {
82618
82952
  if (ent.startsWith("."))
82619
82953
  continue;
82620
- const dirPath = join78(bundledRoot, ent);
82954
+ const dirPath = join80(bundledRoot, ent);
82621
82955
  try {
82622
82956
  if (!statSync31(dirPath).isDirectory())
82623
82957
  continue;
@@ -82761,10 +83095,10 @@ function registerHostdMcpCommand(program3) {
82761
83095
  // src/cli/hostd.ts
82762
83096
  init_source();
82763
83097
  init_helpers();
82764
- import { existsSync as existsSync83, mkdirSync as mkdirSync46, readdirSync as readdirSync33, readFileSync as readFileSync68, writeFileSync as writeFileSync39, statSync as statSync32, copyFileSync as copyFileSync12 } from "node:fs";
82765
- import { homedir as homedir46 } from "node:os";
82766
- import { join as join79 } from "node:path";
82767
- import { spawnSync as spawnSync13 } from "node:child_process";
83098
+ import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync69, writeFileSync as writeFileSync40, statSync as statSync32, copyFileSync as copyFileSync12 } from "node:fs";
83099
+ import { homedir as homedir47 } from "node:os";
83100
+ import { join as join81 } from "node:path";
83101
+ import { spawnSync as spawnSync14 } from "node:child_process";
82768
83102
  init_audit_reader();
82769
83103
  var DEFAULT_IMAGE_TAG = "latest";
82770
83104
  var HOSTD_COMPOSE_PROJECT = "switchroom-hostd";
@@ -82854,14 +83188,14 @@ networks:
82854
83188
  `;
82855
83189
  }
82856
83190
  function hostdDir() {
82857
- return join79(homedir46(), ".switchroom", "hostd");
83191
+ return join81(homedir47(), ".switchroom", "hostd");
82858
83192
  }
82859
83193
  function hostdComposePath() {
82860
- return join79(hostdDir(), "docker-compose.yml");
83194
+ return join81(hostdDir(), "docker-compose.yml");
82861
83195
  }
82862
83196
  function backupExistingCompose() {
82863
83197
  const p = hostdComposePath();
82864
- if (!existsSync83(p))
83198
+ if (!existsSync84(p))
82865
83199
  return null;
82866
83200
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
82867
83201
  const bak = `${p}.bak-${ts}`;
@@ -82869,7 +83203,7 @@ function backupExistingCompose() {
82869
83203
  return bak;
82870
83204
  }
82871
83205
  function runDocker(args) {
82872
- const r = spawnSync13("docker", args, { encoding: "utf8" });
83206
+ const r = spawnSync14("docker", args, { encoding: "utf8" });
82873
83207
  return {
82874
83208
  ok: r.status === 0,
82875
83209
  stdout: r.stdout ?? "",
@@ -82894,9 +83228,9 @@ async function doInstall(opts, program3) {
82894
83228
  }
82895
83229
  const dir = hostdDir();
82896
83230
  const composePath = hostdComposePath();
82897
- mkdirSync46(dir, { recursive: true });
83231
+ mkdirSync47(dir, { recursive: true });
82898
83232
  const yaml = renderHostdComposeFile({
82899
- hostHome: homedir46(),
83233
+ hostHome: homedir47(),
82900
83234
  imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
82901
83235
  operatorUid: resolveOperatorUid()
82902
83236
  });
@@ -82909,7 +83243,7 @@ async function doInstall(opts, program3) {
82909
83243
  const bak = backupExistingCompose();
82910
83244
  if (bak)
82911
83245
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
82912
- writeFileSync39(composePath, yaml, "utf8");
83246
+ writeFileSync40(composePath, yaml, "utf8");
82913
83247
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
82914
83248
  const adminAgents = Object.entries(cfg.agents ?? {}).filter(([, a]) => a?.admin === true).map(([name]) => name);
82915
83249
  console.log(source_default.dim(` agents served (one socket each): ${allAgents.length === 0 ? "(none)" : allAgents.join(", ")}`));
@@ -82940,7 +83274,7 @@ function doStatus() {
82940
83274
  const composeYml = hostdComposePath();
82941
83275
  console.log(source_default.bold("switchroom-hostd"));
82942
83276
  console.log("");
82943
- if (!existsSync83(composeYml)) {
83277
+ if (!existsSync84(composeYml)) {
82944
83278
  console.log(source_default.yellow(" compose: not installed"));
82945
83279
  console.log(source_default.dim(" run `switchroom hostd install` to set up."));
82946
83280
  return;
@@ -82961,14 +83295,14 @@ function doStatus() {
82961
83295
  } else {
82962
83296
  console.log(source_default.green(` container: ${ps.stdout.trim()}`));
82963
83297
  }
82964
- if (existsSync83(dir)) {
83298
+ if (existsSync84(dir)) {
82965
83299
  const entries = [];
82966
83300
  try {
82967
83301
  for (const name of readdirSync33(dir)) {
82968
83302
  if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
82969
83303
  continue;
82970
- const sockPath = join79(dir, name, "sock");
82971
- if (existsSync83(sockPath)) {
83304
+ const sockPath = join81(dir, name, "sock");
83305
+ if (existsSync84(sockPath)) {
82972
83306
  const st = statSync32(sockPath);
82973
83307
  if ((st.mode & 61440) === 49152) {
82974
83308
  entries.push(`${name} \u2192 ${sockPath}`);
@@ -82987,7 +83321,7 @@ function doStatus() {
82987
83321
  }
82988
83322
  function doUninstall() {
82989
83323
  const composeYml = hostdComposePath();
82990
- if (!existsSync83(composeYml)) {
83324
+ if (!existsSync84(composeYml)) {
82991
83325
  console.log(source_default.yellow(" No hostd install detected (no compose file at this path)."));
82992
83326
  return;
82993
83327
  }
@@ -83011,12 +83345,12 @@ function registerHostdCommand(program3) {
83011
83345
  hostd.command("uninstall").description("Stop the hostd container. Leaves the compose file in place for re-install.").action(() => doUninstall());
83012
83346
  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) => {
83013
83347
  const logPath = opts.path ?? defaultAuditLogPath2();
83014
- if (!existsSync83(logPath)) {
83348
+ if (!existsSync84(logPath)) {
83015
83349
  console.error(source_default.yellow(`Audit log not found at ${logPath}.`) + source_default.gray(`
83016
83350
  The log is created when hostd handles its first privileged-verb request.`));
83017
83351
  return;
83018
83352
  }
83019
- const raw = readFileSync68(logPath, "utf-8");
83353
+ const raw = readFileSync69(logPath, "utf-8");
83020
83354
  const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
83021
83355
  const filters = {
83022
83356
  agent: opts.agent,
@@ -83058,10 +83392,10 @@ The log is created when hostd handles its first privileged-verb request.`));
83058
83392
  // src/cli/webd.ts
83059
83393
  init_source();
83060
83394
  init_helpers();
83061
- import { existsSync as existsSync84, mkdirSync as mkdirSync47, writeFileSync as writeFileSync40, copyFileSync as copyFileSync13 } from "node:fs";
83062
- import { homedir as homedir47 } from "node:os";
83063
- import { join as join80 } from "node:path";
83064
- import { spawnSync as spawnSync14 } from "node:child_process";
83395
+ import { existsSync as existsSync85, mkdirSync as mkdirSync48, writeFileSync as writeFileSync41, copyFileSync as copyFileSync13 } from "node:fs";
83396
+ import { homedir as homedir48 } from "node:os";
83397
+ import { join as join82 } from "node:path";
83398
+ import { spawnSync as spawnSync15 } from "node:child_process";
83065
83399
  var DEFAULT_IMAGE_TAG2 = "latest";
83066
83400
  var WEB_COMPOSE_PROJECT = "switchroom-web";
83067
83401
  function renderWebComposeFile(opts) {
@@ -83141,14 +83475,14 @@ services:
83141
83475
  `;
83142
83476
  }
83143
83477
  function webdDir() {
83144
- return join80(homedir47(), ".switchroom", "web");
83478
+ return join82(homedir48(), ".switchroom", "web");
83145
83479
  }
83146
83480
  function webdComposePath() {
83147
- return join80(webdDir(), "docker-compose.yml");
83481
+ return join82(webdDir(), "docker-compose.yml");
83148
83482
  }
83149
83483
  function backupExistingCompose2() {
83150
83484
  const p = webdComposePath();
83151
- if (!existsSync84(p))
83485
+ if (!existsSync85(p))
83152
83486
  return null;
83153
83487
  const ts = new Date().toISOString().replace(/[:.]/g, "-");
83154
83488
  const bak = `${p}.bak-${ts}`;
@@ -83156,7 +83490,7 @@ function backupExistingCompose2() {
83156
83490
  return bak;
83157
83491
  }
83158
83492
  function runDocker2(args) {
83159
- const r = spawnSync14("docker", args, { encoding: "utf8" });
83493
+ const r = spawnSync15("docker", args, { encoding: "utf8" });
83160
83494
  return {
83161
83495
  ok: r.status === 0,
83162
83496
  stdout: r.stdout ?? "",
@@ -83173,9 +83507,9 @@ async function doInstall2(opts) {
83173
83507
  }
83174
83508
  const dir = webdDir();
83175
83509
  const composePath = webdComposePath();
83176
- mkdirSync47(dir, { recursive: true });
83510
+ mkdirSync48(dir, { recursive: true });
83177
83511
  const yaml = renderWebComposeFile({
83178
- hostHome: homedir47(),
83512
+ hostHome: homedir48(),
83179
83513
  imageTag: opts.tag ?? DEFAULT_IMAGE_TAG2,
83180
83514
  operatorUid
83181
83515
  });
@@ -83188,7 +83522,7 @@ async function doInstall2(opts) {
83188
83522
  const bak = backupExistingCompose2();
83189
83523
  if (bak)
83190
83524
  console.log(source_default.dim(` Backed up existing compose to ${bak}`));
83191
- writeFileSync40(composePath, yaml, "utf8");
83525
+ writeFileSync41(composePath, yaml, "utf8");
83192
83526
  console.log(source_default.green(` \u2713 Wrote ${composePath}`));
83193
83527
  console.log(source_default.dim(` running as uid ${operatorUid} (operator), network_mode: host`));
83194
83528
  console.log(source_default.dim(` Pulling ghcr.io/switchroom/switchroom-web:${opts.tag ?? DEFAULT_IMAGE_TAG2}\u2026`));
@@ -83220,7 +83554,7 @@ function doStatus2() {
83220
83554
  const composeYml = webdComposePath();
83221
83555
  console.log(source_default.bold("switchroom-web"));
83222
83556
  console.log("");
83223
- if (!existsSync84(composeYml)) {
83557
+ if (!existsSync85(composeYml)) {
83224
83558
  console.log(source_default.yellow(" compose: not installed"));
83225
83559
  console.log(source_default.dim(" run `switchroom webd install` to set up."));
83226
83560
  return;
@@ -83244,7 +83578,7 @@ function doStatus2() {
83244
83578
  }
83245
83579
  function doUninstall2() {
83246
83580
  const composeYml = webdComposePath();
83247
- if (!existsSync84(composeYml)) {
83581
+ if (!existsSync85(composeYml)) {
83248
83582
  console.log(source_default.yellow(" No web-service install detected (no compose file at this path)."));
83249
83583
  return;
83250
83584
  }