claudemesh-cli 1.29.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.29.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",
@@ -3941,6 +3942,38 @@ var init_lifecycle = __esm(() => {
3941
3942
  init_paths2();
3942
3943
  });
3943
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
+
3944
3977
  // src/commands/launch.ts
3945
3978
  var exports_launch = {};
3946
3979
  __export(exports_launch, {
@@ -4344,6 +4377,8 @@ async function runLaunch(flags, rawArgs) {
4344
4377
  };
4345
4378
  writeFileSync7(join5(tmpDir, "config.json"), JSON.stringify(sessionConfig, null, 2) + `
4346
4379
  `, "utf-8");
4380
+ const isResume = args.resume !== null || args.continueSession;
4381
+ const claudeSessionId = isResume ? undefined : randomUUID();
4347
4382
  let sessionTokenFilePath = null;
4348
4383
  let sessionTokenForCleanup = null;
4349
4384
  try {
@@ -4351,6 +4386,27 @@ async function runLaunch(flags, rawArgs) {
4351
4386
  const minted = mintSessionToken2(tmpDir);
4352
4387
  sessionTokenFilePath = minted.filePath;
4353
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 {}
4354
4410
  const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4355
4411
  const sessionIdForRegister = claudeSessionId ?? randomUUID();
4356
4412
  await ipc2({
@@ -4365,7 +4421,8 @@ async function runLaunch(flags, rawArgs) {
4365
4421
  pid: process.pid,
4366
4422
  cwd: process.cwd(),
4367
4423
  ...role ? { role } : {},
4368
- ...parsedGroups.length > 0 ? { groups: parsedGroups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`) } : {}
4424
+ ...parsedGroups.length > 0 ? { groups: parsedGroups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`) } : {},
4425
+ ...presencePayload ? { presence: presencePayload } : {}
4369
4426
  }
4370
4427
  }).catch(() => null);
4371
4428
  process._claudemeshTokenEnv = {
@@ -4429,8 +4486,6 @@ async function runLaunch(flags, rawArgs) {
4429
4486
  }
4430
4487
  filtered.push(args.claudeArgs[i]);
4431
4488
  }
4432
- const isResume = args.resume !== null || args.continueSession;
4433
- const claudeSessionId = isResume ? undefined : randomUUID();
4434
4489
  const claudeArgs = [
4435
4490
  "--dangerously-load-development-channels",
4436
4491
  "server:claudemesh",
@@ -8440,7 +8495,7 @@ async function runMsgStatus(id, opts) {
8440
8495
  console.log(JSON.stringify(result, null, 2));
8441
8496
  return EXIT.SUCCESS;
8442
8497
  }
8443
- render.section(`message ${id.slice(0, 12)}…`);
8498
+ render.section(`message ${lookupId.slice(0, 12)}…`);
8444
8499
  render.kv([
8445
8500
  ["target", result.targetSpec],
8446
8501
  ["delivered", result.delivered ? "yes" : "no"],
@@ -9258,13 +9313,27 @@ function startReaper() {
9258
9313
  return;
9259
9314
  reaperHandle = setInterval(reapDead, REAPER_INTERVAL_MS).unref?.() ?? reaperHandle;
9260
9315
  }
9316
+ function setRegistryHooks(next) {
9317
+ hooks.onRegister = next.onRegister;
9318
+ hooks.onDeregister = next.onDeregister;
9319
+ }
9261
9320
  function registerSession(info) {
9262
9321
  const priorToken = bySessionId.get(info.sessionId);
9263
- if (priorToken && priorToken !== info.token)
9264
- byToken.delete(priorToken);
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
+ }
9265
9331
  const stored = { ...info, registeredAt: Date.now() };
9266
9332
  byToken.set(info.token, stored);
9267
9333
  bySessionId.set(info.sessionId, info.token);
9334
+ try {
9335
+ hooks.onRegister?.(stored);
9336
+ } catch {}
9268
9337
  return stored;
9269
9338
  }
9270
9339
  function deregisterByToken(token) {
@@ -9274,6 +9343,9 @@ function deregisterByToken(token) {
9274
9343
  byToken.delete(token);
9275
9344
  if (bySessionId.get(entry.sessionId) === token)
9276
9345
  bySessionId.delete(entry.sessionId);
9346
+ try {
9347
+ hooks.onDeregister?.(entry);
9348
+ } catch {}
9277
9349
  return true;
9278
9350
  }
9279
9351
  function resolveToken(token) {
@@ -9305,12 +9377,13 @@ function reapDead() {
9305
9377
  for (const t of dead)
9306
9378
  deregisterByToken(t);
9307
9379
  }
9308
- var TTL_MS, REAPER_INTERVAL_MS, byToken, bySessionId, reaperHandle = null;
9380
+ var TTL_MS, REAPER_INTERVAL_MS, byToken, bySessionId, hooks, reaperHandle = null;
9309
9381
  var init_session_registry = __esm(() => {
9310
9382
  TTL_MS = 24 * 60 * 60 * 1000;
9311
9383
  REAPER_INTERVAL_MS = 30 * 1000;
9312
9384
  byToken = new Map;
9313
9385
  bySessionId = new Map;
9386
+ hooks = {};
9314
9387
  });
9315
9388
 
9316
9389
  // src/daemon/ipc/server.ts
@@ -9461,6 +9534,28 @@ function makeHandler(opts) {
9461
9534
  const cwd = typeof body.cwd === "string" ? body.cwd : undefined;
9462
9535
  const role = typeof body.role === "string" ? body.role : undefined;
9463
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
+ }
9464
9559
  const stored = registerSession({
9465
9560
  token: token.toLowerCase(),
9466
9561
  sessionId,
@@ -9469,10 +9564,20 @@ function makeHandler(opts) {
9469
9564
  pid,
9470
9565
  cwd,
9471
9566
  role,
9472
- groups
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
9473
9580
  });
9474
- opts.log("info", "session_registered", { sessionId, mesh, pid });
9475
- respond(res, 200, { ok: true, registered_at: stored.registeredAt });
9476
9581
  } catch (e) {
9477
9582
  respond(res, 400, { error: String(e) });
9478
9583
  }
@@ -10573,9 +10678,168 @@ var init_broker = __esm(() => {
10573
10678
  BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
10574
10679
  });
10575
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
+
10576
10840
  // src/daemon/drain.ts
10577
10841
  function startDrainWorker(opts) {
10578
- const log2 = opts.log ?? defaultLog2;
10842
+ const log2 = opts.log ?? defaultLog3;
10579
10843
  let stopped = false;
10580
10844
  let wakeResolve = null;
10581
10845
  let wakePromise = new Promise((r) => {
@@ -10719,7 +10983,7 @@ async function randomNonce2() {
10719
10983
  const { randomBytes: randomBytes6 } = await import("node:crypto");
10720
10984
  return randomBytes6(24).toString("base64");
10721
10985
  }
10722
- function defaultLog2(level, msg, meta) {
10986
+ function defaultLog3(level, msg, meta) {
10723
10987
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10724
10988
  if (level === "info")
10725
10989
  process.stdout.write(line + `
@@ -11062,6 +11326,56 @@ async function runDaemon(opts = {}) {
11062
11326
  }
11063
11327
  let drain = null;
11064
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
+ });
11065
11379
  startReaper();
11066
11380
  const ipc2 = startIpcServer({
11067
11381
  localToken,
@@ -11105,6 +11419,12 @@ async function runDaemon(opts = {}) {
11105
11419
  await b.close();
11106
11420
  } catch {}
11107
11421
  }
11422
+ for (const b of sessionBrokers.values()) {
11423
+ try {
11424
+ await b.close();
11425
+ } catch {}
11426
+ }
11427
+ sessionBrokers.clear();
11108
11428
  await ipc2.close();
11109
11429
  try {
11110
11430
  outboxDb.close();
@@ -11126,6 +11446,7 @@ var init_run = __esm(() => {
11126
11446
  init_server();
11127
11447
  init_session_registry();
11128
11448
  init_broker();
11449
+ init_session_broker();
11129
11450
  init_drain();
11130
11451
  init_inbound();
11131
11452
  init_identity();
@@ -11831,11 +12152,11 @@ function uninstallAllowedTools() {
11831
12152
  }
11832
12153
  function installHooks() {
11833
12154
  const settings = readClaudeSettings();
11834
- const hooks = (settings.hooks ??= {}) ?? {};
12155
+ const hooks2 = (settings.hooks ??= {}) ?? {};
11835
12156
  let added = 0;
11836
12157
  let unchanged = 0;
11837
12158
  const ensure = (event, command) => {
11838
- const list = hooks[event] ??= [];
12159
+ const list = hooks2[event] ??= [];
11839
12160
  const alreadyPresent = list.some((entry) => (entry.hooks ?? []).some((h) => h.command === command));
11840
12161
  if (alreadyPresent) {
11841
12162
  unchanged += 1;
@@ -11846,7 +12167,7 @@ function installHooks() {
11846
12167
  };
11847
12168
  ensure("Stop", HOOK_COMMAND_STOP);
11848
12169
  ensure("UserPromptSubmit", HOOK_COMMAND_USER_PROMPT);
11849
- settings.hooks = hooks;
12170
+ settings.hooks = hooks2;
11850
12171
  writeClaudeSettings(settings);
11851
12172
  return { added, unchanged };
11852
12173
  }
@@ -11854,24 +12175,24 @@ function uninstallHooks() {
11854
12175
  if (!existsSync13(CLAUDE_SETTINGS))
11855
12176
  return 0;
11856
12177
  const settings = readClaudeSettings();
11857
- const hooks = settings.hooks;
11858
- if (!hooks)
12178
+ const hooks2 = settings.hooks;
12179
+ if (!hooks2)
11859
12180
  return 0;
11860
12181
  let removed = 0;
11861
- for (const event of Object.keys(hooks)) {
12182
+ for (const event of Object.keys(hooks2)) {
11862
12183
  const kept = [];
11863
- for (const entry of hooks[event] ?? []) {
12184
+ for (const entry of hooks2[event] ?? []) {
11864
12185
  const filtered = (entry.hooks ?? []).filter((h) => !(h.command ?? "").includes(HOOK_MARKER));
11865
12186
  removed += (entry.hooks ?? []).length - filtered.length;
11866
12187
  if (filtered.length > 0)
11867
12188
  kept.push({ ...entry, hooks: filtered });
11868
12189
  }
11869
12190
  if (kept.length === 0)
11870
- delete hooks[event];
12191
+ delete hooks2[event];
11871
12192
  else
11872
- hooks[event] = kept;
12193
+ hooks2[event] = kept;
11873
12194
  }
11874
- settings.hooks = hooks;
12195
+ settings.hooks = hooks2;
11875
12196
  writeClaudeSettings(settings);
11876
12197
  return removed;
11877
12198
  }
@@ -12176,10 +12497,10 @@ async function uninstall() {
12176
12497
  try {
12177
12498
  const raw = readFileSync13(PATHS.CLAUDE_SETTINGS, "utf-8");
12178
12499
  const config = JSON.parse(raw);
12179
- const hooks = config.hooks;
12180
- if (hooks) {
12500
+ const hooks2 = config.hooks;
12501
+ if (hooks2) {
12181
12502
  let removedHooks = 0;
12182
- for (const [event, entries] of Object.entries(hooks)) {
12503
+ for (const [event, entries] of Object.entries(hooks2)) {
12183
12504
  if (!Array.isArray(entries))
12184
12505
  continue;
12185
12506
  const filtered = entries.filter((h) => {
@@ -12189,9 +12510,9 @@ async function uninstall() {
12189
12510
  if (filtered.length < entries.length) {
12190
12511
  removedHooks += entries.length - filtered.length;
12191
12512
  if (filtered.length === 0)
12192
- delete hooks[event];
12513
+ delete hooks2[event];
12193
12514
  else
12194
- hooks[event] = filtered;
12515
+ hooks2[event] = filtered;
12195
12516
  }
12196
12517
  }
12197
12518
  if (removedHooks > 0) {
@@ -12393,8 +12714,8 @@ async function checkBrokerWs() {
12393
12714
  const wsUrl = URLS.BROKER;
12394
12715
  const start = Date.now();
12395
12716
  try {
12396
- const WebSocket3 = (await import("ws")).default;
12397
- const ws = new WebSocket3(wsUrl);
12717
+ const WebSocket4 = (await import("ws")).default;
12718
+ const ws = new WebSocket4(wsUrl);
12398
12719
  const result = await new Promise((resolve2) => {
12399
12720
  const timer = setTimeout(() => {
12400
12721
  try {
@@ -12505,11 +12826,11 @@ __export(exports_status, {
12505
12826
  runStatus: () => runStatus2
12506
12827
  });
12507
12828
  import { statSync as statSync4, existsSync as existsSync16 } from "node:fs";
12508
- import WebSocket3 from "ws";
12829
+ import WebSocket4 from "ws";
12509
12830
  async function probeBroker(url, timeoutMs = 4000) {
12510
12831
  return new Promise((resolve2) => {
12511
12832
  const started = Date.now();
12512
- const ws = new WebSocket3(url);
12833
+ const ws = new WebSocket4(url);
12513
12834
  const timer = setTimeout(() => {
12514
12835
  try {
12515
12836
  ws.terminate();
@@ -14530,17 +14851,17 @@ async function runUnwatch(id, opts) {
14530
14851
  }
14531
14852
  async function runWebhookList(opts) {
14532
14853
  return await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
14533
- const hooks = await client.listWebhooks();
14854
+ const hooks2 = await client.listWebhooks();
14534
14855
  if (opts.json) {
14535
- emitJson(hooks);
14856
+ emitJson(hooks2);
14536
14857
  return EXIT.SUCCESS;
14537
14858
  }
14538
- if (hooks.length === 0) {
14859
+ if (hooks2.length === 0) {
14539
14860
  render.info(dim("(no webhooks)"));
14540
14861
  return EXIT.SUCCESS;
14541
14862
  }
14542
- render.section(`webhooks (${hooks.length})`);
14543
- for (const h of hooks) {
14863
+ render.section(`webhooks (${hooks2.length})`);
14864
+ for (const h of hooks2) {
14544
14865
  const dot = h.active ? "●" : dim("○");
14545
14866
  process.stdout.write(` ${dot} ${bold(h.name)} ${dim("· " + h.url)}
14546
14867
  `);
@@ -15511,7 +15832,7 @@ __export(exports_file, {
15511
15832
  runFileShare: () => runFileShare,
15512
15833
  runFileGet: () => runFileGet
15513
15834
  });
15514
- import { hostname as osHostname } from "node:os";
15835
+ import { hostname as osHostname2 } from "node:os";
15515
15836
  import { resolve as resolvePath, basename, dirname as dirname9 } from "node:path";
15516
15837
  import { statSync as statSync7, existsSync as existsSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync11 } from "node:fs";
15517
15838
  function emitJson2(data) {
@@ -15543,7 +15864,7 @@ async function runFileShare(filePath, opts) {
15543
15864
  return await withMesh({ meshSlug: opts.mesh ?? null }, async (client, mesh) => {
15544
15865
  if (opts.to && !opts.upload) {
15545
15866
  const peers = await client.listPeers();
15546
- const myHost = osHostname();
15867
+ const myHost = osHostname2();
15547
15868
  const target = peers.find((p) => {
15548
15869
  if (!p.hostname || p.hostname !== myHost)
15549
15870
  return false;
@@ -15705,7 +16026,7 @@ var require_client = __commonJS((exports) => {
15705
16026
  var ws_1 = __importDefault(__require("ws"));
15706
16027
  var crypto_js_1 = require_crypto();
15707
16028
  var MAX_QUEUED2 = 100;
15708
- var HELLO_ACK_TIMEOUT_MS3 = 5000;
16029
+ var HELLO_ACK_TIMEOUT_MS4 = 5000;
15709
16030
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
15710
16031
 
15711
16032
  class MeshClient extends node_events_1.EventEmitter {
@@ -15770,7 +16091,7 @@ var require_client = __commonJS((exports) => {
15770
16091
  this.debug("hello_ack timeout");
15771
16092
  ws.close();
15772
16093
  reject(new Error("hello_ack timeout"));
15773
- }, HELLO_ACK_TIMEOUT_MS3);
16094
+ }, HELLO_ACK_TIMEOUT_MS4);
15774
16095
  };
15775
16096
  const onMessage = (raw) => {
15776
16097
  let msg;
@@ -19550,4 +19871,4 @@ main().catch((err) => {
19550
19871
  process.exit(EXIT.INTERNAL_ERROR);
19551
19872
  });
19552
19873
 
19553
- //# debugId=FBD494B01BF81C7164756E2164756E21
19874
+ //# debugId=A7687DD2912DA01C64756E2164756E21