claudemesh-cli 1.28.0 → 1.29.0

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.
@@ -103,7 +103,7 @@ __export(exports_urls, {
103
103
  VERSION: () => VERSION,
104
104
  URLS: () => URLS
105
105
  });
106
- var URLS, VERSION = "1.28.0", env;
106
+ var URLS, VERSION = "1.29.0", env;
107
107
  var init_urls = __esm(() => {
108
108
  URLS = {
109
109
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -3668,7 +3668,50 @@ var init_local_token = __esm(() => {
3668
3668
  init_paths2();
3669
3669
  });
3670
3670
 
3671
+ // src/services/session/token.ts
3672
+ var exports_token = {};
3673
+ __export(exports_token, {
3674
+ readSessionTokenFromEnv: () => readSessionTokenFromEnv,
3675
+ mintSessionToken: () => mintSessionToken,
3676
+ TOKEN_FILE_ENV: () => TOKEN_FILE_ENV
3677
+ });
3678
+ import { randomBytes as randomBytes5 } from "node:crypto";
3679
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
3680
+ function mintSessionToken(dir, fileName = "session-token") {
3681
+ const token = randomBytes5(32).toString("hex");
3682
+ const filePath = `${dir}/${fileName}`;
3683
+ writeFileSync5(filePath, token, { mode: 384 });
3684
+ return { token, filePath };
3685
+ }
3686
+ function readSessionTokenFromEnv(env2 = process.env) {
3687
+ const direct = env2.CLAUDEMESH_IPC_TOKEN;
3688
+ if (direct && /^[0-9a-f]{64}$/i.test(direct))
3689
+ return direct.toLowerCase();
3690
+ const path = env2[ENV_TOKEN_FILE];
3691
+ if (!path)
3692
+ return null;
3693
+ try {
3694
+ if (!existsSync5(path))
3695
+ return null;
3696
+ const raw = readFileSync5(path, "utf8").trim();
3697
+ if (/^[0-9a-f]{64}$/i.test(raw))
3698
+ return raw.toLowerCase();
3699
+ return null;
3700
+ } catch {
3701
+ return null;
3702
+ }
3703
+ }
3704
+ var ENV_TOKEN_FILE = "CLAUDEMESH_IPC_TOKEN_FILE", TOKEN_FILE_ENV;
3705
+ var init_token = __esm(() => {
3706
+ TOKEN_FILE_ENV = ENV_TOKEN_FILE;
3707
+ });
3708
+
3671
3709
  // src/daemon/ipc/client.ts
3710
+ var exports_client = {};
3711
+ __export(exports_client, {
3712
+ ipc: () => ipc,
3713
+ IpcError: () => IpcError
3714
+ });
3672
3715
  import { request as httpRequest } from "node:http";
3673
3716
  async function ipc(opts) {
3674
3717
  const useTcp = !!opts.preferTcp;
@@ -3688,6 +3731,11 @@ async function ipc(opts) {
3688
3731
  throw new IpcError(0, null, "daemon local token not found; is the daemon running?");
3689
3732
  headers.authorization = `Bearer ${tok}`;
3690
3733
  }
3734
+ if (!useTcp) {
3735
+ const sessionTok = readSessionTokenFromEnv();
3736
+ if (sessionTok)
3737
+ headers.authorization = `ClaudeMesh-Session ${sessionTok}`;
3738
+ }
3691
3739
  return new Promise((resolve, reject) => {
3692
3740
  const req = httpRequest(useTcp ? { host: DAEMON_TCP_HOST, port: DAEMON_TCP_DEFAULT_PORT, path: opts.path, method: opts.method ?? "GET", headers } : { socketPath: DAEMON_PATHS.SOCK_FILE, path: opts.path, method: opts.method ?? "GET", headers }, (res) => {
3693
3741
  const chunks = [];
@@ -3712,6 +3760,7 @@ var IpcError;
3712
3760
  var init_client3 = __esm(() => {
3713
3761
  init_paths2();
3714
3762
  init_local_token();
3763
+ init_token();
3715
3764
  IpcError = class IpcError extends Error {
3716
3765
  status;
3717
3766
  payload;
@@ -3729,7 +3778,7 @@ __export(exports_lifecycle, {
3729
3778
  ensureDaemonReady: () => ensureDaemonReady,
3730
3779
  _resetDaemonReadyCache: () => _resetDaemonReadyCache
3731
3780
  });
3732
- import { existsSync as existsSync5, readFileSync as readFileSync5, statSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
3781
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
3733
3782
  import { join as join4 } from "node:path";
3734
3783
  async function ensureDaemonReady(opts = {}) {
3735
3784
  if (lastResultThisProcess && (lastResultThisProcess.state === "up" || lastResultThisProcess.state === "started")) {
@@ -3770,7 +3819,7 @@ async function runEnsureDaemon(opts) {
3770
3819
  return { state: "spawn-failed", durationMs: Date.now() - t0, reason: spawnRes.reason };
3771
3820
  }
3772
3821
  async function probeDaemon() {
3773
- if (!existsSync5(DAEMON_PATHS.SOCK_FILE))
3822
+ if (!existsSync6(DAEMON_PATHS.SOCK_FILE))
3774
3823
  return "absent";
3775
3824
  try {
3776
3825
  const res = await ipc({ path: "/v1/version", timeoutMs: PROBE_TIMEOUT_MS });
@@ -3803,7 +3852,7 @@ function recentSpawnFailureFresh() {
3803
3852
  }
3804
3853
  function markSpawnFailure() {
3805
3854
  try {
3806
- writeFileSync5(SPAWN_FAIL_FILE(), String(Date.now()), { mode: 384 });
3855
+ writeFileSync6(SPAWN_FAIL_FILE(), String(Date.now()), { mode: 384 });
3807
3856
  } catch {}
3808
3857
  }
3809
3858
  function clearSpawnFailure() {
@@ -3840,9 +3889,9 @@ async function spawnDaemon(opts) {
3840
3889
  }
3841
3890
  async function acquireOrShareLock(_opts) {
3842
3891
  const lockPath = SPAWN_LOCK_FILE();
3843
- if (existsSync5(lockPath)) {
3892
+ if (existsSync6(lockPath)) {
3844
3893
  try {
3845
- const pidStr = readFileSync5(lockPath, "utf8").trim();
3894
+ const pidStr = readFileSync6(lockPath, "utf8").trim();
3846
3895
  const pid = Number.parseInt(pidStr, 10);
3847
3896
  if (Number.isFinite(pid) && pid > 0) {
3848
3897
  try {
@@ -3853,7 +3902,7 @@ async function acquireOrShareLock(_opts) {
3853
3902
  } catch {}
3854
3903
  }
3855
3904
  try {
3856
- writeFileSync5(lockPath, String(process.pid), { mode: 384 });
3905
+ writeFileSync6(lockPath, String(process.pid), { mode: 384 });
3857
3906
  } catch {}
3858
3907
  return "acquired";
3859
3908
  }
@@ -3865,7 +3914,7 @@ function releaseLock() {
3865
3914
  async function pollForSocket(budgetMs) {
3866
3915
  const start = Date.now();
3867
3916
  while (Date.now() - start < budgetMs) {
3868
- if (existsSync5(DAEMON_PATHS.SOCK_FILE)) {
3917
+ if (existsSync6(DAEMON_PATHS.SOCK_FILE)) {
3869
3918
  const probe = await probeDaemon();
3870
3919
  if (probe === "up")
3871
3920
  return { ok: true };
@@ -3899,7 +3948,7 @@ __export(exports_launch, {
3899
3948
  });
3900
3949
  import { spawnSync as spawnSync2 } from "node:child_process";
3901
3950
  import { randomUUID } from "node:crypto";
3902
- import { mkdtempSync, writeFileSync as writeFileSync6, rmSync, readdirSync, statSync as statSync2, existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
3951
+ import { mkdtempSync, writeFileSync as writeFileSync7, rmSync, readdirSync, statSync as statSync2, existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
3903
3952
  import { tmpdir, hostname as hostname2, homedir as homedir3 } from "node:os";
3904
3953
  import { join as join5 } from "node:path";
3905
3954
  import { createInterface as createInterface4 } from "node:readline";
@@ -4248,8 +4297,8 @@ async function runLaunch(flags, rawArgs) {
4248
4297
  await ensureDaemonRunning(mesh.slug, args.quiet);
4249
4298
  try {
4250
4299
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4251
- if (existsSync6(claudeConfigPath)) {
4252
- const claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4300
+ if (existsSync7(claudeConfigPath)) {
4301
+ const claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4253
4302
  const mcpServers = claudeConfig.mcpServers ?? {};
4254
4303
  let cleaned = 0;
4255
4304
  for (const key of Object.keys(mcpServers)) {
@@ -4267,7 +4316,7 @@ async function runLaunch(flags, rawArgs) {
4267
4316
  }
4268
4317
  if (cleaned > 0) {
4269
4318
  claudeConfig.mcpServers = mcpServers;
4270
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4319
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4271
4320
  `, "utf-8");
4272
4321
  }
4273
4322
  }
@@ -4293,8 +4342,37 @@ async function runLaunch(flags, rawArgs) {
4293
4342
  ...parsedGroups.length > 0 ? { groups: parsedGroups } : {},
4294
4343
  messageMode
4295
4344
  };
4296
- writeFileSync6(join5(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
4345
+ writeFileSync7(join5(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
4297
4346
  `, "utf-8");
4347
+ let sessionTokenFilePath = null;
4348
+ let sessionTokenForCleanup = null;
4349
+ try {
4350
+ const { mintSessionToken: mintSessionToken2, TOKEN_FILE_ENV: TOKEN_FILE_ENV2 } = await Promise.resolve().then(() => (init_token(), exports_token));
4351
+ const minted = mintSessionToken2(tmpDir);
4352
+ sessionTokenFilePath = minted.filePath;
4353
+ sessionTokenForCleanup = minted.token;
4354
+ const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4355
+ const sessionIdForRegister = claudeSessionId ?? randomUUID();
4356
+ await ipc2({
4357
+ method: "POST",
4358
+ path: "/v1/sessions/register",
4359
+ timeoutMs: 3000,
4360
+ body: {
4361
+ token: minted.token,
4362
+ session_id: sessionIdForRegister,
4363
+ mesh: mesh.slug,
4364
+ display_name: displayName,
4365
+ pid: process.pid,
4366
+ cwd: process.cwd(),
4367
+ ...role ? { role } : {},
4368
+ ...parsedGroups.length > 0 ? { groups: parsedGroups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`) } : {}
4369
+ }
4370
+ }).catch(() => null);
4371
+ process._claudemeshTokenEnv = {
4372
+ name: TOKEN_FILE_ENV2,
4373
+ value: minted.filePath
4374
+ };
4375
+ } catch {}
4298
4376
  if (!args.quiet) {
4299
4377
  printBanner(displayName, mesh.slug, role, parsedGroups, messageMode);
4300
4378
  }
@@ -4303,7 +4381,7 @@ async function runLaunch(flags, rawArgs) {
4303
4381
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4304
4382
  let claudeConfig = {};
4305
4383
  try {
4306
- claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4384
+ claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4307
4385
  } catch {
4308
4386
  claudeConfig = {};
4309
4387
  }
@@ -4330,7 +4408,7 @@ async function runLaunch(flags, rawArgs) {
4330
4408
  meshMcpEntries.push({ key: entryKey, entry });
4331
4409
  }
4332
4410
  claudeConfig.mcpServers = mcpServers;
4333
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4411
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4334
4412
  `, "utf-8");
4335
4413
  if (!args.quiet && meshMcpEntries.length > 0) {
4336
4414
  console.log(` ${meshMcpEntries.length} mesh service(s) registered as native MCPs:`);
@@ -4372,7 +4450,7 @@ async function runLaunch(flags, rawArgs) {
4372
4450
  join5(homedir3(), ".claude", "bin", "claude")
4373
4451
  ];
4374
4452
  for (const c of candidates) {
4375
- if (existsSync6(c)) {
4453
+ if (existsSync7(c)) {
4376
4454
  claudeBin = c;
4377
4455
  break;
4378
4456
  }
@@ -4382,13 +4460,13 @@ async function runLaunch(flags, rawArgs) {
4382
4460
  if (meshMcpEntries.length > 0) {
4383
4461
  try {
4384
4462
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4385
- const claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4463
+ const claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4386
4464
  const mcpServers = claudeConfig.mcpServers ?? {};
4387
4465
  for (const { key } of meshMcpEntries) {
4388
4466
  delete mcpServers[key];
4389
4467
  }
4390
4468
  claudeConfig.mcpServers = mcpServers;
4391
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4469
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4392
4470
  `, "utf-8");
4393
4471
  } catch {}
4394
4472
  }
@@ -4418,6 +4496,7 @@ async function runLaunch(flags, rawArgs) {
4418
4496
  CLAUDEMESH_CONFIG_DIR: tmpDir,
4419
4497
  CLAUDEMESH_DISPLAY_NAME: displayName,
4420
4498
  ...claudeSessionId ? { CLAUDEMESH_SESSION_ID: claudeSessionId } : {},
4499
+ ...sessionTokenFilePath ? { CLAUDEMESH_IPC_TOKEN_FILE: sessionTokenFilePath } : {},
4421
4500
  MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
4422
4501
  MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
4423
4502
  ...role ? { CLAUDEMESH_ROLE: role } : {}
@@ -5085,7 +5164,7 @@ __export(exports_join, {
5085
5164
  runJoin: () => runJoin
5086
5165
  });
5087
5166
  import sodium3 from "libsodium-wrappers";
5088
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync4 } from "node:fs";
5167
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync4 } from "node:fs";
5089
5168
  import { join as join6, dirname as dirname3 } from "node:path";
5090
5169
  import { homedir as homedir4, hostname as hostname3 } from "node:os";
5091
5170
  function deriveAppBaseUrl() {
@@ -5210,7 +5289,7 @@ async function runJoin(args) {
5210
5289
  const inviteFile = join6(configDir, `invite-${payload.mesh_slug}.txt`);
5211
5290
  try {
5212
5291
  mkdirSync4(dirname3(inviteFile), { recursive: true });
5213
- writeFileSync7(inviteFile, link, "utf-8");
5292
+ writeFileSync8(inviteFile, link, "utf-8");
5214
5293
  } catch {}
5215
5294
  console.log("");
5216
5295
  console.log(`✓ Joined "${payload.mesh_slug}" as ${displayName}${enroll.alreadyMember ? " (already a member — re-enrolled with same pubkey)" : ""}`);
@@ -7660,6 +7739,45 @@ var init_daemon_route = __esm(() => {
7660
7739
  init_warnings();
7661
7740
  });
7662
7741
 
7742
+ // src/services/session/resolve.ts
7743
+ var exports_resolve = {};
7744
+ __export(exports_resolve, {
7745
+ getSessionInfo: () => getSessionInfo,
7746
+ _resetSessionCache: () => _resetSessionCache
7747
+ });
7748
+ async function getSessionInfo() {
7749
+ if (cached !== undefined)
7750
+ return cached;
7751
+ const tok = readSessionTokenFromEnv();
7752
+ if (!tok) {
7753
+ cached = null;
7754
+ return null;
7755
+ }
7756
+ try {
7757
+ const res = await ipc({
7758
+ path: "/v1/sessions/me",
7759
+ timeoutMs: 1500
7760
+ });
7761
+ if (res.status !== 200 || !res.body.session) {
7762
+ cached = null;
7763
+ return null;
7764
+ }
7765
+ cached = res.body.session;
7766
+ return cached;
7767
+ } catch {
7768
+ cached = null;
7769
+ return null;
7770
+ }
7771
+ }
7772
+ function _resetSessionCache() {
7773
+ cached = undefined;
7774
+ }
7775
+ var cached = undefined;
7776
+ var init_resolve = __esm(() => {
7777
+ init_client3();
7778
+ init_token();
7779
+ });
7780
+
7663
7781
  // src/commands/peers.ts
7664
7782
  var exports_peers = {};
7665
7783
  __export(exports_peers, {
@@ -7699,7 +7817,14 @@ function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
7699
7817
  }
7700
7818
  async function runPeers(flags) {
7701
7819
  const config = readConfig();
7702
- const slugs = flags.mesh ? [flags.mesh] : config.meshes.map((m) => m.slug);
7820
+ let slugs;
7821
+ if (flags.mesh) {
7822
+ slugs = [flags.mesh];
7823
+ } else {
7824
+ const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
7825
+ const sess = await getSessionInfo2();
7826
+ slugs = sess ? [sess.mesh] : config.meshes.map((m) => m.slug);
7827
+ }
7703
7828
  if (slugs.length === 0) {
7704
7829
  render.err("No meshes joined.");
7705
7830
  render.hint("claudemesh <invite-url> # join + launch");
@@ -8669,12 +8794,12 @@ var init_whoami = __esm(() => {
8669
8794
  });
8670
8795
 
8671
8796
  // src/daemon/lock.ts
8672
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "node:fs";
8797
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync8, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
8673
8798
  import { dirname as dirname4 } from "node:path";
8674
8799
  function acquireSingletonLock() {
8675
8800
  mkdirSync5(dirname4(DAEMON_PATHS.PID_FILE), { recursive: true, mode: 448 });
8676
- if (existsSync7(DAEMON_PATHS.PID_FILE)) {
8677
- const raw = readFileSync7(DAEMON_PATHS.PID_FILE, "utf8").trim();
8801
+ if (existsSync8(DAEMON_PATHS.PID_FILE)) {
8802
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8678
8803
  const oldPid = Number.parseInt(raw, 10);
8679
8804
  if (Number.isFinite(oldPid) && oldPid > 0 && isProcessAlive(oldPid)) {
8680
8805
  return { result: "already-running", pid: oldPid };
@@ -8682,22 +8807,22 @@ function acquireSingletonLock() {
8682
8807
  try {
8683
8808
  unlinkSync3(DAEMON_PATHS.PID_FILE);
8684
8809
  } catch {}
8685
- writeFileSync8(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8810
+ writeFileSync9(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8686
8811
  return { result: "stale", pid: process.pid };
8687
8812
  }
8688
- writeFileSync8(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8813
+ writeFileSync9(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8689
8814
  return { result: "acquired", pid: process.pid };
8690
8815
  }
8691
8816
  function releaseSingletonLock() {
8692
8817
  try {
8693
- const raw = readFileSync7(DAEMON_PATHS.PID_FILE, "utf8").trim();
8818
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8694
8819
  if (Number.parseInt(raw, 10) === process.pid)
8695
8820
  unlinkSync3(DAEMON_PATHS.PID_FILE);
8696
8821
  } catch {}
8697
8822
  }
8698
8823
  function readRunningPid() {
8699
8824
  try {
8700
- const raw = readFileSync7(DAEMON_PATHS.PID_FILE, "utf8").trim();
8825
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8701
8826
  const pid = Number.parseInt(raw, 10);
8702
8827
  if (Number.isFinite(pid) && pid > 0 && isProcessAlive(pid))
8703
8828
  return pid;
@@ -8843,17 +8968,17 @@ function requeueDeadOrPending(db, args) {
8843
8968
 
8844
8969
  // src/daemon/db/sqlite.ts
8845
8970
  async function loadSqlite() {
8846
- if (cached)
8847
- return cached;
8971
+ if (cached2)
8972
+ return cached2;
8848
8973
  try {
8849
8974
  const mod = await import("node:sqlite");
8850
- cached = mod.DatabaseSync;
8851
- return cached;
8975
+ cached2 = mod.DatabaseSync;
8976
+ return cached2;
8852
8977
  } catch (nodeErr) {
8853
8978
  try {
8854
8979
  const bunMod = await import("bun:sqlite");
8855
- cached = bunMod.Database;
8856
- return cached;
8980
+ cached2 = bunMod.Database;
8981
+ return cached2;
8857
8982
  } catch {
8858
8983
  const msg = `claudemesh daemon requires Node.js 22.5+ for the embedded SQLite store (node:sqlite), or Bun (bun:sqlite) for dev. Current: ${process.version}. Original error: ${String(nodeErr)}`;
8859
8984
  throw new Error(msg);
@@ -8884,7 +9009,7 @@ function inImmediateTx(db, fn) {
8884
9009
  throw err;
8885
9010
  }
8886
9011
  }
8887
- var cached = null;
9012
+ var cached2 = null;
8888
9013
 
8889
9014
  // src/daemon/fingerprint.ts
8890
9015
  import { createHash } from "node:crypto";
@@ -9127,9 +9252,70 @@ function bindSseStream(res, bus) {
9127
9252
  return cleanup;
9128
9253
  }
9129
9254
 
9255
+ // src/daemon/session-registry.ts
9256
+ function startReaper() {
9257
+ if (reaperHandle)
9258
+ return;
9259
+ reaperHandle = setInterval(reapDead, REAPER_INTERVAL_MS).unref?.() ?? reaperHandle;
9260
+ }
9261
+ function registerSession(info) {
9262
+ const priorToken = bySessionId.get(info.sessionId);
9263
+ if (priorToken && priorToken !== info.token)
9264
+ byToken.delete(priorToken);
9265
+ const stored = { ...info, registeredAt: Date.now() };
9266
+ byToken.set(info.token, stored);
9267
+ bySessionId.set(info.sessionId, info.token);
9268
+ return stored;
9269
+ }
9270
+ function deregisterByToken(token) {
9271
+ const entry = byToken.get(token);
9272
+ if (!entry)
9273
+ return false;
9274
+ byToken.delete(token);
9275
+ if (bySessionId.get(entry.sessionId) === token)
9276
+ bySessionId.delete(entry.sessionId);
9277
+ return true;
9278
+ }
9279
+ function resolveToken(token) {
9280
+ const entry = byToken.get(token);
9281
+ if (!entry)
9282
+ return null;
9283
+ if (Date.now() - entry.registeredAt > TTL_MS) {
9284
+ deregisterByToken(token);
9285
+ return null;
9286
+ }
9287
+ return entry;
9288
+ }
9289
+ function listSessions() {
9290
+ return [...byToken.values()];
9291
+ }
9292
+ function reapDead() {
9293
+ const dead = [];
9294
+ for (const [token, info] of byToken.entries()) {
9295
+ if (Date.now() - info.registeredAt > TTL_MS) {
9296
+ dead.push(token);
9297
+ continue;
9298
+ }
9299
+ try {
9300
+ process.kill(info.pid, 0);
9301
+ } catch {
9302
+ dead.push(token);
9303
+ }
9304
+ }
9305
+ for (const t of dead)
9306
+ deregisterByToken(t);
9307
+ }
9308
+ var TTL_MS, REAPER_INTERVAL_MS, byToken, bySessionId, reaperHandle = null;
9309
+ var init_session_registry = __esm(() => {
9310
+ TTL_MS = 24 * 60 * 60 * 1000;
9311
+ REAPER_INTERVAL_MS = 30 * 1000;
9312
+ byToken = new Map;
9313
+ bySessionId = new Map;
9314
+ });
9315
+
9130
9316
  // src/daemon/ipc/server.ts
9131
9317
  import { createServer as createServer2 } from "node:http";
9132
- import { chmodSync as chmodSync3, existsSync as existsSync8, unlinkSync as unlinkSync4 } from "node:fs";
9318
+ import { chmodSync as chmodSync3, existsSync as existsSync9, unlinkSync as unlinkSync4 } from "node:fs";
9133
9319
  import { timingSafeEqual } from "node:crypto";
9134
9320
  import { randomUUID as randomUUID3 } from "node:crypto";
9135
9321
  function startIpcServer(opts) {
@@ -9145,7 +9331,7 @@ function startIpcServer(opts) {
9145
9331
  meshConfigs: opts.meshConfigs,
9146
9332
  onPendingInserted: opts.onPendingInserted
9147
9333
  });
9148
- if (existsSync8(DAEMON_PATHS.SOCK_FILE)) {
9334
+ if (existsSync9(DAEMON_PATHS.SOCK_FILE)) {
9149
9335
  try {
9150
9336
  unlinkSync4(DAEMON_PATHS.SOCK_FILE);
9151
9337
  } catch {}
@@ -9231,11 +9417,19 @@ function makeHandler(opts) {
9231
9417
  return;
9232
9418
  }
9233
9419
  }
9420
+ let session = null;
9421
+ {
9422
+ const authz = req.headers.authorization ?? "";
9423
+ const sm = /^ClaudeMesh-Session\s+([0-9a-f]{64})$/i.exec(authz.trim());
9424
+ if (sm && sm[1])
9425
+ session = resolveToken(sm[1].toLowerCase());
9426
+ }
9427
+ const meshFromCtx = (explicit) => explicit && explicit.trim() ? explicit : session?.mesh ?? null;
9234
9428
  if (req.method === "GET" && url.pathname === "/v1/version") {
9235
9429
  respond(res, 200, {
9236
9430
  daemon_version: VERSION,
9237
9431
  ipc_api: "v1",
9238
- ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills", "state", "memory"],
9432
+ ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills", "state", "memory", "sessions"],
9239
9433
  schema_version: 1
9240
9434
  });
9241
9435
  return;
@@ -9244,6 +9438,70 @@ function makeHandler(opts) {
9244
9438
  respond(res, 200, { ok: true, pid: process.pid });
9245
9439
  return;
9246
9440
  }
9441
+ if (req.method === "POST" && url.pathname === "/v1/sessions/register") {
9442
+ try {
9443
+ const body = await readJsonBody(req, 64 * 1024);
9444
+ if (!body) {
9445
+ respond(res, 400, { error: "missing body" });
9446
+ return;
9447
+ }
9448
+ const token = typeof body.token === "string" ? body.token : "";
9449
+ if (!/^[0-9a-f]{64}$/i.test(token)) {
9450
+ respond(res, 400, { error: "token must be 64 hex chars" });
9451
+ return;
9452
+ }
9453
+ const sessionId = typeof body.session_id === "string" ? body.session_id : "";
9454
+ const mesh = typeof body.mesh === "string" ? body.mesh : "";
9455
+ const displayName = typeof body.display_name === "string" ? body.display_name : "";
9456
+ const pid = typeof body.pid === "number" ? body.pid : 0;
9457
+ if (!sessionId || !mesh || !displayName || !pid) {
9458
+ respond(res, 400, { error: "session_id, mesh, display_name, pid all required" });
9459
+ return;
9460
+ }
9461
+ const cwd = typeof body.cwd === "string" ? body.cwd : undefined;
9462
+ const role = typeof body.role === "string" ? body.role : undefined;
9463
+ const groups = Array.isArray(body.groups) ? body.groups.filter((g) => typeof g === "string") : undefined;
9464
+ const stored = registerSession({
9465
+ token: token.toLowerCase(),
9466
+ sessionId,
9467
+ mesh,
9468
+ displayName,
9469
+ pid,
9470
+ cwd,
9471
+ role,
9472
+ groups
9473
+ });
9474
+ opts.log("info", "session_registered", { sessionId, mesh, pid });
9475
+ respond(res, 200, { ok: true, registered_at: stored.registeredAt });
9476
+ } catch (e) {
9477
+ respond(res, 400, { error: String(e) });
9478
+ }
9479
+ return;
9480
+ }
9481
+ if (req.method === "DELETE" && url.pathname.startsWith("/v1/sessions/")) {
9482
+ const tail = url.pathname.slice("/v1/sessions/".length);
9483
+ if (!/^[0-9a-f]{64}$/i.test(tail)) {
9484
+ respond(res, 400, { error: "invalid token" });
9485
+ return;
9486
+ }
9487
+ const ok = deregisterByToken(tail.toLowerCase());
9488
+ respond(res, ok ? 200 : 404, { ok, token_prefix: tail.slice(0, 8) });
9489
+ return;
9490
+ }
9491
+ if (req.method === "GET" && url.pathname === "/v1/sessions/me") {
9492
+ if (!session) {
9493
+ respond(res, 401, { error: "no session token" });
9494
+ return;
9495
+ }
9496
+ const { token, ...redacted } = session;
9497
+ respond(res, 200, { session: { ...redacted, token_prefix: token.slice(0, 8) } });
9498
+ return;
9499
+ }
9500
+ if (req.method === "GET" && url.pathname === "/v1/sessions") {
9501
+ const all = listSessions().map(({ token, ...rest }) => ({ ...rest, token_prefix: token.slice(0, 8) }));
9502
+ respond(res, 200, { sessions: all });
9503
+ return;
9504
+ }
9247
9505
  if (req.method === "GET" && url.pathname === "/v1/events") {
9248
9506
  if (!opts.bus) {
9249
9507
  respond(res, 503, { error: "event bus not initialised" });
@@ -9257,7 +9515,7 @@ function makeHandler(opts) {
9257
9515
  respond(res, 503, { error: "broker not initialised" });
9258
9516
  return;
9259
9517
  }
9260
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9518
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9261
9519
  try {
9262
9520
  const all = [];
9263
9521
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9282,7 +9540,7 @@ function makeHandler(opts) {
9282
9540
  respond(res, 503, { error: "broker not initialised" });
9283
9541
  return;
9284
9542
  }
9285
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9543
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9286
9544
  const key = url.searchParams.get("key");
9287
9545
  try {
9288
9546
  if (key) {
@@ -9323,7 +9581,7 @@ function makeHandler(opts) {
9323
9581
  respond(res, 400, { error: "missing 'key' (string)" });
9324
9582
  return;
9325
9583
  }
9326
- const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
9584
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : null);
9327
9585
  let chosen = requested;
9328
9586
  if (!chosen && opts.brokers.size === 1)
9329
9587
  chosen = opts.brokers.keys().next().value;
@@ -9349,7 +9607,7 @@ function makeHandler(opts) {
9349
9607
  return;
9350
9608
  }
9351
9609
  const query = url.searchParams.get("q") ?? "";
9352
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9610
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9353
9611
  try {
9354
9612
  const all = [];
9355
9613
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9376,7 +9634,7 @@ function makeHandler(opts) {
9376
9634
  respond(res, 400, { error: "missing 'content' (string)" });
9377
9635
  return;
9378
9636
  }
9379
- const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
9637
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : null);
9380
9638
  let chosen = requested;
9381
9639
  if (!chosen && opts.brokers.size === 1)
9382
9640
  chosen = opts.brokers.keys().next().value;
@@ -9434,7 +9692,7 @@ function makeHandler(opts) {
9434
9692
  return;
9435
9693
  }
9436
9694
  const query = url.searchParams.get("query") ?? undefined;
9437
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9695
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9438
9696
  try {
9439
9697
  const all = [];
9440
9698
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9464,7 +9722,7 @@ function makeHandler(opts) {
9464
9722
  respond(res, 400, { error: "missing skill name" });
9465
9723
  return;
9466
9724
  }
9467
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9725
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9468
9726
  try {
9469
9727
  for (const [slug, b] of opts.brokers.entries()) {
9470
9728
  if (filterMesh && filterMesh !== slug)
@@ -9492,7 +9750,7 @@ function makeHandler(opts) {
9492
9750
  respond(res, 400, { error: "expected JSON object" });
9493
9751
  return;
9494
9752
  }
9495
- const requested = (typeof body.mesh === "string" ? body.mesh : url.searchParams.get("mesh")) || null;
9753
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : url.searchParams.get("mesh"));
9496
9754
  const targets = requested ? [opts.brokers.get(requested)].filter(Boolean) : [...opts.brokers.values()];
9497
9755
  if (targets.length === 0) {
9498
9756
  respond(res, 404, { error: "mesh_not_attached", mesh: requested });
@@ -9776,16 +10034,16 @@ function parseSendRequest(body, idempotencyHeader) {
9776
10034
  }
9777
10035
  async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
9778
10036
  const { encryptDirect: encryptDirect2 } = await Promise.resolve().then(() => (init_box(), exports_box));
9779
- const { randomBytes: randomBytes5 } = await import("node:crypto");
10037
+ const { randomBytes: randomBytes6 } = await import("node:crypto");
9780
10038
  const to = req.to.trim();
9781
10039
  if (to.startsWith("#") && /^#[0-9a-z_-]{20,}$/i.test(to)) {
9782
10040
  const ciphertext = Buffer.from(req.message, "utf8").toString("base64");
9783
- const nonce = randomBytes5(24).toString("base64");
10041
+ const nonce = randomBytes6(24).toString("base64");
9784
10042
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
9785
10043
  }
9786
10044
  if (to.startsWith("@") || to === "*") {
9787
10045
  const ciphertext = Buffer.from(req.message, "utf8").toString("base64");
9788
- const nonce = randomBytes5(24).toString("base64");
10046
+ const nonce = randomBytes6(24).toString("base64");
9789
10047
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
9790
10048
  }
9791
10049
  if (/^[0-9a-f]{64}$/i.test(to)) {
@@ -9826,6 +10084,7 @@ function respond(res, status, body) {
9826
10084
  var init_server = __esm(() => {
9827
10085
  init_paths2();
9828
10086
  init_send2();
10087
+ init_session_registry();
9829
10088
  init_urls();
9830
10089
  });
9831
10090
 
@@ -10457,8 +10716,8 @@ function bufferToHex(b) {
10457
10716
  return s;
10458
10717
  }
10459
10718
  async function randomNonce2() {
10460
- const { randomBytes: randomBytes5 } = await import("node:crypto");
10461
- return randomBytes5(24).toString("base64");
10719
+ const { randomBytes: randomBytes6 } = await import("node:crypto");
10720
+ return randomBytes6(24).toString("base64");
10462
10721
  }
10463
10722
  function defaultLog2(level, msg, meta) {
10464
10723
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
@@ -10580,7 +10839,7 @@ __export(exports_identity, {
10580
10839
  checkFingerprint: () => checkFingerprint,
10581
10840
  acceptCurrentHost: () => acceptCurrentHost
10582
10841
  });
10583
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "node:fs";
10842
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "node:fs";
10584
10843
  import { join as join7 } from "node:path";
10585
10844
  import { createHash as createHash2 } from "node:crypto";
10586
10845
  import { networkInterfaces } from "node:os";
@@ -10601,13 +10860,13 @@ function computeCurrentFingerprint() {
10601
10860
  }
10602
10861
  function checkFingerprint() {
10603
10862
  const current = computeCurrentFingerprint();
10604
- if (!existsSync9(path())) {
10605
- writeFileSync9(path(), JSON.stringify(current, null, 2), { mode: 384 });
10863
+ if (!existsSync10(path())) {
10864
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
10606
10865
  return { result: "first_run", current };
10607
10866
  }
10608
10867
  let stored;
10609
10868
  try {
10610
- stored = JSON.parse(readFileSync8(path(), "utf8"));
10869
+ stored = JSON.parse(readFileSync9(path(), "utf8"));
10611
10870
  } catch {
10612
10871
  return { result: "unavailable", current };
10613
10872
  }
@@ -10617,14 +10876,14 @@ function checkFingerprint() {
10617
10876
  }
10618
10877
  function acceptCurrentHost() {
10619
10878
  const current = computeCurrentFingerprint();
10620
- writeFileSync9(path(), JSON.stringify(current, null, 2), { mode: 384 });
10879
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
10621
10880
  return current;
10622
10881
  }
10623
10882
  function readHostId() {
10624
10883
  if (process.platform === "linux") {
10625
10884
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
10626
10885
  try {
10627
- const raw = readFileSync8(p, "utf8").trim();
10886
+ const raw = readFileSync9(p, "utf8").trim();
10628
10887
  if (raw)
10629
10888
  return `linux:${raw}`;
10630
10889
  } catch {}
@@ -10669,16 +10928,16 @@ var init_identity = __esm(() => {
10669
10928
  });
10670
10929
 
10671
10930
  // src/daemon/run.ts
10672
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync9 } from "node:fs";
10931
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10 } from "node:fs";
10673
10932
  function detectContainer() {
10674
10933
  if (process.env.KUBERNETES_SERVICE_HOST)
10675
10934
  return true;
10676
10935
  if (process.env.CONTAINER === "1")
10677
10936
  return true;
10678
10937
  try {
10679
- if (existsSync10("/.dockerenv"))
10938
+ if (existsSync11("/.dockerenv"))
10680
10939
  return true;
10681
- const cg = readFileSync9("/proc/1/cgroup", "utf8");
10940
+ const cg = readFileSync10("/proc/1/cgroup", "utf8");
10682
10941
  if (/(docker|kubepods|containerd)/.test(cg))
10683
10942
  return true;
10684
10943
  } catch {}
@@ -10803,6 +11062,7 @@ async function runDaemon(opts = {}) {
10803
11062
  }
10804
11063
  let drain = null;
10805
11064
  drain = startDrainWorker({ db: outboxDb, brokers });
11065
+ startReaper();
10806
11066
  const ipc2 = startIpcServer({
10807
11067
  localToken,
10808
11068
  tcpEnabled,
@@ -10864,6 +11124,7 @@ var init_run = __esm(() => {
10864
11124
  init_lock();
10865
11125
  init_local_token();
10866
11126
  init_server();
11127
+ init_session_registry();
10867
11128
  init_broker();
10868
11129
  init_drain();
10869
11130
  init_inbound();
@@ -10879,7 +11140,7 @@ __export(exports_service_install, {
10879
11140
  installService: () => installService,
10880
11141
  detectPlatform: () => detectPlatform
10881
11142
  });
10882
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10, unlinkSync as unlinkSync5, readFileSync as readFileSync10 } from "node:fs";
11143
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync11, unlinkSync as unlinkSync5, readFileSync as readFileSync11 } from "node:fs";
10883
11144
  import { execSync as execSync2 } from "node:child_process";
10884
11145
  import { homedir as homedir5 } from "node:os";
10885
11146
  import { join as join8, dirname as dirname5 } from "node:path";
@@ -10900,7 +11161,7 @@ function installService(args) {
10900
11161
  if (isCi() && !args.allowCi) {
10901
11162
  throw new Error("Refusing to install persistent service in CI; pass --allow-ci-persistent to override.");
10902
11163
  }
10903
- if (!existsSync11(args.binaryPath)) {
11164
+ if (!existsSync12(args.binaryPath)) {
10904
11165
  throw new Error(`binary not found at ${args.binaryPath}`);
10905
11166
  }
10906
11167
  mkdirSync7(DAEMON_PATHS.DAEMON_DIR, { recursive: true, mode: 448 });
@@ -10916,7 +11177,7 @@ function uninstallService() {
10916
11177
  try {
10917
11178
  execSync2(`launchctl bootout gui/$(id -u)/${SERVICE_LABEL}`, { stdio: "ignore" });
10918
11179
  } catch {}
10919
- if (existsSync11(p)) {
11180
+ if (existsSync12(p)) {
10920
11181
  unlinkSync5(p);
10921
11182
  removed.push(p);
10922
11183
  }
@@ -10925,7 +11186,7 @@ function uninstallService() {
10925
11186
  try {
10926
11187
  execSync2(`systemctl --user disable --now ${SYSTEMD_UNIT}`, { stdio: "ignore" });
10927
11188
  } catch {}
10928
- if (existsSync11(p)) {
11189
+ if (existsSync12(p)) {
10929
11190
  unlinkSync5(p);
10930
11191
  removed.push(p);
10931
11192
  }
@@ -10978,7 +11239,7 @@ function installDarwin(args) {
10978
11239
  </dict>
10979
11240
  </plist>
10980
11241
  `;
10981
- writeFileSync10(plist, xml, { mode: 420 });
11242
+ writeFileSync11(plist, xml, { mode: 420 });
10982
11243
  return {
10983
11244
  platform: "darwin",
10984
11245
  unitPath: plist,
@@ -11015,7 +11276,7 @@ Environment=PATH=/usr/local/bin:/usr/bin:/bin
11015
11276
  [Install]
11016
11277
  WantedBy=default.target
11017
11278
  `;
11018
- writeFileSync10(unit, content, { mode: 420 });
11279
+ writeFileSync11(unit, content, { mode: 420 });
11019
11280
  return {
11020
11281
  platform: "linux",
11021
11282
  unitPath: unit,
@@ -11035,10 +11296,10 @@ function readInstalledUnit() {
11035
11296
  if (!platform5)
11036
11297
  return { platform: null, path: null, content: null };
11037
11298
  const path2 = platform5 === "darwin" ? darwinPlistPath() : linuxUnitPath();
11038
- if (!existsSync11(path2))
11299
+ if (!existsSync12(path2))
11039
11300
  return { platform: platform5, path: null, content: null };
11040
11301
  try {
11041
- return { platform: platform5, path: path2, content: readFileSync10(path2, "utf8") };
11302
+ return { platform: platform5, path: path2, content: readFileSync11(path2, "utf8") };
11042
11303
  } catch {
11043
11304
  return { platform: platform5, path: path2, content: null };
11044
11305
  }
@@ -11380,19 +11641,19 @@ __export(exports_install, {
11380
11641
  import {
11381
11642
  chmodSync as chmodSync4,
11382
11643
  copyFileSync,
11383
- existsSync as existsSync12,
11644
+ existsSync as existsSync13,
11384
11645
  mkdirSync as mkdirSync8,
11385
- readFileSync as readFileSync11,
11386
- writeFileSync as writeFileSync11
11646
+ readFileSync as readFileSync12,
11647
+ writeFileSync as writeFileSync12
11387
11648
  } from "node:fs";
11388
11649
  import { homedir as homedir6, platform as platform5 } from "node:os";
11389
11650
  import { dirname as dirname6, join as join9, resolve } from "node:path";
11390
11651
  import { fileURLToPath } from "node:url";
11391
11652
  import { spawnSync as spawnSync3 } from "node:child_process";
11392
11653
  function readClaudeConfig() {
11393
- if (!existsSync12(CLAUDE_CONFIG))
11654
+ if (!existsSync13(CLAUDE_CONFIG))
11394
11655
  return {};
11395
- const text = readFileSync11(CLAUDE_CONFIG, "utf-8").trim();
11656
+ const text = readFileSync12(CLAUDE_CONFIG, "utf-8").trim();
11396
11657
  if (!text)
11397
11658
  return {};
11398
11659
  try {
@@ -11402,7 +11663,7 @@ function readClaudeConfig() {
11402
11663
  }
11403
11664
  }
11404
11665
  function backupClaudeConfig() {
11405
- if (!existsSync12(CLAUDE_CONFIG))
11666
+ if (!existsSync13(CLAUDE_CONFIG))
11406
11667
  return;
11407
11668
  const backupDir = join9(dirname6(CLAUDE_CONFIG), ".claude", "backups");
11408
11669
  mkdirSync8(backupDir, { recursive: true });
@@ -11431,7 +11692,7 @@ function patchMcpServer(entry) {
11431
11692
  return action;
11432
11693
  }
11433
11694
  function removeMcpServer() {
11434
- if (!existsSync12(CLAUDE_CONFIG))
11695
+ if (!existsSync13(CLAUDE_CONFIG))
11435
11696
  return false;
11436
11697
  backupClaudeConfig();
11437
11698
  const cfg = readClaudeConfig();
@@ -11445,7 +11706,7 @@ function removeMcpServer() {
11445
11706
  }
11446
11707
  function flushClaudeConfig(obj) {
11447
11708
  mkdirSync8(dirname6(CLAUDE_CONFIG), { recursive: true });
11448
- writeFileSync11(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
11709
+ writeFileSync12(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
11449
11710
  `, "utf-8");
11450
11711
  try {
11451
11712
  chmodSync4(CLAUDE_CONFIG, 384);
@@ -11468,7 +11729,7 @@ function resolveBundledSkillsDir() {
11468
11729
  const here = fileURLToPath(import.meta.url);
11469
11730
  const pkgRoot = resolve(dirname6(here), "..", "..");
11470
11731
  const skillsDir = join9(pkgRoot, "skills");
11471
- if (existsSync12(skillsDir))
11732
+ if (existsSync13(skillsDir))
11472
11733
  return skillsDir;
11473
11734
  return null;
11474
11735
  }
@@ -11503,7 +11764,7 @@ function uninstallSkills() {
11503
11764
  if (!entry.isDirectory())
11504
11765
  continue;
11505
11766
  const dstDir = join9(CLAUDE_SKILLS_ROOT, entry.name);
11506
- if (existsSync12(dstDir)) {
11767
+ if (existsSync13(dstDir)) {
11507
11768
  try {
11508
11769
  fs.rmSync(dstDir, { recursive: true, force: true });
11509
11770
  removed.push(entry.name);
@@ -11528,9 +11789,9 @@ function entriesEqual(a, b) {
11528
11789
  return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
11529
11790
  }
11530
11791
  function readClaudeSettings() {
11531
- if (!existsSync12(CLAUDE_SETTINGS))
11792
+ if (!existsSync13(CLAUDE_SETTINGS))
11532
11793
  return {};
11533
- const text = readFileSync11(CLAUDE_SETTINGS, "utf-8").trim();
11794
+ const text = readFileSync12(CLAUDE_SETTINGS, "utf-8").trim();
11534
11795
  if (!text)
11535
11796
  return {};
11536
11797
  try {
@@ -11541,7 +11802,7 @@ function readClaudeSettings() {
11541
11802
  }
11542
11803
  function writeClaudeSettings(obj) {
11543
11804
  mkdirSync8(dirname6(CLAUDE_SETTINGS), { recursive: true });
11544
- writeFileSync11(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
11805
+ writeFileSync12(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
11545
11806
  `, "utf-8");
11546
11807
  }
11547
11808
  function installAllowedTools() {
@@ -11555,7 +11816,7 @@ function installAllowedTools() {
11555
11816
  return { added: toAdd, unchanged: CLAUDEMESH_TOOLS.length - toAdd.length };
11556
11817
  }
11557
11818
  function uninstallAllowedTools() {
11558
- if (!existsSync12(CLAUDE_SETTINGS))
11819
+ if (!existsSync13(CLAUDE_SETTINGS))
11559
11820
  return 0;
11560
11821
  const settings = readClaudeSettings();
11561
11822
  const existing = settings.allowedTools ?? [];
@@ -11590,7 +11851,7 @@ function installHooks() {
11590
11851
  return { added, unchanged };
11591
11852
  }
11592
11853
  function uninstallHooks() {
11593
- if (!existsSync12(CLAUDE_SETTINGS))
11854
+ if (!existsSync13(CLAUDE_SETTINGS))
11594
11855
  return 0;
11595
11856
  const settings = readClaudeSettings();
11596
11857
  const hooks = settings.hooks;
@@ -11640,7 +11901,7 @@ function runInstall(args = []) {
11640
11901
  render.err("`bun` is not on PATH.", "Install Bun first: https://bun.com");
11641
11902
  process.exit(1);
11642
11903
  }
11643
- if (!existsSync12(entry)) {
11904
+ if (!existsSync13(entry)) {
11644
11905
  render.err(`MCP entry not found at ${entry}`);
11645
11906
  process.exit(1);
11646
11907
  }
@@ -11885,7 +12146,7 @@ var exports_uninstall = {};
11885
12146
  __export(exports_uninstall, {
11886
12147
  uninstall: () => uninstall
11887
12148
  });
11888
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync13, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
12149
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, existsSync as existsSync14, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
11889
12150
  import { join as join10, dirname as dirname7 } from "node:path";
11890
12151
  import { homedir as homedir7 } from "node:os";
11891
12152
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -11893,27 +12154,27 @@ function bundledSkillsDir() {
11893
12154
  const here = fileURLToPath2(import.meta.url);
11894
12155
  const pkgRoot = join10(dirname7(here), "..", "..");
11895
12156
  const skillsDir = join10(pkgRoot, "skills");
11896
- return existsSync13(skillsDir) ? skillsDir : null;
12157
+ return existsSync14(skillsDir) ? skillsDir : null;
11897
12158
  }
11898
12159
  async function uninstall() {
11899
12160
  let removed = 0;
11900
- if (existsSync13(PATHS.CLAUDE_JSON)) {
12161
+ if (existsSync14(PATHS.CLAUDE_JSON)) {
11901
12162
  try {
11902
- const raw = readFileSync12(PATHS.CLAUDE_JSON, "utf-8");
12163
+ const raw = readFileSync13(PATHS.CLAUDE_JSON, "utf-8");
11903
12164
  const config = JSON.parse(raw);
11904
12165
  const servers = config.mcpServers;
11905
12166
  if (servers && "claudemesh" in servers) {
11906
12167
  delete servers.claudemesh;
11907
- writeFileSync12(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
12168
+ writeFileSync13(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
11908
12169
  `, "utf-8");
11909
12170
  render.ok("removed MCP server", dim("~/.claude.json"));
11910
12171
  removed++;
11911
12172
  }
11912
12173
  } catch {}
11913
12174
  }
11914
- if (existsSync13(PATHS.CLAUDE_SETTINGS)) {
12175
+ if (existsSync14(PATHS.CLAUDE_SETTINGS)) {
11915
12176
  try {
11916
- const raw = readFileSync12(PATHS.CLAUDE_SETTINGS, "utf-8");
12177
+ const raw = readFileSync13(PATHS.CLAUDE_SETTINGS, "utf-8");
11917
12178
  const config = JSON.parse(raw);
11918
12179
  const hooks = config.hooks;
11919
12180
  if (hooks) {
@@ -11934,7 +12195,7 @@ async function uninstall() {
11934
12195
  }
11935
12196
  }
11936
12197
  if (removedHooks > 0) {
11937
- writeFileSync12(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
12198
+ writeFileSync13(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
11938
12199
  `, "utf-8");
11939
12200
  render.ok(`removed ${removedHooks} claudemesh hook${removedHooks === 1 ? "" : "s"}`, dim("settings.json"));
11940
12201
  removed++;
@@ -11950,7 +12211,7 @@ async function uninstall() {
11950
12211
  if (!entry.isDirectory())
11951
12212
  continue;
11952
12213
  const dst = join10(CLAUDE_SKILLS_ROOT2, entry.name);
11953
- if (existsSync13(dst)) {
12214
+ if (existsSync14(dst)) {
11954
12215
  try {
11955
12216
  rmSync2(dst, { recursive: true, force: true });
11956
12217
  removedSkills.push(entry.name);
@@ -11982,7 +12243,7 @@ var exports_doctor = {};
11982
12243
  __export(exports_doctor, {
11983
12244
  runDoctor: () => runDoctor
11984
12245
  });
11985
- import { existsSync as existsSync14, readFileSync as readFileSync13, statSync as statSync3 } from "node:fs";
12246
+ import { existsSync as existsSync15, readFileSync as readFileSync14, statSync as statSync3 } from "node:fs";
11986
12247
  import { homedir as homedir8, platform as platform6 } from "node:os";
11987
12248
  import { join as join11 } from "node:path";
11988
12249
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -12009,7 +12270,7 @@ function checkClaudeOnPath() {
12009
12270
  }
12010
12271
  function checkMcpRegistered() {
12011
12272
  const claudeConfig = join11(homedir8(), ".claude.json");
12012
- if (!existsSync14(claudeConfig)) {
12273
+ if (!existsSync15(claudeConfig)) {
12013
12274
  return {
12014
12275
  name: "claudemesh MCP registered in ~/.claude.json",
12015
12276
  pass: false,
@@ -12017,7 +12278,7 @@ function checkMcpRegistered() {
12017
12278
  };
12018
12279
  }
12019
12280
  try {
12020
- const cfg = JSON.parse(readFileSync13(claudeConfig, "utf-8"));
12281
+ const cfg = JSON.parse(readFileSync14(claudeConfig, "utf-8"));
12021
12282
  const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
12022
12283
  return {
12023
12284
  name: "claudemesh MCP registered in ~/.claude.json",
@@ -12035,7 +12296,7 @@ function checkMcpRegistered() {
12035
12296
  }
12036
12297
  function checkHooksRegistered() {
12037
12298
  const settings = join11(homedir8(), ".claude", "settings.json");
12038
- if (!existsSync14(settings)) {
12299
+ if (!existsSync15(settings)) {
12039
12300
  return {
12040
12301
  name: "Status hooks registered in ~/.claude/settings.json",
12041
12302
  pass: false,
@@ -12043,7 +12304,7 @@ function checkHooksRegistered() {
12043
12304
  };
12044
12305
  }
12045
12306
  try {
12046
- const raw = readFileSync13(settings, "utf-8");
12307
+ const raw = readFileSync14(settings, "utf-8");
12047
12308
  const has = raw.includes("claudemesh hook ");
12048
12309
  return {
12049
12310
  name: "Status hooks registered in ~/.claude/settings.json",
@@ -12060,7 +12321,7 @@ function checkHooksRegistered() {
12060
12321
  }
12061
12322
  function checkConfigFile() {
12062
12323
  const path2 = getConfigPath();
12063
- if (!existsSync14(path2)) {
12324
+ if (!existsSync15(path2)) {
12064
12325
  return {
12065
12326
  name: "~/.claudemesh/config.json exists and parses",
12066
12327
  pass: true,
@@ -12243,7 +12504,7 @@ var exports_status = {};
12243
12504
  __export(exports_status, {
12244
12505
  runStatus: () => runStatus2
12245
12506
  });
12246
- import { statSync as statSync4, existsSync as existsSync15 } from "node:fs";
12507
+ import { statSync as statSync4, existsSync as existsSync16 } from "node:fs";
12247
12508
  import WebSocket3 from "ws";
12248
12509
  async function probeBroker(url, timeoutMs = 4000) {
12249
12510
  return new Promise((resolve2) => {
@@ -12273,7 +12534,7 @@ async function runStatus2() {
12273
12534
  render.section(`status (v${VERSION})`);
12274
12535
  const configPath = getConfigPath();
12275
12536
  let configPermsNote = "missing";
12276
- if (existsSync15(configPath)) {
12537
+ if (existsSync16(configPath)) {
12277
12538
  const mode = (statSync4(configPath).mode & 511).toString(8).padStart(4, "0");
12278
12539
  configPermsNote = mode === "0600" ? `${mode}` : `${mode} — expected 0600`;
12279
12540
  }
@@ -12419,13 +12680,13 @@ var init_check_claude_binary = __esm(() => {
12419
12680
  });
12420
12681
 
12421
12682
  // src/services/health/check-mcp-registered.ts
12422
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
12683
+ import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
12423
12684
  function checkMcpRegistered2() {
12424
12685
  try {
12425
- if (!existsSync16(PATHS.CLAUDE_JSON)) {
12686
+ if (!existsSync17(PATHS.CLAUDE_JSON)) {
12426
12687
  return { name: "mcp-registered", ok: false, message: "~/.claude.json not found" };
12427
12688
  }
12428
- const raw = readFileSync14(PATHS.CLAUDE_JSON, "utf-8");
12689
+ const raw = readFileSync15(PATHS.CLAUDE_JSON, "utf-8");
12429
12690
  const config = JSON.parse(raw);
12430
12691
  if (config.mcpServers && "claudemesh" in config.mcpServers) {
12431
12692
  return { name: "mcp-registered", ok: true, message: "MCP server registered" };
@@ -12440,13 +12701,13 @@ var init_check_mcp_registered = __esm(() => {
12440
12701
  });
12441
12702
 
12442
12703
  // src/services/health/check-hooks-registered.ts
12443
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
12704
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
12444
12705
  function checkHooksRegistered2() {
12445
12706
  try {
12446
- if (!existsSync17(PATHS.CLAUDE_SETTINGS)) {
12707
+ if (!existsSync18(PATHS.CLAUDE_SETTINGS)) {
12447
12708
  return { name: "hooks-registered", ok: false, message: "~/.claude/settings.json not found" };
12448
12709
  }
12449
- const raw = readFileSync15(PATHS.CLAUDE_SETTINGS, "utf-8");
12710
+ const raw = readFileSync16(PATHS.CLAUDE_SETTINGS, "utf-8");
12450
12711
  const config = JSON.parse(raw);
12451
12712
  if (config.hooks) {
12452
12713
  return { name: "hooks-registered", ok: true, message: "Hooks configured" };
@@ -12461,10 +12722,10 @@ var init_check_hooks_registered = __esm(() => {
12461
12722
  });
12462
12723
 
12463
12724
  // src/services/health/check-config-perms.ts
12464
- import { existsSync as existsSync18, statSync as statSync5 } from "node:fs";
12725
+ import { existsSync as existsSync19, statSync as statSync5 } from "node:fs";
12465
12726
  function checkConfigPerms() {
12466
12727
  const configFile = PATHS.CONFIG_FILE;
12467
- if (!existsSync18(configFile)) {
12728
+ if (!existsSync19(configFile)) {
12468
12729
  return { name: "config-perms", ok: true, message: "No config file yet (first run)" };
12469
12730
  }
12470
12731
  try {
@@ -12482,13 +12743,13 @@ var init_check_config_perms = __esm(() => {
12482
12743
  });
12483
12744
 
12484
12745
  // src/services/health/check-keypairs-valid.ts
12485
- import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
12746
+ import { existsSync as existsSync20, readFileSync as readFileSync17 } from "node:fs";
12486
12747
  function checkKeypairsValid() {
12487
- if (!existsSync19(PATHS.CONFIG_FILE)) {
12748
+ if (!existsSync20(PATHS.CONFIG_FILE)) {
12488
12749
  return { name: "keypairs-valid", ok: true, message: "No config (first run)" };
12489
12750
  }
12490
12751
  try {
12491
- const raw = readFileSync16(PATHS.CONFIG_FILE, "utf-8");
12752
+ const raw = readFileSync17(PATHS.CONFIG_FILE, "utf-8");
12492
12753
  const config = JSON.parse(raw);
12493
12754
  const meshes = config.meshes ?? [];
12494
12755
  if (meshes.length === 0) {
@@ -12969,7 +13230,7 @@ __export(exports_url_handler, {
12969
13230
  runUrlHandler: () => runUrlHandler
12970
13231
  });
12971
13232
  import { platform as platform7, homedir as homedir9 } from "node:os";
12972
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, writeFileSync as writeFileSync13, rmSync as rmSync3, chmodSync as chmodSync5 } from "node:fs";
13233
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync14, rmSync as rmSync3, chmodSync as chmodSync5 } from "node:fs";
12973
13234
  import { join as join12 } from "node:path";
12974
13235
  import { spawnSync as spawnSync5 } from "node:child_process";
12975
13236
  function resolveClaudemeshBin() {
@@ -13003,7 +13264,7 @@ function installDarwin2() {
13003
13264
  </array>
13004
13265
  </dict>
13005
13266
  </plist>`;
13006
- writeFileSync13(join12(contents, "Info.plist"), plist);
13267
+ writeFileSync14(join12(contents, "Info.plist"), plist);
13007
13268
  const shim = `#!/bin/sh
13008
13269
  URL="$1"
13009
13270
  CODE=\${URL#claudemesh://}
@@ -13018,7 +13279,7 @@ end tell
13018
13279
  EOF
13019
13280
  `;
13020
13281
  const shimPath = join12(macOS, "open-url");
13021
- writeFileSync13(shimPath, shim);
13282
+ writeFileSync14(shimPath, shim);
13022
13283
  chmodSync5(shimPath, 493);
13023
13284
  const lsreg = spawnSync5("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", ["-f", appDir], { encoding: "utf-8" });
13024
13285
  if (lsreg.status !== 0) {
@@ -13042,7 +13303,7 @@ MimeType=x-scheme-handler/claudemesh;
13042
13303
  NoDisplay=true
13043
13304
  `;
13044
13305
  const desktopPath = join12(appsDir, "claudemesh.desktop");
13045
- writeFileSync13(desktopPath, desktop);
13306
+ writeFileSync14(desktopPath, desktop);
13046
13307
  const xdg1 = spawnSync5("xdg-mime", ["default", "claudemesh.desktop", "x-scheme-handler/claudemesh"], { encoding: "utf-8" });
13047
13308
  if (xdg1.status !== 0) {
13048
13309
  render.warn("xdg-mime not available — skipped mime default registration");
@@ -13065,7 +13326,7 @@ function installWindows() {
13065
13326
  `@="\\"${binPath.replace(/\\/g, "\\\\")}\\" \\"%1\\""`
13066
13327
  ];
13067
13328
  const regPath = join12(homedir9(), "claudemesh-handler.reg");
13068
- writeFileSync13(regPath, lines.join(`\r
13329
+ writeFileSync14(regPath, lines.join(`\r
13069
13330
  `));
13070
13331
  const res = spawnSync5("reg.exe", ["import", regPath], { encoding: "utf-8" });
13071
13332
  if (res.status !== 0) {
@@ -13077,14 +13338,14 @@ function installWindows() {
13077
13338
  }
13078
13339
  function uninstallDarwin() {
13079
13340
  const appDir = join12(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
13080
- if (existsSync20(appDir))
13341
+ if (existsSync21(appDir))
13081
13342
  rmSync3(appDir, { recursive: true, force: true });
13082
13343
  render.ok("removed claudemesh:// handler on macOS");
13083
13344
  return EXIT.SUCCESS;
13084
13345
  }
13085
13346
  function uninstallLinux() {
13086
13347
  const desktopPath = join12(homedir9(), ".local", "share", "applications", "claudemesh.desktop");
13087
- if (existsSync20(desktopPath))
13348
+ if (existsSync21(desktopPath))
13088
13349
  rmSync3(desktopPath, { force: true });
13089
13350
  render.ok("removed claudemesh:// handler on Linux");
13090
13351
  return EXIT.SUCCESS;
@@ -13129,7 +13390,7 @@ var exports_status_line = {};
13129
13390
  __export(exports_status_line, {
13130
13391
  runStatusLine: () => runStatusLine
13131
13392
  });
13132
- import { existsSync as existsSync21, readFileSync as readFileSync17 } from "node:fs";
13393
+ import { existsSync as existsSync22, readFileSync as readFileSync18 } from "node:fs";
13133
13394
  import { join as join13 } from "node:path";
13134
13395
  import { homedir as homedir10 } from "node:os";
13135
13396
  async function runStatusLine() {
@@ -13141,9 +13402,9 @@ async function runStatusLine() {
13141
13402
  }
13142
13403
  const cachePath = join13(homedir10(), ".claudemesh", "peer-cache.json");
13143
13404
  let cache = {};
13144
- if (existsSync21(cachePath)) {
13405
+ if (existsSync22(cachePath)) {
13145
13406
  try {
13146
- cache = JSON.parse(readFileSync17(cachePath, "utf-8"));
13407
+ cache = JSON.parse(readFileSync18(cachePath, "utf-8"));
13147
13408
  } catch {}
13148
13409
  }
13149
13410
  const pick = config.meshes[0];
@@ -13174,7 +13435,7 @@ __export(exports_backup, {
13174
13435
  runRestore: () => runRestore,
13175
13436
  runBackup: () => runBackup
13176
13437
  });
13177
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22 } from "node:fs";
13438
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, existsSync as existsSync23 } from "node:fs";
13178
13439
  import { createInterface as createInterface11 } from "node:readline";
13179
13440
  function readHidden(prompt5) {
13180
13441
  return new Promise((resolve2) => {
@@ -13216,11 +13477,11 @@ async function deriveKey(pass, salt, s) {
13216
13477
  }
13217
13478
  async function runBackup(outPath) {
13218
13479
  const configPath = getConfigPath();
13219
- if (!existsSync22(configPath)) {
13480
+ if (!existsSync23(configPath)) {
13220
13481
  console.error(" No config found — nothing to back up. Join a mesh first.");
13221
13482
  return EXIT.NOT_FOUND;
13222
13483
  }
13223
- const plaintext = readFileSync18(configPath);
13484
+ const plaintext = readFileSync19(configPath);
13224
13485
  const pass = await readHidden(" Passphrase (min 12 chars): ");
13225
13486
  if (pass.length < 12) {
13226
13487
  console.error(" ✗ Passphrase too short.");
@@ -13238,7 +13499,7 @@ async function runBackup(outPath) {
13238
13499
  const ciphertext = Buffer.from(s.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, key));
13239
13500
  const blob = Buffer.concat([MAGIC, salt, nonce, ciphertext]);
13240
13501
  const file = outPath ?? `claudemesh-backup-${new Date().toISOString().replace(/[:.]/g, "-")}.cmb`;
13241
- writeFileSync14(file, blob, { mode: 384 });
13502
+ writeFileSync15(file, blob, { mode: 384 });
13242
13503
  console.log(`
13243
13504
  ✓ Backup saved: ${file}`);
13244
13505
  console.log(` Size: ${blob.length} bytes. Guard the passphrase — there is no recovery.
@@ -13250,11 +13511,11 @@ async function runRestore(inPath) {
13250
13511
  console.error(" Usage: claudemesh restore <backup-file>");
13251
13512
  return EXIT.INVALID_ARGS;
13252
13513
  }
13253
- if (!existsSync22(inPath)) {
13514
+ if (!existsSync23(inPath)) {
13254
13515
  console.error(` ✗ File not found: ${inPath}`);
13255
13516
  return EXIT.NOT_FOUND;
13256
13517
  }
13257
- const blob = readFileSync18(inPath);
13518
+ const blob = readFileSync19(inPath);
13258
13519
  if (blob.length < 4 + 16 + 24 + 17 || !blob.subarray(0, 4).equals(MAGIC)) {
13259
13520
  console.error(" ✗ Not a claudemesh backup file (bad magic).");
13260
13521
  return EXIT.INVALID_ARGS;
@@ -13273,12 +13534,12 @@ async function runRestore(inPath) {
13273
13534
  return EXIT.INTERNAL_ERROR;
13274
13535
  }
13275
13536
  const configPath = getConfigPath();
13276
- if (existsSync22(configPath)) {
13537
+ if (existsSync23(configPath)) {
13277
13538
  const backupOld = `${configPath}.before-restore.${Date.now()}`;
13278
- writeFileSync14(backupOld, readFileSync18(configPath), { mode: 384 });
13539
+ writeFileSync15(backupOld, readFileSync19(configPath), { mode: 384 });
13279
13540
  console.log(` ↻ Existing config saved to ${backupOld}`);
13280
13541
  }
13281
- writeFileSync14(configPath, Buffer.from(plaintext), { mode: 384 });
13542
+ writeFileSync15(configPath, Buffer.from(plaintext), { mode: 384 });
13282
13543
  console.log(`
13283
13544
  ✓ Config restored to ${configPath}`);
13284
13545
  console.log(" Run `claudemesh list` to verify your meshes.\n");
@@ -13298,7 +13559,7 @@ __export(exports_upgrade, {
13298
13559
  runUpgrade: () => runUpgrade
13299
13560
  });
13300
13561
  import { spawnSync as spawnSync6 } from "node:child_process";
13301
- import { existsSync as existsSync23 } from "node:fs";
13562
+ import { existsSync as existsSync24 } from "node:fs";
13302
13563
  import { dirname as dirname8, join as join14, resolve as resolve2 } from "node:path";
13303
13564
  async function latestVersion() {
13304
13565
  try {
@@ -13313,14 +13574,14 @@ async function latestVersion() {
13313
13574
  }
13314
13575
  function findNpm() {
13315
13576
  const portable = join14(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
13316
- if (existsSync23(portable)) {
13577
+ if (existsSync24(portable)) {
13317
13578
  return { npm: portable, prefix: join14(process.env.HOME ?? "", ".claudemesh") };
13318
13579
  }
13319
13580
  let cur = resolve2(process.argv[1] ?? ".");
13320
13581
  for (let i = 0;i < 6; i++) {
13321
13582
  cur = dirname8(cur);
13322
13583
  const candidate = join14(cur, "bin", "npm");
13323
- if (existsSync23(candidate))
13584
+ if (existsSync24(candidate))
13324
13585
  return { npm: candidate };
13325
13586
  }
13326
13587
  return { npm: "npm" };
@@ -13382,7 +13643,7 @@ __export(exports_grants, {
13382
13643
  runBlock: () => runBlock,
13383
13644
  isAllowed: () => isAllowed
13384
13645
  });
13385
- import { existsSync as existsSync24, mkdirSync as mkdirSync10, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "node:fs";
13646
+ import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "node:fs";
13386
13647
  import { homedir as homedir11 } from "node:os";
13387
13648
  import { join as join15 } from "node:path";
13388
13649
  async function syncToBroker(meshSlug, grants) {
@@ -13402,19 +13663,19 @@ async function syncToBroker(meshSlug, grants) {
13402
13663
  }
13403
13664
  }
13404
13665
  function readGrants() {
13405
- if (!existsSync24(GRANT_FILE))
13666
+ if (!existsSync25(GRANT_FILE))
13406
13667
  return {};
13407
13668
  try {
13408
- return JSON.parse(readFileSync19(GRANT_FILE, "utf-8"));
13669
+ return JSON.parse(readFileSync20(GRANT_FILE, "utf-8"));
13409
13670
  } catch {
13410
13671
  return {};
13411
13672
  }
13412
13673
  }
13413
13674
  function writeGrants(g) {
13414
13675
  const dir = join15(homedir11(), ".claudemesh");
13415
- if (!existsSync24(dir))
13676
+ if (!existsSync25(dir))
13416
13677
  mkdirSync10(dir, { recursive: true });
13417
- writeFileSync15(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
13678
+ writeFileSync16(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
13418
13679
  }
13419
13680
  function resolveCaps(input) {
13420
13681
  if (input.includes("all"))
@@ -15252,7 +15513,7 @@ __export(exports_file, {
15252
15513
  });
15253
15514
  import { hostname as osHostname } from "node:os";
15254
15515
  import { resolve as resolvePath, basename, dirname as dirname9 } from "node:path";
15255
- import { statSync as statSync7, existsSync as existsSync25, writeFileSync as writeFileSync16, mkdirSync as mkdirSync11 } from "node:fs";
15516
+ import { statSync as statSync7, existsSync as existsSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync11 } from "node:fs";
15256
15517
  function emitJson2(data) {
15257
15518
  console.log(JSON.stringify(data, null, 2));
15258
15519
  }
@@ -15269,7 +15530,7 @@ async function runFileShare(filePath, opts) {
15269
15530
  return EXIT.INVALID_ARGS;
15270
15531
  }
15271
15532
  const absPath = resolvePath(filePath);
15272
- if (!existsSync25(absPath)) {
15533
+ if (!existsSync26(absPath)) {
15273
15534
  render.err(`File not found: ${absPath}`);
15274
15535
  return EXIT.INVALID_ARGS;
15275
15536
  }
@@ -15349,7 +15610,7 @@ async function runFileGet(fileId, opts) {
15349
15610
  const buf = Buffer.from(await res.arrayBuffer());
15350
15611
  const outPath = opts.out ? resolvePath(opts.out) : resolvePath(process.cwd(), meta.name);
15351
15612
  mkdirSync11(dirname9(outPath), { recursive: true });
15352
- writeFileSync16(outPath, buf);
15613
+ writeFileSync17(outPath, buf);
15353
15614
  if (opts.json) {
15354
15615
  emitJson2({ fileId, name: meta.name, savedTo: outPath, sizeBytes: buf.length });
15355
15616
  } else {
@@ -15959,7 +16220,7 @@ __export(exports_bridge, {
15959
16220
  runBridge: () => runBridge,
15960
16221
  bridgeConfigTemplate: () => bridgeConfigTemplate
15961
16222
  });
15962
- import { readFileSync as readFileSync20, existsSync as existsSync26 } from "node:fs";
16223
+ import { readFileSync as readFileSync21, existsSync as existsSync27 } from "node:fs";
15963
16224
  function parseConfig(text) {
15964
16225
  const trimmed = text.trim();
15965
16226
  if (trimmed.startsWith("{"))
@@ -16003,13 +16264,13 @@ async function runBridge(configPath) {
16003
16264
  render.err("Usage: claudemesh bridge run <config.yaml>");
16004
16265
  return EXIT.INVALID_ARGS;
16005
16266
  }
16006
- if (!existsSync26(configPath)) {
16267
+ if (!existsSync27(configPath)) {
16007
16268
  render.err(`config file not found: ${configPath}`);
16008
16269
  return EXIT.NOT_FOUND;
16009
16270
  }
16010
16271
  let cfg;
16011
16272
  try {
16012
- cfg = parseConfig(readFileSync20(configPath, "utf-8"));
16273
+ cfg = parseConfig(readFileSync21(configPath, "utf-8"));
16013
16274
  } catch (e) {
16014
16275
  render.err(`failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
16015
16276
  return EXIT.INVALID_ARGS;
@@ -16413,9 +16674,9 @@ function cacheKey(apiKeySecret, topicName) {
16413
16674
  async function getTopicKey(args) {
16414
16675
  const cacheId = cacheKey(args.apiKeySecret, args.topicName);
16415
16676
  if (!args.fresh) {
16416
- const cached2 = cache.get(cacheId);
16417
- if (cached2)
16418
- return { ok: true, topicKey: cached2.topicKey };
16677
+ const cached3 = cache.get(cacheId);
16678
+ if (cached3)
16679
+ return { ok: true, topicKey: cached3.topicKey };
16419
16680
  }
16420
16681
  let sealed;
16421
16682
  try {
@@ -16983,11 +17244,11 @@ import {
16983
17244
  ListResourcesRequestSchema,
16984
17245
  ReadResourceRequestSchema
16985
17246
  } from "@modelcontextprotocol/sdk/types.js";
16986
- import { existsSync as existsSync27 } from "node:fs";
17247
+ import { existsSync as existsSync28 } from "node:fs";
16987
17248
  import { request as httpRequest2 } from "node:http";
16988
17249
  async function daemonReady() {
16989
17250
  for (let i = 0;i < DAEMON_BOOT_RETRIES; i++) {
16990
- if (existsSync27(DAEMON_PATHS.SOCK_FILE))
17251
+ if (existsSync28(DAEMON_PATHS.SOCK_FILE))
16991
17252
  return true;
16992
17253
  await new Promise((r) => setTimeout(r, DAEMON_BOOT_RETRY_MS));
16993
17254
  }
@@ -17317,9 +17578,9 @@ async function startServiceProxy(serviceName) {
17317
17578
  const fetched = await client.getServiceTools(serviceName);
17318
17579
  tools = fetched;
17319
17580
  } catch {
17320
- const cached2 = client.serviceCatalog.find((s) => s.name === serviceName);
17321
- if (cached2)
17322
- tools = cached2.tools;
17581
+ const cached3 = client.serviceCatalog.find((s) => s.name === serviceName);
17582
+ if (cached3)
17583
+ tools = cached3.tools;
17323
17584
  }
17324
17585
  if (tools.length === 0) {
17325
17586
  process.stderr.write(`[mesh:${serviceName}] no tools found — service may not be running
@@ -19289,4 +19550,4 @@ main().catch((err) => {
19289
19550
  process.exit(EXIT.INTERNAL_ERROR);
19290
19551
  });
19291
19552
 
19292
- //# debugId=04E4A5313AE4F0EA64756E2164756E21
19553
+ //# debugId=FBD494B01BF81C7164756E2164756E21