claudemesh-cli 1.3.0 → 1.5.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/entrypoints/cli.js +1018 -3077
- package/dist/entrypoints/cli.js.map +12 -10
- package/dist/entrypoints/mcp.js +4 -2592
- package/dist/entrypoints/mcp.js.map +4 -4
- package/package.json +1 -1
- package/skills/claudemesh/SKILL.md +57 -39
package/dist/entrypoints/mcp.js
CHANGED
|
@@ -31,924 +31,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
31
31
|
// src/mcp/tools/definitions.ts
|
|
32
32
|
var TOOLS;
|
|
33
33
|
var init_definitions = __esm(() => {
|
|
34
|
-
TOOLS = [
|
|
35
|
-
{
|
|
36
|
-
name: "send_message",
|
|
37
|
-
description: "Send a message to a peer in one of your joined meshes. `to` can be a peer display name (resolved via list_peers), hex pubkey, @group, `#channel`, or `*` for broadcast. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
|
|
38
|
-
inputSchema: {
|
|
39
|
-
type: "object",
|
|
40
|
-
properties: {
|
|
41
|
-
to: {
|
|
42
|
-
oneOf: [
|
|
43
|
-
{ type: "string", description: "Peer name, pubkey, @group" },
|
|
44
|
-
{ type: "array", items: { type: "string" }, description: "Multiple targets" }
|
|
45
|
-
],
|
|
46
|
-
description: "Single target or array of targets"
|
|
47
|
-
},
|
|
48
|
-
message: { type: "string", description: "Message text" },
|
|
49
|
-
priority: {
|
|
50
|
-
type: "string",
|
|
51
|
-
enum: ["now", "next", "low"],
|
|
52
|
-
description: "Delivery priority (default: next)"
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
required: ["to", "message"]
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: "list_peers",
|
|
60
|
-
description: "List peers across all joined meshes. Shows name, mesh, status (idle/working/dnd), and current summary.",
|
|
61
|
-
inputSchema: {
|
|
62
|
-
type: "object",
|
|
63
|
-
properties: {
|
|
64
|
-
mesh_slug: {
|
|
65
|
-
type: "string",
|
|
66
|
-
description: "Only list peers in this mesh (optional)"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: "message_status",
|
|
73
|
-
description: "Check the delivery status of a sent message. Shows whether each recipient received it.",
|
|
74
|
-
inputSchema: {
|
|
75
|
-
type: "object",
|
|
76
|
-
properties: {
|
|
77
|
-
id: {
|
|
78
|
-
type: "string",
|
|
79
|
-
description: "Message ID (returned by send_message)"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
required: ["id"]
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: "check_messages",
|
|
87
|
-
description: "Pull any undelivered messages from the broker. Normally messages arrive via push; use this to drain the queue after being offline.",
|
|
88
|
-
inputSchema: { type: "object", properties: {} }
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "set_summary",
|
|
92
|
-
description: "Set a 1–2 sentence summary of what you're working on. Visible to other peers.",
|
|
93
|
-
inputSchema: {
|
|
94
|
-
type: "object",
|
|
95
|
-
properties: {
|
|
96
|
-
summary: { type: "string", description: "1-2 sentence summary" }
|
|
97
|
-
},
|
|
98
|
-
required: ["summary"]
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "set_status",
|
|
103
|
-
description: "Manually override your status. `dnd` blocks everything except `now`-priority messages.",
|
|
104
|
-
inputSchema: {
|
|
105
|
-
type: "object",
|
|
106
|
-
properties: {
|
|
107
|
-
status: {
|
|
108
|
-
type: "string",
|
|
109
|
-
enum: ["idle", "working", "dnd"],
|
|
110
|
-
description: "Your status"
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
required: ["status"]
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: "set_visible",
|
|
118
|
-
description: "Control your visibility in the mesh. When hidden, you won't appear in list_peers and won't receive broadcasts — but direct messages still reach you.",
|
|
119
|
-
inputSchema: {
|
|
120
|
-
type: "object",
|
|
121
|
-
properties: {
|
|
122
|
-
visible: {
|
|
123
|
-
type: "boolean",
|
|
124
|
-
description: "true to be visible (default), false to hide"
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
required: ["visible"]
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: "set_profile",
|
|
132
|
-
description: "Set your public profile — what other peers see about you. Avatar (emoji), title, bio, and capabilities list.",
|
|
133
|
-
inputSchema: {
|
|
134
|
-
type: "object",
|
|
135
|
-
properties: {
|
|
136
|
-
avatar: {
|
|
137
|
-
type: "string",
|
|
138
|
-
description: "Emoji or URL for your avatar"
|
|
139
|
-
},
|
|
140
|
-
title: {
|
|
141
|
-
type: "string",
|
|
142
|
-
description: "Short role label (e.g. 'Frontend Lead', 'DevOps')"
|
|
143
|
-
},
|
|
144
|
-
bio: {
|
|
145
|
-
type: "string",
|
|
146
|
-
description: "One-liner about yourself"
|
|
147
|
-
},
|
|
148
|
-
capabilities: {
|
|
149
|
-
type: "array",
|
|
150
|
-
items: { type: "string" },
|
|
151
|
-
description: "What you can help with"
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
name: "join_group",
|
|
158
|
-
description: "Join a group with an optional role. Other peers see your group membership in list_peers.",
|
|
159
|
-
inputSchema: {
|
|
160
|
-
type: "object",
|
|
161
|
-
properties: {
|
|
162
|
-
name: { type: "string", description: "Group name (without @)" },
|
|
163
|
-
role: {
|
|
164
|
-
type: "string",
|
|
165
|
-
description: "Your role in the group (e.g. lead, member, observer)"
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
required: ["name"]
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
name: "leave_group",
|
|
173
|
-
description: "Leave a group.",
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: {
|
|
177
|
-
name: { type: "string", description: "Group name (without @)" }
|
|
178
|
-
},
|
|
179
|
-
required: ["name"]
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: "set_state",
|
|
184
|
-
description: "Set a shared state value visible to all peers in the mesh. Pushes a change notification.",
|
|
185
|
-
inputSchema: {
|
|
186
|
-
type: "object",
|
|
187
|
-
properties: {
|
|
188
|
-
key: { type: "string" },
|
|
189
|
-
value: { description: "Any JSON value" }
|
|
190
|
-
},
|
|
191
|
-
required: ["key", "value"]
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "get_state",
|
|
196
|
-
description: "Read a shared state value.",
|
|
197
|
-
inputSchema: {
|
|
198
|
-
type: "object",
|
|
199
|
-
properties: {
|
|
200
|
-
key: { type: "string" }
|
|
201
|
-
},
|
|
202
|
-
required: ["key"]
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
name: "list_state",
|
|
207
|
-
description: "List all shared state keys and values in the mesh.",
|
|
208
|
-
inputSchema: { type: "object", properties: {} }
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
name: "remember",
|
|
212
|
-
description: "Store persistent knowledge in the mesh's shared memory. Survives across sessions.",
|
|
213
|
-
inputSchema: {
|
|
214
|
-
type: "object",
|
|
215
|
-
properties: {
|
|
216
|
-
content: {
|
|
217
|
-
type: "string",
|
|
218
|
-
description: "The knowledge to remember"
|
|
219
|
-
},
|
|
220
|
-
tags: {
|
|
221
|
-
type: "array",
|
|
222
|
-
items: { type: "string" },
|
|
223
|
-
description: "Optional categorization tags"
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
required: ["content"]
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
name: "recall",
|
|
231
|
-
description: "Search the mesh's shared memory by relevance.",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
query: { type: "string", description: "Search query" }
|
|
236
|
-
},
|
|
237
|
-
required: ["query"]
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
name: "forget",
|
|
242
|
-
description: "Remove a memory from the mesh's shared knowledge.",
|
|
243
|
-
inputSchema: {
|
|
244
|
-
type: "object",
|
|
245
|
-
properties: {
|
|
246
|
-
id: { type: "string", description: "Memory ID to forget" }
|
|
247
|
-
},
|
|
248
|
-
required: ["id"]
|
|
249
|
-
}
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
name: "share_file",
|
|
253
|
-
description: "Share a persistent file with the mesh. All current and future peers can access it. If `to` is specified, the file is E2E encrypted and only accessible to that peer (and you).",
|
|
254
|
-
inputSchema: {
|
|
255
|
-
type: "object",
|
|
256
|
-
properties: {
|
|
257
|
-
path: { type: "string", description: "Local file path to share" },
|
|
258
|
-
name: {
|
|
259
|
-
type: "string",
|
|
260
|
-
description: "Display name (defaults to filename)"
|
|
261
|
-
},
|
|
262
|
-
tags: {
|
|
263
|
-
type: "array",
|
|
264
|
-
items: { type: "string" },
|
|
265
|
-
description: "Tags for categorization"
|
|
266
|
-
},
|
|
267
|
-
to: {
|
|
268
|
-
type: "string",
|
|
269
|
-
description: "Peer display name or pubkey hex — if set, file is E2E encrypted for this peer only"
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
required: ["path"]
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
name: "get_file",
|
|
277
|
-
description: "Download a shared file to a local path.",
|
|
278
|
-
inputSchema: {
|
|
279
|
-
type: "object",
|
|
280
|
-
properties: {
|
|
281
|
-
id: { type: "string", description: "File ID" },
|
|
282
|
-
save_to: {
|
|
283
|
-
type: "string",
|
|
284
|
-
description: "Local path to save the file"
|
|
285
|
-
}
|
|
286
|
-
},
|
|
287
|
-
required: ["id", "save_to"]
|
|
288
|
-
}
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
name: "list_files",
|
|
292
|
-
description: "List files shared in the mesh.",
|
|
293
|
-
inputSchema: {
|
|
294
|
-
type: "object",
|
|
295
|
-
properties: {
|
|
296
|
-
query: { type: "string", description: "Search by name or tags" },
|
|
297
|
-
from: { type: "string", description: "Filter by uploader name" }
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
name: "file_status",
|
|
303
|
-
description: "Check who has accessed a shared file.",
|
|
304
|
-
inputSchema: {
|
|
305
|
-
type: "object",
|
|
306
|
-
properties: {
|
|
307
|
-
id: { type: "string", description: "File ID" }
|
|
308
|
-
},
|
|
309
|
-
required: ["id"]
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
name: "delete_file",
|
|
314
|
-
description: "Remove a shared file from the mesh.",
|
|
315
|
-
inputSchema: {
|
|
316
|
-
type: "object",
|
|
317
|
-
properties: {
|
|
318
|
-
id: { type: "string", description: "File ID" }
|
|
319
|
-
},
|
|
320
|
-
required: ["id"]
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
name: "grant_file_access",
|
|
325
|
-
description: "Grant a peer access to an E2E encrypted file you shared. You must be the owner.",
|
|
326
|
-
inputSchema: {
|
|
327
|
-
type: "object",
|
|
328
|
-
properties: {
|
|
329
|
-
fileId: { type: "string", description: "File ID" },
|
|
330
|
-
to: { type: "string", description: "Peer display name or pubkey hex to grant access to" }
|
|
331
|
-
},
|
|
332
|
-
required: ["fileId", "to"]
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
{
|
|
336
|
-
name: "vector_store",
|
|
337
|
-
description: "Store an embedding in a per-mesh Qdrant collection. Auto-creates the collection on first use.",
|
|
338
|
-
inputSchema: {
|
|
339
|
-
type: "object",
|
|
340
|
-
properties: {
|
|
341
|
-
collection: { type: "string", description: "Collection name" },
|
|
342
|
-
text: { type: "string", description: "Text to embed and store" },
|
|
343
|
-
metadata: {
|
|
344
|
-
type: "object",
|
|
345
|
-
description: "Optional metadata to attach"
|
|
346
|
-
}
|
|
347
|
-
},
|
|
348
|
-
required: ["collection", "text"]
|
|
349
|
-
}
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
name: "vector_search",
|
|
353
|
-
description: "Semantic search over stored embeddings in a collection.",
|
|
354
|
-
inputSchema: {
|
|
355
|
-
type: "object",
|
|
356
|
-
properties: {
|
|
357
|
-
collection: { type: "string", description: "Collection name" },
|
|
358
|
-
query: { type: "string", description: "Search query text" },
|
|
359
|
-
limit: {
|
|
360
|
-
type: "number",
|
|
361
|
-
description: "Max results (default: 10)"
|
|
362
|
-
}
|
|
363
|
-
},
|
|
364
|
-
required: ["collection", "query"]
|
|
365
|
-
}
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
name: "vector_delete",
|
|
369
|
-
description: "Remove an embedding from a collection.",
|
|
370
|
-
inputSchema: {
|
|
371
|
-
type: "object",
|
|
372
|
-
properties: {
|
|
373
|
-
collection: { type: "string", description: "Collection name" },
|
|
374
|
-
id: { type: "string", description: "Embedding ID to delete" }
|
|
375
|
-
},
|
|
376
|
-
required: ["collection", "id"]
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
{
|
|
380
|
-
name: "list_collections",
|
|
381
|
-
description: "List vector collections in this mesh.",
|
|
382
|
-
inputSchema: { type: "object", properties: {} }
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
name: "graph_query",
|
|
386
|
-
description: "Run a read-only Cypher query on the per-mesh Neo4j database.",
|
|
387
|
-
inputSchema: {
|
|
388
|
-
type: "object",
|
|
389
|
-
properties: {
|
|
390
|
-
cypher: { type: "string", description: "Cypher MATCH query" }
|
|
391
|
-
},
|
|
392
|
-
required: ["cypher"]
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
name: "graph_execute",
|
|
397
|
-
description: "Run a write Cypher query (CREATE, MERGE, DELETE) on the per-mesh Neo4j database.",
|
|
398
|
-
inputSchema: {
|
|
399
|
-
type: "object",
|
|
400
|
-
properties: {
|
|
401
|
-
cypher: { type: "string", description: "Cypher write query" }
|
|
402
|
-
},
|
|
403
|
-
required: ["cypher"]
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
{
|
|
407
|
-
name: "mesh_query",
|
|
408
|
-
description: "Run a SELECT query on the per-mesh shared database.",
|
|
409
|
-
inputSchema: {
|
|
410
|
-
type: "object",
|
|
411
|
-
properties: {
|
|
412
|
-
sql: { type: "string", description: "SQL SELECT query" }
|
|
413
|
-
},
|
|
414
|
-
required: ["sql"]
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
name: "mesh_execute",
|
|
419
|
-
description: "Run DDL/DML on the per-mesh database (CREATE TABLE, INSERT, UPDATE, DELETE).",
|
|
420
|
-
inputSchema: {
|
|
421
|
-
type: "object",
|
|
422
|
-
properties: {
|
|
423
|
-
sql: { type: "string", description: "SQL statement" }
|
|
424
|
-
},
|
|
425
|
-
required: ["sql"]
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
name: "mesh_schema",
|
|
430
|
-
description: "List tables and columns in the per-mesh shared database.",
|
|
431
|
-
inputSchema: { type: "object", properties: {} }
|
|
432
|
-
},
|
|
433
|
-
{
|
|
434
|
-
name: "create_stream",
|
|
435
|
-
description: "Create a real-time data stream in the mesh.",
|
|
436
|
-
inputSchema: {
|
|
437
|
-
type: "object",
|
|
438
|
-
properties: {
|
|
439
|
-
name: { type: "string", description: "Stream name" }
|
|
440
|
-
},
|
|
441
|
-
required: ["name"]
|
|
442
|
-
}
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
name: "publish",
|
|
446
|
-
description: "Push data to a stream. Subscribers receive it in real-time.",
|
|
447
|
-
inputSchema: {
|
|
448
|
-
type: "object",
|
|
449
|
-
properties: {
|
|
450
|
-
stream: { type: "string", description: "Stream name" },
|
|
451
|
-
data: { description: "Any JSON data to publish" }
|
|
452
|
-
},
|
|
453
|
-
required: ["stream", "data"]
|
|
454
|
-
}
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
name: "subscribe",
|
|
458
|
-
description: "Subscribe to a stream. Data pushes arrive as channel notifications.",
|
|
459
|
-
inputSchema: {
|
|
460
|
-
type: "object",
|
|
461
|
-
properties: {
|
|
462
|
-
stream: { type: "string", description: "Stream name" }
|
|
463
|
-
},
|
|
464
|
-
required: ["stream"]
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
name: "list_streams",
|
|
469
|
-
description: "List active streams in the mesh.",
|
|
470
|
-
inputSchema: { type: "object", properties: {} }
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
name: "share_context",
|
|
474
|
-
description: "Share your session understanding with the mesh. Call after exploring a codebase area.",
|
|
475
|
-
inputSchema: {
|
|
476
|
-
type: "object",
|
|
477
|
-
properties: {
|
|
478
|
-
summary: {
|
|
479
|
-
type: "string",
|
|
480
|
-
description: "Summary of what you explored/learned"
|
|
481
|
-
},
|
|
482
|
-
files_read: {
|
|
483
|
-
type: "array",
|
|
484
|
-
items: { type: "string" },
|
|
485
|
-
description: "File paths you read"
|
|
486
|
-
},
|
|
487
|
-
key_findings: {
|
|
488
|
-
type: "array",
|
|
489
|
-
items: { type: "string" },
|
|
490
|
-
description: "Key findings or insights"
|
|
491
|
-
},
|
|
492
|
-
tags: {
|
|
493
|
-
type: "array",
|
|
494
|
-
items: { type: "string" },
|
|
495
|
-
description: "Tags for categorization"
|
|
496
|
-
}
|
|
497
|
-
},
|
|
498
|
-
required: ["summary"]
|
|
499
|
-
}
|
|
500
|
-
},
|
|
501
|
-
{
|
|
502
|
-
name: "get_context",
|
|
503
|
-
description: "Find context from peers who explored an area. Check before re-reading files another peer already analyzed.",
|
|
504
|
-
inputSchema: {
|
|
505
|
-
type: "object",
|
|
506
|
-
properties: {
|
|
507
|
-
query: {
|
|
508
|
-
type: "string",
|
|
509
|
-
description: "Search query (file path, topic, etc.)"
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
required: ["query"]
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
name: "list_contexts",
|
|
517
|
-
description: "See what all peers currently know about the codebase.",
|
|
518
|
-
inputSchema: { type: "object", properties: {} }
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
name: "create_task",
|
|
522
|
-
description: "Create a work item for the mesh.",
|
|
523
|
-
inputSchema: {
|
|
524
|
-
type: "object",
|
|
525
|
-
properties: {
|
|
526
|
-
title: { type: "string", description: "Task title" },
|
|
527
|
-
assignee: {
|
|
528
|
-
type: "string",
|
|
529
|
-
description: "Peer name to assign (optional)"
|
|
530
|
-
},
|
|
531
|
-
priority: {
|
|
532
|
-
type: "string",
|
|
533
|
-
enum: ["low", "normal", "high", "urgent"],
|
|
534
|
-
description: "Priority level (default: normal)"
|
|
535
|
-
},
|
|
536
|
-
tags: {
|
|
537
|
-
type: "array",
|
|
538
|
-
items: { type: "string" },
|
|
539
|
-
description: "Tags for categorization"
|
|
540
|
-
}
|
|
541
|
-
},
|
|
542
|
-
required: ["title"]
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
name: "claim_task",
|
|
547
|
-
description: "Claim an unclaimed task to take ownership.",
|
|
548
|
-
inputSchema: {
|
|
549
|
-
type: "object",
|
|
550
|
-
properties: {
|
|
551
|
-
id: { type: "string", description: "Task ID" }
|
|
552
|
-
},
|
|
553
|
-
required: ["id"]
|
|
554
|
-
}
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
name: "complete_task",
|
|
558
|
-
description: "Mark a task as done with an optional result summary.",
|
|
559
|
-
inputSchema: {
|
|
560
|
-
type: "object",
|
|
561
|
-
properties: {
|
|
562
|
-
id: { type: "string", description: "Task ID" },
|
|
563
|
-
result: {
|
|
564
|
-
type: "string",
|
|
565
|
-
description: "Summary of what was done"
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
required: ["id"]
|
|
569
|
-
}
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
name: "list_tasks",
|
|
573
|
-
description: "List tasks filtered by status and/or assignee.",
|
|
574
|
-
inputSchema: {
|
|
575
|
-
type: "object",
|
|
576
|
-
properties: {
|
|
577
|
-
status: {
|
|
578
|
-
type: "string",
|
|
579
|
-
enum: ["open", "claimed", "completed"],
|
|
580
|
-
description: "Filter by status"
|
|
581
|
-
},
|
|
582
|
-
assignee: {
|
|
583
|
-
type: "string",
|
|
584
|
-
description: "Filter by assignee name"
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
},
|
|
589
|
-
{
|
|
590
|
-
name: "schedule_reminder",
|
|
591
|
-
description: "Schedule a one-shot or recurring message. Without `to`, it fires back to yourself (a self-reminder). With `to`, it delivers to a peer, @group, or * broadcast. For one-shot, provide `deliver_at` or `in_seconds`. For recurring, provide `cron` (standard 5-field expression). The broker persists schedules to the database — they survive restarts. Receivers see `subtype: reminder` in the push envelope.",
|
|
592
|
-
inputSchema: {
|
|
593
|
-
type: "object",
|
|
594
|
-
properties: {
|
|
595
|
-
message: { type: "string", description: "Message or reminder text" },
|
|
596
|
-
deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver (one-shot)" },
|
|
597
|
-
in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds (one-shot)" },
|
|
598
|
-
cron: { type: "string", description: "Cron expression for recurring reminders (e.g. '0 */2 * * *' for every 2 hours, '30 9 * * 1-5' for 9:30 weekdays)" },
|
|
599
|
-
to: {
|
|
600
|
-
type: "string",
|
|
601
|
-
description: "Recipient: display name, pubkey hex, @group, or * (omit for self-reminder)"
|
|
602
|
-
}
|
|
603
|
-
},
|
|
604
|
-
required: ["message"]
|
|
605
|
-
}
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
name: "list_scheduled",
|
|
609
|
-
description: "List all your pending scheduled messages: id, recipient, preview, and delivery time.",
|
|
610
|
-
inputSchema: { type: "object", properties: {} }
|
|
611
|
-
},
|
|
612
|
-
{
|
|
613
|
-
name: "cancel_scheduled",
|
|
614
|
-
description: "Cancel a pending scheduled message before it fires.",
|
|
615
|
-
inputSchema: {
|
|
616
|
-
type: "object",
|
|
617
|
-
properties: {
|
|
618
|
-
id: { type: "string", description: "Scheduled message ID" }
|
|
619
|
-
},
|
|
620
|
-
required: ["id"]
|
|
621
|
-
}
|
|
622
|
-
},
|
|
623
|
-
{
|
|
624
|
-
name: "mesh_info",
|
|
625
|
-
description: "Get a complete overview of the mesh: peers, groups, state, memory, files, tasks, streams, tables. Call on session start for full situational awareness.",
|
|
626
|
-
inputSchema: { type: "object", properties: {} }
|
|
627
|
-
},
|
|
628
|
-
{
|
|
629
|
-
name: "mesh_stats",
|
|
630
|
-
description: "View resource usage stats for all peers: messages sent/received, tool calls, uptime, errors.",
|
|
631
|
-
inputSchema: { type: "object", properties: {} }
|
|
632
|
-
},
|
|
633
|
-
{
|
|
634
|
-
name: "mesh_mcp_register",
|
|
635
|
-
description: "Register an MCP server with the mesh. Other peers can invoke its tools through the mesh without restarting their sessions. Provide the server name, description, and full tool definitions.",
|
|
636
|
-
inputSchema: {
|
|
637
|
-
type: "object",
|
|
638
|
-
properties: {
|
|
639
|
-
server_name: { type: "string", description: "Unique name for the MCP server (e.g. 'github', 'jira')" },
|
|
640
|
-
description: { type: "string", description: "What this MCP server does" },
|
|
641
|
-
tools: {
|
|
642
|
-
type: "array",
|
|
643
|
-
items: {
|
|
644
|
-
type: "object",
|
|
645
|
-
properties: {
|
|
646
|
-
name: { type: "string" },
|
|
647
|
-
description: { type: "string" },
|
|
648
|
-
inputSchema: { type: "object", description: "JSON Schema for tool arguments" }
|
|
649
|
-
},
|
|
650
|
-
required: ["name", "description", "inputSchema"]
|
|
651
|
-
},
|
|
652
|
-
description: "Tool definitions to expose"
|
|
653
|
-
},
|
|
654
|
-
persistent: {
|
|
655
|
-
type: "boolean",
|
|
656
|
-
description: "If true, registration survives peer disconnect. Other peers see it as 'offline' until you reconnect. Default: false"
|
|
657
|
-
}
|
|
658
|
-
},
|
|
659
|
-
required: ["server_name", "description", "tools"]
|
|
660
|
-
}
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
name: "mesh_mcp_list",
|
|
664
|
-
description: "List MCP servers available in the mesh with their tools. Shows which peer hosts each server.",
|
|
665
|
-
inputSchema: { type: "object", properties: {} }
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
name: "mesh_tool_call",
|
|
669
|
-
description: "Call a tool on a mesh-registered MCP server. Route: you -> broker -> hosting peer -> execute -> result back. Timeout: 30s.",
|
|
670
|
-
inputSchema: {
|
|
671
|
-
type: "object",
|
|
672
|
-
properties: {
|
|
673
|
-
server_name: { type: "string", description: "Name of the MCP server" },
|
|
674
|
-
tool_name: { type: "string", description: "Name of the tool to call" },
|
|
675
|
-
args: { type: "object", description: "Tool arguments (JSON object)" }
|
|
676
|
-
},
|
|
677
|
-
required: ["server_name", "tool_name"]
|
|
678
|
-
}
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
name: "mesh_mcp_remove",
|
|
682
|
-
description: "Unregister an MCP server you previously registered with the mesh.",
|
|
683
|
-
inputSchema: {
|
|
684
|
-
type: "object",
|
|
685
|
-
properties: {
|
|
686
|
-
server_name: { type: "string", description: "Name of the MCP server to remove" }
|
|
687
|
-
},
|
|
688
|
-
required: ["server_name"]
|
|
689
|
-
}
|
|
690
|
-
},
|
|
691
|
-
{
|
|
692
|
-
name: "mesh_set_clock",
|
|
693
|
-
description: "Set the simulation clock speed. x1 = real-time, x10 = 10x faster, x100 = 100x. Peers receive heartbeat ticks at the simulated rate.",
|
|
694
|
-
inputSchema: {
|
|
695
|
-
type: "object",
|
|
696
|
-
properties: {
|
|
697
|
-
speed: {
|
|
698
|
-
type: "number",
|
|
699
|
-
description: "Speed multiplier (1-100). x1 = tick every 60s, x10 = tick every 6s, x100 = tick every 600ms."
|
|
700
|
-
}
|
|
701
|
-
},
|
|
702
|
-
required: ["speed"]
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
{
|
|
706
|
-
name: "mesh_pause_clock",
|
|
707
|
-
description: "Pause the simulation clock. Ticks stop until resumed.",
|
|
708
|
-
inputSchema: { type: "object", properties: {} }
|
|
709
|
-
},
|
|
710
|
-
{
|
|
711
|
-
name: "mesh_resume_clock",
|
|
712
|
-
description: "Resume a paused simulation clock.",
|
|
713
|
-
inputSchema: { type: "object", properties: {} }
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
name: "mesh_clock",
|
|
717
|
-
description: "Get current simulation clock status: speed, tick count, simulated time.",
|
|
718
|
-
inputSchema: { type: "object", properties: {} }
|
|
719
|
-
},
|
|
720
|
-
{
|
|
721
|
-
name: "share_skill",
|
|
722
|
-
description: "Publish a reusable skill to the mesh. Other peers can discover and load it as a slash command. If a skill with the same name exists, it is updated. Skills are automatically exposed as MCP prompts and skill:// resources for native Claude Code integration.",
|
|
723
|
-
inputSchema: {
|
|
724
|
-
type: "object",
|
|
725
|
-
properties: {
|
|
726
|
-
name: { type: "string", description: "Unique skill name (e.g. 'code-review', 'deploy-checklist'). Becomes the slash command name." },
|
|
727
|
-
description: { type: "string", description: "Short description of what the skill does" },
|
|
728
|
-
instructions: { type: "string", description: "Full instructions/prompt markdown. Can include frontmatter (---) block." },
|
|
729
|
-
tags: {
|
|
730
|
-
type: "array",
|
|
731
|
-
items: { type: "string" },
|
|
732
|
-
description: "Tags for discoverability"
|
|
733
|
-
},
|
|
734
|
-
when_to_use: { type: "string", description: "Detailed description of when Claude should auto-invoke this skill" },
|
|
735
|
-
allowed_tools: {
|
|
736
|
-
type: "array",
|
|
737
|
-
items: { type: "string" },
|
|
738
|
-
description: "Tool names this skill is allowed to use (e.g. ['Bash', 'Read', 'Edit'])"
|
|
739
|
-
},
|
|
740
|
-
model: { type: "string", description: "Model override (e.g. 'sonnet', 'opus', 'haiku')" },
|
|
741
|
-
context: { type: "string", enum: ["inline", "fork"], description: "Execution context: 'inline' (default) or 'fork' (sub-agent)" },
|
|
742
|
-
agent: { type: "string", description: "Agent type when forked (e.g. 'general-purpose')" },
|
|
743
|
-
user_invocable: { type: "boolean", description: "Whether users can invoke via /skill-name (default: true)" },
|
|
744
|
-
argument_hint: { type: "string", description: "Hint text for arguments (e.g. '<file-path>')" }
|
|
745
|
-
},
|
|
746
|
-
required: ["name", "description", "instructions"]
|
|
747
|
-
}
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
name: "get_skill",
|
|
751
|
-
description: "Load a skill's full instructions by name. Use to acquire capabilities shared by other peers.",
|
|
752
|
-
inputSchema: {
|
|
753
|
-
type: "object",
|
|
754
|
-
properties: {
|
|
755
|
-
name: { type: "string", description: "Skill name to load" }
|
|
756
|
-
},
|
|
757
|
-
required: ["name"]
|
|
758
|
-
}
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
name: "list_skills",
|
|
762
|
-
description: "Browse available skills in the mesh. Optionally filter by keyword across name, description, and tags.",
|
|
763
|
-
inputSchema: {
|
|
764
|
-
type: "object",
|
|
765
|
-
properties: {
|
|
766
|
-
query: { type: "string", description: "Search keyword (optional)" }
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
},
|
|
770
|
-
{
|
|
771
|
-
name: "remove_skill",
|
|
772
|
-
description: "Remove a skill you published from the mesh.",
|
|
773
|
-
inputSchema: {
|
|
774
|
-
type: "object",
|
|
775
|
-
properties: {
|
|
776
|
-
name: { type: "string", description: "Skill name to remove" }
|
|
777
|
-
},
|
|
778
|
-
required: ["name"]
|
|
779
|
-
}
|
|
780
|
-
},
|
|
781
|
-
{
|
|
782
|
-
name: "ping_mesh",
|
|
783
|
-
description: "Send test messages through the full pipeline and measure round-trip timing per priority. Diagnoses push delivery issues.",
|
|
784
|
-
inputSchema: {
|
|
785
|
-
type: "object",
|
|
786
|
-
properties: {
|
|
787
|
-
priorities: {
|
|
788
|
-
type: "array",
|
|
789
|
-
items: { type: "string", enum: ["now", "next", "low"] },
|
|
790
|
-
description: 'Priorities to test (default: ["now", "next"])'
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
},
|
|
795
|
-
{
|
|
796
|
-
name: "read_peer_file",
|
|
797
|
-
description: "Read a file from another peer's project. Specify the peer (by name) and the file path relative to their working directory. The peer must be online and sharing files. Max file size: 1MB.",
|
|
798
|
-
inputSchema: {
|
|
799
|
-
type: "object",
|
|
800
|
-
properties: {
|
|
801
|
-
peer: { type: "string", description: "Peer display name or pubkey" },
|
|
802
|
-
path: { type: "string", description: "File path relative to peer's working directory" }
|
|
803
|
-
},
|
|
804
|
-
required: ["peer", "path"]
|
|
805
|
-
}
|
|
806
|
-
},
|
|
807
|
-
{
|
|
808
|
-
name: "list_peer_files",
|
|
809
|
-
description: "List files in a peer's shared directory. Returns a tree of file names (not contents). The peer must be online and sharing files.",
|
|
810
|
-
inputSchema: {
|
|
811
|
-
type: "object",
|
|
812
|
-
properties: {
|
|
813
|
-
peer: { type: "string", description: "Peer display name or pubkey" },
|
|
814
|
-
path: { type: "string", description: "Directory path relative to peer's cwd (default: root)" },
|
|
815
|
-
pattern: { type: "string", description: "Glob-like filter pattern (e.g. '*.ts', 'src/*')" }
|
|
816
|
-
},
|
|
817
|
-
required: ["peer"]
|
|
818
|
-
}
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
name: "create_webhook",
|
|
822
|
-
description: "Create an inbound webhook. Returns a URL that external services (GitHub, CI/CD, monitoring) can POST to — the payload becomes a mesh message to all peers.",
|
|
823
|
-
inputSchema: {
|
|
824
|
-
type: "object",
|
|
825
|
-
properties: {
|
|
826
|
-
name: {
|
|
827
|
-
type: "string",
|
|
828
|
-
description: "Webhook name (e.g. 'github-ci', 'datadog-alerts')"
|
|
829
|
-
}
|
|
830
|
-
},
|
|
831
|
-
required: ["name"]
|
|
832
|
-
}
|
|
833
|
-
},
|
|
834
|
-
{
|
|
835
|
-
name: "list_webhooks",
|
|
836
|
-
description: "List active webhooks for this mesh.",
|
|
837
|
-
inputSchema: { type: "object", properties: {} }
|
|
838
|
-
},
|
|
839
|
-
{
|
|
840
|
-
name: "delete_webhook",
|
|
841
|
-
description: "Deactivate a webhook.",
|
|
842
|
-
inputSchema: {
|
|
843
|
-
type: "object",
|
|
844
|
-
properties: {
|
|
845
|
-
name: { type: "string", description: "Webhook name to deactivate" }
|
|
846
|
-
},
|
|
847
|
-
required: ["name"]
|
|
848
|
-
}
|
|
849
|
-
},
|
|
850
|
-
{
|
|
851
|
-
name: "mesh_mcp_deploy",
|
|
852
|
-
description: "Deploy an MCP server to the mesh from a zip file or git repo. Runs on the broker VPS, persists across peer sessions. Default scope: private (only you).",
|
|
853
|
-
inputSchema: {
|
|
854
|
-
type: "object",
|
|
855
|
-
properties: {
|
|
856
|
-
server_name: { type: "string", description: "Unique name for the server in this mesh" },
|
|
857
|
-
file_id: { type: "string", description: "File ID of uploaded zip (from share_file)" },
|
|
858
|
-
git_url: { type: "string", description: "Git repo URL" },
|
|
859
|
-
git_branch: { type: "string", description: "Branch to clone (default: main)" },
|
|
860
|
-
npx_package: { type: "string", description: "npm package name to run via npx (e.g. @upstash/context7-mcp)" },
|
|
861
|
-
env: { type: "object", description: "Environment variables. Use $vault:<key> for vault secrets." },
|
|
862
|
-
runtime: { type: "string", enum: ["node", "python", "bun"], description: "Runtime (auto-detected if omitted)" },
|
|
863
|
-
memory_mb: { type: "number", description: "Memory limit in MB (default: 256)" },
|
|
864
|
-
network_allow: { type: "array", items: { type: "string" }, description: "Allowed outbound hosts (default: none)" },
|
|
865
|
-
scope: { description: "Visibility: 'peer' (default), 'mesh', or {group/groups/role/peers}" }
|
|
866
|
-
},
|
|
867
|
-
required: ["server_name"]
|
|
868
|
-
}
|
|
869
|
-
},
|
|
870
|
-
{
|
|
871
|
-
name: "mesh_mcp_undeploy",
|
|
872
|
-
description: "Stop and remove a managed MCP server from the mesh.",
|
|
873
|
-
inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
name: "mesh_mcp_update",
|
|
877
|
-
description: "Pull latest code and restart a git-sourced MCP server.",
|
|
878
|
-
inputSchema: { type: "object", properties: { server_name: { type: "string" } }, required: ["server_name"] }
|
|
879
|
-
},
|
|
880
|
-
{
|
|
881
|
-
name: "mesh_mcp_logs",
|
|
882
|
-
description: "View recent logs from a managed MCP server.",
|
|
883
|
-
inputSchema: { type: "object", properties: { server_name: { type: "string" }, lines: { type: "number", description: "Lines (default: 50, max: 1000)" } }, required: ["server_name"] }
|
|
884
|
-
},
|
|
885
|
-
{
|
|
886
|
-
name: "mesh_mcp_scope",
|
|
887
|
-
description: "Get or set the visibility scope of a deployed MCP server.",
|
|
888
|
-
inputSchema: { type: "object", properties: { server_name: { type: "string" }, scope: { description: "New scope to set. Omit to read current." } }, required: ["server_name"] }
|
|
889
|
-
},
|
|
890
|
-
{
|
|
891
|
-
name: "mesh_mcp_schema",
|
|
892
|
-
description: "Inspect tool schemas for a deployed MCP server.",
|
|
893
|
-
inputSchema: { type: "object", properties: { server_name: { type: "string" }, tool_name: { type: "string", description: "Specific tool (omit for all)" } }, required: ["server_name"] }
|
|
894
|
-
},
|
|
895
|
-
{
|
|
896
|
-
name: "mesh_mcp_catalog",
|
|
897
|
-
description: "List all deployed services in the mesh with status, scope, and tool count.",
|
|
898
|
-
inputSchema: { type: "object", properties: {} }
|
|
899
|
-
},
|
|
900
|
-
{
|
|
901
|
-
name: "mesh_skill_deploy",
|
|
902
|
-
description: "Deploy a multi-file skill bundle from a zip or git repo.",
|
|
903
|
-
inputSchema: { type: "object", properties: { file_id: { type: "string" }, git_url: { type: "string" }, git_branch: { type: "string" } } }
|
|
904
|
-
},
|
|
905
|
-
{
|
|
906
|
-
name: "vault_set",
|
|
907
|
-
description: "Store an encrypted credential in your vault. Reference in mesh_mcp_deploy with $vault:<key>.",
|
|
908
|
-
inputSchema: { type: "object", properties: { key: { type: "string" }, value: { type: "string", description: "Secret value or local file path (for type=file)" }, type: { type: "string", enum: ["env", "file"] }, mount_path: { type: "string" }, description: { type: "string" } }, required: ["key", "value"] }
|
|
909
|
-
},
|
|
910
|
-
{
|
|
911
|
-
name: "vault_list",
|
|
912
|
-
description: "List your vault entries (keys and metadata only, no secret values).",
|
|
913
|
-
inputSchema: { type: "object", properties: {} }
|
|
914
|
-
},
|
|
915
|
-
{
|
|
916
|
-
name: "vault_delete",
|
|
917
|
-
description: "Remove a credential from your vault.",
|
|
918
|
-
inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] }
|
|
919
|
-
},
|
|
920
|
-
{
|
|
921
|
-
name: "mesh_watch",
|
|
922
|
-
description: "Watch a URL for changes. The broker polls it at the given interval and notifies you when the response changes. Works with any URL — websites (hash mode), JSON APIs (json mode), or status codes (status mode).",
|
|
923
|
-
inputSchema: {
|
|
924
|
-
type: "object",
|
|
925
|
-
properties: {
|
|
926
|
-
url: { type: "string", description: "URL to watch" },
|
|
927
|
-
mode: { type: "string", enum: ["hash", "json", "status"], description: "Detection mode: hash (SHA-256 of body), json (extract jsonpath value), status (HTTP status code). Default: hash" },
|
|
928
|
-
extract: { type: "string", description: "For json mode: dot path to extract (e.g. 'status' or 'data.deployments[0].status')" },
|
|
929
|
-
interval: { type: "number", description: "Poll interval in seconds (min: 5, default: 30)" },
|
|
930
|
-
notify_on: { type: "string", description: "When to notify: 'change' (default), 'match:<value>', 'not_match:<value>'" },
|
|
931
|
-
headers: { type: "object", description: "Optional HTTP headers (e.g. for auth)" },
|
|
932
|
-
label: { type: "string", description: "Human-readable label for this watch" }
|
|
933
|
-
},
|
|
934
|
-
required: ["url"]
|
|
935
|
-
}
|
|
936
|
-
},
|
|
937
|
-
{
|
|
938
|
-
name: "mesh_unwatch",
|
|
939
|
-
description: "Stop watching a URL.",
|
|
940
|
-
inputSchema: {
|
|
941
|
-
type: "object",
|
|
942
|
-
properties: { watch_id: { type: "string" } },
|
|
943
|
-
required: ["watch_id"]
|
|
944
|
-
}
|
|
945
|
-
},
|
|
946
|
-
{
|
|
947
|
-
name: "mesh_watches",
|
|
948
|
-
description: "List your active URL watches.",
|
|
949
|
-
inputSchema: { type: "object", properties: {} }
|
|
950
|
-
}
|
|
951
|
-
];
|
|
34
|
+
TOOLS = [];
|
|
952
35
|
});
|
|
953
36
|
|
|
954
37
|
// src/constants/paths.ts
|
|
@@ -1092,11 +175,6 @@ var init_facade = __esm(() => {
|
|
|
1092
175
|
});
|
|
1093
176
|
|
|
1094
177
|
// src/services/crypto/keypair.ts
|
|
1095
|
-
var exports_keypair = {};
|
|
1096
|
-
__export(exports_keypair, {
|
|
1097
|
-
generateKeypair: () => generateKeypair,
|
|
1098
|
-
ensureSodium: () => ensureSodium
|
|
1099
|
-
});
|
|
1100
178
|
import sodium from "libsodium-wrappers";
|
|
1101
179
|
async function ensureSodium() {
|
|
1102
180
|
if (!ready) {
|
|
@@ -1117,13 +195,6 @@ var ready = false;
|
|
|
1117
195
|
var init_keypair = () => {};
|
|
1118
196
|
|
|
1119
197
|
// src/services/crypto/file-crypto.ts
|
|
1120
|
-
var exports_file_crypto = {};
|
|
1121
|
-
__export(exports_file_crypto, {
|
|
1122
|
-
sealKeyForPeer: () => sealKeyForPeer,
|
|
1123
|
-
openSealedKey: () => openSealedKey,
|
|
1124
|
-
encryptFile: () => encryptFile,
|
|
1125
|
-
decryptFile: () => decryptFile
|
|
1126
|
-
});
|
|
1127
198
|
async function encryptFile(plaintext) {
|
|
1128
199
|
const s = await ensureSodium();
|
|
1129
200
|
const key = s.randombytes_buf(s.crypto_secretbox_KEYBYTES);
|
|
@@ -3295,16 +2366,6 @@ async function startClients(config) {
|
|
|
3295
2366
|
configGroups = config.groups ?? [];
|
|
3296
2367
|
await Promise.allSettled(config.meshes.map(ensureClient));
|
|
3297
2368
|
}
|
|
3298
|
-
function findClient(needle) {
|
|
3299
|
-
const byId = clients.get(needle);
|
|
3300
|
-
if (byId)
|
|
3301
|
-
return byId;
|
|
3302
|
-
for (const c of clients.values()) {
|
|
3303
|
-
if (c.meshSlug === needle)
|
|
3304
|
-
return c;
|
|
3305
|
-
}
|
|
3306
|
-
return null;
|
|
3307
|
-
}
|
|
3308
2369
|
function allClients() {
|
|
3309
2370
|
return [...clients.values()];
|
|
3310
2371
|
}
|
|
@@ -3749,7 +2810,7 @@ __export(exports_urls, {
|
|
|
3749
2810
|
VERSION: () => VERSION,
|
|
3750
2811
|
URLS: () => URLS
|
|
3751
2812
|
});
|
|
3752
|
-
var URLS, VERSION = "1.
|
|
2813
|
+
var URLS, VERSION = "1.5.0", env;
|
|
3753
2814
|
var init_urls = __esm(() => {
|
|
3754
2815
|
URLS = {
|
|
3755
2816
|
BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
|
|
@@ -4643,135 +3704,6 @@ function relativeTime(isoStr) {
|
|
|
4643
3704
|
const days = Math.floor(hours / 24);
|
|
4644
3705
|
return `${days} day${days !== 1 ? "s" : ""} ago`;
|
|
4645
3706
|
}
|
|
4646
|
-
function text(msg, isError = false) {
|
|
4647
|
-
return {
|
|
4648
|
-
content: [{ type: "text", text: msg }],
|
|
4649
|
-
...isError ? { isError: true } : {}
|
|
4650
|
-
};
|
|
4651
|
-
}
|
|
4652
|
-
async function resolveClient(to) {
|
|
4653
|
-
const clients2 = allClients();
|
|
4654
|
-
if (clients2.length === 0) {
|
|
4655
|
-
return { client: null, targetSpec: to, error: "no meshes joined" };
|
|
4656
|
-
}
|
|
4657
|
-
let targetClients = clients2;
|
|
4658
|
-
let target = to;
|
|
4659
|
-
const colonIdx = to.indexOf(":");
|
|
4660
|
-
if (colonIdx > 0 && colonIdx < to.length - 1) {
|
|
4661
|
-
const slug = to.slice(0, colonIdx);
|
|
4662
|
-
const rest = to.slice(colonIdx + 1);
|
|
4663
|
-
const match = findClient(slug);
|
|
4664
|
-
if (match) {
|
|
4665
|
-
targetClients = [match];
|
|
4666
|
-
target = rest;
|
|
4667
|
-
}
|
|
4668
|
-
}
|
|
4669
|
-
if (target.startsWith("#") || target.startsWith("@") || target === "*") {
|
|
4670
|
-
if (targetClients.length === 1) {
|
|
4671
|
-
return { client: targetClients[0], targetSpec: target };
|
|
4672
|
-
}
|
|
4673
|
-
return {
|
|
4674
|
-
client: null,
|
|
4675
|
-
targetSpec: target,
|
|
4676
|
-
error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
|
|
4677
|
-
};
|
|
4678
|
-
}
|
|
4679
|
-
if (/^[0-9a-f]{8,64}$/.test(target)) {
|
|
4680
|
-
const hits = [];
|
|
4681
|
-
for (const c of targetClients) {
|
|
4682
|
-
const peers = await c.listPeers();
|
|
4683
|
-
for (const p of peers) {
|
|
4684
|
-
if (p.pubkey.startsWith(target)) {
|
|
4685
|
-
hits.push({ mesh: c, pubkey: p.pubkey, displayName: p.displayName });
|
|
4686
|
-
}
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
if (hits.length === 1) {
|
|
4690
|
-
return { client: hits[0].mesh, targetSpec: hits[0].pubkey };
|
|
4691
|
-
}
|
|
4692
|
-
if (hits.length > 1) {
|
|
4693
|
-
const lines = hits.map((h) => ` - ${h.displayName} @ ${h.mesh.meshSlug} · pubkey ${h.pubkey.slice(0, 20)}…`).join(`
|
|
4694
|
-
`);
|
|
4695
|
-
return {
|
|
4696
|
-
client: null,
|
|
4697
|
-
targetSpec: target,
|
|
4698
|
-
error: `ambiguous pubkey prefix "${target}" matches ${hits.length} peers:
|
|
4699
|
-
${lines}
|
|
4700
|
-
Use a longer prefix.`
|
|
4701
|
-
};
|
|
4702
|
-
}
|
|
4703
|
-
if (target.length === 64) {
|
|
4704
|
-
if (targetClients.length === 1) {
|
|
4705
|
-
return { client: targetClients[0], targetSpec: target };
|
|
4706
|
-
}
|
|
4707
|
-
return {
|
|
4708
|
-
client: null,
|
|
4709
|
-
targetSpec: target,
|
|
4710
|
-
error: `multiple meshes joined; prefix target with "<mesh-slug>:" (joined: ${clients2.map((c) => c.meshSlug).join(", ")})`
|
|
4711
|
-
};
|
|
4712
|
-
}
|
|
4713
|
-
return {
|
|
4714
|
-
client: null,
|
|
4715
|
-
targetSpec: target,
|
|
4716
|
-
error: `no online peer's pubkey starts with "${target}".`
|
|
4717
|
-
};
|
|
4718
|
-
}
|
|
4719
|
-
const nameLower = target.toLowerCase();
|
|
4720
|
-
const candidates = [];
|
|
4721
|
-
const exactMatches = [];
|
|
4722
|
-
const partialMatches = [];
|
|
4723
|
-
for (const c of targetClients) {
|
|
4724
|
-
const ownSession = c.getSessionPubkey();
|
|
4725
|
-
const peers = await c.listPeers();
|
|
4726
|
-
candidates.push({ mesh: c.meshSlug, peers });
|
|
4727
|
-
for (const p of peers) {
|
|
4728
|
-
if (ownSession && p.pubkey === ownSession)
|
|
4729
|
-
continue;
|
|
4730
|
-
const nameLow = p.displayName.toLowerCase();
|
|
4731
|
-
if (nameLow === nameLower) {
|
|
4732
|
-
exactMatches.push({ mesh: c, pubkey: p.pubkey, displayName: p.displayName, cwd: p.cwd });
|
|
4733
|
-
} else if (nameLow.includes(nameLower)) {
|
|
4734
|
-
partialMatches.push({ mesh: c, pubkey: p.pubkey, displayName: p.displayName, cwd: p.cwd });
|
|
4735
|
-
}
|
|
4736
|
-
}
|
|
4737
|
-
}
|
|
4738
|
-
if (exactMatches.length === 1) {
|
|
4739
|
-
return { client: exactMatches[0].mesh, targetSpec: exactMatches[0].pubkey };
|
|
4740
|
-
}
|
|
4741
|
-
if (exactMatches.length > 1) {
|
|
4742
|
-
const lines = exactMatches.map((m) => ` - ${m.displayName} · pubkey ${m.pubkey.slice(0, 16)}…${m.cwd ? ` · cwd ${m.cwd}` : ""}`).join(`
|
|
4743
|
-
`);
|
|
4744
|
-
return {
|
|
4745
|
-
client: null,
|
|
4746
|
-
targetSpec: target,
|
|
4747
|
-
error: `"${target}" is ambiguous — ${exactMatches.length} peers share that display name:
|
|
4748
|
-
${lines}
|
|
4749
|
-
` + `Disambiguate by pubkey prefix (e.g. send to "${exactMatches[0].pubkey.slice(0, 12)}…").`
|
|
4750
|
-
};
|
|
4751
|
-
}
|
|
4752
|
-
if (partialMatches.length === 1) {
|
|
4753
|
-
process.stderr.write(`[claudemesh] resolved "${target}" → "${partialMatches[0].displayName}" (partial match)
|
|
4754
|
-
`);
|
|
4755
|
-
return { client: partialMatches[0].mesh, targetSpec: partialMatches[0].pubkey };
|
|
4756
|
-
}
|
|
4757
|
-
if (partialMatches.length > 1) {
|
|
4758
|
-
const lines = partialMatches.map((m) => ` - ${m.displayName} · pubkey ${m.pubkey.slice(0, 16)}…`).join(`
|
|
4759
|
-
`);
|
|
4760
|
-
return {
|
|
4761
|
-
client: null,
|
|
4762
|
-
targetSpec: target,
|
|
4763
|
-
error: `"${target}" partially matches ${partialMatches.length} peers:
|
|
4764
|
-
${lines}
|
|
4765
|
-
Be more specific, or use a pubkey prefix.`
|
|
4766
|
-
};
|
|
4767
|
-
}
|
|
4768
|
-
const known = candidates.flatMap((c) => c.peers.map((p) => `${c.mesh}/${p.displayName}`));
|
|
4769
|
-
return {
|
|
4770
|
-
client: null,
|
|
4771
|
-
targetSpec: target,
|
|
4772
|
-
error: `peer "${target}" not found. ` + (known.length ? `Known peers: ${known.slice(0, 10).join(", ")}${known.length > 10 ? ", …" : ""}` : "No connected peers on your mesh(es). Use pubkey hex, @group, or * for broadcast.")
|
|
4773
|
-
};
|
|
4774
|
-
}
|
|
4775
3707
|
async function resolvePeerName(client, pubkey) {
|
|
4776
3708
|
const now = Date.now();
|
|
4777
3709
|
if (now - peerNameCacheAge > CACHE_TTL_MS) {
|
|
@@ -4789,22 +3721,6 @@ function decryptFailedWarning(senderPubkey) {
|
|
|
4789
3721
|
const who = senderPubkey ? senderPubkey.slice(0, 12) + "…" : "unknown sender";
|
|
4790
3722
|
return `⚠ message from ${who} failed to decrypt (tampered or wrong keypair)`;
|
|
4791
3723
|
}
|
|
4792
|
-
function formatPush(p, meshSlug) {
|
|
4793
|
-
const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
|
|
4794
|
-
const tag = p.subtype === "reminder" ? " [REMINDER]" : "";
|
|
4795
|
-
return `[${meshSlug}]${tag} from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
|
|
4796
|
-
${body}`;
|
|
4797
|
-
}
|
|
4798
|
-
function maybeWarnDeprecated(toolName) {
|
|
4799
|
-
const hint = DEPRECATED_TOOL_HINTS[toolName];
|
|
4800
|
-
if (!hint)
|
|
4801
|
-
return;
|
|
4802
|
-
if (warnedTools.has(toolName))
|
|
4803
|
-
return;
|
|
4804
|
-
warnedTools.add(toolName);
|
|
4805
|
-
process.stderr.write(`[claudemesh] mcp tool "${toolName}" is soft-deprecated in 1.1.0 ` + `and will be removed in 2.0.0. Use \`${hint}\` instead.
|
|
4806
|
-
`);
|
|
4807
|
-
}
|
|
4808
3724
|
async function startMcpServer() {
|
|
4809
3725
|
const serviceIdx = process.argv.indexOf("--service");
|
|
4810
3726
|
if (serviceIdx !== -1 && process.argv[serviceIdx + 1]) {
|
|
@@ -5092,1482 +4008,6 @@ ${manifest.allowed_tools.map((t) => ` - ${t}`).join(`
|
|
|
5092
4008
|
]
|
|
5093
4009
|
};
|
|
5094
4010
|
});
|
|
5095
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
5096
|
-
const { name, arguments: args } = req.params;
|
|
5097
|
-
for (const c of allClients()) {
|
|
5098
|
-
c.incrementToolCalls();
|
|
5099
|
-
}
|
|
5100
|
-
if (config.meshes.length === 0) {
|
|
5101
|
-
return text("No meshes joined. Run `claudemesh join https://claudemesh.com/join/<token>` first.", true);
|
|
5102
|
-
}
|
|
5103
|
-
maybeWarnDeprecated(name);
|
|
5104
|
-
switch (name) {
|
|
5105
|
-
case "send_message": {
|
|
5106
|
-
const { to, message, priority } = args ?? {};
|
|
5107
|
-
if (!to || !message)
|
|
5108
|
-
return text("send_message: `to` and `message` required", true);
|
|
5109
|
-
const targets = Array.isArray(to) ? to : [to];
|
|
5110
|
-
const results = [];
|
|
5111
|
-
const seen = new Set;
|
|
5112
|
-
for (const target of targets) {
|
|
5113
|
-
const { client, targetSpec, error: error2 } = await resolveClient(target);
|
|
5114
|
-
if (!client) {
|
|
5115
|
-
results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
|
|
5116
|
-
continue;
|
|
5117
|
-
}
|
|
5118
|
-
if (seen.has(targetSpec))
|
|
5119
|
-
continue;
|
|
5120
|
-
seen.add(targetSpec);
|
|
5121
|
-
const result = await client.send(targetSpec, message, priority ?? "next");
|
|
5122
|
-
if (!result.ok) {
|
|
5123
|
-
results.push(`✗ ${target}: ${result.error}`);
|
|
5124
|
-
} else {
|
|
5125
|
-
results.push(`✓ ${target} → ${result.messageId}`);
|
|
5126
|
-
}
|
|
5127
|
-
}
|
|
5128
|
-
return text(results.join(`
|
|
5129
|
-
`));
|
|
5130
|
-
}
|
|
5131
|
-
case "list_peers": {
|
|
5132
|
-
const { mesh_slug } = args ?? {};
|
|
5133
|
-
const clients2 = mesh_slug ? [findClient(mesh_slug)].filter(Boolean) : allClients();
|
|
5134
|
-
if (clients2.length === 0)
|
|
5135
|
-
return text(mesh_slug ? `list_peers: no joined mesh "${mesh_slug}"` : "list_peers: no joined meshes", true);
|
|
5136
|
-
const sections = [];
|
|
5137
|
-
const statusCache = {};
|
|
5138
|
-
for (const c of clients2) {
|
|
5139
|
-
const peers = await c.listPeers();
|
|
5140
|
-
const header = `## ${c.meshSlug} (${c.status}, mesh ${c.meshId.slice(0, 8)}…)`;
|
|
5141
|
-
statusCache[c.meshSlug] = {
|
|
5142
|
-
total: peers.length,
|
|
5143
|
-
online: peers.filter((p) => p.status !== "offline").length,
|
|
5144
|
-
updatedAt: new Date().toISOString(),
|
|
5145
|
-
you: process.env.CLAUDEMESH_DISPLAY_NAME ?? undefined
|
|
5146
|
-
};
|
|
5147
|
-
if (peers.length === 0) {
|
|
5148
|
-
sections.push(`${header}
|
|
5149
|
-
No peers connected.`);
|
|
5150
|
-
} else {
|
|
5151
|
-
const pubkeyCounts = new Map;
|
|
5152
|
-
for (const p of peers)
|
|
5153
|
-
pubkeyCounts.set(p.pubkey, (pubkeyCounts.get(p.pubkey) ?? 0) + 1);
|
|
5154
|
-
const peerLines = peers.map((p) => {
|
|
5155
|
-
const summary = p.summary ? ` — "${p.summary}"` : "";
|
|
5156
|
-
const groupsStr = p.groups?.length ? ` [${p.groups.map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ")}]` : "";
|
|
5157
|
-
const meta = [];
|
|
5158
|
-
if (p.peerType)
|
|
5159
|
-
meta.push(`type:${p.peerType}`);
|
|
5160
|
-
if (p.channel)
|
|
5161
|
-
meta.push(`channel:${p.channel}`);
|
|
5162
|
-
if (p.model)
|
|
5163
|
-
meta.push(`model:${p.model}`);
|
|
5164
|
-
const metaStr = meta.length ? ` {${meta.join(", ")}}` : "";
|
|
5165
|
-
const cwdStr = p.cwd ? ` cwd:${p.cwd}` : "";
|
|
5166
|
-
const locality = p.hostname && p.hostname === __require("os").hostname() ? "local" : "remote";
|
|
5167
|
-
const localityTag = ` [${locality}]`;
|
|
5168
|
-
const profileAvatar = p.profile?.avatar ? `${p.profile.avatar} ` : "";
|
|
5169
|
-
const profileTitle = p.profile?.title ? ` (${p.profile.title})` : "";
|
|
5170
|
-
const hiddenTag = p.visible === false ? " [hidden]" : "";
|
|
5171
|
-
const sameKeyCount = pubkeyCounts.get(p.pubkey) ?? 1;
|
|
5172
|
-
const sameKeyTag = sameKeyCount > 1 ? ` [shares key with ${sameKeyCount - 1} other session(s)]` : "";
|
|
5173
|
-
return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${localityTag}${hiddenTag}${sameKeyTag}${groupsStr}${metaStr} (pubkey: ${p.pubkey.slice(0, 16)}…)${cwdStr}${summary}`;
|
|
5174
|
-
});
|
|
5175
|
-
sections.push(`${header}
|
|
5176
|
-
${peerLines.join(`
|
|
5177
|
-
`)}`);
|
|
5178
|
-
}
|
|
5179
|
-
}
|
|
5180
|
-
try {
|
|
5181
|
-
const { writeFileSync: writeFileSync4, mkdirSync: mkdirSync4, existsSync: existsSync6 } = await import("node:fs");
|
|
5182
|
-
const { join: joinPath } = await import("node:path");
|
|
5183
|
-
const { homedir: homedir4 } = await import("node:os");
|
|
5184
|
-
const dir = joinPath(homedir4(), ".claudemesh");
|
|
5185
|
-
if (!existsSync6(dir))
|
|
5186
|
-
mkdirSync4(dir, { recursive: true });
|
|
5187
|
-
writeFileSync4(joinPath(dir, "peer-cache.json"), JSON.stringify(statusCache));
|
|
5188
|
-
} catch {}
|
|
5189
|
-
return text(sections.join(`
|
|
5190
|
-
|
|
5191
|
-
`));
|
|
5192
|
-
}
|
|
5193
|
-
case "message_status": {
|
|
5194
|
-
const { id } = args ?? {};
|
|
5195
|
-
if (!id)
|
|
5196
|
-
return text("message_status: `id` required", true);
|
|
5197
|
-
const clients2 = allClients();
|
|
5198
|
-
if (!clients2.length)
|
|
5199
|
-
return text("message_status: not connected", true);
|
|
5200
|
-
let result = null;
|
|
5201
|
-
for (const c of clients2) {
|
|
5202
|
-
result = await c.messageStatus(id);
|
|
5203
|
-
if (result)
|
|
5204
|
-
break;
|
|
5205
|
-
}
|
|
5206
|
-
if (!result)
|
|
5207
|
-
return text(`Message ${id} not found or timed out.`);
|
|
5208
|
-
const recipientLines = result.recipients.map((r) => ` - ${r.name} (${r.pubkey.slice(0, 12)}…): ${r.status}`);
|
|
5209
|
-
return text(`Message ${id.slice(0, 12)}… → ${result.targetSpec}
|
|
5210
|
-
Delivered: ${result.delivered}${result.deliveredAt ? ` at ${result.deliveredAt}` : ""}
|
|
5211
|
-
Recipients:
|
|
5212
|
-
${recipientLines.join(`
|
|
5213
|
-
`)}`);
|
|
5214
|
-
}
|
|
5215
|
-
case "check_messages": {
|
|
5216
|
-
const drained = [];
|
|
5217
|
-
for (const c of allClients()) {
|
|
5218
|
-
const msgs = c.drainPushBuffer();
|
|
5219
|
-
for (const m of msgs)
|
|
5220
|
-
drained.push(formatPush(m, c.meshSlug));
|
|
5221
|
-
}
|
|
5222
|
-
if (drained.length === 0)
|
|
5223
|
-
return text("No new messages.");
|
|
5224
|
-
return text(`${drained.length} new message(s):
|
|
5225
|
-
|
|
5226
|
-
${drained.join(`
|
|
5227
|
-
|
|
5228
|
-
---
|
|
5229
|
-
|
|
5230
|
-
`)}`);
|
|
5231
|
-
}
|
|
5232
|
-
case "set_summary": {
|
|
5233
|
-
const { summary } = args ?? {};
|
|
5234
|
-
if (!summary)
|
|
5235
|
-
return text("set_summary: `summary` required", true);
|
|
5236
|
-
for (const c of allClients())
|
|
5237
|
-
await c.setSummary(summary);
|
|
5238
|
-
return text(`Summary set: "${summary}" (visible to ${allClients().length} mesh(es)).`);
|
|
5239
|
-
}
|
|
5240
|
-
case "set_status": {
|
|
5241
|
-
const { status } = args ?? {};
|
|
5242
|
-
if (!status)
|
|
5243
|
-
return text("set_status: `status` required", true);
|
|
5244
|
-
const s = status;
|
|
5245
|
-
for (const c of allClients())
|
|
5246
|
-
await c.setStatus(s);
|
|
5247
|
-
return text(`Status set to ${s} across ${allClients().length} mesh(es).`);
|
|
5248
|
-
}
|
|
5249
|
-
case "set_visible": {
|
|
5250
|
-
const { visible } = args ?? {};
|
|
5251
|
-
if (visible === undefined)
|
|
5252
|
-
return text("set_visible: `visible` required", true);
|
|
5253
|
-
for (const c of allClients())
|
|
5254
|
-
await c.setVisible(visible);
|
|
5255
|
-
return text(visible ? "You are now visible to peers." : "You are now hidden. Direct messages still reach you, but you won't appear in list_peers or receive broadcasts.");
|
|
5256
|
-
}
|
|
5257
|
-
case "set_profile": {
|
|
5258
|
-
const { avatar, title, bio, capabilities } = args ?? {};
|
|
5259
|
-
const profile = { avatar, title, bio, capabilities };
|
|
5260
|
-
for (const c of allClients())
|
|
5261
|
-
await c.setProfile(profile);
|
|
5262
|
-
const parts = [];
|
|
5263
|
-
if (avatar)
|
|
5264
|
-
parts.push(`Avatar: ${avatar}`);
|
|
5265
|
-
if (title)
|
|
5266
|
-
parts.push(`Title: ${title}`);
|
|
5267
|
-
if (bio)
|
|
5268
|
-
parts.push(`Bio: ${bio}`);
|
|
5269
|
-
if (capabilities?.length)
|
|
5270
|
-
parts.push(`Capabilities: ${capabilities.join(", ")}`);
|
|
5271
|
-
return text(parts.length > 0 ? `Profile updated:
|
|
5272
|
-
${parts.join(`
|
|
5273
|
-
`)}` : "Profile cleared.");
|
|
5274
|
-
}
|
|
5275
|
-
case "join_group": {
|
|
5276
|
-
const { name: groupName, role } = args ?? {};
|
|
5277
|
-
if (!groupName)
|
|
5278
|
-
return text("join_group: `name` required", true);
|
|
5279
|
-
for (const c of allClients())
|
|
5280
|
-
await c.joinGroup(groupName, role);
|
|
5281
|
-
return text(`Joined @${groupName}${role ? ` as ${role}` : ""}`);
|
|
5282
|
-
}
|
|
5283
|
-
case "leave_group": {
|
|
5284
|
-
const { name: groupName } = args ?? {};
|
|
5285
|
-
if (!groupName)
|
|
5286
|
-
return text("leave_group: `name` required", true);
|
|
5287
|
-
for (const c of allClients())
|
|
5288
|
-
await c.leaveGroup(groupName);
|
|
5289
|
-
return text(`Left @${groupName}`);
|
|
5290
|
-
}
|
|
5291
|
-
case "set_state": {
|
|
5292
|
-
const { key, value } = args ?? {};
|
|
5293
|
-
if (!key)
|
|
5294
|
-
return text("set_state: `key` required", true);
|
|
5295
|
-
for (const c of allClients())
|
|
5296
|
-
await c.setState(key, value);
|
|
5297
|
-
return text(`State set: ${key} = ${JSON.stringify(value)}`);
|
|
5298
|
-
}
|
|
5299
|
-
case "get_state": {
|
|
5300
|
-
const { key } = args ?? {};
|
|
5301
|
-
if (!key)
|
|
5302
|
-
return text("get_state: `key` required", true);
|
|
5303
|
-
const client = allClients()[0];
|
|
5304
|
-
if (!client)
|
|
5305
|
-
return text("get_state: not connected", true);
|
|
5306
|
-
const result = await client.getState(key);
|
|
5307
|
-
if (!result)
|
|
5308
|
-
return text(`State "${key}" not found.`);
|
|
5309
|
-
return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
|
|
5310
|
-
}
|
|
5311
|
-
case "list_state": {
|
|
5312
|
-
const client = allClients()[0];
|
|
5313
|
-
if (!client)
|
|
5314
|
-
return text("list_state: not connected", true);
|
|
5315
|
-
const entries = await client.listState();
|
|
5316
|
-
if (entries.length === 0)
|
|
5317
|
-
return text("No shared state set.");
|
|
5318
|
-
const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
|
|
5319
|
-
return text(lines.join(`
|
|
5320
|
-
`));
|
|
5321
|
-
}
|
|
5322
|
-
case "remember": {
|
|
5323
|
-
const { content, tags } = args ?? {};
|
|
5324
|
-
if (!content)
|
|
5325
|
-
return text("remember: `content` required", true);
|
|
5326
|
-
const client = allClients()[0];
|
|
5327
|
-
if (!client)
|
|
5328
|
-
return text("remember: not connected", true);
|
|
5329
|
-
const id = await client.remember(content, tags);
|
|
5330
|
-
return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
|
|
5331
|
-
}
|
|
5332
|
-
case "recall": {
|
|
5333
|
-
const { query } = args ?? {};
|
|
5334
|
-
if (!query)
|
|
5335
|
-
return text("recall: `query` required", true);
|
|
5336
|
-
const client = allClients()[0];
|
|
5337
|
-
if (!client)
|
|
5338
|
-
return text("recall: not connected", true);
|
|
5339
|
-
const memories = await client.recall(query);
|
|
5340
|
-
if (memories.length === 0)
|
|
5341
|
-
return text(`No memories found for "${query}".`);
|
|
5342
|
-
const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
|
|
5343
|
-
return text(`${memories.length} memor${memories.length === 1 ? "y" : "ies"}:
|
|
5344
|
-
${lines.join(`
|
|
5345
|
-
`)}`);
|
|
5346
|
-
}
|
|
5347
|
-
case "forget": {
|
|
5348
|
-
const { id } = args ?? {};
|
|
5349
|
-
if (!id)
|
|
5350
|
-
return text("forget: `id` required", true);
|
|
5351
|
-
const client = allClients()[0];
|
|
5352
|
-
if (!client)
|
|
5353
|
-
return text("forget: not connected", true);
|
|
5354
|
-
await client.forget(id);
|
|
5355
|
-
return text(`Forgotten: ${id}`);
|
|
5356
|
-
}
|
|
5357
|
-
case "schedule_reminder": {
|
|
5358
|
-
const sArgs = args ?? {};
|
|
5359
|
-
if (!sArgs.message)
|
|
5360
|
-
return text("schedule_reminder: `message` required", true);
|
|
5361
|
-
const client = allClients()[0];
|
|
5362
|
-
if (!client)
|
|
5363
|
-
return text("schedule_reminder: not connected", true);
|
|
5364
|
-
const isCron = !!sArgs.cron;
|
|
5365
|
-
let deliverAt;
|
|
5366
|
-
if (isCron) {
|
|
5367
|
-
deliverAt = 0;
|
|
5368
|
-
} else if (sArgs.deliver_at) {
|
|
5369
|
-
deliverAt = Number(sArgs.deliver_at);
|
|
5370
|
-
} else if (sArgs.in_seconds) {
|
|
5371
|
-
deliverAt = Date.now() + Number(sArgs.in_seconds) * 1000;
|
|
5372
|
-
} else {
|
|
5373
|
-
return text("schedule_reminder: provide `deliver_at` (ms timestamp), `in_seconds`, or `cron` expression", true);
|
|
5374
|
-
}
|
|
5375
|
-
const isSelf = !sArgs.to;
|
|
5376
|
-
let targetSpec;
|
|
5377
|
-
if (isSelf) {
|
|
5378
|
-
targetSpec = client.getSessionPubkey() ?? "*";
|
|
5379
|
-
} else {
|
|
5380
|
-
const to = sArgs.to;
|
|
5381
|
-
if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
|
|
5382
|
-
const peers = await client.listPeers();
|
|
5383
|
-
const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
|
|
5384
|
-
if (!match) {
|
|
5385
|
-
const names = peers.map((p) => p.displayName).join(", ");
|
|
5386
|
-
return text(`schedule_reminder: peer "${to}" not found. Online: ${names || "(none)"}`, true);
|
|
5387
|
-
}
|
|
5388
|
-
targetSpec = match.pubkey;
|
|
5389
|
-
} else {
|
|
5390
|
-
targetSpec = to;
|
|
5391
|
-
}
|
|
5392
|
-
}
|
|
5393
|
-
const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true, sArgs.cron);
|
|
5394
|
-
if (!result)
|
|
5395
|
-
return text("schedule_reminder: broker did not acknowledge — check connection", true);
|
|
5396
|
-
if (isCron) {
|
|
5397
|
-
const nextFire = new Date(result.deliverAt).toISOString();
|
|
5398
|
-
return text(isSelf ? `Recurring self-reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" — cron: ${sArgs.cron}, next fire: ${nextFire}` : `Recurring reminder to "${sArgs.to}" scheduled (${result.scheduledId.slice(0, 8)}) — cron: ${sArgs.cron}, next fire: ${nextFire}`);
|
|
5399
|
-
}
|
|
5400
|
-
const when = new Date(result.deliverAt).toISOString();
|
|
5401
|
-
return text(isSelf ? `Self-reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" at ${when}` : `Reminder to "${sArgs.to}" scheduled (${result.scheduledId.slice(0, 8)}) for ${when}`);
|
|
5402
|
-
}
|
|
5403
|
-
case "list_scheduled": {
|
|
5404
|
-
const client = allClients()[0];
|
|
5405
|
-
if (!client)
|
|
5406
|
-
return text("list_scheduled: not connected", true);
|
|
5407
|
-
const scheduled = await client.listScheduled();
|
|
5408
|
-
if (scheduled.length === 0)
|
|
5409
|
-
return text("No pending scheduled messages.");
|
|
5410
|
-
const lines = scheduled.map((m) => `- [${m.id.slice(0, 8)}] → ${m.to === client.getSessionPubkey() ? "self (reminder)" : m.to} at ${new Date(m.deliverAt).toISOString()}: "${m.message.slice(0, 60)}${m.message.length > 60 ? "…" : ""}"`);
|
|
5411
|
-
return text(`${scheduled.length} scheduled:
|
|
5412
|
-
${lines.join(`
|
|
5413
|
-
`)}`);
|
|
5414
|
-
}
|
|
5415
|
-
case "cancel_scheduled": {
|
|
5416
|
-
const client = allClients()[0];
|
|
5417
|
-
if (!client)
|
|
5418
|
-
return text("cancel_scheduled: not connected", true);
|
|
5419
|
-
const { id: schedId } = args ?? {};
|
|
5420
|
-
if (!schedId)
|
|
5421
|
-
return text("cancel_scheduled: `id` required", true);
|
|
5422
|
-
const ok = await client.cancelScheduled(schedId);
|
|
5423
|
-
return text(ok ? `Cancelled: ${schedId}` : `Not found or already fired: ${schedId}`, !ok);
|
|
5424
|
-
}
|
|
5425
|
-
case "share_file": {
|
|
5426
|
-
const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
|
|
5427
|
-
if (!filePath)
|
|
5428
|
-
return text("share_file: `path` required", true);
|
|
5429
|
-
const { existsSync: existsSync6 } = await import("node:fs");
|
|
5430
|
-
if (!existsSync6(filePath))
|
|
5431
|
-
return text(`share_file: file not found: ${filePath}`, true);
|
|
5432
|
-
const client = allClients()[0];
|
|
5433
|
-
if (!client)
|
|
5434
|
-
return text("share_file: not connected", true);
|
|
5435
|
-
if (fileTo) {
|
|
5436
|
-
const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
5437
|
-
const { readFileSync: readFileSync4, writeFileSync: writeFileSync4, mkdtempSync, unlinkSync: unlinkSync3, rmdirSync } = await import("node:fs");
|
|
5438
|
-
const { tmpdir } = await import("node:os");
|
|
5439
|
-
const { join: join4, basename } = await import("node:path");
|
|
5440
|
-
const peers = await client.listPeers();
|
|
5441
|
-
const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
|
|
5442
|
-
if (!targetPeer) {
|
|
5443
|
-
return text(`share_file: peer not found: ${fileTo}`, true);
|
|
5444
|
-
}
|
|
5445
|
-
const plaintext = readFileSync4(filePath);
|
|
5446
|
-
const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
|
|
5447
|
-
const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
|
|
5448
|
-
const myPubkey = client.getSessionPubkey();
|
|
5449
|
-
const sealedForSelf = myPubkey ? await sealKeyForPeer2(key, myPubkey) : null;
|
|
5450
|
-
const fileKeys = [
|
|
5451
|
-
{ peerPubkey: targetPeer.pubkey, sealedKey: sealedForTarget },
|
|
5452
|
-
...sealedForSelf && myPubkey ? [{ peerPubkey: myPubkey, sealedKey: sealedForSelf }] : []
|
|
5453
|
-
];
|
|
5454
|
-
const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
|
|
5455
|
-
const sodium2 = await ensureSodium2();
|
|
5456
|
-
const nonceBytes = sodium2.from_base64(nonce, sodium2.base64_variants.ORIGINAL);
|
|
5457
|
-
const combined = new Uint8Array(nonceBytes.length + ciphertext.length);
|
|
5458
|
-
combined.set(nonceBytes, 0);
|
|
5459
|
-
combined.set(ciphertext, nonceBytes.length);
|
|
5460
|
-
const rawName = fileName ?? basename(filePath);
|
|
5461
|
-
const baseName = basename(rawName).replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 255);
|
|
5462
|
-
const tmpDir = mkdtempSync(join4(tmpdir(), "cm-"));
|
|
5463
|
-
const tmpPath = join4(tmpDir, baseName);
|
|
5464
|
-
writeFileSync4(tmpPath, combined);
|
|
5465
|
-
try {
|
|
5466
|
-
const fileId = await client.uploadFile(tmpPath, client.meshId, client.meshSlug, {
|
|
5467
|
-
name: baseName,
|
|
5468
|
-
tags,
|
|
5469
|
-
persistent: true,
|
|
5470
|
-
encrypted: true,
|
|
5471
|
-
ownerPubkey: myPubkey ?? undefined,
|
|
5472
|
-
fileKeys
|
|
5473
|
-
});
|
|
5474
|
-
return text(`Shared (E2E encrypted): ${baseName} → ${targetPeer.displayName} (${fileId})`);
|
|
5475
|
-
} catch (e) {
|
|
5476
|
-
return text(`share_file: upload failed — ${e instanceof Error ? e.message : String(e)}`, true);
|
|
5477
|
-
} finally {
|
|
5478
|
-
try {
|
|
5479
|
-
unlinkSync3(tmpPath);
|
|
5480
|
-
} catch {}
|
|
5481
|
-
try {
|
|
5482
|
-
rmdirSync(tmpDir);
|
|
5483
|
-
} catch {}
|
|
5484
|
-
}
|
|
5485
|
-
}
|
|
5486
|
-
try {
|
|
5487
|
-
const fileId = await client.uploadFile(filePath, client.meshId, client.meshSlug, {
|
|
5488
|
-
name: fileName,
|
|
5489
|
-
tags,
|
|
5490
|
-
persistent: true
|
|
5491
|
-
});
|
|
5492
|
-
return text(`Shared: ${fileName ?? filePath} (${fileId})`);
|
|
5493
|
-
} catch (e) {
|
|
5494
|
-
return text(`share_file: upload failed — ${e instanceof Error ? e.message : String(e)}`, true);
|
|
5495
|
-
}
|
|
5496
|
-
}
|
|
5497
|
-
case "get_file": {
|
|
5498
|
-
const { id, save_to } = args ?? {};
|
|
5499
|
-
if (!id || !save_to)
|
|
5500
|
-
return text("get_file: `id` and `save_to` required", true);
|
|
5501
|
-
const client = allClients()[0];
|
|
5502
|
-
if (!client)
|
|
5503
|
-
return text("get_file: not connected", true);
|
|
5504
|
-
const result = await client.getFile(id);
|
|
5505
|
-
if (!result)
|
|
5506
|
-
return text(`get_file: file ${id} not found`, true);
|
|
5507
|
-
if (result.encrypted) {
|
|
5508
|
-
const genericErr = "get_file: could not decrypt — you may not have access to this file";
|
|
5509
|
-
if (!result.sealedKey)
|
|
5510
|
-
return text(genericErr, true);
|
|
5511
|
-
const { openSealedKey: openSealedKey2, decryptFile: decryptFile2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
5512
|
-
const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
|
|
5513
|
-
const myPubkey = client.getSessionPubkey();
|
|
5514
|
-
const mySecret = client.getSessionSecretKey();
|
|
5515
|
-
if (!myPubkey || !mySecret)
|
|
5516
|
-
return text(genericErr, true);
|
|
5517
|
-
const kf = await openSealedKey2(result.sealedKey, myPubkey, mySecret);
|
|
5518
|
-
if (!kf)
|
|
5519
|
-
return text(genericErr, true);
|
|
5520
|
-
const MAX_DOWNLOAD = 104857600;
|
|
5521
|
-
const resp = await fetch(result.url, { signal: AbortSignal.timeout(30000) });
|
|
5522
|
-
if (!resp.ok)
|
|
5523
|
-
return text(`get_file: download failed (${resp.status})`, true);
|
|
5524
|
-
const contentLength = parseInt(resp.headers.get("content-length") ?? "0", 10);
|
|
5525
|
-
if (contentLength > MAX_DOWNLOAD)
|
|
5526
|
-
return text(`get_file: file too large (${contentLength} bytes)`, true);
|
|
5527
|
-
const buf = new Uint8Array(await resp.arrayBuffer());
|
|
5528
|
-
if (buf.length > MAX_DOWNLOAD)
|
|
5529
|
-
return text(`get_file: file too large (${buf.length} bytes)`, true);
|
|
5530
|
-
const sodium2 = await ensureSodium2();
|
|
5531
|
-
const NONCE_BYTES = sodium2.crypto_secretbox_NONCEBYTES;
|
|
5532
|
-
if (buf.length < NONCE_BYTES)
|
|
5533
|
-
return text(genericErr, true);
|
|
5534
|
-
const nonce = sodium2.to_base64(buf.slice(0, NONCE_BYTES), sodium2.base64_variants.ORIGINAL);
|
|
5535
|
-
const ciphertext = buf.slice(NONCE_BYTES);
|
|
5536
|
-
const plaintext = await decryptFile2(ciphertext, nonce, kf);
|
|
5537
|
-
if (!plaintext)
|
|
5538
|
-
return text(genericErr, true);
|
|
5539
|
-
const { writeFileSync: writeFileSync5, mkdirSync: mkdirSync5 } = await import("node:fs");
|
|
5540
|
-
const { dirname: dirname2 } = await import("node:path");
|
|
5541
|
-
mkdirSync5(dirname2(save_to), { recursive: true });
|
|
5542
|
-
writeFileSync5(save_to, plaintext);
|
|
5543
|
-
return text(`Downloaded and decrypted: ${result.name} → ${save_to}`);
|
|
5544
|
-
}
|
|
5545
|
-
let res = await fetch(result.url, { signal: AbortSignal.timeout(1e4) }).catch(() => null);
|
|
5546
|
-
if (!res || !res.ok) {
|
|
5547
|
-
const brokerHttp = client.brokerUrl.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
|
|
5548
|
-
res = await fetch(`${brokerHttp}/download/${id}?mesh=${client.meshId}`, { signal: AbortSignal.timeout(30000) });
|
|
5549
|
-
}
|
|
5550
|
-
if (!res.ok)
|
|
5551
|
-
return text(`get_file: download failed (${res.status})`, true);
|
|
5552
|
-
const { writeFileSync: writeFileSync4, mkdirSync: mkdirSync4 } = await import("node:fs");
|
|
5553
|
-
const { dirname } = await import("node:path");
|
|
5554
|
-
mkdirSync4(dirname(save_to), { recursive: true });
|
|
5555
|
-
writeFileSync4(save_to, Buffer.from(await res.arrayBuffer()));
|
|
5556
|
-
return text(`Downloaded: ${result.name} → ${save_to}`);
|
|
5557
|
-
}
|
|
5558
|
-
case "list_files": {
|
|
5559
|
-
const { query, from } = args ?? {};
|
|
5560
|
-
const client = allClients()[0];
|
|
5561
|
-
if (!client)
|
|
5562
|
-
return text("list_files: not connected", true);
|
|
5563
|
-
const files = await client.listFiles(query, from);
|
|
5564
|
-
if (files.length === 0)
|
|
5565
|
-
return text("No files found.");
|
|
5566
|
-
const lines = files.map((f) => `- **${f.name}** (${f.id.slice(0, 8)}…, ${f.size} bytes) by ${f.uploadedBy}${f.tags.length ? ` [${f.tags.join(", ")}]` : ""}`);
|
|
5567
|
-
return text(lines.join(`
|
|
5568
|
-
`));
|
|
5569
|
-
}
|
|
5570
|
-
case "file_status": {
|
|
5571
|
-
const { id } = args ?? {};
|
|
5572
|
-
if (!id)
|
|
5573
|
-
return text("file_status: `id` required", true);
|
|
5574
|
-
const client = allClients()[0];
|
|
5575
|
-
if (!client)
|
|
5576
|
-
return text("file_status: not connected", true);
|
|
5577
|
-
const accesses = await client.fileStatus(id);
|
|
5578
|
-
if (accesses.length === 0)
|
|
5579
|
-
return text("No one has accessed this file yet.");
|
|
5580
|
-
const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
|
|
5581
|
-
return text(`Accessed by:
|
|
5582
|
-
${lines.join(`
|
|
5583
|
-
`)}`);
|
|
5584
|
-
}
|
|
5585
|
-
case "delete_file": {
|
|
5586
|
-
const { id } = args ?? {};
|
|
5587
|
-
if (!id)
|
|
5588
|
-
return text("delete_file: `id` required", true);
|
|
5589
|
-
const client = allClients()[0];
|
|
5590
|
-
if (!client)
|
|
5591
|
-
return text("delete_file: not connected", true);
|
|
5592
|
-
await client.deleteFile(id);
|
|
5593
|
-
return text(`Deleted: ${id}`);
|
|
5594
|
-
}
|
|
5595
|
-
case "vector_store": {
|
|
5596
|
-
const { collection, text: storeText, metadata } = args ?? {};
|
|
5597
|
-
if (!collection || !storeText)
|
|
5598
|
-
return text("vector_store: `collection` and `text` required", true);
|
|
5599
|
-
const client = allClients()[0];
|
|
5600
|
-
if (!client)
|
|
5601
|
-
return text("vector_store: not connected", true);
|
|
5602
|
-
const id = await client.vectorStore(collection, storeText, metadata);
|
|
5603
|
-
return text(`Stored in ${collection}${id ? ` (${id})` : ""}`);
|
|
5604
|
-
}
|
|
5605
|
-
case "vector_search": {
|
|
5606
|
-
const { collection, query, limit } = args ?? {};
|
|
5607
|
-
if (!collection || !query)
|
|
5608
|
-
return text("vector_search: `collection` and `query` required", true);
|
|
5609
|
-
const client = allClients()[0];
|
|
5610
|
-
if (!client)
|
|
5611
|
-
return text("vector_search: not connected", true);
|
|
5612
|
-
const results = await client.vectorSearch(collection, query, limit);
|
|
5613
|
-
if (results.length === 0)
|
|
5614
|
-
return text(`No results in ${collection} for "${query}".`);
|
|
5615
|
-
const lines = results.map((r) => `- [${r.id.slice(0, 8)}…] (score: ${r.score.toFixed(3)}) ${r.text.slice(0, 120)}${r.text.length > 120 ? "…" : ""}`);
|
|
5616
|
-
return text(`${results.length} result(s) in ${collection}:
|
|
5617
|
-
${lines.join(`
|
|
5618
|
-
`)}`);
|
|
5619
|
-
}
|
|
5620
|
-
case "vector_delete": {
|
|
5621
|
-
const { collection, id } = args ?? {};
|
|
5622
|
-
if (!collection || !id)
|
|
5623
|
-
return text("vector_delete: `collection` and `id` required", true);
|
|
5624
|
-
const client = allClients()[0];
|
|
5625
|
-
if (!client)
|
|
5626
|
-
return text("vector_delete: not connected", true);
|
|
5627
|
-
await client.vectorDelete(collection, id);
|
|
5628
|
-
return text(`Deleted ${id} from ${collection}`);
|
|
5629
|
-
}
|
|
5630
|
-
case "list_collections": {
|
|
5631
|
-
const client = allClients()[0];
|
|
5632
|
-
if (!client)
|
|
5633
|
-
return text("list_collections: not connected", true);
|
|
5634
|
-
const collections = await client.listCollections();
|
|
5635
|
-
if (collections.length === 0)
|
|
5636
|
-
return text("No vector collections.");
|
|
5637
|
-
return text(`Collections:
|
|
5638
|
-
${collections.map((c) => `- ${c}`).join(`
|
|
5639
|
-
`)}`);
|
|
5640
|
-
}
|
|
5641
|
-
case "graph_query": {
|
|
5642
|
-
const { cypher } = args ?? {};
|
|
5643
|
-
if (!cypher)
|
|
5644
|
-
return text("graph_query: `cypher` required", true);
|
|
5645
|
-
const client = allClients()[0];
|
|
5646
|
-
if (!client)
|
|
5647
|
-
return text("graph_query: not connected", true);
|
|
5648
|
-
const rows = await client.graphQuery(cypher);
|
|
5649
|
-
if (rows.length === 0)
|
|
5650
|
-
return text("No results.");
|
|
5651
|
-
return text(JSON.stringify(rows, null, 2));
|
|
5652
|
-
}
|
|
5653
|
-
case "graph_execute": {
|
|
5654
|
-
const { cypher } = args ?? {};
|
|
5655
|
-
if (!cypher)
|
|
5656
|
-
return text("graph_execute: `cypher` required", true);
|
|
5657
|
-
const client = allClients()[0];
|
|
5658
|
-
if (!client)
|
|
5659
|
-
return text("graph_execute: not connected", true);
|
|
5660
|
-
const rows = await client.graphExecute(cypher);
|
|
5661
|
-
return text(rows.length > 0 ? JSON.stringify(rows, null, 2) : "Executed successfully.");
|
|
5662
|
-
}
|
|
5663
|
-
case "share_context": {
|
|
5664
|
-
const { summary, files_read, key_findings, tags } = args ?? {};
|
|
5665
|
-
if (!summary)
|
|
5666
|
-
return text("share_context: `summary` required", true);
|
|
5667
|
-
const client = allClients()[0];
|
|
5668
|
-
if (!client)
|
|
5669
|
-
return text("share_context: not connected", true);
|
|
5670
|
-
await client.shareContext(summary, files_read, key_findings, tags);
|
|
5671
|
-
return text(`Context shared: "${summary.slice(0, 80)}${summary.length > 80 ? "…" : ""}"`);
|
|
5672
|
-
}
|
|
5673
|
-
case "get_context": {
|
|
5674
|
-
const { query } = args ?? {};
|
|
5675
|
-
if (!query)
|
|
5676
|
-
return text("get_context: `query` required", true);
|
|
5677
|
-
const client = allClients()[0];
|
|
5678
|
-
if (!client)
|
|
5679
|
-
return text("get_context: not connected", true);
|
|
5680
|
-
const contexts = await client.getContext(query);
|
|
5681
|
-
if (contexts.length === 0)
|
|
5682
|
-
return text(`No context found for "${query}".`);
|
|
5683
|
-
const lines = contexts.map((c) => {
|
|
5684
|
-
const files = c.filesRead.length ? `
|
|
5685
|
-
Files: ${c.filesRead.join(", ")}` : "";
|
|
5686
|
-
const findings = c.keyFindings.length ? `
|
|
5687
|
-
Findings: ${c.keyFindings.join("; ")}` : "";
|
|
5688
|
-
return `- **${c.peerName}** (${c.updatedAt}): ${c.summary}${files}${findings}`;
|
|
5689
|
-
});
|
|
5690
|
-
return text(`${contexts.length} context(s):
|
|
5691
|
-
${lines.join(`
|
|
5692
|
-
`)}`);
|
|
5693
|
-
}
|
|
5694
|
-
case "list_contexts": {
|
|
5695
|
-
const client = allClients()[0];
|
|
5696
|
-
if (!client)
|
|
5697
|
-
return text("list_contexts: not connected", true);
|
|
5698
|
-
const contexts = await client.listContexts();
|
|
5699
|
-
if (contexts.length === 0)
|
|
5700
|
-
return text("No peer contexts shared yet.");
|
|
5701
|
-
const lines = contexts.map((c) => `- **${c.peerName}**: ${c.summary}${c.tags.length ? ` [${c.tags.join(", ")}]` : ""}`);
|
|
5702
|
-
return text(`Peer contexts:
|
|
5703
|
-
${lines.join(`
|
|
5704
|
-
`)}`);
|
|
5705
|
-
}
|
|
5706
|
-
case "create_task": {
|
|
5707
|
-
const { title, assignee, priority, tags } = args ?? {};
|
|
5708
|
-
if (!title)
|
|
5709
|
-
return text("create_task: `title` required", true);
|
|
5710
|
-
const client = allClients()[0];
|
|
5711
|
-
if (!client)
|
|
5712
|
-
return text("create_task: not connected", true);
|
|
5713
|
-
const id = await client.createTask(title, assignee, priority, tags);
|
|
5714
|
-
return text(`Task created${id ? ` (${id})` : ""}: "${title}"${assignee ? ` → ${assignee}` : ""}`);
|
|
5715
|
-
}
|
|
5716
|
-
case "claim_task": {
|
|
5717
|
-
const { id } = args ?? {};
|
|
5718
|
-
if (!id)
|
|
5719
|
-
return text("claim_task: `id` required", true);
|
|
5720
|
-
const client = allClients()[0];
|
|
5721
|
-
if (!client)
|
|
5722
|
-
return text("claim_task: not connected", true);
|
|
5723
|
-
await client.claimTask(id);
|
|
5724
|
-
return text(`Claimed task: ${id}`);
|
|
5725
|
-
}
|
|
5726
|
-
case "complete_task": {
|
|
5727
|
-
const { id, result } = args ?? {};
|
|
5728
|
-
if (!id)
|
|
5729
|
-
return text("complete_task: `id` required", true);
|
|
5730
|
-
const client = allClients()[0];
|
|
5731
|
-
if (!client)
|
|
5732
|
-
return text("complete_task: not connected", true);
|
|
5733
|
-
await client.completeTask(id, result);
|
|
5734
|
-
return text(`Completed task: ${id}${result ? ` — ${result}` : ""}`);
|
|
5735
|
-
}
|
|
5736
|
-
case "list_tasks": {
|
|
5737
|
-
const { status, assignee } = args ?? {};
|
|
5738
|
-
const client = allClients()[0];
|
|
5739
|
-
if (!client)
|
|
5740
|
-
return text("list_tasks: not connected", true);
|
|
5741
|
-
const tasks = await client.listTasks(status, assignee);
|
|
5742
|
-
if (tasks.length === 0)
|
|
5743
|
-
return text("No tasks found.");
|
|
5744
|
-
const lines = tasks.map((t) => `- [${t.id.slice(0, 8)}…] **${t.title}** (${t.status}, ${t.priority}) ${t.assignee ? `→ ${t.assignee}` : "unassigned"} (by ${t.createdBy})`);
|
|
5745
|
-
return text(`${tasks.length} task(s):
|
|
5746
|
-
${lines.join(`
|
|
5747
|
-
`)}`);
|
|
5748
|
-
}
|
|
5749
|
-
case "mesh_query": {
|
|
5750
|
-
const { sql: querySql } = args ?? {};
|
|
5751
|
-
if (!querySql)
|
|
5752
|
-
return text("mesh_query: `sql` required", true);
|
|
5753
|
-
const client = allClients()[0];
|
|
5754
|
-
if (!client)
|
|
5755
|
-
return text("mesh_query: not connected", true);
|
|
5756
|
-
const result = await client.meshQuery(querySql);
|
|
5757
|
-
if (!result)
|
|
5758
|
-
return text("mesh_query: query failed or timed out", true);
|
|
5759
|
-
if (result.rows.length === 0)
|
|
5760
|
-
return text(`Query returned 0 rows.`);
|
|
5761
|
-
const header = `| ${result.columns.join(" | ")} |`;
|
|
5762
|
-
const sep = `| ${result.columns.map(() => "---").join(" | ")} |`;
|
|
5763
|
-
const rows = result.rows.map((r) => `| ${result.columns.map((c) => String(r[c] ?? "")).join(" | ")} |`);
|
|
5764
|
-
return text(`${result.rowCount} row(s):
|
|
5765
|
-
${header}
|
|
5766
|
-
${sep}
|
|
5767
|
-
${rows.join(`
|
|
5768
|
-
`)}`);
|
|
5769
|
-
}
|
|
5770
|
-
case "mesh_execute": {
|
|
5771
|
-
const { sql: execSql } = args ?? {};
|
|
5772
|
-
if (!execSql)
|
|
5773
|
-
return text("mesh_execute: `sql` required", true);
|
|
5774
|
-
const client = allClients()[0];
|
|
5775
|
-
if (!client)
|
|
5776
|
-
return text("mesh_execute: not connected", true);
|
|
5777
|
-
await client.meshExecute(execSql);
|
|
5778
|
-
return text(`Executed.`);
|
|
5779
|
-
}
|
|
5780
|
-
case "mesh_schema": {
|
|
5781
|
-
const client = allClients()[0];
|
|
5782
|
-
if (!client)
|
|
5783
|
-
return text("mesh_schema: not connected", true);
|
|
5784
|
-
const tables = await client.meshSchema();
|
|
5785
|
-
if (!tables || tables.length === 0)
|
|
5786
|
-
return text("No tables in mesh database.");
|
|
5787
|
-
const lines = tables.map((t) => `**${t.name}**: ${t.columns.map((c) => `${c.name} (${c.type}${c.nullable ? ", nullable" : ""})`).join(", ")}`);
|
|
5788
|
-
return text(lines.join(`
|
|
5789
|
-
`));
|
|
5790
|
-
}
|
|
5791
|
-
case "create_stream": {
|
|
5792
|
-
const { name: streamName } = args ?? {};
|
|
5793
|
-
if (!streamName)
|
|
5794
|
-
return text("create_stream: `name` required", true);
|
|
5795
|
-
const client = allClients()[0];
|
|
5796
|
-
if (!client)
|
|
5797
|
-
return text("create_stream: not connected", true);
|
|
5798
|
-
const streamId = await client.createStream(streamName);
|
|
5799
|
-
return text(`Stream created: ${streamName}${streamId ? ` (${streamId})` : ""}`);
|
|
5800
|
-
}
|
|
5801
|
-
case "publish": {
|
|
5802
|
-
const { stream: pubStream, data: pubData } = args ?? {};
|
|
5803
|
-
if (!pubStream)
|
|
5804
|
-
return text("publish: `stream` required", true);
|
|
5805
|
-
const client = allClients()[0];
|
|
5806
|
-
if (!client)
|
|
5807
|
-
return text("publish: not connected", true);
|
|
5808
|
-
await client.publish(pubStream, pubData);
|
|
5809
|
-
return text(`Published to ${pubStream}.`);
|
|
5810
|
-
}
|
|
5811
|
-
case "subscribe": {
|
|
5812
|
-
const { stream: subStream } = args ?? {};
|
|
5813
|
-
if (!subStream)
|
|
5814
|
-
return text("subscribe: `stream` required", true);
|
|
5815
|
-
const client = allClients()[0];
|
|
5816
|
-
if (!client)
|
|
5817
|
-
return text("subscribe: not connected", true);
|
|
5818
|
-
await client.subscribe(subStream);
|
|
5819
|
-
return text(`Subscribed to ${subStream}. Data pushes will arrive as channel notifications.`);
|
|
5820
|
-
}
|
|
5821
|
-
case "list_streams": {
|
|
5822
|
-
const client = allClients()[0];
|
|
5823
|
-
if (!client)
|
|
5824
|
-
return text("list_streams: not connected", true);
|
|
5825
|
-
const streams = await client.listStreams();
|
|
5826
|
-
if (streams.length === 0)
|
|
5827
|
-
return text("No active streams.");
|
|
5828
|
-
const lines = streams.map((s) => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`);
|
|
5829
|
-
return text(lines.join(`
|
|
5830
|
-
`));
|
|
5831
|
-
}
|
|
5832
|
-
case "mesh_set_clock": {
|
|
5833
|
-
const { speed } = args ?? {};
|
|
5834
|
-
if (!speed || speed < 1 || speed > 100)
|
|
5835
|
-
return text("mesh_set_clock: speed must be 1-100", true);
|
|
5836
|
-
const client = allClients()[0];
|
|
5837
|
-
if (!client)
|
|
5838
|
-
return text("mesh_set_clock: not connected", true);
|
|
5839
|
-
const result = await client.setClock(speed);
|
|
5840
|
-
if (!result)
|
|
5841
|
-
return text("mesh_set_clock: timed out", true);
|
|
5842
|
-
return text([
|
|
5843
|
-
`**Clock set to x${result.speed}**`,
|
|
5844
|
-
`Paused: ${result.paused}`,
|
|
5845
|
-
`Tick: ${result.tick}`,
|
|
5846
|
-
`Sim time: ${result.simTime}`,
|
|
5847
|
-
`Started at: ${result.startedAt}`
|
|
5848
|
-
].join(`
|
|
5849
|
-
`));
|
|
5850
|
-
}
|
|
5851
|
-
case "mesh_pause_clock": {
|
|
5852
|
-
const client = allClients()[0];
|
|
5853
|
-
if (!client)
|
|
5854
|
-
return text("mesh_pause_clock: not connected", true);
|
|
5855
|
-
const result = await client.pauseClock();
|
|
5856
|
-
if (!result)
|
|
5857
|
-
return text("mesh_pause_clock: timed out", true);
|
|
5858
|
-
return text([
|
|
5859
|
-
"**Clock paused**",
|
|
5860
|
-
`Speed: x${result.speed}`,
|
|
5861
|
-
`Tick: ${result.tick}`,
|
|
5862
|
-
`Sim time: ${result.simTime}`
|
|
5863
|
-
].join(`
|
|
5864
|
-
`));
|
|
5865
|
-
}
|
|
5866
|
-
case "mesh_resume_clock": {
|
|
5867
|
-
const client = allClients()[0];
|
|
5868
|
-
if (!client)
|
|
5869
|
-
return text("mesh_resume_clock: not connected", true);
|
|
5870
|
-
const result = await client.resumeClock();
|
|
5871
|
-
if (!result)
|
|
5872
|
-
return text("mesh_resume_clock: timed out", true);
|
|
5873
|
-
return text([
|
|
5874
|
-
"**Clock resumed**",
|
|
5875
|
-
`Speed: x${result.speed}`,
|
|
5876
|
-
`Tick: ${result.tick}`,
|
|
5877
|
-
`Sim time: ${result.simTime}`
|
|
5878
|
-
].join(`
|
|
5879
|
-
`));
|
|
5880
|
-
}
|
|
5881
|
-
case "mesh_clock": {
|
|
5882
|
-
const client = allClients()[0];
|
|
5883
|
-
if (!client)
|
|
5884
|
-
return text("mesh_clock: not connected", true);
|
|
5885
|
-
const result = await client.getClock();
|
|
5886
|
-
if (!result)
|
|
5887
|
-
return text("mesh_clock: timed out", true);
|
|
5888
|
-
const statusLabel = result.speed === 0 ? "not started" : result.paused ? "paused" : "running";
|
|
5889
|
-
return text([
|
|
5890
|
-
`**Clock status: ${statusLabel}**`,
|
|
5891
|
-
`Speed: x${result.speed}`,
|
|
5892
|
-
`Tick: ${result.tick}`,
|
|
5893
|
-
`Sim time: ${result.simTime}`,
|
|
5894
|
-
`Started at: ${result.startedAt}`
|
|
5895
|
-
].join(`
|
|
5896
|
-
`));
|
|
5897
|
-
}
|
|
5898
|
-
case "mesh_info": {
|
|
5899
|
-
const client = allClients()[0];
|
|
5900
|
-
if (!client)
|
|
5901
|
-
return text("mesh_info: not connected", true);
|
|
5902
|
-
const info = await client.meshInfo();
|
|
5903
|
-
if (!info)
|
|
5904
|
-
return text("mesh_info: timed out", true);
|
|
5905
|
-
const lines = [
|
|
5906
|
-
`**Mesh**: ${info.mesh}`,
|
|
5907
|
-
`**Peers**: ${info.peers}`,
|
|
5908
|
-
`**Groups**: ${info.groups?.join(", ") || "none"}`,
|
|
5909
|
-
`**State keys**: ${info.stateKeys?.join(", ") || "none"}`,
|
|
5910
|
-
`**Memories**: ${info.memoryCount}`,
|
|
5911
|
-
`**Files**: ${info.fileCount}`,
|
|
5912
|
-
`**Tasks**: open=${info.tasks?.open ?? 0}, claimed=${info.tasks?.claimed ?? 0}, done=${info.tasks?.done ?? 0}`,
|
|
5913
|
-
`**Streams**: ${info.streams?.join(", ") || "none"}`,
|
|
5914
|
-
`**Tables**: ${info.tables?.join(", ") || "none"}`,
|
|
5915
|
-
`**Your name**: ${info.yourName}`,
|
|
5916
|
-
`**Your groups**: ${info.yourGroups?.map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ") || "none"}`
|
|
5917
|
-
];
|
|
5918
|
-
return text(lines.join(`
|
|
5919
|
-
`));
|
|
5920
|
-
}
|
|
5921
|
-
case "mesh_stats": {
|
|
5922
|
-
const clients2 = allClients();
|
|
5923
|
-
if (clients2.length === 0)
|
|
5924
|
-
return text("mesh_stats: no joined meshes", true);
|
|
5925
|
-
const sections = [];
|
|
5926
|
-
for (const c of clients2) {
|
|
5927
|
-
const peers = await c.listPeers();
|
|
5928
|
-
const header = `## ${c.meshSlug}`;
|
|
5929
|
-
const rows = peers.map((p) => {
|
|
5930
|
-
const s = p.stats;
|
|
5931
|
-
if (!s)
|
|
5932
|
-
return `| ${p.displayName} | - | - | - | - | - |`;
|
|
5933
|
-
const up = s.uptime != null ? `${Math.floor(s.uptime / 60)}m` : "-";
|
|
5934
|
-
return `| ${p.displayName} | ${s.messagesIn ?? 0} | ${s.messagesOut ?? 0} | ${s.toolCalls ?? 0} | ${up} | ${s.errors ?? 0} |`;
|
|
5935
|
-
});
|
|
5936
|
-
sections.push(`${header}
|
|
5937
|
-
| Peer | Msgs In | Msgs Out | Tool Calls | Uptime | Errors |
|
|
5938
|
-
|------|---------|----------|------------|--------|--------|
|
|
5939
|
-
${rows.join(`
|
|
5940
|
-
`)}`);
|
|
5941
|
-
}
|
|
5942
|
-
return text(sections.join(`
|
|
5943
|
-
|
|
5944
|
-
`));
|
|
5945
|
-
}
|
|
5946
|
-
case "share_skill": {
|
|
5947
|
-
const {
|
|
5948
|
-
name: skillName,
|
|
5949
|
-
description: skillDesc,
|
|
5950
|
-
instructions: skillInstr,
|
|
5951
|
-
tags: skillTags,
|
|
5952
|
-
when_to_use,
|
|
5953
|
-
allowed_tools,
|
|
5954
|
-
model,
|
|
5955
|
-
context: skillContext,
|
|
5956
|
-
agent,
|
|
5957
|
-
user_invocable,
|
|
5958
|
-
argument_hint
|
|
5959
|
-
} = args ?? {};
|
|
5960
|
-
if (!skillName || !skillDesc || !skillInstr)
|
|
5961
|
-
return text("share_skill: `name`, `description`, and `instructions` required", true);
|
|
5962
|
-
const client = allClients()[0];
|
|
5963
|
-
if (!client)
|
|
5964
|
-
return text("share_skill: not connected", true);
|
|
5965
|
-
const manifest = {};
|
|
5966
|
-
if (when_to_use)
|
|
5967
|
-
manifest.when_to_use = when_to_use;
|
|
5968
|
-
if (allowed_tools?.length)
|
|
5969
|
-
manifest.allowed_tools = allowed_tools;
|
|
5970
|
-
if (model)
|
|
5971
|
-
manifest.model = model;
|
|
5972
|
-
if (skillContext)
|
|
5973
|
-
manifest.context = skillContext;
|
|
5974
|
-
if (agent)
|
|
5975
|
-
manifest.agent = agent;
|
|
5976
|
-
if (user_invocable === false)
|
|
5977
|
-
manifest.user_invocable = false;
|
|
5978
|
-
if (argument_hint)
|
|
5979
|
-
manifest.argument_hint = argument_hint;
|
|
5980
|
-
const result = await client.shareSkill(skillName, skillDesc, skillInstr, skillTags, Object.keys(manifest).length > 0 ? manifest : undefined);
|
|
5981
|
-
if (!result)
|
|
5982
|
-
return text("share_skill: broker did not acknowledge", true);
|
|
5983
|
-
server.notification({ method: "notifications/prompts/list_changed" });
|
|
5984
|
-
server.notification({ method: "notifications/resources/list_changed" });
|
|
5985
|
-
return text(`Skill "${skillName}" published to the mesh. It will appear as /claudemesh:${skillName} in Claude Code.`);
|
|
5986
|
-
}
|
|
5987
|
-
case "get_skill": {
|
|
5988
|
-
const { name: gsName } = args ?? {};
|
|
5989
|
-
if (!gsName)
|
|
5990
|
-
return text("get_skill: `name` required", true);
|
|
5991
|
-
const client = allClients()[0];
|
|
5992
|
-
if (!client)
|
|
5993
|
-
return text("get_skill: not connected", true);
|
|
5994
|
-
const skill = await client.getSkill(gsName);
|
|
5995
|
-
if (!skill)
|
|
5996
|
-
return text(`Skill "${gsName}" not found in the mesh.`);
|
|
5997
|
-
const manifest = skill.manifest;
|
|
5998
|
-
const metaLines = [];
|
|
5999
|
-
if (manifest) {
|
|
6000
|
-
if (manifest.when_to_use)
|
|
6001
|
-
metaLines.push(`**When to use:** ${manifest.when_to_use}`);
|
|
6002
|
-
if (manifest.allowed_tools)
|
|
6003
|
-
metaLines.push(`**Allowed tools:** ${manifest.allowed_tools.join(", ")}`);
|
|
6004
|
-
if (manifest.model)
|
|
6005
|
-
metaLines.push(`**Model:** ${manifest.model}`);
|
|
6006
|
-
if (manifest.context)
|
|
6007
|
-
metaLines.push(`**Context:** ${manifest.context}`);
|
|
6008
|
-
if (manifest.agent)
|
|
6009
|
-
metaLines.push(`**Agent:** ${manifest.agent}`);
|
|
6010
|
-
}
|
|
6011
|
-
return text(`# Skill: ${skill.name}
|
|
6012
|
-
|
|
6013
|
-
**Description:** ${skill.description}
|
|
6014
|
-
**Author:** ${skill.author}
|
|
6015
|
-
**Tags:** ${skill.tags.length ? skill.tags.join(", ") : "none"}
|
|
6016
|
-
**Created:** ${skill.createdAt}
|
|
6017
|
-
**Slash command:** /claudemesh:${skill.name}
|
|
6018
|
-
` + (metaLines.length ? metaLines.join(`
|
|
6019
|
-
`) + `
|
|
6020
|
-
` : "") + `
|
|
6021
|
-
---
|
|
6022
|
-
|
|
6023
|
-
## Instructions
|
|
6024
|
-
|
|
6025
|
-
${skill.instructions}`);
|
|
6026
|
-
}
|
|
6027
|
-
case "list_skills": {
|
|
6028
|
-
const { query: skillQuery } = args ?? {};
|
|
6029
|
-
const client = allClients()[0];
|
|
6030
|
-
if (!client)
|
|
6031
|
-
return text("list_skills: not connected", true);
|
|
6032
|
-
const skills = await client.listSkills(skillQuery);
|
|
6033
|
-
if (skills.length === 0)
|
|
6034
|
-
return text(skillQuery ? `No skills found for "${skillQuery}".` : "No skills in the mesh yet.");
|
|
6035
|
-
const lines = skills.map((s) => `- **${s.name}**: ${s.description}${s.tags.length ? ` [${s.tags.join(", ")}]` : ""} (by ${s.author})`);
|
|
6036
|
-
return text(`${skills.length} skill(s):
|
|
6037
|
-
${lines.join(`
|
|
6038
|
-
`)}`);
|
|
6039
|
-
}
|
|
6040
|
-
case "remove_skill": {
|
|
6041
|
-
const { name: rsName } = args ?? {};
|
|
6042
|
-
if (!rsName)
|
|
6043
|
-
return text("remove_skill: `name` required", true);
|
|
6044
|
-
const client = allClients()[0];
|
|
6045
|
-
if (!client)
|
|
6046
|
-
return text("remove_skill: not connected", true);
|
|
6047
|
-
const removed = await client.removeSkill(rsName);
|
|
6048
|
-
if (removed) {
|
|
6049
|
-
server.notification({ method: "notifications/prompts/list_changed" });
|
|
6050
|
-
server.notification({ method: "notifications/resources/list_changed" });
|
|
6051
|
-
}
|
|
6052
|
-
return text(removed ? `Skill "${rsName}" removed.` : `Skill "${rsName}" not found.`, !removed);
|
|
6053
|
-
}
|
|
6054
|
-
case "ping_mesh": {
|
|
6055
|
-
const { priorities: pingPriorities } = args ?? {};
|
|
6056
|
-
const toTest = pingPriorities ?? ["now", "next"];
|
|
6057
|
-
const client = allClients()[0];
|
|
6058
|
-
if (!client)
|
|
6059
|
-
return text("ping_mesh: not connected", true);
|
|
6060
|
-
const results = [];
|
|
6061
|
-
results.push(`WS status: ${client.status}`);
|
|
6062
|
-
results.push(`Mesh: ${client.meshSlug}`);
|
|
6063
|
-
const peers = await client.listPeers();
|
|
6064
|
-
const selfPeer = peers.find((p) => p.displayName === myName);
|
|
6065
|
-
results.push(`Your status: ${selfPeer?.status ?? "not found in peer list"}`);
|
|
6066
|
-
results.push(`Peers online: ${peers.length}`);
|
|
6067
|
-
results.push(`Push buffer: ${client.pushHistory.length} buffered`);
|
|
6068
|
-
for (const prio of toTest) {
|
|
6069
|
-
const sendTime = Date.now();
|
|
6070
|
-
const target = peers.find((p) => p.displayName !== myName);
|
|
6071
|
-
const sendResult = await client.send(target?.pubkey ?? "*", `__ping__ ${prio} from ${myName} at ${new Date().toISOString()}`, prio);
|
|
6072
|
-
const ackTime = Date.now();
|
|
6073
|
-
if (!sendResult.ok) {
|
|
6074
|
-
results.push(`[${prio}] SEND FAILED: ${sendResult.error}`);
|
|
6075
|
-
} else {
|
|
6076
|
-
results.push(`[${prio}] send→ack: ${ackTime - sendTime}ms (msgId: ${sendResult.messageId?.slice(0, 12)})`);
|
|
6077
|
-
if (prio !== "now" && selfPeer?.status === "working") {
|
|
6078
|
-
results.push(` ⚠ peer status is "working" — broker holds "${prio}" until idle`);
|
|
6079
|
-
}
|
|
6080
|
-
}
|
|
6081
|
-
}
|
|
6082
|
-
results.push("");
|
|
6083
|
-
results.push("Pipeline check:");
|
|
6084
|
-
results.push(` onPush handlers: active`);
|
|
6085
|
-
results.push(` messageMode: ${messageMode}`);
|
|
6086
|
-
results.push(` server.notification: ${messageMode === "off" ? "disabled (mode=off)" : "enabled"}`);
|
|
6087
|
-
return text(results.join(`
|
|
6088
|
-
`));
|
|
6089
|
-
}
|
|
6090
|
-
case "mesh_mcp_register": {
|
|
6091
|
-
const { server_name, description, tools: regTools, persistent: regPersistent } = args ?? {};
|
|
6092
|
-
if (!server_name || !description || !regTools?.length)
|
|
6093
|
-
return text("mesh_mcp_register: `server_name`, `description`, and `tools` required", true);
|
|
6094
|
-
const client = allClients()[0];
|
|
6095
|
-
if (!client)
|
|
6096
|
-
return text("mesh_mcp_register: not connected", true);
|
|
6097
|
-
const result = await client.mcpRegister(server_name, description, regTools, regPersistent);
|
|
6098
|
-
if (!result)
|
|
6099
|
-
return text("mesh_mcp_register: broker did not acknowledge", true);
|
|
6100
|
-
const persistLabel = regPersistent ? " (persistent — survives disconnect)" : "";
|
|
6101
|
-
return text(`Registered MCP server "${result.serverName}" with ${result.toolCount} tool(s)${persistLabel}. Other peers can now call its tools via mesh_tool_call.`);
|
|
6102
|
-
}
|
|
6103
|
-
case "mesh_mcp_list": {
|
|
6104
|
-
const client = allClients()[0];
|
|
6105
|
-
if (!client)
|
|
6106
|
-
return text("mesh_mcp_list: not connected", true);
|
|
6107
|
-
const servers = await client.mcpList();
|
|
6108
|
-
if (servers.length === 0)
|
|
6109
|
-
return text("No MCP servers registered in the mesh.");
|
|
6110
|
-
const lines = servers.map((s) => {
|
|
6111
|
-
const toolList = s.tools.map((t) => ` - **${t.name}**: ${t.description}`).join(`
|
|
6112
|
-
`);
|
|
6113
|
-
const status = s.online === false ? ` [OFFLINE${s.offlineSince ? ` since ${s.offlineSince}` : ""}]` : "";
|
|
6114
|
-
return `- **${s.name}** (hosted by ${s.hostedBy})${status}: ${s.description}
|
|
6115
|
-
${toolList}`;
|
|
6116
|
-
});
|
|
6117
|
-
return text(`${servers.length} MCP server(s) in mesh:
|
|
6118
|
-
${lines.join(`
|
|
6119
|
-
`)}`);
|
|
6120
|
-
}
|
|
6121
|
-
case "mesh_tool_call": {
|
|
6122
|
-
const { server_name: callServer, tool_name: callTool, args: callArgs } = args ?? {};
|
|
6123
|
-
if (!callServer || !callTool)
|
|
6124
|
-
return text("mesh_tool_call: `server_name` and `tool_name` required", true);
|
|
6125
|
-
const client = allClients()[0];
|
|
6126
|
-
if (!client)
|
|
6127
|
-
return text("mesh_tool_call: not connected", true);
|
|
6128
|
-
const callResult = await client.mcpCall(callServer, callTool, callArgs ?? {});
|
|
6129
|
-
if (callResult.error)
|
|
6130
|
-
return text(`mesh_tool_call error: ${callResult.error}`, true);
|
|
6131
|
-
return text(typeof callResult.result === "string" ? callResult.result : JSON.stringify(callResult.result, null, 2));
|
|
6132
|
-
}
|
|
6133
|
-
case "mesh_mcp_remove": {
|
|
6134
|
-
const { server_name: rmServer } = args ?? {};
|
|
6135
|
-
if (!rmServer)
|
|
6136
|
-
return text("mesh_mcp_remove: `server_name` required", true);
|
|
6137
|
-
const client = allClients()[0];
|
|
6138
|
-
if (!client)
|
|
6139
|
-
return text("mesh_mcp_remove: not connected", true);
|
|
6140
|
-
await client.mcpUnregister(rmServer);
|
|
6141
|
-
return text(`Unregistered MCP server "${rmServer}" from the mesh.`);
|
|
6142
|
-
}
|
|
6143
|
-
case "grant_file_access": {
|
|
6144
|
-
const { fileId, to: grantTo } = args ?? {};
|
|
6145
|
-
if (!fileId || !grantTo)
|
|
6146
|
-
return text("grant_file_access: `fileId` and `to` required", true);
|
|
6147
|
-
const client = allClients()[0];
|
|
6148
|
-
if (!client)
|
|
6149
|
-
return text("grant_file_access: not connected", true);
|
|
6150
|
-
const peers = await client.listPeers();
|
|
6151
|
-
const targetPeer = peers.find((p) => p.pubkey === grantTo || p.displayName === grantTo);
|
|
6152
|
-
if (!targetPeer)
|
|
6153
|
-
return text(`grant_file_access: peer not found: ${grantTo}`, true);
|
|
6154
|
-
const result = await client.getFile(fileId);
|
|
6155
|
-
if (!result)
|
|
6156
|
-
return text("grant_file_access: file not found", true);
|
|
6157
|
-
if (!result.encrypted)
|
|
6158
|
-
return text("grant_file_access: file is not encrypted", true);
|
|
6159
|
-
if (!result.sealedKey)
|
|
6160
|
-
return text("grant_file_access: no key available (are you the owner?)", true);
|
|
6161
|
-
const { openSealedKey: openSealedKey2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
6162
|
-
const myPubkey = client.getSessionPubkey();
|
|
6163
|
-
const mySecret = client.getSessionSecretKey();
|
|
6164
|
-
if (!myPubkey || !mySecret)
|
|
6165
|
-
return text("grant_file_access: no session keypair", true);
|
|
6166
|
-
const kf = await openSealedKey2(result.sealedKey, myPubkey, mySecret);
|
|
6167
|
-
if (!kf)
|
|
6168
|
-
return text("grant_file_access: cannot decrypt your own key", true);
|
|
6169
|
-
const sealedForPeer = await sealKeyForPeer2(kf, targetPeer.pubkey);
|
|
6170
|
-
const ok = await client.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
|
|
6171
|
-
if (!ok)
|
|
6172
|
-
return text("grant_file_access: broker did not confirm", true);
|
|
6173
|
-
return text(`Access granted: ${targetPeer.displayName} can now download file ${fileId}`);
|
|
6174
|
-
}
|
|
6175
|
-
case "read_peer_file": {
|
|
6176
|
-
const { peer: peerName, path: filePath } = args ?? {};
|
|
6177
|
-
if (!peerName || !filePath)
|
|
6178
|
-
return text("read_peer_file: `peer` and `path` required", true);
|
|
6179
|
-
const client = allClients()[0];
|
|
6180
|
-
if (!client)
|
|
6181
|
-
return text("read_peer_file: not connected", true);
|
|
6182
|
-
const peers = await client.listPeers();
|
|
6183
|
-
const nameLower = peerName.toLowerCase();
|
|
6184
|
-
let targetPubkey = null;
|
|
6185
|
-
if (/^[0-9a-f]{64}$/.test(peerName)) {
|
|
6186
|
-
targetPubkey = peerName;
|
|
6187
|
-
} else {
|
|
6188
|
-
const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
|
|
6189
|
-
if (!match) {
|
|
6190
|
-
const partials = peers.filter((p) => p.displayName.toLowerCase().includes(nameLower));
|
|
6191
|
-
if (partials.length === 1) {
|
|
6192
|
-
targetPubkey = partials[0].pubkey;
|
|
6193
|
-
} else {
|
|
6194
|
-
const names = peers.map((p) => p.displayName).join(", ");
|
|
6195
|
-
return text(`read_peer_file: peer "${peerName}" not found. Online: ${names || "(none)"}`, true);
|
|
6196
|
-
}
|
|
6197
|
-
} else {
|
|
6198
|
-
targetPubkey = match.pubkey;
|
|
6199
|
-
}
|
|
6200
|
-
}
|
|
6201
|
-
const resolvedPeer = peers.find((p) => p.pubkey === targetPubkey);
|
|
6202
|
-
const isLocal = resolvedPeer?.hostname && resolvedPeer.hostname === __require("os").hostname();
|
|
6203
|
-
let localHint = "";
|
|
6204
|
-
if (isLocal && resolvedPeer?.cwd) {
|
|
6205
|
-
const directPath = __require("path").resolve(resolvedPeer.cwd, filePath);
|
|
6206
|
-
localHint = `
|
|
6207
|
-
|
|
6208
|
-
> **Hint:** This peer is LOCAL (same machine). Next time, read directly: \`${directPath}\` — faster, no size limit.
|
|
6209
|
-
|
|
6210
|
-
`;
|
|
6211
|
-
}
|
|
6212
|
-
const result = await client.requestFile(targetPubkey, filePath);
|
|
6213
|
-
if (result.error)
|
|
6214
|
-
return text(`read_peer_file: ${result.error}`, true);
|
|
6215
|
-
if (!result.content)
|
|
6216
|
-
return text("read_peer_file: empty response from peer", true);
|
|
6217
|
-
try {
|
|
6218
|
-
const decoded = Buffer.from(result.content, "base64").toString("utf-8");
|
|
6219
|
-
return text(localHint + decoded);
|
|
6220
|
-
} catch {
|
|
6221
|
-
return text("read_peer_file: failed to decode file content (binary file?)", true);
|
|
6222
|
-
}
|
|
6223
|
-
}
|
|
6224
|
-
case "list_peer_files": {
|
|
6225
|
-
const { peer: peerName, path: dirPath, pattern } = args ?? {};
|
|
6226
|
-
if (!peerName)
|
|
6227
|
-
return text("list_peer_files: `peer` required", true);
|
|
6228
|
-
const client = allClients()[0];
|
|
6229
|
-
if (!client)
|
|
6230
|
-
return text("list_peer_files: not connected", true);
|
|
6231
|
-
const peers = await client.listPeers();
|
|
6232
|
-
const nameLower = peerName.toLowerCase();
|
|
6233
|
-
let targetPubkey = null;
|
|
6234
|
-
if (/^[0-9a-f]{64}$/.test(peerName)) {
|
|
6235
|
-
targetPubkey = peerName;
|
|
6236
|
-
} else {
|
|
6237
|
-
const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
|
|
6238
|
-
if (!match) {
|
|
6239
|
-
const partials = peers.filter((p) => p.displayName.toLowerCase().includes(nameLower));
|
|
6240
|
-
if (partials.length === 1) {
|
|
6241
|
-
targetPubkey = partials[0].pubkey;
|
|
6242
|
-
} else {
|
|
6243
|
-
const names = peers.map((p) => p.displayName).join(", ");
|
|
6244
|
-
return text(`list_peer_files: peer "${peerName}" not found. Online: ${names || "(none)"}`, true);
|
|
6245
|
-
}
|
|
6246
|
-
} else {
|
|
6247
|
-
targetPubkey = match.pubkey;
|
|
6248
|
-
}
|
|
6249
|
-
}
|
|
6250
|
-
const result = await client.requestDir(targetPubkey, dirPath ?? ".", pattern);
|
|
6251
|
-
if (result.error)
|
|
6252
|
-
return text(`list_peer_files: ${result.error}`, true);
|
|
6253
|
-
if (!result.entries || result.entries.length === 0)
|
|
6254
|
-
return text("No files found.");
|
|
6255
|
-
return text(result.entries.join(`
|
|
6256
|
-
`));
|
|
6257
|
-
}
|
|
6258
|
-
case "create_webhook": {
|
|
6259
|
-
const { name: whName } = args ?? {};
|
|
6260
|
-
if (!whName)
|
|
6261
|
-
return text("create_webhook: `name` required", true);
|
|
6262
|
-
const client = allClients()[0];
|
|
6263
|
-
if (!client)
|
|
6264
|
-
return text("create_webhook: not connected", true);
|
|
6265
|
-
const wh = await client.createWebhook(whName);
|
|
6266
|
-
if (!wh)
|
|
6267
|
-
return text("create_webhook: broker did not acknowledge — check connection", true);
|
|
6268
|
-
return text(`Webhook **${wh.name}** created.
|
|
6269
|
-
|
|
6270
|
-
URL: ${wh.url}
|
|
6271
|
-
Secret: ${wh.secret}
|
|
6272
|
-
|
|
6273
|
-
External services can POST JSON to this URL. The payload will be pushed to all connected mesh peers.`);
|
|
6274
|
-
}
|
|
6275
|
-
case "list_webhooks": {
|
|
6276
|
-
const client = allClients()[0];
|
|
6277
|
-
if (!client)
|
|
6278
|
-
return text("list_webhooks: not connected", true);
|
|
6279
|
-
const webhooks = await client.listWebhooks();
|
|
6280
|
-
if (webhooks.length === 0)
|
|
6281
|
-
return text("No active webhooks.");
|
|
6282
|
-
const lines = webhooks.map((w) => `- **${w.name}** — ${w.url} (created ${w.createdAt})`);
|
|
6283
|
-
return text(`${webhooks.length} webhook(s):
|
|
6284
|
-
${lines.join(`
|
|
6285
|
-
`)}`);
|
|
6286
|
-
}
|
|
6287
|
-
case "delete_webhook": {
|
|
6288
|
-
const { name: delName } = args ?? {};
|
|
6289
|
-
if (!delName)
|
|
6290
|
-
return text("delete_webhook: `name` required", true);
|
|
6291
|
-
const client = allClients()[0];
|
|
6292
|
-
if (!client)
|
|
6293
|
-
return text("delete_webhook: not connected", true);
|
|
6294
|
-
const ok = await client.deleteWebhook(delName);
|
|
6295
|
-
return text(ok ? `Webhook "${delName}" deactivated.` : `Failed to deactivate webhook "${delName}".`, !ok);
|
|
6296
|
-
}
|
|
6297
|
-
case "vault_set": {
|
|
6298
|
-
const { key, value, type: vType, mount_path, description } = args ?? {};
|
|
6299
|
-
if (!key || !value)
|
|
6300
|
-
return text("vault_set: `key` and `value` required", true);
|
|
6301
|
-
if (!/^[a-zA-Z0-9_.-]{1,128}$/.test(key))
|
|
6302
|
-
return text("vault_set: `key` must be 1-128 alphanumeric/underscore/dot/dash chars", true);
|
|
6303
|
-
if (mount_path && (mount_path.includes("..") || mount_path.length > 512))
|
|
6304
|
-
return text("vault_set: invalid `mount_path`", true);
|
|
6305
|
-
if (description && description.length > 500)
|
|
6306
|
-
return text("vault_set: `description` too long (max 500 chars)", true);
|
|
6307
|
-
const client = allClients()[0];
|
|
6308
|
-
if (!client)
|
|
6309
|
-
return text("vault_set: not connected", true);
|
|
6310
|
-
const entryType = vType ?? "env";
|
|
6311
|
-
let plaintextBytes;
|
|
6312
|
-
if (entryType === "file") {
|
|
6313
|
-
const { existsSync: existsSync6, readFileSync: readFileSync4 } = await import("node:fs");
|
|
6314
|
-
if (!existsSync6(value))
|
|
6315
|
-
return text(`vault_set: file not found: ${value}`, true);
|
|
6316
|
-
plaintextBytes = new Uint8Array(readFileSync4(value));
|
|
6317
|
-
} else {
|
|
6318
|
-
plaintextBytes = new TextEncoder().encode(value);
|
|
6319
|
-
}
|
|
6320
|
-
const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
6321
|
-
const { ciphertext, nonce, key: kf } = await encryptFile2(plaintextBytes);
|
|
6322
|
-
const sealedKey = await sealKeyForPeer2(kf, client.getMeshPubkey());
|
|
6323
|
-
const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
|
|
6324
|
-
const sodium2 = await ensureSodium2();
|
|
6325
|
-
const ciphertextB64 = sodium2.to_base64(ciphertext, sodium2.base64_variants.ORIGINAL);
|
|
6326
|
-
const ok = await client.vaultSet(key, ciphertextB64, nonce, sealedKey, entryType, mount_path, description);
|
|
6327
|
-
if (!ok)
|
|
6328
|
-
return text("vault_set: broker did not acknowledge", true);
|
|
6329
|
-
return text(`Vault entry "${key}" stored (${entryType}, E2E encrypted).`);
|
|
6330
|
-
}
|
|
6331
|
-
case "vault_list": {
|
|
6332
|
-
const client = allClients()[0];
|
|
6333
|
-
if (!client)
|
|
6334
|
-
return text("vault_list: not connected", true);
|
|
6335
|
-
const entries = await client.vaultList();
|
|
6336
|
-
if (entries.length === 0)
|
|
6337
|
-
return text("Vault is empty.");
|
|
6338
|
-
const lines = entries.map((e) => `- **${e.key}** (${e.entry_type}${e.mount_path ? ` → ${e.mount_path}` : ""})${e.description ? ` — ${e.description}` : ""} (${e.updated_at})`);
|
|
6339
|
-
return text(`${entries.length} vault entry(s):
|
|
6340
|
-
${lines.join(`
|
|
6341
|
-
`)}`);
|
|
6342
|
-
}
|
|
6343
|
-
case "vault_delete": {
|
|
6344
|
-
const { key } = args ?? {};
|
|
6345
|
-
if (!key)
|
|
6346
|
-
return text("vault_delete: `key` required", true);
|
|
6347
|
-
const client = allClients()[0];
|
|
6348
|
-
if (!client)
|
|
6349
|
-
return text("vault_delete: not connected", true);
|
|
6350
|
-
const ok = await client.vaultDelete(key);
|
|
6351
|
-
return text(ok ? `Vault entry "${key}" deleted.` : `Vault entry "${key}" not found.`);
|
|
6352
|
-
}
|
|
6353
|
-
case "mesh_mcp_deploy": {
|
|
6354
|
-
const { server_name, file_id, git_url, git_branch, npx_package, env: deployEnv, runtime, memory_mb, network_allow, scope } = args ?? {};
|
|
6355
|
-
if (!server_name)
|
|
6356
|
-
return text("mesh_mcp_deploy: `server_name` required", true);
|
|
6357
|
-
if (!file_id && !git_url && !npx_package)
|
|
6358
|
-
return text("mesh_mcp_deploy: one of `file_id`, `git_url`, or `npx_package` required", true);
|
|
6359
|
-
const client = allClients()[0];
|
|
6360
|
-
if (!client)
|
|
6361
|
-
return text("mesh_mcp_deploy: not connected", true);
|
|
6362
|
-
const source = npx_package ? { type: "npx", package: npx_package } : file_id ? { type: "zip", file_id } : { type: "git", url: git_url, branch: git_branch };
|
|
6363
|
-
const resolvedEnv = {};
|
|
6364
|
-
const vaultResolved = [];
|
|
6365
|
-
if (deployEnv) {
|
|
6366
|
-
const vaultRefs = [];
|
|
6367
|
-
for (const [envKey, envVal] of Object.entries(deployEnv)) {
|
|
6368
|
-
if (typeof envVal === "string" && envVal.startsWith("$vault:")) {
|
|
6369
|
-
const parts = envVal.slice(7).split(":");
|
|
6370
|
-
const vaultKey = parts[0];
|
|
6371
|
-
const isFile = parts[1] === "file";
|
|
6372
|
-
const mountPath = isFile ? parts.slice(2).join(":") : undefined;
|
|
6373
|
-
vaultRefs.push({ envKey, vaultKey, isFile, mountPath });
|
|
6374
|
-
} else {
|
|
6375
|
-
resolvedEnv[envKey] = envVal;
|
|
6376
|
-
}
|
|
6377
|
-
}
|
|
6378
|
-
if (vaultRefs.length > 0) {
|
|
6379
|
-
const { openSealedKey: openSealedKey2, decryptFile: decryptFile2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
6380
|
-
const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
|
|
6381
|
-
const sodium2 = await ensureSodium2();
|
|
6382
|
-
const keys = vaultRefs.map((r) => r.vaultKey);
|
|
6383
|
-
const encryptedEntries = await client.vaultGet(keys);
|
|
6384
|
-
for (const ref of vaultRefs) {
|
|
6385
|
-
const entry = encryptedEntries.find((e) => e.key === ref.vaultKey);
|
|
6386
|
-
if (!entry)
|
|
6387
|
-
return text(`mesh_mcp_deploy: a referenced vault key was not found. Use vault_set first.`, true);
|
|
6388
|
-
const kf = await openSealedKey2(entry.sealed_key, client.getMeshPubkey(), client.getMeshSecretKey());
|
|
6389
|
-
if (!kf)
|
|
6390
|
-
return text(`mesh_mcp_deploy: failed to decrypt a vault entry — wrong keypair?`, true);
|
|
6391
|
-
const ciphertextBytes = sodium2.from_base64(entry.ciphertext, sodium2.base64_variants.ORIGINAL);
|
|
6392
|
-
const plainBytes = await decryptFile2(ciphertextBytes, entry.nonce, kf);
|
|
6393
|
-
if (!plainBytes)
|
|
6394
|
-
return text(`mesh_mcp_deploy: failed to decrypt a vault entry — data may be corrupted`, true);
|
|
6395
|
-
if (ref.isFile && ref.mountPath) {
|
|
6396
|
-
resolvedEnv[ref.envKey] = `__vault_file__:${ref.mountPath}:${sodium2.to_base64(plainBytes, sodium2.base64_variants.ORIGINAL)}`;
|
|
6397
|
-
} else {
|
|
6398
|
-
resolvedEnv[ref.envKey] = new TextDecoder().decode(plainBytes);
|
|
6399
|
-
}
|
|
6400
|
-
vaultResolved.push(ref.vaultKey);
|
|
6401
|
-
}
|
|
6402
|
-
}
|
|
6403
|
-
}
|
|
6404
|
-
const config2 = {};
|
|
6405
|
-
if (Object.keys(resolvedEnv).length > 0 || deployEnv && Object.keys(deployEnv).length > 0) {
|
|
6406
|
-
config2.env = Object.keys(resolvedEnv).length > 0 ? resolvedEnv : deployEnv;
|
|
6407
|
-
}
|
|
6408
|
-
if (runtime)
|
|
6409
|
-
config2.runtime = runtime;
|
|
6410
|
-
if (memory_mb)
|
|
6411
|
-
config2.memory_mb = memory_mb;
|
|
6412
|
-
if (network_allow)
|
|
6413
|
-
config2.network_allow = network_allow;
|
|
6414
|
-
const result = await client.mcpDeploy(server_name, source, Object.keys(config2).length > 0 ? config2 : undefined, scope);
|
|
6415
|
-
const toolList = result.tools?.map((t) => ` - ${t.name}: ${t.description}`).join(`
|
|
6416
|
-
`) ?? " (pending)";
|
|
6417
|
-
let vaultNote = "";
|
|
6418
|
-
if (vaultResolved.length > 0) {
|
|
6419
|
-
vaultNote = `
|
|
6420
|
-
|
|
6421
|
-
Vault keys resolved: ${vaultResolved.join(", ")} (decrypted client-side, sent over TLS)`;
|
|
6422
|
-
}
|
|
6423
|
-
return text(`Deployed "${server_name}" (status: ${result.status}).
|
|
6424
|
-
|
|
6425
|
-
Tools:
|
|
6426
|
-
${toolList}
|
|
6427
|
-
|
|
6428
|
-
Default scope: peer (private). Use mesh_mcp_scope to share.${vaultNote}`);
|
|
6429
|
-
}
|
|
6430
|
-
case "mesh_mcp_undeploy": {
|
|
6431
|
-
const { server_name } = args ?? {};
|
|
6432
|
-
if (!server_name)
|
|
6433
|
-
return text("mesh_mcp_undeploy: `server_name` required", true);
|
|
6434
|
-
const client = allClients()[0];
|
|
6435
|
-
if (!client)
|
|
6436
|
-
return text("mesh_mcp_undeploy: not connected", true);
|
|
6437
|
-
const ok = await client.mcpUndeploy(server_name);
|
|
6438
|
-
return text(ok ? `Service "${server_name}" undeployed.` : `Failed to undeploy "${server_name}".`);
|
|
6439
|
-
}
|
|
6440
|
-
case "mesh_mcp_update": {
|
|
6441
|
-
const { server_name } = args ?? {};
|
|
6442
|
-
if (!server_name)
|
|
6443
|
-
return text("mesh_mcp_update: `server_name` required", true);
|
|
6444
|
-
const client = allClients()[0];
|
|
6445
|
-
if (!client)
|
|
6446
|
-
return text("mesh_mcp_update: not connected", true);
|
|
6447
|
-
const result = await client.mcpUpdate(server_name);
|
|
6448
|
-
return text(`Updated "${server_name}" (status: ${result.status}).`);
|
|
6449
|
-
}
|
|
6450
|
-
case "mesh_mcp_logs": {
|
|
6451
|
-
const { server_name, lines: logLines } = args ?? {};
|
|
6452
|
-
if (!server_name)
|
|
6453
|
-
return text("mesh_mcp_logs: `server_name` required", true);
|
|
6454
|
-
const client = allClients()[0];
|
|
6455
|
-
if (!client)
|
|
6456
|
-
return text("mesh_mcp_logs: not connected", true);
|
|
6457
|
-
const logs = await client.mcpLogs(server_name, logLines);
|
|
6458
|
-
if (logs.length === 0)
|
|
6459
|
-
return text(`No logs for "${server_name}".`);
|
|
6460
|
-
return text(`Logs for "${server_name}" (${logs.length} lines):
|
|
6461
|
-
\`\`\`
|
|
6462
|
-
${logs.join(`
|
|
6463
|
-
`)}
|
|
6464
|
-
\`\`\``);
|
|
6465
|
-
}
|
|
6466
|
-
case "mesh_mcp_scope": {
|
|
6467
|
-
const { server_name, scope } = args ?? {};
|
|
6468
|
-
if (!server_name)
|
|
6469
|
-
return text("mesh_mcp_scope: `server_name` required", true);
|
|
6470
|
-
const client = allClients()[0];
|
|
6471
|
-
if (!client)
|
|
6472
|
-
return text("mesh_mcp_scope: not connected", true);
|
|
6473
|
-
const result = await client.mcpScope(server_name, scope);
|
|
6474
|
-
if (scope !== undefined) {
|
|
6475
|
-
return text(`Scope for "${server_name}" updated to: ${JSON.stringify(result.scope)}`);
|
|
6476
|
-
}
|
|
6477
|
-
return text(`**${server_name}** scope: ${JSON.stringify(result.scope)}
|
|
6478
|
-
Deployed by: ${result.deployed_by}`);
|
|
6479
|
-
}
|
|
6480
|
-
case "mesh_mcp_schema": {
|
|
6481
|
-
const { server_name, tool_name } = args ?? {};
|
|
6482
|
-
if (!server_name)
|
|
6483
|
-
return text("mesh_mcp_schema: `server_name` required", true);
|
|
6484
|
-
const client = allClients()[0];
|
|
6485
|
-
if (!client)
|
|
6486
|
-
return text("mesh_mcp_schema: not connected", true);
|
|
6487
|
-
const tools = await client.mcpServiceSchema(server_name, tool_name);
|
|
6488
|
-
if (tools.length === 0)
|
|
6489
|
-
return text(`No tools found for "${server_name}"${tool_name ? ` (tool: ${tool_name})` : ""}.`);
|
|
6490
|
-
const lines = tools.map((t) => `### ${t.name}
|
|
6491
|
-
${t.description}
|
|
6492
|
-
\`\`\`json
|
|
6493
|
-
${JSON.stringify(t.inputSchema, null, 2)}
|
|
6494
|
-
\`\`\``);
|
|
6495
|
-
return text(`Tools for "${server_name}":
|
|
6496
|
-
|
|
6497
|
-
${lines.join(`
|
|
6498
|
-
|
|
6499
|
-
`)}`);
|
|
6500
|
-
}
|
|
6501
|
-
case "mesh_mcp_catalog": {
|
|
6502
|
-
const client = allClients()[0];
|
|
6503
|
-
if (!client)
|
|
6504
|
-
return text("mesh_mcp_catalog: not connected", true);
|
|
6505
|
-
const services = await client.mcpCatalog();
|
|
6506
|
-
if (services.length === 0)
|
|
6507
|
-
return text("No services deployed in the mesh.");
|
|
6508
|
-
const lines = services.map((s) => {
|
|
6509
|
-
const scopeStr = typeof s.scope === "string" ? s.scope : JSON.stringify(s.scope);
|
|
6510
|
-
return `- **${s.name}** (${s.type}, ${s.status}) — ${s.description}
|
|
6511
|
-
${s.tool_count} tools | scope: ${scopeStr} | by ${s.deployed_by} | ${s.source_type}${s.runtime ? ` (${s.runtime})` : ""}`;
|
|
6512
|
-
});
|
|
6513
|
-
return text(`${services.length} service(s) in mesh:
|
|
6514
|
-
|
|
6515
|
-
${lines.join(`
|
|
6516
|
-
`)}`);
|
|
6517
|
-
}
|
|
6518
|
-
case "mesh_skill_deploy": {
|
|
6519
|
-
const { file_id, git_url, git_branch } = args ?? {};
|
|
6520
|
-
if (!file_id && !git_url)
|
|
6521
|
-
return text("mesh_skill_deploy: either `file_id` or `git_url` required", true);
|
|
6522
|
-
const client = allClients()[0];
|
|
6523
|
-
if (!client)
|
|
6524
|
-
return text("mesh_skill_deploy: not connected", true);
|
|
6525
|
-
const source = file_id ? { type: "zip", file_id } : { type: "git", url: git_url, branch: git_branch };
|
|
6526
|
-
const result = await client.skillDeploy(source);
|
|
6527
|
-
return text(`Skill "${result.name}" deployed.
|
|
6528
|
-
Files: ${result.files.join(", ")}`);
|
|
6529
|
-
}
|
|
6530
|
-
case "mesh_watch": {
|
|
6531
|
-
const { url, mode, extract, interval, notify_on, headers, label } = args ?? {};
|
|
6532
|
-
if (!url)
|
|
6533
|
-
return text("mesh_watch: `url` required", true);
|
|
6534
|
-
const client = allClients()[0];
|
|
6535
|
-
if (!client)
|
|
6536
|
-
return text("mesh_watch: not connected", true);
|
|
6537
|
-
const result = await client.watch(url, { mode, extract, interval, notify_on, headers, label });
|
|
6538
|
-
if (result.error)
|
|
6539
|
-
return text(`mesh_watch: ${result.error}`, true);
|
|
6540
|
-
return text(`Watching "${label ?? url}" (${result.mode}, every ${result.interval}s)
|
|
6541
|
-
Watch ID: ${result.watchId}`);
|
|
6542
|
-
}
|
|
6543
|
-
case "mesh_unwatch": {
|
|
6544
|
-
const { watch_id } = args ?? {};
|
|
6545
|
-
if (!watch_id)
|
|
6546
|
-
return text("mesh_unwatch: `watch_id` required", true);
|
|
6547
|
-
const client = allClients()[0];
|
|
6548
|
-
if (!client)
|
|
6549
|
-
return text("mesh_unwatch: not connected", true);
|
|
6550
|
-
await client.unwatch(watch_id);
|
|
6551
|
-
return text(`Watch ${watch_id} stopped.`);
|
|
6552
|
-
}
|
|
6553
|
-
case "mesh_watches": {
|
|
6554
|
-
const client = allClients()[0];
|
|
6555
|
-
if (!client)
|
|
6556
|
-
return text("mesh_watches: not connected", true);
|
|
6557
|
-
const watches = await client.watchList();
|
|
6558
|
-
if (watches.length === 0)
|
|
6559
|
-
return text("No active watches.");
|
|
6560
|
-
const lines = watches.map((w) => `- **${w.id}** ${w.label ? `(${w.label}) ` : ""}${w.url}
|
|
6561
|
-
mode: ${w.mode} | interval: ${w.interval}s | last: ${w.lastValue?.slice(0, 30) ?? "pending"} | checked: ${w.lastCheck ?? "never"}`);
|
|
6562
|
-
return text(`${watches.length} active watch(es):
|
|
6563
|
-
|
|
6564
|
-
${lines.join(`
|
|
6565
|
-
`)}`);
|
|
6566
|
-
}
|
|
6567
|
-
default:
|
|
6568
|
-
return text(`Unknown tool: ${name}`, true);
|
|
6569
|
-
}
|
|
6570
|
-
});
|
|
6571
4011
|
const transport = new StdioServerTransport;
|
|
6572
4012
|
await server.connect(transport);
|
|
6573
4013
|
const bridges = [];
|
|
@@ -6875,41 +4315,13 @@ async function startServiceProxy(serviceName) {
|
|
|
6875
4315
|
process.on("SIGTERM", shutdown);
|
|
6876
4316
|
process.on("SIGINT", shutdown);
|
|
6877
4317
|
}
|
|
6878
|
-
var peerNameCache, peerNameCacheAge = 0, CACHE_TTL_MS = 30000
|
|
4318
|
+
var peerNameCache, peerNameCacheAge = 0, CACHE_TTL_MS = 30000;
|
|
6879
4319
|
var init_server2 = __esm(() => {
|
|
6880
4320
|
init_definitions();
|
|
6881
4321
|
init_facade();
|
|
6882
4322
|
init_facade3();
|
|
6883
4323
|
init_server();
|
|
6884
4324
|
peerNameCache = new Map;
|
|
6885
|
-
DEPRECATED_TOOL_HINTS = {
|
|
6886
|
-
send_message: "claudemesh send <to> <msg> [--mesh X] [--priority Y]",
|
|
6887
|
-
list_peers: "claudemesh peers --json",
|
|
6888
|
-
check_messages: "claudemesh inbox --json",
|
|
6889
|
-
message_status: "claudemesh msg-status <id>",
|
|
6890
|
-
set_profile: "claudemesh profile --avatar X --bio Y",
|
|
6891
|
-
set_status: "claudemesh status set <state>",
|
|
6892
|
-
set_summary: "claudemesh summary <text>",
|
|
6893
|
-
set_visible: "claudemesh visible <true|false>",
|
|
6894
|
-
join_group: "claudemesh group join @<name> [--role X]",
|
|
6895
|
-
leave_group: "claudemesh group leave @<name>",
|
|
6896
|
-
get_state: "claudemesh state get <key> --json",
|
|
6897
|
-
set_state: "claudemesh state set <key> <value>",
|
|
6898
|
-
list_state: "claudemesh state list --json",
|
|
6899
|
-
remember: "claudemesh remember <text>",
|
|
6900
|
-
recall: "claudemesh recall <query> --json",
|
|
6901
|
-
forget: "claudemesh forget <id>",
|
|
6902
|
-
schedule_reminder: "claudemesh remind <msg> --in/--at/--cron",
|
|
6903
|
-
list_scheduled: "claudemesh remind list --json",
|
|
6904
|
-
cancel_scheduled: "claudemesh remind cancel <id>",
|
|
6905
|
-
mesh_info: "claudemesh info --json",
|
|
6906
|
-
mesh_stats: "claudemesh stats --json",
|
|
6907
|
-
mesh_clock: "claudemesh clock --json",
|
|
6908
|
-
ping_mesh: "claudemesh ping",
|
|
6909
|
-
claim_task: "claudemesh task claim <id>",
|
|
6910
|
-
complete_task: "claudemesh task complete <id>"
|
|
6911
|
-
};
|
|
6912
|
-
warnedTools = new Set;
|
|
6913
4325
|
});
|
|
6914
4326
|
|
|
6915
4327
|
// src/entrypoints/mcp.ts
|
|
@@ -6920,4 +4332,4 @@ startMcpServer().catch((err) => {
|
|
|
6920
4332
|
process.exit(1);
|
|
6921
4333
|
});
|
|
6922
4334
|
|
|
6923
|
-
//# debugId=
|
|
4335
|
+
//# debugId=3095927E5265159264756E2164756E21
|