claudemesh-cli 1.34.8 → 1.34.10

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.34.8", env;
107
+ var URLS, VERSION = "1.34.10", env;
108
108
  var init_urls = __esm(() => {
109
109
  URLS = {
110
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -4331,6 +4331,7 @@ async function ensureDaemonRunning(meshSlug, quiet) {
4331
4331
  if (res.state === "up") {
4332
4332
  if (!quiet)
4333
4333
  render.ok("daemon already running");
4334
+ await warnIfDaemonStale(quiet);
4334
4335
  return;
4335
4336
  }
4336
4337
  if (res.state === "started") {
@@ -4338,7 +4339,22 @@ async function ensureDaemonRunning(meshSlug, quiet) {
4338
4339
  render.ok(`daemon ready (${res.durationMs}ms)`);
4339
4340
  return;
4340
4341
  }
4341
- render.warn(`daemon ${res.state}${res.reason ? `: ${res.reason}` : ""}`, "Run `claudemesh daemon up --mesh " + meshSlug + "` manually, then re-launch.");
4342
+ render.warn(`daemon ${res.state}${res.reason ? `: ${res.reason}` : ""}`, "Run `claudemesh daemon up` manually, then re-launch.");
4343
+ }
4344
+ async function warnIfDaemonStale(quiet) {
4345
+ if (quiet)
4346
+ return;
4347
+ try {
4348
+ const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4349
+ const { VERSION: VERSION2 } = await Promise.resolve().then(() => (init_urls(), exports_urls));
4350
+ const res = await ipc2({ path: "/v1/version", timeoutMs: 1500 });
4351
+ if (res.status !== 200)
4352
+ return;
4353
+ const daemonVersion = res.body.daemon_version ?? "";
4354
+ if (!daemonVersion || daemonVersion === VERSION2)
4355
+ return;
4356
+ render.warn(`daemon is ${daemonVersion}, CLI is ${VERSION2} — restart to pick up new fixes.`, "Run: `claudemesh daemon down && claudemesh daemon up` (no --mesh — daemon attaches to every joined mesh; restart the launchd / systemd-user unit if you installed one).");
4357
+ } catch {}
4342
4358
  }
4343
4359
  function parseGroupsString(raw) {
4344
4360
  return raw.split(",").map((s) => s.trim()).filter(Boolean).map((token) => {
@@ -9703,7 +9719,29 @@ function writeSse(res, e, idCounter) {
9703
9719
 
9704
9720
  `);
9705
9721
  }
9706
- function bindSseStream(res, bus) {
9722
+ function shouldDeliver(e, f) {
9723
+ if (!f.sessionPubkey && !f.memberPubkey && !f.meshSlug)
9724
+ return true;
9725
+ if (f.meshSlug) {
9726
+ const eventMesh = typeof e.data.mesh === "string" ? e.data.mesh : null;
9727
+ if (eventMesh && eventMesh !== f.meshSlug)
9728
+ return false;
9729
+ }
9730
+ if (e.kind !== "message")
9731
+ return true;
9732
+ const recipientKind = typeof e.data.recipient_kind === "string" ? e.data.recipient_kind : null;
9733
+ const recipientPubkey = typeof e.data.recipient_pubkey === "string" ? e.data.recipient_pubkey.toLowerCase() : null;
9734
+ if (!recipientKind || !recipientPubkey)
9735
+ return true;
9736
+ if (recipientKind === "session") {
9737
+ return !!f.sessionPubkey && f.sessionPubkey.toLowerCase() === recipientPubkey;
9738
+ }
9739
+ if (recipientKind === "member") {
9740
+ return !!f.memberPubkey && f.memberPubkey.toLowerCase() === recipientPubkey;
9741
+ }
9742
+ return true;
9743
+ }
9744
+ function bindSseStream(res, bus, filter = {}) {
9707
9745
  res.statusCode = 200;
9708
9746
  res.setHeader("Content-Type", "text/event-stream");
9709
9747
  res.setHeader("Cache-Control", "no-cache, no-transform");
@@ -9713,7 +9751,11 @@ function bindSseStream(res, bus) {
9713
9751
 
9714
9752
  `);
9715
9753
  let counter = 0;
9716
- const unsubscribe = bus.subscribe((e) => writeSse(res, e, ++counter));
9754
+ const unsubscribe = bus.subscribe((e) => {
9755
+ if (!shouldDeliver(e, filter))
9756
+ return;
9757
+ writeSse(res, e, ++counter);
9758
+ });
9717
9759
  const heartbeat = setInterval(() => {
9718
9760
  try {
9719
9761
  res.write(`: keepalive
@@ -10130,7 +10172,16 @@ function makeHandler(opts) {
10130
10172
  respond(res, 503, { error: "event bus not initialised" });
10131
10173
  return;
10132
10174
  }
10133
- bindSseStream(res, opts.bus);
10175
+ const filter = {};
10176
+ if (session?.presence?.sessionPubkey)
10177
+ filter.sessionPubkey = session.presence.sessionPubkey;
10178
+ if (session?.mesh) {
10179
+ filter.meshSlug = session.mesh;
10180
+ const meshCfg = opts.meshConfigs?.get(session.mesh);
10181
+ if (meshCfg?.pubkey)
10182
+ filter.memberPubkey = meshCfg.pubkey;
10183
+ }
10184
+ bindSseStream(res, opts.bus, filter);
10134
10185
  return;
10135
10186
  }
10136
10187
  if (req.method === "GET" && url.pathname === "/v1/peers") {
@@ -11438,6 +11489,8 @@ class SessionBrokerClient {
11438
11489
  return;
11439
11490
  }
11440
11491
  if (msg.type === "push" || msg.type === "inbound") {
11492
+ if (msg.subtype === "system")
11493
+ return;
11441
11494
  const senderPubkey = String(msg.senderPubkey ?? "").toLowerCase();
11442
11495
  if (senderPubkey && senderPubkey === this.opts.sessionPubkey.toLowerCase()) {
11443
11496
  this.log("info", "self_echo_dropped", { sender: senderPubkey.slice(0, 12) });
@@ -11778,10 +11831,14 @@ var init_inbox_pruner = __esm(() => {
11778
11831
  import { randomUUID as randomUUID4 } from "node:crypto";
11779
11832
  async function handleBrokerPush(msg, ctx) {
11780
11833
  if (msg.subtype === "system" && typeof msg.event === "string") {
11834
+ const eventData = msg.eventData ?? {};
11835
+ const eventPubkey = typeof eventData.pubkey === "string" ? eventData.pubkey : "";
11836
+ if (eventPubkey && ctx.isOwnPubkey?.(eventPubkey))
11837
+ return;
11781
11838
  ctx.bus.publish(mapSystemEventKind(msg.event), {
11782
11839
  mesh: ctx.meshSlug,
11783
11840
  event: msg.event,
11784
- ...msg.eventData ?? {}
11841
+ ...eventData
11785
11842
  });
11786
11843
  return;
11787
11844
  }
@@ -11835,7 +11892,9 @@ async function handleBrokerPush(msg, ctx) {
11835
11892
  priority,
11836
11893
  ...subtype ? { subtype } : {},
11837
11894
  body,
11838
- created_at: createdAt
11895
+ created_at: createdAt,
11896
+ ...ctx.recipientPubkey ? { recipient_pubkey: ctx.recipientPubkey } : {},
11897
+ ...ctx.recipientKind ? { recipient_kind: ctx.recipientKind } : {}
11839
11898
  });
11840
11899
  }
11841
11900
  async function decryptOrFallback(args) {
@@ -12047,22 +12106,7 @@ async function runDaemon(opts = {}) {
12047
12106
  }
12048
12107
  const bus = new EventBus;
12049
12108
  const cfg = readConfig();
12050
- let meshes;
12051
- if (opts.mesh) {
12052
- const found = cfg.meshes.find((m) => m.slug === opts.mesh);
12053
- if (!found) {
12054
- process.stderr.write(`mesh not found: ${opts.mesh}
12055
- `);
12056
- process.stderr.write(`joined meshes: ${cfg.meshes.map((m) => m.slug).join(", ") || "(none)"}
12057
- `);
12058
- releaseSingletonLock();
12059
- try {
12060
- outboxDb.close();
12061
- } catch {}
12062
- return 2;
12063
- }
12064
- meshes = [found];
12065
- } else if (cfg.meshes.length === 0) {
12109
+ if (cfg.meshes.length === 0) {
12066
12110
  process.stderr.write(`no mesh joined; run \`claudemesh join <invite-url>\` first
12067
12111
  `);
12068
12112
  releaseSingletonLock();
@@ -12070,15 +12114,15 @@ async function runDaemon(opts = {}) {
12070
12114
  outboxDb.close();
12071
12115
  } catch {}
12072
12116
  return 2;
12073
- } else {
12074
- meshes = cfg.meshes;
12075
12117
  }
12118
+ const meshes = cfg.meshes;
12119
+ const sessionBrokers = new Map;
12120
+ const sessionBrokersByPubkey = new Map;
12076
12121
  const brokers = new Map;
12077
12122
  const meshConfigs = new Map;
12078
12123
  for (const mesh of meshes) {
12079
12124
  meshConfigs.set(mesh.slug, mesh);
12080
12125
  const broker = new DaemonBrokerClient(mesh, {
12081
- displayName: opts.displayName,
12082
12126
  onStatusChange: (s) => {
12083
12127
  process.stdout.write(JSON.stringify({
12084
12128
  msg: "broker_status",
@@ -12091,9 +12135,8 @@ async function runDaemon(opts = {}) {
12091
12135
  },
12092
12136
  onPush: (m) => {
12093
12137
  const senderMemberPk = String(m.senderMemberPubkey ?? "").toLowerCase();
12094
- const senderPubkey = String(m.senderPubkey ?? "").toLowerCase();
12095
12138
  const ownMember = mesh.pubkey.toLowerCase();
12096
- if (senderMemberPk && senderMemberPk === ownMember && senderPubkey === ownMember) {
12139
+ if (senderMemberPk && senderMemberPk === ownMember) {
12097
12140
  return;
12098
12141
  }
12099
12142
  handleBrokerPush(m, {
@@ -12101,7 +12144,15 @@ async function runDaemon(opts = {}) {
12101
12144
  bus,
12102
12145
  meshSlug: mesh.slug,
12103
12146
  recipientSecretKeyHex: mesh.secretKey,
12104
- ackClientMessage: (cmid, bmid) => broker.sendClientAck(cmid, bmid)
12147
+ ackClientMessage: (cmid, bmid) => broker.sendClientAck(cmid, bmid),
12148
+ isOwnPubkey: (pubkey) => {
12149
+ const lower = pubkey.toLowerCase();
12150
+ if (lower === ownMember)
12151
+ return true;
12152
+ return sessionBrokersByPubkey.has(lower);
12153
+ },
12154
+ recipientPubkey: mesh.pubkey,
12155
+ recipientKind: "member"
12105
12156
  });
12106
12157
  }
12107
12158
  });
@@ -12109,8 +12160,6 @@ async function runDaemon(opts = {}) {
12109
12160
  `));
12110
12161
  brokers.set(mesh.slug, broker);
12111
12162
  }
12112
- const sessionBrokers = new Map;
12113
- const sessionBrokersByPubkey = new Map;
12114
12163
  let drain = null;
12115
12164
  drain = startDrainWorker({
12116
12165
  db: outboxDb,
@@ -12142,6 +12191,7 @@ async function runDaemon(opts = {}) {
12142
12191
  prior.close().catch(() => {});
12143
12192
  }
12144
12193
  const sessionSecretKeyHex = info.presence.sessionSecretKey;
12194
+ const sessionPubkeyHex = info.presence.sessionPubkey;
12145
12195
  const client = new SessionBrokerClient({
12146
12196
  mesh: meshConfig,
12147
12197
  sessionPubkey: info.presence.sessionPubkey,
@@ -12159,7 +12209,9 @@ async function runDaemon(opts = {}) {
12159
12209
  meshSlug: meshConfig.slug,
12160
12210
  recipientSecretKeyHex: meshConfig.secretKey,
12161
12211
  sessionSecretKeyHex,
12162
- ackClientMessage: (cmid, bmid) => client.sendClientAck(cmid, bmid)
12212
+ ackClientMessage: (cmid, bmid) => client.sendClientAck(cmid, bmid),
12213
+ recipientPubkey: sessionPubkeyHex,
12214
+ recipientKind: "session"
12163
12215
  });
12164
12216
  }
12165
12217
  });
@@ -12207,6 +12259,7 @@ async function runDaemon(opts = {}) {
12207
12259
  }
12208
12260
  process.stdout.write(JSON.stringify({
12209
12261
  msg: "daemon_started",
12262
+ version: VERSION,
12210
12263
  pid: process.pid,
12211
12264
  sock: DAEMON_PATHS.SOCK_FILE,
12212
12265
  tcp: tcpEnabled ? `127.0.0.1:47823` : null,
@@ -12262,6 +12315,7 @@ var init_run = __esm(() => {
12262
12315
  init_inbound();
12263
12316
  init_identity();
12264
12317
  init_facade();
12318
+ init_urls();
12265
12319
  });
12266
12320
 
12267
12321
  // src/daemon/service-install.ts
@@ -12481,11 +12535,17 @@ async function runDaemonCommand(sub, opts, rest = []) {
12481
12535
  return printDaemonUsage();
12482
12536
  case "up":
12483
12537
  case "start":
12538
+ if (opts.mesh) {
12539
+ process.stderr.write(`[claudemesh] --mesh on \`daemon up\` is deprecated; the daemon attaches to every joined mesh automatically. ` + `Ignoring --mesh ${opts.mesh}.
12540
+ `);
12541
+ }
12542
+ if (opts.displayName) {
12543
+ process.stderr.write(`[claudemesh] --name on \`daemon up\` is deprecated; per-mesh display names live in config.json (set at join time), ` + `and session display names come from \`claudemesh launch --name\`. Ignoring --name ${opts.displayName}.
12544
+ `);
12545
+ }
12484
12546
  return runDaemon({
12485
12547
  tcpEnabled: !opts.noTcp,
12486
- publicHealthCheck: opts.publicHealth,
12487
- mesh: opts.mesh,
12488
- displayName: opts.displayName
12548
+ publicHealthCheck: opts.publicHealth
12489
12549
  });
12490
12550
  case "help":
12491
12551
  case "--help":
@@ -12528,11 +12588,10 @@ COMMANDS
12528
12588
  accept-host pin the current host fingerprint
12529
12589
  outbox list list local outbox rows (newest first)
12530
12590
  outbox requeue <id> re-enqueue an aborted / dead outbox row
12531
- install-service --mesh <s> write launchd (macOS) / systemd-user (Linux) unit
12591
+ install-service write launchd (macOS) / systemd-user (Linux) unit
12532
12592
  uninstall-service remove the platform service unit
12533
12593
 
12534
12594
  OPTIONS
12535
- --mesh <slug> attach to / target this mesh
12536
12595
  --name <displayName> override CLAUDEMESH_DISPLAY_NAME
12537
12596
  --no-tcp disable the loopback TCP listener (UDS only)
12538
12597
  --public-health expose /v1/health unauthenticated on TCP
@@ -12640,11 +12699,17 @@ async function runInstallService(opts) {
12640
12699
  return 1;
12641
12700
  }
12642
12701
  }
12702
+ if (opts.mesh) {
12703
+ process.stderr.write(`[claudemesh] --mesh on \`daemon install-service\` is deprecated and ignored; the daemon attaches to every joined mesh.
12704
+ `);
12705
+ }
12706
+ if (opts.displayName) {
12707
+ process.stderr.write(`[claudemesh] --name on \`daemon install-service\` is deprecated and ignored; per-mesh names live in config.json, session names come from \`claudemesh launch --name\`.
12708
+ `);
12709
+ }
12643
12710
  try {
12644
12711
  const r = installService2({
12645
- binaryPath: binary,
12646
- ...opts.mesh ? { meshSlug: opts.mesh } : {},
12647
- ...opts.displayName ? { displayName: opts.displayName } : {}
12712
+ binaryPath: binary
12648
12713
  });
12649
12714
  if (opts.json) {
12650
12715
  process.stdout.write(JSON.stringify({ ok: true, ...r }) + `
@@ -18796,11 +18861,23 @@ ${mf.allowed_tools.map((t) => ` - ${t}`).join(`
18796
18861
  } else if (ev.kind === "peer_join" || ev.kind === "peer_leave" || ev.kind === "system") {
18797
18862
  const d = ev.data;
18798
18863
  const eventName = String(d.event ?? ev.kind);
18864
+ const renderPeerLine = (verb) => {
18865
+ const name = String(d.name ?? "unknown");
18866
+ const pubkey = String(d.pubkey ?? "");
18867
+ const pubkeyTag = pubkey ? ` (${pubkey.slice(0, 8)})` : "";
18868
+ const groups = Array.isArray(d.groups) ? d.groups : [];
18869
+ const groupNames = groups.map((g) => typeof g === "object" && g !== null && ("name" in g) ? String(g.name) : typeof g === "string" ? g : "").filter(Boolean);
18870
+ const groupsTag = groupNames.length > 0 ? ` [${groupNames.join(", ")}]` : "";
18871
+ const lastSeen = typeof d.lastSeenAt === "string" ? d.lastSeenAt : null;
18872
+ const summary = typeof d.summary === "string" && d.summary.trim() ? d.summary.trim() : null;
18873
+ const returningTail = lastSeen ? ` — last seen ${new Date(lastSeen).toLocaleTimeString()}${summary ? ` · "${summary.slice(0, 80)}"` : ""}` : "";
18874
+ return `[system] Peer "${name}"${pubkeyTag}${groupsTag} ${verb} the mesh${returningTail}`;
18875
+ };
18799
18876
  let content;
18800
18877
  if (ev.kind === "peer_join") {
18801
- content = `[system] Peer "${String(d.name ?? "unknown")}" joined the mesh`;
18878
+ content = renderPeerLine(eventName === "peer_returned" ? "returned to" : "joined");
18802
18879
  } else if (ev.kind === "peer_leave") {
18803
- content = `[system] Peer "${String(d.name ?? "unknown")}" left the mesh`;
18880
+ content = renderPeerLine("left");
18804
18881
  } else {
18805
18882
  content = `[system] ${eventName}: ${JSON.stringify(d).slice(0, 240)}`;
18806
18883
  }
@@ -18812,7 +18889,12 @@ ${mf.allowed_tools.map((t) => ` - ${t}`).join(`
18812
18889
  meta: {
18813
18890
  kind: "system",
18814
18891
  event: eventName,
18815
- mesh_slug: String(d.mesh ?? "")
18892
+ mesh_slug: String(d.mesh ?? ""),
18893
+ ...typeof d.name === "string" ? { peer_name: d.name } : {},
18894
+ ...typeof d.pubkey === "string" ? { peer_pubkey: d.pubkey } : {},
18895
+ ...Array.isArray(d.groups) ? { peer_groups: JSON.stringify(d.groups) } : {},
18896
+ ...typeof d.lastSeenAt === "string" ? { peer_last_seen_at: d.lastSeenAt } : {},
18897
+ ...typeof d.summary === "string" ? { peer_summary: d.summary } : {}
18816
18898
  }
18817
18899
  }
18818
18900
  });
@@ -19934,16 +20016,18 @@ Security
19934
20016
  claudemesh backup [file] encrypt config → portable recovery file
19935
20017
  claudemesh restore <file> restore config from a backup file
19936
20018
 
19937
- Daemon (long-lived peer mesh runtime, v0.9.0)
19938
- claudemesh daemon up start daemon (alias: start) [--mesh <slug>] [--no-tcp]
20019
+ Daemon (long-lived peer mesh runtime — universal across every joined mesh)
20020
+ claudemesh daemon up start daemon (alias: start) [--no-tcp]
19939
20021
  claudemesh daemon status show running pid + IPC health [--json]
19940
20022
  claudemesh daemon down stop daemon (alias: stop)
19941
20023
  claudemesh daemon version ipc + schema version of running daemon
19942
20024
  claudemesh daemon outbox list list local outbox rows [--failed|--pending|--inflight|--done]
19943
20025
  claudemesh daemon outbox requeue <id> re-enqueue an aborted/dead row [--new-client-id <id>]
19944
20026
  claudemesh daemon accept-host pin current host fingerprint
19945
- claudemesh daemon install-service --mesh <slug> write launchd / systemd-user unit
19946
- claudemesh daemon uninstall-service remove the unit
20027
+ claudemesh daemon install-service write launchd / systemd-user unit
20028
+ claudemesh daemon uninstall-service remove the unit
20029
+ Note: the daemon attaches to every mesh in ~/.claudemesh/config.json
20030
+ automatically; --mesh on up / install-service is deprecated and ignored.
19947
20031
 
19948
20032
  Setup
19949
20033
  claudemesh install register MCP server + hooks
@@ -21045,4 +21129,4 @@ main().catch((err) => {
21045
21129
  process.exit(EXIT.INTERNAL_ERROR);
21046
21130
  });
21047
21131
 
21048
- //# debugId=99681AB539B313DF64756E2164756E21
21132
+ //# debugId=80324EA6AC54078664756E2164756E21