claudemesh-cli 0.3.0 → 0.4.0
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 +290 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -46250,8 +46250,11 @@ var TOOLS = [
|
|
|
46250
46250
|
type: "object",
|
|
46251
46251
|
properties: {
|
|
46252
46252
|
to: {
|
|
46253
|
-
|
|
46254
|
-
|
|
46253
|
+
oneOf: [
|
|
46254
|
+
{ type: "string", description: "Peer name, pubkey, @group" },
|
|
46255
|
+
{ type: "array", items: { type: "string" }, description: "Multiple targets" }
|
|
46256
|
+
],
|
|
46257
|
+
description: "Single target or array of targets"
|
|
46255
46258
|
},
|
|
46256
46259
|
message: { type: "string", description: "Message text" },
|
|
46257
46260
|
priority: {
|
|
@@ -46415,6 +46418,74 @@ var TOOLS = [
|
|
|
46415
46418
|
},
|
|
46416
46419
|
required: ["id"]
|
|
46417
46420
|
}
|
|
46421
|
+
},
|
|
46422
|
+
{
|
|
46423
|
+
name: "share_file",
|
|
46424
|
+
description: "Share a persistent file with the mesh. All current and future peers can access it.",
|
|
46425
|
+
inputSchema: {
|
|
46426
|
+
type: "object",
|
|
46427
|
+
properties: {
|
|
46428
|
+
path: { type: "string", description: "Local file path to share" },
|
|
46429
|
+
name: {
|
|
46430
|
+
type: "string",
|
|
46431
|
+
description: "Display name (defaults to filename)"
|
|
46432
|
+
},
|
|
46433
|
+
tags: {
|
|
46434
|
+
type: "array",
|
|
46435
|
+
items: { type: "string" },
|
|
46436
|
+
description: "Tags for categorization"
|
|
46437
|
+
}
|
|
46438
|
+
},
|
|
46439
|
+
required: ["path"]
|
|
46440
|
+
}
|
|
46441
|
+
},
|
|
46442
|
+
{
|
|
46443
|
+
name: "get_file",
|
|
46444
|
+
description: "Download a shared file to a local path.",
|
|
46445
|
+
inputSchema: {
|
|
46446
|
+
type: "object",
|
|
46447
|
+
properties: {
|
|
46448
|
+
id: { type: "string", description: "File ID" },
|
|
46449
|
+
save_to: {
|
|
46450
|
+
type: "string",
|
|
46451
|
+
description: "Local path to save the file"
|
|
46452
|
+
}
|
|
46453
|
+
},
|
|
46454
|
+
required: ["id", "save_to"]
|
|
46455
|
+
}
|
|
46456
|
+
},
|
|
46457
|
+
{
|
|
46458
|
+
name: "list_files",
|
|
46459
|
+
description: "List files shared in the mesh.",
|
|
46460
|
+
inputSchema: {
|
|
46461
|
+
type: "object",
|
|
46462
|
+
properties: {
|
|
46463
|
+
query: { type: "string", description: "Search by name or tags" },
|
|
46464
|
+
from: { type: "string", description: "Filter by uploader name" }
|
|
46465
|
+
}
|
|
46466
|
+
}
|
|
46467
|
+
},
|
|
46468
|
+
{
|
|
46469
|
+
name: "file_status",
|
|
46470
|
+
description: "Check who has accessed a shared file.",
|
|
46471
|
+
inputSchema: {
|
|
46472
|
+
type: "object",
|
|
46473
|
+
properties: {
|
|
46474
|
+
id: { type: "string", description: "File ID" }
|
|
46475
|
+
},
|
|
46476
|
+
required: ["id"]
|
|
46477
|
+
}
|
|
46478
|
+
},
|
|
46479
|
+
{
|
|
46480
|
+
name: "delete_file",
|
|
46481
|
+
description: "Remove a shared file from the mesh.",
|
|
46482
|
+
inputSchema: {
|
|
46483
|
+
type: "object",
|
|
46484
|
+
properties: {
|
|
46485
|
+
id: { type: "string", description: "File ID" }
|
|
46486
|
+
},
|
|
46487
|
+
required: ["id"]
|
|
46488
|
+
}
|
|
46418
46489
|
}
|
|
46419
46490
|
];
|
|
46420
46491
|
|
|
@@ -46785,6 +46856,9 @@ class BrokerClient {
|
|
|
46785
46856
|
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
46786
46857
|
}
|
|
46787
46858
|
messageStatusResolvers = [];
|
|
46859
|
+
fileUrlResolvers = [];
|
|
46860
|
+
fileListResolvers = [];
|
|
46861
|
+
fileStatusResolvers = [];
|
|
46788
46862
|
async messageStatus(messageId) {
|
|
46789
46863
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46790
46864
|
return null;
|
|
@@ -46800,6 +46874,79 @@ class BrokerClient {
|
|
|
46800
46874
|
}, 5000);
|
|
46801
46875
|
});
|
|
46802
46876
|
}
|
|
46877
|
+
async getFile(fileId) {
|
|
46878
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46879
|
+
return null;
|
|
46880
|
+
return new Promise((resolve) => {
|
|
46881
|
+
this.fileUrlResolvers.push(resolve);
|
|
46882
|
+
this.ws.send(JSON.stringify({ type: "get_file", fileId }));
|
|
46883
|
+
setTimeout(() => {
|
|
46884
|
+
const idx = this.fileUrlResolvers.indexOf(resolve);
|
|
46885
|
+
if (idx !== -1) {
|
|
46886
|
+
this.fileUrlResolvers.splice(idx, 1);
|
|
46887
|
+
resolve(null);
|
|
46888
|
+
}
|
|
46889
|
+
}, 5000);
|
|
46890
|
+
});
|
|
46891
|
+
}
|
|
46892
|
+
async listFiles(query, from) {
|
|
46893
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46894
|
+
return [];
|
|
46895
|
+
return new Promise((resolve) => {
|
|
46896
|
+
this.fileListResolvers.push(resolve);
|
|
46897
|
+
this.ws.send(JSON.stringify({ type: "list_files", query, from }));
|
|
46898
|
+
setTimeout(() => {
|
|
46899
|
+
const idx = this.fileListResolvers.indexOf(resolve);
|
|
46900
|
+
if (idx !== -1) {
|
|
46901
|
+
this.fileListResolvers.splice(idx, 1);
|
|
46902
|
+
resolve([]);
|
|
46903
|
+
}
|
|
46904
|
+
}, 5000);
|
|
46905
|
+
});
|
|
46906
|
+
}
|
|
46907
|
+
async fileStatus(fileId) {
|
|
46908
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46909
|
+
return [];
|
|
46910
|
+
return new Promise((resolve) => {
|
|
46911
|
+
this.fileStatusResolvers.push(resolve);
|
|
46912
|
+
this.ws.send(JSON.stringify({ type: "file_status", fileId }));
|
|
46913
|
+
setTimeout(() => {
|
|
46914
|
+
const idx = this.fileStatusResolvers.indexOf(resolve);
|
|
46915
|
+
if (idx !== -1) {
|
|
46916
|
+
this.fileStatusResolvers.splice(idx, 1);
|
|
46917
|
+
resolve([]);
|
|
46918
|
+
}
|
|
46919
|
+
}, 5000);
|
|
46920
|
+
});
|
|
46921
|
+
}
|
|
46922
|
+
async deleteFile(fileId) {
|
|
46923
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
46924
|
+
return;
|
|
46925
|
+
this.ws.send(JSON.stringify({ type: "delete_file", fileId }));
|
|
46926
|
+
}
|
|
46927
|
+
async uploadFile(filePath, meshId, memberId, opts) {
|
|
46928
|
+
const { readFileSync: readFileSync2 } = await import("node:fs");
|
|
46929
|
+
const { basename } = await import("node:path");
|
|
46930
|
+
const data = readFileSync2(filePath);
|
|
46931
|
+
const fileName = opts.name ?? basename(filePath);
|
|
46932
|
+
const brokerHttp = this.mesh.brokerUrl.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
|
|
46933
|
+
const res = await fetch(`${brokerHttp}/upload`, {
|
|
46934
|
+
method: "POST",
|
|
46935
|
+
headers: {
|
|
46936
|
+
"Content-Type": "application/octet-stream",
|
|
46937
|
+
"X-Mesh-Id": meshId,
|
|
46938
|
+
"X-Member-Id": memberId,
|
|
46939
|
+
"X-File-Name": fileName,
|
|
46940
|
+
"X-Tags": JSON.stringify(opts.tags ?? []),
|
|
46941
|
+
"X-Persistent": String(opts.persistent ?? true),
|
|
46942
|
+
"X-Target-Spec": opts.targetSpec ?? ""
|
|
46943
|
+
},
|
|
46944
|
+
body: data,
|
|
46945
|
+
signal: AbortSignal.timeout(30000)
|
|
46946
|
+
});
|
|
46947
|
+
const body = await res.json();
|
|
46948
|
+
return body.fileId ?? null;
|
|
46949
|
+
}
|
|
46803
46950
|
onStateChange(handler) {
|
|
46804
46951
|
this.stateChangeHandlers.add(handler);
|
|
46805
46952
|
return () => this.stateChangeHandlers.delete(handler);
|
|
@@ -46941,6 +47088,31 @@ class BrokerClient {
|
|
|
46941
47088
|
resolver(msg);
|
|
46942
47089
|
return;
|
|
46943
47090
|
}
|
|
47091
|
+
if (msg.type === "file_url") {
|
|
47092
|
+
const resolver = this.fileUrlResolvers.shift();
|
|
47093
|
+
if (resolver) {
|
|
47094
|
+
if (msg.url) {
|
|
47095
|
+
resolver({ url: String(msg.url), name: String(msg.name ?? "") });
|
|
47096
|
+
} else {
|
|
47097
|
+
resolver(null);
|
|
47098
|
+
}
|
|
47099
|
+
}
|
|
47100
|
+
return;
|
|
47101
|
+
}
|
|
47102
|
+
if (msg.type === "file_list") {
|
|
47103
|
+
const files = msg.files ?? [];
|
|
47104
|
+
const resolver = this.fileListResolvers.shift();
|
|
47105
|
+
if (resolver)
|
|
47106
|
+
resolver(files);
|
|
47107
|
+
return;
|
|
47108
|
+
}
|
|
47109
|
+
if (msg.type === "file_status_result") {
|
|
47110
|
+
const accesses = msg.accesses ?? [];
|
|
47111
|
+
const resolver = this.fileStatusResolvers.shift();
|
|
47112
|
+
if (resolver)
|
|
47113
|
+
resolver(accesses);
|
|
47114
|
+
return;
|
|
47115
|
+
}
|
|
46944
47116
|
if (msg.type === "error") {
|
|
46945
47117
|
this.debug(`broker error: ${msg.code} ${msg.message}`);
|
|
46946
47118
|
const id = msg.id ? String(msg.id) : null;
|
|
@@ -47141,9 +47313,24 @@ When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATEL
|
|
|
47141
47313
|
| remember(content, tags?) | Store persistent knowledge with optional tags. |
|
|
47142
47314
|
| recall(query) | Full-text search over mesh memory. |
|
|
47143
47315
|
| forget(id) | Soft-delete a memory entry. |
|
|
47316
|
+
| share_file(path, name?, tags?) | Share a persistent file with the mesh. |
|
|
47317
|
+
| get_file(id, save_to) | Download a shared file to a local path. |
|
|
47318
|
+
| list_files(query?, from?) | Find files shared in the mesh. |
|
|
47319
|
+
| file_status(id) | Check who has accessed a file. |
|
|
47320
|
+
| delete_file(id) | Remove a shared file from the mesh. |
|
|
47144
47321
|
|
|
47145
47322
|
If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
|
|
47146
47323
|
|
|
47324
|
+
Multi-target: send_message accepts an array of targets for the 'to' field.
|
|
47325
|
+
send_message(to: ["Alice", "@backend"], message: "sprint starts")
|
|
47326
|
+
Targets are deduplicated — each peer receives the message once.
|
|
47327
|
+
|
|
47328
|
+
Targeted views: when different audiences need different details about the same event,
|
|
47329
|
+
send tailored messages instead of one generic broadcast:
|
|
47330
|
+
send_message(to: "@frontend", message: "Auth v2: useAuth hook changed, see src/auth/")
|
|
47331
|
+
send_message(to: "@backend", message: "Auth v2: new /api/auth/v2 endpoints, v1 deprecated")
|
|
47332
|
+
send_message(to: "@pm", message: "Auth v2 done. 3 points, no blockers.")
|
|
47333
|
+
|
|
47147
47334
|
## Groups
|
|
47148
47335
|
Groups are routing labels. Send to @groupname to multicast to all members. Roles are metadata that peers interpret: a "lead" gathers input before synthesizing a response, a "member" contributes when asked, an "observer" watches silently. Join and leave groups dynamically with join_group/leave_group. Check list_peers to see who belongs to which groups and their roles.
|
|
47149
47336
|
|
|
@@ -47153,6 +47340,10 @@ Shared key-value store scoped to the mesh. Use get_state/set_state for live coor
|
|
|
47153
47340
|
## Memory
|
|
47154
47341
|
Persistent knowledge that survives across sessions. Use remember(content, tags?) to store lessons, decisions, and incidents. Use recall(query) to search before asking peers. New peers should recall at session start to load institutional knowledge.
|
|
47155
47342
|
|
|
47343
|
+
## Files
|
|
47344
|
+
share_file for persistent references, send_message(file:) for ephemeral attachments.
|
|
47345
|
+
Tags on shared files make them searchable. Use list_files to find what peers shared.
|
|
47346
|
+
|
|
47156
47347
|
## Priority
|
|
47157
47348
|
- "now": interrupt immediately, even if recipient is in DND (use for urgent: broken deploy, blocking issue)
|
|
47158
47349
|
- "next" (default): deliver when recipient goes idle (normal coordination)
|
|
@@ -47174,13 +47365,27 @@ Call list_peers at session start to understand who is online, their roles, and w
|
|
|
47174
47365
|
const { to, message, priority } = args ?? {};
|
|
47175
47366
|
if (!to || !message)
|
|
47176
47367
|
return text("send_message: `to` and `message` required", true);
|
|
47177
|
-
const
|
|
47178
|
-
|
|
47179
|
-
|
|
47180
|
-
|
|
47181
|
-
|
|
47182
|
-
|
|
47183
|
-
|
|
47368
|
+
const targets = Array.isArray(to) ? to : [to];
|
|
47369
|
+
const results = [];
|
|
47370
|
+
const seen = new Set;
|
|
47371
|
+
for (const target of targets) {
|
|
47372
|
+
const { client, targetSpec, error: error2 } = await resolveClient(target);
|
|
47373
|
+
if (!client) {
|
|
47374
|
+
results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
|
|
47375
|
+
continue;
|
|
47376
|
+
}
|
|
47377
|
+
if (seen.has(targetSpec))
|
|
47378
|
+
continue;
|
|
47379
|
+
seen.add(targetSpec);
|
|
47380
|
+
const result = await client.send(targetSpec, message, priority ?? "next");
|
|
47381
|
+
if (!result.ok) {
|
|
47382
|
+
results.push(`✗ ${target}: ${result.error}`);
|
|
47383
|
+
} else {
|
|
47384
|
+
results.push(`✓ ${target} → ${result.messageId}`);
|
|
47385
|
+
}
|
|
47386
|
+
}
|
|
47387
|
+
return text(results.join(`
|
|
47388
|
+
`));
|
|
47184
47389
|
}
|
|
47185
47390
|
case "list_peers": {
|
|
47186
47391
|
const { mesh_slug } = args ?? {};
|
|
@@ -47342,6 +47547,81 @@ ${lines.join(`
|
|
|
47342
47547
|
await client.forget(id);
|
|
47343
47548
|
return text(`Forgotten: ${id}`);
|
|
47344
47549
|
}
|
|
47550
|
+
case "share_file": {
|
|
47551
|
+
const { path: filePath, name: fileName, tags } = args ?? {};
|
|
47552
|
+
if (!filePath)
|
|
47553
|
+
return text("share_file: `path` required", true);
|
|
47554
|
+
const { existsSync: existsSync2 } = await import("node:fs");
|
|
47555
|
+
if (!existsSync2(filePath))
|
|
47556
|
+
return text(`share_file: file not found: ${filePath}`, true);
|
|
47557
|
+
const client = allClients()[0];
|
|
47558
|
+
if (!client)
|
|
47559
|
+
return text("share_file: not connected", true);
|
|
47560
|
+
const fileId = await client.uploadFile(filePath, client.meshId, client.meshSlug, {
|
|
47561
|
+
name: fileName,
|
|
47562
|
+
tags,
|
|
47563
|
+
persistent: true
|
|
47564
|
+
});
|
|
47565
|
+
if (!fileId)
|
|
47566
|
+
return text("share_file: upload failed", true);
|
|
47567
|
+
return text(`Shared: ${fileName ?? filePath} (${fileId})`);
|
|
47568
|
+
}
|
|
47569
|
+
case "get_file": {
|
|
47570
|
+
const { id, save_to } = args ?? {};
|
|
47571
|
+
if (!id || !save_to)
|
|
47572
|
+
return text("get_file: `id` and `save_to` required", true);
|
|
47573
|
+
const client = allClients()[0];
|
|
47574
|
+
if (!client)
|
|
47575
|
+
return text("get_file: not connected", true);
|
|
47576
|
+
const result = await client.getFile(id);
|
|
47577
|
+
if (!result)
|
|
47578
|
+
return text(`get_file: file ${id} not found`, true);
|
|
47579
|
+
const res = await fetch(result.url, { signal: AbortSignal.timeout(30000) });
|
|
47580
|
+
if (!res.ok)
|
|
47581
|
+
return text(`get_file: download failed (${res.status})`, true);
|
|
47582
|
+
const { writeFileSync: writeFileSync2, mkdirSync: mkdirSync2 } = await import("node:fs");
|
|
47583
|
+
const { dirname: dirname2 } = await import("node:path");
|
|
47584
|
+
mkdirSync2(dirname2(save_to), { recursive: true });
|
|
47585
|
+
writeFileSync2(save_to, Buffer.from(await res.arrayBuffer()));
|
|
47586
|
+
return text(`Downloaded: ${result.name} → ${save_to}`);
|
|
47587
|
+
}
|
|
47588
|
+
case "list_files": {
|
|
47589
|
+
const { query, from } = args ?? {};
|
|
47590
|
+
const client = allClients()[0];
|
|
47591
|
+
if (!client)
|
|
47592
|
+
return text("list_files: not connected", true);
|
|
47593
|
+
const files = await client.listFiles(query, from);
|
|
47594
|
+
if (files.length === 0)
|
|
47595
|
+
return text("No files found.");
|
|
47596
|
+
const lines = files.map((f) => `- **${f.name}** (${f.id.slice(0, 8)}…, ${f.size} bytes) by ${f.uploadedBy}${f.tags.length ? ` [${f.tags.join(", ")}]` : ""}`);
|
|
47597
|
+
return text(lines.join(`
|
|
47598
|
+
`));
|
|
47599
|
+
}
|
|
47600
|
+
case "file_status": {
|
|
47601
|
+
const { id } = args ?? {};
|
|
47602
|
+
if (!id)
|
|
47603
|
+
return text("file_status: `id` required", true);
|
|
47604
|
+
const client = allClients()[0];
|
|
47605
|
+
if (!client)
|
|
47606
|
+
return text("file_status: not connected", true);
|
|
47607
|
+
const accesses = await client.fileStatus(id);
|
|
47608
|
+
if (accesses.length === 0)
|
|
47609
|
+
return text("No one has accessed this file yet.");
|
|
47610
|
+
const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
|
|
47611
|
+
return text(`Accessed by:
|
|
47612
|
+
${lines.join(`
|
|
47613
|
+
`)}`);
|
|
47614
|
+
}
|
|
47615
|
+
case "delete_file": {
|
|
47616
|
+
const { id } = args ?? {};
|
|
47617
|
+
if (!id)
|
|
47618
|
+
return text("delete_file: `id` required", true);
|
|
47619
|
+
const client = allClients()[0];
|
|
47620
|
+
if (!client)
|
|
47621
|
+
return text("delete_file: not connected", true);
|
|
47622
|
+
await client.deleteFile(id);
|
|
47623
|
+
return text(`Deleted: ${id}`);
|
|
47624
|
+
}
|
|
47345
47625
|
default:
|
|
47346
47626
|
return text(`Unknown tool: ${name}`, true);
|
|
47347
47627
|
}
|
|
@@ -48273,7 +48553,7 @@ init_config();
|
|
|
48273
48553
|
// package.json
|
|
48274
48554
|
var package_default = {
|
|
48275
48555
|
name: "claudemesh-cli",
|
|
48276
|
-
version: "0.
|
|
48556
|
+
version: "0.4.0",
|
|
48277
48557
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
48278
48558
|
keywords: [
|
|
48279
48559
|
"claude-code",
|