claudemesh-cli 1.28.0 → 1.30.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.
@@ -92,7 +92,8 @@ var init_exit_codes = __esm(() => {
92
92
  ALREADY_EXISTS: 6,
93
93
  PERMISSION_DENIED: 7,
94
94
  INTERNAL_ERROR: 8,
95
- CLAUDE_MISSING: 9
95
+ CLAUDE_MISSING: 9,
96
+ IO_ERROR: 10
96
97
  };
97
98
  });
98
99
 
@@ -103,7 +104,7 @@ __export(exports_urls, {
103
104
  VERSION: () => VERSION,
104
105
  URLS: () => URLS
105
106
  });
106
- var URLS, VERSION = "1.28.0", env;
107
+ var URLS, VERSION = "1.30.0", env;
107
108
  var init_urls = __esm(() => {
108
109
  URLS = {
109
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -3668,7 +3669,50 @@ var init_local_token = __esm(() => {
3668
3669
  init_paths2();
3669
3670
  });
3670
3671
 
3672
+ // src/services/session/token.ts
3673
+ var exports_token = {};
3674
+ __export(exports_token, {
3675
+ readSessionTokenFromEnv: () => readSessionTokenFromEnv,
3676
+ mintSessionToken: () => mintSessionToken,
3677
+ TOKEN_FILE_ENV: () => TOKEN_FILE_ENV
3678
+ });
3679
+ import { randomBytes as randomBytes5 } from "node:crypto";
3680
+ import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
3681
+ function mintSessionToken(dir, fileName = "session-token") {
3682
+ const token = randomBytes5(32).toString("hex");
3683
+ const filePath = `${dir}/${fileName}`;
3684
+ writeFileSync5(filePath, token, { mode: 384 });
3685
+ return { token, filePath };
3686
+ }
3687
+ function readSessionTokenFromEnv(env2 = process.env) {
3688
+ const direct = env2.CLAUDEMESH_IPC_TOKEN;
3689
+ if (direct && /^[0-9a-f]{64}$/i.test(direct))
3690
+ return direct.toLowerCase();
3691
+ const path = env2[ENV_TOKEN_FILE];
3692
+ if (!path)
3693
+ return null;
3694
+ try {
3695
+ if (!existsSync5(path))
3696
+ return null;
3697
+ const raw = readFileSync5(path, "utf8").trim();
3698
+ if (/^[0-9a-f]{64}$/i.test(raw))
3699
+ return raw.toLowerCase();
3700
+ return null;
3701
+ } catch {
3702
+ return null;
3703
+ }
3704
+ }
3705
+ var ENV_TOKEN_FILE = "CLAUDEMESH_IPC_TOKEN_FILE", TOKEN_FILE_ENV;
3706
+ var init_token = __esm(() => {
3707
+ TOKEN_FILE_ENV = ENV_TOKEN_FILE;
3708
+ });
3709
+
3671
3710
  // src/daemon/ipc/client.ts
3711
+ var exports_client = {};
3712
+ __export(exports_client, {
3713
+ ipc: () => ipc,
3714
+ IpcError: () => IpcError
3715
+ });
3672
3716
  import { request as httpRequest } from "node:http";
3673
3717
  async function ipc(opts) {
3674
3718
  const useTcp = !!opts.preferTcp;
@@ -3688,6 +3732,11 @@ async function ipc(opts) {
3688
3732
  throw new IpcError(0, null, "daemon local token not found; is the daemon running?");
3689
3733
  headers.authorization = `Bearer ${tok}`;
3690
3734
  }
3735
+ if (!useTcp) {
3736
+ const sessionTok = readSessionTokenFromEnv();
3737
+ if (sessionTok)
3738
+ headers.authorization = `ClaudeMesh-Session ${sessionTok}`;
3739
+ }
3691
3740
  return new Promise((resolve, reject) => {
3692
3741
  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
3742
  const chunks = [];
@@ -3712,6 +3761,7 @@ var IpcError;
3712
3761
  var init_client3 = __esm(() => {
3713
3762
  init_paths2();
3714
3763
  init_local_token();
3764
+ init_token();
3715
3765
  IpcError = class IpcError extends Error {
3716
3766
  status;
3717
3767
  payload;
@@ -3729,7 +3779,7 @@ __export(exports_lifecycle, {
3729
3779
  ensureDaemonReady: () => ensureDaemonReady,
3730
3780
  _resetDaemonReadyCache: () => _resetDaemonReadyCache
3731
3781
  });
3732
- import { existsSync as existsSync5, readFileSync as readFileSync5, statSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
3782
+ import { existsSync as existsSync6, readFileSync as readFileSync6, statSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "node:fs";
3733
3783
  import { join as join4 } from "node:path";
3734
3784
  async function ensureDaemonReady(opts = {}) {
3735
3785
  if (lastResultThisProcess && (lastResultThisProcess.state === "up" || lastResultThisProcess.state === "started")) {
@@ -3770,7 +3820,7 @@ async function runEnsureDaemon(opts) {
3770
3820
  return { state: "spawn-failed", durationMs: Date.now() - t0, reason: spawnRes.reason };
3771
3821
  }
3772
3822
  async function probeDaemon() {
3773
- if (!existsSync5(DAEMON_PATHS.SOCK_FILE))
3823
+ if (!existsSync6(DAEMON_PATHS.SOCK_FILE))
3774
3824
  return "absent";
3775
3825
  try {
3776
3826
  const res = await ipc({ path: "/v1/version", timeoutMs: PROBE_TIMEOUT_MS });
@@ -3803,7 +3853,7 @@ function recentSpawnFailureFresh() {
3803
3853
  }
3804
3854
  function markSpawnFailure() {
3805
3855
  try {
3806
- writeFileSync5(SPAWN_FAIL_FILE(), String(Date.now()), { mode: 384 });
3856
+ writeFileSync6(SPAWN_FAIL_FILE(), String(Date.now()), { mode: 384 });
3807
3857
  } catch {}
3808
3858
  }
3809
3859
  function clearSpawnFailure() {
@@ -3840,9 +3890,9 @@ async function spawnDaemon(opts) {
3840
3890
  }
3841
3891
  async function acquireOrShareLock(_opts) {
3842
3892
  const lockPath = SPAWN_LOCK_FILE();
3843
- if (existsSync5(lockPath)) {
3893
+ if (existsSync6(lockPath)) {
3844
3894
  try {
3845
- const pidStr = readFileSync5(lockPath, "utf8").trim();
3895
+ const pidStr = readFileSync6(lockPath, "utf8").trim();
3846
3896
  const pid = Number.parseInt(pidStr, 10);
3847
3897
  if (Number.isFinite(pid) && pid > 0) {
3848
3898
  try {
@@ -3853,7 +3903,7 @@ async function acquireOrShareLock(_opts) {
3853
3903
  } catch {}
3854
3904
  }
3855
3905
  try {
3856
- writeFileSync5(lockPath, String(process.pid), { mode: 384 });
3906
+ writeFileSync6(lockPath, String(process.pid), { mode: 384 });
3857
3907
  } catch {}
3858
3908
  return "acquired";
3859
3909
  }
@@ -3865,7 +3915,7 @@ function releaseLock() {
3865
3915
  async function pollForSocket(budgetMs) {
3866
3916
  const start = Date.now();
3867
3917
  while (Date.now() - start < budgetMs) {
3868
- if (existsSync5(DAEMON_PATHS.SOCK_FILE)) {
3918
+ if (existsSync6(DAEMON_PATHS.SOCK_FILE)) {
3869
3919
  const probe = await probeDaemon();
3870
3920
  if (probe === "up")
3871
3921
  return { ok: true };
@@ -3892,6 +3942,38 @@ var init_lifecycle = __esm(() => {
3892
3942
  init_paths2();
3893
3943
  });
3894
3944
 
3945
+ // src/services/broker/session-hello-sig.ts
3946
+ var exports_session_hello_sig = {};
3947
+ __export(exports_session_hello_sig, {
3948
+ signSessionHello: () => signSessionHello,
3949
+ signParentAttestation: () => signParentAttestation,
3950
+ DEFAULT_ATTESTATION_TTL_MS: () => DEFAULT_ATTESTATION_TTL_MS
3951
+ });
3952
+ async function signParentAttestation(args) {
3953
+ const s = await ensureSodium();
3954
+ const expiresAt = (args.now ?? Date.now()) + (args.ttlMs ?? DEFAULT_ATTESTATION_TTL_MS);
3955
+ const canonical = `claudemesh-session-attest|${args.parentMemberPubkey}|${args.sessionPubkey}|${expiresAt}`;
3956
+ const sig = s.crypto_sign_detached(s.from_string(canonical), s.from_hex(args.parentSecretKey));
3957
+ return {
3958
+ sessionPubkey: args.sessionPubkey,
3959
+ parentMemberPubkey: args.parentMemberPubkey,
3960
+ expiresAt,
3961
+ signature: s.to_hex(sig)
3962
+ };
3963
+ }
3964
+ async function signSessionHello(args) {
3965
+ const s = await ensureSodium();
3966
+ const timestamp2 = args.now ?? Date.now();
3967
+ const canonical = `claudemesh-session-hello|${args.meshId}|${args.parentMemberPubkey}|${args.sessionPubkey}|${timestamp2}`;
3968
+ const sig = s.crypto_sign_detached(s.from_string(canonical), s.from_hex(args.sessionSecretKey));
3969
+ return { timestamp: timestamp2, signature: s.to_hex(sig) };
3970
+ }
3971
+ var DEFAULT_ATTESTATION_TTL_MS;
3972
+ var init_session_hello_sig = __esm(() => {
3973
+ init_keypair();
3974
+ DEFAULT_ATTESTATION_TTL_MS = 12 * 60 * 60 * 1000;
3975
+ });
3976
+
3895
3977
  // src/commands/launch.ts
3896
3978
  var exports_launch = {};
3897
3979
  __export(exports_launch, {
@@ -3899,7 +3981,7 @@ __export(exports_launch, {
3899
3981
  });
3900
3982
  import { spawnSync as spawnSync2 } from "node:child_process";
3901
3983
  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";
3984
+ import { mkdtempSync, writeFileSync as writeFileSync7, rmSync, readdirSync, statSync as statSync2, existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
3903
3985
  import { tmpdir, hostname as hostname2, homedir as homedir3 } from "node:os";
3904
3986
  import { join as join5 } from "node:path";
3905
3987
  import { createInterface as createInterface4 } from "node:readline";
@@ -4248,8 +4330,8 @@ async function runLaunch(flags, rawArgs) {
4248
4330
  await ensureDaemonRunning(mesh.slug, args.quiet);
4249
4331
  try {
4250
4332
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4251
- if (existsSync6(claudeConfigPath)) {
4252
- const claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4333
+ if (existsSync7(claudeConfigPath)) {
4334
+ const claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4253
4335
  const mcpServers = claudeConfig.mcpServers ?? {};
4254
4336
  let cleaned = 0;
4255
4337
  for (const key of Object.keys(mcpServers)) {
@@ -4267,7 +4349,7 @@ async function runLaunch(flags, rawArgs) {
4267
4349
  }
4268
4350
  if (cleaned > 0) {
4269
4351
  claudeConfig.mcpServers = mcpServers;
4270
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4352
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4271
4353
  `, "utf-8");
4272
4354
  }
4273
4355
  }
@@ -4293,8 +4375,61 @@ async function runLaunch(flags, rawArgs) {
4293
4375
  ...parsedGroups.length > 0 ? { groups: parsedGroups } : {},
4294
4376
  messageMode
4295
4377
  };
4296
- writeFileSync6(join5(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
4378
+ writeFileSync7(join5(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
4297
4379
  `, "utf-8");
4380
+ const isResume = args.resume !== null || args.continueSession;
4381
+ const claudeSessionId = isResume ? undefined : randomUUID();
4382
+ let sessionTokenFilePath = null;
4383
+ let sessionTokenForCleanup = null;
4384
+ try {
4385
+ const { mintSessionToken: mintSessionToken2, TOKEN_FILE_ENV: TOKEN_FILE_ENV2 } = await Promise.resolve().then(() => (init_token(), exports_token));
4386
+ const minted = mintSessionToken2(tmpDir);
4387
+ sessionTokenFilePath = minted.filePath;
4388
+ sessionTokenForCleanup = minted.token;
4389
+ let presencePayload;
4390
+ try {
4391
+ const { generateKeypair: generateKeypair4 } = await Promise.resolve().then(() => (init_facade7(), exports_facade4));
4392
+ const { signParentAttestation: signParentAttestation2 } = await Promise.resolve().then(() => (init_session_hello_sig(), exports_session_hello_sig));
4393
+ const sessionKp = await generateKeypair4();
4394
+ const att = await signParentAttestation2({
4395
+ parentMemberPubkey: mesh.pubkey,
4396
+ parentSecretKey: mesh.secretKey,
4397
+ sessionPubkey: sessionKp.publicKey
4398
+ });
4399
+ presencePayload = {
4400
+ session_pubkey: sessionKp.publicKey,
4401
+ session_secret_key: sessionKp.secretKey,
4402
+ parent_attestation: {
4403
+ session_pubkey: att.sessionPubkey,
4404
+ parent_member_pubkey: att.parentMemberPubkey,
4405
+ expires_at: att.expiresAt,
4406
+ signature: att.signature
4407
+ }
4408
+ };
4409
+ } catch {}
4410
+ const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4411
+ const sessionIdForRegister = claudeSessionId ?? randomUUID();
4412
+ await ipc2({
4413
+ method: "POST",
4414
+ path: "/v1/sessions/register",
4415
+ timeoutMs: 3000,
4416
+ body: {
4417
+ token: minted.token,
4418
+ session_id: sessionIdForRegister,
4419
+ mesh: mesh.slug,
4420
+ display_name: displayName,
4421
+ pid: process.pid,
4422
+ cwd: process.cwd(),
4423
+ ...role ? { role } : {},
4424
+ ...parsedGroups.length > 0 ? { groups: parsedGroups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`) } : {},
4425
+ ...presencePayload ? { presence: presencePayload } : {}
4426
+ }
4427
+ }).catch(() => null);
4428
+ process._claudemeshTokenEnv = {
4429
+ name: TOKEN_FILE_ENV2,
4430
+ value: minted.filePath
4431
+ };
4432
+ } catch {}
4298
4433
  if (!args.quiet) {
4299
4434
  printBanner(displayName, mesh.slug, role, parsedGroups, messageMode);
4300
4435
  }
@@ -4303,7 +4438,7 @@ async function runLaunch(flags, rawArgs) {
4303
4438
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4304
4439
  let claudeConfig = {};
4305
4440
  try {
4306
- claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4441
+ claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4307
4442
  } catch {
4308
4443
  claudeConfig = {};
4309
4444
  }
@@ -4330,7 +4465,7 @@ async function runLaunch(flags, rawArgs) {
4330
4465
  meshMcpEntries.push({ key: entryKey, entry });
4331
4466
  }
4332
4467
  claudeConfig.mcpServers = mcpServers;
4333
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4468
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4334
4469
  `, "utf-8");
4335
4470
  if (!args.quiet && meshMcpEntries.length > 0) {
4336
4471
  console.log(` ${meshMcpEntries.length} mesh service(s) registered as native MCPs:`);
@@ -4351,8 +4486,6 @@ async function runLaunch(flags, rawArgs) {
4351
4486
  }
4352
4487
  filtered.push(args.claudeArgs[i]);
4353
4488
  }
4354
- const isResume = args.resume !== null || args.continueSession;
4355
- const claudeSessionId = isResume ? undefined : randomUUID();
4356
4489
  const claudeArgs = [
4357
4490
  "--dangerously-load-development-channels",
4358
4491
  "server:claudemesh",
@@ -4372,7 +4505,7 @@ async function runLaunch(flags, rawArgs) {
4372
4505
  join5(homedir3(), ".claude", "bin", "claude")
4373
4506
  ];
4374
4507
  for (const c of candidates) {
4375
- if (existsSync6(c)) {
4508
+ if (existsSync7(c)) {
4376
4509
  claudeBin = c;
4377
4510
  break;
4378
4511
  }
@@ -4382,13 +4515,13 @@ async function runLaunch(flags, rawArgs) {
4382
4515
  if (meshMcpEntries.length > 0) {
4383
4516
  try {
4384
4517
  const claudeConfigPath = join5(homedir3(), ".claude.json");
4385
- const claudeConfig = JSON.parse(readFileSync6(claudeConfigPath, "utf-8"));
4518
+ const claudeConfig = JSON.parse(readFileSync7(claudeConfigPath, "utf-8"));
4386
4519
  const mcpServers = claudeConfig.mcpServers ?? {};
4387
4520
  for (const { key } of meshMcpEntries) {
4388
4521
  delete mcpServers[key];
4389
4522
  }
4390
4523
  claudeConfig.mcpServers = mcpServers;
4391
- writeFileSync6(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4524
+ writeFileSync7(claudeConfigPath, JSON.stringify(claudeConfig, null, 2) + `
4392
4525
  `, "utf-8");
4393
4526
  } catch {}
4394
4527
  }
@@ -4418,6 +4551,7 @@ async function runLaunch(flags, rawArgs) {
4418
4551
  CLAUDEMESH_CONFIG_DIR: tmpDir,
4419
4552
  CLAUDEMESH_DISPLAY_NAME: displayName,
4420
4553
  ...claudeSessionId ? { CLAUDEMESH_SESSION_ID: claudeSessionId } : {},
4554
+ ...sessionTokenFilePath ? { CLAUDEMESH_IPC_TOKEN_FILE: sessionTokenFilePath } : {},
4421
4555
  MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
4422
4556
  MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
4423
4557
  ...role ? { CLAUDEMESH_ROLE: role } : {}
@@ -5085,7 +5219,7 @@ __export(exports_join, {
5085
5219
  runJoin: () => runJoin
5086
5220
  });
5087
5221
  import sodium3 from "libsodium-wrappers";
5088
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync4 } from "node:fs";
5222
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync4 } from "node:fs";
5089
5223
  import { join as join6, dirname as dirname3 } from "node:path";
5090
5224
  import { homedir as homedir4, hostname as hostname3 } from "node:os";
5091
5225
  function deriveAppBaseUrl() {
@@ -5210,7 +5344,7 @@ async function runJoin(args) {
5210
5344
  const inviteFile = join6(configDir, `invite-${payload.mesh_slug}.txt`);
5211
5345
  try {
5212
5346
  mkdirSync4(dirname3(inviteFile), { recursive: true });
5213
- writeFileSync7(inviteFile, link, "utf-8");
5347
+ writeFileSync8(inviteFile, link, "utf-8");
5214
5348
  } catch {}
5215
5349
  console.log("");
5216
5350
  console.log(`✓ Joined "${payload.mesh_slug}" as ${displayName}${enroll.alreadyMember ? " (already a member — re-enrolled with same pubkey)" : ""}`);
@@ -7660,6 +7794,45 @@ var init_daemon_route = __esm(() => {
7660
7794
  init_warnings();
7661
7795
  });
7662
7796
 
7797
+ // src/services/session/resolve.ts
7798
+ var exports_resolve = {};
7799
+ __export(exports_resolve, {
7800
+ getSessionInfo: () => getSessionInfo,
7801
+ _resetSessionCache: () => _resetSessionCache
7802
+ });
7803
+ async function getSessionInfo() {
7804
+ if (cached !== undefined)
7805
+ return cached;
7806
+ const tok = readSessionTokenFromEnv();
7807
+ if (!tok) {
7808
+ cached = null;
7809
+ return null;
7810
+ }
7811
+ try {
7812
+ const res = await ipc({
7813
+ path: "/v1/sessions/me",
7814
+ timeoutMs: 1500
7815
+ });
7816
+ if (res.status !== 200 || !res.body.session) {
7817
+ cached = null;
7818
+ return null;
7819
+ }
7820
+ cached = res.body.session;
7821
+ return cached;
7822
+ } catch {
7823
+ cached = null;
7824
+ return null;
7825
+ }
7826
+ }
7827
+ function _resetSessionCache() {
7828
+ cached = undefined;
7829
+ }
7830
+ var cached = undefined;
7831
+ var init_resolve = __esm(() => {
7832
+ init_client3();
7833
+ init_token();
7834
+ });
7835
+
7663
7836
  // src/commands/peers.ts
7664
7837
  var exports_peers = {};
7665
7838
  __export(exports_peers, {
@@ -7699,7 +7872,14 @@ function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
7699
7872
  }
7700
7873
  async function runPeers(flags) {
7701
7874
  const config = readConfig();
7702
- const slugs = flags.mesh ? [flags.mesh] : config.meshes.map((m) => m.slug);
7875
+ let slugs;
7876
+ if (flags.mesh) {
7877
+ slugs = [flags.mesh];
7878
+ } else {
7879
+ const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
7880
+ const sess = await getSessionInfo2();
7881
+ slugs = sess ? [sess.mesh] : config.meshes.map((m) => m.slug);
7882
+ }
7703
7883
  if (slugs.length === 0) {
7704
7884
  render.err("No meshes joined.");
7705
7885
  render.hint("claudemesh <invite-url> # join + launch");
@@ -8315,7 +8495,7 @@ async function runMsgStatus(id, opts) {
8315
8495
  console.log(JSON.stringify(result, null, 2));
8316
8496
  return EXIT.SUCCESS;
8317
8497
  }
8318
- render.section(`message ${id.slice(0, 12)}…`);
8498
+ render.section(`message ${lookupId.slice(0, 12)}…`);
8319
8499
  render.kv([
8320
8500
  ["target", result.targetSpec],
8321
8501
  ["delivered", result.delivered ? "yes" : "no"],
@@ -8669,12 +8849,12 @@ var init_whoami = __esm(() => {
8669
8849
  });
8670
8850
 
8671
8851
  // src/daemon/lock.ts
8672
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync7, unlinkSync as unlinkSync3, writeFileSync as writeFileSync8 } from "node:fs";
8852
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync8, unlinkSync as unlinkSync3, writeFileSync as writeFileSync9 } from "node:fs";
8673
8853
  import { dirname as dirname4 } from "node:path";
8674
8854
  function acquireSingletonLock() {
8675
8855
  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();
8856
+ if (existsSync8(DAEMON_PATHS.PID_FILE)) {
8857
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8678
8858
  const oldPid = Number.parseInt(raw, 10);
8679
8859
  if (Number.isFinite(oldPid) && oldPid > 0 && isProcessAlive(oldPid)) {
8680
8860
  return { result: "already-running", pid: oldPid };
@@ -8682,22 +8862,22 @@ function acquireSingletonLock() {
8682
8862
  try {
8683
8863
  unlinkSync3(DAEMON_PATHS.PID_FILE);
8684
8864
  } catch {}
8685
- writeFileSync8(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8865
+ writeFileSync9(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8686
8866
  return { result: "stale", pid: process.pid };
8687
8867
  }
8688
- writeFileSync8(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8868
+ writeFileSync9(DAEMON_PATHS.PID_FILE, String(process.pid), { mode: 384 });
8689
8869
  return { result: "acquired", pid: process.pid };
8690
8870
  }
8691
8871
  function releaseSingletonLock() {
8692
8872
  try {
8693
- const raw = readFileSync7(DAEMON_PATHS.PID_FILE, "utf8").trim();
8873
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8694
8874
  if (Number.parseInt(raw, 10) === process.pid)
8695
8875
  unlinkSync3(DAEMON_PATHS.PID_FILE);
8696
8876
  } catch {}
8697
8877
  }
8698
8878
  function readRunningPid() {
8699
8879
  try {
8700
- const raw = readFileSync7(DAEMON_PATHS.PID_FILE, "utf8").trim();
8880
+ const raw = readFileSync8(DAEMON_PATHS.PID_FILE, "utf8").trim();
8701
8881
  const pid = Number.parseInt(raw, 10);
8702
8882
  if (Number.isFinite(pid) && pid > 0 && isProcessAlive(pid))
8703
8883
  return pid;
@@ -8843,17 +9023,17 @@ function requeueDeadOrPending(db, args) {
8843
9023
 
8844
9024
  // src/daemon/db/sqlite.ts
8845
9025
  async function loadSqlite() {
8846
- if (cached)
8847
- return cached;
9026
+ if (cached2)
9027
+ return cached2;
8848
9028
  try {
8849
9029
  const mod = await import("node:sqlite");
8850
- cached = mod.DatabaseSync;
8851
- return cached;
9030
+ cached2 = mod.DatabaseSync;
9031
+ return cached2;
8852
9032
  } catch (nodeErr) {
8853
9033
  try {
8854
9034
  const bunMod = await import("bun:sqlite");
8855
- cached = bunMod.Database;
8856
- return cached;
9035
+ cached2 = bunMod.Database;
9036
+ return cached2;
8857
9037
  } catch {
8858
9038
  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
9039
  throw new Error(msg);
@@ -8884,7 +9064,7 @@ function inImmediateTx(db, fn) {
8884
9064
  throw err;
8885
9065
  }
8886
9066
  }
8887
- var cached = null;
9067
+ var cached2 = null;
8888
9068
 
8889
9069
  // src/daemon/fingerprint.ts
8890
9070
  import { createHash } from "node:crypto";
@@ -9127,9 +9307,88 @@ function bindSseStream(res, bus) {
9127
9307
  return cleanup;
9128
9308
  }
9129
9309
 
9310
+ // src/daemon/session-registry.ts
9311
+ function startReaper() {
9312
+ if (reaperHandle)
9313
+ return;
9314
+ reaperHandle = setInterval(reapDead, REAPER_INTERVAL_MS).unref?.() ?? reaperHandle;
9315
+ }
9316
+ function setRegistryHooks(next) {
9317
+ hooks.onRegister = next.onRegister;
9318
+ hooks.onDeregister = next.onDeregister;
9319
+ }
9320
+ function registerSession(info) {
9321
+ const priorToken = bySessionId.get(info.sessionId);
9322
+ if (priorToken && priorToken !== info.token) {
9323
+ const prior = byToken.get(priorToken);
9324
+ if (prior) {
9325
+ byToken.delete(priorToken);
9326
+ try {
9327
+ hooks.onDeregister?.(prior);
9328
+ } catch {}
9329
+ }
9330
+ }
9331
+ const stored = { ...info, registeredAt: Date.now() };
9332
+ byToken.set(info.token, stored);
9333
+ bySessionId.set(info.sessionId, info.token);
9334
+ try {
9335
+ hooks.onRegister?.(stored);
9336
+ } catch {}
9337
+ return stored;
9338
+ }
9339
+ function deregisterByToken(token) {
9340
+ const entry = byToken.get(token);
9341
+ if (!entry)
9342
+ return false;
9343
+ byToken.delete(token);
9344
+ if (bySessionId.get(entry.sessionId) === token)
9345
+ bySessionId.delete(entry.sessionId);
9346
+ try {
9347
+ hooks.onDeregister?.(entry);
9348
+ } catch {}
9349
+ return true;
9350
+ }
9351
+ function resolveToken(token) {
9352
+ const entry = byToken.get(token);
9353
+ if (!entry)
9354
+ return null;
9355
+ if (Date.now() - entry.registeredAt > TTL_MS) {
9356
+ deregisterByToken(token);
9357
+ return null;
9358
+ }
9359
+ return entry;
9360
+ }
9361
+ function listSessions() {
9362
+ return [...byToken.values()];
9363
+ }
9364
+ function reapDead() {
9365
+ const dead = [];
9366
+ for (const [token, info] of byToken.entries()) {
9367
+ if (Date.now() - info.registeredAt > TTL_MS) {
9368
+ dead.push(token);
9369
+ continue;
9370
+ }
9371
+ try {
9372
+ process.kill(info.pid, 0);
9373
+ } catch {
9374
+ dead.push(token);
9375
+ }
9376
+ }
9377
+ for (const t of dead)
9378
+ deregisterByToken(t);
9379
+ }
9380
+ var TTL_MS, REAPER_INTERVAL_MS, byToken, bySessionId, hooks, reaperHandle = null;
9381
+ var init_session_registry = __esm(() => {
9382
+ TTL_MS = 24 * 60 * 60 * 1000;
9383
+ REAPER_INTERVAL_MS = 30 * 1000;
9384
+ byToken = new Map;
9385
+ bySessionId = new Map;
9386
+ hooks = {};
9387
+ });
9388
+
9130
9389
  // src/daemon/ipc/server.ts
9131
9390
  import { createServer as createServer2 } from "node:http";
9132
- import { chmodSync as chmodSync3, existsSync as existsSync8, unlinkSync as unlinkSync4 } from "node:fs";
9391
+ import { chmodSync as chmodSync3, existsSync as existsSync9, unlinkSync as unlinkSync4 } from "node:fs";
9133
9392
  import { timingSafeEqual } from "node:crypto";
9134
9393
  import { randomUUID as randomUUID3 } from "node:crypto";
9135
9394
  function startIpcServer(opts) {
@@ -9145,7 +9404,7 @@ function startIpcServer(opts) {
9145
9404
  meshConfigs: opts.meshConfigs,
9146
9405
  onPendingInserted: opts.onPendingInserted
9147
9406
  });
9148
- if (existsSync8(DAEMON_PATHS.SOCK_FILE)) {
9407
+ if (existsSync9(DAEMON_PATHS.SOCK_FILE)) {
9149
9408
  try {
9150
9409
  unlinkSync4(DAEMON_PATHS.SOCK_FILE);
9151
9410
  } catch {}
@@ -9231,11 +9490,19 @@ function makeHandler(opts) {
9231
9490
  return;
9232
9491
  }
9233
9492
  }
9493
+ let session = null;
9494
+ {
9495
+ const authz = req.headers.authorization ?? "";
9496
+ const sm = /^ClaudeMesh-Session\s+([0-9a-f]{64})$/i.exec(authz.trim());
9497
+ if (sm && sm[1])
9498
+ session = resolveToken(sm[1].toLowerCase());
9499
+ }
9500
+ const meshFromCtx = (explicit) => explicit && explicit.trim() ? explicit : session?.mesh ?? null;
9234
9501
  if (req.method === "GET" && url.pathname === "/v1/version") {
9235
9502
  respond(res, 200, {
9236
9503
  daemon_version: VERSION,
9237
9504
  ipc_api: "v1",
9238
- ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills", "state", "memory"],
9505
+ ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills", "state", "memory", "sessions"],
9239
9506
  schema_version: 1
9240
9507
  });
9241
9508
  return;
@@ -9244,6 +9511,102 @@ function makeHandler(opts) {
9244
9511
  respond(res, 200, { ok: true, pid: process.pid });
9245
9512
  return;
9246
9513
  }
9514
+ if (req.method === "POST" && url.pathname === "/v1/sessions/register") {
9515
+ try {
9516
+ const body = await readJsonBody(req, 64 * 1024);
9517
+ if (!body) {
9518
+ respond(res, 400, { error: "missing body" });
9519
+ return;
9520
+ }
9521
+ const token = typeof body.token === "string" ? body.token : "";
9522
+ if (!/^[0-9a-f]{64}$/i.test(token)) {
9523
+ respond(res, 400, { error: "token must be 64 hex chars" });
9524
+ return;
9525
+ }
9526
+ const sessionId = typeof body.session_id === "string" ? body.session_id : "";
9527
+ const mesh = typeof body.mesh === "string" ? body.mesh : "";
9528
+ const displayName = typeof body.display_name === "string" ? body.display_name : "";
9529
+ const pid = typeof body.pid === "number" ? body.pid : 0;
9530
+ if (!sessionId || !mesh || !displayName || !pid) {
9531
+ respond(res, 400, { error: "session_id, mesh, display_name, pid all required" });
9532
+ return;
9533
+ }
9534
+ const cwd = typeof body.cwd === "string" ? body.cwd : undefined;
9535
+ const role = typeof body.role === "string" ? body.role : undefined;
9536
+ const groups = Array.isArray(body.groups) ? body.groups.filter((g) => typeof g === "string") : undefined;
9537
+ let presence;
9538
+ const rawPresence = body.presence;
9539
+ if (rawPresence && typeof rawPresence === "object") {
9540
+ const p = rawPresence;
9541
+ const sessionPubkey = typeof p.session_pubkey === "string" ? p.session_pubkey.toLowerCase() : "";
9542
+ const sessionSecretKey = typeof p.session_secret_key === "string" ? p.session_secret_key.toLowerCase() : "";
9543
+ const att = p.parent_attestation;
9544
+ if (/^[0-9a-f]{64}$/.test(sessionPubkey) && /^[0-9a-f]{128}$/.test(sessionSecretKey) && att && typeof att === "object" && typeof att.session_pubkey === "string" && typeof att.parent_member_pubkey === "string" && typeof att.expires_at === "number" && typeof att.signature === "string") {
9545
+ presence = {
9546
+ sessionPubkey,
9547
+ sessionSecretKey,
9548
+ parentAttestation: {
9549
+ sessionPubkey: att.session_pubkey.toLowerCase(),
9550
+ parentMemberPubkey: att.parent_member_pubkey.toLowerCase(),
9551
+ expiresAt: att.expires_at,
9552
+ signature: att.signature.toLowerCase()
9553
+ }
9554
+ };
9555
+ } else {
9556
+ opts.log("warn", "session_register_presence_malformed", { mesh });
9557
+ }
9558
+ }
9559
+ const stored = registerSession({
9560
+ token: token.toLowerCase(),
9561
+ sessionId,
9562
+ mesh,
9563
+ displayName,
9564
+ pid,
9565
+ cwd,
9566
+ role,
9567
+ groups,
9568
+ ...presence ? { presence } : {}
9569
+ });
9570
+ opts.log("info", "session_registered", {
9571
+ sessionId,
9572
+ mesh,
9573
+ pid,
9574
+ presence: presence ? "yes" : "no"
9575
+ });
9576
+ respond(res, 200, {
9577
+ ok: true,
9578
+ registered_at: stored.registeredAt,
9579
+ presence_accepted: !!presence
9580
+ });
9581
+ } catch (e) {
9582
+ respond(res, 400, { error: String(e) });
9583
+ }
9584
+ return;
9585
+ }
9586
+ if (req.method === "DELETE" && url.pathname.startsWith("/v1/sessions/")) {
9587
+ const tail = url.pathname.slice("/v1/sessions/".length);
9588
+ if (!/^[0-9a-f]{64}$/i.test(tail)) {
9589
+ respond(res, 400, { error: "invalid token" });
9590
+ return;
9591
+ }
9592
+ const ok = deregisterByToken(tail.toLowerCase());
9593
+ respond(res, ok ? 200 : 404, { ok, token_prefix: tail.slice(0, 8) });
9594
+ return;
9595
+ }
9596
+ if (req.method === "GET" && url.pathname === "/v1/sessions/me") {
9597
+ if (!session) {
9598
+ respond(res, 401, { error: "no session token" });
9599
+ return;
9600
+ }
9601
+ const { token, ...redacted } = session;
9602
+ respond(res, 200, { session: { ...redacted, token_prefix: token.slice(0, 8) } });
9603
+ return;
9604
+ }
9605
+ if (req.method === "GET" && url.pathname === "/v1/sessions") {
9606
+ const all = listSessions().map(({ token, ...rest }) => ({ ...rest, token_prefix: token.slice(0, 8) }));
9607
+ respond(res, 200, { sessions: all });
9608
+ return;
9609
+ }
9247
9610
  if (req.method === "GET" && url.pathname === "/v1/events") {
9248
9611
  if (!opts.bus) {
9249
9612
  respond(res, 503, { error: "event bus not initialised" });
@@ -9257,7 +9620,7 @@ function makeHandler(opts) {
9257
9620
  respond(res, 503, { error: "broker not initialised" });
9258
9621
  return;
9259
9622
  }
9260
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9623
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9261
9624
  try {
9262
9625
  const all = [];
9263
9626
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9282,7 +9645,7 @@ function makeHandler(opts) {
9282
9645
  respond(res, 503, { error: "broker not initialised" });
9283
9646
  return;
9284
9647
  }
9285
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9648
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9286
9649
  const key = url.searchParams.get("key");
9287
9650
  try {
9288
9651
  if (key) {
@@ -9323,7 +9686,7 @@ function makeHandler(opts) {
9323
9686
  respond(res, 400, { error: "missing 'key' (string)" });
9324
9687
  return;
9325
9688
  }
9326
- const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
9689
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : null);
9327
9690
  let chosen = requested;
9328
9691
  if (!chosen && opts.brokers.size === 1)
9329
9692
  chosen = opts.brokers.keys().next().value;
@@ -9349,7 +9712,7 @@ function makeHandler(opts) {
9349
9712
  return;
9350
9713
  }
9351
9714
  const query = url.searchParams.get("q") ?? "";
9352
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9715
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9353
9716
  try {
9354
9717
  const all = [];
9355
9718
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9376,7 +9739,7 @@ function makeHandler(opts) {
9376
9739
  respond(res, 400, { error: "missing 'content' (string)" });
9377
9740
  return;
9378
9741
  }
9379
- const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
9742
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : null);
9380
9743
  let chosen = requested;
9381
9744
  if (!chosen && opts.brokers.size === 1)
9382
9745
  chosen = opts.brokers.keys().next().value;
@@ -9434,7 +9797,7 @@ function makeHandler(opts) {
9434
9797
  return;
9435
9798
  }
9436
9799
  const query = url.searchParams.get("query") ?? undefined;
9437
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9800
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9438
9801
  try {
9439
9802
  const all = [];
9440
9803
  for (const [slug, b] of opts.brokers.entries()) {
@@ -9464,7 +9827,7 @@ function makeHandler(opts) {
9464
9827
  respond(res, 400, { error: "missing skill name" });
9465
9828
  return;
9466
9829
  }
9467
- const filterMesh = url.searchParams.get("mesh") ?? undefined;
9830
+ const filterMesh = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
9468
9831
  try {
9469
9832
  for (const [slug, b] of opts.brokers.entries()) {
9470
9833
  if (filterMesh && filterMesh !== slug)
@@ -9492,7 +9855,7 @@ function makeHandler(opts) {
9492
9855
  respond(res, 400, { error: "expected JSON object" });
9493
9856
  return;
9494
9857
  }
9495
- const requested = (typeof body.mesh === "string" ? body.mesh : url.searchParams.get("mesh")) || null;
9858
+ const requested = meshFromCtx(typeof body.mesh === "string" ? body.mesh : url.searchParams.get("mesh"));
9496
9859
  const targets = requested ? [opts.brokers.get(requested)].filter(Boolean) : [...opts.brokers.values()];
9497
9860
  if (targets.length === 0) {
9498
9861
  respond(res, 404, { error: "mesh_not_attached", mesh: requested });
@@ -9776,16 +10139,16 @@ function parseSendRequest(body, idempotencyHeader) {
9776
10139
  }
9777
10140
  async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
9778
10141
  const { encryptDirect: encryptDirect2 } = await Promise.resolve().then(() => (init_box(), exports_box));
9779
- const { randomBytes: randomBytes5 } = await import("node:crypto");
10142
+ const { randomBytes: randomBytes6 } = await import("node:crypto");
9780
10143
  const to = req.to.trim();
9781
10144
  if (to.startsWith("#") && /^#[0-9a-z_-]{20,}$/i.test(to)) {
9782
10145
  const ciphertext = Buffer.from(req.message, "utf8").toString("base64");
9783
- const nonce = randomBytes5(24).toString("base64");
10146
+ const nonce = randomBytes6(24).toString("base64");
9784
10147
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
9785
10148
  }
9786
10149
  if (to.startsWith("@") || to === "*") {
9787
10150
  const ciphertext = Buffer.from(req.message, "utf8").toString("base64");
9788
- const nonce = randomBytes5(24).toString("base64");
10151
+ const nonce = randomBytes6(24).toString("base64");
9789
10152
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
9790
10153
  }
9791
10154
  if (/^[0-9a-f]{64}$/i.test(to)) {
@@ -9826,6 +10189,7 @@ function respond(res, status, body) {
9826
10189
  var init_server = __esm(() => {
9827
10190
  init_paths2();
9828
10191
  init_send2();
10192
+ init_session_registry();
9829
10193
  init_urls();
9830
10194
  });
9831
10195
 
@@ -10314,9 +10678,168 @@ var init_broker = __esm(() => {
10314
10678
  BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
10315
10679
  });
10316
10680
 
10681
+ // src/daemon/session-broker.ts
10682
+ import { hostname as osHostname } from "node:os";
10683
+ import WebSocket3 from "ws";
10684
+
10685
+ class SessionBrokerClient {
10686
+ opts;
10687
+ ws = null;
10688
+ _status = "closed";
10689
+ closed = false;
10690
+ reconnectAttempt = 0;
10691
+ reconnectTimer = null;
10692
+ helloTimer = null;
10693
+ constructor(opts) {
10694
+ this.opts = opts;
10695
+ }
10696
+ get status() {
10697
+ return this._status;
10698
+ }
10699
+ get meshSlug() {
10700
+ return this.opts.mesh.slug;
10701
+ }
10702
+ get sessionPubkey() {
10703
+ return this.opts.sessionPubkey;
10704
+ }
10705
+ log = (level, msg, meta) => {
10706
+ (this.opts.log ?? defaultLog2)(level, msg, {
10707
+ mesh: this.opts.mesh.slug,
10708
+ session_pubkey: this.opts.sessionPubkey.slice(0, 12),
10709
+ ...meta
10710
+ });
10711
+ };
10712
+ setStatus(s) {
10713
+ if (this._status === s)
10714
+ return;
10715
+ this._status = s;
10716
+ this.opts.onStatusChange?.(s);
10717
+ }
10718
+ async connect() {
10719
+ if (this.closed)
10720
+ throw new Error("client_closed");
10721
+ if (this._status === "connecting" || this._status === "open")
10722
+ return;
10723
+ this.setStatus("connecting");
10724
+ const ws = new WebSocket3(this.opts.mesh.brokerUrl);
10725
+ this.ws = ws;
10726
+ return new Promise((resolve, reject) => {
10727
+ ws.on("open", async () => {
10728
+ try {
10729
+ const { timestamp: timestamp2, signature } = await signSessionHello({
10730
+ meshId: this.opts.mesh.meshId,
10731
+ parentMemberPubkey: this.opts.mesh.pubkey,
10732
+ sessionPubkey: this.opts.sessionPubkey,
10733
+ sessionSecretKey: this.opts.sessionSecretKey
10734
+ });
10735
+ ws.send(JSON.stringify({
10736
+ type: "session_hello",
10737
+ meshId: this.opts.mesh.meshId,
10738
+ parentMemberId: this.opts.mesh.memberId,
10739
+ parentMemberPubkey: this.opts.mesh.pubkey,
10740
+ sessionPubkey: this.opts.sessionPubkey,
10741
+ parentAttestation: this.opts.parentAttestation,
10742
+ displayName: this.opts.displayName,
10743
+ sessionId: this.opts.sessionId,
10744
+ pid: this.opts.pid,
10745
+ cwd: this.opts.cwd ?? process.cwd(),
10746
+ hostname: osHostname(),
10747
+ peerType: "ai",
10748
+ channel: "claudemesh-session",
10749
+ ...this.opts.groups && this.opts.groups.length > 0 ? { groups: this.opts.groups } : {},
10750
+ ...this.opts.role ? { role: this.opts.role } : {},
10751
+ timestamp: timestamp2,
10752
+ signature
10753
+ }));
10754
+ this.helloTimer = setTimeout(() => {
10755
+ this.log("warn", "session_hello_ack_timeout");
10756
+ try {
10757
+ ws.close();
10758
+ } catch {}
10759
+ reject(new Error("session_hello_ack_timeout"));
10760
+ }, HELLO_ACK_TIMEOUT_MS3);
10761
+ } catch (e) {
10762
+ reject(e instanceof Error ? e : new Error(String(e)));
10763
+ }
10764
+ });
10765
+ ws.on("message", (raw) => {
10766
+ let msg;
10767
+ try {
10768
+ msg = JSON.parse(raw.toString());
10769
+ } catch {
10770
+ return;
10771
+ }
10772
+ if (msg.type === "hello_ack") {
10773
+ if (this.helloTimer)
10774
+ clearTimeout(this.helloTimer);
10775
+ this.helloTimer = null;
10776
+ this.setStatus("open");
10777
+ this.reconnectAttempt = 0;
10778
+ resolve();
10779
+ return;
10780
+ }
10781
+ if (msg.type === "error") {
10782
+ this.log("warn", "broker_error", { code: msg.code, message: msg.message });
10783
+ if (msg.code === "unknown_message_type") {
10784
+ this.closed = true;
10785
+ }
10786
+ return;
10787
+ }
10788
+ });
10789
+ ws.on("close", (code, reason) => {
10790
+ if (this.helloTimer) {
10791
+ clearTimeout(this.helloTimer);
10792
+ this.helloTimer = null;
10793
+ }
10794
+ if (this.closed) {
10795
+ this.setStatus("closed");
10796
+ return;
10797
+ }
10798
+ this.setStatus("reconnecting");
10799
+ const wait = BACKOFF_CAPS_MS2[Math.min(this.reconnectAttempt, BACKOFF_CAPS_MS2.length - 1)] ?? 30000;
10800
+ this.reconnectAttempt++;
10801
+ this.log("info", "session_broker_reconnect_scheduled", { wait_ms: wait, code, reason: reason.toString("utf8") });
10802
+ this.reconnectTimer = setTimeout(() => this.connect().catch((err) => this.log("warn", "session_broker_reconnect_failed", { err: String(err) })), wait);
10803
+ if (this._status === "connecting")
10804
+ reject(new Error(`closed_before_hello_${code}`));
10805
+ });
10806
+ ws.on("error", (err) => this.log("warn", "session_broker_ws_error", { err: err.message }));
10807
+ });
10808
+ }
10809
+ async close() {
10810
+ this.closed = true;
10811
+ if (this.reconnectTimer) {
10812
+ clearTimeout(this.reconnectTimer);
10813
+ this.reconnectTimer = null;
10814
+ }
10815
+ if (this.helloTimer) {
10816
+ clearTimeout(this.helloTimer);
10817
+ this.helloTimer = null;
10818
+ }
10819
+ try {
10820
+ this.ws?.close();
10821
+ } catch {}
10822
+ this.setStatus("closed");
10823
+ }
10824
+ }
10825
+ function defaultLog2(level, msg, meta) {
10826
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10827
+ if (level === "info")
10828
+ process.stdout.write(line + `
10829
+ `);
10830
+ else
10831
+ process.stderr.write(line + `
10832
+ `);
10833
+ }
10834
+ var HELLO_ACK_TIMEOUT_MS3 = 5000, BACKOFF_CAPS_MS2;
10835
+ var init_session_broker = __esm(() => {
10836
+ init_session_hello_sig();
10837
+ BACKOFF_CAPS_MS2 = [1000, 2000, 4000, 8000, 16000, 30000];
10838
+ });
10839
+
10317
10840
  // src/daemon/drain.ts
10318
10841
  function startDrainWorker(opts) {
10319
- const log2 = opts.log ?? defaultLog2;
10842
+ const log2 = opts.log ?? defaultLog3;
10320
10843
  let stopped = false;
10321
10844
  let wakeResolve = null;
10322
10845
  let wakePromise = new Promise((r) => {
@@ -10457,10 +10980,10 @@ function bufferToHex(b) {
10457
10980
  return s;
10458
10981
  }
10459
10982
  async function randomNonce2() {
10460
- const { randomBytes: randomBytes5 } = await import("node:crypto");
10461
- return randomBytes5(24).toString("base64");
10983
+ const { randomBytes: randomBytes6 } = await import("node:crypto");
10984
+ return randomBytes6(24).toString("base64");
10462
10985
  }
10463
- function defaultLog2(level, msg, meta) {
10986
+ function defaultLog3(level, msg, meta) {
10464
10987
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10465
10988
  if (level === "info")
10466
10989
  process.stdout.write(line + `
@@ -10580,7 +11103,7 @@ __export(exports_identity, {
10580
11103
  checkFingerprint: () => checkFingerprint,
10581
11104
  acceptCurrentHost: () => acceptCurrentHost
10582
11105
  });
10583
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "node:fs";
11106
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "node:fs";
10584
11107
  import { join as join7 } from "node:path";
10585
11108
  import { createHash as createHash2 } from "node:crypto";
10586
11109
  import { networkInterfaces } from "node:os";
@@ -10601,13 +11124,13 @@ function computeCurrentFingerprint() {
10601
11124
  }
10602
11125
  function checkFingerprint() {
10603
11126
  const current = computeCurrentFingerprint();
10604
- if (!existsSync9(path())) {
10605
- writeFileSync9(path(), JSON.stringify(current, null, 2), { mode: 384 });
11127
+ if (!existsSync10(path())) {
11128
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
10606
11129
  return { result: "first_run", current };
10607
11130
  }
10608
11131
  let stored;
10609
11132
  try {
10610
- stored = JSON.parse(readFileSync8(path(), "utf8"));
11133
+ stored = JSON.parse(readFileSync9(path(), "utf8"));
10611
11134
  } catch {
10612
11135
  return { result: "unavailable", current };
10613
11136
  }
@@ -10617,14 +11140,14 @@ function checkFingerprint() {
10617
11140
  }
10618
11141
  function acceptCurrentHost() {
10619
11142
  const current = computeCurrentFingerprint();
10620
- writeFileSync9(path(), JSON.stringify(current, null, 2), { mode: 384 });
11143
+ writeFileSync10(path(), JSON.stringify(current, null, 2), { mode: 384 });
10621
11144
  return current;
10622
11145
  }
10623
11146
  function readHostId() {
10624
11147
  if (process.platform === "linux") {
10625
11148
  for (const p of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
10626
11149
  try {
10627
- const raw = readFileSync8(p, "utf8").trim();
11150
+ const raw = readFileSync9(p, "utf8").trim();
10628
11151
  if (raw)
10629
11152
  return `linux:${raw}`;
10630
11153
  } catch {}
@@ -10669,16 +11192,16 @@ var init_identity = __esm(() => {
10669
11192
  });
10670
11193
 
10671
11194
  // src/daemon/run.ts
10672
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync9 } from "node:fs";
11195
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10 } from "node:fs";
10673
11196
  function detectContainer() {
10674
11197
  if (process.env.KUBERNETES_SERVICE_HOST)
10675
11198
  return true;
10676
11199
  if (process.env.CONTAINER === "1")
10677
11200
  return true;
10678
11201
  try {
10679
- if (existsSync10("/.dockerenv"))
11202
+ if (existsSync11("/.dockerenv"))
10680
11203
  return true;
10681
- const cg = readFileSync9("/proc/1/cgroup", "utf8");
11204
+ const cg = readFileSync10("/proc/1/cgroup", "utf8");
10682
11205
  if (/(docker|kubepods|containerd)/.test(cg))
10683
11206
  return true;
10684
11207
  } catch {}
@@ -10803,6 +11326,57 @@ async function runDaemon(opts = {}) {
10803
11326
  }
10804
11327
  let drain = null;
10805
11328
  drain = startDrainWorker({ db: outboxDb, brokers });
11329
+ const sessionBrokers = new Map;
11330
+ setRegistryHooks({
11331
+ onRegister: (info) => {
11332
+ if (!info.presence)
11333
+ return;
11334
+ const meshConfig = meshConfigs.get(info.mesh);
11335
+ if (!meshConfig) {
11336
+ process.stderr.write(JSON.stringify({
11337
+ level: "warn",
11338
+ msg: "session_broker_no_mesh_config",
11339
+ mesh: info.mesh,
11340
+ ts: new Date().toISOString()
11341
+ }) + `
11342
+ `);
11343
+ return;
11344
+ }
11345
+ const prior = sessionBrokers.get(info.token);
11346
+ if (prior) {
11347
+ sessionBrokers.delete(info.token);
11348
+ prior.close().catch(() => {});
11349
+ }
11350
+ const client = new SessionBrokerClient({
11351
+ mesh: meshConfig,
11352
+ sessionPubkey: info.presence.sessionPubkey,
11353
+ sessionSecretKey: info.presence.sessionSecretKey,
11354
+ parentAttestation: info.presence.parentAttestation,
11355
+ sessionId: info.sessionId,
11356
+ displayName: info.displayName,
11357
+ ...info.role ? { role: info.role } : {},
11358
+ ...info.cwd ? { cwd: info.cwd } : {},
11359
+ pid: info.pid
11360
+ });
11361
+ sessionBrokers.set(info.token, client);
11362
+ client.connect().catch((err) => process.stderr.write(JSON.stringify({
11363
+ level: "warn",
11364
+ msg: "session_broker_connect_failed",
11365
+ mesh: info.mesh,
11366
+ err: String(err),
11367
+ ts: new Date().toISOString()
11368
+ }) + `
11369
+ `));
11370
+ },
11371
+ onDeregister: (info) => {
11372
+ const client = sessionBrokers.get(info.token);
11373
+ if (!client)
11374
+ return;
11375
+ sessionBrokers.delete(info.token);
11376
+ client.close().catch(() => {});
11377
+ }
11378
+ });
11379
+ startReaper();
10806
11380
  const ipc2 = startIpcServer({
10807
11381
  localToken,
10808
11382
  tcpEnabled,
@@ -10845,6 +11419,12 @@ async function runDaemon(opts = {}) {
10845
11419
  await b.close();
10846
11420
  } catch {}
10847
11421
  }
11422
+ for (const b of sessionBrokers.values()) {
11423
+ try {
11424
+ await b.close();
11425
+ } catch {}
11426
+ }
11427
+ sessionBrokers.clear();
10848
11428
  await ipc2.close();
10849
11429
  try {
10850
11430
  outboxDb.close();
@@ -10864,7 +11444,9 @@ var init_run = __esm(() => {
10864
11444
  init_lock();
10865
11445
  init_local_token();
10866
11446
  init_server();
11447
+ init_session_registry();
10867
11448
  init_broker();
11449
+ init_session_broker();
10868
11450
  init_drain();
10869
11451
  init_inbound();
10870
11452
  init_identity();
@@ -10879,7 +11461,7 @@ __export(exports_service_install, {
10879
11461
  installService: () => installService,
10880
11462
  detectPlatform: () => detectPlatform
10881
11463
  });
10882
- import { existsSync as existsSync11, mkdirSync as mkdirSync7, writeFileSync as writeFileSync10, unlinkSync as unlinkSync5, readFileSync as readFileSync10 } from "node:fs";
11464
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync11, unlinkSync as unlinkSync5, readFileSync as readFileSync11 } from "node:fs";
10883
11465
  import { execSync as execSync2 } from "node:child_process";
10884
11466
  import { homedir as homedir5 } from "node:os";
10885
11467
  import { join as join8, dirname as dirname5 } from "node:path";
@@ -10900,7 +11482,7 @@ function installService(args) {
10900
11482
  if (isCi() && !args.allowCi) {
10901
11483
  throw new Error("Refusing to install persistent service in CI; pass --allow-ci-persistent to override.");
10902
11484
  }
10903
- if (!existsSync11(args.binaryPath)) {
11485
+ if (!existsSync12(args.binaryPath)) {
10904
11486
  throw new Error(`binary not found at ${args.binaryPath}`);
10905
11487
  }
10906
11488
  mkdirSync7(DAEMON_PATHS.DAEMON_DIR, { recursive: true, mode: 448 });
@@ -10916,7 +11498,7 @@ function uninstallService() {
10916
11498
  try {
10917
11499
  execSync2(`launchctl bootout gui/$(id -u)/${SERVICE_LABEL}`, { stdio: "ignore" });
10918
11500
  } catch {}
10919
- if (existsSync11(p)) {
11501
+ if (existsSync12(p)) {
10920
11502
  unlinkSync5(p);
10921
11503
  removed.push(p);
10922
11504
  }
@@ -10925,7 +11507,7 @@ function uninstallService() {
10925
11507
  try {
10926
11508
  execSync2(`systemctl --user disable --now ${SYSTEMD_UNIT}`, { stdio: "ignore" });
10927
11509
  } catch {}
10928
- if (existsSync11(p)) {
11510
+ if (existsSync12(p)) {
10929
11511
  unlinkSync5(p);
10930
11512
  removed.push(p);
10931
11513
  }
@@ -10978,7 +11560,7 @@ function installDarwin(args) {
10978
11560
  </dict>
10979
11561
  </plist>
10980
11562
  `;
10981
- writeFileSync10(plist, xml, { mode: 420 });
11563
+ writeFileSync11(plist, xml, { mode: 420 });
10982
11564
  return {
10983
11565
  platform: "darwin",
10984
11566
  unitPath: plist,
@@ -11015,7 +11597,7 @@ Environment=PATH=/usr/local/bin:/usr/bin:/bin
11015
11597
  [Install]
11016
11598
  WantedBy=default.target
11017
11599
  `;
11018
- writeFileSync10(unit, content, { mode: 420 });
11600
+ writeFileSync11(unit, content, { mode: 420 });
11019
11601
  return {
11020
11602
  platform: "linux",
11021
11603
  unitPath: unit,
@@ -11035,10 +11617,10 @@ function readInstalledUnit() {
11035
11617
  if (!platform5)
11036
11618
  return { platform: null, path: null, content: null };
11037
11619
  const path2 = platform5 === "darwin" ? darwinPlistPath() : linuxUnitPath();
11038
- if (!existsSync11(path2))
11620
+ if (!existsSync12(path2))
11039
11621
  return { platform: platform5, path: null, content: null };
11040
11622
  try {
11041
- return { platform: platform5, path: path2, content: readFileSync10(path2, "utf8") };
11623
+ return { platform: platform5, path: path2, content: readFileSync11(path2, "utf8") };
11042
11624
  } catch {
11043
11625
  return { platform: platform5, path: path2, content: null };
11044
11626
  }
@@ -11380,19 +11962,19 @@ __export(exports_install, {
11380
11962
  import {
11381
11963
  chmodSync as chmodSync4,
11382
11964
  copyFileSync,
11383
- existsSync as existsSync12,
11965
+ existsSync as existsSync13,
11384
11966
  mkdirSync as mkdirSync8,
11385
- readFileSync as readFileSync11,
11386
- writeFileSync as writeFileSync11
11967
+ readFileSync as readFileSync12,
11968
+ writeFileSync as writeFileSync12
11387
11969
  } from "node:fs";
11388
11970
  import { homedir as homedir6, platform as platform5 } from "node:os";
11389
11971
  import { dirname as dirname6, join as join9, resolve } from "node:path";
11390
11972
  import { fileURLToPath } from "node:url";
11391
11973
  import { spawnSync as spawnSync3 } from "node:child_process";
11392
11974
  function readClaudeConfig() {
11393
- if (!existsSync12(CLAUDE_CONFIG))
11975
+ if (!existsSync13(CLAUDE_CONFIG))
11394
11976
  return {};
11395
- const text = readFileSync11(CLAUDE_CONFIG, "utf-8").trim();
11977
+ const text = readFileSync12(CLAUDE_CONFIG, "utf-8").trim();
11396
11978
  if (!text)
11397
11979
  return {};
11398
11980
  try {
@@ -11402,7 +11984,7 @@ function readClaudeConfig() {
11402
11984
  }
11403
11985
  }
11404
11986
  function backupClaudeConfig() {
11405
- if (!existsSync12(CLAUDE_CONFIG))
11987
+ if (!existsSync13(CLAUDE_CONFIG))
11406
11988
  return;
11407
11989
  const backupDir = join9(dirname6(CLAUDE_CONFIG), ".claude", "backups");
11408
11990
  mkdirSync8(backupDir, { recursive: true });
@@ -11431,7 +12013,7 @@ function patchMcpServer(entry) {
11431
12013
  return action;
11432
12014
  }
11433
12015
  function removeMcpServer() {
11434
- if (!existsSync12(CLAUDE_CONFIG))
12016
+ if (!existsSync13(CLAUDE_CONFIG))
11435
12017
  return false;
11436
12018
  backupClaudeConfig();
11437
12019
  const cfg = readClaudeConfig();
@@ -11445,7 +12027,7 @@ function removeMcpServer() {
11445
12027
  }
11446
12028
  function flushClaudeConfig(obj) {
11447
12029
  mkdirSync8(dirname6(CLAUDE_CONFIG), { recursive: true });
11448
- writeFileSync11(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
12030
+ writeFileSync12(CLAUDE_CONFIG, JSON.stringify(obj, null, 2) + `
11449
12031
  `, "utf-8");
11450
12032
  try {
11451
12033
  chmodSync4(CLAUDE_CONFIG, 384);
@@ -11468,7 +12050,7 @@ function resolveBundledSkillsDir() {
11468
12050
  const here = fileURLToPath(import.meta.url);
11469
12051
  const pkgRoot = resolve(dirname6(here), "..", "..");
11470
12052
  const skillsDir = join9(pkgRoot, "skills");
11471
- if (existsSync12(skillsDir))
12053
+ if (existsSync13(skillsDir))
11472
12054
  return skillsDir;
11473
12055
  return null;
11474
12056
  }
@@ -11503,7 +12085,7 @@ function uninstallSkills() {
11503
12085
  if (!entry.isDirectory())
11504
12086
  continue;
11505
12087
  const dstDir = join9(CLAUDE_SKILLS_ROOT, entry.name);
11506
- if (existsSync12(dstDir)) {
12088
+ if (existsSync13(dstDir)) {
11507
12089
  try {
11508
12090
  fs.rmSync(dstDir, { recursive: true, force: true });
11509
12091
  removed.push(entry.name);
@@ -11528,9 +12110,9 @@ function entriesEqual(a, b) {
11528
12110
  return a.command === b.command && JSON.stringify(a.args ?? []) === JSON.stringify(b.args ?? []);
11529
12111
  }
11530
12112
  function readClaudeSettings() {
11531
- if (!existsSync12(CLAUDE_SETTINGS))
12113
+ if (!existsSync13(CLAUDE_SETTINGS))
11532
12114
  return {};
11533
- const text = readFileSync11(CLAUDE_SETTINGS, "utf-8").trim();
12115
+ const text = readFileSync12(CLAUDE_SETTINGS, "utf-8").trim();
11534
12116
  if (!text)
11535
12117
  return {};
11536
12118
  try {
@@ -11541,7 +12123,7 @@ function readClaudeSettings() {
11541
12123
  }
11542
12124
  function writeClaudeSettings(obj) {
11543
12125
  mkdirSync8(dirname6(CLAUDE_SETTINGS), { recursive: true });
11544
- writeFileSync11(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
12126
+ writeFileSync12(CLAUDE_SETTINGS, JSON.stringify(obj, null, 2) + `
11545
12127
  `, "utf-8");
11546
12128
  }
11547
12129
  function installAllowedTools() {
@@ -11555,7 +12137,7 @@ function installAllowedTools() {
11555
12137
  return { added: toAdd, unchanged: CLAUDEMESH_TOOLS.length - toAdd.length };
11556
12138
  }
11557
12139
  function uninstallAllowedTools() {
11558
- if (!existsSync12(CLAUDE_SETTINGS))
12140
+ if (!existsSync13(CLAUDE_SETTINGS))
11559
12141
  return 0;
11560
12142
  const settings = readClaudeSettings();
11561
12143
  const existing = settings.allowedTools ?? [];
@@ -11570,11 +12152,11 @@ function uninstallAllowedTools() {
11570
12152
  }
11571
12153
  function installHooks() {
11572
12154
  const settings = readClaudeSettings();
11573
- const hooks = (settings.hooks ??= {}) ?? {};
12155
+ const hooks2 = (settings.hooks ??= {}) ?? {};
11574
12156
  let added = 0;
11575
12157
  let unchanged = 0;
11576
12158
  const ensure = (event, command) => {
11577
- const list = hooks[event] ??= [];
12159
+ const list = hooks2[event] ??= [];
11578
12160
  const alreadyPresent = list.some((entry) => (entry.hooks ?? []).some((h) => h.command === command));
11579
12161
  if (alreadyPresent) {
11580
12162
  unchanged += 1;
@@ -11585,32 +12167,32 @@ function installHooks() {
11585
12167
  };
11586
12168
  ensure("Stop", HOOK_COMMAND_STOP);
11587
12169
  ensure("UserPromptSubmit", HOOK_COMMAND_USER_PROMPT);
11588
- settings.hooks = hooks;
12170
+ settings.hooks = hooks2;
11589
12171
  writeClaudeSettings(settings);
11590
12172
  return { added, unchanged };
11591
12173
  }
11592
12174
  function uninstallHooks() {
11593
- if (!existsSync12(CLAUDE_SETTINGS))
12175
+ if (!existsSync13(CLAUDE_SETTINGS))
11594
12176
  return 0;
11595
12177
  const settings = readClaudeSettings();
11596
- const hooks = settings.hooks;
11597
- if (!hooks)
12178
+ const hooks2 = settings.hooks;
12179
+ if (!hooks2)
11598
12180
  return 0;
11599
12181
  let removed = 0;
11600
- for (const event of Object.keys(hooks)) {
12182
+ for (const event of Object.keys(hooks2)) {
11601
12183
  const kept = [];
11602
- for (const entry of hooks[event] ?? []) {
12184
+ for (const entry of hooks2[event] ?? []) {
11603
12185
  const filtered = (entry.hooks ?? []).filter((h) => !(h.command ?? "").includes(HOOK_MARKER));
11604
12186
  removed += (entry.hooks ?? []).length - filtered.length;
11605
12187
  if (filtered.length > 0)
11606
12188
  kept.push({ ...entry, hooks: filtered });
11607
12189
  }
11608
12190
  if (kept.length === 0)
11609
- delete hooks[event];
12191
+ delete hooks2[event];
11610
12192
  else
11611
- hooks[event] = kept;
12193
+ hooks2[event] = kept;
11612
12194
  }
11613
- settings.hooks = hooks;
12195
+ settings.hooks = hooks2;
11614
12196
  writeClaudeSettings(settings);
11615
12197
  return removed;
11616
12198
  }
@@ -11640,7 +12222,7 @@ function runInstall(args = []) {
11640
12222
  render.err("`bun` is not on PATH.", "Install Bun first: https://bun.com");
11641
12223
  process.exit(1);
11642
12224
  }
11643
- if (!existsSync12(entry)) {
12225
+ if (!existsSync13(entry)) {
11644
12226
  render.err(`MCP entry not found at ${entry}`);
11645
12227
  process.exit(1);
11646
12228
  }
@@ -11885,7 +12467,7 @@ var exports_uninstall = {};
11885
12467
  __export(exports_uninstall, {
11886
12468
  uninstall: () => uninstall
11887
12469
  });
11888
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync12, existsSync as existsSync13, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
12470
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, existsSync as existsSync14, rmSync as rmSync2, readdirSync as readdirSync2 } from "node:fs";
11889
12471
  import { join as join10, dirname as dirname7 } from "node:path";
11890
12472
  import { homedir as homedir7 } from "node:os";
11891
12473
  import { fileURLToPath as fileURLToPath2 } from "node:url";
@@ -11893,32 +12475,32 @@ function bundledSkillsDir() {
11893
12475
  const here = fileURLToPath2(import.meta.url);
11894
12476
  const pkgRoot = join10(dirname7(here), "..", "..");
11895
12477
  const skillsDir = join10(pkgRoot, "skills");
11896
- return existsSync13(skillsDir) ? skillsDir : null;
12478
+ return existsSync14(skillsDir) ? skillsDir : null;
11897
12479
  }
11898
12480
  async function uninstall() {
11899
12481
  let removed = 0;
11900
- if (existsSync13(PATHS.CLAUDE_JSON)) {
12482
+ if (existsSync14(PATHS.CLAUDE_JSON)) {
11901
12483
  try {
11902
- const raw = readFileSync12(PATHS.CLAUDE_JSON, "utf-8");
12484
+ const raw = readFileSync13(PATHS.CLAUDE_JSON, "utf-8");
11903
12485
  const config = JSON.parse(raw);
11904
12486
  const servers = config.mcpServers;
11905
12487
  if (servers && "claudemesh" in servers) {
11906
12488
  delete servers.claudemesh;
11907
- writeFileSync12(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
12489
+ writeFileSync13(PATHS.CLAUDE_JSON, JSON.stringify(config, null, 2) + `
11908
12490
  `, "utf-8");
11909
12491
  render.ok("removed MCP server", dim("~/.claude.json"));
11910
12492
  removed++;
11911
12493
  }
11912
12494
  } catch {}
11913
12495
  }
11914
- if (existsSync13(PATHS.CLAUDE_SETTINGS)) {
12496
+ if (existsSync14(PATHS.CLAUDE_SETTINGS)) {
11915
12497
  try {
11916
- const raw = readFileSync12(PATHS.CLAUDE_SETTINGS, "utf-8");
12498
+ const raw = readFileSync13(PATHS.CLAUDE_SETTINGS, "utf-8");
11917
12499
  const config = JSON.parse(raw);
11918
- const hooks = config.hooks;
11919
- if (hooks) {
12500
+ const hooks2 = config.hooks;
12501
+ if (hooks2) {
11920
12502
  let removedHooks = 0;
11921
- for (const [event, entries] of Object.entries(hooks)) {
12503
+ for (const [event, entries] of Object.entries(hooks2)) {
11922
12504
  if (!Array.isArray(entries))
11923
12505
  continue;
11924
12506
  const filtered = entries.filter((h) => {
@@ -11928,13 +12510,13 @@ async function uninstall() {
11928
12510
  if (filtered.length < entries.length) {
11929
12511
  removedHooks += entries.length - filtered.length;
11930
12512
  if (filtered.length === 0)
11931
- delete hooks[event];
12513
+ delete hooks2[event];
11932
12514
  else
11933
- hooks[event] = filtered;
12515
+ hooks2[event] = filtered;
11934
12516
  }
11935
12517
  }
11936
12518
  if (removedHooks > 0) {
11937
- writeFileSync12(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
12519
+ writeFileSync13(PATHS.CLAUDE_SETTINGS, JSON.stringify(config, null, 2) + `
11938
12520
  `, "utf-8");
11939
12521
  render.ok(`removed ${removedHooks} claudemesh hook${removedHooks === 1 ? "" : "s"}`, dim("settings.json"));
11940
12522
  removed++;
@@ -11950,7 +12532,7 @@ async function uninstall() {
11950
12532
  if (!entry.isDirectory())
11951
12533
  continue;
11952
12534
  const dst = join10(CLAUDE_SKILLS_ROOT2, entry.name);
11953
- if (existsSync13(dst)) {
12535
+ if (existsSync14(dst)) {
11954
12536
  try {
11955
12537
  rmSync2(dst, { recursive: true, force: true });
11956
12538
  removedSkills.push(entry.name);
@@ -11982,7 +12564,7 @@ var exports_doctor = {};
11982
12564
  __export(exports_doctor, {
11983
12565
  runDoctor: () => runDoctor
11984
12566
  });
11985
- import { existsSync as existsSync14, readFileSync as readFileSync13, statSync as statSync3 } from "node:fs";
12567
+ import { existsSync as existsSync15, readFileSync as readFileSync14, statSync as statSync3 } from "node:fs";
11986
12568
  import { homedir as homedir8, platform as platform6 } from "node:os";
11987
12569
  import { join as join11 } from "node:path";
11988
12570
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -12009,7 +12591,7 @@ function checkClaudeOnPath() {
12009
12591
  }
12010
12592
  function checkMcpRegistered() {
12011
12593
  const claudeConfig = join11(homedir8(), ".claude.json");
12012
- if (!existsSync14(claudeConfig)) {
12594
+ if (!existsSync15(claudeConfig)) {
12013
12595
  return {
12014
12596
  name: "claudemesh MCP registered in ~/.claude.json",
12015
12597
  pass: false,
@@ -12017,7 +12599,7 @@ function checkMcpRegistered() {
12017
12599
  };
12018
12600
  }
12019
12601
  try {
12020
- const cfg = JSON.parse(readFileSync13(claudeConfig, "utf-8"));
12602
+ const cfg = JSON.parse(readFileSync14(claudeConfig, "utf-8"));
12021
12603
  const registered = Boolean(cfg.mcpServers?.["claudemesh"]);
12022
12604
  return {
12023
12605
  name: "claudemesh MCP registered in ~/.claude.json",
@@ -12035,7 +12617,7 @@ function checkMcpRegistered() {
12035
12617
  }
12036
12618
  function checkHooksRegistered() {
12037
12619
  const settings = join11(homedir8(), ".claude", "settings.json");
12038
- if (!existsSync14(settings)) {
12620
+ if (!existsSync15(settings)) {
12039
12621
  return {
12040
12622
  name: "Status hooks registered in ~/.claude/settings.json",
12041
12623
  pass: false,
@@ -12043,7 +12625,7 @@ function checkHooksRegistered() {
12043
12625
  };
12044
12626
  }
12045
12627
  try {
12046
- const raw = readFileSync13(settings, "utf-8");
12628
+ const raw = readFileSync14(settings, "utf-8");
12047
12629
  const has = raw.includes("claudemesh hook ");
12048
12630
  return {
12049
12631
  name: "Status hooks registered in ~/.claude/settings.json",
@@ -12060,7 +12642,7 @@ function checkHooksRegistered() {
12060
12642
  }
12061
12643
  function checkConfigFile() {
12062
12644
  const path2 = getConfigPath();
12063
- if (!existsSync14(path2)) {
12645
+ if (!existsSync15(path2)) {
12064
12646
  return {
12065
12647
  name: "~/.claudemesh/config.json exists and parses",
12066
12648
  pass: true,
@@ -12132,8 +12714,8 @@ async function checkBrokerWs() {
12132
12714
  const wsUrl = URLS.BROKER;
12133
12715
  const start = Date.now();
12134
12716
  try {
12135
- const WebSocket3 = (await import("ws")).default;
12136
- const ws = new WebSocket3(wsUrl);
12717
+ const WebSocket4 = (await import("ws")).default;
12718
+ const ws = new WebSocket4(wsUrl);
12137
12719
  const result = await new Promise((resolve2) => {
12138
12720
  const timer = setTimeout(() => {
12139
12721
  try {
@@ -12243,12 +12825,12 @@ var exports_status = {};
12243
12825
  __export(exports_status, {
12244
12826
  runStatus: () => runStatus2
12245
12827
  });
12246
- import { statSync as statSync4, existsSync as existsSync15 } from "node:fs";
12247
- import WebSocket3 from "ws";
12828
+ import { statSync as statSync4, existsSync as existsSync16 } from "node:fs";
12829
+ import WebSocket4 from "ws";
12248
12830
  async function probeBroker(url, timeoutMs = 4000) {
12249
12831
  return new Promise((resolve2) => {
12250
12832
  const started = Date.now();
12251
- const ws = new WebSocket3(url);
12833
+ const ws = new WebSocket4(url);
12252
12834
  const timer = setTimeout(() => {
12253
12835
  try {
12254
12836
  ws.terminate();
@@ -12273,7 +12855,7 @@ async function runStatus2() {
12273
12855
  render.section(`status (v${VERSION})`);
12274
12856
  const configPath = getConfigPath();
12275
12857
  let configPermsNote = "missing";
12276
- if (existsSync15(configPath)) {
12858
+ if (existsSync16(configPath)) {
12277
12859
  const mode = (statSync4(configPath).mode & 511).toString(8).padStart(4, "0");
12278
12860
  configPermsNote = mode === "0600" ? `${mode}` : `${mode} — expected 0600`;
12279
12861
  }
@@ -12419,13 +13001,13 @@ var init_check_claude_binary = __esm(() => {
12419
13001
  });
12420
13002
 
12421
13003
  // src/services/health/check-mcp-registered.ts
12422
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "node:fs";
13004
+ import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
12423
13005
  function checkMcpRegistered2() {
12424
13006
  try {
12425
- if (!existsSync16(PATHS.CLAUDE_JSON)) {
13007
+ if (!existsSync17(PATHS.CLAUDE_JSON)) {
12426
13008
  return { name: "mcp-registered", ok: false, message: "~/.claude.json not found" };
12427
13009
  }
12428
- const raw = readFileSync14(PATHS.CLAUDE_JSON, "utf-8");
13010
+ const raw = readFileSync15(PATHS.CLAUDE_JSON, "utf-8");
12429
13011
  const config = JSON.parse(raw);
12430
13012
  if (config.mcpServers && "claudemesh" in config.mcpServers) {
12431
13013
  return { name: "mcp-registered", ok: true, message: "MCP server registered" };
@@ -12440,13 +13022,13 @@ var init_check_mcp_registered = __esm(() => {
12440
13022
  });
12441
13023
 
12442
13024
  // src/services/health/check-hooks-registered.ts
12443
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
13025
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "node:fs";
12444
13026
  function checkHooksRegistered2() {
12445
13027
  try {
12446
- if (!existsSync17(PATHS.CLAUDE_SETTINGS)) {
13028
+ if (!existsSync18(PATHS.CLAUDE_SETTINGS)) {
12447
13029
  return { name: "hooks-registered", ok: false, message: "~/.claude/settings.json not found" };
12448
13030
  }
12449
- const raw = readFileSync15(PATHS.CLAUDE_SETTINGS, "utf-8");
13031
+ const raw = readFileSync16(PATHS.CLAUDE_SETTINGS, "utf-8");
12450
13032
  const config = JSON.parse(raw);
12451
13033
  if (config.hooks) {
12452
13034
  return { name: "hooks-registered", ok: true, message: "Hooks configured" };
@@ -12461,10 +13043,10 @@ var init_check_hooks_registered = __esm(() => {
12461
13043
  });
12462
13044
 
12463
13045
  // src/services/health/check-config-perms.ts
12464
- import { existsSync as existsSync18, statSync as statSync5 } from "node:fs";
13046
+ import { existsSync as existsSync19, statSync as statSync5 } from "node:fs";
12465
13047
  function checkConfigPerms() {
12466
13048
  const configFile = PATHS.CONFIG_FILE;
12467
- if (!existsSync18(configFile)) {
13049
+ if (!existsSync19(configFile)) {
12468
13050
  return { name: "config-perms", ok: true, message: "No config file yet (first run)" };
12469
13051
  }
12470
13052
  try {
@@ -12482,13 +13064,13 @@ var init_check_config_perms = __esm(() => {
12482
13064
  });
12483
13065
 
12484
13066
  // src/services/health/check-keypairs-valid.ts
12485
- import { existsSync as existsSync19, readFileSync as readFileSync16 } from "node:fs";
13067
+ import { existsSync as existsSync20, readFileSync as readFileSync17 } from "node:fs";
12486
13068
  function checkKeypairsValid() {
12487
- if (!existsSync19(PATHS.CONFIG_FILE)) {
13069
+ if (!existsSync20(PATHS.CONFIG_FILE)) {
12488
13070
  return { name: "keypairs-valid", ok: true, message: "No config (first run)" };
12489
13071
  }
12490
13072
  try {
12491
- const raw = readFileSync16(PATHS.CONFIG_FILE, "utf-8");
13073
+ const raw = readFileSync17(PATHS.CONFIG_FILE, "utf-8");
12492
13074
  const config = JSON.parse(raw);
12493
13075
  const meshes = config.meshes ?? [];
12494
13076
  if (meshes.length === 0) {
@@ -12969,7 +13551,7 @@ __export(exports_url_handler, {
12969
13551
  runUrlHandler: () => runUrlHandler
12970
13552
  });
12971
13553
  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";
13554
+ import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync14, rmSync as rmSync3, chmodSync as chmodSync5 } from "node:fs";
12973
13555
  import { join as join12 } from "node:path";
12974
13556
  import { spawnSync as spawnSync5 } from "node:child_process";
12975
13557
  function resolveClaudemeshBin() {
@@ -13003,7 +13585,7 @@ function installDarwin2() {
13003
13585
  </array>
13004
13586
  </dict>
13005
13587
  </plist>`;
13006
- writeFileSync13(join12(contents, "Info.plist"), plist);
13588
+ writeFileSync14(join12(contents, "Info.plist"), plist);
13007
13589
  const shim = `#!/bin/sh
13008
13590
  URL="$1"
13009
13591
  CODE=\${URL#claudemesh://}
@@ -13018,7 +13600,7 @@ end tell
13018
13600
  EOF
13019
13601
  `;
13020
13602
  const shimPath = join12(macOS, "open-url");
13021
- writeFileSync13(shimPath, shim);
13603
+ writeFileSync14(shimPath, shim);
13022
13604
  chmodSync5(shimPath, 493);
13023
13605
  const lsreg = spawnSync5("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", ["-f", appDir], { encoding: "utf-8" });
13024
13606
  if (lsreg.status !== 0) {
@@ -13042,7 +13624,7 @@ MimeType=x-scheme-handler/claudemesh;
13042
13624
  NoDisplay=true
13043
13625
  `;
13044
13626
  const desktopPath = join12(appsDir, "claudemesh.desktop");
13045
- writeFileSync13(desktopPath, desktop);
13627
+ writeFileSync14(desktopPath, desktop);
13046
13628
  const xdg1 = spawnSync5("xdg-mime", ["default", "claudemesh.desktop", "x-scheme-handler/claudemesh"], { encoding: "utf-8" });
13047
13629
  if (xdg1.status !== 0) {
13048
13630
  render.warn("xdg-mime not available — skipped mime default registration");
@@ -13065,7 +13647,7 @@ function installWindows() {
13065
13647
  `@="\\"${binPath.replace(/\\/g, "\\\\")}\\" \\"%1\\""`
13066
13648
  ];
13067
13649
  const regPath = join12(homedir9(), "claudemesh-handler.reg");
13068
- writeFileSync13(regPath, lines.join(`\r
13650
+ writeFileSync14(regPath, lines.join(`\r
13069
13651
  `));
13070
13652
  const res = spawnSync5("reg.exe", ["import", regPath], { encoding: "utf-8" });
13071
13653
  if (res.status !== 0) {
@@ -13077,14 +13659,14 @@ function installWindows() {
13077
13659
  }
13078
13660
  function uninstallDarwin() {
13079
13661
  const appDir = join12(homedir9(), "Library", "Application Support", "claudemesh", "ClaudemeshHandler.app");
13080
- if (existsSync20(appDir))
13662
+ if (existsSync21(appDir))
13081
13663
  rmSync3(appDir, { recursive: true, force: true });
13082
13664
  render.ok("removed claudemesh:// handler on macOS");
13083
13665
  return EXIT.SUCCESS;
13084
13666
  }
13085
13667
  function uninstallLinux() {
13086
13668
  const desktopPath = join12(homedir9(), ".local", "share", "applications", "claudemesh.desktop");
13087
- if (existsSync20(desktopPath))
13669
+ if (existsSync21(desktopPath))
13088
13670
  rmSync3(desktopPath, { force: true });
13089
13671
  render.ok("removed claudemesh:// handler on Linux");
13090
13672
  return EXIT.SUCCESS;
@@ -13129,7 +13711,7 @@ var exports_status_line = {};
13129
13711
  __export(exports_status_line, {
13130
13712
  runStatusLine: () => runStatusLine
13131
13713
  });
13132
- import { existsSync as existsSync21, readFileSync as readFileSync17 } from "node:fs";
13714
+ import { existsSync as existsSync22, readFileSync as readFileSync18 } from "node:fs";
13133
13715
  import { join as join13 } from "node:path";
13134
13716
  import { homedir as homedir10 } from "node:os";
13135
13717
  async function runStatusLine() {
@@ -13141,9 +13723,9 @@ async function runStatusLine() {
13141
13723
  }
13142
13724
  const cachePath = join13(homedir10(), ".claudemesh", "peer-cache.json");
13143
13725
  let cache = {};
13144
- if (existsSync21(cachePath)) {
13726
+ if (existsSync22(cachePath)) {
13145
13727
  try {
13146
- cache = JSON.parse(readFileSync17(cachePath, "utf-8"));
13728
+ cache = JSON.parse(readFileSync18(cachePath, "utf-8"));
13147
13729
  } catch {}
13148
13730
  }
13149
13731
  const pick = config.meshes[0];
@@ -13174,7 +13756,7 @@ __export(exports_backup, {
13174
13756
  runRestore: () => runRestore,
13175
13757
  runBackup: () => runBackup
13176
13758
  });
13177
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync14, existsSync as existsSync22 } from "node:fs";
13759
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync15, existsSync as existsSync23 } from "node:fs";
13178
13760
  import { createInterface as createInterface11 } from "node:readline";
13179
13761
  function readHidden(prompt5) {
13180
13762
  return new Promise((resolve2) => {
@@ -13216,11 +13798,11 @@ async function deriveKey(pass, salt, s) {
13216
13798
  }
13217
13799
  async function runBackup(outPath) {
13218
13800
  const configPath = getConfigPath();
13219
- if (!existsSync22(configPath)) {
13801
+ if (!existsSync23(configPath)) {
13220
13802
  console.error(" No config found — nothing to back up. Join a mesh first.");
13221
13803
  return EXIT.NOT_FOUND;
13222
13804
  }
13223
- const plaintext = readFileSync18(configPath);
13805
+ const plaintext = readFileSync19(configPath);
13224
13806
  const pass = await readHidden(" Passphrase (min 12 chars): ");
13225
13807
  if (pass.length < 12) {
13226
13808
  console.error(" ✗ Passphrase too short.");
@@ -13238,7 +13820,7 @@ async function runBackup(outPath) {
13238
13820
  const ciphertext = Buffer.from(s.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, null, null, nonce, key));
13239
13821
  const blob = Buffer.concat([MAGIC, salt, nonce, ciphertext]);
13240
13822
  const file = outPath ?? `claudemesh-backup-${new Date().toISOString().replace(/[:.]/g, "-")}.cmb`;
13241
- writeFileSync14(file, blob, { mode: 384 });
13823
+ writeFileSync15(file, blob, { mode: 384 });
13242
13824
  console.log(`
13243
13825
  ✓ Backup saved: ${file}`);
13244
13826
  console.log(` Size: ${blob.length} bytes. Guard the passphrase — there is no recovery.
@@ -13250,11 +13832,11 @@ async function runRestore(inPath) {
13250
13832
  console.error(" Usage: claudemesh restore <backup-file>");
13251
13833
  return EXIT.INVALID_ARGS;
13252
13834
  }
13253
- if (!existsSync22(inPath)) {
13835
+ if (!existsSync23(inPath)) {
13254
13836
  console.error(` ✗ File not found: ${inPath}`);
13255
13837
  return EXIT.NOT_FOUND;
13256
13838
  }
13257
- const blob = readFileSync18(inPath);
13839
+ const blob = readFileSync19(inPath);
13258
13840
  if (blob.length < 4 + 16 + 24 + 17 || !blob.subarray(0, 4).equals(MAGIC)) {
13259
13841
  console.error(" ✗ Not a claudemesh backup file (bad magic).");
13260
13842
  return EXIT.INVALID_ARGS;
@@ -13273,12 +13855,12 @@ async function runRestore(inPath) {
13273
13855
  return EXIT.INTERNAL_ERROR;
13274
13856
  }
13275
13857
  const configPath = getConfigPath();
13276
- if (existsSync22(configPath)) {
13858
+ if (existsSync23(configPath)) {
13277
13859
  const backupOld = `${configPath}.before-restore.${Date.now()}`;
13278
- writeFileSync14(backupOld, readFileSync18(configPath), { mode: 384 });
13860
+ writeFileSync15(backupOld, readFileSync19(configPath), { mode: 384 });
13279
13861
  console.log(` ↻ Existing config saved to ${backupOld}`);
13280
13862
  }
13281
- writeFileSync14(configPath, Buffer.from(plaintext), { mode: 384 });
13863
+ writeFileSync15(configPath, Buffer.from(plaintext), { mode: 384 });
13282
13864
  console.log(`
13283
13865
  ✓ Config restored to ${configPath}`);
13284
13866
  console.log(" Run `claudemesh list` to verify your meshes.\n");
@@ -13298,7 +13880,7 @@ __export(exports_upgrade, {
13298
13880
  runUpgrade: () => runUpgrade
13299
13881
  });
13300
13882
  import { spawnSync as spawnSync6 } from "node:child_process";
13301
- import { existsSync as existsSync23 } from "node:fs";
13883
+ import { existsSync as existsSync24 } from "node:fs";
13302
13884
  import { dirname as dirname8, join as join14, resolve as resolve2 } from "node:path";
13303
13885
  async function latestVersion() {
13304
13886
  try {
@@ -13313,14 +13895,14 @@ async function latestVersion() {
13313
13895
  }
13314
13896
  function findNpm() {
13315
13897
  const portable = join14(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
13316
- if (existsSync23(portable)) {
13898
+ if (existsSync24(portable)) {
13317
13899
  return { npm: portable, prefix: join14(process.env.HOME ?? "", ".claudemesh") };
13318
13900
  }
13319
13901
  let cur = resolve2(process.argv[1] ?? ".");
13320
13902
  for (let i = 0;i < 6; i++) {
13321
13903
  cur = dirname8(cur);
13322
13904
  const candidate = join14(cur, "bin", "npm");
13323
- if (existsSync23(candidate))
13905
+ if (existsSync24(candidate))
13324
13906
  return { npm: candidate };
13325
13907
  }
13326
13908
  return { npm: "npm" };
@@ -13382,7 +13964,7 @@ __export(exports_grants, {
13382
13964
  runBlock: () => runBlock,
13383
13965
  isAllowed: () => isAllowed
13384
13966
  });
13385
- import { existsSync as existsSync24, mkdirSync as mkdirSync10, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "node:fs";
13967
+ import { existsSync as existsSync25, mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "node:fs";
13386
13968
  import { homedir as homedir11 } from "node:os";
13387
13969
  import { join as join15 } from "node:path";
13388
13970
  async function syncToBroker(meshSlug, grants) {
@@ -13402,19 +13984,19 @@ async function syncToBroker(meshSlug, grants) {
13402
13984
  }
13403
13985
  }
13404
13986
  function readGrants() {
13405
- if (!existsSync24(GRANT_FILE))
13987
+ if (!existsSync25(GRANT_FILE))
13406
13988
  return {};
13407
13989
  try {
13408
- return JSON.parse(readFileSync19(GRANT_FILE, "utf-8"));
13990
+ return JSON.parse(readFileSync20(GRANT_FILE, "utf-8"));
13409
13991
  } catch {
13410
13992
  return {};
13411
13993
  }
13412
13994
  }
13413
13995
  function writeGrants(g) {
13414
13996
  const dir = join15(homedir11(), ".claudemesh");
13415
- if (!existsSync24(dir))
13997
+ if (!existsSync25(dir))
13416
13998
  mkdirSync10(dir, { recursive: true });
13417
- writeFileSync15(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
13999
+ writeFileSync16(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
13418
14000
  }
13419
14001
  function resolveCaps(input) {
13420
14002
  if (input.includes("all"))
@@ -14269,17 +14851,17 @@ async function runUnwatch(id, opts) {
14269
14851
  }
14270
14852
  async function runWebhookList(opts) {
14271
14853
  return await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
14272
- const hooks = await client.listWebhooks();
14854
+ const hooks2 = await client.listWebhooks();
14273
14855
  if (opts.json) {
14274
- emitJson(hooks);
14856
+ emitJson(hooks2);
14275
14857
  return EXIT.SUCCESS;
14276
14858
  }
14277
- if (hooks.length === 0) {
14859
+ if (hooks2.length === 0) {
14278
14860
  render.info(dim("(no webhooks)"));
14279
14861
  return EXIT.SUCCESS;
14280
14862
  }
14281
- render.section(`webhooks (${hooks.length})`);
14282
- for (const h of hooks) {
14863
+ render.section(`webhooks (${hooks2.length})`);
14864
+ for (const h of hooks2) {
14283
14865
  const dot = h.active ? "●" : dim("○");
14284
14866
  process.stdout.write(` ${dot} ${bold(h.name)} ${dim("· " + h.url)}
14285
14867
  `);
@@ -15250,9 +15832,9 @@ __export(exports_file, {
15250
15832
  runFileShare: () => runFileShare,
15251
15833
  runFileGet: () => runFileGet
15252
15834
  });
15253
- import { hostname as osHostname } from "node:os";
15835
+ import { hostname as osHostname2 } from "node:os";
15254
15836
  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";
15837
+ import { statSync as statSync7, existsSync as existsSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync11 } from "node:fs";
15256
15838
  function emitJson2(data) {
15257
15839
  console.log(JSON.stringify(data, null, 2));
15258
15840
  }
@@ -15269,7 +15851,7 @@ async function runFileShare(filePath, opts) {
15269
15851
  return EXIT.INVALID_ARGS;
15270
15852
  }
15271
15853
  const absPath = resolvePath(filePath);
15272
- if (!existsSync25(absPath)) {
15854
+ if (!existsSync26(absPath)) {
15273
15855
  render.err(`File not found: ${absPath}`);
15274
15856
  return EXIT.INVALID_ARGS;
15275
15857
  }
@@ -15282,7 +15864,7 @@ async function runFileShare(filePath, opts) {
15282
15864
  return await withMesh({ meshSlug: opts.mesh ?? null }, async (client, mesh) => {
15283
15865
  if (opts.to && !opts.upload) {
15284
15866
  const peers = await client.listPeers();
15285
- const myHost = osHostname();
15867
+ const myHost = osHostname2();
15286
15868
  const target = peers.find((p) => {
15287
15869
  if (!p.hostname || p.hostname !== myHost)
15288
15870
  return false;
@@ -15349,7 +15931,7 @@ async function runFileGet(fileId, opts) {
15349
15931
  const buf = Buffer.from(await res.arrayBuffer());
15350
15932
  const outPath = opts.out ? resolvePath(opts.out) : resolvePath(process.cwd(), meta.name);
15351
15933
  mkdirSync11(dirname9(outPath), { recursive: true });
15352
- writeFileSync16(outPath, buf);
15934
+ writeFileSync17(outPath, buf);
15353
15935
  if (opts.json) {
15354
15936
  emitJson2({ fileId, name: meta.name, savedTo: outPath, sizeBytes: buf.length });
15355
15937
  } else {
@@ -15444,7 +16026,7 @@ var require_client = __commonJS((exports) => {
15444
16026
  var ws_1 = __importDefault(__require("ws"));
15445
16027
  var crypto_js_1 = require_crypto();
15446
16028
  var MAX_QUEUED2 = 100;
15447
- var HELLO_ACK_TIMEOUT_MS3 = 5000;
16029
+ var HELLO_ACK_TIMEOUT_MS4 = 5000;
15448
16030
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
15449
16031
 
15450
16032
  class MeshClient extends node_events_1.EventEmitter {
@@ -15509,7 +16091,7 @@ var require_client = __commonJS((exports) => {
15509
16091
  this.debug("hello_ack timeout");
15510
16092
  ws.close();
15511
16093
  reject(new Error("hello_ack timeout"));
15512
- }, HELLO_ACK_TIMEOUT_MS3);
16094
+ }, HELLO_ACK_TIMEOUT_MS4);
15513
16095
  };
15514
16096
  const onMessage = (raw) => {
15515
16097
  let msg;
@@ -15959,7 +16541,7 @@ __export(exports_bridge, {
15959
16541
  runBridge: () => runBridge,
15960
16542
  bridgeConfigTemplate: () => bridgeConfigTemplate
15961
16543
  });
15962
- import { readFileSync as readFileSync20, existsSync as existsSync26 } from "node:fs";
16544
+ import { readFileSync as readFileSync21, existsSync as existsSync27 } from "node:fs";
15963
16545
  function parseConfig(text) {
15964
16546
  const trimmed = text.trim();
15965
16547
  if (trimmed.startsWith("{"))
@@ -16003,13 +16585,13 @@ async function runBridge(configPath) {
16003
16585
  render.err("Usage: claudemesh bridge run <config.yaml>");
16004
16586
  return EXIT.INVALID_ARGS;
16005
16587
  }
16006
- if (!existsSync26(configPath)) {
16588
+ if (!existsSync27(configPath)) {
16007
16589
  render.err(`config file not found: ${configPath}`);
16008
16590
  return EXIT.NOT_FOUND;
16009
16591
  }
16010
16592
  let cfg;
16011
16593
  try {
16012
- cfg = parseConfig(readFileSync20(configPath, "utf-8"));
16594
+ cfg = parseConfig(readFileSync21(configPath, "utf-8"));
16013
16595
  } catch (e) {
16014
16596
  render.err(`failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
16015
16597
  return EXIT.INVALID_ARGS;
@@ -16413,9 +16995,9 @@ function cacheKey(apiKeySecret, topicName) {
16413
16995
  async function getTopicKey(args) {
16414
16996
  const cacheId = cacheKey(args.apiKeySecret, args.topicName);
16415
16997
  if (!args.fresh) {
16416
- const cached2 = cache.get(cacheId);
16417
- if (cached2)
16418
- return { ok: true, topicKey: cached2.topicKey };
16998
+ const cached3 = cache.get(cacheId);
16999
+ if (cached3)
17000
+ return { ok: true, topicKey: cached3.topicKey };
16419
17001
  }
16420
17002
  let sealed;
16421
17003
  try {
@@ -16983,11 +17565,11 @@ import {
16983
17565
  ListResourcesRequestSchema,
16984
17566
  ReadResourceRequestSchema
16985
17567
  } from "@modelcontextprotocol/sdk/types.js";
16986
- import { existsSync as existsSync27 } from "node:fs";
17568
+ import { existsSync as existsSync28 } from "node:fs";
16987
17569
  import { request as httpRequest2 } from "node:http";
16988
17570
  async function daemonReady() {
16989
17571
  for (let i = 0;i < DAEMON_BOOT_RETRIES; i++) {
16990
- if (existsSync27(DAEMON_PATHS.SOCK_FILE))
17572
+ if (existsSync28(DAEMON_PATHS.SOCK_FILE))
16991
17573
  return true;
16992
17574
  await new Promise((r) => setTimeout(r, DAEMON_BOOT_RETRY_MS));
16993
17575
  }
@@ -17317,9 +17899,9 @@ async function startServiceProxy(serviceName) {
17317
17899
  const fetched = await client.getServiceTools(serviceName);
17318
17900
  tools = fetched;
17319
17901
  } catch {
17320
- const cached2 = client.serviceCatalog.find((s) => s.name === serviceName);
17321
- if (cached2)
17322
- tools = cached2.tools;
17902
+ const cached3 = client.serviceCatalog.find((s) => s.name === serviceName);
17903
+ if (cached3)
17904
+ tools = cached3.tools;
17323
17905
  }
17324
17906
  if (tools.length === 0) {
17325
17907
  process.stderr.write(`[mesh:${serviceName}] no tools found — service may not be running
@@ -19289,4 +19871,4 @@ main().catch((err) => {
19289
19871
  process.exit(EXIT.INTERNAL_ERROR);
19290
19872
  });
19291
19873
 
19292
- //# debugId=04E4A5313AE4F0EA64756E2164756E21
19874
+ //# debugId=A7687DD2912DA01C64756E2164756E21