claudemesh-cli 1.32.1 → 1.34.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.
@@ -104,7 +104,7 @@ __export(exports_urls, {
104
104
  VERSION: () => VERSION,
105
105
  URLS: () => URLS
106
106
  });
107
- var URLS, VERSION = "1.32.1", env;
107
+ var URLS, VERSION = "1.34.0", env;
108
108
  var init_urls = __esm(() => {
109
109
  URLS = {
110
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -4019,6 +4019,7 @@ __export(exports_daemon_route, {
4019
4019
  tryListStateViaDaemon: () => tryListStateViaDaemon,
4020
4020
  tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
4021
4021
  tryListPeersViaDaemon: () => tryListPeersViaDaemon,
4022
+ tryListInboxViaDaemon: () => tryListInboxViaDaemon,
4022
4023
  tryGetStateViaDaemon: () => tryGetStateViaDaemon,
4023
4024
  tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
4024
4025
  tryForgetViaDaemon: () => tryForgetViaDaemon
@@ -4049,6 +4050,22 @@ async function tryListPeersViaDaemon(mesh) {
4049
4050
  return null;
4050
4051
  }
4051
4052
  }
4053
+ async function tryListInboxViaDaemon(mesh, limit = 100) {
4054
+ if (!await daemonReachable())
4055
+ return null;
4056
+ try {
4057
+ const path = `/v1/inbox${meshQuery(mesh)}${meshQuery(mesh) ? "&" : "?"}limit=${limit}`;
4058
+ const res = await ipc({ path, timeoutMs: 3000 });
4059
+ if (res.status !== 200)
4060
+ return null;
4061
+ return Array.isArray(res.body.items) ? res.body.items : [];
4062
+ } catch (err) {
4063
+ const msg = String(err);
4064
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4065
+ return null;
4066
+ return null;
4067
+ }
4068
+ }
4052
4069
  async function tryListSkillsViaDaemon(mesh) {
4053
4070
  if (!await daemonReachable())
4054
4071
  return null;
@@ -7948,8 +7965,15 @@ async function listPeersForMesh(slug) {
7948
7965
  function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
7949
7966
  const isSelf = !!(selfMemberPubkey && peer.memberPubkey && peer.memberPubkey === selfMemberPubkey);
7950
7967
  const isThisSession = !!(isSelf && selfSessionPubkey && peer.pubkey === selfSessionPubkey);
7951
- const role = peer.profile?.role?.trim() || undefined;
7952
- return { ...peer, ...role ? { role } : {}, isSelf, isThisSession };
7968
+ const peerRole = peer.peerRole ?? "session";
7969
+ const profileRole = peer.profile?.role?.trim() || undefined;
7970
+ return {
7971
+ ...peer,
7972
+ ...profileRole ? { role: profileRole } : {},
7973
+ peerRole,
7974
+ isSelf,
7975
+ isThisSession
7976
+ };
7953
7977
  }
7954
7978
  async function runPeers(flags) {
7955
7979
  const config = readConfig();
@@ -7977,13 +8001,13 @@ async function runPeers(flags) {
7977
8001
  allJson.push({ mesh: slug, peers: projected });
7978
8002
  continue;
7979
8003
  }
7980
- const visible = flags.all ? peers : peers.filter((p) => p.channel !== "claudemesh-daemon");
8004
+ const visible = flags.all ? peers : peers.filter((p) => p.peerRole !== "control-plane");
7981
8005
  const sorted = visible.slice().sort((a, b) => {
7982
8006
  const score = (p) => p.isThisSession ? 0 : p.isSelf ? 1 : 2;
7983
8007
  return score(a) - score(b);
7984
8008
  });
7985
- const hiddenDaemons = peers.length - visible.length;
7986
- const header = hiddenDaemons > 0 ? `peers on ${slug} (${sorted.length}, ${hiddenDaemons} daemon hidden — use --all)` : `peers on ${slug} (${sorted.length})`;
8009
+ const hiddenControlPlane = peers.length - visible.length;
8010
+ const header = hiddenControlPlane > 0 ? `peers on ${slug} (${sorted.length}, ${hiddenControlPlane} control-plane hidden — use --all)` : `peers on ${slug} (${sorted.length})`;
7987
8011
  render.section(header);
7988
8012
  if (sorted.length === 0) {
7989
8013
  render.info(dim(" (no peers connected)"));
@@ -8220,38 +8244,47 @@ var exports_inbox = {};
8220
8244
  __export(exports_inbox, {
8221
8245
  runInbox: () => runInbox
8222
8246
  });
8223
- function formatMessage(msg) {
8224
- const text = msg.plaintext ?? `[encrypted: ${msg.ciphertext.slice(0, 32)}…]`;
8225
- const from = msg.senderPubkey.slice(0, 8);
8226
- const time = new Date(msg.createdAt).toLocaleTimeString();
8227
- const kindTag = msg.kind === "direct" ? "→ direct" : msg.kind;
8228
- return ` ${bold(from)} ${dim(`[${kindTag}] ${time}`)}
8247
+ function formatMessage(msg, includeMesh) {
8248
+ const text = msg.body ?? "[encrypted]";
8249
+ const from = msg.sender_name && msg.sender_name !== msg.sender_pubkey.slice(0, 8) ? `${msg.sender_name} (${msg.sender_pubkey.slice(0, 8)})` : msg.sender_pubkey.slice(0, 8);
8250
+ const time = new Date(msg.received_at).toLocaleTimeString();
8251
+ const topicTag = msg.topic ? ` (#${msg.topic})` : "";
8252
+ const meshTag = includeMesh ? ` [${msg.mesh}]` : "";
8253
+ return ` ${bold(from)} ${dim(`${meshTag}${topicTag} ${time}`)}
8229
8254
  ${text}`;
8230
8255
  }
8231
8256
  async function runInbox(flags) {
8232
- const waitMs = (flags.wait ?? 1) * 1000;
8233
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
8234
- await new Promise((resolve) => setTimeout(resolve, waitMs));
8235
- const messages = client.drainPushBuffer();
8257
+ const meshSlug = flags.mesh;
8258
+ const items = await tryListInboxViaDaemon(meshSlug, flags.limit ?? 100);
8259
+ if (items === null) {
8236
8260
  if (flags.json) {
8237
- process.stdout.write(JSON.stringify(messages, null, 2) + `
8261
+ process.stdout.write(`[]
8238
8262
  `);
8239
8263
  return;
8240
8264
  }
8241
- if (messages.length === 0) {
8242
- render.info(dim(`No messages on mesh "${mesh.slug}".`));
8243
- return;
8244
- }
8245
- render.section(`inbox — ${mesh.slug} (${messages.length} message${messages.length === 1 ? "" : "s"})`);
8246
- for (const msg of messages) {
8247
- process.stdout.write(formatMessage(msg) + `
8265
+ render.info(dim("Daemon not reachable. Run `claudemesh daemon up` and retry."));
8266
+ return;
8267
+ }
8268
+ if (flags.json) {
8269
+ process.stdout.write(JSON.stringify(items, null, 2) + `
8270
+ `);
8271
+ return;
8272
+ }
8273
+ if (items.length === 0) {
8274
+ const scope = meshSlug ? `mesh "${meshSlug}"` : "any mesh";
8275
+ render.info(dim(`No messages on ${scope}.`));
8276
+ return;
8277
+ }
8278
+ const heading = meshSlug ? `inbox — ${meshSlug} (${items.length} message${items.length === 1 ? "" : "s"})` : `inbox (${items.length} message${items.length === 1 ? "" : "s"})`;
8279
+ render.section(heading);
8280
+ for (const msg of items) {
8281
+ process.stdout.write(formatMessage(msg, !meshSlug) + `
8248
8282
 
8249
8283
  `);
8250
- }
8251
- });
8284
+ }
8252
8285
  }
8253
8286
  var init_inbox = __esm(() => {
8254
- init_connect();
8287
+ init_daemon_route();
8255
8288
  init_render();
8256
8289
  init_styles();
8257
8290
  });
@@ -9142,6 +9175,9 @@ function migrateOutbox(db) {
9142
9175
  db.exec(`ALTER TABLE outbox ADD COLUMN ciphertext TEXT`);
9143
9176
  if (!hasPriority)
9144
9177
  db.exec(`ALTER TABLE outbox ADD COLUMN priority TEXT`);
9178
+ const hasSenderSessionPk = columnExists(db, "outbox", "sender_session_pubkey");
9179
+ if (!hasSenderSessionPk)
9180
+ db.exec(`ALTER TABLE outbox ADD COLUMN sender_session_pubkey TEXT`);
9145
9181
  }
9146
9182
  function columnExists(db, table, column) {
9147
9183
  const rows = db.prepare(`PRAGMA table_info(${table})`).all();
@@ -9152,7 +9188,8 @@ function findByClientId(db, clientMessageId) {
9152
9188
  SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
9153
9189
  attempts, next_attempt_at, status, last_error, delivered_at,
9154
9190
  broker_message_id, aborted_at, aborted_by, superseded_by,
9155
- mesh, target_spec, nonce, ciphertext, priority
9191
+ mesh, target_spec, nonce, ciphertext, priority,
9192
+ sender_session_pubkey
9156
9193
  FROM outbox WHERE client_message_id = ?
9157
9194
  `).get(clientMessageId);
9158
9195
  return row ?? null;
@@ -9162,9 +9199,10 @@ function insertPending(db, input) {
9162
9199
  INSERT INTO outbox (
9163
9200
  id, client_message_id, request_fingerprint, payload,
9164
9201
  enqueued_at, attempts, next_attempt_at, status,
9165
- mesh, target_spec, nonce, ciphertext, priority
9166
- ) VALUES (?, ?, ?, ?, ?, 0, ?, 'pending', ?, ?, ?, ?, ?)
9167
- `).run(input.id, input.client_message_id, input.request_fingerprint, input.payload, input.now, input.now, input.mesh ?? null, input.target_spec ?? null, input.nonce ?? null, input.ciphertext ?? null, input.priority ?? null);
9202
+ mesh, target_spec, nonce, ciphertext, priority,
9203
+ sender_session_pubkey
9204
+ ) VALUES (?, ?, ?, ?, ?, 0, ?, 'pending', ?, ?, ?, ?, ?, ?)
9205
+ `).run(input.id, input.client_message_id, input.request_fingerprint, input.payload, input.now, input.now, input.mesh ?? null, input.target_spec ?? null, input.nonce ?? null, input.ciphertext ?? null, input.priority ?? null, input.sender_session_pubkey ?? null);
9168
9206
  }
9169
9207
  function fingerprintsEqual(a, b) {
9170
9208
  if (a.length !== b.length)
@@ -9185,7 +9223,8 @@ function listOutbox(db, p = {}) {
9185
9223
  SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
9186
9224
  attempts, next_attempt_at, status, last_error, delivered_at,
9187
9225
  broker_message_id, aborted_at, aborted_by, superseded_by,
9188
- mesh, target_spec, nonce, ciphertext, priority
9226
+ mesh, target_spec, nonce, ciphertext, priority,
9227
+ sender_session_pubkey
9189
9228
  FROM outbox
9190
9229
  ${where.length ? "WHERE " + where.join(" AND ") : ""}
9191
9230
  ORDER BY enqueued_at DESC
@@ -9199,7 +9238,8 @@ function findById(db, id) {
9199
9238
  SELECT id, client_message_id, request_fingerprint, payload, enqueued_at,
9200
9239
  attempts, next_attempt_at, status, last_error, delivered_at,
9201
9240
  broker_message_id, aborted_at, aborted_by, superseded_by,
9202
- mesh, target_spec, nonce, ciphertext, priority
9241
+ mesh, target_spec, nonce, ciphertext, priority,
9242
+ sender_session_pubkey
9203
9243
  FROM outbox WHERE id = ?
9204
9244
  `).get(id) ?? null;
9205
9245
  }
@@ -9347,7 +9387,8 @@ function acceptSend(req, deps) {
9347
9387
  target_spec: req.target_spec,
9348
9388
  nonce: req.nonce,
9349
9389
  ciphertext: req.ciphertext,
9350
- priority: req.priority
9390
+ priority: req.priority,
9391
+ sender_session_pubkey: req.sender_session_pubkey
9351
9392
  });
9352
9393
  return { kind: "accepted_pending", status: 202, client_message_id: clientId };
9353
9394
  }
@@ -9446,6 +9487,10 @@ function listInbox(db, p) {
9446
9487
  where.push("sender_pubkey = ?");
9447
9488
  args.push(p.fromPubkey);
9448
9489
  }
9490
+ if (p.mesh !== undefined) {
9491
+ where.push("mesh = ?");
9492
+ args.push(p.mesh);
9493
+ }
9449
9494
  const sql = `
9450
9495
  SELECT id, client_message_id, broker_message_id, mesh, topic,
9451
9496
  sender_pubkey, sender_name, body, meta, received_at, reply_to_id
@@ -10197,10 +10242,12 @@ function makeHandler(opts) {
10197
10242
  const fromPubkey = url.searchParams.get("from") ?? undefined;
10198
10243
  const limitRaw = url.searchParams.get("limit");
10199
10244
  const limit = limitRaw ? Number.parseInt(limitRaw, 10) : undefined;
10245
+ const meshFilter = meshFromCtx(url.searchParams.get("mesh")) ?? undefined;
10200
10246
  const rows = listInbox(opts.inboxDb, {
10201
10247
  since: Number.isFinite(since) ? since : undefined,
10202
10248
  topic,
10203
10249
  fromPubkey,
10250
+ ...meshFilter ? { mesh: meshFilter } : {},
10204
10251
  limit: Number.isFinite(limit ?? NaN) ? limit : undefined
10205
10252
  });
10206
10253
  respond(res, 200, {
@@ -10317,12 +10364,17 @@ function makeHandler(opts) {
10317
10364
  respond(res, 404, { error: "mesh_not_attached", mesh: chosenSlug });
10318
10365
  return;
10319
10366
  }
10367
+ const senderSessionPubkey = session?.presence?.sessionPubkey;
10368
+ const senderSecretKey = session?.presence?.sessionSecretKey ?? meshCfg.secretKey;
10320
10369
  try {
10321
- const routed = await resolveAndEncrypt(parsed.req, broker, meshCfg.secretKey, chosenSlug);
10370
+ const routed = await resolveAndEncrypt(parsed.req, broker, senderSecretKey, chosenSlug);
10322
10371
  parsed.req.target_spec = routed.target_spec;
10323
10372
  parsed.req.ciphertext = routed.ciphertext;
10324
10373
  parsed.req.nonce = routed.nonce;
10325
10374
  parsed.req.mesh = routed.mesh;
10375
+ if (senderSessionPubkey) {
10376
+ parsed.req.sender_session_pubkey = senderSessionPubkey;
10377
+ }
10326
10378
  } catch (e) {
10327
10379
  respond(res, 502, { error: "route_failed", detail: String(e) });
10328
10380
  return;
@@ -10450,9 +10502,7 @@ async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
10450
10502
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
10451
10503
  }
10452
10504
  if (/^[0-9a-f]{64}$/i.test(to)) {
10453
- const sessionKeys2 = broker.getSessionKeys();
10454
- const senderSecret2 = sessionKeys2?.sessionSecretKey ?? meshSecretKey;
10455
- const env3 = await encryptDirect2(req.message, to, senderSecret2);
10505
+ const env3 = await encryptDirect2(req.message, to, meshSecretKey);
10456
10506
  return { target_spec: to, ciphertext: env3.ciphertext, nonce: env3.nonce, mesh: meshSlug ?? "" };
10457
10507
  }
10458
10508
  const peers = await broker.listPeers().catch(() => []);
@@ -10463,18 +10513,14 @@ async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
10463
10513
  if (matches2.length > 1)
10464
10514
  throw new Error(`prefix "${to}" is ambiguous (${matches2.length} matches)`);
10465
10515
  const recipient2 = matches2[0].pubkey;
10466
- const sessionKeys2 = broker.getSessionKeys();
10467
- const senderSecret2 = sessionKeys2?.sessionSecretKey ?? meshSecretKey;
10468
- const env3 = await encryptDirect2(req.message, recipient2, senderSecret2);
10516
+ const env3 = await encryptDirect2(req.message, recipient2, meshSecretKey);
10469
10517
  return { target_spec: recipient2, ciphertext: env3.ciphertext, nonce: env3.nonce, mesh: meshSlug ?? "" };
10470
10518
  }
10471
10519
  const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
10472
10520
  if (!match)
10473
10521
  throw new Error(`peer "${to}" not found`);
10474
10522
  const recipient = match.pubkey;
10475
- const sessionKeys = broker.getSessionKeys();
10476
- const senderSecret = sessionKeys?.sessionSecretKey ?? meshSecretKey;
10477
- const env2 = await encryptDirect2(req.message, recipient, senderSecret);
10523
+ const env2 = await encryptDirect2(req.message, recipient, meshSecretKey);
10478
10524
  return { target_spec: recipient, ciphertext: env2.ciphertext, nonce: env2.nonce, mesh: meshSlug ?? "" };
10479
10525
  }
10480
10526
  function respond(res, status, body) {
@@ -10491,18 +10537,149 @@ var init_server = __esm(() => {
10491
10537
  init_urls();
10492
10538
  });
10493
10539
 
10494
- // src/daemon/broker.ts
10540
+ // src/daemon/ws-lifecycle.ts
10495
10541
  import WebSocket2 from "ws";
10542
+ function connectWsWithBackoff(opts) {
10543
+ const helloAckTimeoutMs = opts.helloAckTimeoutMs ?? DEFAULT_HELLO_ACK_TIMEOUT_MS;
10544
+ const backoffCapsMs = opts.backoffCapsMs ?? DEFAULT_BACKOFF_CAPS_MS;
10545
+ const log2 = opts.log ?? defaultLog;
10546
+ let ws = null;
10547
+ let status = "closed";
10548
+ let closed = false;
10549
+ let reconnectAttempt = 0;
10550
+ let reconnectTimer = null;
10551
+ let helloTimer = null;
10552
+ const setStatus = (s) => {
10553
+ if (status === s)
10554
+ return;
10555
+ status = s;
10556
+ opts.onStatusChange?.(s);
10557
+ };
10558
+ const openOnce = () => {
10559
+ if (closed)
10560
+ return Promise.reject(new Error("client_closed"));
10561
+ setStatus("connecting");
10562
+ log2("info", "ws_open_attempt", { url: opts.url });
10563
+ const sock = new WebSocket2(opts.url);
10564
+ ws = sock;
10565
+ return new Promise((resolve, reject) => {
10566
+ sock.on("open", () => {
10567
+ log2("info", "ws_open_ok", { url: opts.url });
10568
+ (async () => {
10569
+ try {
10570
+ const hello = await opts.buildHello();
10571
+ sock.send(JSON.stringify(hello));
10572
+ log2("info", "ws_hello_sent", { url: opts.url });
10573
+ helloTimer = setTimeout(() => {
10574
+ log2("warn", "hello_ack_timeout", { url: opts.url });
10575
+ try {
10576
+ sock.close();
10577
+ } catch {}
10578
+ reject(new Error("hello_ack_timeout"));
10579
+ }, helloAckTimeoutMs);
10580
+ } catch (e) {
10581
+ log2("warn", "ws_build_hello_threw", { err: String(e) });
10582
+ reject(e instanceof Error ? e : new Error(String(e)));
10583
+ }
10584
+ })();
10585
+ });
10586
+ sock.on("message", (raw) => {
10587
+ let msg;
10588
+ try {
10589
+ msg = JSON.parse(raw.toString());
10590
+ } catch {
10591
+ return;
10592
+ }
10593
+ if (opts.isHelloAck(msg)) {
10594
+ if (helloTimer) {
10595
+ clearTimeout(helloTimer);
10596
+ helloTimer = null;
10597
+ }
10598
+ setStatus("open");
10599
+ reconnectAttempt = 0;
10600
+ log2("info", "ws_hello_acked", { url: opts.url });
10601
+ resolve();
10602
+ return;
10603
+ }
10604
+ opts.onMessage(msg);
10605
+ });
10606
+ sock.on("close", (code, reason) => {
10607
+ if (helloTimer) {
10608
+ clearTimeout(helloTimer);
10609
+ helloTimer = null;
10610
+ }
10611
+ const reasonStr = reason.toString("utf8");
10612
+ log2("warn", "ws_closed", { url: opts.url, code, reason: reasonStr, status });
10613
+ opts.onBeforeReconnect?.(code, reasonStr);
10614
+ if (closed) {
10615
+ setStatus("closed");
10616
+ return;
10617
+ }
10618
+ setStatus("reconnecting");
10619
+ const wait = backoffCapsMs[Math.min(reconnectAttempt, backoffCapsMs.length - 1)] ?? 30000;
10620
+ reconnectAttempt++;
10621
+ log2("info", "ws_reconnect_scheduled", { url: opts.url, wait_ms: wait, code, reason: reasonStr });
10622
+ reconnectTimer = setTimeout(() => openOnce().catch((err) => log2("warn", "ws_reconnect_failed", { url: opts.url, err: String(err) })), wait);
10623
+ if (status === "connecting" || status === "reconnecting") {
10624
+ reject(new Error(`closed_before_hello_${code}`));
10625
+ }
10626
+ });
10627
+ sock.on("error", (err) => log2("warn", "ws_error", { url: opts.url, err: err.message }));
10628
+ });
10629
+ };
10630
+ return openOnce().then(() => {
10631
+ const handle = {
10632
+ get status() {
10633
+ return status;
10634
+ },
10635
+ get ws() {
10636
+ return ws;
10637
+ },
10638
+ send(payload) {
10639
+ if (!ws || ws.readyState !== ws.OPEN) {
10640
+ throw new Error("ws_not_open");
10641
+ }
10642
+ ws.send(JSON.stringify(payload));
10643
+ },
10644
+ async close() {
10645
+ closed = true;
10646
+ if (reconnectTimer) {
10647
+ clearTimeout(reconnectTimer);
10648
+ reconnectTimer = null;
10649
+ }
10650
+ if (helloTimer) {
10651
+ clearTimeout(helloTimer);
10652
+ helloTimer = null;
10653
+ }
10654
+ try {
10655
+ ws?.close();
10656
+ } catch {}
10657
+ setStatus("closed");
10658
+ }
10659
+ };
10660
+ return handle;
10661
+ });
10662
+ }
10663
+ var DEFAULT_HELLO_ACK_TIMEOUT_MS = 5000, DEFAULT_BACKOFF_CAPS_MS, defaultLog = (level, msg, meta) => {
10664
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10665
+ if (level === "info")
10666
+ process.stdout.write(line + `
10667
+ `);
10668
+ else
10669
+ process.stderr.write(line + `
10670
+ `);
10671
+ };
10672
+ var init_ws_lifecycle = __esm(() => {
10673
+ DEFAULT_BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
10674
+ });
10496
10675
 
10676
+ // src/daemon/broker.ts
10497
10677
  class DaemonBrokerClient {
10498
10678
  mesh;
10499
10679
  opts;
10500
- ws = null;
10680
+ lifecycle = null;
10501
10681
  _status = "closed";
10502
10682
  closed = false;
10503
- reconnectAttempt = 0;
10504
- reconnectTimer = null;
10505
- helloTimer = null;
10506
10683
  pendingAcks = new Map;
10507
10684
  peerListResolvers = new Map;
10508
10685
  skillListResolvers = new Map;
@@ -10511,8 +10688,6 @@ class DaemonBrokerClient {
10511
10688
  stateListResolvers = new Map;
10512
10689
  memoryStoreResolvers = new Map;
10513
10690
  memoryRecallResolvers = new Map;
10514
- sessionPubkey = null;
10515
- sessionSecretKey = null;
10516
10691
  opens = [];
10517
10692
  reqCounter = 0;
10518
10693
  constructor(mesh, opts = {}) {
@@ -10529,72 +10704,39 @@ class DaemonBrokerClient {
10529
10704
  return this.mesh.meshId;
10530
10705
  }
10531
10706
  log = (level, msg, meta) => {
10532
- (this.opts.log ?? defaultLog)(level, msg, { mesh: this.mesh.slug, ...meta });
10707
+ (this.opts.log ?? defaultLog2)(level, msg, { mesh: this.mesh.slug, ...meta });
10533
10708
  };
10534
- setConnStatus(s) {
10535
- if (this._status === s)
10536
- return;
10537
- this._status = s;
10538
- this.opts.onStatusChange?.(s);
10539
- }
10540
10709
  async connect() {
10541
10710
  if (this.closed)
10542
10711
  throw new Error("client_closed");
10543
10712
  if (this._status === "connecting" || this._status === "open")
10544
10713
  return;
10545
- this.setConnStatus("connecting");
10546
- const ws = new WebSocket2(this.mesh.brokerUrl);
10547
- this.ws = ws;
10548
- return new Promise((resolve, reject) => {
10549
- ws.on("open", async () => {
10550
- try {
10551
- if (!this.sessionPubkey) {
10552
- const { generateKeypair: generateKeypair4 } = await Promise.resolve().then(() => (init_facade7(), exports_facade4));
10553
- const kp = await generateKeypair4();
10554
- this.sessionPubkey = kp.publicKey;
10555
- this.sessionSecretKey = kp.secretKey;
10556
- }
10557
- const { timestamp: timestamp2, signature } = await signHello(this.mesh.meshId, this.mesh.memberId, this.mesh.pubkey, this.mesh.secretKey);
10558
- ws.send(JSON.stringify({
10559
- type: "hello",
10560
- meshId: this.mesh.meshId,
10561
- memberId: this.mesh.memberId,
10562
- pubkey: this.mesh.pubkey,
10563
- sessionPubkey: this.sessionPubkey,
10564
- displayName: this.opts.displayName,
10565
- sessionId: `daemon-${process.pid}`,
10566
- pid: process.pid,
10567
- cwd: process.cwd(),
10568
- hostname: __require("node:os").hostname(),
10569
- peerType: "ai",
10570
- channel: "claudemesh-daemon",
10571
- timestamp: timestamp2,
10572
- signature
10573
- }));
10574
- this.helloTimer = setTimeout(() => {
10575
- this.log("warn", "broker_hello_ack_timeout");
10576
- try {
10577
- ws.close();
10578
- } catch {}
10579
- reject(new Error("hello_ack_timeout"));
10580
- }, HELLO_ACK_TIMEOUT_MS2);
10581
- } catch (e) {
10582
- reject(e instanceof Error ? e : new Error(String(e)));
10583
- }
10584
- });
10585
- ws.on("message", (raw) => {
10586
- let msg;
10587
- try {
10588
- msg = JSON.parse(raw.toString());
10589
- } catch {
10590
- return;
10591
- }
10592
- if (msg.type === "hello_ack") {
10593
- if (this.helloTimer)
10594
- clearTimeout(this.helloTimer);
10595
- this.helloTimer = null;
10596
- this.setConnStatus("open");
10597
- this.reconnectAttempt = 0;
10714
+ this.lifecycle = await connectWsWithBackoff({
10715
+ url: this.mesh.brokerUrl,
10716
+ buildHello: async () => {
10717
+ const { timestamp: timestamp2, signature } = await signHello(this.mesh.meshId, this.mesh.memberId, this.mesh.pubkey, this.mesh.secretKey);
10718
+ return {
10719
+ type: "hello",
10720
+ meshId: this.mesh.meshId,
10721
+ memberId: this.mesh.memberId,
10722
+ pubkey: this.mesh.pubkey,
10723
+ displayName: this.opts.displayName,
10724
+ sessionId: `daemon-${process.pid}`,
10725
+ pid: process.pid,
10726
+ cwd: process.cwd(),
10727
+ hostname: __require("node:os").hostname(),
10728
+ peerType: "ai",
10729
+ channel: "claudemesh-daemon",
10730
+ timestamp: timestamp2,
10731
+ signature
10732
+ };
10733
+ },
10734
+ isHelloAck: (msg) => msg.type === "hello_ack",
10735
+ onMessage: (msg) => this.handleMessage(msg),
10736
+ onStatusChange: (s) => {
10737
+ this._status = s;
10738
+ this.opts.onStatusChange?.(s);
10739
+ if (s === "open") {
10598
10740
  const queued = this.opens.slice();
10599
10741
  this.opens.length = 0;
10600
10742
  for (const fn of queued) {
@@ -10604,123 +10746,121 @@ class DaemonBrokerClient {
10604
10746
  this.log("warn", "open_handler_failed", { err: String(e) });
10605
10747
  }
10606
10748
  }
10607
- resolve();
10608
- return;
10609
- }
10610
- if (msg.type === "ack") {
10611
- const id = String(msg.id ?? "");
10612
- const ack = this.pendingAcks.get(id);
10613
- if (ack) {
10614
- this.pendingAcks.delete(id);
10615
- clearTimeout(ack.timer);
10616
- if (typeof msg.error === "string" && msg.error.length > 0) {
10617
- ack.resolve({ ok: false, error: msg.error, permanent: classifyPermanent(msg.error) });
10618
- } else {
10619
- ack.resolve({ ok: true, messageId: String(msg.messageId ?? id) });
10620
- }
10621
- }
10622
- return;
10623
- }
10624
- if (msg.type === "peers_list") {
10625
- const reqId = String(msg._reqId ?? "");
10626
- const pending = this.peerListResolvers.get(reqId);
10627
- if (pending) {
10628
- this.peerListResolvers.delete(reqId);
10629
- clearTimeout(pending.timer);
10630
- pending.resolve(Array.isArray(msg.peers) ? msg.peers : []);
10631
- }
10632
- return;
10633
- }
10634
- if (msg.type === "skill_list") {
10635
- const reqId = String(msg._reqId ?? "");
10636
- const pending = this.skillListResolvers.get(reqId);
10637
- if (pending) {
10638
- this.skillListResolvers.delete(reqId);
10639
- clearTimeout(pending.timer);
10640
- pending.resolve(Array.isArray(msg.skills) ? msg.skills : []);
10641
- }
10642
- return;
10643
- }
10644
- if (msg.type === "skill_data") {
10645
- const reqId = String(msg._reqId ?? "");
10646
- const pending = this.skillDataResolvers.get(reqId);
10647
- if (pending) {
10648
- this.skillDataResolvers.delete(reqId);
10649
- clearTimeout(pending.timer);
10650
- pending.resolve(msg.skill ?? null);
10651
- }
10652
- return;
10653
- }
10654
- if (msg.type === "state_value" || msg.type === "state_data") {
10655
- const reqId = String(msg._reqId ?? "");
10656
- const pending = this.stateGetResolvers.get(reqId);
10657
- if (pending) {
10658
- this.stateGetResolvers.delete(reqId);
10659
- clearTimeout(pending.timer);
10660
- pending.resolve(msg.state ?? msg.row ?? null);
10661
- }
10662
- return;
10663
10749
  }
10664
- if (msg.type === "state_list") {
10665
- const reqId = String(msg._reqId ?? "");
10666
- const pending = this.stateListResolvers.get(reqId);
10667
- if (pending) {
10668
- this.stateListResolvers.delete(reqId);
10669
- clearTimeout(pending.timer);
10670
- pending.resolve(Array.isArray(msg.entries) ? msg.entries : []);
10671
- }
10672
- return;
10673
- }
10674
- if (msg.type === "memory_stored") {
10675
- const reqId = String(msg._reqId ?? "");
10676
- const pending = this.memoryStoreResolvers.get(reqId);
10677
- if (pending) {
10678
- this.memoryStoreResolvers.delete(reqId);
10679
- clearTimeout(pending.timer);
10680
- pending.resolve(typeof msg.memoryId === "string" ? msg.memoryId : null);
10681
- }
10682
- return;
10683
- }
10684
- if (msg.type === "memory_recall_result") {
10685
- const reqId = String(msg._reqId ?? "");
10686
- const pending = this.memoryRecallResolvers.get(reqId);
10687
- if (pending) {
10688
- this.memoryRecallResolvers.delete(reqId);
10689
- clearTimeout(pending.timer);
10690
- pending.resolve(Array.isArray(msg.matches) ? msg.matches : []);
10691
- }
10692
- return;
10693
- }
10694
- if (msg.type === "push" || msg.type === "inbound") {
10695
- this.opts.onPush?.(msg);
10696
- return;
10697
- }
10698
- });
10699
- ws.on("close", (code, reason) => {
10700
- if (this.helloTimer) {
10701
- clearTimeout(this.helloTimer);
10702
- this.helloTimer = null;
10703
- }
10704
- this.failPendingAcks(`broker_disconnected_${code}`);
10705
- if (this.closed) {
10706
- this.setConnStatus("closed");
10707
- return;
10750
+ },
10751
+ onBeforeReconnect: (code) => this.failPendingAcks(`broker_disconnected_${code}`),
10752
+ log: (level, msg, meta) => this.log(level, `broker_${msg}`, meta)
10753
+ });
10754
+ }
10755
+ handleMessage(msg) {
10756
+ if (msg.type === "ack") {
10757
+ const id = String(msg.id ?? "");
10758
+ const ack = this.pendingAcks.get(id);
10759
+ if (ack) {
10760
+ this.pendingAcks.delete(id);
10761
+ clearTimeout(ack.timer);
10762
+ if (typeof msg.error === "string" && msg.error.length > 0) {
10763
+ ack.resolve({ ok: false, error: msg.error, permanent: classifyPermanent(msg.error) });
10764
+ } else {
10765
+ ack.resolve({ ok: true, messageId: String(msg.messageId ?? id) });
10708
10766
  }
10709
- this.setConnStatus("reconnecting");
10710
- const wait = BACKOFF_CAPS_MS[Math.min(this.reconnectAttempt, BACKOFF_CAPS_MS.length - 1)] ?? 30000;
10711
- this.reconnectAttempt++;
10712
- this.log("info", "broker_reconnect_scheduled", { wait_ms: wait, code, reason: reason.toString("utf8") });
10713
- this.reconnectTimer = setTimeout(() => this.connect().catch((err) => this.log("warn", "broker_reconnect_failed", { err: String(err) })), wait);
10714
- if (this._status === "connecting")
10715
- reject(new Error(`closed_before_hello_${code}`));
10767
+ }
10768
+ return;
10769
+ }
10770
+ if (msg.type === "peers_list") {
10771
+ const reqId = String(msg._reqId ?? "");
10772
+ const pending = this.peerListResolvers.get(reqId);
10773
+ if (pending) {
10774
+ this.peerListResolvers.delete(reqId);
10775
+ clearTimeout(pending.timer);
10776
+ pending.resolve(Array.isArray(msg.peers) ? msg.peers : []);
10777
+ }
10778
+ return;
10779
+ }
10780
+ if (msg.type === "skill_list") {
10781
+ const reqId = String(msg._reqId ?? "");
10782
+ const pending = this.skillListResolvers.get(reqId);
10783
+ if (pending) {
10784
+ this.skillListResolvers.delete(reqId);
10785
+ clearTimeout(pending.timer);
10786
+ pending.resolve(Array.isArray(msg.skills) ? msg.skills : []);
10787
+ }
10788
+ return;
10789
+ }
10790
+ if (msg.type === "skill_data") {
10791
+ const reqId = String(msg._reqId ?? "");
10792
+ const pending = this.skillDataResolvers.get(reqId);
10793
+ if (pending) {
10794
+ this.skillDataResolvers.delete(reqId);
10795
+ clearTimeout(pending.timer);
10796
+ pending.resolve(msg.skill ?? null);
10797
+ }
10798
+ return;
10799
+ }
10800
+ if (msg.type === "state_value" || msg.type === "state_data") {
10801
+ const reqId = String(msg._reqId ?? "");
10802
+ const pending = this.stateGetResolvers.get(reqId);
10803
+ if (pending) {
10804
+ this.stateGetResolvers.delete(reqId);
10805
+ clearTimeout(pending.timer);
10806
+ pending.resolve(msg.state ?? msg.row ?? null);
10807
+ }
10808
+ return;
10809
+ }
10810
+ if (msg.type === "state_list") {
10811
+ const reqId = String(msg._reqId ?? "");
10812
+ const pending = this.stateListResolvers.get(reqId);
10813
+ if (pending) {
10814
+ this.stateListResolvers.delete(reqId);
10815
+ clearTimeout(pending.timer);
10816
+ pending.resolve(Array.isArray(msg.entries) ? msg.entries : []);
10817
+ }
10818
+ return;
10819
+ }
10820
+ if (msg.type === "memory_stored") {
10821
+ const reqId = String(msg._reqId ?? "");
10822
+ const pending = this.memoryStoreResolvers.get(reqId);
10823
+ if (pending) {
10824
+ this.memoryStoreResolvers.delete(reqId);
10825
+ clearTimeout(pending.timer);
10826
+ pending.resolve(typeof msg.memoryId === "string" ? msg.memoryId : null);
10827
+ }
10828
+ return;
10829
+ }
10830
+ if (msg.type === "memory_recall_result") {
10831
+ const reqId = String(msg._reqId ?? "");
10832
+ const pending = this.memoryRecallResolvers.get(reqId);
10833
+ if (pending) {
10834
+ this.memoryRecallResolvers.delete(reqId);
10835
+ clearTimeout(pending.timer);
10836
+ pending.resolve(Array.isArray(msg.matches) ? msg.matches : []);
10837
+ }
10838
+ return;
10839
+ }
10840
+ if (msg.type === "push" || msg.type === "inbound") {
10841
+ this.opts.onPush?.(msg);
10842
+ return;
10843
+ }
10844
+ }
10845
+ isOpen() {
10846
+ const sock = this.lifecycle?.ws;
10847
+ return !!sock && sock.readyState === sock.OPEN;
10848
+ }
10849
+ sendClientAck(clientMessageId, brokerMessageId) {
10850
+ if (!this.isOpen())
10851
+ return;
10852
+ try {
10853
+ this.lifecycle.send({
10854
+ type: "client_ack",
10855
+ clientMessageId,
10856
+ ...brokerMessageId ? { brokerMessageId } : {}
10716
10857
  });
10717
- ws.on("error", (err) => this.log("warn", "broker_ws_error", { err: err.message }));
10718
- });
10858
+ } catch {}
10719
10859
  }
10720
10860
  send(req) {
10721
10861
  return new Promise((resolve) => {
10722
10862
  const dispatch = () => {
10723
- if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
10863
+ if (!this.isOpen()) {
10724
10864
  resolve({ ok: false, error: "broker_not_open", permanent: false });
10725
10865
  return;
10726
10866
  }
@@ -10732,7 +10872,7 @@ class DaemonBrokerClient {
10732
10872
  }, SEND_ACK_TIMEOUT_MS);
10733
10873
  this.pendingAcks.set(id, { resolve, timer });
10734
10874
  try {
10735
- this.ws.send(JSON.stringify({
10875
+ this.lifecycle.send({
10736
10876
  type: "send",
10737
10877
  id,
10738
10878
  client_message_id: id,
@@ -10741,7 +10881,7 @@ class DaemonBrokerClient {
10741
10881
  priority: req.priority,
10742
10882
  nonce: req.nonce,
10743
10883
  ciphertext: req.ciphertext
10744
- }));
10884
+ });
10745
10885
  } catch (e) {
10746
10886
  this.pendingAcks.delete(id);
10747
10887
  clearTimeout(timer);
@@ -10755,7 +10895,7 @@ class DaemonBrokerClient {
10755
10895
  });
10756
10896
  }
10757
10897
  async listPeers(timeoutMs = 5000) {
10758
- if (this._status !== "open" || !this.ws)
10898
+ if (this._status !== "open" || !this.lifecycle)
10759
10899
  return [];
10760
10900
  return new Promise((resolve) => {
10761
10901
  const reqId = `pl-${++this.reqCounter}`;
@@ -10765,7 +10905,7 @@ class DaemonBrokerClient {
10765
10905
  }, timeoutMs);
10766
10906
  this.peerListResolvers.set(reqId, { resolve, timer });
10767
10907
  try {
10768
- this.ws.send(JSON.stringify({ type: "list_peers", _reqId: reqId }));
10908
+ this.lifecycle.send({ type: "list_peers", _reqId: reqId });
10769
10909
  } catch {
10770
10910
  this.peerListResolvers.delete(reqId);
10771
10911
  clearTimeout(timer);
@@ -10774,7 +10914,7 @@ class DaemonBrokerClient {
10774
10914
  });
10775
10915
  }
10776
10916
  async listSkills(query, timeoutMs = 5000) {
10777
- if (this._status !== "open" || !this.ws)
10917
+ if (this._status !== "open" || !this.lifecycle)
10778
10918
  return [];
10779
10919
  return new Promise((resolve) => {
10780
10920
  const reqId = `sl-${++this.reqCounter}`;
@@ -10784,7 +10924,7 @@ class DaemonBrokerClient {
10784
10924
  }, timeoutMs);
10785
10925
  this.skillListResolvers.set(reqId, { resolve, timer });
10786
10926
  try {
10787
- this.ws.send(JSON.stringify({ type: "list_skills", query, _reqId: reqId }));
10927
+ this.lifecycle.send({ type: "list_skills", query, _reqId: reqId });
10788
10928
  } catch {
10789
10929
  this.skillListResolvers.delete(reqId);
10790
10930
  clearTimeout(timer);
@@ -10793,7 +10933,7 @@ class DaemonBrokerClient {
10793
10933
  });
10794
10934
  }
10795
10935
  async getSkill(name, timeoutMs = 5000) {
10796
- if (this._status !== "open" || !this.ws)
10936
+ if (this._status !== "open" || !this.lifecycle)
10797
10937
  return null;
10798
10938
  return new Promise((resolve) => {
10799
10939
  const reqId = `sg-${++this.reqCounter}`;
@@ -10803,7 +10943,7 @@ class DaemonBrokerClient {
10803
10943
  }, timeoutMs);
10804
10944
  this.skillDataResolvers.set(reqId, { resolve, timer });
10805
10945
  try {
10806
- this.ws.send(JSON.stringify({ type: "get_skill", name, _reqId: reqId }));
10946
+ this.lifecycle.send({ type: "get_skill", name, _reqId: reqId });
10807
10947
  } catch {
10808
10948
  this.skillDataResolvers.delete(reqId);
10809
10949
  clearTimeout(timer);
@@ -10812,7 +10952,7 @@ class DaemonBrokerClient {
10812
10952
  });
10813
10953
  }
10814
10954
  async getState(key, timeoutMs = 5000) {
10815
- if (this._status !== "open" || !this.ws)
10955
+ if (this._status !== "open" || !this.lifecycle)
10816
10956
  return null;
10817
10957
  return new Promise((resolve) => {
10818
10958
  const reqId = `sg-${++this.reqCounter}`;
@@ -10822,7 +10962,7 @@ class DaemonBrokerClient {
10822
10962
  }, timeoutMs);
10823
10963
  this.stateGetResolvers.set(reqId, { resolve, timer });
10824
10964
  try {
10825
- this.ws.send(JSON.stringify({ type: "get_state", key, _reqId: reqId }));
10965
+ this.lifecycle.send({ type: "get_state", key, _reqId: reqId });
10826
10966
  } catch {
10827
10967
  this.stateGetResolvers.delete(reqId);
10828
10968
  clearTimeout(timer);
@@ -10831,7 +10971,7 @@ class DaemonBrokerClient {
10831
10971
  });
10832
10972
  }
10833
10973
  async listState(timeoutMs = 5000) {
10834
- if (this._status !== "open" || !this.ws)
10974
+ if (this._status !== "open" || !this.lifecycle)
10835
10975
  return [];
10836
10976
  return new Promise((resolve) => {
10837
10977
  const reqId = `sl-${++this.reqCounter}`;
@@ -10841,7 +10981,7 @@ class DaemonBrokerClient {
10841
10981
  }, timeoutMs);
10842
10982
  this.stateListResolvers.set(reqId, { resolve, timer });
10843
10983
  try {
10844
- this.ws.send(JSON.stringify({ type: "list_state", _reqId: reqId }));
10984
+ this.lifecycle.send({ type: "list_state", _reqId: reqId });
10845
10985
  } catch {
10846
10986
  this.stateListResolvers.delete(reqId);
10847
10987
  clearTimeout(timer);
@@ -10850,14 +10990,14 @@ class DaemonBrokerClient {
10850
10990
  });
10851
10991
  }
10852
10992
  setState(key, value) {
10853
- if (this._status !== "open" || !this.ws)
10993
+ if (this._status !== "open" || !this.lifecycle)
10854
10994
  return;
10855
10995
  try {
10856
- this.ws.send(JSON.stringify({ type: "set_state", key, value }));
10996
+ this.lifecycle.send({ type: "set_state", key, value });
10857
10997
  } catch {}
10858
10998
  }
10859
10999
  async remember(content, tags, timeoutMs = 5000) {
10860
- if (this._status !== "open" || !this.ws)
11000
+ if (this._status !== "open" || !this.lifecycle)
10861
11001
  return null;
10862
11002
  return new Promise((resolve) => {
10863
11003
  const reqId = `mr-${++this.reqCounter}`;
@@ -10867,7 +11007,7 @@ class DaemonBrokerClient {
10867
11007
  }, timeoutMs);
10868
11008
  this.memoryStoreResolvers.set(reqId, { resolve, timer });
10869
11009
  try {
10870
- this.ws.send(JSON.stringify({ type: "remember", content, tags, _reqId: reqId }));
11010
+ this.lifecycle.send({ type: "remember", content, tags, _reqId: reqId });
10871
11011
  } catch {
10872
11012
  this.memoryStoreResolvers.delete(reqId);
10873
11013
  clearTimeout(timer);
@@ -10876,7 +11016,7 @@ class DaemonBrokerClient {
10876
11016
  });
10877
11017
  }
10878
11018
  async recall(query, timeoutMs = 5000) {
10879
- if (this._status !== "open" || !this.ws)
11019
+ if (this._status !== "open" || !this.lifecycle)
10880
11020
  return [];
10881
11021
  return new Promise((resolve) => {
10882
11022
  const reqId = `mc-${++this.reqCounter}`;
@@ -10886,7 +11026,7 @@ class DaemonBrokerClient {
10886
11026
  }, timeoutMs);
10887
11027
  this.memoryRecallResolvers.set(reqId, { resolve, timer });
10888
11028
  try {
10889
- this.ws.send(JSON.stringify({ type: "recall", query, _reqId: reqId }));
11029
+ this.lifecycle.send({ type: "recall", query, _reqId: reqId });
10890
11030
  } catch {
10891
11031
  this.memoryRecallResolvers.delete(reqId);
10892
11032
  clearTimeout(timer);
@@ -10895,60 +11035,50 @@ class DaemonBrokerClient {
10895
11035
  });
10896
11036
  }
10897
11037
  forget(memoryId) {
10898
- if (this._status !== "open" || !this.ws)
11038
+ if (this._status !== "open" || !this.lifecycle)
10899
11039
  return;
10900
11040
  try {
10901
- this.ws.send(JSON.stringify({ type: "forget", memoryId }));
11041
+ this.lifecycle.send({ type: "forget", memoryId });
10902
11042
  } catch {}
10903
11043
  }
10904
11044
  setProfile(profile) {
10905
- if (this._status !== "open" || !this.ws)
11045
+ if (this._status !== "open" || !this.lifecycle)
10906
11046
  return;
10907
11047
  try {
10908
- this.ws.send(JSON.stringify({ type: "set_profile", ...profile }));
11048
+ this.lifecycle.send({ type: "set_profile", ...profile });
10909
11049
  } catch {}
10910
11050
  }
10911
11051
  setSummary(summary) {
10912
- if (this._status !== "open" || !this.ws)
11052
+ if (this._status !== "open" || !this.lifecycle)
10913
11053
  return;
10914
11054
  try {
10915
- this.ws.send(JSON.stringify({ type: "set_summary", summary }));
11055
+ this.lifecycle.send({ type: "set_summary", summary });
10916
11056
  } catch {}
10917
11057
  }
10918
11058
  setStatus(status) {
10919
- if (this._status !== "open" || !this.ws)
11059
+ if (this._status !== "open" || !this.lifecycle)
10920
11060
  return;
10921
11061
  try {
10922
- this.ws.send(JSON.stringify({ type: "set_status", status }));
11062
+ this.lifecycle.send({ type: "set_status", status });
10923
11063
  } catch {}
10924
11064
  }
10925
11065
  setVisible(visible) {
10926
- if (this._status !== "open" || !this.ws)
11066
+ if (this._status !== "open" || !this.lifecycle)
10927
11067
  return;
10928
11068
  try {
10929
- this.ws.send(JSON.stringify({ type: "set_visible", visible }));
11069
+ this.lifecycle.send({ type: "set_visible", visible });
10930
11070
  } catch {}
10931
11071
  }
10932
11072
  async close() {
10933
11073
  this.closed = true;
10934
- if (this.reconnectTimer) {
10935
- clearTimeout(this.reconnectTimer);
10936
- this.reconnectTimer = null;
10937
- }
10938
- if (this.helloTimer) {
10939
- clearTimeout(this.helloTimer);
10940
- this.helloTimer = null;
10941
- }
10942
11074
  this.failPendingAcks("daemon_shutdown");
10943
- try {
10944
- this.ws?.close();
10945
- } catch {}
10946
- this.setConnStatus("closed");
10947
- }
10948
- getSessionKeys() {
10949
- if (!this.sessionPubkey || !this.sessionSecretKey)
10950
- return null;
10951
- return { sessionPubkey: this.sessionPubkey, sessionSecretKey: this.sessionSecretKey };
11075
+ if (this.lifecycle) {
11076
+ try {
11077
+ await this.lifecycle.close();
11078
+ } catch {}
11079
+ this.lifecycle = null;
11080
+ }
11081
+ this._status = "closed";
10952
11082
  }
10953
11083
  failPendingAcks(reason) {
10954
11084
  for (const [id, ack] of this.pendingAcks) {
@@ -10958,7 +11088,7 @@ class DaemonBrokerClient {
10958
11088
  }
10959
11089
  }
10960
11090
  }
10961
- function defaultLog(level, msg, meta) {
11091
+ function defaultLog2(level, msg, meta) {
10962
11092
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10963
11093
  if (level === "info")
10964
11094
  process.stdout.write(line + `
@@ -10970,24 +11100,26 @@ function defaultLog(level, msg, meta) {
10970
11100
  function classifyPermanent(err) {
10971
11101
  return /payload_too_large|forbidden|not_found|invalid|schema|auth|signature/i.test(err);
10972
11102
  }
10973
- var HELLO_ACK_TIMEOUT_MS2 = 5000, SEND_ACK_TIMEOUT_MS = 15000, BACKOFF_CAPS_MS;
11103
+ var SEND_ACK_TIMEOUT_MS = 15000;
10974
11104
  var init_broker = __esm(() => {
10975
11105
  init_hello_sig();
10976
- BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
11106
+ init_ws_lifecycle();
10977
11107
  });
10978
11108
 
10979
11109
  // src/daemon/session-broker.ts
10980
11110
  import { hostname as osHostname } from "node:os";
10981
- import WebSocket3 from "ws";
11111
+ function classifyPermanent2(error2) {
11112
+ return /unknown|invalid|forbidden|not_authorized|target_not_found/i.test(error2);
11113
+ }
10982
11114
 
10983
11115
  class SessionBrokerClient {
10984
11116
  opts;
10985
- ws = null;
11117
+ lifecycle = null;
10986
11118
  _status = "closed";
10987
11119
  closed = false;
10988
- reconnectAttempt = 0;
10989
- reconnectTimer = null;
10990
- helloTimer = null;
11120
+ brokerUnsupported = false;
11121
+ pendingAcks = new Map;
11122
+ opens = [];
10991
11123
  constructor(opts) {
10992
11124
  this.opts = opts;
10993
11125
  }
@@ -11001,85 +11133,68 @@ class SessionBrokerClient {
11001
11133
  return this.opts.sessionPubkey;
11002
11134
  }
11003
11135
  log = (level, msg, meta) => {
11004
- (this.opts.log ?? defaultLog2)(level, msg, {
11136
+ (this.opts.log ?? defaultLog3)(level, msg, {
11005
11137
  mesh: this.opts.mesh.slug,
11006
11138
  session_pubkey: this.opts.sessionPubkey.slice(0, 12),
11007
11139
  ...meta
11008
11140
  });
11009
11141
  };
11010
- setStatus(s) {
11011
- if (this._status === s)
11012
- return;
11013
- this._status = s;
11014
- this.opts.onStatusChange?.(s);
11015
- }
11016
11142
  async connect() {
11017
11143
  if (this.closed)
11018
11144
  throw new Error("client_closed");
11019
11145
  if (this._status === "connecting" || this._status === "open")
11020
11146
  return;
11021
- this.setStatus("connecting");
11022
- const ws = new WebSocket3(this.opts.mesh.brokerUrl);
11023
- this.ws = ws;
11024
- return new Promise((resolve, reject) => {
11025
- ws.on("open", async () => {
11026
- try {
11027
- const { timestamp: timestamp2, signature } = await signSessionHello({
11028
- meshId: this.opts.mesh.meshId,
11029
- parentMemberPubkey: this.opts.mesh.pubkey,
11030
- sessionPubkey: this.opts.sessionPubkey,
11031
- sessionSecretKey: this.opts.sessionSecretKey
11032
- });
11033
- ws.send(JSON.stringify({
11034
- type: "session_hello",
11035
- meshId: this.opts.mesh.meshId,
11036
- parentMemberId: this.opts.mesh.memberId,
11037
- parentMemberPubkey: this.opts.mesh.pubkey,
11038
- sessionPubkey: this.opts.sessionPubkey,
11039
- parentAttestation: this.opts.parentAttestation,
11040
- displayName: this.opts.displayName,
11041
- sessionId: this.opts.sessionId,
11042
- pid: this.opts.pid,
11043
- cwd: this.opts.cwd ?? process.cwd(),
11044
- hostname: osHostname(),
11045
- peerType: "ai",
11046
- channel: "claudemesh-session",
11047
- ...this.opts.groups && this.opts.groups.length > 0 ? { groups: this.opts.groups } : {},
11048
- ...this.opts.role ? { role: this.opts.role } : {},
11049
- timestamp: timestamp2,
11050
- signature
11051
- }));
11052
- this.helloTimer = setTimeout(() => {
11053
- this.log("warn", "session_hello_ack_timeout");
11054
- try {
11055
- ws.close();
11056
- } catch {}
11057
- reject(new Error("session_hello_ack_timeout"));
11058
- }, HELLO_ACK_TIMEOUT_MS3);
11059
- } catch (e) {
11060
- reject(e instanceof Error ? e : new Error(String(e)));
11061
- }
11062
- });
11063
- ws.on("message", (raw) => {
11064
- let msg;
11065
- try {
11066
- msg = JSON.parse(raw.toString());
11067
- } catch {
11068
- return;
11069
- }
11070
- if (msg.type === "hello_ack") {
11071
- if (this.helloTimer)
11072
- clearTimeout(this.helloTimer);
11073
- this.helloTimer = null;
11074
- this.setStatus("open");
11075
- this.reconnectAttempt = 0;
11076
- resolve();
11077
- return;
11078
- }
11147
+ this.lifecycle = await connectWsWithBackoff({
11148
+ url: this.opts.mesh.brokerUrl,
11149
+ buildHello: async () => {
11150
+ const { timestamp: timestamp2, signature } = await signSessionHello({
11151
+ meshId: this.opts.mesh.meshId,
11152
+ parentMemberPubkey: this.opts.mesh.pubkey,
11153
+ sessionPubkey: this.opts.sessionPubkey,
11154
+ sessionSecretKey: this.opts.sessionSecretKey
11155
+ });
11156
+ return {
11157
+ type: "session_hello",
11158
+ meshId: this.opts.mesh.meshId,
11159
+ parentMemberId: this.opts.mesh.memberId,
11160
+ parentMemberPubkey: this.opts.mesh.pubkey,
11161
+ sessionPubkey: this.opts.sessionPubkey,
11162
+ parentAttestation: this.opts.parentAttestation,
11163
+ displayName: this.opts.displayName,
11164
+ sessionId: this.opts.sessionId,
11165
+ pid: this.opts.pid,
11166
+ cwd: this.opts.cwd ?? process.cwd(),
11167
+ hostname: osHostname(),
11168
+ peerType: "ai",
11169
+ channel: "claudemesh-session",
11170
+ ...this.opts.groups && this.opts.groups.length > 0 ? { groups: this.opts.groups } : {},
11171
+ ...this.opts.role ? { role: this.opts.role } : {},
11172
+ timestamp: timestamp2,
11173
+ signature
11174
+ };
11175
+ },
11176
+ isHelloAck: (msg) => msg.type === "hello_ack",
11177
+ onMessage: (msg) => {
11079
11178
  if (msg.type === "error") {
11080
11179
  this.log("warn", "broker_error", { code: msg.code, message: msg.message });
11081
11180
  if (msg.code === "unknown_message_type") {
11181
+ this.brokerUnsupported = true;
11082
11182
  this.closed = true;
11183
+ this.lifecycle?.close();
11184
+ }
11185
+ return;
11186
+ }
11187
+ if (msg.type === "ack") {
11188
+ const id = String(msg.id ?? "");
11189
+ const ack = this.pendingAcks.get(id);
11190
+ if (ack) {
11191
+ this.pendingAcks.delete(id);
11192
+ clearTimeout(ack.timer);
11193
+ if (typeof msg.error === "string" && msg.error.length > 0) {
11194
+ ack.resolve({ ok: false, error: msg.error, permanent: classifyPermanent2(msg.error) });
11195
+ } else {
11196
+ ack.resolve({ ok: true, messageId: String(msg.messageId ?? id) });
11197
+ }
11083
11198
  }
11084
11199
  return;
11085
11200
  }
@@ -11087,44 +11202,104 @@ class SessionBrokerClient {
11087
11202
  this.opts.onPush?.(msg);
11088
11203
  return;
11089
11204
  }
11090
- });
11091
- ws.on("close", (code, reason) => {
11092
- if (this.helloTimer) {
11093
- clearTimeout(this.helloTimer);
11094
- this.helloTimer = null;
11205
+ },
11206
+ onStatusChange: (s) => {
11207
+ this._status = s;
11208
+ this.opts.onStatusChange?.(s);
11209
+ if (s === "open") {
11210
+ const queued = this.opens.slice();
11211
+ this.opens.length = 0;
11212
+ for (const fn of queued) {
11213
+ try {
11214
+ fn();
11215
+ } catch (e) {
11216
+ this.log("warn", "session_open_handler_failed", { err: String(e) });
11217
+ }
11218
+ }
11219
+ } else if (s === "closed" || s === "reconnecting") {
11220
+ this.failPendingAcks(`session_ws_${s}`);
11095
11221
  }
11096
- if (this.closed) {
11097
- this.setStatus("closed");
11222
+ },
11223
+ log: (level, msg, meta) => this.log(level, `session_broker_${msg}`, meta)
11224
+ });
11225
+ }
11226
+ sendClientAck(clientMessageId, brokerMessageId) {
11227
+ if (this._status !== "open" || !this.lifecycle)
11228
+ return;
11229
+ try {
11230
+ this.lifecycle.send({
11231
+ type: "client_ack",
11232
+ clientMessageId,
11233
+ ...brokerMessageId ? { brokerMessageId } : {}
11234
+ });
11235
+ } catch {}
11236
+ }
11237
+ isOpen() {
11238
+ const sock = this.lifecycle?.ws;
11239
+ return !!sock && sock.readyState === sock.OPEN;
11240
+ }
11241
+ send(req) {
11242
+ return new Promise((resolve) => {
11243
+ const dispatch = () => {
11244
+ if (!this.isOpen() || !this.lifecycle) {
11245
+ resolve({ ok: false, error: "session_ws_not_open", permanent: false });
11098
11246
  return;
11099
11247
  }
11100
- this.setStatus("reconnecting");
11101
- const wait = BACKOFF_CAPS_MS2[Math.min(this.reconnectAttempt, BACKOFF_CAPS_MS2.length - 1)] ?? 30000;
11102
- this.reconnectAttempt++;
11103
- this.log("info", "session_broker_reconnect_scheduled", { wait_ms: wait, code, reason: reason.toString("utf8") });
11104
- this.reconnectTimer = setTimeout(() => this.connect().catch((err) => this.log("warn", "session_broker_reconnect_failed", { err: String(err) })), wait);
11105
- if (this._status === "connecting")
11106
- reject(new Error(`closed_before_hello_${code}`));
11107
- });
11108
- ws.on("error", (err) => this.log("warn", "session_broker_ws_error", { err: err.message }));
11248
+ const id = req.client_message_id;
11249
+ const timer = setTimeout(() => {
11250
+ if (this.pendingAcks.delete(id)) {
11251
+ resolve({ ok: false, error: "ack_timeout", permanent: false });
11252
+ }
11253
+ }, SEND_ACK_TIMEOUT_MS2);
11254
+ this.pendingAcks.set(id, { resolve, timer });
11255
+ try {
11256
+ this.lifecycle.send({
11257
+ type: "send",
11258
+ id,
11259
+ client_message_id: id,
11260
+ request_fingerprint: req.request_fingerprint_hex,
11261
+ targetSpec: req.targetSpec,
11262
+ priority: req.priority,
11263
+ nonce: req.nonce,
11264
+ ciphertext: req.ciphertext
11265
+ });
11266
+ } catch (e) {
11267
+ this.pendingAcks.delete(id);
11268
+ clearTimeout(timer);
11269
+ resolve({ ok: false, error: `ws_write_failed: ${String(e)}`, permanent: false });
11270
+ }
11271
+ };
11272
+ if (this._status === "open")
11273
+ dispatch();
11274
+ else
11275
+ this.opens.push(dispatch);
11109
11276
  });
11110
11277
  }
11278
+ failPendingAcks(reason) {
11279
+ if (this.pendingAcks.size === 0)
11280
+ return;
11281
+ const entries = [...this.pendingAcks.entries()];
11282
+ this.pendingAcks.clear();
11283
+ for (const [, ack] of entries) {
11284
+ clearTimeout(ack.timer);
11285
+ ack.resolve({ ok: false, error: reason, permanent: false });
11286
+ }
11287
+ }
11111
11288
  async close() {
11112
11289
  this.closed = true;
11113
- if (this.reconnectTimer) {
11114
- clearTimeout(this.reconnectTimer);
11115
- this.reconnectTimer = null;
11116
- }
11117
- if (this.helloTimer) {
11118
- clearTimeout(this.helloTimer);
11119
- this.helloTimer = null;
11290
+ if (this.lifecycle) {
11291
+ try {
11292
+ await this.lifecycle.close();
11293
+ } catch {}
11294
+ this.lifecycle = null;
11120
11295
  }
11121
- try {
11122
- this.ws?.close();
11123
- } catch {}
11124
- this.setStatus("closed");
11296
+ this._status = "closed";
11297
+ }
11298
+ get isBrokerUnsupported() {
11299
+ return this.brokerUnsupported;
11125
11300
  }
11126
11301
  }
11127
- function defaultLog2(level, msg, meta) {
11302
+ function defaultLog3(level, msg, meta) {
11128
11303
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
11129
11304
  if (level === "info")
11130
11305
  process.stdout.write(line + `
@@ -11133,15 +11308,15 @@ function defaultLog2(level, msg, meta) {
11133
11308
  process.stderr.write(line + `
11134
11309
  `);
11135
11310
  }
11136
- var HELLO_ACK_TIMEOUT_MS3 = 5000, BACKOFF_CAPS_MS2;
11311
+ var SEND_ACK_TIMEOUT_MS2 = 15000;
11137
11312
  var init_session_broker = __esm(() => {
11138
11313
  init_session_hello_sig();
11139
- BACKOFF_CAPS_MS2 = [1000, 2000, 4000, 8000, 16000, 30000];
11314
+ init_ws_lifecycle();
11140
11315
  });
11141
11316
 
11142
11317
  // src/daemon/drain.ts
11143
11318
  function startDrainWorker(opts) {
11144
- const log2 = opts.log ?? defaultLog3;
11319
+ const log2 = opts.log ?? defaultLog4;
11145
11320
  let stopped = false;
11146
11321
  let wakeResolve = null;
11147
11322
  let wakePromise = new Promise((r) => {
@@ -11183,7 +11358,8 @@ async function drainOnce(opts, log2) {
11183
11358
  const now = Date.now();
11184
11359
  const rows = opts.db.prepare(`
11185
11360
  SELECT id, client_message_id, request_fingerprint, payload, attempts,
11186
- target_spec, nonce, ciphertext, priority, mesh
11361
+ target_spec, nonce, ciphertext, priority, mesh,
11362
+ sender_session_pubkey
11187
11363
  FROM outbox
11188
11364
  WHERE status = 'pending' AND next_attempt_at <= ?
11189
11365
  ORDER BY enqueued_at
@@ -11195,17 +11371,21 @@ async function drainOnce(opts, log2) {
11195
11371
  if (markInflight(opts.db, row.id, now) === 0)
11196
11372
  continue;
11197
11373
  const fpHex = bufferToHex(row.request_fingerprint);
11198
- let broker;
11374
+ let daemonBroker;
11199
11375
  if (row.mesh) {
11200
- broker = opts.brokers.get(row.mesh);
11376
+ daemonBroker = opts.brokers.get(row.mesh);
11201
11377
  } else if (opts.brokers.size === 1) {
11202
- broker = opts.brokers.values().next().value;
11378
+ daemonBroker = opts.brokers.values().next().value;
11203
11379
  }
11204
- if (!broker) {
11380
+ if (!daemonBroker) {
11205
11381
  log2("warn", "drain_no_broker_for_mesh", { id: row.id, mesh: row.mesh ?? "(null)" });
11206
11382
  markDead(opts.db, row.id, `no_broker_for_mesh:${row.mesh ?? "null"}`);
11207
11383
  continue;
11208
11384
  }
11385
+ let sessionBroker;
11386
+ if (row.sender_session_pubkey && opts.getSessionBrokerByPubkey) {
11387
+ sessionBroker = opts.getSessionBrokerByPubkey(row.sender_session_pubkey);
11388
+ }
11209
11389
  let targetSpec;
11210
11390
  let nonce;
11211
11391
  let ciphertext;
@@ -11221,16 +11401,29 @@ async function drainOnce(opts, log2) {
11221
11401
  ciphertext = Buffer.from(row.payload).toString("base64");
11222
11402
  priority = "next";
11223
11403
  }
11404
+ const sendArgs = {
11405
+ targetSpec,
11406
+ priority,
11407
+ nonce,
11408
+ ciphertext,
11409
+ client_message_id: row.client_message_id,
11410
+ request_fingerprint_hex: fpHex
11411
+ };
11224
11412
  let res;
11225
11413
  try {
11226
- res = await broker.send({
11227
- targetSpec,
11228
- priority,
11229
- nonce,
11230
- ciphertext,
11231
- client_message_id: row.client_message_id,
11232
- request_fingerprint_hex: fpHex
11233
- });
11414
+ if (row.sender_session_pubkey) {
11415
+ if (!sessionBroker || !sessionBroker.isOpen()) {
11416
+ log2("info", "drain_session_ws_not_ready", {
11417
+ id: row.id,
11418
+ session_pubkey: row.sender_session_pubkey.slice(0, 12)
11419
+ });
11420
+ backoffPending(opts.db, row.id, row.attempts + 1, "session_ws_not_open", "session_ws_not_open");
11421
+ continue;
11422
+ }
11423
+ res = await sessionBroker.send(sendArgs);
11424
+ } else {
11425
+ res = await daemonBroker.send(sendArgs);
11426
+ }
11234
11427
  } catch (e) {
11235
11428
  log2("warn", "drain_send_threw", { id: row.id, err: String(e) });
11236
11429
  backoffPending(opts.db, row.id, row.attempts + 1, "exception", String(e));
@@ -11285,7 +11478,7 @@ async function randomNonce2() {
11285
11478
  const { randomBytes: randomBytes6 } = await import("node:crypto");
11286
11479
  return randomBytes6(24).toString("base64");
11287
11480
  }
11288
- function defaultLog3(level, msg, meta) {
11481
+ function defaultLog4(level, msg, meta) {
11289
11482
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
11290
11483
  if (level === "info")
11291
11484
  process.stdout.write(line + `
@@ -11342,6 +11535,7 @@ async function handleBrokerPush(msg, ctx) {
11342
11535
  received_at: Date.now(),
11343
11536
  reply_to_id: replyToId
11344
11537
  });
11538
+ ctx.ackClientMessage?.(clientMessageId, brokerMessageId);
11345
11539
  if (!inserted)
11346
11540
  return;
11347
11541
  ctx.bus.publish("message", {
@@ -11612,13 +11806,12 @@ async function runDaemon(opts = {}) {
11612
11806
  bus.publish("broker_status", { mesh: mesh.slug, status: s });
11613
11807
  },
11614
11808
  onPush: (m) => {
11615
- const sessionKeys = broker.getSessionKeys();
11616
11809
  handleBrokerPush(m, {
11617
11810
  db: inboxDb,
11618
11811
  bus,
11619
11812
  meshSlug: mesh.slug,
11620
11813
  recipientSecretKeyHex: mesh.secretKey,
11621
- sessionSecretKeyHex: sessionKeys?.sessionSecretKey
11814
+ ackClientMessage: (cmid, bmid) => broker.sendClientAck(cmid, bmid)
11622
11815
  });
11623
11816
  }
11624
11817
  });
@@ -11626,9 +11819,14 @@ async function runDaemon(opts = {}) {
11626
11819
  `));
11627
11820
  brokers.set(mesh.slug, broker);
11628
11821
  }
11629
- let drain = null;
11630
- drain = startDrainWorker({ db: outboxDb, brokers });
11631
11822
  const sessionBrokers = new Map;
11823
+ const sessionBrokersByPubkey = new Map;
11824
+ let drain = null;
11825
+ drain = startDrainWorker({
11826
+ db: outboxDb,
11827
+ brokers,
11828
+ getSessionBrokerByPubkey: (pubkey) => sessionBrokersByPubkey.get(pubkey)
11829
+ });
11632
11830
  setRegistryHooks({
11633
11831
  onRegister: (info) => {
11634
11832
  if (!info.presence)
@@ -11647,6 +11845,9 @@ async function runDaemon(opts = {}) {
11647
11845
  const prior = sessionBrokers.get(info.token);
11648
11846
  if (prior) {
11649
11847
  sessionBrokers.delete(info.token);
11848
+ if (sessionBrokersByPubkey.get(prior.sessionPubkey) === prior) {
11849
+ sessionBrokersByPubkey.delete(prior.sessionPubkey);
11850
+ }
11650
11851
  prior.close().catch(() => {});
11651
11852
  }
11652
11853
  const sessionSecretKeyHex = info.presence.sessionSecretKey;
@@ -11666,11 +11867,13 @@ async function runDaemon(opts = {}) {
11666
11867
  bus,
11667
11868
  meshSlug: meshConfig.slug,
11668
11869
  recipientSecretKeyHex: meshConfig.secretKey,
11669
- sessionSecretKeyHex
11870
+ sessionSecretKeyHex,
11871
+ ackClientMessage: (cmid, bmid) => client.sendClientAck(cmid, bmid)
11670
11872
  });
11671
11873
  }
11672
11874
  });
11673
11875
  sessionBrokers.set(info.token, client);
11876
+ sessionBrokersByPubkey.set(info.presence.sessionPubkey, client);
11674
11877
  client.connect().catch((err) => process.stderr.write(JSON.stringify({
11675
11878
  level: "warn",
11676
11879
  msg: "session_broker_connect_failed",
@@ -11685,6 +11888,9 @@ async function runDaemon(opts = {}) {
11685
11888
  if (!client)
11686
11889
  return;
11687
11890
  sessionBrokers.delete(info.token);
11891
+ if (sessionBrokersByPubkey.get(client.sessionPubkey) === client) {
11892
+ sessionBrokersByPubkey.delete(client.sessionPubkey);
11893
+ }
11688
11894
  client.close().catch(() => {});
11689
11895
  }
11690
11896
  });
@@ -13076,8 +13282,8 @@ async function checkBrokerWs() {
13076
13282
  const wsUrl = URLS.BROKER;
13077
13283
  const start = Date.now();
13078
13284
  try {
13079
- const WebSocket4 = (await import("ws")).default;
13080
- const ws = new WebSocket4(wsUrl);
13285
+ const WebSocket3 = (await import("ws")).default;
13286
+ const ws = new WebSocket3(wsUrl);
13081
13287
  const result = await new Promise((resolve2) => {
13082
13288
  const timer = setTimeout(() => {
13083
13289
  try {
@@ -13188,11 +13394,11 @@ __export(exports_status, {
13188
13394
  runStatus: () => runStatus2
13189
13395
  });
13190
13396
  import { statSync as statSync4, existsSync as existsSync16 } from "node:fs";
13191
- import WebSocket4 from "ws";
13397
+ import WebSocket3 from "ws";
13192
13398
  async function probeBroker(url, timeoutMs = 4000) {
13193
13399
  return new Promise((resolve2) => {
13194
13400
  const started = Date.now();
13195
- const ws = new WebSocket4(url);
13401
+ const ws = new WebSocket3(url);
13196
13402
  const timer = setTimeout(() => {
13197
13403
  try {
13198
13404
  ws.terminate();
@@ -16402,7 +16608,7 @@ var require_client = __commonJS((exports) => {
16402
16608
  var ws_1 = __importDefault(__require("ws"));
16403
16609
  var crypto_js_1 = require_crypto();
16404
16610
  var MAX_QUEUED2 = 100;
16405
- var HELLO_ACK_TIMEOUT_MS4 = 5000;
16611
+ var HELLO_ACK_TIMEOUT_MS2 = 5000;
16406
16612
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
16407
16613
 
16408
16614
  class MeshClient extends node_events_1.EventEmitter {
@@ -16467,7 +16673,7 @@ var require_client = __commonJS((exports) => {
16467
16673
  this.debug("hello_ack timeout");
16468
16674
  ws.close();
16469
16675
  reject(new Error("hello_ack timeout"));
16470
- }, HELLO_ACK_TIMEOUT_MS4);
16676
+ }, HELLO_ACK_TIMEOUT_MS2);
16471
16677
  };
16472
16678
  const onMessage = (raw) => {
16473
16679
  let msg;
@@ -19479,7 +19685,7 @@ async function main() {
19479
19685
  }
19480
19686
  case "inbox": {
19481
19687
  const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
19482
- await runInbox2({ json: !!flags.json });
19688
+ await runInbox2({ mesh: flags.mesh, json: !!flags.json, limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined });
19483
19689
  break;
19484
19690
  }
19485
19691
  case "state": {
@@ -19734,7 +19940,7 @@ async function main() {
19734
19940
  await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[1] ?? "", positionals.slice(2).join(" "));
19735
19941
  } else if (sub === "inbox") {
19736
19942
  const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
19737
- await runInbox2({ json: !!flags.json });
19943
+ await runInbox2({ mesh: flags.mesh, json: !!flags.json, limit: typeof flags.limit === "number" ? flags.limit : typeof flags.limit === "string" ? Number.parseInt(flags.limit, 10) : undefined });
19738
19944
  } else if (sub === "status") {
19739
19945
  const { runMsgStatus: runMsgStatus2 } = await Promise.resolve().then(() => (init_broker_actions(), exports_broker_actions));
19740
19946
  process.exit(await runMsgStatus2(positionals[1], { mesh: flags.mesh, json: !!flags.json }));
@@ -20287,4 +20493,4 @@ main().catch((err) => {
20287
20493
  process.exit(EXIT.INTERNAL_ERROR);
20288
20494
  });
20289
20495
 
20290
- //# debugId=FAFB4F8D5ECBBBB864756E2164756E21
20496
+ //# debugId=55E9142D00D4EE3764756E2164756E21