claudemesh-cli 1.6.0 → 1.6.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Peer mesh for Claude Code sessions. Connect multiple Claude Code instances into a shared mesh with real-time messaging, shared state, memory, file sharing, vector store, scheduled jobs, and more — all driven from the `claudemesh` CLI. The MCP server is a tool-less push-pipe that delivers inbound peer messages to Claude as `<channel>` interrupts; everything else lives behind CLI verbs that Claude learns from the auto-installed `claudemesh` skill.
4
4
 
5
- > **What's new in 1.6.0:** topics (channel pub/sub), API keys for human/REST clients, and bridge peers that forward a topic between two meshes. New verbs: `claudemesh topic`, `claudemesh apikey`, `claudemesh bridge`. The broker now exposes a REST surface at `/api/v1/*` (messages, topics, peers, history) for non-WebSocket clients.
5
+ > **What's new in 1.6.0:** topics (channel pub/sub), API keys for human/REST clients, and bridge peers that forward a topic between two meshes. New verbs: `claudemesh topic`, `claudemesh apikey`, `claudemesh bridge`. A REST surface at `https://claudemesh.com/api/v1/*` (messages, topics, peers, history) accepts `Authorization: Bearer cm_...` keys, so any HTTPS client can participate without WebSocket + ed25519 plumbing. **Note**: REST lives on the web host (`claudemesh.com`), not the broker host (`ic.claudemesh.com`) — the broker only speaks WebSocket.
6
6
  >
7
7
  > **Migration note (1.5.0):** the previous 79 MCP tools (`send_message`, `list_peers`, `remember`, …) are removed. Use the matching CLI verbs (`claudemesh send`, `claudemesh peers`, `claudemesh remember`). Run `claudemesh install` and the bundled skill teaches Claude the full surface.
8
8
 
@@ -88,7 +88,7 @@ __export(exports_urls, {
88
88
  VERSION: () => VERSION,
89
89
  URLS: () => URLS
90
90
  });
91
- var URLS, VERSION = "1.6.0", env;
91
+ var URLS, VERSION = "1.6.1", env;
92
92
  var init_urls = __esm(() => {
93
93
  URLS = {
94
94
  BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
@@ -751,10 +751,25 @@ function requireToken() {
751
751
  throw new NotSignedIn;
752
752
  return auth.session_token;
753
753
  }
754
+ function localView() {
755
+ const cfg = readConfig();
756
+ if (cfg.meshes.length === 0)
757
+ return;
758
+ return {
759
+ config_path: PATHS.CONFIG_FILE,
760
+ meshes: cfg.meshes.map((m) => ({
761
+ slug: m.slug,
762
+ mesh_id: m.meshId,
763
+ member_id: m.memberId,
764
+ pubkey_prefix: m.pubkey.slice(0, 12)
765
+ }))
766
+ };
767
+ }
754
768
  async function whoAmI() {
755
769
  const auth = getStoredToken();
770
+ const local = localView();
756
771
  if (!auth)
757
- return { signed_in: false };
772
+ return { signed_in: false, local };
758
773
  try {
759
774
  const profile = await exports_my.getProfile(auth.session_token);
760
775
  const meshes = await exports_my.getMeshes(auth.session_token);
@@ -763,12 +778,13 @@ async function whoAmI() {
763
778
  signed_in: true,
764
779
  user: profile,
765
780
  token_source: auth.token_source,
766
- meshes: { owned, guest: meshes.length - owned }
781
+ meshes: { owned, guest: meshes.length - owned },
782
+ local
767
783
  };
768
784
  } catch (err) {
769
785
  if (err instanceof ApiError && err.isUnauthorized) {
770
786
  clearToken();
771
- return { signed_in: false };
787
+ return { signed_in: false, local };
772
788
  }
773
789
  throw err;
774
790
  }
@@ -791,6 +807,8 @@ async function register(callbackPort) {
791
807
  var init_client2 = __esm(() => {
792
808
  init_facade3();
793
809
  init_facade3();
810
+ init_facade();
811
+ init_paths();
794
812
  init_token_store();
795
813
  init_errors2();
796
814
  });
@@ -1138,6 +1156,7 @@ class BrokerClient {
1138
1156
  topicHistoryResolvers = new Map;
1139
1157
  apiKeyCreatedResolvers = new Map;
1140
1158
  apiKeyListResolvers = new Map;
1159
+ apiKeyRevokeResolvers = new Map;
1141
1160
  sharedDirs = [process.cwd()];
1142
1161
  _serviceCatalog = [];
1143
1162
  get serviceCatalog() {
@@ -1546,9 +1565,21 @@ class BrokerClient {
1546
1565
  });
1547
1566
  }
1548
1567
  async apiKeyRevoke(id) {
1549
- if (!this.ws || this.ws.readyState !== this.ws.OPEN)
1550
- return;
1551
- this.ws.send(JSON.stringify({ type: "apikey_revoke", id }));
1568
+ if (!this.ws || this.ws.readyState !== this.ws.OPEN) {
1569
+ return { ok: false, code: "not_connected", message: "broker not connected" };
1570
+ }
1571
+ return new Promise((resolve) => {
1572
+ const reqId = this.makeReqId();
1573
+ this.apiKeyRevokeResolvers.set(reqId, {
1574
+ resolve,
1575
+ timer: setTimeout(() => {
1576
+ if (this.apiKeyRevokeResolvers.delete(reqId)) {
1577
+ resolve({ ok: false, code: "timeout", message: "broker did not respond within 5s" });
1578
+ }
1579
+ }, 5000)
1580
+ });
1581
+ this.ws.send(JSON.stringify({ type: "apikey_revoke", id, _reqId: reqId }));
1582
+ });
1552
1583
  }
1553
1584
  async setState(key, value) {
1554
1585
  if (!this.ws || this.ws.readyState !== this.ws.OPEN)
@@ -2667,6 +2698,28 @@ class BrokerClient {
2667
2698
  this.resolveFromMap(this.apiKeyListResolvers, msgReqId, msg.keys ?? []);
2668
2699
  return;
2669
2700
  }
2701
+ if (msg.type === "apikey_revoke_response") {
2702
+ const status = String(msg.status ?? "");
2703
+ if (status === "revoked") {
2704
+ this.resolveFromMap(this.apiKeyRevokeResolvers, msgReqId, {
2705
+ ok: true,
2706
+ id: String(msg.id ?? "")
2707
+ });
2708
+ } else if (status === "not_found") {
2709
+ this.resolveFromMap(this.apiKeyRevokeResolvers, msgReqId, {
2710
+ ok: false,
2711
+ code: "not_found",
2712
+ message: "no api key matches that id in this mesh"
2713
+ });
2714
+ } else if (status === "not_unique") {
2715
+ this.resolveFromMap(this.apiKeyRevokeResolvers, msgReqId, {
2716
+ ok: false,
2717
+ code: "not_unique",
2718
+ message: `prefix matches ${Number(msg.matches ?? 0)} keys; use the full id`
2719
+ });
2720
+ }
2721
+ return;
2722
+ }
2670
2723
  if (msg.type === "push") {
2671
2724
  this._statsCounters.messagesIn++;
2672
2725
  const nonce = String(msg.nonce ?? "");
@@ -7521,18 +7574,33 @@ async function whoami(opts) {
7521
7574
  const result = await whoAmI();
7522
7575
  if (opts.json) {
7523
7576
  console.log(JSON.stringify({ schema_version: "1.0", ...result }, null, 2));
7524
- return EXIT.SUCCESS;
7577
+ return result.signed_in || result.local ? EXIT.SUCCESS : EXIT.AUTH_FAILED;
7525
7578
  }
7526
- if (!result.signed_in) {
7527
- render.err("Not signed in", "Run `claudemesh login` to sign in.");
7579
+ if (!result.signed_in && !result.local) {
7580
+ render.err("Not signed in", "Run `claudemesh login` to sign in or `claudemesh <invite>` to join.");
7528
7581
  return EXIT.AUTH_FAILED;
7529
7582
  }
7530
7583
  render.section("whoami");
7531
- render.kv([
7532
- ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
7533
- ["token", `${result.token_source} ${dim("(~/.claudemesh/auth.json)")}`],
7534
- ...result.meshes ? [["meshes", `${result.meshes.owned} owned · ${result.meshes.guest} guest`]] : []
7535
- ]);
7584
+ if (result.signed_in) {
7585
+ render.kv([
7586
+ ["user", `${bold(result.user.display_name)} ${dim(`(${result.user.email})`)}`],
7587
+ ["token", `${result.token_source} ${dim("(~/.claudemesh/auth.json)")}`],
7588
+ ...result.meshes ? [["meshes", `${result.meshes.owned} owned · ${result.meshes.guest} guest`]] : []
7589
+ ]);
7590
+ } else {
7591
+ render.kv([
7592
+ ["web", dim("not signed in · run `claudemesh login` for account features")]
7593
+ ]);
7594
+ }
7595
+ if (result.local) {
7596
+ render.blank();
7597
+ render.kv([
7598
+ ["local", `${result.local.meshes.length} mesh${result.local.meshes.length === 1 ? "" : "es"} · ${dim(result.local.config_path)}`]
7599
+ ]);
7600
+ for (const m of result.local.meshes) {
7601
+ console.log(` ${clay("●")} ${bold(m.slug)} ${dim(`member ${m.member_id.slice(0, 8)}… pk ${m.pubkey_prefix}…`)}`);
7602
+ }
7603
+ }
7536
7604
  render.blank();
7537
7605
  return EXIT.SUCCESS;
7538
7606
  }
@@ -11394,11 +11462,19 @@ async function runApiKeyRevoke(id, flags) {
11394
11462
  return EXIT.INVALID_ARGS;
11395
11463
  }
11396
11464
  return await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
11397
- await client.apiKeyRevoke(id);
11465
+ const result = await client.apiKeyRevoke(id);
11466
+ if (!result.ok) {
11467
+ if (flags.json) {
11468
+ console.log(JSON.stringify({ ok: false, code: result.code, message: result.message }));
11469
+ } else {
11470
+ render.err(`${result.code}: ${result.message}`);
11471
+ }
11472
+ return result.code === "not_found" ? EXIT.NOT_FOUND : result.code === "not_unique" ? EXIT.INVALID_ARGS : EXIT.INTERNAL_ERROR;
11473
+ }
11398
11474
  if (flags.json)
11399
- console.log(JSON.stringify({ revoked: id }));
11475
+ console.log(JSON.stringify({ revoked: result.id }));
11400
11476
  else
11401
- render.ok("revoked", clay(id.slice(0, 8)));
11477
+ render.ok("revoked", clay(result.id.slice(0, 8)));
11402
11478
  return EXIT.SUCCESS;
11403
11479
  });
11404
11480
  }
@@ -13976,4 +14052,4 @@ main().catch((err) => {
13976
14052
  process.exit(EXIT.INTERNAL_ERROR);
13977
14053
  });
13978
14054
 
13979
- //# debugId=E78F8FEDBA22CD8C64756E2164756E21
14055
+ //# debugId=131DC57DB8537D5464756E2164756E21