claudemesh-cli 1.32.1 → 1.33.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.33.0", env;
108
108
  var init_urls = __esm(() => {
109
109
  URLS = {
110
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -7948,8 +7948,15 @@ async function listPeersForMesh(slug) {
7948
7948
  function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
7949
7949
  const isSelf = !!(selfMemberPubkey && peer.memberPubkey && peer.memberPubkey === selfMemberPubkey);
7950
7950
  const isThisSession = !!(isSelf && selfSessionPubkey && peer.pubkey === selfSessionPubkey);
7951
- const role = peer.profile?.role?.trim() || undefined;
7952
- return { ...peer, ...role ? { role } : {}, isSelf, isThisSession };
7951
+ const peerRole = peer.peerRole ?? "session";
7952
+ const profileRole = peer.profile?.role?.trim() || undefined;
7953
+ return {
7954
+ ...peer,
7955
+ ...profileRole ? { role: profileRole } : {},
7956
+ peerRole,
7957
+ isSelf,
7958
+ isThisSession
7959
+ };
7953
7960
  }
7954
7961
  async function runPeers(flags) {
7955
7962
  const config = readConfig();
@@ -7977,13 +7984,13 @@ async function runPeers(flags) {
7977
7984
  allJson.push({ mesh: slug, peers: projected });
7978
7985
  continue;
7979
7986
  }
7980
- const visible = flags.all ? peers : peers.filter((p) => p.channel !== "claudemesh-daemon");
7987
+ const visible = flags.all ? peers : peers.filter((p) => p.peerRole !== "control-plane");
7981
7988
  const sorted = visible.slice().sort((a, b) => {
7982
7989
  const score = (p) => p.isThisSession ? 0 : p.isSelf ? 1 : 2;
7983
7990
  return score(a) - score(b);
7984
7991
  });
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})`;
7992
+ const hiddenControlPlane = peers.length - visible.length;
7993
+ const header = hiddenControlPlane > 0 ? `peers on ${slug} (${sorted.length}, ${hiddenControlPlane} control-plane hidden — use --all)` : `peers on ${slug} (${sorted.length})`;
7987
7994
  render.section(header);
7988
7995
  if (sorted.length === 0) {
7989
7996
  render.info(dim(" (no peers connected)"));
@@ -10450,9 +10457,7 @@ async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
10450
10457
  return { target_spec: to, ciphertext, nonce, mesh: meshSlug ?? "" };
10451
10458
  }
10452
10459
  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);
10460
+ const env3 = await encryptDirect2(req.message, to, meshSecretKey);
10456
10461
  return { target_spec: to, ciphertext: env3.ciphertext, nonce: env3.nonce, mesh: meshSlug ?? "" };
10457
10462
  }
10458
10463
  const peers = await broker.listPeers().catch(() => []);
@@ -10463,18 +10468,14 @@ async function resolveAndEncrypt(req, broker, meshSecretKey, meshSlug) {
10463
10468
  if (matches2.length > 1)
10464
10469
  throw new Error(`prefix "${to}" is ambiguous (${matches2.length} matches)`);
10465
10470
  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);
10471
+ const env3 = await encryptDirect2(req.message, recipient2, meshSecretKey);
10469
10472
  return { target_spec: recipient2, ciphertext: env3.ciphertext, nonce: env3.nonce, mesh: meshSlug ?? "" };
10470
10473
  }
10471
10474
  const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
10472
10475
  if (!match)
10473
10476
  throw new Error(`peer "${to}" not found`);
10474
10477
  const recipient = match.pubkey;
10475
- const sessionKeys = broker.getSessionKeys();
10476
- const senderSecret = sessionKeys?.sessionSecretKey ?? meshSecretKey;
10477
- const env2 = await encryptDirect2(req.message, recipient, senderSecret);
10478
+ const env2 = await encryptDirect2(req.message, recipient, meshSecretKey);
10478
10479
  return { target_spec: recipient, ciphertext: env2.ciphertext, nonce: env2.nonce, mesh: meshSlug ?? "" };
10479
10480
  }
10480
10481
  function respond(res, status, body) {
@@ -10491,18 +10492,149 @@ var init_server = __esm(() => {
10491
10492
  init_urls();
10492
10493
  });
10493
10494
 
10494
- // src/daemon/broker.ts
10495
+ // src/daemon/ws-lifecycle.ts
10495
10496
  import WebSocket2 from "ws";
10497
+ function connectWsWithBackoff(opts) {
10498
+ const helloAckTimeoutMs = opts.helloAckTimeoutMs ?? DEFAULT_HELLO_ACK_TIMEOUT_MS;
10499
+ const backoffCapsMs = opts.backoffCapsMs ?? DEFAULT_BACKOFF_CAPS_MS;
10500
+ const log2 = opts.log ?? defaultLog;
10501
+ let ws = null;
10502
+ let status = "closed";
10503
+ let closed = false;
10504
+ let reconnectAttempt = 0;
10505
+ let reconnectTimer = null;
10506
+ let helloTimer = null;
10507
+ const setStatus = (s) => {
10508
+ if (status === s)
10509
+ return;
10510
+ status = s;
10511
+ opts.onStatusChange?.(s);
10512
+ };
10513
+ const openOnce = () => {
10514
+ if (closed)
10515
+ return Promise.reject(new Error("client_closed"));
10516
+ setStatus("connecting");
10517
+ log2("info", "ws_open_attempt", { url: opts.url });
10518
+ const sock = new WebSocket2(opts.url);
10519
+ ws = sock;
10520
+ return new Promise((resolve, reject) => {
10521
+ sock.on("open", () => {
10522
+ log2("info", "ws_open_ok", { url: opts.url });
10523
+ (async () => {
10524
+ try {
10525
+ const hello = await opts.buildHello();
10526
+ sock.send(JSON.stringify(hello));
10527
+ log2("info", "ws_hello_sent", { url: opts.url });
10528
+ helloTimer = setTimeout(() => {
10529
+ log2("warn", "hello_ack_timeout", { url: opts.url });
10530
+ try {
10531
+ sock.close();
10532
+ } catch {}
10533
+ reject(new Error("hello_ack_timeout"));
10534
+ }, helloAckTimeoutMs);
10535
+ } catch (e) {
10536
+ log2("warn", "ws_build_hello_threw", { err: String(e) });
10537
+ reject(e instanceof Error ? e : new Error(String(e)));
10538
+ }
10539
+ })();
10540
+ });
10541
+ sock.on("message", (raw) => {
10542
+ let msg;
10543
+ try {
10544
+ msg = JSON.parse(raw.toString());
10545
+ } catch {
10546
+ return;
10547
+ }
10548
+ if (opts.isHelloAck(msg)) {
10549
+ if (helloTimer) {
10550
+ clearTimeout(helloTimer);
10551
+ helloTimer = null;
10552
+ }
10553
+ setStatus("open");
10554
+ reconnectAttempt = 0;
10555
+ log2("info", "ws_hello_acked", { url: opts.url });
10556
+ resolve();
10557
+ return;
10558
+ }
10559
+ opts.onMessage(msg);
10560
+ });
10561
+ sock.on("close", (code, reason) => {
10562
+ if (helloTimer) {
10563
+ clearTimeout(helloTimer);
10564
+ helloTimer = null;
10565
+ }
10566
+ const reasonStr = reason.toString("utf8");
10567
+ log2("warn", "ws_closed", { url: opts.url, code, reason: reasonStr, status });
10568
+ opts.onBeforeReconnect?.(code, reasonStr);
10569
+ if (closed) {
10570
+ setStatus("closed");
10571
+ return;
10572
+ }
10573
+ setStatus("reconnecting");
10574
+ const wait = backoffCapsMs[Math.min(reconnectAttempt, backoffCapsMs.length - 1)] ?? 30000;
10575
+ reconnectAttempt++;
10576
+ log2("info", "ws_reconnect_scheduled", { url: opts.url, wait_ms: wait, code, reason: reasonStr });
10577
+ reconnectTimer = setTimeout(() => openOnce().catch((err) => log2("warn", "ws_reconnect_failed", { url: opts.url, err: String(err) })), wait);
10578
+ if (status === "connecting" || status === "reconnecting") {
10579
+ reject(new Error(`closed_before_hello_${code}`));
10580
+ }
10581
+ });
10582
+ sock.on("error", (err) => log2("warn", "ws_error", { url: opts.url, err: err.message }));
10583
+ });
10584
+ };
10585
+ return openOnce().then(() => {
10586
+ const handle = {
10587
+ get status() {
10588
+ return status;
10589
+ },
10590
+ get ws() {
10591
+ return ws;
10592
+ },
10593
+ send(payload) {
10594
+ if (!ws || ws.readyState !== ws.OPEN) {
10595
+ throw new Error("ws_not_open");
10596
+ }
10597
+ ws.send(JSON.stringify(payload));
10598
+ },
10599
+ async close() {
10600
+ closed = true;
10601
+ if (reconnectTimer) {
10602
+ clearTimeout(reconnectTimer);
10603
+ reconnectTimer = null;
10604
+ }
10605
+ if (helloTimer) {
10606
+ clearTimeout(helloTimer);
10607
+ helloTimer = null;
10608
+ }
10609
+ try {
10610
+ ws?.close();
10611
+ } catch {}
10612
+ setStatus("closed");
10613
+ }
10614
+ };
10615
+ return handle;
10616
+ });
10617
+ }
10618
+ var DEFAULT_HELLO_ACK_TIMEOUT_MS = 5000, DEFAULT_BACKOFF_CAPS_MS, defaultLog = (level, msg, meta) => {
10619
+ const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10620
+ if (level === "info")
10621
+ process.stdout.write(line + `
10622
+ `);
10623
+ else
10624
+ process.stderr.write(line + `
10625
+ `);
10626
+ };
10627
+ var init_ws_lifecycle = __esm(() => {
10628
+ DEFAULT_BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
10629
+ });
10496
10630
 
10631
+ // src/daemon/broker.ts
10497
10632
  class DaemonBrokerClient {
10498
10633
  mesh;
10499
10634
  opts;
10500
- ws = null;
10635
+ lifecycle = null;
10501
10636
  _status = "closed";
10502
10637
  closed = false;
10503
- reconnectAttempt = 0;
10504
- reconnectTimer = null;
10505
- helloTimer = null;
10506
10638
  pendingAcks = new Map;
10507
10639
  peerListResolvers = new Map;
10508
10640
  skillListResolvers = new Map;
@@ -10511,8 +10643,6 @@ class DaemonBrokerClient {
10511
10643
  stateListResolvers = new Map;
10512
10644
  memoryStoreResolvers = new Map;
10513
10645
  memoryRecallResolvers = new Map;
10514
- sessionPubkey = null;
10515
- sessionSecretKey = null;
10516
10646
  opens = [];
10517
10647
  reqCounter = 0;
10518
10648
  constructor(mesh, opts = {}) {
@@ -10529,72 +10659,39 @@ class DaemonBrokerClient {
10529
10659
  return this.mesh.meshId;
10530
10660
  }
10531
10661
  log = (level, msg, meta) => {
10532
- (this.opts.log ?? defaultLog)(level, msg, { mesh: this.mesh.slug, ...meta });
10662
+ (this.opts.log ?? defaultLog2)(level, msg, { mesh: this.mesh.slug, ...meta });
10533
10663
  };
10534
- setConnStatus(s) {
10535
- if (this._status === s)
10536
- return;
10537
- this._status = s;
10538
- this.opts.onStatusChange?.(s);
10539
- }
10540
10664
  async connect() {
10541
10665
  if (this.closed)
10542
10666
  throw new Error("client_closed");
10543
10667
  if (this._status === "connecting" || this._status === "open")
10544
10668
  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;
10669
+ this.lifecycle = await connectWsWithBackoff({
10670
+ url: this.mesh.brokerUrl,
10671
+ buildHello: async () => {
10672
+ const { timestamp: timestamp2, signature } = await signHello(this.mesh.meshId, this.mesh.memberId, this.mesh.pubkey, this.mesh.secretKey);
10673
+ return {
10674
+ type: "hello",
10675
+ meshId: this.mesh.meshId,
10676
+ memberId: this.mesh.memberId,
10677
+ pubkey: this.mesh.pubkey,
10678
+ displayName: this.opts.displayName,
10679
+ sessionId: `daemon-${process.pid}`,
10680
+ pid: process.pid,
10681
+ cwd: process.cwd(),
10682
+ hostname: __require("node:os").hostname(),
10683
+ peerType: "ai",
10684
+ channel: "claudemesh-daemon",
10685
+ timestamp: timestamp2,
10686
+ signature
10687
+ };
10688
+ },
10689
+ isHelloAck: (msg) => msg.type === "hello_ack",
10690
+ onMessage: (msg) => this.handleMessage(msg),
10691
+ onStatusChange: (s) => {
10692
+ this._status = s;
10693
+ this.opts.onStatusChange?.(s);
10694
+ if (s === "open") {
10598
10695
  const queued = this.opens.slice();
10599
10696
  this.opens.length = 0;
10600
10697
  for (const fn of queued) {
@@ -10604,123 +10701,121 @@ class DaemonBrokerClient {
10604
10701
  this.log("warn", "open_handler_failed", { err: String(e) });
10605
10702
  }
10606
10703
  }
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
- }
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
10704
  }
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;
10705
+ },
10706
+ onBeforeReconnect: (code) => this.failPendingAcks(`broker_disconnected_${code}`),
10707
+ log: (level, msg, meta) => this.log(level, `broker_${msg}`, meta)
10708
+ });
10709
+ }
10710
+ handleMessage(msg) {
10711
+ if (msg.type === "ack") {
10712
+ const id = String(msg.id ?? "");
10713
+ const ack = this.pendingAcks.get(id);
10714
+ if (ack) {
10715
+ this.pendingAcks.delete(id);
10716
+ clearTimeout(ack.timer);
10717
+ if (typeof msg.error === "string" && msg.error.length > 0) {
10718
+ ack.resolve({ ok: false, error: msg.error, permanent: classifyPermanent(msg.error) });
10719
+ } else {
10720
+ ack.resolve({ ok: true, messageId: String(msg.messageId ?? id) });
10708
10721
  }
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}`));
10722
+ }
10723
+ return;
10724
+ }
10725
+ if (msg.type === "peers_list") {
10726
+ const reqId = String(msg._reqId ?? "");
10727
+ const pending = this.peerListResolvers.get(reqId);
10728
+ if (pending) {
10729
+ this.peerListResolvers.delete(reqId);
10730
+ clearTimeout(pending.timer);
10731
+ pending.resolve(Array.isArray(msg.peers) ? msg.peers : []);
10732
+ }
10733
+ return;
10734
+ }
10735
+ if (msg.type === "skill_list") {
10736
+ const reqId = String(msg._reqId ?? "");
10737
+ const pending = this.skillListResolvers.get(reqId);
10738
+ if (pending) {
10739
+ this.skillListResolvers.delete(reqId);
10740
+ clearTimeout(pending.timer);
10741
+ pending.resolve(Array.isArray(msg.skills) ? msg.skills : []);
10742
+ }
10743
+ return;
10744
+ }
10745
+ if (msg.type === "skill_data") {
10746
+ const reqId = String(msg._reqId ?? "");
10747
+ const pending = this.skillDataResolvers.get(reqId);
10748
+ if (pending) {
10749
+ this.skillDataResolvers.delete(reqId);
10750
+ clearTimeout(pending.timer);
10751
+ pending.resolve(msg.skill ?? null);
10752
+ }
10753
+ return;
10754
+ }
10755
+ if (msg.type === "state_value" || msg.type === "state_data") {
10756
+ const reqId = String(msg._reqId ?? "");
10757
+ const pending = this.stateGetResolvers.get(reqId);
10758
+ if (pending) {
10759
+ this.stateGetResolvers.delete(reqId);
10760
+ clearTimeout(pending.timer);
10761
+ pending.resolve(msg.state ?? msg.row ?? null);
10762
+ }
10763
+ return;
10764
+ }
10765
+ if (msg.type === "state_list") {
10766
+ const reqId = String(msg._reqId ?? "");
10767
+ const pending = this.stateListResolvers.get(reqId);
10768
+ if (pending) {
10769
+ this.stateListResolvers.delete(reqId);
10770
+ clearTimeout(pending.timer);
10771
+ pending.resolve(Array.isArray(msg.entries) ? msg.entries : []);
10772
+ }
10773
+ return;
10774
+ }
10775
+ if (msg.type === "memory_stored") {
10776
+ const reqId = String(msg._reqId ?? "");
10777
+ const pending = this.memoryStoreResolvers.get(reqId);
10778
+ if (pending) {
10779
+ this.memoryStoreResolvers.delete(reqId);
10780
+ clearTimeout(pending.timer);
10781
+ pending.resolve(typeof msg.memoryId === "string" ? msg.memoryId : null);
10782
+ }
10783
+ return;
10784
+ }
10785
+ if (msg.type === "memory_recall_result") {
10786
+ const reqId = String(msg._reqId ?? "");
10787
+ const pending = this.memoryRecallResolvers.get(reqId);
10788
+ if (pending) {
10789
+ this.memoryRecallResolvers.delete(reqId);
10790
+ clearTimeout(pending.timer);
10791
+ pending.resolve(Array.isArray(msg.matches) ? msg.matches : []);
10792
+ }
10793
+ return;
10794
+ }
10795
+ if (msg.type === "push" || msg.type === "inbound") {
10796
+ this.opts.onPush?.(msg);
10797
+ return;
10798
+ }
10799
+ }
10800
+ isOpen() {
10801
+ const sock = this.lifecycle?.ws;
10802
+ return !!sock && sock.readyState === sock.OPEN;
10803
+ }
10804
+ sendClientAck(clientMessageId, brokerMessageId) {
10805
+ if (!this.isOpen())
10806
+ return;
10807
+ try {
10808
+ this.lifecycle.send({
10809
+ type: "client_ack",
10810
+ clientMessageId,
10811
+ ...brokerMessageId ? { brokerMessageId } : {}
10716
10812
  });
10717
- ws.on("error", (err) => this.log("warn", "broker_ws_error", { err: err.message }));
10718
- });
10813
+ } catch {}
10719
10814
  }
10720
10815
  send(req) {
10721
10816
  return new Promise((resolve) => {
10722
10817
  const dispatch = () => {
10723
- if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
10818
+ if (!this.isOpen()) {
10724
10819
  resolve({ ok: false, error: "broker_not_open", permanent: false });
10725
10820
  return;
10726
10821
  }
@@ -10732,7 +10827,7 @@ class DaemonBrokerClient {
10732
10827
  }, SEND_ACK_TIMEOUT_MS);
10733
10828
  this.pendingAcks.set(id, { resolve, timer });
10734
10829
  try {
10735
- this.ws.send(JSON.stringify({
10830
+ this.lifecycle.send({
10736
10831
  type: "send",
10737
10832
  id,
10738
10833
  client_message_id: id,
@@ -10741,7 +10836,7 @@ class DaemonBrokerClient {
10741
10836
  priority: req.priority,
10742
10837
  nonce: req.nonce,
10743
10838
  ciphertext: req.ciphertext
10744
- }));
10839
+ });
10745
10840
  } catch (e) {
10746
10841
  this.pendingAcks.delete(id);
10747
10842
  clearTimeout(timer);
@@ -10755,7 +10850,7 @@ class DaemonBrokerClient {
10755
10850
  });
10756
10851
  }
10757
10852
  async listPeers(timeoutMs = 5000) {
10758
- if (this._status !== "open" || !this.ws)
10853
+ if (this._status !== "open" || !this.lifecycle)
10759
10854
  return [];
10760
10855
  return new Promise((resolve) => {
10761
10856
  const reqId = `pl-${++this.reqCounter}`;
@@ -10765,7 +10860,7 @@ class DaemonBrokerClient {
10765
10860
  }, timeoutMs);
10766
10861
  this.peerListResolvers.set(reqId, { resolve, timer });
10767
10862
  try {
10768
- this.ws.send(JSON.stringify({ type: "list_peers", _reqId: reqId }));
10863
+ this.lifecycle.send({ type: "list_peers", _reqId: reqId });
10769
10864
  } catch {
10770
10865
  this.peerListResolvers.delete(reqId);
10771
10866
  clearTimeout(timer);
@@ -10774,7 +10869,7 @@ class DaemonBrokerClient {
10774
10869
  });
10775
10870
  }
10776
10871
  async listSkills(query, timeoutMs = 5000) {
10777
- if (this._status !== "open" || !this.ws)
10872
+ if (this._status !== "open" || !this.lifecycle)
10778
10873
  return [];
10779
10874
  return new Promise((resolve) => {
10780
10875
  const reqId = `sl-${++this.reqCounter}`;
@@ -10784,7 +10879,7 @@ class DaemonBrokerClient {
10784
10879
  }, timeoutMs);
10785
10880
  this.skillListResolvers.set(reqId, { resolve, timer });
10786
10881
  try {
10787
- this.ws.send(JSON.stringify({ type: "list_skills", query, _reqId: reqId }));
10882
+ this.lifecycle.send({ type: "list_skills", query, _reqId: reqId });
10788
10883
  } catch {
10789
10884
  this.skillListResolvers.delete(reqId);
10790
10885
  clearTimeout(timer);
@@ -10793,7 +10888,7 @@ class DaemonBrokerClient {
10793
10888
  });
10794
10889
  }
10795
10890
  async getSkill(name, timeoutMs = 5000) {
10796
- if (this._status !== "open" || !this.ws)
10891
+ if (this._status !== "open" || !this.lifecycle)
10797
10892
  return null;
10798
10893
  return new Promise((resolve) => {
10799
10894
  const reqId = `sg-${++this.reqCounter}`;
@@ -10803,7 +10898,7 @@ class DaemonBrokerClient {
10803
10898
  }, timeoutMs);
10804
10899
  this.skillDataResolvers.set(reqId, { resolve, timer });
10805
10900
  try {
10806
- this.ws.send(JSON.stringify({ type: "get_skill", name, _reqId: reqId }));
10901
+ this.lifecycle.send({ type: "get_skill", name, _reqId: reqId });
10807
10902
  } catch {
10808
10903
  this.skillDataResolvers.delete(reqId);
10809
10904
  clearTimeout(timer);
@@ -10812,7 +10907,7 @@ class DaemonBrokerClient {
10812
10907
  });
10813
10908
  }
10814
10909
  async getState(key, timeoutMs = 5000) {
10815
- if (this._status !== "open" || !this.ws)
10910
+ if (this._status !== "open" || !this.lifecycle)
10816
10911
  return null;
10817
10912
  return new Promise((resolve) => {
10818
10913
  const reqId = `sg-${++this.reqCounter}`;
@@ -10822,7 +10917,7 @@ class DaemonBrokerClient {
10822
10917
  }, timeoutMs);
10823
10918
  this.stateGetResolvers.set(reqId, { resolve, timer });
10824
10919
  try {
10825
- this.ws.send(JSON.stringify({ type: "get_state", key, _reqId: reqId }));
10920
+ this.lifecycle.send({ type: "get_state", key, _reqId: reqId });
10826
10921
  } catch {
10827
10922
  this.stateGetResolvers.delete(reqId);
10828
10923
  clearTimeout(timer);
@@ -10831,7 +10926,7 @@ class DaemonBrokerClient {
10831
10926
  });
10832
10927
  }
10833
10928
  async listState(timeoutMs = 5000) {
10834
- if (this._status !== "open" || !this.ws)
10929
+ if (this._status !== "open" || !this.lifecycle)
10835
10930
  return [];
10836
10931
  return new Promise((resolve) => {
10837
10932
  const reqId = `sl-${++this.reqCounter}`;
@@ -10841,7 +10936,7 @@ class DaemonBrokerClient {
10841
10936
  }, timeoutMs);
10842
10937
  this.stateListResolvers.set(reqId, { resolve, timer });
10843
10938
  try {
10844
- this.ws.send(JSON.stringify({ type: "list_state", _reqId: reqId }));
10939
+ this.lifecycle.send({ type: "list_state", _reqId: reqId });
10845
10940
  } catch {
10846
10941
  this.stateListResolvers.delete(reqId);
10847
10942
  clearTimeout(timer);
@@ -10850,14 +10945,14 @@ class DaemonBrokerClient {
10850
10945
  });
10851
10946
  }
10852
10947
  setState(key, value) {
10853
- if (this._status !== "open" || !this.ws)
10948
+ if (this._status !== "open" || !this.lifecycle)
10854
10949
  return;
10855
10950
  try {
10856
- this.ws.send(JSON.stringify({ type: "set_state", key, value }));
10951
+ this.lifecycle.send({ type: "set_state", key, value });
10857
10952
  } catch {}
10858
10953
  }
10859
10954
  async remember(content, tags, timeoutMs = 5000) {
10860
- if (this._status !== "open" || !this.ws)
10955
+ if (this._status !== "open" || !this.lifecycle)
10861
10956
  return null;
10862
10957
  return new Promise((resolve) => {
10863
10958
  const reqId = `mr-${++this.reqCounter}`;
@@ -10867,7 +10962,7 @@ class DaemonBrokerClient {
10867
10962
  }, timeoutMs);
10868
10963
  this.memoryStoreResolvers.set(reqId, { resolve, timer });
10869
10964
  try {
10870
- this.ws.send(JSON.stringify({ type: "remember", content, tags, _reqId: reqId }));
10965
+ this.lifecycle.send({ type: "remember", content, tags, _reqId: reqId });
10871
10966
  } catch {
10872
10967
  this.memoryStoreResolvers.delete(reqId);
10873
10968
  clearTimeout(timer);
@@ -10876,7 +10971,7 @@ class DaemonBrokerClient {
10876
10971
  });
10877
10972
  }
10878
10973
  async recall(query, timeoutMs = 5000) {
10879
- if (this._status !== "open" || !this.ws)
10974
+ if (this._status !== "open" || !this.lifecycle)
10880
10975
  return [];
10881
10976
  return new Promise((resolve) => {
10882
10977
  const reqId = `mc-${++this.reqCounter}`;
@@ -10886,7 +10981,7 @@ class DaemonBrokerClient {
10886
10981
  }, timeoutMs);
10887
10982
  this.memoryRecallResolvers.set(reqId, { resolve, timer });
10888
10983
  try {
10889
- this.ws.send(JSON.stringify({ type: "recall", query, _reqId: reqId }));
10984
+ this.lifecycle.send({ type: "recall", query, _reqId: reqId });
10890
10985
  } catch {
10891
10986
  this.memoryRecallResolvers.delete(reqId);
10892
10987
  clearTimeout(timer);
@@ -10895,60 +10990,50 @@ class DaemonBrokerClient {
10895
10990
  });
10896
10991
  }
10897
10992
  forget(memoryId) {
10898
- if (this._status !== "open" || !this.ws)
10993
+ if (this._status !== "open" || !this.lifecycle)
10899
10994
  return;
10900
10995
  try {
10901
- this.ws.send(JSON.stringify({ type: "forget", memoryId }));
10996
+ this.lifecycle.send({ type: "forget", memoryId });
10902
10997
  } catch {}
10903
10998
  }
10904
10999
  setProfile(profile) {
10905
- if (this._status !== "open" || !this.ws)
11000
+ if (this._status !== "open" || !this.lifecycle)
10906
11001
  return;
10907
11002
  try {
10908
- this.ws.send(JSON.stringify({ type: "set_profile", ...profile }));
11003
+ this.lifecycle.send({ type: "set_profile", ...profile });
10909
11004
  } catch {}
10910
11005
  }
10911
11006
  setSummary(summary) {
10912
- if (this._status !== "open" || !this.ws)
11007
+ if (this._status !== "open" || !this.lifecycle)
10913
11008
  return;
10914
11009
  try {
10915
- this.ws.send(JSON.stringify({ type: "set_summary", summary }));
11010
+ this.lifecycle.send({ type: "set_summary", summary });
10916
11011
  } catch {}
10917
11012
  }
10918
11013
  setStatus(status) {
10919
- if (this._status !== "open" || !this.ws)
11014
+ if (this._status !== "open" || !this.lifecycle)
10920
11015
  return;
10921
11016
  try {
10922
- this.ws.send(JSON.stringify({ type: "set_status", status }));
11017
+ this.lifecycle.send({ type: "set_status", status });
10923
11018
  } catch {}
10924
11019
  }
10925
11020
  setVisible(visible) {
10926
- if (this._status !== "open" || !this.ws)
11021
+ if (this._status !== "open" || !this.lifecycle)
10927
11022
  return;
10928
11023
  try {
10929
- this.ws.send(JSON.stringify({ type: "set_visible", visible }));
11024
+ this.lifecycle.send({ type: "set_visible", visible });
10930
11025
  } catch {}
10931
11026
  }
10932
11027
  async close() {
10933
11028
  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
11029
  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 };
11030
+ if (this.lifecycle) {
11031
+ try {
11032
+ await this.lifecycle.close();
11033
+ } catch {}
11034
+ this.lifecycle = null;
11035
+ }
11036
+ this._status = "closed";
10952
11037
  }
10953
11038
  failPendingAcks(reason) {
10954
11039
  for (const [id, ack] of this.pendingAcks) {
@@ -10958,7 +11043,7 @@ class DaemonBrokerClient {
10958
11043
  }
10959
11044
  }
10960
11045
  }
10961
- function defaultLog(level, msg, meta) {
11046
+ function defaultLog2(level, msg, meta) {
10962
11047
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
10963
11048
  if (level === "info")
10964
11049
  process.stdout.write(line + `
@@ -10970,24 +11055,21 @@ function defaultLog(level, msg, meta) {
10970
11055
  function classifyPermanent(err) {
10971
11056
  return /payload_too_large|forbidden|not_found|invalid|schema|auth|signature/i.test(err);
10972
11057
  }
10973
- var HELLO_ACK_TIMEOUT_MS2 = 5000, SEND_ACK_TIMEOUT_MS = 15000, BACKOFF_CAPS_MS;
11058
+ var SEND_ACK_TIMEOUT_MS = 15000;
10974
11059
  var init_broker = __esm(() => {
10975
11060
  init_hello_sig();
10976
- BACKOFF_CAPS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
11061
+ init_ws_lifecycle();
10977
11062
  });
10978
11063
 
10979
11064
  // src/daemon/session-broker.ts
10980
11065
  import { hostname as osHostname } from "node:os";
10981
- import WebSocket3 from "ws";
10982
11066
 
10983
11067
  class SessionBrokerClient {
10984
11068
  opts;
10985
- ws = null;
11069
+ lifecycle = null;
10986
11070
  _status = "closed";
10987
11071
  closed = false;
10988
- reconnectAttempt = 0;
10989
- reconnectTimer = null;
10990
- helloTimer = null;
11072
+ brokerUnsupported = false;
10991
11073
  constructor(opts) {
10992
11074
  this.opts = opts;
10993
11075
  }
@@ -11001,85 +11083,54 @@ class SessionBrokerClient {
11001
11083
  return this.opts.sessionPubkey;
11002
11084
  }
11003
11085
  log = (level, msg, meta) => {
11004
- (this.opts.log ?? defaultLog2)(level, msg, {
11086
+ (this.opts.log ?? defaultLog3)(level, msg, {
11005
11087
  mesh: this.opts.mesh.slug,
11006
11088
  session_pubkey: this.opts.sessionPubkey.slice(0, 12),
11007
11089
  ...meta
11008
11090
  });
11009
11091
  };
11010
- setStatus(s) {
11011
- if (this._status === s)
11012
- return;
11013
- this._status = s;
11014
- this.opts.onStatusChange?.(s);
11015
- }
11016
11092
  async connect() {
11017
11093
  if (this.closed)
11018
11094
  throw new Error("client_closed");
11019
11095
  if (this._status === "connecting" || this._status === "open")
11020
11096
  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
- }
11097
+ this.lifecycle = await connectWsWithBackoff({
11098
+ url: this.opts.mesh.brokerUrl,
11099
+ buildHello: async () => {
11100
+ const { timestamp: timestamp2, signature } = await signSessionHello({
11101
+ meshId: this.opts.mesh.meshId,
11102
+ parentMemberPubkey: this.opts.mesh.pubkey,
11103
+ sessionPubkey: this.opts.sessionPubkey,
11104
+ sessionSecretKey: this.opts.sessionSecretKey
11105
+ });
11106
+ return {
11107
+ type: "session_hello",
11108
+ meshId: this.opts.mesh.meshId,
11109
+ parentMemberId: this.opts.mesh.memberId,
11110
+ parentMemberPubkey: this.opts.mesh.pubkey,
11111
+ sessionPubkey: this.opts.sessionPubkey,
11112
+ parentAttestation: this.opts.parentAttestation,
11113
+ displayName: this.opts.displayName,
11114
+ sessionId: this.opts.sessionId,
11115
+ pid: this.opts.pid,
11116
+ cwd: this.opts.cwd ?? process.cwd(),
11117
+ hostname: osHostname(),
11118
+ peerType: "ai",
11119
+ channel: "claudemesh-session",
11120
+ ...this.opts.groups && this.opts.groups.length > 0 ? { groups: this.opts.groups } : {},
11121
+ ...this.opts.role ? { role: this.opts.role } : {},
11122
+ timestamp: timestamp2,
11123
+ signature
11124
+ };
11125
+ },
11126
+ isHelloAck: (msg) => msg.type === "hello_ack",
11127
+ onMessage: (msg) => {
11079
11128
  if (msg.type === "error") {
11080
11129
  this.log("warn", "broker_error", { code: msg.code, message: msg.message });
11081
11130
  if (msg.code === "unknown_message_type") {
11131
+ this.brokerUnsupported = true;
11082
11132
  this.closed = true;
11133
+ this.lifecycle?.close();
11083
11134
  }
11084
11135
  return;
11085
11136
  }
@@ -11087,44 +11138,40 @@ class SessionBrokerClient {
11087
11138
  this.opts.onPush?.(msg);
11088
11139
  return;
11089
11140
  }
11090
- });
11091
- ws.on("close", (code, reason) => {
11092
- if (this.helloTimer) {
11093
- clearTimeout(this.helloTimer);
11094
- this.helloTimer = null;
11095
- }
11096
- if (this.closed) {
11097
- this.setStatus("closed");
11098
- return;
11099
- }
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 }));
11141
+ },
11142
+ onStatusChange: (s) => {
11143
+ this._status = s;
11144
+ this.opts.onStatusChange?.(s);
11145
+ },
11146
+ log: (level, msg, meta) => this.log(level, `session_broker_${msg}`, meta)
11109
11147
  });
11110
11148
  }
11149
+ sendClientAck(clientMessageId, brokerMessageId) {
11150
+ if (this._status !== "open" || !this.lifecycle)
11151
+ return;
11152
+ try {
11153
+ this.lifecycle.send({
11154
+ type: "client_ack",
11155
+ clientMessageId,
11156
+ ...brokerMessageId ? { brokerMessageId } : {}
11157
+ });
11158
+ } catch {}
11159
+ }
11111
11160
  async close() {
11112
11161
  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;
11162
+ if (this.lifecycle) {
11163
+ try {
11164
+ await this.lifecycle.close();
11165
+ } catch {}
11166
+ this.lifecycle = null;
11120
11167
  }
11121
- try {
11122
- this.ws?.close();
11123
- } catch {}
11124
- this.setStatus("closed");
11168
+ this._status = "closed";
11169
+ }
11170
+ get isBrokerUnsupported() {
11171
+ return this.brokerUnsupported;
11125
11172
  }
11126
11173
  }
11127
- function defaultLog2(level, msg, meta) {
11174
+ function defaultLog3(level, msg, meta) {
11128
11175
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
11129
11176
  if (level === "info")
11130
11177
  process.stdout.write(line + `
@@ -11133,15 +11180,14 @@ function defaultLog2(level, msg, meta) {
11133
11180
  process.stderr.write(line + `
11134
11181
  `);
11135
11182
  }
11136
- var HELLO_ACK_TIMEOUT_MS3 = 5000, BACKOFF_CAPS_MS2;
11137
11183
  var init_session_broker = __esm(() => {
11138
11184
  init_session_hello_sig();
11139
- BACKOFF_CAPS_MS2 = [1000, 2000, 4000, 8000, 16000, 30000];
11185
+ init_ws_lifecycle();
11140
11186
  });
11141
11187
 
11142
11188
  // src/daemon/drain.ts
11143
11189
  function startDrainWorker(opts) {
11144
- const log2 = opts.log ?? defaultLog3;
11190
+ const log2 = opts.log ?? defaultLog4;
11145
11191
  let stopped = false;
11146
11192
  let wakeResolve = null;
11147
11193
  let wakePromise = new Promise((r) => {
@@ -11285,7 +11331,7 @@ async function randomNonce2() {
11285
11331
  const { randomBytes: randomBytes6 } = await import("node:crypto");
11286
11332
  return randomBytes6(24).toString("base64");
11287
11333
  }
11288
- function defaultLog3(level, msg, meta) {
11334
+ function defaultLog4(level, msg, meta) {
11289
11335
  const line = JSON.stringify({ level, msg, ...meta, ts: new Date().toISOString() });
11290
11336
  if (level === "info")
11291
11337
  process.stdout.write(line + `
@@ -11342,6 +11388,7 @@ async function handleBrokerPush(msg, ctx) {
11342
11388
  received_at: Date.now(),
11343
11389
  reply_to_id: replyToId
11344
11390
  });
11391
+ ctx.ackClientMessage?.(clientMessageId, brokerMessageId);
11345
11392
  if (!inserted)
11346
11393
  return;
11347
11394
  ctx.bus.publish("message", {
@@ -11612,13 +11659,12 @@ async function runDaemon(opts = {}) {
11612
11659
  bus.publish("broker_status", { mesh: mesh.slug, status: s });
11613
11660
  },
11614
11661
  onPush: (m) => {
11615
- const sessionKeys = broker.getSessionKeys();
11616
11662
  handleBrokerPush(m, {
11617
11663
  db: inboxDb,
11618
11664
  bus,
11619
11665
  meshSlug: mesh.slug,
11620
11666
  recipientSecretKeyHex: mesh.secretKey,
11621
- sessionSecretKeyHex: sessionKeys?.sessionSecretKey
11667
+ ackClientMessage: (cmid, bmid) => broker.sendClientAck(cmid, bmid)
11622
11668
  });
11623
11669
  }
11624
11670
  });
@@ -11666,7 +11712,8 @@ async function runDaemon(opts = {}) {
11666
11712
  bus,
11667
11713
  meshSlug: meshConfig.slug,
11668
11714
  recipientSecretKeyHex: meshConfig.secretKey,
11669
- sessionSecretKeyHex
11715
+ sessionSecretKeyHex,
11716
+ ackClientMessage: (cmid, bmid) => client.sendClientAck(cmid, bmid)
11670
11717
  });
11671
11718
  }
11672
11719
  });
@@ -13076,8 +13123,8 @@ async function checkBrokerWs() {
13076
13123
  const wsUrl = URLS.BROKER;
13077
13124
  const start = Date.now();
13078
13125
  try {
13079
- const WebSocket4 = (await import("ws")).default;
13080
- const ws = new WebSocket4(wsUrl);
13126
+ const WebSocket3 = (await import("ws")).default;
13127
+ const ws = new WebSocket3(wsUrl);
13081
13128
  const result = await new Promise((resolve2) => {
13082
13129
  const timer = setTimeout(() => {
13083
13130
  try {
@@ -13188,11 +13235,11 @@ __export(exports_status, {
13188
13235
  runStatus: () => runStatus2
13189
13236
  });
13190
13237
  import { statSync as statSync4, existsSync as existsSync16 } from "node:fs";
13191
- import WebSocket4 from "ws";
13238
+ import WebSocket3 from "ws";
13192
13239
  async function probeBroker(url, timeoutMs = 4000) {
13193
13240
  return new Promise((resolve2) => {
13194
13241
  const started = Date.now();
13195
- const ws = new WebSocket4(url);
13242
+ const ws = new WebSocket3(url);
13196
13243
  const timer = setTimeout(() => {
13197
13244
  try {
13198
13245
  ws.terminate();
@@ -16402,7 +16449,7 @@ var require_client = __commonJS((exports) => {
16402
16449
  var ws_1 = __importDefault(__require("ws"));
16403
16450
  var crypto_js_1 = require_crypto();
16404
16451
  var MAX_QUEUED2 = 100;
16405
- var HELLO_ACK_TIMEOUT_MS4 = 5000;
16452
+ var HELLO_ACK_TIMEOUT_MS2 = 5000;
16406
16453
  var BACKOFF_CAPS2 = [1000, 2000, 4000, 8000, 16000, 30000];
16407
16454
 
16408
16455
  class MeshClient extends node_events_1.EventEmitter {
@@ -16467,7 +16514,7 @@ var require_client = __commonJS((exports) => {
16467
16514
  this.debug("hello_ack timeout");
16468
16515
  ws.close();
16469
16516
  reject(new Error("hello_ack timeout"));
16470
- }, HELLO_ACK_TIMEOUT_MS4);
16517
+ }, HELLO_ACK_TIMEOUT_MS2);
16471
16518
  };
16472
16519
  const onMessage = (raw) => {
16473
16520
  let msg;
@@ -20287,4 +20334,4 @@ main().catch((err) => {
20287
20334
  process.exit(EXIT.INTERNAL_ERROR);
20288
20335
  });
20289
20336
 
20290
- //# debugId=FAFB4F8D5ECBBBB864756E2164756E21
20337
+ //# debugId=DC25A8288183DA4364756E2164756E21