claudemesh-cli 1.0.0-alpha.3 → 1.0.0-alpha.31
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 +8474 -6647
- package/dist/entrypoints/cli.js.map +71 -55
- package/dist/entrypoints/mcp.js +405 -22
- package/dist/entrypoints/mcp.js.map +9 -4
- package/package.json +23 -20
- package/LICENSE.md +0 -37
package/dist/entrypoints/mcp.js
CHANGED
|
@@ -3286,6 +3286,359 @@ var init_facade3 = __esm(() => {
|
|
|
3286
3286
|
init_errors();
|
|
3287
3287
|
});
|
|
3288
3288
|
|
|
3289
|
+
// src/commands/connect.ts
|
|
3290
|
+
import { hostname } from "node:os";
|
|
3291
|
+
import { createInterface } from "node:readline";
|
|
3292
|
+
async function pickMesh(meshes) {
|
|
3293
|
+
console.log(`
|
|
3294
|
+
Select mesh:`);
|
|
3295
|
+
meshes.forEach((m, i) => {
|
|
3296
|
+
console.log(` ${i + 1}) ${m.slug}`);
|
|
3297
|
+
});
|
|
3298
|
+
console.log("");
|
|
3299
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3300
|
+
return new Promise((resolve) => {
|
|
3301
|
+
rl.question(" Choice [1]: ", (answer) => {
|
|
3302
|
+
rl.close();
|
|
3303
|
+
const idx = parseInt(answer || "1", 10) - 1;
|
|
3304
|
+
if (idx >= 0 && idx < meshes.length) {
|
|
3305
|
+
resolve(meshes[idx]);
|
|
3306
|
+
} else {
|
|
3307
|
+
console.error(" Invalid choice, using first mesh.");
|
|
3308
|
+
resolve(meshes[0]);
|
|
3309
|
+
}
|
|
3310
|
+
});
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3313
|
+
async function withMesh(opts, fn) {
|
|
3314
|
+
const config = readConfig();
|
|
3315
|
+
if (config.meshes.length === 0) {
|
|
3316
|
+
console.error("No meshes joined. Run `claudemesh join <url>` first.");
|
|
3317
|
+
process.exit(1);
|
|
3318
|
+
}
|
|
3319
|
+
let mesh;
|
|
3320
|
+
if (opts.meshSlug) {
|
|
3321
|
+
const found = config.meshes.find((m) => m.slug === opts.meshSlug);
|
|
3322
|
+
if (!found) {
|
|
3323
|
+
console.error(`Mesh "${opts.meshSlug}" not found. Joined: ${config.meshes.map((m) => m.slug).join(", ")}`);
|
|
3324
|
+
process.exit(1);
|
|
3325
|
+
}
|
|
3326
|
+
mesh = found;
|
|
3327
|
+
} else if (config.meshes.length === 1) {
|
|
3328
|
+
mesh = config.meshes[0];
|
|
3329
|
+
} else {
|
|
3330
|
+
mesh = await pickMesh(config.meshes);
|
|
3331
|
+
}
|
|
3332
|
+
const displayName = opts.displayName ?? config.displayName ?? `${hostname()}-${process.pid}`;
|
|
3333
|
+
const client = new BrokerClient(mesh, { displayName });
|
|
3334
|
+
try {
|
|
3335
|
+
await client.connect();
|
|
3336
|
+
const result = await fn(client, mesh);
|
|
3337
|
+
return result;
|
|
3338
|
+
} finally {
|
|
3339
|
+
client.close();
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
var init_connect = __esm(() => {
|
|
3343
|
+
init_facade3();
|
|
3344
|
+
init_facade();
|
|
3345
|
+
});
|
|
3346
|
+
|
|
3347
|
+
// src/ui/styles.ts
|
|
3348
|
+
function moveTo(row, col) {
|
|
3349
|
+
return isTTY ? `\x1B[${row};${col}H` : "";
|
|
3350
|
+
}
|
|
3351
|
+
function visibleLength(s) {
|
|
3352
|
+
return s.replace(/\x1b\[[^m]*m/g, "").length;
|
|
3353
|
+
}
|
|
3354
|
+
var isTTY, esc = (code) => (s) => isTTY ? `${code}${s}\x1B[0m` : s, orange, clay, amber, bold, dim, green, yellow, red, cyan, boldOrange, HIDE_CURSOR, SHOW_CURSOR, CLEAR_SCREEN, CLEAR_LINE, icons;
|
|
3355
|
+
var init_styles = __esm(() => {
|
|
3356
|
+
isTTY = process.stdout.isTTY && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
3357
|
+
orange = esc("\x1B[38;5;208m");
|
|
3358
|
+
clay = esc("\x1B[38;5;173m");
|
|
3359
|
+
amber = esc("\x1B[38;5;214m");
|
|
3360
|
+
bold = esc("\x1B[1m");
|
|
3361
|
+
dim = esc("\x1B[2m");
|
|
3362
|
+
green = esc("\x1B[32m");
|
|
3363
|
+
yellow = esc("\x1B[33m");
|
|
3364
|
+
red = esc("\x1B[31m");
|
|
3365
|
+
cyan = esc("\x1B[36m");
|
|
3366
|
+
boldOrange = esc("\x1B[1m\x1B[38;5;208m");
|
|
3367
|
+
HIDE_CURSOR = isTTY ? "\x1B[?25l" : "";
|
|
3368
|
+
SHOW_CURSOR = isTTY ? "\x1B[?25h" : "";
|
|
3369
|
+
CLEAR_SCREEN = isTTY ? "\x1B[2J\x1B[H" : "";
|
|
3370
|
+
CLEAR_LINE = isTTY ? "\x1B[K" : "";
|
|
3371
|
+
icons = {
|
|
3372
|
+
check: "✔",
|
|
3373
|
+
cross: "✘",
|
|
3374
|
+
warn: "⚠",
|
|
3375
|
+
arrow: "→",
|
|
3376
|
+
bullet: "●",
|
|
3377
|
+
dash: "—",
|
|
3378
|
+
ellipsis: "…"
|
|
3379
|
+
};
|
|
3380
|
+
});
|
|
3381
|
+
|
|
3382
|
+
// src/ui/render.ts
|
|
3383
|
+
var OUT, ERR, INDENT = " ", render;
|
|
3384
|
+
var init_render = __esm(() => {
|
|
3385
|
+
init_styles();
|
|
3386
|
+
OUT = process.stdout;
|
|
3387
|
+
ERR = process.stderr;
|
|
3388
|
+
render = {
|
|
3389
|
+
blank() {
|
|
3390
|
+
OUT.write(`
|
|
3391
|
+
`);
|
|
3392
|
+
},
|
|
3393
|
+
ok(msg, detail) {
|
|
3394
|
+
const d = detail ? ` ${dim("(" + detail + ")")}` : "";
|
|
3395
|
+
OUT.write(`${INDENT}${green(icons.check)} ${msg}${d}
|
|
3396
|
+
`);
|
|
3397
|
+
},
|
|
3398
|
+
warn(msg, hint) {
|
|
3399
|
+
OUT.write(`${INDENT}${yellow(icons.warn)} ${msg}
|
|
3400
|
+
`);
|
|
3401
|
+
if (hint)
|
|
3402
|
+
OUT.write(`${INDENT} ${dim(hint)}
|
|
3403
|
+
`);
|
|
3404
|
+
},
|
|
3405
|
+
err(msg, hint) {
|
|
3406
|
+
ERR.write(`${INDENT}${red(icons.cross)} ${msg}
|
|
3407
|
+
`);
|
|
3408
|
+
if (hint)
|
|
3409
|
+
ERR.write(`${INDENT} ${dim(hint)}
|
|
3410
|
+
`);
|
|
3411
|
+
},
|
|
3412
|
+
info(msg) {
|
|
3413
|
+
OUT.write(`${INDENT}${msg}
|
|
3414
|
+
`);
|
|
3415
|
+
},
|
|
3416
|
+
section(title) {
|
|
3417
|
+
OUT.write(`
|
|
3418
|
+
${INDENT}${dim("—")} ${clay(title)}
|
|
3419
|
+
|
|
3420
|
+
`);
|
|
3421
|
+
},
|
|
3422
|
+
heading(title) {
|
|
3423
|
+
OUT.write(`${INDENT}${bold(title)}
|
|
3424
|
+
`);
|
|
3425
|
+
},
|
|
3426
|
+
kv(pairs, opts) {
|
|
3427
|
+
const pad = opts?.padTo ?? Math.max(...pairs.map(([k]) => k.length)) + 2;
|
|
3428
|
+
for (const [k, v] of pairs) {
|
|
3429
|
+
OUT.write(`${INDENT}${dim(k.padEnd(pad, " "))}${v}
|
|
3430
|
+
`);
|
|
3431
|
+
}
|
|
3432
|
+
},
|
|
3433
|
+
code(snippet) {
|
|
3434
|
+
for (const line of snippet.split(`
|
|
3435
|
+
`)) {
|
|
3436
|
+
OUT.write(`${INDENT} ${cyan(line)}
|
|
3437
|
+
`);
|
|
3438
|
+
}
|
|
3439
|
+
},
|
|
3440
|
+
link(url) {
|
|
3441
|
+
OUT.write(`${INDENT}${clay(url)}
|
|
3442
|
+
`);
|
|
3443
|
+
},
|
|
3444
|
+
hint(msg) {
|
|
3445
|
+
OUT.write(`${INDENT}${dim(icons.arrow + " " + msg)}
|
|
3446
|
+
`);
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3449
|
+
});
|
|
3450
|
+
|
|
3451
|
+
// src/constants/exit-codes.ts
|
|
3452
|
+
var EXIT;
|
|
3453
|
+
var init_exit_codes = __esm(() => {
|
|
3454
|
+
EXIT = {
|
|
3455
|
+
SUCCESS: 0,
|
|
3456
|
+
USER_CANCELLED: 1,
|
|
3457
|
+
AUTH_FAILED: 2,
|
|
3458
|
+
INVALID_ARGS: 3,
|
|
3459
|
+
NETWORK_ERROR: 4,
|
|
3460
|
+
NOT_FOUND: 5,
|
|
3461
|
+
ALREADY_EXISTS: 6,
|
|
3462
|
+
PERMISSION_DENIED: 7,
|
|
3463
|
+
INTERNAL_ERROR: 8,
|
|
3464
|
+
CLAUDE_MISSING: 9
|
|
3465
|
+
};
|
|
3466
|
+
});
|
|
3467
|
+
|
|
3468
|
+
// src/commands/grants.ts
|
|
3469
|
+
var exports_grants = {};
|
|
3470
|
+
__export(exports_grants, {
|
|
3471
|
+
runRevoke: () => runRevoke,
|
|
3472
|
+
runGrants: () => runGrants,
|
|
3473
|
+
runGrant: () => runGrant,
|
|
3474
|
+
runBlock: () => runBlock,
|
|
3475
|
+
isAllowed: () => isAllowed
|
|
3476
|
+
});
|
|
3477
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3478
|
+
import { homedir as homedir2 } from "node:os";
|
|
3479
|
+
import { join as join2 } from "node:path";
|
|
3480
|
+
function readGrants() {
|
|
3481
|
+
if (!existsSync2(GRANT_FILE))
|
|
3482
|
+
return {};
|
|
3483
|
+
try {
|
|
3484
|
+
return JSON.parse(readFileSync2(GRANT_FILE, "utf-8"));
|
|
3485
|
+
} catch {
|
|
3486
|
+
return {};
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
function writeGrants(g) {
|
|
3490
|
+
const dir = join2(homedir2(), ".claudemesh");
|
|
3491
|
+
if (!existsSync2(dir))
|
|
3492
|
+
mkdirSync2(dir, { recursive: true });
|
|
3493
|
+
writeFileSync2(GRANT_FILE, JSON.stringify(g, null, 2), { mode: 384 });
|
|
3494
|
+
}
|
|
3495
|
+
function resolveCaps(input) {
|
|
3496
|
+
if (input.includes("all"))
|
|
3497
|
+
return [...ALL_CAPS];
|
|
3498
|
+
return input.filter((c) => ALL_CAPS.includes(c));
|
|
3499
|
+
}
|
|
3500
|
+
async function resolvePeer(meshSlug, name) {
|
|
3501
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
3502
|
+
const peers = await client.listPeers();
|
|
3503
|
+
const match = peers.find((p) => p.displayName === name || p.pubkey === name || p.pubkey.startsWith(name));
|
|
3504
|
+
return match ? { displayName: match.displayName, pubkey: match.pubkey } : null;
|
|
3505
|
+
});
|
|
3506
|
+
}
|
|
3507
|
+
function pickMesh2(slug) {
|
|
3508
|
+
const cfg = readConfig();
|
|
3509
|
+
if (slug)
|
|
3510
|
+
return cfg.meshes.find((m) => m.slug === slug) ? slug : null;
|
|
3511
|
+
return cfg.meshes[0]?.slug ?? null;
|
|
3512
|
+
}
|
|
3513
|
+
async function runGrant(peer, caps, opts = {}) {
|
|
3514
|
+
if (!peer || caps.length === 0) {
|
|
3515
|
+
render.err("Usage: claudemesh grant <peer> <capability...>");
|
|
3516
|
+
render.hint(`Capabilities: ${ALL_CAPS.join(", ")}, all`);
|
|
3517
|
+
return EXIT.INVALID_ARGS;
|
|
3518
|
+
}
|
|
3519
|
+
const mesh = pickMesh2(opts.mesh);
|
|
3520
|
+
if (!mesh) {
|
|
3521
|
+
render.err("No matching mesh — join one first.");
|
|
3522
|
+
return EXIT.NOT_FOUND;
|
|
3523
|
+
}
|
|
3524
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
3525
|
+
if (!resolved) {
|
|
3526
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
3527
|
+
return EXIT.NOT_FOUND;
|
|
3528
|
+
}
|
|
3529
|
+
const wanted = resolveCaps(caps);
|
|
3530
|
+
if (wanted.length === 0) {
|
|
3531
|
+
render.err(`Unknown capabilities: ${caps.join(", ")}`);
|
|
3532
|
+
return EXIT.INVALID_ARGS;
|
|
3533
|
+
}
|
|
3534
|
+
const store = readGrants();
|
|
3535
|
+
const meshGrants = store[mesh] ?? {};
|
|
3536
|
+
const existing = meshGrants[resolved.pubkey] ?? DEFAULT_CAPS.slice();
|
|
3537
|
+
const merged = Array.from(new Set([...existing, ...wanted]));
|
|
3538
|
+
meshGrants[resolved.pubkey] = merged;
|
|
3539
|
+
store[mesh] = meshGrants;
|
|
3540
|
+
writeGrants(store);
|
|
3541
|
+
render.ok(`Granted ${wanted.join(", ")} to ${resolved.displayName} on ${mesh}.`);
|
|
3542
|
+
render.kv([["now", merged.join(", ")]]);
|
|
3543
|
+
return EXIT.SUCCESS;
|
|
3544
|
+
}
|
|
3545
|
+
async function runRevoke(peer, caps, opts = {}) {
|
|
3546
|
+
if (!peer || caps.length === 0) {
|
|
3547
|
+
render.err("Usage: claudemesh revoke <peer> <capability...>");
|
|
3548
|
+
return EXIT.INVALID_ARGS;
|
|
3549
|
+
}
|
|
3550
|
+
const mesh = pickMesh2(opts.mesh);
|
|
3551
|
+
if (!mesh) {
|
|
3552
|
+
render.err("No matching mesh.");
|
|
3553
|
+
return EXIT.NOT_FOUND;
|
|
3554
|
+
}
|
|
3555
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
3556
|
+
if (!resolved) {
|
|
3557
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
3558
|
+
return EXIT.NOT_FOUND;
|
|
3559
|
+
}
|
|
3560
|
+
const wanted = caps.includes("all") ? ALL_CAPS.slice() : resolveCaps(caps);
|
|
3561
|
+
const store = readGrants();
|
|
3562
|
+
const meshGrants = store[mesh] ?? {};
|
|
3563
|
+
const existing = meshGrants[resolved.pubkey] ?? DEFAULT_CAPS.slice();
|
|
3564
|
+
const after = existing.filter((c) => !wanted.includes(c));
|
|
3565
|
+
meshGrants[resolved.pubkey] = after;
|
|
3566
|
+
store[mesh] = meshGrants;
|
|
3567
|
+
writeGrants(store);
|
|
3568
|
+
render.ok(`Revoked ${wanted.join(", ")} from ${resolved.displayName} on ${mesh}.`);
|
|
3569
|
+
render.kv([["now", after.length ? after.join(", ") : "(none)"]]);
|
|
3570
|
+
return EXIT.SUCCESS;
|
|
3571
|
+
}
|
|
3572
|
+
async function runBlock(peer, opts = {}) {
|
|
3573
|
+
if (!peer) {
|
|
3574
|
+
render.err("Usage: claudemesh block <peer>");
|
|
3575
|
+
return EXIT.INVALID_ARGS;
|
|
3576
|
+
}
|
|
3577
|
+
const mesh = pickMesh2(opts.mesh);
|
|
3578
|
+
if (!mesh) {
|
|
3579
|
+
render.err("No matching mesh.");
|
|
3580
|
+
return EXIT.NOT_FOUND;
|
|
3581
|
+
}
|
|
3582
|
+
const resolved = await resolvePeer(mesh, peer);
|
|
3583
|
+
if (!resolved) {
|
|
3584
|
+
render.err(`Peer "${peer}" not found on ${mesh}.`);
|
|
3585
|
+
return EXIT.NOT_FOUND;
|
|
3586
|
+
}
|
|
3587
|
+
const store = readGrants();
|
|
3588
|
+
const meshGrants = store[mesh] ?? {};
|
|
3589
|
+
meshGrants[resolved.pubkey] = [];
|
|
3590
|
+
store[mesh] = meshGrants;
|
|
3591
|
+
writeGrants(store);
|
|
3592
|
+
render.ok(`Blocked ${resolved.displayName} on ${mesh} (all capabilities revoked).`);
|
|
3593
|
+
render.hint(`Undo with: claudemesh grant ${resolved.displayName} all --mesh ${mesh}`);
|
|
3594
|
+
return EXIT.SUCCESS;
|
|
3595
|
+
}
|
|
3596
|
+
async function runGrants(opts = {}) {
|
|
3597
|
+
const mesh = pickMesh2(opts.mesh);
|
|
3598
|
+
if (!mesh) {
|
|
3599
|
+
render.err("No matching mesh.");
|
|
3600
|
+
return EXIT.NOT_FOUND;
|
|
3601
|
+
}
|
|
3602
|
+
const store = readGrants();
|
|
3603
|
+
const meshGrants = store[mesh] ?? {};
|
|
3604
|
+
if (opts.json) {
|
|
3605
|
+
console.log(JSON.stringify({ schema_version: "1.0", mesh, grants: meshGrants }, null, 2));
|
|
3606
|
+
return EXIT.SUCCESS;
|
|
3607
|
+
}
|
|
3608
|
+
render.section(`grants on ${mesh}`);
|
|
3609
|
+
const peerPubkeys = Object.keys(meshGrants);
|
|
3610
|
+
if (peerPubkeys.length === 0) {
|
|
3611
|
+
render.info("(no overrides — all peers use default caps: " + DEFAULT_CAPS.join(", ") + ")");
|
|
3612
|
+
return EXIT.SUCCESS;
|
|
3613
|
+
}
|
|
3614
|
+
await withMesh({ meshSlug: mesh }, async (client) => {
|
|
3615
|
+
const peers = await client.listPeers();
|
|
3616
|
+
const byPk = new Map(peers.map((p) => [p.pubkey, p.displayName]));
|
|
3617
|
+
for (const [pk, caps] of Object.entries(meshGrants)) {
|
|
3618
|
+
const name = byPk.get(pk) ?? `${pk.slice(0, 10)}…`;
|
|
3619
|
+
render.kv([[name, caps.length ? caps.join(", ") : "(blocked)"]]);
|
|
3620
|
+
}
|
|
3621
|
+
});
|
|
3622
|
+
return EXIT.SUCCESS;
|
|
3623
|
+
}
|
|
3624
|
+
function isAllowed(meshSlug, peerPubkey, cap) {
|
|
3625
|
+
const store = readGrants();
|
|
3626
|
+
const entry = store[meshSlug]?.[peerPubkey];
|
|
3627
|
+
if (entry === undefined)
|
|
3628
|
+
return DEFAULT_CAPS.includes(cap);
|
|
3629
|
+
return entry.includes(cap);
|
|
3630
|
+
}
|
|
3631
|
+
var ALL_CAPS, DEFAULT_CAPS, GRANT_FILE;
|
|
3632
|
+
var init_grants = __esm(() => {
|
|
3633
|
+
init_facade();
|
|
3634
|
+
init_connect();
|
|
3635
|
+
init_render();
|
|
3636
|
+
init_exit_codes();
|
|
3637
|
+
ALL_CAPS = ["read", "dm", "broadcast", "state-read", "state-write", "file-read"];
|
|
3638
|
+
DEFAULT_CAPS = ["read", "dm", "broadcast", "state-read"];
|
|
3639
|
+
GRANT_FILE = join2(homedir2(), ".claudemesh", "grants.json");
|
|
3640
|
+
});
|
|
3641
|
+
|
|
3289
3642
|
// src/mcp/server.ts
|
|
3290
3643
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3291
3644
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -3710,9 +4063,16 @@ ${manifest.allowed_tools.map((t) => ` - ${t}`).join(`
|
|
|
3710
4063
|
if (clients2.length === 0)
|
|
3711
4064
|
return text(mesh_slug ? `list_peers: no joined mesh "${mesh_slug}"` : "list_peers: no joined meshes", true);
|
|
3712
4065
|
const sections = [];
|
|
4066
|
+
const statusCache = {};
|
|
3713
4067
|
for (const c of clients2) {
|
|
3714
4068
|
const peers = await c.listPeers();
|
|
3715
4069
|
const header = `## ${c.meshSlug} (${c.status}, mesh ${c.meshId.slice(0, 8)}…)`;
|
|
4070
|
+
statusCache[c.meshSlug] = {
|
|
4071
|
+
total: peers.length,
|
|
4072
|
+
online: peers.filter((p) => p.status !== "offline").length,
|
|
4073
|
+
updatedAt: new Date().toISOString(),
|
|
4074
|
+
you: process.env.CLAUDEMESH_DISPLAY_NAME ?? undefined
|
|
4075
|
+
};
|
|
3716
4076
|
if (peers.length === 0) {
|
|
3717
4077
|
sections.push(`${header}
|
|
3718
4078
|
No peers connected.`);
|
|
@@ -3741,6 +4101,15 @@ ${peerLines.join(`
|
|
|
3741
4101
|
`)}`);
|
|
3742
4102
|
}
|
|
3743
4103
|
}
|
|
4104
|
+
try {
|
|
4105
|
+
const { writeFileSync: writeFileSync3, mkdirSync: mkdirSync3, existsSync: existsSync3 } = await import("node:fs");
|
|
4106
|
+
const { join: joinPath } = await import("node:path");
|
|
4107
|
+
const { homedir: homedir3 } = await import("node:os");
|
|
4108
|
+
const dir = joinPath(homedir3(), ".claudemesh");
|
|
4109
|
+
if (!existsSync3(dir))
|
|
4110
|
+
mkdirSync3(dir, { recursive: true });
|
|
4111
|
+
writeFileSync3(joinPath(dir, "peer-cache.json"), JSON.stringify(statusCache));
|
|
4112
|
+
} catch {}
|
|
3744
4113
|
return text(sections.join(`
|
|
3745
4114
|
|
|
3746
4115
|
`));
|
|
@@ -3762,8 +4131,8 @@ ${peerLines.join(`
|
|
|
3762
4131
|
return text(`Message ${id} not found or timed out.`);
|
|
3763
4132
|
const recipientLines = result.recipients.map((r) => ` - ${r.name} (${r.pubkey.slice(0, 12)}…): ${r.status}`);
|
|
3764
4133
|
return text(`Message ${id.slice(0, 12)}… → ${result.targetSpec}
|
|
3765
|
-
|
|
3766
|
-
|
|
4134
|
+
Delivered: ${result.delivered}${result.deliveredAt ? ` at ${result.deliveredAt}` : ""}
|
|
4135
|
+
Recipients:
|
|
3767
4136
|
${recipientLines.join(`
|
|
3768
4137
|
`)}`);
|
|
3769
4138
|
}
|
|
@@ -3981,23 +4350,23 @@ ${lines.join(`
|
|
|
3981
4350
|
const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
|
|
3982
4351
|
if (!filePath)
|
|
3983
4352
|
return text("share_file: `path` required", true);
|
|
3984
|
-
const { existsSync:
|
|
3985
|
-
if (!
|
|
4353
|
+
const { existsSync: existsSync3 } = await import("node:fs");
|
|
4354
|
+
if (!existsSync3(filePath))
|
|
3986
4355
|
return text(`share_file: file not found: ${filePath}`, true);
|
|
3987
4356
|
const client = allClients()[0];
|
|
3988
4357
|
if (!client)
|
|
3989
4358
|
return text("share_file: not connected", true);
|
|
3990
4359
|
if (fileTo) {
|
|
3991
4360
|
const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
3992
|
-
const { readFileSync:
|
|
4361
|
+
const { readFileSync: readFileSync3, writeFileSync: writeFileSync3, mkdtempSync, unlinkSync, rmdirSync } = await import("node:fs");
|
|
3993
4362
|
const { tmpdir } = await import("node:os");
|
|
3994
|
-
const { join:
|
|
4363
|
+
const { join: join3, basename } = await import("node:path");
|
|
3995
4364
|
const peers = await client.listPeers();
|
|
3996
4365
|
const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
|
|
3997
4366
|
if (!targetPeer) {
|
|
3998
4367
|
return text(`share_file: peer not found: ${fileTo}`, true);
|
|
3999
4368
|
}
|
|
4000
|
-
const plaintext =
|
|
4369
|
+
const plaintext = readFileSync3(filePath);
|
|
4001
4370
|
const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
|
|
4002
4371
|
const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
|
|
4003
4372
|
const myPubkey = client.getSessionPubkey();
|
|
@@ -4014,9 +4383,9 @@ ${lines.join(`
|
|
|
4014
4383
|
combined.set(ciphertext, nonceBytes.length);
|
|
4015
4384
|
const rawName = fileName ?? basename(filePath);
|
|
4016
4385
|
const baseName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 255);
|
|
4017
|
-
const tmpDir = mkdtempSync(
|
|
4018
|
-
const tmpPath =
|
|
4019
|
-
|
|
4386
|
+
const tmpDir = mkdtempSync(join3(tmpdir(), "cm-"));
|
|
4387
|
+
const tmpPath = join3(tmpDir, baseName);
|
|
4388
|
+
writeFileSync3(tmpPath, combined);
|
|
4020
4389
|
try {
|
|
4021
4390
|
const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
|
|
4022
4391
|
name: baseName,
|
|
@@ -4091,10 +4460,10 @@ ${lines.join(`
|
|
|
4091
4460
|
const plaintext = await decryptFile2(ciphertext, nonce, kf);
|
|
4092
4461
|
if (!plaintext)
|
|
4093
4462
|
return text(genericErr, true);
|
|
4094
|
-
const { writeFileSync:
|
|
4463
|
+
const { writeFileSync: writeFileSync4, mkdirSync: mkdirSync4 } = await import("node:fs");
|
|
4095
4464
|
const { dirname: dirname2 } = await import("node:path");
|
|
4096
|
-
|
|
4097
|
-
|
|
4465
|
+
mkdirSync4(dirname2(save_to), { recursive: true });
|
|
4466
|
+
writeFileSync4(save_to, plaintext);
|
|
4098
4467
|
return text(`Downloaded and decrypted: ${result.name} → ${save_to}`);
|
|
4099
4468
|
}
|
|
4100
4469
|
let res = await fetch(result.url, { signal: AbortSignal.timeout(1e4) }).catch(() => null);
|
|
@@ -4104,10 +4473,10 @@ ${lines.join(`
|
|
|
4104
4473
|
}
|
|
4105
4474
|
if (!res.ok)
|
|
4106
4475
|
return text(`get_file: download failed (${res.status})`, true);
|
|
4107
|
-
const { writeFileSync:
|
|
4476
|
+
const { writeFileSync: writeFileSync3, mkdirSync: mkdirSync3 } = await import("node:fs");
|
|
4108
4477
|
const { dirname } = await import("node:path");
|
|
4109
|
-
|
|
4110
|
-
|
|
4478
|
+
mkdirSync3(dirname(save_to), { recursive: true });
|
|
4479
|
+
writeFileSync3(save_to, Buffer.from(await res.arrayBuffer()));
|
|
4111
4480
|
return text(`Downloaded: ${result.name} → ${save_to}`);
|
|
4112
4481
|
}
|
|
4113
4482
|
case "list_files": {
|
|
@@ -4865,10 +5234,10 @@ ${lines.join(`
|
|
|
4865
5234
|
const entryType = vType ?? "env";
|
|
4866
5235
|
let plaintextBytes;
|
|
4867
5236
|
if (entryType === "file") {
|
|
4868
|
-
const { existsSync:
|
|
4869
|
-
if (!
|
|
5237
|
+
const { existsSync: existsSync3, readFileSync: readFileSync3 } = await import("node:fs");
|
|
5238
|
+
if (!existsSync3(value))
|
|
4870
5239
|
return text(`vault_set: file not found: ${value}`, true);
|
|
4871
|
-
plaintextBytes = new Uint8Array(
|
|
5240
|
+
plaintextBytes = new Uint8Array(readFileSync3(value));
|
|
4872
5241
|
} else {
|
|
4873
5242
|
plaintextBytes = new TextEncoder().encode(value);
|
|
4874
5243
|
}
|
|
@@ -5196,6 +5565,17 @@ ${lines.join(`
|
|
|
5196
5565
|
}
|
|
5197
5566
|
const fromPubkey = msg.senderPubkey || "";
|
|
5198
5567
|
const fromName = fromPubkey ? await resolvePeerName(client, fromPubkey) : "unknown";
|
|
5568
|
+
if (fromPubkey) {
|
|
5569
|
+
try {
|
|
5570
|
+
const { isAllowed: isAllowed2 } = await Promise.resolve().then(() => (init_grants(), exports_grants));
|
|
5571
|
+
const kindCap = msg.kind === "broadcast" ? "broadcast" : "dm";
|
|
5572
|
+
if (!isAllowed2(client.meshSlug, fromPubkey, kindCap)) {
|
|
5573
|
+
process.stderr.write(`[claudemesh] dropped ${kindCap} from ${fromName} (not granted)
|
|
5574
|
+
`);
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
} catch {}
|
|
5578
|
+
}
|
|
5199
5579
|
if (messageMode === "inbox") {
|
|
5200
5580
|
try {
|
|
5201
5581
|
await server.notification({
|
|
@@ -5208,7 +5588,10 @@ ${lines.join(`
|
|
|
5208
5588
|
} catch {}
|
|
5209
5589
|
return;
|
|
5210
5590
|
}
|
|
5211
|
-
const
|
|
5591
|
+
const body = msg.plaintext ?? decryptFailedWarning(fromPubkey);
|
|
5592
|
+
const prioBadge = msg.priority === "now" ? "[URGENT] " : msg.priority === "low" ? "[low] " : "";
|
|
5593
|
+
const kindBadge = msg.kind === "broadcast" ? " (broadcast)" : "";
|
|
5594
|
+
const content = `${prioBadge}${fromName}${kindBadge}: ${body}`;
|
|
5212
5595
|
try {
|
|
5213
5596
|
await server.notification({
|
|
5214
5597
|
method: "notifications/claude/channel",
|
|
@@ -5227,7 +5610,7 @@ ${lines.join(`
|
|
|
5227
5610
|
}
|
|
5228
5611
|
}
|
|
5229
5612
|
});
|
|
5230
|
-
process.stderr.write(`[claudemesh] pushed: from=${fromName} content=${
|
|
5613
|
+
process.stderr.write(`[claudemesh] pushed: from=${fromName} content=${body.slice(0, 60)}
|
|
5231
5614
|
`);
|
|
5232
5615
|
} catch (pushErr) {
|
|
5233
5616
|
process.stderr.write(`[claudemesh] push FAILED: ${pushErr}
|
|
@@ -5421,4 +5804,4 @@ startMcpServer().catch((err) => {
|
|
|
5421
5804
|
process.exit(1);
|
|
5422
5805
|
});
|
|
5423
5806
|
|
|
5424
|
-
//# debugId=
|
|
5807
|
+
//# debugId=89A595C2EEA0442364756E2164756E21
|