claudemesh-cli 1.0.0-alpha.28 → 1.0.0-alpha.29
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/entrypoints/cli.js +464 -80
- package/dist/entrypoints/cli.js.map +12 -9
- package/dist/entrypoints/mcp.js +388 -24
- package/dist/entrypoints/mcp.js.map +9 -4
- package/package.json +1 -1
package/dist/entrypoints/cli.js
CHANGED
|
@@ -119,6 +119,22 @@ var init_timings = __esm(() => {
|
|
|
119
119
|
};
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
// src/utils/url.ts
|
|
123
|
+
function isInviteUrl(input) {
|
|
124
|
+
return /^https?:\/\/[^/]+\/(?:[a-z]{2}\/)?i\//.test(input) || /^https?:\/\/[^/]+\/(?:[a-z]{2}\/)?join\//.test(input) || /^ic:\/\//.test(input) || /^claudemesh:\/\//.test(input);
|
|
125
|
+
}
|
|
126
|
+
function normaliseInviteUrl(input, host = "claudemesh.com") {
|
|
127
|
+
const trimmed = input.trim();
|
|
128
|
+
if (trimmed.startsWith("claudemesh://")) {
|
|
129
|
+
const rest = trimmed.slice("claudemesh://".length).replace(/^\/+/, "");
|
|
130
|
+
const m = rest.match(/^(?:i|join)\/(.+)$/);
|
|
131
|
+
const tail = m ? m[1] : rest;
|
|
132
|
+
const kind = rest.startsWith("join/") ? "join" : "i";
|
|
133
|
+
return `https://${host}/${kind}/${tail}`;
|
|
134
|
+
}
|
|
135
|
+
return trimmed;
|
|
136
|
+
}
|
|
137
|
+
|
|
122
138
|
// src/constants/paths.ts
|
|
123
139
|
import { homedir } from "node:os";
|
|
124
140
|
import { join } from "node:path";
|
|
@@ -3951,16 +3967,73 @@ var init_login = __esm(() => {
|
|
|
3951
3967
|
init_urls();
|
|
3952
3968
|
});
|
|
3953
3969
|
|
|
3954
|
-
// src/
|
|
3955
|
-
var
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3970
|
+
// src/ui/render.ts
|
|
3971
|
+
var OUT, ERR, INDENT = " ", render;
|
|
3972
|
+
var init_render = __esm(() => {
|
|
3973
|
+
init_styles();
|
|
3974
|
+
OUT = process.stdout;
|
|
3975
|
+
ERR = process.stderr;
|
|
3976
|
+
render = {
|
|
3977
|
+
blank() {
|
|
3978
|
+
OUT.write(`
|
|
3979
|
+
`);
|
|
3980
|
+
},
|
|
3981
|
+
ok(msg, detail) {
|
|
3982
|
+
const d = detail ? ` ${dim("(" + detail + ")")}` : "";
|
|
3983
|
+
OUT.write(`${INDENT}${green(icons.check)} ${msg}${d}
|
|
3984
|
+
`);
|
|
3985
|
+
},
|
|
3986
|
+
warn(msg, hint) {
|
|
3987
|
+
OUT.write(`${INDENT}${yellow(icons.warn)} ${msg}
|
|
3988
|
+
`);
|
|
3989
|
+
if (hint)
|
|
3990
|
+
OUT.write(`${INDENT} ${dim(hint)}
|
|
3991
|
+
`);
|
|
3992
|
+
},
|
|
3993
|
+
err(msg, hint) {
|
|
3994
|
+
ERR.write(`${INDENT}${red(icons.cross)} ${msg}
|
|
3995
|
+
`);
|
|
3996
|
+
if (hint)
|
|
3997
|
+
ERR.write(`${INDENT} ${dim(hint)}
|
|
3998
|
+
`);
|
|
3999
|
+
},
|
|
4000
|
+
info(msg) {
|
|
4001
|
+
OUT.write(`${INDENT}${msg}
|
|
4002
|
+
`);
|
|
4003
|
+
},
|
|
4004
|
+
section(title) {
|
|
4005
|
+
OUT.write(`
|
|
4006
|
+
${INDENT}${dim("—")} ${clay(title)}
|
|
4007
|
+
|
|
4008
|
+
`);
|
|
4009
|
+
},
|
|
4010
|
+
heading(title) {
|
|
4011
|
+
OUT.write(`${INDENT}${bold(title)}
|
|
4012
|
+
`);
|
|
4013
|
+
},
|
|
4014
|
+
kv(pairs, opts) {
|
|
4015
|
+
const pad = opts?.padTo ?? Math.max(...pairs.map(([k]) => k.length)) + 2;
|
|
4016
|
+
for (const [k, v] of pairs) {
|
|
4017
|
+
OUT.write(`${INDENT}${dim(k.padEnd(pad, " "))}${v}
|
|
4018
|
+
`);
|
|
4019
|
+
}
|
|
4020
|
+
},
|
|
4021
|
+
code(snippet) {
|
|
4022
|
+
for (const line of snippet.split(`
|
|
4023
|
+
`)) {
|
|
4024
|
+
OUT.write(`${INDENT} ${cyan(line)}
|
|
4025
|
+
`);
|
|
4026
|
+
}
|
|
4027
|
+
},
|
|
4028
|
+
link(url) {
|
|
4029
|
+
OUT.write(`${INDENT}${clay(url)}
|
|
4030
|
+
`);
|
|
4031
|
+
},
|
|
4032
|
+
hint(msg) {
|
|
4033
|
+
OUT.write(`${INDENT}${dim(icons.arrow + " " + msg)}
|
|
4034
|
+
`);
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
3964
4037
|
});
|
|
3965
4038
|
|
|
3966
4039
|
// src/commands/welcome.ts
|
|
@@ -3970,35 +4043,53 @@ __export(exports_welcome, {
|
|
|
3970
4043
|
_stub: () => runWelcome
|
|
3971
4044
|
});
|
|
3972
4045
|
import { createInterface as createInterface5 } from "node:readline";
|
|
3973
|
-
|
|
3974
|
-
const config = readConfig();
|
|
3975
|
-
if (config.meshes.length > 0)
|
|
3976
|
-
return EXIT.SUCCESS;
|
|
3977
|
-
renderWelcome();
|
|
4046
|
+
function prompt2(q) {
|
|
3978
4047
|
const rl = createInterface5({ input: process.stdin, output: process.stdout });
|
|
3979
4048
|
return new Promise((resolve) => {
|
|
3980
|
-
rl.question(
|
|
4049
|
+
rl.question(q, (a) => {
|
|
3981
4050
|
rl.close();
|
|
3982
|
-
|
|
3983
|
-
if (choice === "1")
|
|
3984
|
-
resolve(await register2());
|
|
3985
|
-
else if (choice === "2")
|
|
3986
|
-
resolve(await login());
|
|
3987
|
-
else if (choice === "3") {
|
|
3988
|
-
console.log(`
|
|
3989
|
-
Run: claudemesh join <invite-url>
|
|
3990
|
-
`);
|
|
3991
|
-
resolve(EXIT.SUCCESS);
|
|
3992
|
-
} else
|
|
3993
|
-
resolve(EXIT.USER_CANCELLED);
|
|
4051
|
+
resolve(a.trim());
|
|
3994
4052
|
});
|
|
3995
4053
|
});
|
|
3996
4054
|
}
|
|
4055
|
+
async function runWelcome() {
|
|
4056
|
+
const config = readConfig();
|
|
4057
|
+
if (config.meshes.length > 0)
|
|
4058
|
+
return EXIT.SUCCESS;
|
|
4059
|
+
renderWelcome();
|
|
4060
|
+
render.info("Do you already have an invite link? (y/n) [n]");
|
|
4061
|
+
const hasInvite = (await prompt2(" > ")).toLowerCase().startsWith("y");
|
|
4062
|
+
if (hasInvite) {
|
|
4063
|
+
render.blank();
|
|
4064
|
+
render.info("Paste your invite link (claudemesh.com/i/... or claudemesh://...)");
|
|
4065
|
+
const raw = await prompt2(" > ");
|
|
4066
|
+
if (!raw || !isInviteUrl(raw)) {
|
|
4067
|
+
render.err("That doesn't look like a claudemesh invite URL.");
|
|
4068
|
+
render.hint("Check your email — the link starts with https://claudemesh.com/i/");
|
|
4069
|
+
return EXIT.INVALID_ARGS;
|
|
4070
|
+
}
|
|
4071
|
+
const normalised = normaliseInviteUrl(raw);
|
|
4072
|
+
render.blank();
|
|
4073
|
+
render.ok(`Joining via ${normalised}`);
|
|
4074
|
+
const { runLaunch: runLaunch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
|
|
4075
|
+
await runLaunch2({
|
|
4076
|
+
join: normalised,
|
|
4077
|
+
name: process.env.USER ?? process.env.USERNAME ?? undefined,
|
|
4078
|
+
yes: false
|
|
4079
|
+
}, []);
|
|
4080
|
+
return EXIT.SUCCESS;
|
|
4081
|
+
}
|
|
4082
|
+
render.blank();
|
|
4083
|
+
render.info("Opening claudemesh.com so you can sign in and create your first mesh.");
|
|
4084
|
+
render.hint("After sign-in, paste the sync token back here when prompted.");
|
|
4085
|
+
render.blank();
|
|
4086
|
+
return await login();
|
|
4087
|
+
}
|
|
3997
4088
|
var init_welcome2 = __esm(() => {
|
|
3998
4089
|
init_facade();
|
|
3999
4090
|
init_welcome();
|
|
4000
4091
|
init_login();
|
|
4001
|
-
|
|
4092
|
+
init_render();
|
|
4002
4093
|
init_exit_codes();
|
|
4003
4094
|
});
|
|
4004
4095
|
|
|
@@ -4656,7 +4747,7 @@ __export(exports_delete_mesh, {
|
|
|
4656
4747
|
deleteMesh: () => deleteMesh
|
|
4657
4748
|
});
|
|
4658
4749
|
import { createInterface as createInterface6 } from "node:readline";
|
|
4659
|
-
function
|
|
4750
|
+
function prompt3(question) {
|
|
4660
4751
|
const rl = createInterface6({ input: process.stdin, output: process.stdout });
|
|
4661
4752
|
return new Promise((resolve) => {
|
|
4662
4753
|
rl.question(question, (a) => {
|
|
@@ -4698,7 +4789,7 @@ async function deleteMesh(slug, opts = {}) {
|
|
|
4698
4789
|
console.log(` ${bold(String(i + 1) + ")")} ${m.slug} ${dim("(" + m.name + ")")}`);
|
|
4699
4790
|
});
|
|
4700
4791
|
console.log("");
|
|
4701
|
-
const choice = await
|
|
4792
|
+
const choice = await prompt3(" Choice: ");
|
|
4702
4793
|
const idx = parseInt(choice, 10) - 1;
|
|
4703
4794
|
if (idx < 0 || idx >= config.meshes.length) {
|
|
4704
4795
|
console.log(" Cancelled.");
|
|
@@ -4718,7 +4809,7 @@ async function deleteMesh(slug, opts = {}) {
|
|
|
4718
4809
|
console.log(` ${bold("2)")} ${red("Delete everywhere")} ${dim("(removes for all members)")}`);
|
|
4719
4810
|
console.log(` ${bold("3)")} Cancel`);
|
|
4720
4811
|
console.log("");
|
|
4721
|
-
const choice = await
|
|
4812
|
+
const choice = await prompt3(" Choice [1]: ") || "1";
|
|
4722
4813
|
if (choice === "3") {
|
|
4723
4814
|
console.log(" Cancelled.");
|
|
4724
4815
|
return EXIT.USER_CANCELLED;
|
|
@@ -4726,7 +4817,7 @@ async function deleteMesh(slug, opts = {}) {
|
|
|
4726
4817
|
if (choice === "2") {
|
|
4727
4818
|
console.log(`
|
|
4728
4819
|
${red("Warning:")} This will delete ${bold(slug)} for all members.`);
|
|
4729
|
-
const confirm = await
|
|
4820
|
+
const confirm = await prompt3(` Type "${slug}" to confirm: `);
|
|
4730
4821
|
if (confirm.toLowerCase() !== slug.toLowerCase()) {
|
|
4731
4822
|
console.log(" Cancelled.");
|
|
4732
4823
|
return EXIT.USER_CANCELLED;
|
|
@@ -4755,7 +4846,7 @@ async function deleteMesh(slug, opts = {}) {
|
|
|
4755
4846
|
${yellow(icons.warn)} Only the mesh owner can delete it from the server.`));
|
|
4756
4847
|
}
|
|
4757
4848
|
console.log("");
|
|
4758
|
-
const choice = await
|
|
4849
|
+
const choice = await prompt3(" Choice [1]: ") || "1";
|
|
4759
4850
|
if (choice === "2") {
|
|
4760
4851
|
console.log(" Cancelled.");
|
|
4761
4852
|
return EXIT.USER_CANCELLED;
|
|
@@ -5888,7 +5979,7 @@ __export(exports_invite, {
|
|
|
5888
5979
|
invite: () => invite
|
|
5889
5980
|
});
|
|
5890
5981
|
import { createInterface as createInterface7 } from "node:readline";
|
|
5891
|
-
function
|
|
5982
|
+
function prompt4(question) {
|
|
5892
5983
|
const rl = createInterface7({ input: process.stdin, output: process.stdout });
|
|
5893
5984
|
return new Promise((resolve) => {
|
|
5894
5985
|
rl.question(question, (a) => {
|
|
@@ -5920,7 +6011,7 @@ async function invite(email, opts = {}) {
|
|
|
5920
6011
|
console.log(` ${bold(String(i + 1) + ")")} ${m.slug} ${dim("(" + m.name + ")")}`);
|
|
5921
6012
|
});
|
|
5922
6013
|
console.log("");
|
|
5923
|
-
const choice = await
|
|
6014
|
+
const choice = await prompt4(" Choice [1]: ") || "1";
|
|
5924
6015
|
const idx = parseInt(choice, 10) - 1;
|
|
5925
6016
|
meshSlug = config.meshes[idx >= 0 && idx < config.meshes.length ? idx : 0].slug;
|
|
5926
6017
|
}
|
|
@@ -6585,6 +6676,18 @@ var init_profile = __esm(() => {
|
|
|
6585
6676
|
init_facade();
|
|
6586
6677
|
});
|
|
6587
6678
|
|
|
6679
|
+
// src/commands/register.ts
|
|
6680
|
+
var exports_register = {};
|
|
6681
|
+
__export(exports_register, {
|
|
6682
|
+
register: () => register2
|
|
6683
|
+
});
|
|
6684
|
+
async function register2() {
|
|
6685
|
+
return login();
|
|
6686
|
+
}
|
|
6687
|
+
var init_register = __esm(() => {
|
|
6688
|
+
init_login();
|
|
6689
|
+
});
|
|
6690
|
+
|
|
6588
6691
|
// src/commands/logout.ts
|
|
6589
6692
|
var exports_logout = {};
|
|
6590
6693
|
__export(exports_logout, {
|
|
@@ -8294,9 +8397,9 @@ __export(exports_backup, {
|
|
|
8294
8397
|
});
|
|
8295
8398
|
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync15 } from "node:fs";
|
|
8296
8399
|
import { createInterface as createInterface10 } from "node:readline";
|
|
8297
|
-
function readHidden(
|
|
8400
|
+
function readHidden(prompt5) {
|
|
8298
8401
|
return new Promise((resolve2) => {
|
|
8299
|
-
process.stdout.write(
|
|
8402
|
+
process.stdout.write(prompt5);
|
|
8300
8403
|
const rl = createInterface10({ input: process.stdin, output: process.stdout, terminal: true });
|
|
8301
8404
|
const stdin = process.stdin;
|
|
8302
8405
|
const wasRaw = Boolean(stdin.isRaw);
|
|
@@ -8410,6 +8513,261 @@ var init_backup = __esm(() => {
|
|
|
8410
8513
|
MAGIC = Buffer.from("CMB1", "utf-8");
|
|
8411
8514
|
});
|
|
8412
8515
|
|
|
8516
|
+
// src/commands/upgrade.ts
|
|
8517
|
+
var exports_upgrade = {};
|
|
8518
|
+
__export(exports_upgrade, {
|
|
8519
|
+
runUpgrade: () => runUpgrade
|
|
8520
|
+
});
|
|
8521
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
8522
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
8523
|
+
import { dirname as dirname3, join as join8, resolve as resolve2 } from "node:path";
|
|
8524
|
+
async function latestAlpha() {
|
|
8525
|
+
try {
|
|
8526
|
+
const res = await fetch(URLS.NPM_REGISTRY, { signal: AbortSignal.timeout(8000) });
|
|
8527
|
+
if (!res.ok)
|
|
8528
|
+
return null;
|
|
8529
|
+
const body = await res.json();
|
|
8530
|
+
return body["dist-tags"]?.alpha ?? body["dist-tags"]?.latest ?? null;
|
|
8531
|
+
} catch {
|
|
8532
|
+
return null;
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
function findNpm() {
|
|
8536
|
+
const portable = join8(process.env.HOME ?? "", ".claudemesh", "node", "bin", "npm");
|
|
8537
|
+
if (existsSync16(portable)) {
|
|
8538
|
+
return { npm: portable, prefix: join8(process.env.HOME ?? "", ".claudemesh") };
|
|
8539
|
+
}
|
|
8540
|
+
let cur = resolve2(process.argv[1] ?? ".");
|
|
8541
|
+
for (let i = 0;i < 6; i++) {
|
|
8542
|
+
cur = dirname3(cur);
|
|
8543
|
+
const candidate = join8(cur, "bin", "npm");
|
|
8544
|
+
if (existsSync16(candidate))
|
|
8545
|
+
return { npm: candidate };
|
|
8546
|
+
}
|
|
8547
|
+
return { npm: "npm" };
|
|
8548
|
+
}
|
|
8549
|
+
async function runUpgrade(opts = {}) {
|
|
8550
|
+
render.section("claudemesh upgrade");
|
|
8551
|
+
render.kv([
|
|
8552
|
+
["installed", VERSION],
|
|
8553
|
+
["checking", "npm registry…"]
|
|
8554
|
+
]);
|
|
8555
|
+
const latest = await latestAlpha();
|
|
8556
|
+
if (!latest) {
|
|
8557
|
+
render.warn("Could not reach npm registry — skipped.");
|
|
8558
|
+
return EXIT.SUCCESS;
|
|
8559
|
+
}
|
|
8560
|
+
render.kv([["latest", latest]]);
|
|
8561
|
+
if (latest === VERSION) {
|
|
8562
|
+
render.blank();
|
|
8563
|
+
render.ok(`Already on latest (${latest}).`);
|
|
8564
|
+
return EXIT.SUCCESS;
|
|
8565
|
+
}
|
|
8566
|
+
if (opts.check) {
|
|
8567
|
+
render.blank();
|
|
8568
|
+
render.warn(`Update available: ${VERSION} → ${latest}`);
|
|
8569
|
+
render.hint("Run: claudemesh upgrade");
|
|
8570
|
+
return EXIT.SUCCESS;
|
|
8571
|
+
}
|
|
8572
|
+
const { npm, prefix } = findNpm();
|
|
8573
|
+
const args = ["install", "-g"];
|
|
8574
|
+
if (prefix)
|
|
8575
|
+
args.push("--prefix", prefix);
|
|
8576
|
+
args.push("claudemesh-cli@alpha");
|
|
8577
|
+
render.blank();
|
|
8578
|
+
render.info(`Updating ${VERSION} → ${latest}…`);
|
|
8579
|
+
render.hint(`${npm} ${args.join(" ")}`);
|
|
8580
|
+
render.blank();
|
|
8581
|
+
const res = spawnSync6(npm, args, { stdio: "inherit" });
|
|
8582
|
+
if (res.status !== 0) {
|
|
8583
|
+
render.err(`npm exited with status ${res.status}`);
|
|
8584
|
+
render.hint("Try: npm i -g claudemesh-cli@alpha");
|
|
8585
|
+
return EXIT.INTERNAL_ERROR;
|
|
8586
|
+
}
|
|
8587
|
+
render.blank();
|
|
8588
|
+
render.ok(`Upgraded to ${latest}.`);
|
|
8589
|
+
return EXIT.SUCCESS;
|
|
8590
|
+
}
|
|
8591
|
+
var init_upgrade = __esm(() => {
|
|
8592
|
+
init_urls();
|
|
8593
|
+
init_render();
|
|
8594
|
+
init_exit_codes();
|
|
8595
|
+
});
|
|
8596
|
+
|
|
8597
|
+
// src/commands/grants.ts
|
|
8598
|
+
var exports_grants = {};
|
|
8599
|
+
__export(exports_grants, {
|
|
8600
|
+
runRevoke: () => runRevoke,
|
|
8601
|
+
runGrants: () => runGrants,
|
|
8602
|
+
runGrant: () => runGrant,
|
|
8603
|
+
runBlock: () => runBlock,
|
|
8604
|
+
isAllowed: () => isAllowed
|
|
8605
|
+
});
|
|
8606
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
|
|
8607
|
+
import { homedir as homedir8 } from "node:os";
|
|
8608
|
+
import { join as join9 } from "node:path";
|
|
8609
|
+
function readGrants() {
|
|
8610
|
+
if (!existsSync17(GRANT_FILE))
|
|
8611
|
+
return {};
|
|
8612
|
+
try {
|
|
8613
|
+
return JSON.parse(readFileSync12(GRANT_FILE, "utf-8"));
|
|
8614
|
+
} catch {
|
|
8615
|
+
return {};
|
|
8616
|
+
}
|
|
8617
|
+
}
|
|
8618
|
+
function writeGrants(g) {
|
|
8619
|
+
const dir = join9(homedir8(), ".claudemesh");
|
|
8620
|
+
if (!existsSync17(dir))
|
|
8621
|
+
mkdirSync5(dir, { recursive: true });
|
|
8622
|
+
writeFileSync9(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
|
|
8623
|
+
}
|
|
8624
|
+
function resolveCaps(input) {
|
|
8625
|
+
if (input.includes("all"))
|
|
8626
|
+
return [...ALL_CAPS];
|
|
8627
|
+
return input.filter((c) => ALL_CAPS.includes(c));
|
|
8628
|
+
}
|
|
8629
|
+
async function resolvePeer(meshSlug, name) {
|
|
8630
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
8631
|
+
const peers = await client.listPeers();
|
|
8632
|
+
const match = peers.find((p) => p.displayName === name || p.pubkey === name || p.pubkey.startsWith(name));
|
|
8633
|
+
return match ? { displayName: match.displayName, pubkey: match.pubkey } : null;
|
|
8634
|
+
});
|
|
8635
|
+
}
|
|
8636
|
+
function pickMesh2(slug) {
|
|
8637
|
+
const cfg = readConfig();
|
|
8638
|
+
if (slug)
|
|
8639
|
+
return cfg.meshes.find((m) => m.slug === slug) ? slug : null;
|
|
8640
|
+
return cfg.meshes[0]?.slug ?? null;
|
|
8641
|
+
}
|
|
8642
|
+
async function runGrant(peer, caps, opts = {}) {
|
|
8643
|
+
if (!peer || caps.length === 0) {
|
|
8644
|
+
render.err("Usage: claudemesh grant <peer> <capability...>");
|
|
8645
|
+
render.hint(`Capabilities: ${ALL_CAPS.join(", ")}, all`);
|
|
8646
|
+
return EXIT.INVALID_ARGS;
|
|
8647
|
+
}
|
|
8648
|
+
const mesh = pickMesh2(opts.mesh);
|
|
8649
|
+
if (!mesh) {
|
|
8650
|
+
render.err("No matching mesh — join one first.");
|
|
8651
|
+
return EXIT.NOT_FOUND;
|
|
8652
|
+
}
|
|
8653
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
8654
|
+
if (!resolved) {
|
|
8655
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
8656
|
+
return EXIT.NOT_FOUND;
|
|
8657
|
+
}
|
|
8658
|
+
const wanted = resolveCaps(caps);
|
|
8659
|
+
if (wanted.length === 0) {
|
|
8660
|
+
render.err(`Unknown capabilities: ${caps.join(", ")}`);
|
|
8661
|
+
return EXIT.INVALID_ARGS;
|
|
8662
|
+
}
|
|
8663
|
+
const store = readGrants();
|
|
8664
|
+
const meshGrants = store[mesh] ?? {};
|
|
8665
|
+
const existing = meshGrants[resolved.pubkey] ?? DEFAULT_CAPS.slice();
|
|
8666
|
+
const merged = Array.from(new Set([...existing, ...wanted]));
|
|
8667
|
+
meshGrants[resolved.pubkey] = merged;
|
|
8668
|
+
store[mesh] = meshGrants;
|
|
8669
|
+
writeGrants(store);
|
|
8670
|
+
render.ok(`Granted ${wanted.join(", ")} to ${resolved.displayName} on ${mesh}.`);
|
|
8671
|
+
render.kv([["now", merged.join(", ")]]);
|
|
8672
|
+
return EXIT.SUCCESS;
|
|
8673
|
+
}
|
|
8674
|
+
async function runRevoke(peer, caps, opts = {}) {
|
|
8675
|
+
if (!peer || caps.length === 0) {
|
|
8676
|
+
render.err("Usage: claudemesh revoke <peer> <capability...>");
|
|
8677
|
+
return EXIT.INVALID_ARGS;
|
|
8678
|
+
}
|
|
8679
|
+
const mesh = pickMesh2(opts.mesh);
|
|
8680
|
+
if (!mesh) {
|
|
8681
|
+
render.err("No matching mesh.");
|
|
8682
|
+
return EXIT.NOT_FOUND;
|
|
8683
|
+
}
|
|
8684
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
8685
|
+
if (!resolved) {
|
|
8686
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
8687
|
+
return EXIT.NOT_FOUND;
|
|
8688
|
+
}
|
|
8689
|
+
const wanted = caps.includes("all") ? ALL_CAPS.slice() : resolveCaps(caps);
|
|
8690
|
+
const store = readGrants();
|
|
8691
|
+
const meshGrants = store[mesh] ?? {};
|
|
8692
|
+
const existing = meshGrants[resolved.pubkey] ?? DEFAULT_CAPS.slice();
|
|
8693
|
+
const after = existing.filter((c) => !wanted.includes(c));
|
|
8694
|
+
meshGrants[resolved.pubkey] = after;
|
|
8695
|
+
store[mesh] = meshGrants;
|
|
8696
|
+
writeGrants(store);
|
|
8697
|
+
render.ok(`Revoked ${wanted.join(", ")} from ${resolved.displayName} on ${mesh}.`);
|
|
8698
|
+
render.kv([["now", after.length ? after.join(", ") : "(none)"]]);
|
|
8699
|
+
return EXIT.SUCCESS;
|
|
8700
|
+
}
|
|
8701
|
+
async function runBlock(peer, opts = {}) {
|
|
8702
|
+
if (!peer) {
|
|
8703
|
+
render.err("Usage: claudemesh block <peer>");
|
|
8704
|
+
return EXIT.INVALID_ARGS;
|
|
8705
|
+
}
|
|
8706
|
+
const mesh = pickMesh2(opts.mesh);
|
|
8707
|
+
if (!mesh) {
|
|
8708
|
+
render.err("No matching mesh.");
|
|
8709
|
+
return EXIT.NOT_FOUND;
|
|
8710
|
+
}
|
|
8711
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
8712
|
+
if (!resolved) {
|
|
8713
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
8714
|
+
return EXIT.NOT_FOUND;
|
|
8715
|
+
}
|
|
8716
|
+
const store = readGrants();
|
|
8717
|
+
const meshGrants = store[mesh] ?? {};
|
|
8718
|
+
meshGrants[resolved.pubkey] = [];
|
|
8719
|
+
store[mesh] = meshGrants;
|
|
8720
|
+
writeGrants(store);
|
|
8721
|
+
render.ok(`Blocked ${resolved.displayName} on ${mesh} (all capabilities revoked).`);
|
|
8722
|
+
render.hint(`Undo with: claudemesh grant ${resolved.displayName} all --mesh ${mesh}`);
|
|
8723
|
+
return EXIT.SUCCESS;
|
|
8724
|
+
}
|
|
8725
|
+
async function runGrants(opts = {}) {
|
|
8726
|
+
const mesh = pickMesh2(opts.mesh);
|
|
8727
|
+
if (!mesh) {
|
|
8728
|
+
render.err("No matching mesh.");
|
|
8729
|
+
return EXIT.NOT_FOUND;
|
|
8730
|
+
}
|
|
8731
|
+
const store = readGrants();
|
|
8732
|
+
const meshGrants = store[mesh] ?? {};
|
|
8733
|
+
if (opts.json) {
|
|
8734
|
+
console.log(JSON.stringify({ schema_version: "1.0", mesh, grants: meshGrants }, null, 2));
|
|
8735
|
+
return EXIT.SUCCESS;
|
|
8736
|
+
}
|
|
8737
|
+
render.section(`grants on ${mesh}`);
|
|
8738
|
+
const peerPubkeys = Object.keys(meshGrants);
|
|
8739
|
+
if (peerPubkeys.length === 0) {
|
|
8740
|
+
render.info("(no overrides — all peers use default caps: " + DEFAULT_CAPS.join(", ") + ")");
|
|
8741
|
+
return EXIT.SUCCESS;
|
|
8742
|
+
}
|
|
8743
|
+
await withMesh({ meshSlug: mesh }, async (client) => {
|
|
8744
|
+
const peers = await client.listPeers();
|
|
8745
|
+
const byPk = new Map(peers.map((p) => [p.pubkey, p.displayName]));
|
|
8746
|
+
for (const [pk, caps] of Object.entries(meshGrants)) {
|
|
8747
|
+
const name = byPk.get(pk) ?? `${pk.slice(0, 10)}…`;
|
|
8748
|
+
render.kv([[name, caps.length ? caps.join(", ") : "(blocked)"]]);
|
|
8749
|
+
}
|
|
8750
|
+
});
|
|
8751
|
+
return EXIT.SUCCESS;
|
|
8752
|
+
}
|
|
8753
|
+
function isAllowed(meshSlug, peerPubkey, cap) {
|
|
8754
|
+
const store = readGrants();
|
|
8755
|
+
const entry = store[meshSlug]?.[peerPubkey];
|
|
8756
|
+
if (entry === undefined)
|
|
8757
|
+
return DEFAULT_CAPS.includes(cap);
|
|
8758
|
+
return entry.includes(cap);
|
|
8759
|
+
}
|
|
8760
|
+
var ALL_CAPS, DEFAULT_CAPS, GRANT_FILE;
|
|
8761
|
+
var init_grants = __esm(() => {
|
|
8762
|
+
init_facade();
|
|
8763
|
+
init_connect();
|
|
8764
|
+
init_render();
|
|
8765
|
+
init_exit_codes();
|
|
8766
|
+
ALL_CAPS = ["read", "dm", "broadcast", "state-read", "state-write", "file-read"];
|
|
8767
|
+
DEFAULT_CAPS = ["read", "dm", "broadcast", "state-read"];
|
|
8768
|
+
GRANT_FILE = join9(homedir8(), ".claudemesh", "grants.json");
|
|
8769
|
+
});
|
|
8770
|
+
|
|
8413
8771
|
// src/mcp/tools/definitions.ts
|
|
8414
8772
|
var TOOLS;
|
|
8415
8773
|
var init_definitions = __esm(() => {
|
|
@@ -9796,13 +10154,13 @@ ${peerLines.join(`
|
|
|
9796
10154
|
}
|
|
9797
10155
|
}
|
|
9798
10156
|
try {
|
|
9799
|
-
const { writeFileSync:
|
|
10157
|
+
const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync6, existsSync: existsSync18 } = await import("node:fs");
|
|
9800
10158
|
const { join: joinPath } = await import("node:path");
|
|
9801
|
-
const { homedir:
|
|
9802
|
-
const dir = joinPath(
|
|
9803
|
-
if (!
|
|
9804
|
-
|
|
9805
|
-
|
|
10159
|
+
const { homedir: homedir9 } = await import("node:os");
|
|
10160
|
+
const dir = joinPath(homedir9(), ".claudemesh");
|
|
10161
|
+
if (!existsSync18(dir))
|
|
10162
|
+
mkdirSync6(dir, { recursive: true });
|
|
10163
|
+
writeFileSync10(joinPath(dir, "peer-cache.json"), JSON.stringify(statusCache));
|
|
9806
10164
|
} catch {}
|
|
9807
10165
|
return text(sections.join(`
|
|
9808
10166
|
|
|
@@ -10044,23 +10402,23 @@ ${lines.join(`
|
|
|
10044
10402
|
const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
|
|
10045
10403
|
if (!filePath)
|
|
10046
10404
|
return text("share_file: `path` required", true);
|
|
10047
|
-
const { existsSync:
|
|
10048
|
-
if (!
|
|
10405
|
+
const { existsSync: existsSync18 } = await import("node:fs");
|
|
10406
|
+
if (!existsSync18(filePath))
|
|
10049
10407
|
return text(`share_file: file not found: ${filePath}`, true);
|
|
10050
10408
|
const client = allClients()[0];
|
|
10051
10409
|
if (!client)
|
|
10052
10410
|
return text("share_file: not connected", true);
|
|
10053
10411
|
if (fileTo) {
|
|
10054
10412
|
const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
10055
|
-
const { readFileSync:
|
|
10413
|
+
const { readFileSync: readFileSync13, writeFileSync: writeFileSync10, mkdtempSync: mkdtempSync2, unlinkSync: unlinkSync2, rmdirSync } = await import("node:fs");
|
|
10056
10414
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
10057
|
-
const { join:
|
|
10415
|
+
const { join: join10, basename } = await import("node:path");
|
|
10058
10416
|
const peers = await client.listPeers();
|
|
10059
10417
|
const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
|
|
10060
10418
|
if (!targetPeer) {
|
|
10061
10419
|
return text(`share_file: peer not found: ${fileTo}`, true);
|
|
10062
10420
|
}
|
|
10063
|
-
const plaintext =
|
|
10421
|
+
const plaintext = readFileSync13(filePath);
|
|
10064
10422
|
const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
|
|
10065
10423
|
const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
|
|
10066
10424
|
const myPubkey = client.getSessionPubkey();
|
|
@@ -10077,9 +10435,9 @@ ${lines.join(`
|
|
|
10077
10435
|
combined.set(ciphertext, nonceBytes.length);
|
|
10078
10436
|
const rawName = fileName ?? basename(filePath);
|
|
10079
10437
|
const baseName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 255);
|
|
10080
|
-
const tmpDir = mkdtempSync2(
|
|
10081
|
-
const tmpPath =
|
|
10082
|
-
|
|
10438
|
+
const tmpDir = mkdtempSync2(join10(tmpdir2(), "cm-"));
|
|
10439
|
+
const tmpPath = join10(tmpDir, baseName);
|
|
10440
|
+
writeFileSync10(tmpPath, combined);
|
|
10083
10441
|
try {
|
|
10084
10442
|
const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
|
|
10085
10443
|
name: baseName,
|
|
@@ -10154,10 +10512,10 @@ ${lines.join(`
|
|
|
10154
10512
|
const plaintext = await decryptFile2(ciphertext, nonce, kf);
|
|
10155
10513
|
if (!plaintext)
|
|
10156
10514
|
return text(genericErr, true);
|
|
10157
|
-
const { writeFileSync:
|
|
10158
|
-
const { dirname:
|
|
10159
|
-
|
|
10160
|
-
|
|
10515
|
+
const { writeFileSync: writeFileSync11, mkdirSync: mkdirSync7 } = await import("node:fs");
|
|
10516
|
+
const { dirname: dirname5 } = await import("node:path");
|
|
10517
|
+
mkdirSync7(dirname5(save_to), { recursive: true });
|
|
10518
|
+
writeFileSync11(save_to, plaintext);
|
|
10161
10519
|
return text(`Downloaded and decrypted: ${result.name} → ${save_to}`);
|
|
10162
10520
|
}
|
|
10163
10521
|
let res = await fetch(result.url, { signal: AbortSignal.timeout(1e4) }).catch(() => null);
|
|
@@ -10167,10 +10525,10 @@ ${lines.join(`
|
|
|
10167
10525
|
}
|
|
10168
10526
|
if (!res.ok)
|
|
10169
10527
|
return text(`get_file: download failed (${res.status})`, true);
|
|
10170
|
-
const { writeFileSync:
|
|
10171
|
-
const { dirname:
|
|
10172
|
-
|
|
10173
|
-
|
|
10528
|
+
const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync6 } = await import("node:fs");
|
|
10529
|
+
const { dirname: dirname4 } = await import("node:path");
|
|
10530
|
+
mkdirSync6(dirname4(save_to), { recursive: true });
|
|
10531
|
+
writeFileSync10(save_to, Buffer.from(await res.arrayBuffer()));
|
|
10174
10532
|
return text(`Downloaded: ${result.name} → ${save_to}`);
|
|
10175
10533
|
}
|
|
10176
10534
|
case "list_files": {
|
|
@@ -10928,10 +11286,10 @@ ${lines.join(`
|
|
|
10928
11286
|
const entryType = vType ?? "env";
|
|
10929
11287
|
let plaintextBytes;
|
|
10930
11288
|
if (entryType === "file") {
|
|
10931
|
-
const { existsSync:
|
|
10932
|
-
if (!
|
|
11289
|
+
const { existsSync: existsSync18, readFileSync: readFileSync13 } = await import("node:fs");
|
|
11290
|
+
if (!existsSync18(value))
|
|
10933
11291
|
return text(`vault_set: file not found: ${value}`, true);
|
|
10934
|
-
plaintextBytes = new Uint8Array(
|
|
11292
|
+
plaintextBytes = new Uint8Array(readFileSync13(value));
|
|
10935
11293
|
} else {
|
|
10936
11294
|
plaintextBytes = new TextEncoder().encode(value);
|
|
10937
11295
|
}
|
|
@@ -11259,6 +11617,17 @@ ${lines.join(`
|
|
|
11259
11617
|
}
|
|
11260
11618
|
const fromPubkey = msg.senderPubkey || "";
|
|
11261
11619
|
const fromName = fromPubkey ? await resolvePeerName(client, fromPubkey) : "unknown";
|
|
11620
|
+
if (fromPubkey) {
|
|
11621
|
+
try {
|
|
11622
|
+
const { isAllowed: isAllowed2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
11623
|
+
const kindCap = msg.kind === "broadcast" ? "broadcast" : "dm";
|
|
11624
|
+
if (!isAllowed2(client.meshSlug, fromPubkey, kindCap)) {
|
|
11625
|
+
process.stderr.write(`[claudemesh] dropped ${kindCap} from ${fromName} (not granted)
|
|
11626
|
+
`);
|
|
11627
|
+
return;
|
|
11628
|
+
}
|
|
11629
|
+
} catch {}
|
|
11630
|
+
}
|
|
11262
11631
|
if (messageMode === "inbox") {
|
|
11263
11632
|
try {
|
|
11264
11633
|
await server.notification({
|
|
@@ -11734,22 +12103,6 @@ function renderVersion() {
|
|
|
11734
12103
|
return " " + boldOrange("claudemesh") + " v" + VERSION;
|
|
11735
12104
|
}
|
|
11736
12105
|
|
|
11737
|
-
// src/utils/url.ts
|
|
11738
|
-
function isInviteUrl(input) {
|
|
11739
|
-
return /^https?:\/\/[^/]+\/(?:[a-z]{2}\/)?i\//.test(input) || /^https?:\/\/[^/]+\/(?:[a-z]{2}\/)?join\//.test(input) || /^ic:\/\//.test(input) || /^claudemesh:\/\//.test(input);
|
|
11740
|
-
}
|
|
11741
|
-
function normaliseInviteUrl(input, host = "claudemesh.com") {
|
|
11742
|
-
const trimmed = input.trim();
|
|
11743
|
-
if (trimmed.startsWith("claudemesh://")) {
|
|
11744
|
-
const rest = trimmed.slice("claudemesh://".length).replace(/^\/+/, "");
|
|
11745
|
-
const m = rest.match(/^(?:i|join)\/(.+)$/);
|
|
11746
|
-
const tail = m ? m[1] : rest;
|
|
11747
|
-
const kind = rest.startsWith("join/") ? "join" : "i";
|
|
11748
|
-
return `https://${host}/${kind}/${tail}`;
|
|
11749
|
-
}
|
|
11750
|
-
return trimmed;
|
|
11751
|
-
}
|
|
11752
|
-
|
|
11753
12106
|
// src/entrypoints/cli.ts
|
|
11754
12107
|
installSignalHandlers();
|
|
11755
12108
|
installErrorHandlers();
|
|
@@ -11791,6 +12144,10 @@ Auth
|
|
|
11791
12144
|
|
|
11792
12145
|
Security
|
|
11793
12146
|
claudemesh verify [peer] show ed25519 safety numbers (SAS)
|
|
12147
|
+
claudemesh grant <peer> <cap> grant capability (dm, broadcast, state-read, all)
|
|
12148
|
+
claudemesh revoke <peer> <cap> revoke capability (or 'all')
|
|
12149
|
+
claudemesh block <peer> revoke all capabilities (silent drop)
|
|
12150
|
+
claudemesh grants list per-peer overrides for current mesh
|
|
11794
12151
|
claudemesh backup [file] encrypt config → portable recovery file
|
|
11795
12152
|
claudemesh restore <file> restore config from a backup file
|
|
11796
12153
|
|
|
@@ -11802,6 +12159,7 @@ Setup
|
|
|
11802
12159
|
claudemesh sync refresh mesh list from dashboard
|
|
11803
12160
|
claudemesh completions <shell> emit bash / zsh / fish completion script
|
|
11804
12161
|
claudemesh url-handler install register claudemesh:// click-to-launch
|
|
12162
|
+
claudemesh upgrade self-update to latest alpha (rustup-style)
|
|
11805
12163
|
|
|
11806
12164
|
Flags
|
|
11807
12165
|
--version, -V show version
|
|
@@ -12038,6 +12396,32 @@ async function main() {
|
|
|
12038
12396
|
process.exit(await runRestore2(positionals[0]));
|
|
12039
12397
|
break;
|
|
12040
12398
|
}
|
|
12399
|
+
case "upgrade":
|
|
12400
|
+
case "update": {
|
|
12401
|
+
const { runUpgrade: runUpgrade2 } = await Promise.resolve().then(() => (init_upgrade(), exports_upgrade));
|
|
12402
|
+
process.exit(await runUpgrade2({ check: !!flags.check, yes: !!flags.y || !!flags.yes }));
|
|
12403
|
+
break;
|
|
12404
|
+
}
|
|
12405
|
+
case "grant": {
|
|
12406
|
+
const { runGrant: runGrant2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
12407
|
+
process.exit(await runGrant2(positionals[0], positionals.slice(1), { mesh: flags.mesh }));
|
|
12408
|
+
break;
|
|
12409
|
+
}
|
|
12410
|
+
case "revoke": {
|
|
12411
|
+
const { runRevoke: runRevoke2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
12412
|
+
process.exit(await runRevoke2(positionals[0], positionals.slice(1), { mesh: flags.mesh }));
|
|
12413
|
+
break;
|
|
12414
|
+
}
|
|
12415
|
+
case "block": {
|
|
12416
|
+
const { runBlock: runBlock2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
12417
|
+
process.exit(await runBlock2(positionals[0], { mesh: flags.mesh }));
|
|
12418
|
+
break;
|
|
12419
|
+
}
|
|
12420
|
+
case "grants": {
|
|
12421
|
+
const { runGrants: runGrants2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
12422
|
+
process.exit(await runGrants2({ mesh: flags.mesh, json: !!flags.json }));
|
|
12423
|
+
break;
|
|
12424
|
+
}
|
|
12041
12425
|
case "mcp": {
|
|
12042
12426
|
const { runMcp: runMcp2 } = await Promise.resolve().then(() => (init_mcp(), exports_mcp));
|
|
12043
12427
|
await runMcp2();
|
|
@@ -12065,4 +12449,4 @@ main().catch((err) => {
|
|
|
12065
12449
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
12066
12450
|
});
|
|
12067
12451
|
|
|
12068
|
-
//# debugId=
|
|
12452
|
+
//# debugId=9DC7B5B7388FF04464756E2164756E21
|