claudemesh-cli 0.10.4 → 0.10.6
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 +204 -42
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -40189,7 +40189,7 @@ var package_default;
|
|
|
40189
40189
|
var init_package = __esm(() => {
|
|
40190
40190
|
package_default = {
|
|
40191
40191
|
name: "claudemesh-cli",
|
|
40192
|
-
version: "0.10.
|
|
40192
|
+
version: "0.10.6",
|
|
40193
40193
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
40194
40194
|
keywords: [
|
|
40195
40195
|
"claude-code",
|
|
@@ -53720,18 +53720,170 @@ async function enrollWithBroker2(args) {
|
|
|
53720
53720
|
// src/commands/join.ts
|
|
53721
53721
|
init_keypair();
|
|
53722
53722
|
init_config();
|
|
53723
|
+
|
|
53724
|
+
// src/lib/invite-v2.ts
|
|
53725
|
+
var import_libsodium_wrappers2 = __toESM(require_libsodium_wrappers(), 1);
|
|
53726
|
+
async function ensureSodium2() {
|
|
53727
|
+
await import_libsodium_wrappers2.default.ready;
|
|
53728
|
+
return import_libsodium_wrappers2.default;
|
|
53729
|
+
}
|
|
53730
|
+
async function generateX25519Keypair() {
|
|
53731
|
+
const s = await ensureSodium2();
|
|
53732
|
+
const kp = s.crypto_box_keypair();
|
|
53733
|
+
const publicKeyB64 = s.to_base64(kp.publicKey, s.base64_variants.URLSAFE_NO_PADDING);
|
|
53734
|
+
return { publicKeyB64, secretKey: kp.privateKey };
|
|
53735
|
+
}
|
|
53736
|
+
async function claimInviteV2(opts) {
|
|
53737
|
+
const s = await ensureSodium2();
|
|
53738
|
+
const { publicKeyB64, secretKey } = await generateX25519Keypair();
|
|
53739
|
+
const publicKeyBytes = s.from_base64(publicKeyB64, s.base64_variants.URLSAFE_NO_PADDING);
|
|
53740
|
+
const base = opts.appBaseUrl.replace(/\/$/, "");
|
|
53741
|
+
const code = encodeURIComponent(opts.code);
|
|
53742
|
+
const url = `${base}/api/public/invites/${code}/claim`;
|
|
53743
|
+
let res;
|
|
53744
|
+
try {
|
|
53745
|
+
res = await fetch(url, {
|
|
53746
|
+
method: "POST",
|
|
53747
|
+
headers: {
|
|
53748
|
+
"Content-Type": "application/json",
|
|
53749
|
+
Accept: "application/json"
|
|
53750
|
+
},
|
|
53751
|
+
body: JSON.stringify({ recipient_x25519_pubkey: publicKeyB64 }),
|
|
53752
|
+
signal: AbortSignal.timeout(15000)
|
|
53753
|
+
});
|
|
53754
|
+
} catch (e) {
|
|
53755
|
+
throw new Error(`claim request failed (network): ${e instanceof Error ? e.message : String(e)}`);
|
|
53756
|
+
}
|
|
53757
|
+
let parsed = null;
|
|
53758
|
+
try {
|
|
53759
|
+
parsed = await res.json();
|
|
53760
|
+
} catch {}
|
|
53761
|
+
if (!res.ok) {
|
|
53762
|
+
const err = parsed ?? {};
|
|
53763
|
+
const reason = err.error ?? err.code ?? err.message ?? `HTTP ${res.status}`;
|
|
53764
|
+
switch (res.status) {
|
|
53765
|
+
case 400:
|
|
53766
|
+
throw new Error(`invite claim rejected: ${reason}`);
|
|
53767
|
+
case 404:
|
|
53768
|
+
throw new Error(`invite not found: ${reason}`);
|
|
53769
|
+
case 410:
|
|
53770
|
+
throw new Error(`invite no longer usable: ${reason}`);
|
|
53771
|
+
default:
|
|
53772
|
+
throw new Error(`invite claim failed (${res.status}): ${reason}`);
|
|
53773
|
+
}
|
|
53774
|
+
}
|
|
53775
|
+
const body = parsed ?? {};
|
|
53776
|
+
if (!body.sealed_root_key || !body.mesh_id || !body.member_id || !body.owner_pubkey || !body.canonical_v2) {
|
|
53777
|
+
throw new Error(`invite claim response malformed: missing required field(s)`);
|
|
53778
|
+
}
|
|
53779
|
+
let rootKey;
|
|
53780
|
+
try {
|
|
53781
|
+
const sealed = s.from_base64(body.sealed_root_key, s.base64_variants.URLSAFE_NO_PADDING);
|
|
53782
|
+
const opened = s.crypto_box_seal_open(sealed, publicKeyBytes, secretKey);
|
|
53783
|
+
if (!opened)
|
|
53784
|
+
throw new Error("crypto_box_seal_open returned empty");
|
|
53785
|
+
rootKey = opened;
|
|
53786
|
+
} catch (e) {
|
|
53787
|
+
throw new Error(`failed to unseal root key (server sealed to wrong pubkey?): ${e instanceof Error ? e.message : String(e)}`);
|
|
53788
|
+
}
|
|
53789
|
+
if (rootKey.length !== 32) {
|
|
53790
|
+
throw new Error(`unsealed root key has wrong length: ${rootKey.length} (expected 32)`);
|
|
53791
|
+
}
|
|
53792
|
+
return {
|
|
53793
|
+
meshId: body.mesh_id,
|
|
53794
|
+
memberId: body.member_id,
|
|
53795
|
+
ownerPubkey: body.owner_pubkey,
|
|
53796
|
+
canonicalV2: body.canonical_v2,
|
|
53797
|
+
rootKey
|
|
53798
|
+
};
|
|
53799
|
+
}
|
|
53800
|
+
function parseV2InviteInput(input) {
|
|
53801
|
+
const trimmed = input.trim();
|
|
53802
|
+
const urlMatch = trimmed.match(/^https?:\/\/[^/]+(?:\/[a-z]{2})?\/i\/([A-Za-z0-9]+)\/?$/);
|
|
53803
|
+
if (urlMatch)
|
|
53804
|
+
return urlMatch[1];
|
|
53805
|
+
const schemelessMatch = trimmed.match(/^[^/]+(?:\/[a-z]{2})?\/i\/([A-Za-z0-9]+)\/?$/);
|
|
53806
|
+
if (schemelessMatch)
|
|
53807
|
+
return schemelessMatch[1];
|
|
53808
|
+
if (/^[A-Za-z0-9]{6,16}$/.test(trimmed))
|
|
53809
|
+
return trimmed;
|
|
53810
|
+
return null;
|
|
53811
|
+
}
|
|
53812
|
+
|
|
53813
|
+
// src/commands/join.ts
|
|
53723
53814
|
init_env();
|
|
53815
|
+
var import_libsodium_wrappers3 = __toESM(require_libsodium_wrappers(), 1);
|
|
53724
53816
|
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
|
|
53725
53817
|
import { join as join3, dirname as dirname3 } from "node:path";
|
|
53726
53818
|
import { homedir as homedir3, hostname } from "node:os";
|
|
53819
|
+
function deriveAppBaseUrl() {
|
|
53820
|
+
const override = process.env.CLAUDEMESH_APP_URL;
|
|
53821
|
+
if (override)
|
|
53822
|
+
return override.replace(/\/$/, "");
|
|
53823
|
+
try {
|
|
53824
|
+
const u = new URL(env.CLAUDEMESH_BROKER_URL);
|
|
53825
|
+
const host = u.host.replace(/^ic\./, "");
|
|
53826
|
+
const scheme = u.protocol === "wss:" ? "https:" : "http:";
|
|
53827
|
+
return `${scheme}//${host}`;
|
|
53828
|
+
} catch {
|
|
53829
|
+
return "https://claudemesh.com";
|
|
53830
|
+
}
|
|
53831
|
+
}
|
|
53832
|
+
async function runJoinV2(code) {
|
|
53833
|
+
const appBaseUrl = deriveAppBaseUrl();
|
|
53834
|
+
console.log(`Claiming invite ${code} via ${appBaseUrl}…`);
|
|
53835
|
+
let claim;
|
|
53836
|
+
try {
|
|
53837
|
+
claim = await claimInviteV2({ appBaseUrl, code });
|
|
53838
|
+
} catch (e) {
|
|
53839
|
+
console.error(`claudemesh: ${e instanceof Error ? e.message : String(e)}`);
|
|
53840
|
+
process.exit(1);
|
|
53841
|
+
}
|
|
53842
|
+
const keypair = await generateKeypair2();
|
|
53843
|
+
const displayName = `${hostname()}-${process.pid}`;
|
|
53844
|
+
await import_libsodium_wrappers3.default.ready;
|
|
53845
|
+
const rootKeyB64 = import_libsodium_wrappers3.default.to_base64(claim.rootKey, import_libsodium_wrappers3.default.base64_variants.URLSAFE_NO_PADDING);
|
|
53846
|
+
const fallbackSlug = `mesh-${claim.meshId.slice(0, 8)}`;
|
|
53847
|
+
const config2 = loadConfig();
|
|
53848
|
+
config2.meshes = config2.meshes.filter((m) => m.meshId !== claim.meshId);
|
|
53849
|
+
config2.meshes.push({
|
|
53850
|
+
meshId: claim.meshId,
|
|
53851
|
+
memberId: claim.memberId,
|
|
53852
|
+
slug: fallbackSlug,
|
|
53853
|
+
name: fallbackSlug,
|
|
53854
|
+
pubkey: keypair.publicKey,
|
|
53855
|
+
secretKey: keypair.secretKey,
|
|
53856
|
+
brokerUrl: env.CLAUDEMESH_BROKER_URL,
|
|
53857
|
+
joinedAt: new Date().toISOString(),
|
|
53858
|
+
rootKey: rootKeyB64,
|
|
53859
|
+
inviteVersion: 2
|
|
53860
|
+
});
|
|
53861
|
+
saveConfig(config2);
|
|
53862
|
+
console.log("");
|
|
53863
|
+
console.log(`✓ Joined mesh ${claim.meshId} via v2 invite`);
|
|
53864
|
+
console.log(` member id: ${claim.memberId}`);
|
|
53865
|
+
console.log(` pubkey: ${keypair.publicKey.slice(0, 16)}…`);
|
|
53866
|
+
console.log(` broker: ${env.CLAUDEMESH_BROKER_URL}`);
|
|
53867
|
+
console.log(` config: ${getConfigPath()}`);
|
|
53868
|
+
console.log("");
|
|
53869
|
+
console.log("Restart Claude Code to pick up the new mesh.");
|
|
53870
|
+
}
|
|
53727
53871
|
async function runJoin(args) {
|
|
53728
53872
|
const link = args[0];
|
|
53729
53873
|
if (!link) {
|
|
53730
|
-
console.error("Usage: claudemesh join <invite-url-or-
|
|
53874
|
+
console.error("Usage: claudemesh join <invite-url-or-code>");
|
|
53731
53875
|
console.error("");
|
|
53732
|
-
console.error("
|
|
53876
|
+
console.error("Examples:");
|
|
53877
|
+
console.error(" claudemesh join https://claudemesh.com/i/abc12345");
|
|
53878
|
+
console.error(" claudemesh join abc12345");
|
|
53879
|
+
console.error(" claudemesh join ic://join/eyJ2IjoxLC4uLn0 (v1 legacy)");
|
|
53733
53880
|
process.exit(1);
|
|
53734
53881
|
}
|
|
53882
|
+
const v2Code = parseV2InviteInput(link);
|
|
53883
|
+
if (v2Code) {
|
|
53884
|
+
await runJoinV2(v2Code);
|
|
53885
|
+
return;
|
|
53886
|
+
}
|
|
53735
53887
|
let invite;
|
|
53736
53888
|
try {
|
|
53737
53889
|
invite = await parseInviteLink2(link);
|
|
@@ -53936,7 +54088,7 @@ async function runHook(args) {
|
|
|
53936
54088
|
// src/commands/launch.ts
|
|
53937
54089
|
init_config();
|
|
53938
54090
|
init_auth();
|
|
53939
|
-
import {
|
|
54091
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
53940
54092
|
import { randomUUID } from "node:crypto";
|
|
53941
54093
|
import { mkdtempSync, writeFileSync as writeFileSync4, rmSync, readdirSync, statSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
53942
54094
|
import { tmpdir, hostname as hostname2, homedir as homedir4 } from "node:os";
|
|
@@ -54380,9 +54532,9 @@ async function runLaunch(flags, rawArgs) {
|
|
|
54380
54532
|
const keypair = await generateKeypair3();
|
|
54381
54533
|
const displayNameForSync = args.name ?? `${hostname2()}-${process.pid}`;
|
|
54382
54534
|
const { syncWithBroker: syncWithBroker2 } = await Promise.resolve().then(() => exports_sync_with_broker);
|
|
54383
|
-
const
|
|
54535
|
+
const result2 = await syncWithBroker2(syncToken, keypair.publicKey, displayNameForSync);
|
|
54384
54536
|
const { saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
54385
|
-
for (const m of
|
|
54537
|
+
for (const m of result2.meshes) {
|
|
54386
54538
|
config2.meshes.push({
|
|
54387
54539
|
meshId: m.mesh_id,
|
|
54388
54540
|
memberId: m.member_id,
|
|
@@ -54394,11 +54546,11 @@ async function runLaunch(flags, rawArgs) {
|
|
|
54394
54546
|
joinedAt: new Date().toISOString()
|
|
54395
54547
|
});
|
|
54396
54548
|
}
|
|
54397
|
-
config2.accountId =
|
|
54549
|
+
config2.accountId = result2.account_id;
|
|
54398
54550
|
saveConfig2(config2);
|
|
54399
54551
|
justSynced = true;
|
|
54400
54552
|
console.log(`
|
|
54401
|
-
${green2("✓")} Synced ${
|
|
54553
|
+
${green2("✓")} Synced ${result2.meshes.length} mesh(es): ${result2.meshes.map((m) => m.slug).join(", ")}
|
|
54402
54554
|
`);
|
|
54403
54555
|
}
|
|
54404
54556
|
if (config2.meshes.length === 0) {
|
|
@@ -54583,19 +54735,6 @@ async function runLaunch(flags, rawArgs) {
|
|
|
54583
54735
|
}
|
|
54584
54736
|
}
|
|
54585
54737
|
}
|
|
54586
|
-
const child = spawn(claudeBin, claudeArgs, {
|
|
54587
|
-
stdio: "inherit",
|
|
54588
|
-
shell: isWindows,
|
|
54589
|
-
env: {
|
|
54590
|
-
...process.env,
|
|
54591
|
-
CLAUDEMESH_CONFIG_DIR: tmpDir,
|
|
54592
|
-
CLAUDEMESH_DISPLAY_NAME: displayName,
|
|
54593
|
-
...claudeSessionId ? { CLAUDEMESH_SESSION_ID: claudeSessionId } : {},
|
|
54594
|
-
MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
|
|
54595
|
-
MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
|
|
54596
|
-
...role ? { CLAUDEMESH_ROLE: role } : {}
|
|
54597
|
-
}
|
|
54598
|
-
});
|
|
54599
54738
|
const cleanup = () => {
|
|
54600
54739
|
if (meshMcpEntries.length > 0) {
|
|
54601
54740
|
try {
|
|
@@ -54614,31 +54753,47 @@ async function runLaunch(flags, rawArgs) {
|
|
|
54614
54753
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
54615
54754
|
} catch {}
|
|
54616
54755
|
};
|
|
54617
|
-
|
|
54618
|
-
|
|
54756
|
+
process.on("exit", cleanup);
|
|
54757
|
+
if (process.stdin.isTTY) {
|
|
54758
|
+
try {
|
|
54759
|
+
process.stdin.setRawMode(false);
|
|
54760
|
+
} catch {}
|
|
54761
|
+
}
|
|
54762
|
+
process.stdin.removeAllListeners("data");
|
|
54763
|
+
process.stdin.removeAllListeners("keypress");
|
|
54764
|
+
process.stdin.removeAllListeners("readable");
|
|
54765
|
+
process.stdin.pause();
|
|
54766
|
+
if (process.stdout.isTTY) {
|
|
54767
|
+
process.stdout.write("\x1B[?25h");
|
|
54768
|
+
process.stdout.write("\x1B[?1049l");
|
|
54769
|
+
}
|
|
54770
|
+
const result = spawnSync2(claudeBin, claudeArgs, {
|
|
54771
|
+
stdio: "inherit",
|
|
54772
|
+
shell: isWindows,
|
|
54773
|
+
env: {
|
|
54774
|
+
...process.env,
|
|
54775
|
+
CLAUDEMESH_CONFIG_DIR: tmpDir,
|
|
54776
|
+
CLAUDEMESH_DISPLAY_NAME: displayName,
|
|
54777
|
+
...claudeSessionId ? { CLAUDEMESH_SESSION_ID: claudeSessionId } : {},
|
|
54778
|
+
MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "30000",
|
|
54779
|
+
MAX_MCP_OUTPUT_TOKENS: process.env.MAX_MCP_OUTPUT_TOKENS ?? "50000",
|
|
54780
|
+
...role ? { CLAUDEMESH_ROLE: role } : {}
|
|
54781
|
+
}
|
|
54782
|
+
});
|
|
54783
|
+
if (result.error) {
|
|
54784
|
+
const err = result.error;
|
|
54619
54785
|
if (err.code === "ENOENT") {
|
|
54620
54786
|
console.error("✗ `claude` not found on PATH. Install Claude Code first.");
|
|
54621
54787
|
} else {
|
|
54622
54788
|
console.error(`✗ failed to launch claude: ${err.message}`);
|
|
54623
54789
|
}
|
|
54624
54790
|
process.exit(1);
|
|
54625
|
-
}
|
|
54626
|
-
|
|
54627
|
-
|
|
54628
|
-
|
|
54629
|
-
|
|
54630
|
-
|
|
54631
|
-
}
|
|
54632
|
-
process.exit(code ?? 0);
|
|
54633
|
-
});
|
|
54634
|
-
process.on("SIGTERM", () => {
|
|
54635
|
-
cleanup();
|
|
54636
|
-
process.exit(0);
|
|
54637
|
-
});
|
|
54638
|
-
process.on("SIGINT", () => {
|
|
54639
|
-
cleanup();
|
|
54640
|
-
process.exit(0);
|
|
54641
|
-
});
|
|
54791
|
+
}
|
|
54792
|
+
if (result.signal) {
|
|
54793
|
+
process.kill(process.pid, result.signal);
|
|
54794
|
+
return;
|
|
54795
|
+
}
|
|
54796
|
+
process.exit(result.status ?? 0);
|
|
54642
54797
|
}
|
|
54643
54798
|
|
|
54644
54799
|
// src/commands/status.ts
|
|
@@ -54729,7 +54884,7 @@ init_version();
|
|
|
54729
54884
|
import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
54730
54885
|
import { homedir as homedir5, platform as platform2 } from "node:os";
|
|
54731
54886
|
import { join as join5 } from "node:path";
|
|
54732
|
-
import { spawnSync as
|
|
54887
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
54733
54888
|
function checkNode() {
|
|
54734
54889
|
const major = Number(process.versions.node.split(".")[0]);
|
|
54735
54890
|
return {
|
|
@@ -54740,7 +54895,7 @@ function checkNode() {
|
|
|
54740
54895
|
};
|
|
54741
54896
|
}
|
|
54742
54897
|
function checkClaudeOnPath() {
|
|
54743
|
-
const res = platform2() === "win32" ?
|
|
54898
|
+
const res = platform2() === "win32" ? spawnSync3("where", ["claude"]) : spawnSync3("sh", ["-c", "command -v claude"]);
|
|
54744
54899
|
const onPath = res.status === 0;
|
|
54745
54900
|
const location = onPath ? res.stdout.toString().trim().split(`
|
|
54746
54901
|
`)[0] : undefined;
|
|
@@ -56284,4 +56439,11 @@ var main = defineCommand({
|
|
|
56284
56439
|
await runWelcome();
|
|
56285
56440
|
}
|
|
56286
56441
|
});
|
|
56442
|
+
var KNOWN_SUBCOMMANDS = new Set(Object.keys(main.subCommands ?? {}));
|
|
56443
|
+
var ROOT_PASSTHROUGH_FLAGS = new Set(["--help", "-h", "--version", "-v"]);
|
|
56444
|
+
var argv = process.argv.slice(2);
|
|
56445
|
+
var first = argv[0];
|
|
56446
|
+
if (first && !ROOT_PASSTHROUGH_FLAGS.has(first) && !KNOWN_SUBCOMMANDS.has(first)) {
|
|
56447
|
+
process.argv.splice(2, 0, "launch");
|
|
56448
|
+
}
|
|
56287
56449
|
runMain(main);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemesh-cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
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/
|
|
52
|
-
"@turbostarter/vitest-config": "0.1.0",
|
|
51
|
+
"@turbostarter/eslint-config": "0.1.0",
|
|
53
52
|
"@turbostarter/prettier-config": "0.1.0",
|
|
54
|
-
"@turbostarter/
|
|
53
|
+
"@turbostarter/vitest-config": "0.1.0",
|
|
54
|
+
"@turbostarter/tsconfig": "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",
|