claudemesh-cli 1.31.6 → 1.32.1

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.31.6", env;
107
+ var URLS, VERSION = "1.32.1", env;
108
108
  var init_urls = __esm(() => {
109
109
  URLS = {
110
110
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -3966,6 +3966,252 @@ var init_lifecycle = __esm(() => {
3966
3966
  init_paths2();
3967
3967
  });
3968
3968
 
3969
+ // src/ui/warnings.ts
3970
+ function warnDaemonState(res, opts = {}) {
3971
+ if (alreadyWarned)
3972
+ return false;
3973
+ if (opts.quiet || opts.json)
3974
+ return false;
3975
+ if (res.state === "up")
3976
+ return false;
3977
+ if (getDaemonPolicy().mode === "strict" && res.state !== "started")
3978
+ return false;
3979
+ alreadyWarned = true;
3980
+ const tag = (label) => `[claudemesh] ${label}`;
3981
+ const hint = (s) => dim(s);
3982
+ switch (res.state) {
3983
+ case "started":
3984
+ process.stderr.write(`${tag("info")} daemon restarted automatically ${hint(`(took ${res.durationMs}ms)`)}
3985
+ `);
3986
+ return true;
3987
+ case "down":
3988
+ process.stderr.write(`${tag("info")} daemon not running — using cold path ${hint("(slower; run `claudemesh daemon up` for warm path)")}
3989
+ `);
3990
+ return true;
3991
+ case "spawn-suppressed":
3992
+ process.stderr.write(`${tag("warn")} ${res.reason ?? "daemon failed to start recently"} — using cold path ${hint("(run `claudemesh doctor`)")}
3993
+ `);
3994
+ return true;
3995
+ case "spawn-failed":
3996
+ process.stderr.write(`${tag("warn")} daemon spawn failed${res.reason ? `: ${res.reason}` : ""} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
3997
+ `);
3998
+ return true;
3999
+ case "service-not-ready":
4000
+ process.stderr.write(`${tag("warn")} ${res.reason ?? "service-managed daemon not responding"} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
4001
+ `);
4002
+ return true;
4003
+ }
4004
+ return false;
4005
+ }
4006
+ var alreadyWarned = false;
4007
+ var init_warnings = __esm(() => {
4008
+ init_policy();
4009
+ init_styles();
4010
+ });
4011
+
4012
+ // src/services/bridge/daemon-route.ts
4013
+ var exports_daemon_route = {};
4014
+ __export(exports_daemon_route, {
4015
+ trySetStateViaDaemon: () => trySetStateViaDaemon,
4016
+ trySendViaDaemon: () => trySendViaDaemon,
4017
+ tryRememberViaDaemon: () => tryRememberViaDaemon,
4018
+ tryRecallViaDaemon: () => tryRecallViaDaemon,
4019
+ tryListStateViaDaemon: () => tryListStateViaDaemon,
4020
+ tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
4021
+ tryListPeersViaDaemon: () => tryListPeersViaDaemon,
4022
+ tryGetStateViaDaemon: () => tryGetStateViaDaemon,
4023
+ tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
4024
+ tryForgetViaDaemon: () => tryForgetViaDaemon
4025
+ });
4026
+ function meshQuery(mesh) {
4027
+ return mesh ? `?mesh=${encodeURIComponent(mesh)}` : "";
4028
+ }
4029
+ async function daemonReachable() {
4030
+ const policy2 = getDaemonPolicy();
4031
+ if (policy2.mode === "no-daemon")
4032
+ return false;
4033
+ const res = await ensureDaemonReady({ noAutoSpawn: false });
4034
+ warnDaemonState(res, {});
4035
+ return res.state === "up" || res.state === "started";
4036
+ }
4037
+ async function tryListPeersViaDaemon(mesh) {
4038
+ if (!await daemonReachable())
4039
+ return null;
4040
+ try {
4041
+ const res = await ipc({ path: `/v1/peers${meshQuery(mesh)}`, timeoutMs: 3000 });
4042
+ if (res.status !== 200)
4043
+ return null;
4044
+ return Array.isArray(res.body.peers) ? res.body.peers : [];
4045
+ } catch (err) {
4046
+ const msg = String(err);
4047
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4048
+ return null;
4049
+ return null;
4050
+ }
4051
+ }
4052
+ async function tryListSkillsViaDaemon(mesh) {
4053
+ if (!await daemonReachable())
4054
+ return null;
4055
+ try {
4056
+ const res = await ipc({ path: `/v1/skills${meshQuery(mesh)}`, timeoutMs: 3000 });
4057
+ if (res.status !== 200)
4058
+ return null;
4059
+ return Array.isArray(res.body.skills) ? res.body.skills : [];
4060
+ } catch (err) {
4061
+ const msg = String(err);
4062
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4063
+ return null;
4064
+ return null;
4065
+ }
4066
+ }
4067
+ async function tryGetSkillViaDaemon(name, mesh) {
4068
+ if (!await daemonReachable())
4069
+ return null;
4070
+ try {
4071
+ const res = await ipc({
4072
+ path: `/v1/skills/${encodeURIComponent(name)}${meshQuery(mesh)}`,
4073
+ timeoutMs: 3000
4074
+ });
4075
+ if (res.status === 404)
4076
+ return null;
4077
+ if (res.status !== 200)
4078
+ return null;
4079
+ return res.body.skill ?? null;
4080
+ } catch {
4081
+ return null;
4082
+ }
4083
+ }
4084
+ async function tryGetStateViaDaemon(key, mesh) {
4085
+ if (!await daemonReachable())
4086
+ return null;
4087
+ try {
4088
+ const path = `/v1/state?key=${encodeURIComponent(key)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
4089
+ const res = await ipc({ path, timeoutMs: 3000 });
4090
+ if (res.status === 404)
4091
+ return;
4092
+ if (res.status !== 200)
4093
+ return null;
4094
+ return res.body.state ?? undefined;
4095
+ } catch (err) {
4096
+ const msg = String(err);
4097
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4098
+ return null;
4099
+ return null;
4100
+ }
4101
+ }
4102
+ async function tryListStateViaDaemon(mesh) {
4103
+ if (!await daemonReachable())
4104
+ return null;
4105
+ try {
4106
+ const res = await ipc({ path: `/v1/state${meshQuery(mesh)}`, timeoutMs: 3000 });
4107
+ if (res.status !== 200)
4108
+ return null;
4109
+ return Array.isArray(res.body.entries) ? res.body.entries : [];
4110
+ } catch (err) {
4111
+ const msg = String(err);
4112
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4113
+ return null;
4114
+ return null;
4115
+ }
4116
+ }
4117
+ async function trySetStateViaDaemon(key, value, mesh) {
4118
+ if (!await daemonReachable())
4119
+ return false;
4120
+ try {
4121
+ const res = await ipc({
4122
+ method: "POST",
4123
+ path: "/v1/state",
4124
+ timeoutMs: 3000,
4125
+ body: { key, value, ...mesh ? { mesh } : {} }
4126
+ });
4127
+ return res.status === 200 && res.body.ok === true;
4128
+ } catch {
4129
+ return false;
4130
+ }
4131
+ }
4132
+ async function tryRememberViaDaemon(content, tags, mesh) {
4133
+ if (!await daemonReachable())
4134
+ return null;
4135
+ try {
4136
+ const res = await ipc({
4137
+ method: "POST",
4138
+ path: "/v1/memory",
4139
+ timeoutMs: 5000,
4140
+ body: { content, ...tags?.length ? { tags } : {}, ...mesh ? { mesh } : {} }
4141
+ });
4142
+ if (res.status !== 200 || !res.body.id)
4143
+ return null;
4144
+ return { id: res.body.id, mesh: res.body.mesh };
4145
+ } catch {
4146
+ return null;
4147
+ }
4148
+ }
4149
+ async function tryRecallViaDaemon(query, mesh) {
4150
+ if (!await daemonReachable())
4151
+ return null;
4152
+ try {
4153
+ const path = `/v1/memory?q=${encodeURIComponent(query)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
4154
+ const res = await ipc({ path, timeoutMs: 5000 });
4155
+ if (res.status !== 200)
4156
+ return null;
4157
+ return Array.isArray(res.body.matches) ? res.body.matches : [];
4158
+ } catch (err) {
4159
+ const msg = String(err);
4160
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4161
+ return null;
4162
+ return null;
4163
+ }
4164
+ }
4165
+ async function tryForgetViaDaemon(id, mesh) {
4166
+ if (!await daemonReachable())
4167
+ return false;
4168
+ try {
4169
+ const path = `/v1/memory/${encodeURIComponent(id)}${meshQuery(mesh)}`;
4170
+ const res = await ipc({ method: "DELETE", path, timeoutMs: 3000 });
4171
+ return res.status === 200 && res.body.ok === true;
4172
+ } catch {
4173
+ return false;
4174
+ }
4175
+ }
4176
+ async function trySendViaDaemon(args) {
4177
+ if (!await daemonReachable())
4178
+ return null;
4179
+ try {
4180
+ const res = await ipc({
4181
+ method: "POST",
4182
+ path: "/v1/send",
4183
+ timeoutMs: 3000,
4184
+ body: {
4185
+ to: args.to,
4186
+ message: args.message,
4187
+ priority: args.priority,
4188
+ ...args.idempotencyKey ? { client_message_id: args.idempotencyKey } : {},
4189
+ ...args.expectedMesh ? { mesh: args.expectedMesh } : {}
4190
+ }
4191
+ });
4192
+ if (res.status === 202 || res.status === 200) {
4193
+ return {
4194
+ ok: true,
4195
+ messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
4196
+ duplicate: res.body.duplicate,
4197
+ status: res.body.status
4198
+ };
4199
+ }
4200
+ return { ok: false, error: res.body.error ?? `daemon http ${res.status}` };
4201
+ } catch (err) {
4202
+ const msg = String(err);
4203
+ if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
4204
+ return null;
4205
+ return { ok: false, error: msg };
4206
+ }
4207
+ }
4208
+ var init_daemon_route = __esm(() => {
4209
+ init_client3();
4210
+ init_lifecycle();
4211
+ init_policy();
4212
+ init_warnings();
4213
+ });
4214
+
3969
4215
  // src/services/broker/session-hello-sig.ts
3970
4216
  var exports_session_hello_sig = {};
3971
4217
  __export(exports_session_hello_sig, {
@@ -4166,18 +4412,61 @@ async function runLaunchWizard(opts) {
4166
4412
  exitFullScreen();
4167
4413
  return { mesh, role, groups, messageMode, skipPermissions };
4168
4414
  }
4169
- function printBanner(name, meshSlug, role, groups, messageMode) {
4415
+ async function printBrokerWelcome(meshSlug) {
4170
4416
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
4171
4417
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
4172
- const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
4173
- const roleSuffix = role ? ` (${role})` : "";
4174
- const groupTags = groups.length ? " [" + groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
4175
- const rule = "─".repeat(60);
4176
- console.log(bold2(`claudemesh launch`) + dim2(` as ${name}${roleSuffix} on ${meshSlug}${groupTags} [${messageMode}]`));
4177
- console.log(rule);
4178
- if (messageMode === "push") {
4179
- console.log("Peer messages arrive as <channel> reminders in real-time.");
4180
- } else if (messageMode === "inbox") {
4418
+ const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[22m` : s;
4419
+ const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[22m` : s;
4420
+ let brokerState = "unknown";
4421
+ try {
4422
+ const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4423
+ const res = await ipc2({
4424
+ path: "/v1/health",
4425
+ timeoutMs: 1500
4426
+ });
4427
+ if (res.status === 200 && res.body?.brokers) {
4428
+ brokerState = res.body.brokers[meshSlug] ?? "unknown";
4429
+ }
4430
+ } catch {}
4431
+ let peerCount = -1;
4432
+ try {
4433
+ const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
4434
+ const peers = await tryListPeersViaDaemon2() ?? [];
4435
+ peerCount = peers.filter((p) => p.channel !== "claudemesh-daemon").length;
4436
+ } catch {}
4437
+ let unread = -1;
4438
+ try {
4439
+ const { ipc: ipc2 } = await Promise.resolve().then(() => (init_client3(), exports_client));
4440
+ const res = await ipc2({
4441
+ path: "/v1/inbox",
4442
+ timeoutMs: 1500
4443
+ });
4444
+ if (res.status === 200 && Array.isArray(res.body?.messages)) {
4445
+ unread = res.body.messages.length;
4446
+ }
4447
+ } catch {}
4448
+ const dot = brokerState === "open" ? green3("●") : yellow2("●");
4449
+ const parts = [];
4450
+ parts.push(`broker ${brokerState === "open" ? "connected" : brokerState}`);
4451
+ if (peerCount >= 0)
4452
+ parts.push(`${peerCount} peer${peerCount === 1 ? "" : "s"} online`);
4453
+ if (unread >= 0)
4454
+ parts.push(`${unread} unread`);
4455
+ console.log(`${dot} ${parts.join(dim2(" · "))}`);
4456
+ console.log("");
4457
+ }
4458
+ function printBanner(name, meshSlug, role, groups, messageMode) {
4459
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
4460
+ const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
4461
+ const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
4462
+ const roleSuffix = role ? ` (${role})` : "";
4463
+ const groupTags = groups.length ? " [" + groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
4464
+ const rule = "─".repeat(60);
4465
+ console.log(bold2(`claudemesh launch`) + dim2(` — as ${name}${roleSuffix} on ${meshSlug}${groupTags} [${messageMode}]`));
4466
+ console.log(rule);
4467
+ if (messageMode === "push") {
4468
+ console.log("Peer messages arrive as <channel> reminders in real-time.");
4469
+ } else if (messageMode === "inbox") {
4181
4470
  console.log("Peer messages held in inbox. Use check_messages to read.");
4182
4471
  } else {
4183
4472
  console.log("Messages off. Use check_messages to poll manually.");
@@ -4456,6 +4745,7 @@ async function runLaunch(flags, rawArgs) {
4456
4745
  } catch {}
4457
4746
  if (!args.quiet) {
4458
4747
  printBanner(displayName, mesh.slug, role, parsedGroups, messageMode);
4748
+ await printBrokerWelcome(mesh.slug);
4459
4749
  }
4460
4750
  const meshMcpEntries = [];
4461
4751
  if (serviceCatalog.length > 0) {
@@ -7424,402 +7714,156 @@ function parseStaleMs(input) {
7424
7714
  if (unit === "h")
7425
7715
  return val * 3600000;
7426
7716
  return null;
7427
- }
7428
- function buildPayload(kind, target, opts) {
7429
- if (opts.all)
7430
- return { type: kind, all: true };
7431
- if (opts.stale) {
7432
- const ms = parseStaleMs(opts.stale);
7433
- if (!ms)
7434
- return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
7435
- return { type: kind, stale: ms };
7436
- }
7437
- if (target)
7438
- return { type: kind, target };
7439
- return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
7440
- }
7441
- async function runDisconnect(target, opts = {}) {
7442
- const config = readConfig();
7443
- const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7444
- if (!meshSlug) {
7445
- render.err("No mesh joined.");
7446
- return EXIT.NOT_FOUND;
7447
- }
7448
- const built = buildPayload("disconnect", target, opts);
7449
- if ("error" in built) {
7450
- render.err(String(built.error));
7451
- return EXIT.INVALID_ARGS;
7452
- }
7453
- return await withMesh({ meshSlug }, async (client) => {
7454
- const result = await client.sendAndWait(built);
7455
- const peers = result?.affected ?? result?.kicked ?? [];
7456
- if (peers.length === 0)
7457
- render.info("No peers matched.");
7458
- else {
7459
- render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
7460
- render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
7461
- }
7462
- return EXIT.SUCCESS;
7463
- });
7464
- }
7465
- async function runKick(target, opts = {}) {
7466
- const config = readConfig();
7467
- const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7468
- if (!meshSlug) {
7469
- render.err("No mesh joined.");
7470
- return EXIT.NOT_FOUND;
7471
- }
7472
- const built = buildPayload("kick", target, opts);
7473
- if ("error" in built) {
7474
- render.err(String(built.error));
7475
- return EXIT.INVALID_ARGS;
7476
- }
7477
- return await withMesh({ meshSlug }, async (client) => {
7478
- const result = await client.sendAndWait(built);
7479
- const peers = result?.affected ?? result?.kicked ?? [];
7480
- if (peers.length === 0)
7481
- render.info("No peers matched.");
7482
- else {
7483
- render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
7484
- render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
7485
- }
7486
- return EXIT.SUCCESS;
7487
- });
7488
- }
7489
- var init_kick = __esm(() => {
7490
- init_connect();
7491
- init_facade();
7492
- init_render();
7493
- init_exit_codes();
7494
- });
7495
-
7496
- // src/commands/ban.ts
7497
- var exports_ban = {};
7498
- __export(exports_ban, {
7499
- runUnban: () => runUnban,
7500
- runBans: () => runBans,
7501
- runBan: () => runBan
7502
- });
7503
- async function runBan(target, opts = {}) {
7504
- if (!target) {
7505
- render.err("Usage: claudemesh ban <peer-name-or-pubkey>");
7506
- return EXIT.INVALID_ARGS;
7507
- }
7508
- const config = readConfig();
7509
- const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7510
- if (!meshSlug) {
7511
- render.err("No mesh joined.");
7512
- return EXIT.NOT_FOUND;
7513
- }
7514
- return await withMesh({ meshSlug }, async (client) => {
7515
- const result = await client.sendAndWait({ type: "ban", target });
7516
- if (result?.banned) {
7517
- render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
7518
- render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
7519
- } else {
7520
- render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
7521
- }
7522
- return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
7523
- });
7524
- }
7525
- async function runUnban(target, opts = {}) {
7526
- if (!target) {
7527
- render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
7528
- return EXIT.INVALID_ARGS;
7529
- }
7530
- const config = readConfig();
7531
- const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7532
- if (!meshSlug) {
7533
- render.err("No mesh joined.");
7534
- return EXIT.NOT_FOUND;
7535
- }
7536
- return await withMesh({ meshSlug }, async (client) => {
7537
- const result = await client.sendAndWait({ type: "unban", target });
7538
- if (result?.unbanned) {
7539
- render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
7540
- } else {
7541
- render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
7542
- }
7543
- return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
7544
- });
7545
- }
7546
- async function runBans(opts = {}) {
7547
- const config = readConfig();
7548
- const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7549
- if (!meshSlug) {
7550
- render.err("No mesh joined.");
7551
- return EXIT.NOT_FOUND;
7552
- }
7553
- return await withMesh({ meshSlug }, async (client) => {
7554
- const result = await client.sendAndWait({ type: "list_bans" });
7555
- const bans = result?.bans ?? [];
7556
- if (opts.json) {
7557
- process.stdout.write(JSON.stringify(bans, null, 2) + `
7558
- `);
7559
- return EXIT.SUCCESS;
7560
- }
7561
- if (bans.length === 0) {
7562
- render.info("No banned members.");
7563
- return EXIT.SUCCESS;
7564
- }
7565
- render.section(`banned members on ${meshSlug}`);
7566
- for (const b of bans) {
7567
- render.kv([[b.name, `${b.pubkey.slice(0, 16)}… · banned ${new Date(b.revokedAt).toLocaleDateString()}`]]);
7568
- }
7569
- return EXIT.SUCCESS;
7570
- });
7571
- }
7572
- var init_ban = __esm(() => {
7573
- init_connect();
7574
- init_facade();
7575
- init_render();
7576
- init_exit_codes();
7577
- });
7578
-
7579
- // src/ui/warnings.ts
7580
- function warnDaemonState(res, opts = {}) {
7581
- if (alreadyWarned)
7582
- return false;
7583
- if (opts.quiet || opts.json)
7584
- return false;
7585
- if (res.state === "up")
7586
- return false;
7587
- if (getDaemonPolicy().mode === "strict" && res.state !== "started")
7588
- return false;
7589
- alreadyWarned = true;
7590
- const tag = (label) => `[claudemesh] ${label}`;
7591
- const hint = (s) => dim(s);
7592
- switch (res.state) {
7593
- case "started":
7594
- process.stderr.write(`${tag("info")} daemon restarted automatically ${hint(`(took ${res.durationMs}ms)`)}
7595
- `);
7596
- return true;
7597
- case "down":
7598
- process.stderr.write(`${tag("info")} daemon not running — using cold path ${hint("(slower; run `claudemesh daemon up` for warm path)")}
7599
- `);
7600
- return true;
7601
- case "spawn-suppressed":
7602
- process.stderr.write(`${tag("warn")} ${res.reason ?? "daemon failed to start recently"} — using cold path ${hint("(run `claudemesh doctor`)")}
7603
- `);
7604
- return true;
7605
- case "spawn-failed":
7606
- process.stderr.write(`${tag("warn")} daemon spawn failed${res.reason ? `: ${res.reason}` : ""} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
7607
- `);
7608
- return true;
7609
- case "service-not-ready":
7610
- process.stderr.write(`${tag("warn")} ${res.reason ?? "service-managed daemon not responding"} — using cold path ${hint("(check ~/.claudemesh/daemon/daemon.log)")}
7611
- `);
7612
- return true;
7613
- }
7614
- return false;
7615
- }
7616
- var alreadyWarned = false;
7617
- var init_warnings = __esm(() => {
7618
- init_policy();
7619
- init_styles();
7620
- });
7621
-
7622
- // src/services/bridge/daemon-route.ts
7623
- var exports_daemon_route = {};
7624
- __export(exports_daemon_route, {
7625
- trySetStateViaDaemon: () => trySetStateViaDaemon,
7626
- trySendViaDaemon: () => trySendViaDaemon,
7627
- tryRememberViaDaemon: () => tryRememberViaDaemon,
7628
- tryRecallViaDaemon: () => tryRecallViaDaemon,
7629
- tryListStateViaDaemon: () => tryListStateViaDaemon,
7630
- tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
7631
- tryListPeersViaDaemon: () => tryListPeersViaDaemon,
7632
- tryGetStateViaDaemon: () => tryGetStateViaDaemon,
7633
- tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
7634
- tryForgetViaDaemon: () => tryForgetViaDaemon
7635
- });
7636
- function meshQuery(mesh) {
7637
- return mesh ? `?mesh=${encodeURIComponent(mesh)}` : "";
7638
- }
7639
- async function daemonReachable() {
7640
- const policy2 = getDaemonPolicy();
7641
- if (policy2.mode === "no-daemon")
7642
- return false;
7643
- const res = await ensureDaemonReady({ noAutoSpawn: false });
7644
- warnDaemonState(res, {});
7645
- return res.state === "up" || res.state === "started";
7646
- }
7647
- async function tryListPeersViaDaemon(mesh) {
7648
- if (!await daemonReachable())
7649
- return null;
7650
- try {
7651
- const res = await ipc({ path: `/v1/peers${meshQuery(mesh)}`, timeoutMs: 3000 });
7652
- if (res.status !== 200)
7653
- return null;
7654
- return Array.isArray(res.body.peers) ? res.body.peers : [];
7655
- } catch (err) {
7656
- const msg = String(err);
7657
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7658
- return null;
7659
- return null;
7660
- }
7661
- }
7662
- async function tryListSkillsViaDaemon(mesh) {
7663
- if (!await daemonReachable())
7664
- return null;
7665
- try {
7666
- const res = await ipc({ path: `/v1/skills${meshQuery(mesh)}`, timeoutMs: 3000 });
7667
- if (res.status !== 200)
7668
- return null;
7669
- return Array.isArray(res.body.skills) ? res.body.skills : [];
7670
- } catch (err) {
7671
- const msg = String(err);
7672
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7673
- return null;
7674
- return null;
7675
- }
7676
- }
7677
- async function tryGetSkillViaDaemon(name, mesh) {
7678
- if (!await daemonReachable())
7679
- return null;
7680
- try {
7681
- const res = await ipc({
7682
- path: `/v1/skills/${encodeURIComponent(name)}${meshQuery(mesh)}`,
7683
- timeoutMs: 3000
7684
- });
7685
- if (res.status === 404)
7686
- return null;
7687
- if (res.status !== 200)
7688
- return null;
7689
- return res.body.skill ?? null;
7690
- } catch {
7691
- return null;
7692
- }
7693
- }
7694
- async function tryGetStateViaDaemon(key, mesh) {
7695
- if (!await daemonReachable())
7696
- return null;
7697
- try {
7698
- const path = `/v1/state?key=${encodeURIComponent(key)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
7699
- const res = await ipc({ path, timeoutMs: 3000 });
7700
- if (res.status === 404)
7701
- return;
7702
- if (res.status !== 200)
7703
- return null;
7704
- return res.body.state ?? undefined;
7705
- } catch (err) {
7706
- const msg = String(err);
7707
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7708
- return null;
7709
- return null;
7710
- }
7711
- }
7712
- async function tryListStateViaDaemon(mesh) {
7713
- if (!await daemonReachable())
7714
- return null;
7715
- try {
7716
- const res = await ipc({ path: `/v1/state${meshQuery(mesh)}`, timeoutMs: 3000 });
7717
- if (res.status !== 200)
7718
- return null;
7719
- return Array.isArray(res.body.entries) ? res.body.entries : [];
7720
- } catch (err) {
7721
- const msg = String(err);
7722
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7723
- return null;
7724
- return null;
7725
- }
7726
- }
7727
- async function trySetStateViaDaemon(key, value, mesh) {
7728
- if (!await daemonReachable())
7729
- return false;
7730
- try {
7731
- const res = await ipc({
7732
- method: "POST",
7733
- path: "/v1/state",
7734
- timeoutMs: 3000,
7735
- body: { key, value, ...mesh ? { mesh } : {} }
7736
- });
7737
- return res.status === 200 && res.body.ok === true;
7738
- } catch {
7739
- return false;
7717
+ }
7718
+ function buildPayload(kind, target, opts) {
7719
+ if (opts.all)
7720
+ return { type: kind, all: true };
7721
+ if (opts.stale) {
7722
+ const ms = parseStaleMs(opts.stale);
7723
+ if (!ms)
7724
+ return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
7725
+ return { type: kind, stale: ms };
7740
7726
  }
7727
+ if (target)
7728
+ return { type: kind, target };
7729
+ return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
7741
7730
  }
7742
- async function tryRememberViaDaemon(content, tags, mesh) {
7743
- if (!await daemonReachable())
7744
- return null;
7745
- try {
7746
- const res = await ipc({
7747
- method: "POST",
7748
- path: "/v1/memory",
7749
- timeoutMs: 5000,
7750
- body: { content, ...tags?.length ? { tags } : {}, ...mesh ? { mesh } : {} }
7751
- });
7752
- if (res.status !== 200 || !res.body.id)
7753
- return null;
7754
- return { id: res.body.id, mesh: res.body.mesh };
7755
- } catch {
7756
- return null;
7731
+ async function runDisconnect(target, opts = {}) {
7732
+ const config = readConfig();
7733
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7734
+ if (!meshSlug) {
7735
+ render.err("No mesh joined.");
7736
+ return EXIT.NOT_FOUND;
7737
+ }
7738
+ const built = buildPayload("disconnect", target, opts);
7739
+ if ("error" in built) {
7740
+ render.err(String(built.error));
7741
+ return EXIT.INVALID_ARGS;
7757
7742
  }
7743
+ return await withMesh({ meshSlug }, async (client) => {
7744
+ const result = await client.sendAndWait(built);
7745
+ const peers = result?.affected ?? result?.kicked ?? [];
7746
+ if (peers.length === 0)
7747
+ render.info("No peers matched.");
7748
+ else {
7749
+ render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
7750
+ render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
7751
+ }
7752
+ return EXIT.SUCCESS;
7753
+ });
7758
7754
  }
7759
- async function tryRecallViaDaemon(query, mesh) {
7760
- if (!await daemonReachable())
7761
- return null;
7762
- try {
7763
- const path = `/v1/memory?q=${encodeURIComponent(query)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
7764
- const res = await ipc({ path, timeoutMs: 5000 });
7765
- if (res.status !== 200)
7766
- return null;
7767
- return Array.isArray(res.body.matches) ? res.body.matches : [];
7768
- } catch (err) {
7769
- const msg = String(err);
7770
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7771
- return null;
7772
- return null;
7755
+ async function runKick(target, opts = {}) {
7756
+ const config = readConfig();
7757
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7758
+ if (!meshSlug) {
7759
+ render.err("No mesh joined.");
7760
+ return EXIT.NOT_FOUND;
7761
+ }
7762
+ const built = buildPayload("kick", target, opts);
7763
+ if ("error" in built) {
7764
+ render.err(String(built.error));
7765
+ return EXIT.INVALID_ARGS;
7773
7766
  }
7767
+ return await withMesh({ meshSlug }, async (client) => {
7768
+ const result = await client.sendAndWait(built);
7769
+ const peers = result?.affected ?? result?.kicked ?? [];
7770
+ if (peers.length === 0)
7771
+ render.info("No peers matched.");
7772
+ else {
7773
+ render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
7774
+ render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
7775
+ }
7776
+ return EXIT.SUCCESS;
7777
+ });
7774
7778
  }
7775
- async function tryForgetViaDaemon(id, mesh) {
7776
- if (!await daemonReachable())
7777
- return false;
7778
- try {
7779
- const path = `/v1/memory/${encodeURIComponent(id)}${meshQuery(mesh)}`;
7780
- const res = await ipc({ method: "DELETE", path, timeoutMs: 3000 });
7781
- return res.status === 200 && res.body.ok === true;
7782
- } catch {
7783
- return false;
7779
+ var init_kick = __esm(() => {
7780
+ init_connect();
7781
+ init_facade();
7782
+ init_render();
7783
+ init_exit_codes();
7784
+ });
7785
+
7786
+ // src/commands/ban.ts
7787
+ var exports_ban = {};
7788
+ __export(exports_ban, {
7789
+ runUnban: () => runUnban,
7790
+ runBans: () => runBans,
7791
+ runBan: () => runBan
7792
+ });
7793
+ async function runBan(target, opts = {}) {
7794
+ if (!target) {
7795
+ render.err("Usage: claudemesh ban <peer-name-or-pubkey>");
7796
+ return EXIT.INVALID_ARGS;
7784
7797
  }
7798
+ const config = readConfig();
7799
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7800
+ if (!meshSlug) {
7801
+ render.err("No mesh joined.");
7802
+ return EXIT.NOT_FOUND;
7803
+ }
7804
+ return await withMesh({ meshSlug }, async (client) => {
7805
+ const result = await client.sendAndWait({ type: "ban", target });
7806
+ if (result?.banned) {
7807
+ render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
7808
+ render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
7809
+ } else {
7810
+ render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
7811
+ }
7812
+ return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
7813
+ });
7785
7814
  }
7786
- async function trySendViaDaemon(args) {
7787
- if (!await daemonReachable())
7788
- return null;
7789
- try {
7790
- const res = await ipc({
7791
- method: "POST",
7792
- path: "/v1/send",
7793
- timeoutMs: 3000,
7794
- body: {
7795
- to: args.to,
7796
- message: args.message,
7797
- priority: args.priority,
7798
- ...args.idempotencyKey ? { client_message_id: args.idempotencyKey } : {},
7799
- ...args.expectedMesh ? { mesh: args.expectedMesh } : {}
7800
- }
7801
- });
7802
- if (res.status === 202 || res.status === 200) {
7803
- return {
7804
- ok: true,
7805
- messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
7806
- duplicate: res.body.duplicate,
7807
- status: res.body.status
7808
- };
7815
+ async function runUnban(target, opts = {}) {
7816
+ if (!target) {
7817
+ render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
7818
+ return EXIT.INVALID_ARGS;
7819
+ }
7820
+ const config = readConfig();
7821
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7822
+ if (!meshSlug) {
7823
+ render.err("No mesh joined.");
7824
+ return EXIT.NOT_FOUND;
7825
+ }
7826
+ return await withMesh({ meshSlug }, async (client) => {
7827
+ const result = await client.sendAndWait({ type: "unban", target });
7828
+ if (result?.unbanned) {
7829
+ render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
7830
+ } else {
7831
+ render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
7809
7832
  }
7810
- return { ok: false, error: res.body.error ?? `daemon http ${res.status}` };
7811
- } catch (err) {
7812
- const msg = String(err);
7813
- if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
7814
- return null;
7815
- return { ok: false, error: msg };
7833
+ return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
7834
+ });
7835
+ }
7836
+ async function runBans(opts = {}) {
7837
+ const config = readConfig();
7838
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
7839
+ if (!meshSlug) {
7840
+ render.err("No mesh joined.");
7841
+ return EXIT.NOT_FOUND;
7816
7842
  }
7843
+ return await withMesh({ meshSlug }, async (client) => {
7844
+ const result = await client.sendAndWait({ type: "list_bans" });
7845
+ const bans = result?.bans ?? [];
7846
+ if (opts.json) {
7847
+ process.stdout.write(JSON.stringify(bans, null, 2) + `
7848
+ `);
7849
+ return EXIT.SUCCESS;
7850
+ }
7851
+ if (bans.length === 0) {
7852
+ render.info("No banned members.");
7853
+ return EXIT.SUCCESS;
7854
+ }
7855
+ render.section(`banned members on ${meshSlug}`);
7856
+ for (const b of bans) {
7857
+ render.kv([[b.name, `${b.pubkey.slice(0, 16)}… · banned ${new Date(b.revokedAt).toLocaleDateString()}`]]);
7858
+ }
7859
+ return EXIT.SUCCESS;
7860
+ });
7817
7861
  }
7818
- var init_daemon_route = __esm(() => {
7819
- init_client3();
7820
- init_lifecycle();
7821
- init_policy();
7822
- init_warnings();
7862
+ var init_ban = __esm(() => {
7863
+ init_connect();
7864
+ init_facade();
7865
+ init_render();
7866
+ init_exit_codes();
7823
7867
  });
7824
7868
 
7825
7869
  // src/services/session/resolve.ts
@@ -7878,18 +7922,26 @@ async function listPeersForMesh(slug) {
7878
7922
  const config = readConfig();
7879
7923
  const joined = config.meshes.find((m) => m.slug === slug);
7880
7924
  const selfMemberPubkey = joined?.pubkey ?? null;
7925
+ let selfSessionPubkey = null;
7926
+ try {
7927
+ const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
7928
+ const sess = await getSessionInfo2();
7929
+ if (sess && sess.mesh === slug && sess.presence?.sessionPubkey) {
7930
+ selfSessionPubkey = sess.presence.sessionPubkey;
7931
+ }
7932
+ } catch {}
7881
7933
  try {
7882
7934
  const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
7883
7935
  const dr = await tryListPeersViaDaemon2();
7884
7936
  if (dr !== null) {
7885
- return dr.map((p) => annotateSelf(p, selfMemberPubkey, null));
7937
+ return dr.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey));
7886
7938
  }
7887
7939
  } catch {}
7888
7940
  let result = [];
7889
7941
  await withMesh({ meshSlug: slug }, async (client) => {
7890
7942
  const all = await client.listPeers();
7891
- const selfSessionPubkey = client.getSessionPubkey();
7892
- result = all.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey));
7943
+ const selfSessionPubkey2 = client.getSessionPubkey();
7944
+ result = all.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey2));
7893
7945
  });
7894
7946
  return result;
7895
7947
  }
@@ -7925,12 +7977,19 @@ async function runPeers(flags) {
7925
7977
  allJson.push({ mesh: slug, peers: projected });
7926
7978
  continue;
7927
7979
  }
7928
- render.section(`peers on ${slug} (${peers.length})`);
7929
- if (peers.length === 0) {
7980
+ const visible = flags.all ? peers : peers.filter((p) => p.channel !== "claudemesh-daemon");
7981
+ const sorted = visible.slice().sort((a, b) => {
7982
+ const score = (p) => p.isThisSession ? 0 : p.isSelf ? 1 : 2;
7983
+ return score(a) - score(b);
7984
+ });
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})`;
7987
+ render.section(header);
7988
+ if (sorted.length === 0) {
7930
7989
  render.info(dim(" (no peers connected)"));
7931
7990
  continue;
7932
7991
  }
7933
- for (const p of peers) {
7992
+ for (const p of sorted) {
7934
7993
  const statusDot = p.status === "working" ? yellow("●") : green("●");
7935
7994
  const name = bold(p.displayName);
7936
7995
  const meta = [];
@@ -7943,6 +8002,7 @@ async function runPeers(flags) {
7943
8002
  const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
7944
8003
  const summary = p.summary ? dim(` — ${p.summary}`) : "";
7945
8004
  const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
8005
+ const sidTag = p.sessionId ? dim(` · sid:${p.sessionId.slice(0, 8)}`) : "";
7946
8006
  const selfTag = p.isThisSession ? dim(" ") + yellow("(this session)") : p.isSelf ? dim(" ") + yellow("(your other session)") : "";
7947
8007
  const inlineTags = [];
7948
8008
  const peerRole = p.profile?.role?.trim();
@@ -7952,7 +8012,7 @@ async function runPeers(flags) {
7952
8012
  inlineTags.push(...p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`));
7953
8013
  }
7954
8014
  const tagsStr = inlineTags.length ? " [" + inlineTags.join(", ") + "]" : "";
7955
- render.info(`${statusDot} ${name}${selfTag}${tagsStr}${metaStr}${pubkeyTag}${summary}`);
8015
+ render.info(`${statusDot} ${name}${selfTag}${tagsStr}${metaStr}${pubkeyTag}${sidTag}${summary}`);
7956
8016
  if (p.cwd)
7957
8017
  render.info(dim(` cwd: ${p.cwd}`));
7958
8018
  if (!peerRole && p.groups.length === 0) {
@@ -8023,13 +8083,73 @@ async function runSend(flags, to, message) {
8023
8083
  to = matches2[0].pubkey ?? to;
8024
8084
  } catch {}
8025
8085
  }
8026
- if (!flags.self && meshSlug) {
8086
+ if (meshSlug) {
8027
8087
  const joined = config.meshes.find((m) => m.slug === meshSlug);
8028
- if (joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase()) {
8088
+ const isOwnMemberKey = joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase();
8089
+ if (isOwnMemberKey && !flags.self) {
8029
8090
  render.err(`Target "${to.slice(0, 16)}…" is your own member pubkey on mesh "${meshSlug}".`);
8030
8091
  render.hint("Pass --self to message a sibling session of your own member, or pick a different peer's pubkey.");
8031
8092
  process.exit(1);
8032
8093
  }
8094
+ if (isOwnMemberKey && flags.self) {
8095
+ try {
8096
+ const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
8097
+ const { getSessionInfo: getSessionInfo2 } = await Promise.resolve().then(() => (init_resolve(), exports_resolve));
8098
+ const peers = await tryListPeersViaDaemon2() ?? [];
8099
+ const session = await getSessionInfo2();
8100
+ const ownSessionPk = session?.presence?.sessionPubkey?.toLowerCase();
8101
+ const siblings = peers.filter((p) => {
8102
+ const r = p;
8103
+ if (!r.pubkey)
8104
+ return false;
8105
+ if (ownSessionPk && r.pubkey.toLowerCase() === ownSessionPk)
8106
+ return false;
8107
+ if (r.channel === "claudemesh-daemon")
8108
+ return false;
8109
+ return r.memberPubkey?.toLowerCase() === to.toLowerCase();
8110
+ });
8111
+ if (siblings.length === 0) {
8112
+ render.err(`--self fan-out: no other sibling sessions of your member online.`);
8113
+ process.exit(1);
8114
+ }
8115
+ const results = [];
8116
+ for (const peer of siblings) {
8117
+ const pk = peer.pubkey;
8118
+ const dr = await trySendViaDaemon({ to: pk, message, priority, expectedMesh: meshSlug ?? undefined });
8119
+ if (dr === null) {
8120
+ results.push({ pubkey: pk, ok: false, error: "daemon path unavailable" });
8121
+ continue;
8122
+ }
8123
+ if (dr.ok) {
8124
+ results.push({
8125
+ pubkey: pk,
8126
+ ok: true,
8127
+ ...dr.messageId ? { messageId: dr.messageId } : {}
8128
+ });
8129
+ } else {
8130
+ results.push({ pubkey: pk, ok: false, error: dr.error });
8131
+ }
8132
+ }
8133
+ const okCount = results.filter((r) => r.ok).length;
8134
+ if (flags.json) {
8135
+ console.log(JSON.stringify({ ok: okCount > 0, fanout: results, via: "daemon" }));
8136
+ } else if (okCount === results.length) {
8137
+ render.ok(`fanned out to ${okCount} sibling session${okCount === 1 ? "" : "s"} (daemon)`);
8138
+ for (const r of results)
8139
+ render.info(dim(` → ${r.pubkey.slice(0, 16)}… ${r.messageId ? dim(r.messageId.slice(0, 8)) : ""}`));
8140
+ } else {
8141
+ render.warn(`fanned out: ${okCount}/${results.length} delivered`);
8142
+ for (const r of results) {
8143
+ const tag = r.ok ? "✔" : "✘";
8144
+ render.info(` ${tag} ${r.pubkey.slice(0, 16)}… ${r.error ? dim(`— ${r.error}`) : ""}`);
8145
+ }
8146
+ }
8147
+ return;
8148
+ } catch (e) {
8149
+ render.err(`--self fan-out failed: ${e instanceof Error ? e.message : String(e)}`);
8150
+ process.exit(1);
8151
+ }
8152
+ }
8033
8153
  }
8034
8154
  {
8035
8155
  const dr = await trySendViaDaemon({ to, message, priority, expectedMesh: meshSlug ?? undefined });
@@ -8879,15 +8999,30 @@ __export(exports_whoami, {
8879
8999
  });
8880
9000
  async function whoami(opts) {
8881
9001
  const result = await whoAmI();
9002
+ const session = await getSessionInfo();
8882
9003
  if (opts.json) {
8883
- console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
8884
- return result.signed_in || result.local ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
9004
+ console.log(JSON.stringify({ schema_version: "1.0", ...result, session }, null, 2));
9005
+ return result.signed_in || result.local || session ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
8885
9006
  }
8886
- if (!result.signed_in && !result.local) {
9007
+ if (!result.signed_in && !result.local && !session) {
8887
9008
  render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
8888
9009
  return EXIT.AUTH_FAILED;
8889
9010
  }
8890
9011
  render.section("whoami");
9012
+ if (session) {
9013
+ const sessionPk = session.presence?.sessionPubkey;
9014
+ const groups = (session.groups ?? []).join(", ") || dim("(none)");
9015
+ render.kv([
9016
+ ["this session", `${yellow(session.displayName)} on ${bold(session.mesh)}`],
9017
+ ["session id", dim(session.sessionId)],
9018
+ ...sessionPk ? [["session pubkey", dim(`${sessionPk.slice(0, 16)}… (full: ${sessionPk})`)]] : [],
9019
+ ...session.role ? [["role", session.role]] : [],
9020
+ ["groups", groups],
9021
+ ...session.cwd ? [["cwd", dim(session.cwd)]] : [],
9022
+ ["pid", String(session.pid)]
9023
+ ]);
9024
+ render.blank();
9025
+ }
8891
9026
  if (result.signed_in) {
8892
9027
  render.kv([
8893
9028
  ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
@@ -8913,6 +9048,7 @@ async function whoami(opts) {
8913
9048
  }
8914
9049
  var init_whoami = __esm(() => {
8915
9050
  init_facade6();
9051
+ init_resolve();
8916
9052
  init_render();
8917
9053
  init_styles();
8918
9054
  init_exit_codes();
@@ -10947,6 +11083,10 @@ class SessionBrokerClient {
10947
11083
  }
10948
11084
  return;
10949
11085
  }
11086
+ if (msg.type === "push" || msg.type === "inbound") {
11087
+ this.opts.onPush?.(msg);
11088
+ return;
11089
+ }
10950
11090
  });
10951
11091
  ws.on("close", (code, reason) => {
10952
11092
  if (this.helloTimer) {
@@ -11509,6 +11649,7 @@ async function runDaemon(opts = {}) {
11509
11649
  sessionBrokers.delete(info.token);
11510
11650
  prior.close().catch(() => {});
11511
11651
  }
11652
+ const sessionSecretKeyHex = info.presence.sessionSecretKey;
11512
11653
  const client = new SessionBrokerClient({
11513
11654
  mesh: meshConfig,
11514
11655
  sessionPubkey: info.presence.sessionPubkey,
@@ -11518,7 +11659,16 @@ async function runDaemon(opts = {}) {
11518
11659
  displayName: info.displayName,
11519
11660
  ...info.role ? { role: info.role } : {},
11520
11661
  ...info.cwd ? { cwd: info.cwd } : {},
11521
- pid: info.pid
11662
+ pid: info.pid,
11663
+ onPush: (m) => {
11664
+ handleBrokerPush(m, {
11665
+ db: inboxDb,
11666
+ bus,
11667
+ meshSlug: meshConfig.slug,
11668
+ recipientSecretKeyHex: meshConfig.secretKey,
11669
+ sessionSecretKeyHex
11670
+ });
11671
+ }
11522
11672
  });
11523
11673
  sessionBrokers.set(info.token, client);
11524
11674
  client.connect().catch((err) => process.stderr.write(JSON.stringify({
@@ -18339,6 +18489,28 @@ var init_seed_test_mesh = __esm(() => {
18339
18489
  });
18340
18490
 
18341
18491
  // src/cli/argv.ts
18492
+ var BOOLEAN_FLAGS = new Set([
18493
+ "self",
18494
+ "json",
18495
+ "all",
18496
+ "yes",
18497
+ "y",
18498
+ "help",
18499
+ "h",
18500
+ "version",
18501
+ "v",
18502
+ "quiet",
18503
+ "strict",
18504
+ "continue",
18505
+ "no-daemon",
18506
+ "no-color",
18507
+ "debug",
18508
+ "allow-ci-persistent",
18509
+ "force",
18510
+ "dry-run",
18511
+ "verbose",
18512
+ "skip-service"
18513
+ ]);
18342
18514
  function parseArgv(argv) {
18343
18515
  const args = argv.slice(2);
18344
18516
  const flags = {};
@@ -18346,18 +18518,32 @@ function parseArgv(argv) {
18346
18518
  let command = "";
18347
18519
  for (let i = 0;i < args.length; i++) {
18348
18520
  const arg = args[i];
18521
+ if (arg.startsWith("--") && arg.includes("=")) {
18522
+ const eq = arg.indexOf("=");
18523
+ const key = arg.slice(2, eq);
18524
+ flags[key] = arg.slice(eq + 1);
18525
+ continue;
18526
+ }
18349
18527
  if (arg.startsWith("--")) {
18350
18528
  const key = arg.slice(2);
18529
+ if (BOOLEAN_FLAGS.has(key)) {
18530
+ flags[key] = true;
18531
+ continue;
18532
+ }
18351
18533
  const next = args[i + 1];
18352
- if (next && !next.startsWith("-")) {
18534
+ if (next !== undefined && !next.startsWith("-")) {
18353
18535
  flags[key] = next;
18354
18536
  i++;
18355
18537
  } else
18356
18538
  flags[key] = true;
18357
18539
  } else if (arg.startsWith("-") && arg.length === 2) {
18358
18540
  const key = arg.slice(1);
18541
+ if (BOOLEAN_FLAGS.has(key)) {
18542
+ flags[key] = true;
18543
+ continue;
18544
+ }
18359
18545
  const next = args[i + 1];
18360
- if (next && !next.startsWith("-")) {
18546
+ if (next !== undefined && !next.startsWith("-")) {
18361
18547
  flags[key] = next;
18362
18548
  i++;
18363
18549
  } else
@@ -18931,6 +19117,10 @@ Peer (resource form, recommended)
18931
19117
 
18932
19118
  Message (resource form)
18933
19119
  claudemesh message send <to> <m> send a message (alias: send)
19120
+ flags: [--priority now|next|low] [--mesh <slug>]
19121
+ [--self] (allow targeting your own member/session pubkey;
19122
+ fans out to every sibling session of your member)
19123
+ [--json] (machine-readable result)
18934
19124
  claudemesh message inbox drain pending (alias: inbox)
18935
19125
  claudemesh message status <id> delivery status (alias: msg-status)
18936
19126
 
@@ -19279,7 +19469,7 @@ async function main() {
19279
19469
  }
19280
19470
  case "peers": {
19281
19471
  const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
19282
- await runPeers2({ mesh: flags.mesh, json: flags.json });
19472
+ await runPeers2({ mesh: flags.mesh, json: flags.json, all: !!flags.all });
19283
19473
  break;
19284
19474
  }
19285
19475
  case "send": {
@@ -19508,7 +19698,7 @@ async function main() {
19508
19698
  }
19509
19699
  case "peer": {
19510
19700
  const sub = positionals[0];
19511
- const f = { mesh: flags.mesh, json: flags.json };
19701
+ const f = { mesh: flags.mesh, json: flags.json, all: !!flags.all };
19512
19702
  const id = positionals[1] ?? "";
19513
19703
  if (sub === "list") {
19514
19704
  const { runPeers: runPeers2 } = await Promise.resolve().then(() => (init_peers(), exports_peers));
@@ -19541,7 +19731,7 @@ async function main() {
19541
19731
  const sub = positionals[0];
19542
19732
  if (sub === "send") {
19543
19733
  const { runSend: runSend2 } = await Promise.resolve().then(() => (init_send(), exports_send));
19544
- await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json }, positionals[1] ?? "", positionals.slice(2).join(" "));
19734
+ await runSend2({ mesh: flags.mesh, priority: flags.priority, json: !!flags.json, self: !!flags.self }, positionals[1] ?? "", positionals.slice(2).join(" "));
19545
19735
  } else if (sub === "inbox") {
19546
19736
  const { runInbox: runInbox2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
19547
19737
  await runInbox2({ json: !!flags.json });
@@ -20097,4 +20287,4 @@ main().catch((err) => {
20097
20287
  process.exit(EXIT.INTERNAL_ERROR);
20098
20288
  });
20099
20289
 
20100
- //# debugId=F22F2E19502D800C64756E2164756E21
20290
+ //# debugId=FAFB4F8D5ECBBBB864756E2164756E21