claudemesh-cli 1.0.0-alpha.42 → 1.0.0-alpha.43

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.
@@ -88,7 +88,7 @@ __export(exports_urls, {
88
88
  VERSION: () => VERSION,
89
89
  URLS: () => URLS
90
90
  });
91
- var URLS, VERSION = "1.0.0-alpha.42", env;
91
+ var URLS, VERSION = "1.0.0-alpha.43", env;
92
92
  var init_urls = __esm(() => {
93
93
  URLS = {
94
94
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -1150,6 +1150,7 @@ class BrokerClient {
1150
1150
  return this._serviceCatalog;
1151
1151
  }
1152
1152
  closed = false;
1153
+ terminalClose = null;
1153
1154
  reconnectAttempt = 0;
1154
1155
  helloTimer = null;
1155
1156
  reconnectTimer = null;
@@ -1277,12 +1278,22 @@ class BrokerClient {
1277
1278
  }
1278
1279
  this.handleServerMessage(msg);
1279
1280
  };
1280
- const onClose = () => {
1281
+ const onClose = (code, reasonBuf) => {
1281
1282
  if (this.helloTimer)
1282
1283
  clearTimeout(this.helloTimer);
1283
1284
  this.helloTimer = null;
1284
1285
  if (this.ws === ws)
1285
1286
  this.ws = null;
1287
+ const reason = reasonBuf?.toString("utf-8") ?? "";
1288
+ if (code === 4001 || code === 4002) {
1289
+ this.closed = true;
1290
+ this.setConnStatus("closed");
1291
+ this.terminalClose = { code, reason };
1292
+ if (this._status !== "open") {
1293
+ reject(new Error(`ws terminal close ${code}: ${reason || "session ended"}`));
1294
+ }
1295
+ return;
1296
+ }
1286
1297
  if (this._status !== "open" && this._status !== "reconnecting") {
1287
1298
  reject(new Error("ws closed before hello_ack"));
1288
1299
  }
@@ -2965,6 +2976,9 @@ class BrokerClient {
2965
2976
  }
2966
2977
  if (msg.type === "error") {
2967
2978
  this.debug(`broker error: ${msg.code} ${msg.message}`);
2979
+ if (msg.code === "revoked") {
2980
+ this.terminalClose = { code: 4002, reason: String(msg.message ?? "revoked") };
2981
+ }
2968
2982
  const id = msg.id ? String(msg.id) : null;
2969
2983
  let handledByPendingSend = false;
2970
2984
  if (id) {
@@ -6164,6 +6178,25 @@ async function withMesh(opts, fn) {
6164
6178
  await client.connect();
6165
6179
  const result = await fn(client, mesh);
6166
6180
  return result;
6181
+ } catch (e) {
6182
+ if (client.terminalClose) {
6183
+ const { code, reason } = client.terminalClose;
6184
+ if (code === 4002) {
6185
+ console.error(`
6186
+ ✘ ${reason}
6187
+ `);
6188
+ } else if (code === 4001) {
6189
+ console.error(`
6190
+ ✘ Kicked from this mesh. Run \`claudemesh\` to rejoin.
6191
+ `);
6192
+ } else {
6193
+ console.error(`
6194
+ ✘ Broker closed connection: ${reason}
6195
+ `);
6196
+ }
6197
+ process.exit(1);
6198
+ }
6199
+ throw e;
6167
6200
  } finally {
6168
6201
  client.close();
6169
6202
  }
@@ -6176,7 +6209,8 @@ var init_connect = __esm(() => {
6176
6209
  // src/commands/kick.ts
6177
6210
  var exports_kick = {};
6178
6211
  __export(exports_kick, {
6179
- runKick: () => runKick
6212
+ runKick: () => runKick,
6213
+ runDisconnect: () => runDisconnect
6180
6214
  });
6181
6215
  function parseStaleMs(input) {
6182
6216
  const m = input.match(/^(\d+)(s|m|h)$/);
@@ -6192,36 +6226,63 @@ function parseStaleMs(input) {
6192
6226
  return val * 3600000;
6193
6227
  return null;
6194
6228
  }
6195
- async function runKick(target, opts = {}) {
6229
+ function buildPayload(kind, target, opts) {
6230
+ if (opts.all)
6231
+ return { type: kind, all: true };
6232
+ if (opts.stale) {
6233
+ const ms = parseStaleMs(opts.stale);
6234
+ if (!ms)
6235
+ return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
6236
+ return { type: kind, stale: ms };
6237
+ }
6238
+ if (target)
6239
+ return { type: kind, target };
6240
+ return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
6241
+ }
6242
+ async function runDisconnect(target, opts = {}) {
6196
6243
  const config = readConfig();
6197
6244
  const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
6198
6245
  if (!meshSlug) {
6199
6246
  render.err("No mesh joined.");
6200
6247
  return EXIT.NOT_FOUND;
6201
6248
  }
6249
+ const built = buildPayload("disconnect", target, opts);
6250
+ if ("error" in built) {
6251
+ render.err(String(built.error));
6252
+ return EXIT.INVALID_ARGS;
6253
+ }
6202
6254
  return await withMesh({ meshSlug }, async (client) => {
6203
- let payload;
6204
- if (opts.all) {
6205
- payload = { type: "kick", all: true };
6206
- } else if (opts.stale) {
6207
- const ms = parseStaleMs(opts.stale);
6208
- if (!ms) {
6209
- render.err(`Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.`);
6210
- return EXIT.INVALID_ARGS;
6211
- }
6212
- payload = { type: "kick", stale: ms };
6213
- } else if (target) {
6214
- payload = { type: "kick", target };
6215
- } else {
6216
- render.err("Usage: claudemesh kick <peer> | --stale 30m | --all");
6217
- return EXIT.INVALID_ARGS;
6255
+ const result = await client.sendAndWait(built);
6256
+ const peers = result?.affected ?? result?.kicked ?? [];
6257
+ if (peers.length === 0)
6258
+ render.info("No peers matched.");
6259
+ else {
6260
+ render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
6261
+ render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
6218
6262
  }
6219
- const result = await client.sendAndWait(payload);
6220
- const kicked = result?.kicked ?? [];
6221
- if (kicked.length === 0) {
6263
+ return EXIT.SUCCESS;
6264
+ });
6265
+ }
6266
+ async function runKick(target, opts = {}) {
6267
+ const config = readConfig();
6268
+ const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
6269
+ if (!meshSlug) {
6270
+ render.err("No mesh joined.");
6271
+ return EXIT.NOT_FOUND;
6272
+ }
6273
+ const built = buildPayload("kick", target, opts);
6274
+ if ("error" in built) {
6275
+ render.err(String(built.error));
6276
+ return EXIT.INVALID_ARGS;
6277
+ }
6278
+ return await withMesh({ meshSlug }, async (client) => {
6279
+ const result = await client.sendAndWait(built);
6280
+ const peers = result?.affected ?? result?.kicked ?? [];
6281
+ if (peers.length === 0)
6222
6282
  render.info("No peers matched.");
6223
- } else {
6224
- render.ok(`Kicked ${kicked.length} peer(s): ${kicked.join(", ")}`);
6283
+ else {
6284
+ render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
6285
+ render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
6225
6286
  }
6226
6287
  return EXIT.SUCCESS;
6227
6288
  });
@@ -7585,7 +7646,7 @@ async function checkNpmLatest() {
7585
7646
  return { name: "CLI up-to-date", pass: true, detail: `npm unreachable (${res.status}) — skipped` };
7586
7647
  }
7587
7648
  const body = await res.json();
7588
- const latest = body["dist-tags"]?.alpha ?? body["dist-tags"]?.latest;
7649
+ const latest = body["dist-tags"]?.latest ?? body["dist-tags"]?.alpha;
7589
7650
  if (!latest)
7590
7651
  return { name: "CLI up-to-date", pass: true, detail: "no dist-tag — skipped" };
7591
7652
  const up = latest === VERSION;
@@ -7593,7 +7654,7 @@ async function checkNpmLatest() {
7593
7654
  name: "CLI up-to-date",
7594
7655
  pass: up,
7595
7656
  detail: up ? `latest ${latest}` : `installed ${VERSION} → latest ${latest}`,
7596
- fix: up ? undefined : "npm i -g claudemesh-cli@alpha"
7657
+ fix: up ? undefined : "npm i -g claudemesh-cli"
7597
7658
  };
7598
7659
  } catch {
7599
7660
  return { name: "CLI up-to-date", pass: true, detail: "npm check skipped" };
@@ -8701,13 +8762,13 @@ __export(exports_upgrade, {
8701
8762
  import { spawnSync as spawnSync6 } from "node:child_process";
8702
8763
  import { existsSync as existsSync16 } from "node:fs";
8703
8764
  import { dirname as dirname3, join as join8, resolve as resolve2 } from "node:path";
8704
- async function latestAlpha() {
8765
+ async function latestVersion() {
8705
8766
  try {
8706
8767
  const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
8707
8768
  if (!res.ok)
8708
8769
  return null;
8709
8770
  const body = await res.json();
8710
- return body["dist-tags"]?.alpha ?? body["dist-tags"]?.latest ?? null;
8771
+ return body["dist-tags"]?.latest ?? body["dist-tags"]?.alpha ?? null;
8711
8772
  } catch {
8712
8773
  return null;
8713
8774
  }
@@ -8732,7 +8793,7 @@ async function runUpgrade(opts = {}) {
8732
8793
  ["installed", VERSION],
8733
8794
  ["checking", "npm registry…"]
8734
8795
  ]);
8735
- const latest = await latestAlpha();
8796
+ const latest = await latestVersion();
8736
8797
  if (!latest) {
8737
8798
  render.warn("Could not reach npm registry — skipped.");
8738
8799
  return EXIT.SUCCESS;
@@ -8753,7 +8814,7 @@ async function runUpgrade(opts = {}) {
8753
8814
  const args = ["install", "-g"];
8754
8815
  if (prefix)
8755
8816
  args.push("--prefix", prefix);
8756
- args.push("claudemesh-cli@alpha");
8817
+ args.push("claudemesh-cli");
8757
8818
  render.blank();
8758
8819
  render.info(`Updating ${VERSION} → ${latest}…`);
8759
8820
  render.hint(`${npm} ${args.join(" ")}`);
@@ -8761,7 +8822,7 @@ async function runUpgrade(opts = {}) {
8761
8822
  const res = spawnSync6(npm, args, { stdio: "inherit" });
8762
8823
  if (res.status !== 0) {
8763
8824
  render.err(`npm exited with status ${res.status}`);
8764
- render.hint("Try: npm i -g claudemesh-cli@alpha");
8825
+ render.hint("Try: npm i -g claudemesh-cli");
8765
8826
  return EXIT.INTERNAL_ERROR;
8766
8827
  }
8767
8828
  render.blank();
@@ -12412,9 +12473,10 @@ Mesh
12412
12473
  claudemesh delete [slug] delete a mesh (alias: rm)
12413
12474
  claudemesh rename <slug> <name> rename a mesh
12414
12475
  claudemesh share [email] share mesh (invite link / send email)
12415
- claudemesh kick <peer> disconnect a peer (can reconnect)
12416
- claudemesh kick --stale 30m disconnect idle peers (> duration)
12417
- claudemesh kick --all disconnect everyone except you
12476
+ claudemesh disconnect <peer> soft disconnect (peer auto-reconnects)
12477
+ claudemesh kick <peer> end session (peer must manually rejoin)
12478
+ claudemesh kick --stale 30m kick peers idle > duration
12479
+ claudemesh kick --all kick everyone except yourself
12418
12480
  claudemesh ban <peer> kick + permanently revoke (can't rejoin)
12419
12481
  claudemesh unban <peer> lift a ban
12420
12482
  claudemesh bans list banned members
@@ -12556,6 +12618,11 @@ async function main() {
12556
12618
  process.exit(await invite2(positionals[0], { mesh: flags.mesh, json: !!flags.json }));
12557
12619
  break;
12558
12620
  }
12621
+ case "disconnect": {
12622
+ const { runDisconnect: runDisconnect2 } = await Promise.resolve().then(() => (init_kick(), exports_kick));
12623
+ process.exit(await runDisconnect2(positionals[0], { mesh: flags.mesh, stale: flags.stale, all: !!flags.all }));
12624
+ break;
12625
+ }
12559
12626
  case "kick": {
12560
12627
  const { runKick: runKick2 } = await Promise.resolve().then(() => (init_kick(), exports_kick));
12561
12628
  process.exit(await runKick2(positionals[0], { mesh: flags.mesh, stale: flags.stale, all: !!flags.all }));
@@ -12763,4 +12830,4 @@ main().catch((err) => {
12763
12830
  process.exit(EXIT.INTERNAL_ERROR);
12764
12831
  });
12765
12832
 
12766
- //# debugId=67051369A1B376A464756E2164756E21
12833
+ //# debugId=EB94FD910CC928B864756E2164756E21