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.
- package/dist/index.js +366 -223
- 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.
|
|
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
|
-
|
|
54939
|
+
let state = detectState();
|
|
54920
54940
|
const { rows } = termSize();
|
|
54921
54941
|
enterFullScreen();
|
|
54922
54942
|
drawTopBar();
|
|
54923
|
-
const logoTop = Math.floor((rows - FRAME_HEIGHT -
|
|
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
|
|
54939
|
-
|
|
54940
|
-
|
|
54941
|
-
|
|
54942
|
-
|
|
54943
|
-
|
|
54944
|
-
|
|
54945
|
-
|
|
54946
|
-
|
|
54947
|
-
|
|
54948
|
-
|
|
54949
|
-
|
|
54950
|
-
|
|
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
|
-
|
|
54954
|
-
console.log(green("$") + ` claudemesh install
|
|
54955
|
-
`);
|
|
54956
|
-
}
|
|
54957
|
-
break;
|
|
55007
|
+
return;
|
|
54958
55008
|
}
|
|
54959
|
-
|
|
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
|
-
|
|
54976
|
-
|
|
54977
|
-
|
|
54978
|
-
|
|
54979
|
-
|
|
54980
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55007
|
-
|
|
55008
|
-
|
|
55009
|
-
|
|
55010
|
-
|
|
55011
|
-
|
|
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
|
-
|
|
55014
|
-
|
|
55015
|
-
|
|
55115
|
+
break;
|
|
55116
|
+
case 2:
|
|
55117
|
+
console.log(green("$") + ` claudemesh status
|
|
55016
55118
|
`);
|
|
55017
|
-
|
|
55018
|
-
|
|
55019
|
-
|
|
55119
|
+
break;
|
|
55120
|
+
case 3:
|
|
55121
|
+
console.log(green("$") + ` claudemesh doctor
|
|
55020
55122
|
`);
|
|
55021
|
-
|
|
55022
|
-
}
|
|
55023
|
-
break;
|
|
55123
|
+
break;
|
|
55024
55124
|
}
|
|
55025
|
-
|
|
55026
|
-
|
|
55027
|
-
|
|
55028
|
-
|
|
55029
|
-
|
|
55030
|
-
|
|
55031
|
-
|
|
55032
|
-
|
|
55033
|
-
|
|
55034
|
-
|
|
55035
|
-
|
|
55036
|
-
|
|
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
|
|
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
|
-
|
|
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 ?? `${
|
|
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
|
-
|
|
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 =
|
|
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 ?? `${
|
|
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.
|
|
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/
|
|
51
|
+
"@turbostarter/vitest-config": "0.1.0",
|
|
52
52
|
"@turbostarter/tsconfig": "0.1.0",
|
|
53
|
-
"@turbostarter/
|
|
54
|
-
"@turbostarter/
|
|
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",
|