claudemesh-cli 0.9.9 → 0.10.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.
Files changed (2) hide show
  1. package/dist/index.js +366 -223
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -40020,6 +40020,88 @@ var init_file_crypto = __esm(() => {
40020
40020
  init_keypair();
40021
40021
  });
40022
40022
 
40023
+ // src/auth/callback-listener.ts
40024
+ import { createServer } from "node:http";
40025
+ function startCallbackListener() {
40026
+ return new Promise((resolveStart) => {
40027
+ let resolveToken;
40028
+ const tokenPromise = new Promise((r) => {
40029
+ resolveToken = r;
40030
+ });
40031
+ const server = createServer((req, res) => {
40032
+ const url = new URL(req.url, "http://localhost");
40033
+ if (req.method === "OPTIONS") {
40034
+ res.writeHead(204, {
40035
+ "Access-Control-Allow-Origin": "https://claudemesh.com",
40036
+ "Access-Control-Allow-Methods": "GET",
40037
+ "Access-Control-Allow-Headers": "Content-Type"
40038
+ });
40039
+ res.end();
40040
+ return;
40041
+ }
40042
+ if (url.pathname === "/ping") {
40043
+ res.writeHead(200, {
40044
+ "Content-Type": "text/plain",
40045
+ "Access-Control-Allow-Origin": "https://claudemesh.com"
40046
+ });
40047
+ res.end("ok");
40048
+ return;
40049
+ }
40050
+ if (url.pathname === "/callback") {
40051
+ const token = url.searchParams.get("token");
40052
+ if (token) {
40053
+ res.writeHead(200, {
40054
+ "Content-Type": "text/html",
40055
+ "Access-Control-Allow-Origin": "https://claudemesh.com"
40056
+ });
40057
+ res.end("<html><body><h2>Done! You can close this tab.</h2><p>Launching claudemesh...</p></body></html>");
40058
+ resolveToken(token);
40059
+ setTimeout(() => server.close(), 500);
40060
+ } else {
40061
+ res.writeHead(400, { "Content-Type": "text/plain" });
40062
+ res.end("Missing token");
40063
+ }
40064
+ return;
40065
+ }
40066
+ res.writeHead(404);
40067
+ res.end();
40068
+ });
40069
+ server.listen(0, "127.0.0.1", () => {
40070
+ const addr = server.address();
40071
+ resolveStart({
40072
+ port: addr.port,
40073
+ token: tokenPromise,
40074
+ close: () => server.close()
40075
+ });
40076
+ });
40077
+ });
40078
+ }
40079
+ var init_callback_listener = () => {};
40080
+
40081
+ // src/auth/open-browser.ts
40082
+ import { exec } from "node:child_process";
40083
+ function openBrowser(url) {
40084
+ if (!url.startsWith("http://") && !url.startsWith("https://")) {
40085
+ return Promise.resolve(false);
40086
+ }
40087
+ const quoted = JSON.stringify(url);
40088
+ const browserCmd = process.env.BROWSER;
40089
+ const cmd = browserCmd ? `${browserCmd} ${quoted}` : process.platform === "darwin" ? `open ${quoted}` : process.platform === "win32" ? `rundll32 url.dll,FileProtocolHandler ${quoted}` : `xdg-open ${quoted}`;
40090
+ return new Promise((resolve2) => {
40091
+ exec(cmd, (err) => resolve2(!err));
40092
+ });
40093
+ }
40094
+ var init_open_browser = () => {};
40095
+
40096
+ // src/auth/pairing-code.ts
40097
+ import { randomBytes as randomBytes2 } from "node:crypto";
40098
+ function generatePairingCode() {
40099
+ const bytes = randomBytes2(4);
40100
+ return Array.from(bytes, (b) => CHARS[b % CHARS.length]).join("");
40101
+ }
40102
+ var CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
40103
+ var init_pairing_code = () => {};
40104
+
40023
40105
  // src/auth/sync-with-broker.ts
40024
40106
  var exports_sync_with_broker = {};
40025
40107
  __export(exports_sync_with_broker, {
@@ -40062,6 +40144,20 @@ function deriveHttpUrl(wssUrl) {
40062
40144
  return url.toString().replace(/\/$/, "");
40063
40145
  }
40064
40146
 
40147
+ // src/auth/index.ts
40148
+ var exports_auth = {};
40149
+ __export(exports_auth, {
40150
+ syncWithBroker: () => syncWithBroker,
40151
+ startCallbackListener: () => startCallbackListener,
40152
+ openBrowser: () => openBrowser,
40153
+ generatePairingCode: () => generatePairingCode
40154
+ });
40155
+ var init_auth = __esm(() => {
40156
+ init_callback_listener();
40157
+ init_open_browser();
40158
+ init_pairing_code();
40159
+ });
40160
+
40065
40161
  // src/tui/colors.ts
40066
40162
  function moveTo(row, col) {
40067
40163
  return isTTY ? `\x1B[${row};${col}H` : "";
@@ -40093,7 +40189,7 @@ var package_default;
40093
40189
  var init_package = __esm(() => {
40094
40190
  package_default = {
40095
40191
  name: "claudemesh-cli",
40096
- version: "0.9.9",
40192
+ version: "0.10.1",
40097
40193
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
40098
40194
  keywords: [
40099
40195
  "claude-code",
@@ -53839,90 +53935,13 @@ async function runHook(args) {
53839
53935
 
53840
53936
  // src/commands/launch.ts
53841
53937
  init_config();
53938
+ init_auth();
53842
53939
  import { spawn } from "node:child_process";
53843
53940
  import { randomUUID } from "node:crypto";
53844
53941
  import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
53845
53942
  import { tmpdir, hostname as hostname2, homedir as homedir4 } from "node:os";
53846
53943
  import { join as join4 } from "node:path";
53847
53944
  import { createInterface } from "node:readline";
53848
-
53849
- // src/auth/callback-listener.ts
53850
- import { createServer } from "node:http";
53851
- function startCallbackListener() {
53852
- return new Promise((resolveStart) => {
53853
- let resolveToken;
53854
- const tokenPromise = new Promise((r) => {
53855
- resolveToken = r;
53856
- });
53857
- const server = createServer((req, res) => {
53858
- const url = new URL(req.url, "http://localhost");
53859
- if (req.method === "OPTIONS") {
53860
- res.writeHead(204, {
53861
- "Access-Control-Allow-Origin": "https://claudemesh.com",
53862
- "Access-Control-Allow-Methods": "GET",
53863
- "Access-Control-Allow-Headers": "Content-Type"
53864
- });
53865
- res.end();
53866
- return;
53867
- }
53868
- if (url.pathname === "/ping") {
53869
- res.writeHead(200, {
53870
- "Content-Type": "text/plain",
53871
- "Access-Control-Allow-Origin": "https://claudemesh.com"
53872
- });
53873
- res.end("ok");
53874
- return;
53875
- }
53876
- if (url.pathname === "/callback") {
53877
- const token = url.searchParams.get("token");
53878
- if (token) {
53879
- res.writeHead(200, {
53880
- "Content-Type": "text/html",
53881
- "Access-Control-Allow-Origin": "https://claudemesh.com"
53882
- });
53883
- res.end("<html><body><h2>Done! You can close this tab.</h2><p>Launching claudemesh...</p></body></html>");
53884
- resolveToken(token);
53885
- setTimeout(() => server.close(), 500);
53886
- } else {
53887
- res.writeHead(400, { "Content-Type": "text/plain" });
53888
- res.end("Missing token");
53889
- }
53890
- return;
53891
- }
53892
- res.writeHead(404);
53893
- res.end();
53894
- });
53895
- server.listen(0, "127.0.0.1", () => {
53896
- const addr = server.address();
53897
- resolveStart({
53898
- port: addr.port,
53899
- token: tokenPromise,
53900
- close: () => server.close()
53901
- });
53902
- });
53903
- });
53904
- }
53905
- // src/auth/open-browser.ts
53906
- import { exec } from "node:child_process";
53907
- function openBrowser(url) {
53908
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
53909
- return Promise.resolve(false);
53910
- }
53911
- const quoted = JSON.stringify(url);
53912
- const browserCmd = process.env.BROWSER;
53913
- const cmd = browserCmd ? `${browserCmd} ${quoted}` : process.platform === "darwin" ? `open ${quoted}` : process.platform === "win32" ? `rundll32 url.dll,FileProtocolHandler ${quoted}` : `xdg-open ${quoted}`;
53914
- return new Promise((resolve2) => {
53915
- exec(cmd, (err) => resolve2(!err));
53916
- });
53917
- }
53918
- // src/auth/pairing-code.ts
53919
- import { randomBytes as randomBytes2 } from "node:crypto";
53920
- var CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
53921
- function generatePairingCode() {
53922
- const bytes = randomBytes2(4);
53923
- return Array.from(bytes, (b) => CHARS[b % CHARS.length]).join("");
53924
- }
53925
- // src/commands/launch.ts
53926
53945
  init_colors();
53927
53946
 
53928
53947
  // src/tui/screen.ts
@@ -54890,8 +54909,9 @@ async function runDoctor() {
54890
54909
  // src/commands/welcome.ts
54891
54910
  init_config();
54892
54911
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
54893
- import { homedir as homedir6 } from "node:os";
54912
+ import { homedir as homedir6, hostname as hostname3 } from "node:os";
54894
54913
  import { join as join6 } from "node:path";
54914
+ import { createInterface as createInterface2 } from "node:readline";
54895
54915
  init_colors();
54896
54916
  init_spinner();
54897
54917
  function detectState() {
@@ -54916,11 +54936,11 @@ async function runWelcome() {
54916
54936
  if (!process.stdout.isTTY) {
54917
54937
  return runWelcomePlain();
54918
54938
  }
54919
- const state = detectState();
54939
+ let state = detectState();
54920
54940
  const { rows } = termSize();
54921
54941
  enterFullScreen();
54922
54942
  drawTopBar();
54923
- const logoTop = Math.floor((rows - FRAME_HEIGHT - 10) / 2);
54943
+ const logoTop = Math.floor((rows - FRAME_HEIGHT - 14) / 2);
54924
54944
  const brandRow = logoTop + FRAME_HEIGHT + 1;
54925
54945
  const subtitleRow = brandRow + 1;
54926
54946
  const contentRow = subtitleRow + 2;
@@ -54935,108 +54955,188 @@ async function runWelcome() {
54935
54955
  interval: 70
54936
54956
  });
54937
54957
  spinner.start();
54938
- let menuRow = contentRow;
54939
- switch (state) {
54940
- case "no-install": {
54941
- writeCentered(contentRow, "Welcome. Let's get you set up.");
54942
- writeCentered(contentRow + 1, "");
54943
- writeCentered(contentRow + 2, dim("MCP server ") + yellow("○") + dim(" not registered"));
54944
- writeCentered(contentRow + 3, dim("Mesh ") + dim("○") + dim(" waiting"));
54945
- menuRow = contentRow + 5;
54946
- spinner.stop();
54947
- const choice = await menuSelect({
54948
- title: "Get started",
54949
- items: ["Install MCP server", "Cancel"],
54950
- row: menuRow
54951
- });
54958
+ let row = contentRow;
54959
+ if (state === "no-install") {
54960
+ writeCentered(row, bold2("Welcome! Let's get you set up."));
54961
+ row += 2;
54962
+ writeCentered(row, `MCP server ${yellow("")} not registered`);
54963
+ const mcpRow = row;
54964
+ row++;
54965
+ writeCentered(row, `Sign in ${dim("○")} waiting`);
54966
+ const signInRow = row;
54967
+ row++;
54968
+ writeCentered(row, `Mesh ${dim("○")} waiting`);
54969
+ const meshRow = row;
54970
+ row += 2;
54971
+ await new Promise((r) => setTimeout(r, 600));
54972
+ spinner.stop();
54973
+ exitFullScreen();
54974
+ runInstall();
54975
+ enterFullScreen();
54976
+ drawTopBar();
54977
+ writeCentered(brandRow, boldOrange("claudemesh"));
54978
+ writeCentered(subtitleRow, dim("peer mesh for Claude Code"));
54979
+ spinner.start();
54980
+ writeCentered(contentRow, bold2("Welcome! Let's get you set up."));
54981
+ writeCentered(mcpRow, `MCP server ${green("✓")} registered`);
54982
+ writeCentered(signInRow, `Sign in ${yellow("○")} waiting`);
54983
+ writeCentered(meshRow, `Mesh ${dim("○")} waiting`);
54984
+ state = detectState();
54985
+ row = meshRow + 2;
54986
+ }
54987
+ if (state === "no-meshes") {
54988
+ if (row === contentRow) {
54989
+ writeCentered(row, `MCP server ${green("✓")} registered`);
54990
+ row++;
54991
+ writeCentered(row, `Sign in ${yellow("○")} no meshes joined`);
54992
+ const signInRow = row;
54993
+ row += 2;
54994
+ }
54995
+ spinner.stop();
54996
+ const choice = await menuSelect({
54997
+ title: "Connect to a mesh",
54998
+ items: [
54999
+ "Sign in via browser (create or join meshes)",
55000
+ "Paste an invite URL",
55001
+ "Exit"
55002
+ ],
55003
+ row
55004
+ });
55005
+ if (choice === 2) {
54952
55006
  exitFullScreen();
54953
- if (choice === 0) {
54954
- console.log(green("$") + ` claudemesh install
54955
- `);
54956
- }
54957
- break;
55007
+ return;
54958
55008
  }
54959
- case "no-meshes": {
54960
- writeCentered(contentRow, green("✓") + " MCP registered. Now join a mesh.");
54961
- writeCentered(contentRow + 2, dim("MCP server ") + green("✓") + dim(" registered"));
54962
- writeCentered(contentRow + 3, dim("Mesh ") + yellow("○") + dim(" none joined"));
54963
- menuRow = contentRow + 5;
54964
- spinner.stop();
54965
- const choice = await menuSelect({
54966
- title: "Next step",
54967
- items: [
54968
- "Join with invite URL",
54969
- "Create a new mesh",
54970
- "Cancel"
54971
- ],
54972
- row: menuRow
54973
- });
55009
+ if (choice === 0) {
54974
55010
  exitFullScreen();
54975
- if (choice === 0) {
54976
- console.log(green("$") + ` claudemesh join https://claudemesh.com/join/<token>
54977
- `);
54978
- console.log(dim(" Don't have an invite? Create one at ") + bold2("https://claudemesh.com") + dim(" or ask a mesh owner."));
54979
- } else if (choice === 1) {
54980
- console.log(green("$") + ` claudemesh create
54981
- `);
55011
+ console.log(dim(` Opening browser for sign-in...
55012
+ `));
55013
+ const { generatePairingCode: generatePairingCode2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
55014
+ const { startCallbackListener: startCallbackListener2, openBrowser: openBrowser2 } = await Promise.resolve().then(() => (init_auth(), exports_auth));
55015
+ const code = generatePairingCode2();
55016
+ const listener = await startCallbackListener2();
55017
+ const url = `https://claudemesh.com/cli-auth?port=${listener.port}&code=${code}&action=sync`;
55018
+ const opened = await openBrowser2(url);
55019
+ if (!opened) {
55020
+ console.log(" Couldn't open browser. Visit:");
55021
+ }
55022
+ console.log(dim(` ${url}
55023
+ `));
55024
+ const manualPromise = new Promise((resolve2) => {
55025
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
55026
+ rl.question(" Paste sync token (or wait for browser): ", (answer) => {
55027
+ rl.close();
55028
+ if (answer.trim())
55029
+ resolve2(answer.trim());
55030
+ });
55031
+ });
55032
+ const timeoutPromise = new Promise((resolve2) => {
55033
+ setTimeout(() => resolve2(null), 900000);
55034
+ });
55035
+ const syncToken = await Promise.race([
55036
+ listener.token,
55037
+ manualPromise,
55038
+ timeoutPromise
55039
+ ]);
55040
+ listener.close();
55041
+ if (!syncToken) {
55042
+ console.error(`
55043
+ Timed out waiting for sign-in.`);
55044
+ process.exit(1);
54982
55045
  }
54983
- break;
55046
+ const { generateKeypair: generateKeypair3 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
55047
+ const keypair = await generateKeypair3();
55048
+ const displayName = `${hostname3()}-${process.pid}`;
55049
+ const { syncWithBroker: syncWithBroker2 } = await Promise.resolve().then(() => exports_sync_with_broker);
55050
+ const result = await syncWithBroker2(syncToken, keypair.publicKey, displayName);
55051
+ const config2 = loadConfig();
55052
+ const { saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
55053
+ for (const m of result.meshes) {
55054
+ config2.meshes.push({
55055
+ meshId: m.mesh_id,
55056
+ memberId: m.member_id,
55057
+ slug: m.slug,
55058
+ name: m.slug,
55059
+ pubkey: keypair.publicKey,
55060
+ secretKey: keypair.secretKey,
55061
+ brokerUrl: m.broker_url,
55062
+ joinedAt: new Date().toISOString()
55063
+ });
55064
+ }
55065
+ config2.accountId = result.account_id;
55066
+ saveConfig2(config2);
55067
+ console.log(`
55068
+ ${green("✓")} Synced ${result.meshes.length} mesh(es): ${result.meshes.map((m) => m.slug).join(", ")}
55069
+ `);
55070
+ await runLaunch({}, []);
55071
+ return;
54984
55072
  }
54985
- case "ready": {
54986
- const cfg = loadConfig();
54987
- const meshNames = cfg.meshes.map((m) => m.slug).join(", ");
54988
- writeCentered(contentRow, green("✓") + " MCP registered");
54989
- writeCentered(contentRow + 1, green("✓") + ` ${cfg.meshes.length} mesh(es): ${meshNames}`);
54990
- writeCentered(contentRow + 2, "");
54991
- writeCentered(contentRow + 3, bold2("Ready to launch."));
54992
- menuRow = contentRow + 5;
54993
- spinner.stop();
54994
- const choice = await menuSelect({
54995
- title: "What next?",
54996
- items: [
54997
- "Launch Claude Code session",
54998
- "View peers",
54999
- "Check status",
55000
- "Run diagnostics",
55001
- "Exit"
55002
- ],
55003
- row: menuRow
55004
- });
55073
+ if (choice === 1) {
55005
55074
  exitFullScreen();
55006
- switch (choice) {
55007
- case 0:
55008
- await runLaunch({}, []);
55009
- return;
55010
- case 1:
55011
- console.log(green("$") + ` claudemesh peers
55075
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
55076
+ const url = await new Promise((resolve2) => {
55077
+ rl.question(" Invite URL: ", (answer) => {
55078
+ rl.close();
55079
+ resolve2(answer.trim());
55080
+ });
55081
+ });
55082
+ if (url) {
55083
+ await runLaunch({ join: url }, []);
55084
+ }
55085
+ return;
55086
+ }
55087
+ }
55088
+ if (state === "ready") {
55089
+ const cfg = loadConfig();
55090
+ const meshNames = cfg.meshes.map((m) => m.slug).join(", ");
55091
+ writeCentered(row, `MCP server ${green("✓")} registered`);
55092
+ row++;
55093
+ writeCentered(row, `Meshes ${green("✓")} ${cfg.meshes.length}: ${meshNames}`);
55094
+ row += 2;
55095
+ spinner.stop();
55096
+ const choice = await menuSelect({
55097
+ title: "What next?",
55098
+ items: [
55099
+ "Launch Claude Code session",
55100
+ "View peers",
55101
+ "Check status",
55102
+ "Run diagnostics",
55103
+ "Exit"
55104
+ ],
55105
+ row
55106
+ });
55107
+ exitFullScreen();
55108
+ switch (choice) {
55109
+ case 0:
55110
+ await runLaunch({}, []);
55111
+ return;
55112
+ case 1:
55113
+ console.log(green("$") + ` claudemesh peers
55012
55114
  `);
55013
- break;
55014
- case 2:
55015
- console.log(green("$") + ` claudemesh status
55115
+ break;
55116
+ case 2:
55117
+ console.log(green("$") + ` claudemesh status
55016
55118
  `);
55017
- break;
55018
- case 3:
55019
- console.log(green("$") + ` claudemesh doctor
55119
+ break;
55120
+ case 3:
55121
+ console.log(green("$") + ` claudemesh doctor
55020
55122
  `);
55021
- break;
55022
- }
55023
- break;
55123
+ break;
55024
55124
  }
55025
- case "broken-config": {
55026
- writeCentered(contentRow, yellow("⚠") + " ~/.claudemesh/config.json is unreadable.");
55027
- menuRow = contentRow + 2;
55028
- spinner.stop();
55029
- const choice = await menuSelect({
55030
- title: "Recover",
55031
- items: ["Run diagnostics", "Cancel"],
55032
- row: menuRow
55033
- });
55034
- exitFullScreen();
55035
- if (choice === 0) {
55036
- console.log(green("$") + ` claudemesh doctor
55125
+ return;
55126
+ }
55127
+ if (state === "broken-config") {
55128
+ writeCentered(row, yellow("⚠") + " ~/.claudemesh/config.json is unreadable.");
55129
+ row += 2;
55130
+ spinner.stop();
55131
+ const choice = await menuSelect({
55132
+ title: "Recover",
55133
+ items: ["Run diagnostics", "Exit"],
55134
+ row
55135
+ });
55136
+ exitFullScreen();
55137
+ if (choice === 0) {
55138
+ console.log(green("$") + ` claudemesh doctor
55037
55139
  `);
55038
- }
55039
- break;
55040
55140
  }
55041
55141
  }
55042
55142
  }
@@ -55064,9 +55164,94 @@ function runWelcomePlain() {
55064
55164
  console.log("");
55065
55165
  }
55066
55166
 
55167
+ // src/commands/peers.ts
55168
+ init_config();
55169
+ import { hostname as hostname4 } from "node:os";
55170
+ async function runPeers(flags) {
55171
+ const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
55172
+ const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
55173
+ const bold3 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
55174
+ const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55175
+ const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
55176
+ const config2 = loadConfig();
55177
+ const meshes = flags.mesh ? config2.meshes.filter((m) => m.slug === flags.mesh) : config2.meshes;
55178
+ if (meshes.length === 0) {
55179
+ console.error(flags.mesh ? `Mesh "${flags.mesh}" not found. Joined: ${config2.meshes.map((m) => m.slug).join(", ")}` : "No meshes joined. Run `claudemesh join <url>` first.");
55180
+ process.exit(1);
55181
+ }
55182
+ const allPeers = [];
55183
+ for (const mesh of meshes) {
55184
+ const displayName = config2.displayName ?? `${hostname4()}-${process.pid}`;
55185
+ const client2 = new BrokerClient(mesh, { displayName });
55186
+ try {
55187
+ await client2.connect();
55188
+ const peers = await client2.listPeers();
55189
+ if (flags.json) {
55190
+ allPeers.push({ mesh: mesh.slug, peers });
55191
+ continue;
55192
+ }
55193
+ console.log(bold3(`Peers on ${mesh.slug}`) + dim2(` (${peers.length})`));
55194
+ console.log("");
55195
+ if (peers.length === 0) {
55196
+ console.log(dim2(" No peers connected."));
55197
+ } else {
55198
+ for (const p of peers) {
55199
+ const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
55200
+ const statusIcon = p.status === "working" ? yellow2("●") : green2("●");
55201
+ const name = bold3(p.displayName);
55202
+ const meta2 = [];
55203
+ if (p.peerType)
55204
+ meta2.push(p.peerType);
55205
+ if (p.channel)
55206
+ meta2.push(p.channel);
55207
+ if (p.model)
55208
+ meta2.push(p.model);
55209
+ const metaStr = meta2.length ? dim2(` (${meta2.join(", ")})`) : "";
55210
+ const cwdStr = p.cwd ? dim2(` cwd: ${p.cwd}`) : "";
55211
+ const summary = p.summary ? dim2(` ${p.summary}`) : "";
55212
+ console.log(` ${statusIcon} ${name}${groups}${metaStr}${summary}`);
55213
+ if (cwdStr)
55214
+ console.log(` ${cwdStr}`);
55215
+ }
55216
+ }
55217
+ console.log("");
55218
+ } catch (e) {
55219
+ console.error(dim2(` Could not connect to ${mesh.slug}: ${e instanceof Error ? e.message : String(e)}`));
55220
+ console.log("");
55221
+ } finally {
55222
+ client2.close();
55223
+ }
55224
+ }
55225
+ if (flags.json) {
55226
+ console.log(JSON.stringify(meshes.length === 1 ? allPeers[0]?.peers : allPeers, null, 2));
55227
+ }
55228
+ }
55229
+
55067
55230
  // src/commands/connect.ts
55068
- import { hostname as hostname3 } from "node:os";
55231
+ import { hostname as hostname5 } from "node:os";
55232
+ import { createInterface as createInterface3 } from "node:readline";
55069
55233
  init_config();
55234
+ async function pickMesh2(meshes) {
55235
+ console.log(`
55236
+ Select mesh:`);
55237
+ meshes.forEach((m, i) => {
55238
+ console.log(` ${i + 1}) ${m.slug}`);
55239
+ });
55240
+ console.log("");
55241
+ const rl = createInterface3({ input: process.stdin, output: process.stdout });
55242
+ return new Promise((resolve2) => {
55243
+ rl.question(" Choice [1]: ", (answer) => {
55244
+ rl.close();
55245
+ const idx = parseInt(answer || "1", 10) - 1;
55246
+ if (idx >= 0 && idx < meshes.length) {
55247
+ resolve2(meshes[idx]);
55248
+ } else {
55249
+ console.error(" Invalid choice, using first mesh.");
55250
+ resolve2(meshes[0]);
55251
+ }
55252
+ });
55253
+ });
55254
+ }
55070
55255
  async function withMesh(opts, fn) {
55071
55256
  const config2 = loadConfig();
55072
55257
  if (config2.meshes.length === 0) {
@@ -55084,11 +55269,9 @@ async function withMesh(opts, fn) {
55084
55269
  } else if (config2.meshes.length === 1) {
55085
55270
  mesh = config2.meshes[0];
55086
55271
  } else {
55087
- console.error(`Multiple meshes joined. Specify one with --mesh <slug>.
55088
- Joined: ${config2.meshes.map((m) => m.slug).join(", ")}`);
55089
- process.exit(1);
55272
+ mesh = await pickMesh2(config2.meshes);
55090
55273
  }
55091
- const displayName = opts.displayName ?? config2.displayName ?? `${hostname3()}-${process.pid}`;
55274
+ const displayName = opts.displayName ?? config2.displayName ?? `${hostname5()}-${process.pid}`;
55092
55275
  const client2 = new BrokerClient(mesh, { displayName });
55093
55276
  try {
55094
55277
  await client2.connect();
@@ -55099,47 +55282,6 @@ Joined: ${config2.meshes.map((m) => m.slug).join(", ")}`);
55099
55282
  }
55100
55283
  }
55101
55284
 
55102
- // src/commands/peers.ts
55103
- async function runPeers(flags) {
55104
- const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
55105
- const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
55106
- const bold3 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
55107
- const green2 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
55108
- const yellow2 = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
55109
- await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
55110
- const peers = await client2.listPeers();
55111
- if (flags.json) {
55112
- console.log(JSON.stringify(peers, null, 2));
55113
- return;
55114
- }
55115
- if (peers.length === 0) {
55116
- console.log(dim2(`No peers connected on mesh "${mesh.slug}".`));
55117
- return;
55118
- }
55119
- console.log(bold3(`Peers on ${mesh.slug}`) + dim2(` (${peers.length})`));
55120
- console.log("");
55121
- for (const p of peers) {
55122
- const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
55123
- const statusIcon = p.status === "working" ? yellow2("●") : green2("●");
55124
- const name = bold3(p.displayName);
55125
- const meta2 = [];
55126
- if (p.peerType)
55127
- meta2.push(p.peerType);
55128
- if (p.channel)
55129
- meta2.push(p.channel);
55130
- if (p.model)
55131
- meta2.push(p.model);
55132
- const metaStr = meta2.length ? dim2(` (${meta2.join(", ")})`) : "";
55133
- const cwdStr = p.cwd ? dim2(` cwd: ${p.cwd}`) : "";
55134
- const summary = p.summary ? dim2(` ${p.summary}`) : "";
55135
- console.log(` ${statusIcon} ${name}${groups}${metaStr}${summary}`);
55136
- if (cwdStr)
55137
- console.log(` ${cwdStr}`);
55138
- }
55139
- console.log("");
55140
- });
55141
- }
55142
-
55143
55285
  // src/commands/send.ts
55144
55286
  async function runSend(flags, to, message) {
55145
55287
  const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
@@ -55602,9 +55744,10 @@ function runCreate(args) {
55602
55744
 
55603
55745
  // src/commands/sync.ts
55604
55746
  init_config();
55605
- import { createInterface as createInterface2 } from "node:readline";
55606
- import { hostname as hostname4 } from "node:os";
55747
+ init_auth();
55607
55748
  init_keypair();
55749
+ import { createInterface as createInterface4 } from "node:readline";
55750
+ import { hostname as hostname6 } from "node:os";
55608
55751
  async function runSync(args) {
55609
55752
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
55610
55753
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
@@ -55617,7 +55760,7 @@ async function runSync(args) {
55617
55760
  console.log(dim2(`Visit: ${url}`));
55618
55761
  await openBrowser(url);
55619
55762
  const manualPromise = new Promise((resolve2) => {
55620
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
55763
+ const rl = createInterface4({ input: process.stdin, output: process.stdout });
55621
55764
  rl.question("Paste sync token (or wait for browser): ", (answer) => {
55622
55765
  rl.close();
55623
55766
  if (answer.trim())
@@ -55638,7 +55781,7 @@ async function runSync(args) {
55638
55781
  process.exit(1);
55639
55782
  }
55640
55783
  const keypair = config2.meshes.length > 0 ? { publicKey: config2.meshes[0].pubkey, secretKey: config2.meshes[0].secretKey } : await generateKeypair2();
55641
- const displayName = config2.displayName ?? `${hostname4()}-${process.pid}`;
55784
+ const displayName = config2.displayName ?? `${hostname6()}-${process.pid}`;
55642
55785
  const result = await syncWithBroker(syncToken, keypair.publicKey, displayName);
55643
55786
  let added = 0;
55644
55787
  for (const m of result.meshes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.9.9",
3
+ "version": "0.10.1",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -48,10 +48,10 @@
48
48
  "prettier": "3.6.2",
49
49
  "typescript": "5.9.3",
50
50
  "vitest": "4.0.14",
51
- "@turbostarter/prettier-config": "0.1.0",
51
+ "@turbostarter/vitest-config": "0.1.0",
52
52
  "@turbostarter/tsconfig": "0.1.0",
53
- "@turbostarter/eslint-config": "0.1.0",
54
- "@turbostarter/vitest-config": "0.1.0"
53
+ "@turbostarter/prettier-config": "0.1.0",
54
+ "@turbostarter/eslint-config": "0.1.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",