claudemesh-cli 0.6.8 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1450 -110
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -46941,6 +46941,46 @@ var TOOLS = [
|
|
|
46941
46941
|
required: ["status"]
|
|
46942
46942
|
}
|
|
46943
46943
|
},
|
|
46944
|
+
{
|
|
46945
|
+
name: "set_visible",
|
|
46946
|
+
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.",
|
|
46947
|
+
inputSchema: {
|
|
46948
|
+
type: "object",
|
|
46949
|
+
properties: {
|
|
46950
|
+
visible: {
|
|
46951
|
+
type: "boolean",
|
|
46952
|
+
description: "true to be visible (default), false to hide"
|
|
46953
|
+
}
|
|
46954
|
+
},
|
|
46955
|
+
required: ["visible"]
|
|
46956
|
+
}
|
|
46957
|
+
},
|
|
46958
|
+
{
|
|
46959
|
+
name: "set_profile",
|
|
46960
|
+
description: "Set your public profile — what other peers see about you. Avatar (emoji), title, bio, and capabilities list.",
|
|
46961
|
+
inputSchema: {
|
|
46962
|
+
type: "object",
|
|
46963
|
+
properties: {
|
|
46964
|
+
avatar: {
|
|
46965
|
+
type: "string",
|
|
46966
|
+
description: "Emoji or URL for your avatar"
|
|
46967
|
+
},
|
|
46968
|
+
title: {
|
|
46969
|
+
type: "string",
|
|
46970
|
+
description: "Short role label (e.g. 'Frontend Lead', 'DevOps')"
|
|
46971
|
+
},
|
|
46972
|
+
bio: {
|
|
46973
|
+
type: "string",
|
|
46974
|
+
description: "One-liner about yourself"
|
|
46975
|
+
},
|
|
46976
|
+
capabilities: {
|
|
46977
|
+
type: "array",
|
|
46978
|
+
items: { type: "string" },
|
|
46979
|
+
description: "What you can help with"
|
|
46980
|
+
}
|
|
46981
|
+
}
|
|
46982
|
+
}
|
|
46983
|
+
},
|
|
46944
46984
|
{
|
|
46945
46985
|
name: "join_group",
|
|
46946
46986
|
description: "Join a group with an optional role. Other peers see your group membership in list_peers.",
|
|
@@ -47376,13 +47416,14 @@ var TOOLS = [
|
|
|
47376
47416
|
},
|
|
47377
47417
|
{
|
|
47378
47418
|
name: "schedule_reminder",
|
|
47379
|
-
description: "Schedule a
|
|
47419
|
+
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.",
|
|
47380
47420
|
inputSchema: {
|
|
47381
47421
|
type: "object",
|
|
47382
47422
|
properties: {
|
|
47383
47423
|
message: { type: "string", description: "Message or reminder text" },
|
|
47384
|
-
deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver" },
|
|
47385
|
-
in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds" },
|
|
47424
|
+
deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver (one-shot)" },
|
|
47425
|
+
in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds (one-shot)" },
|
|
47426
|
+
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)" },
|
|
47386
47427
|
to: {
|
|
47387
47428
|
type: "string",
|
|
47388
47429
|
description: "Recipient: display name, pubkey hex, @group, or * (omit for self-reminder)"
|
|
@@ -47412,6 +47453,144 @@ var TOOLS = [
|
|
|
47412
47453
|
description: "Get a complete overview of the mesh: peers, groups, state, memory, files, tasks, streams, tables. Call on session start for full situational awareness.",
|
|
47413
47454
|
inputSchema: { type: "object", properties: {} }
|
|
47414
47455
|
},
|
|
47456
|
+
{
|
|
47457
|
+
name: "mesh_stats",
|
|
47458
|
+
description: "View resource usage stats for all peers: messages sent/received, tool calls, uptime, errors.",
|
|
47459
|
+
inputSchema: { type: "object", properties: {} }
|
|
47460
|
+
},
|
|
47461
|
+
{
|
|
47462
|
+
name: "mesh_mcp_register",
|
|
47463
|
+
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.",
|
|
47464
|
+
inputSchema: {
|
|
47465
|
+
type: "object",
|
|
47466
|
+
properties: {
|
|
47467
|
+
server_name: { type: "string", description: "Unique name for the MCP server (e.g. 'github', 'jira')" },
|
|
47468
|
+
description: { type: "string", description: "What this MCP server does" },
|
|
47469
|
+
tools: {
|
|
47470
|
+
type: "array",
|
|
47471
|
+
items: {
|
|
47472
|
+
type: "object",
|
|
47473
|
+
properties: {
|
|
47474
|
+
name: { type: "string" },
|
|
47475
|
+
description: { type: "string" },
|
|
47476
|
+
inputSchema: { type: "object", description: "JSON Schema for tool arguments" }
|
|
47477
|
+
},
|
|
47478
|
+
required: ["name", "description", "inputSchema"]
|
|
47479
|
+
},
|
|
47480
|
+
description: "Tool definitions to expose"
|
|
47481
|
+
}
|
|
47482
|
+
},
|
|
47483
|
+
required: ["server_name", "description", "tools"]
|
|
47484
|
+
}
|
|
47485
|
+
},
|
|
47486
|
+
{
|
|
47487
|
+
name: "mesh_mcp_list",
|
|
47488
|
+
description: "List MCP servers available in the mesh with their tools. Shows which peer hosts each server.",
|
|
47489
|
+
inputSchema: { type: "object", properties: {} }
|
|
47490
|
+
},
|
|
47491
|
+
{
|
|
47492
|
+
name: "mesh_tool_call",
|
|
47493
|
+
description: "Call a tool on a mesh-registered MCP server. Route: you -> broker -> hosting peer -> execute -> result back. Timeout: 30s.",
|
|
47494
|
+
inputSchema: {
|
|
47495
|
+
type: "object",
|
|
47496
|
+
properties: {
|
|
47497
|
+
server_name: { type: "string", description: "Name of the MCP server" },
|
|
47498
|
+
tool_name: { type: "string", description: "Name of the tool to call" },
|
|
47499
|
+
args: { type: "object", description: "Tool arguments (JSON object)" }
|
|
47500
|
+
},
|
|
47501
|
+
required: ["server_name", "tool_name"]
|
|
47502
|
+
}
|
|
47503
|
+
},
|
|
47504
|
+
{
|
|
47505
|
+
name: "mesh_mcp_remove",
|
|
47506
|
+
description: "Unregister an MCP server you previously registered with the mesh.",
|
|
47507
|
+
inputSchema: {
|
|
47508
|
+
type: "object",
|
|
47509
|
+
properties: {
|
|
47510
|
+
server_name: { type: "string", description: "Name of the MCP server to remove" }
|
|
47511
|
+
},
|
|
47512
|
+
required: ["server_name"]
|
|
47513
|
+
}
|
|
47514
|
+
},
|
|
47515
|
+
{
|
|
47516
|
+
name: "mesh_set_clock",
|
|
47517
|
+
description: "Set the simulation clock speed. x1 = real-time, x10 = 10x faster, x100 = 100x. Peers receive heartbeat ticks at the simulated rate.",
|
|
47518
|
+
inputSchema: {
|
|
47519
|
+
type: "object",
|
|
47520
|
+
properties: {
|
|
47521
|
+
speed: {
|
|
47522
|
+
type: "number",
|
|
47523
|
+
description: "Speed multiplier (1-100). x1 = tick every 60s, x10 = tick every 6s, x100 = tick every 600ms."
|
|
47524
|
+
}
|
|
47525
|
+
},
|
|
47526
|
+
required: ["speed"]
|
|
47527
|
+
}
|
|
47528
|
+
},
|
|
47529
|
+
{
|
|
47530
|
+
name: "mesh_pause_clock",
|
|
47531
|
+
description: "Pause the simulation clock. Ticks stop until resumed.",
|
|
47532
|
+
inputSchema: { type: "object", properties: {} }
|
|
47533
|
+
},
|
|
47534
|
+
{
|
|
47535
|
+
name: "mesh_resume_clock",
|
|
47536
|
+
description: "Resume a paused simulation clock.",
|
|
47537
|
+
inputSchema: { type: "object", properties: {} }
|
|
47538
|
+
},
|
|
47539
|
+
{
|
|
47540
|
+
name: "mesh_clock",
|
|
47541
|
+
description: "Get current simulation clock status: speed, tick count, simulated time.",
|
|
47542
|
+
inputSchema: { type: "object", properties: {} }
|
|
47543
|
+
},
|
|
47544
|
+
{
|
|
47545
|
+
name: "share_skill",
|
|
47546
|
+
description: "Publish a reusable skill to the mesh. Other peers can discover and load it. If a skill with the same name exists, it is updated.",
|
|
47547
|
+
inputSchema: {
|
|
47548
|
+
type: "object",
|
|
47549
|
+
properties: {
|
|
47550
|
+
name: { type: "string", description: "Unique skill name (e.g. 'code-review', 'deploy-checklist')" },
|
|
47551
|
+
description: { type: "string", description: "Short description of what the skill does" },
|
|
47552
|
+
instructions: { type: "string", description: "Full instructions/prompt that a peer loads to acquire this capability" },
|
|
47553
|
+
tags: {
|
|
47554
|
+
type: "array",
|
|
47555
|
+
items: { type: "string" },
|
|
47556
|
+
description: "Tags for discoverability"
|
|
47557
|
+
}
|
|
47558
|
+
},
|
|
47559
|
+
required: ["name", "description", "instructions"]
|
|
47560
|
+
}
|
|
47561
|
+
},
|
|
47562
|
+
{
|
|
47563
|
+
name: "get_skill",
|
|
47564
|
+
description: "Load a skill's full instructions by name. Use to acquire capabilities shared by other peers.",
|
|
47565
|
+
inputSchema: {
|
|
47566
|
+
type: "object",
|
|
47567
|
+
properties: {
|
|
47568
|
+
name: { type: "string", description: "Skill name to load" }
|
|
47569
|
+
},
|
|
47570
|
+
required: ["name"]
|
|
47571
|
+
}
|
|
47572
|
+
},
|
|
47573
|
+
{
|
|
47574
|
+
name: "list_skills",
|
|
47575
|
+
description: "Browse available skills in the mesh. Optionally filter by keyword across name, description, and tags.",
|
|
47576
|
+
inputSchema: {
|
|
47577
|
+
type: "object",
|
|
47578
|
+
properties: {
|
|
47579
|
+
query: { type: "string", description: "Search keyword (optional)" }
|
|
47580
|
+
}
|
|
47581
|
+
}
|
|
47582
|
+
},
|
|
47583
|
+
{
|
|
47584
|
+
name: "remove_skill",
|
|
47585
|
+
description: "Remove a skill you published from the mesh.",
|
|
47586
|
+
inputSchema: {
|
|
47587
|
+
type: "object",
|
|
47588
|
+
properties: {
|
|
47589
|
+
name: { type: "string", description: "Skill name to remove" }
|
|
47590
|
+
},
|
|
47591
|
+
required: ["name"]
|
|
47592
|
+
}
|
|
47593
|
+
},
|
|
47415
47594
|
{
|
|
47416
47595
|
name: "ping_mesh",
|
|
47417
47596
|
description: "Send test messages through the full pipeline and measure round-trip timing per priority. Diagnoses push delivery issues.",
|
|
@@ -47425,6 +47604,61 @@ var TOOLS = [
|
|
|
47425
47604
|
}
|
|
47426
47605
|
}
|
|
47427
47606
|
}
|
|
47607
|
+
},
|
|
47608
|
+
{
|
|
47609
|
+
name: "read_peer_file",
|
|
47610
|
+
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.",
|
|
47611
|
+
inputSchema: {
|
|
47612
|
+
type: "object",
|
|
47613
|
+
properties: {
|
|
47614
|
+
peer: { type: "string", description: "Peer display name or pubkey" },
|
|
47615
|
+
path: { type: "string", description: "File path relative to peer's working directory" }
|
|
47616
|
+
},
|
|
47617
|
+
required: ["peer", "path"]
|
|
47618
|
+
}
|
|
47619
|
+
},
|
|
47620
|
+
{
|
|
47621
|
+
name: "list_peer_files",
|
|
47622
|
+
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.",
|
|
47623
|
+
inputSchema: {
|
|
47624
|
+
type: "object",
|
|
47625
|
+
properties: {
|
|
47626
|
+
peer: { type: "string", description: "Peer display name or pubkey" },
|
|
47627
|
+
path: { type: "string", description: "Directory path relative to peer's cwd (default: root)" },
|
|
47628
|
+
pattern: { type: "string", description: "Glob-like filter pattern (e.g. '*.ts', 'src/*')" }
|
|
47629
|
+
},
|
|
47630
|
+
required: ["peer"]
|
|
47631
|
+
}
|
|
47632
|
+
},
|
|
47633
|
+
{
|
|
47634
|
+
name: "create_webhook",
|
|
47635
|
+
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.",
|
|
47636
|
+
inputSchema: {
|
|
47637
|
+
type: "object",
|
|
47638
|
+
properties: {
|
|
47639
|
+
name: {
|
|
47640
|
+
type: "string",
|
|
47641
|
+
description: "Webhook name (e.g. 'github-ci', 'datadog-alerts')"
|
|
47642
|
+
}
|
|
47643
|
+
},
|
|
47644
|
+
required: ["name"]
|
|
47645
|
+
}
|
|
47646
|
+
},
|
|
47647
|
+
{
|
|
47648
|
+
name: "list_webhooks",
|
|
47649
|
+
description: "List active webhooks for this mesh.",
|
|
47650
|
+
inputSchema: { type: "object", properties: {} }
|
|
47651
|
+
},
|
|
47652
|
+
{
|
|
47653
|
+
name: "delete_webhook",
|
|
47654
|
+
description: "Deactivate a webhook.",
|
|
47655
|
+
inputSchema: {
|
|
47656
|
+
type: "object",
|
|
47657
|
+
properties: {
|
|
47658
|
+
name: { type: "string", description: "Webhook name to deactivate" }
|
|
47659
|
+
},
|
|
47660
|
+
required: ["name"]
|
|
47661
|
+
}
|
|
47428
47662
|
}
|
|
47429
47663
|
];
|
|
47430
47664
|
|
|
@@ -47510,10 +47744,21 @@ class BrokerClient {
|
|
|
47510
47744
|
sessionPubkey = null;
|
|
47511
47745
|
sessionSecretKey = null;
|
|
47512
47746
|
grantFileAccessResolvers = new Map;
|
|
47747
|
+
peerFileResponseResolvers = new Map;
|
|
47748
|
+
peerDirResponseResolvers = new Map;
|
|
47749
|
+
sharedDirs = [process.cwd()];
|
|
47513
47750
|
closed = false;
|
|
47514
47751
|
reconnectAttempt = 0;
|
|
47515
47752
|
helloTimer = null;
|
|
47516
47753
|
reconnectTimer = null;
|
|
47754
|
+
_statsCounters = {
|
|
47755
|
+
messagesIn: 0,
|
|
47756
|
+
messagesOut: 0,
|
|
47757
|
+
toolCalls: 0,
|
|
47758
|
+
errors: 0
|
|
47759
|
+
};
|
|
47760
|
+
_sessionStartedAt = Date.now();
|
|
47761
|
+
_statsReportTimer = null;
|
|
47517
47762
|
constructor(mesh, opts = {}) {
|
|
47518
47763
|
this.mesh = mesh;
|
|
47519
47764
|
this.opts = opts;
|
|
@@ -47565,6 +47810,9 @@ class BrokerClient {
|
|
|
47565
47810
|
sessionId: `${process.pid}-${Date.now()}`,
|
|
47566
47811
|
pid: process.pid,
|
|
47567
47812
|
cwd: process.cwd(),
|
|
47813
|
+
peerType: "ai",
|
|
47814
|
+
channel: "claude-code",
|
|
47815
|
+
model: process.env.CLAUDE_MODEL || undefined,
|
|
47568
47816
|
timestamp,
|
|
47569
47817
|
signature
|
|
47570
47818
|
}));
|
|
@@ -47592,6 +47840,7 @@ class BrokerClient {
|
|
|
47592
47840
|
this.setConnStatus("open");
|
|
47593
47841
|
this.reconnectAttempt = 0;
|
|
47594
47842
|
this.flushOutbound();
|
|
47843
|
+
this.startStatsReporting();
|
|
47595
47844
|
resolve();
|
|
47596
47845
|
return;
|
|
47597
47846
|
}
|
|
@@ -47631,6 +47880,7 @@ class BrokerClient {
|
|
|
47631
47880
|
nonce = randomNonce();
|
|
47632
47881
|
ciphertext = Buffer.from(message, "utf-8").toString("base64");
|
|
47633
47882
|
}
|
|
47883
|
+
this._statsCounters.messagesOut++;
|
|
47634
47884
|
return new Promise((resolve) => {
|
|
47635
47885
|
if (this.pendingSends.size >= MAX_QUEUED) {
|
|
47636
47886
|
resolve({ ok: false, error: "outbound queue full" });
|
|
@@ -47688,6 +47938,16 @@ class BrokerClient {
|
|
|
47688
47938
|
return;
|
|
47689
47939
|
this.ws.send(JSON.stringify({ type: "set_status", status }));
|
|
47690
47940
|
}
|
|
47941
|
+
async setVisible(visible) {
|
|
47942
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47943
|
+
return;
|
|
47944
|
+
this.ws.send(JSON.stringify({ type: "set_visible", visible }));
|
|
47945
|
+
}
|
|
47946
|
+
async setProfile(profile) {
|
|
47947
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47948
|
+
return;
|
|
47949
|
+
this.ws.send(JSON.stringify({ type: "set_profile", ...profile }));
|
|
47950
|
+
}
|
|
47691
47951
|
async listPeers() {
|
|
47692
47952
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47693
47953
|
return [];
|
|
@@ -47705,6 +47965,34 @@ class BrokerClient {
|
|
|
47705
47965
|
return;
|
|
47706
47966
|
this.ws.send(JSON.stringify({ type: "set_summary", summary }));
|
|
47707
47967
|
}
|
|
47968
|
+
setStats(stats) {
|
|
47969
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47970
|
+
return;
|
|
47971
|
+
const payload = stats ?? {
|
|
47972
|
+
...this._statsCounters,
|
|
47973
|
+
uptime: Math.round((Date.now() - this._sessionStartedAt) / 1000)
|
|
47974
|
+
};
|
|
47975
|
+
this.ws.send(JSON.stringify({ type: "set_stats", stats: payload }));
|
|
47976
|
+
}
|
|
47977
|
+
incrementToolCalls() {
|
|
47978
|
+
this._statsCounters.toolCalls++;
|
|
47979
|
+
}
|
|
47980
|
+
incrementErrors() {
|
|
47981
|
+
this._statsCounters.errors++;
|
|
47982
|
+
}
|
|
47983
|
+
startStatsReporting() {
|
|
47984
|
+
if (this._statsReportTimer)
|
|
47985
|
+
return;
|
|
47986
|
+
this._statsReportTimer = setInterval(() => {
|
|
47987
|
+
this.setStats();
|
|
47988
|
+
}, 60000);
|
|
47989
|
+
}
|
|
47990
|
+
stopStatsReporting() {
|
|
47991
|
+
if (this._statsReportTimer) {
|
|
47992
|
+
clearInterval(this._statsReportTimer);
|
|
47993
|
+
this._statsReportTimer = null;
|
|
47994
|
+
}
|
|
47995
|
+
}
|
|
47708
47996
|
async joinGroup(name, role) {
|
|
47709
47997
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47710
47998
|
return;
|
|
@@ -47773,7 +48061,7 @@ class BrokerClient {
|
|
|
47773
48061
|
return;
|
|
47774
48062
|
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
47775
48063
|
}
|
|
47776
|
-
async scheduleMessage(to, message, deliverAt, isReminder = false) {
|
|
48064
|
+
async scheduleMessage(to, message, deliverAt, isReminder = false, cron) {
|
|
47777
48065
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47778
48066
|
return null;
|
|
47779
48067
|
return new Promise((resolve) => {
|
|
@@ -47788,6 +48076,7 @@ class BrokerClient {
|
|
|
47788
48076
|
message,
|
|
47789
48077
|
deliverAt,
|
|
47790
48078
|
...isReminder ? { subtype: "reminder" } : {},
|
|
48079
|
+
...cron ? { cron, recurring: true } : {},
|
|
47791
48080
|
_reqId: reqId
|
|
47792
48081
|
}));
|
|
47793
48082
|
});
|
|
@@ -47836,6 +48125,10 @@ class BrokerClient {
|
|
|
47836
48125
|
scheduledAckResolvers = new Map;
|
|
47837
48126
|
scheduledListResolvers = new Map;
|
|
47838
48127
|
cancelScheduledResolvers = new Map;
|
|
48128
|
+
mcpRegisterResolvers = new Map;
|
|
48129
|
+
mcpListResolvers = new Map;
|
|
48130
|
+
mcpCallResolvers = new Map;
|
|
48131
|
+
mcpCallForwardHandler = null;
|
|
47839
48132
|
async messageStatus(messageId) {
|
|
47840
48133
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47841
48134
|
return null;
|
|
@@ -48134,7 +48427,105 @@ class BrokerClient {
|
|
|
48134
48427
|
this.stateChangeHandlers.add(handler);
|
|
48135
48428
|
return () => this.stateChangeHandlers.delete(handler);
|
|
48136
48429
|
}
|
|
48430
|
+
async mcpRegister(serverName, description, tools) {
|
|
48431
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48432
|
+
return null;
|
|
48433
|
+
return new Promise((resolve) => {
|
|
48434
|
+
const reqId = this.makeReqId();
|
|
48435
|
+
this.mcpRegisterResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48436
|
+
if (this.mcpRegisterResolvers.delete(reqId))
|
|
48437
|
+
resolve(null);
|
|
48438
|
+
}, 5000) });
|
|
48439
|
+
this.ws.send(JSON.stringify({ type: "mcp_register", serverName, description, tools, _reqId: reqId }));
|
|
48440
|
+
});
|
|
48441
|
+
}
|
|
48442
|
+
async mcpUnregister(serverName) {
|
|
48443
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48444
|
+
return;
|
|
48445
|
+
this.ws.send(JSON.stringify({ type: "mcp_unregister", serverName }));
|
|
48446
|
+
}
|
|
48447
|
+
async mcpList() {
|
|
48448
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48449
|
+
return [];
|
|
48450
|
+
return new Promise((resolve) => {
|
|
48451
|
+
const reqId = this.makeReqId();
|
|
48452
|
+
this.mcpListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48453
|
+
if (this.mcpListResolvers.delete(reqId))
|
|
48454
|
+
resolve([]);
|
|
48455
|
+
}, 5000) });
|
|
48456
|
+
this.ws.send(JSON.stringify({ type: "mcp_list", _reqId: reqId }));
|
|
48457
|
+
});
|
|
48458
|
+
}
|
|
48459
|
+
async mcpCall(serverName, toolName, args) {
|
|
48460
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48461
|
+
return { error: "not connected" };
|
|
48462
|
+
return new Promise((resolve) => {
|
|
48463
|
+
const reqId = this.makeReqId();
|
|
48464
|
+
this.mcpCallResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48465
|
+
if (this.mcpCallResolvers.delete(reqId))
|
|
48466
|
+
resolve({ error: "MCP call timed out (30s)" });
|
|
48467
|
+
}, 30000) });
|
|
48468
|
+
this.ws.send(JSON.stringify({ type: "mcp_call", serverName, toolName, args, _reqId: reqId }));
|
|
48469
|
+
});
|
|
48470
|
+
}
|
|
48471
|
+
onMcpCallForward(handler) {
|
|
48472
|
+
this.mcpCallForwardHandler = handler;
|
|
48473
|
+
}
|
|
48474
|
+
sendMcpCallResponse(callId, result, error2) {
|
|
48475
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48476
|
+
return;
|
|
48477
|
+
this.ws.send(JSON.stringify({ type: "mcp_call_response", callId, result, error: error2 }));
|
|
48478
|
+
}
|
|
48137
48479
|
meshInfoResolvers = new Map;
|
|
48480
|
+
clockStatusResolvers = new Map;
|
|
48481
|
+
async setClock(speed) {
|
|
48482
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48483
|
+
return null;
|
|
48484
|
+
return new Promise((resolve) => {
|
|
48485
|
+
const reqId = this.makeReqId();
|
|
48486
|
+
this.clockStatusResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48487
|
+
if (this.clockStatusResolvers.delete(reqId))
|
|
48488
|
+
resolve(null);
|
|
48489
|
+
}, 5000) });
|
|
48490
|
+
this.ws.send(JSON.stringify({ type: "set_clock", speed, _reqId: reqId }));
|
|
48491
|
+
});
|
|
48492
|
+
}
|
|
48493
|
+
async pauseClock() {
|
|
48494
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48495
|
+
return null;
|
|
48496
|
+
return new Promise((resolve) => {
|
|
48497
|
+
const reqId = this.makeReqId();
|
|
48498
|
+
this.clockStatusResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48499
|
+
if (this.clockStatusResolvers.delete(reqId))
|
|
48500
|
+
resolve(null);
|
|
48501
|
+
}, 5000) });
|
|
48502
|
+
this.ws.send(JSON.stringify({ type: "pause_clock", _reqId: reqId }));
|
|
48503
|
+
});
|
|
48504
|
+
}
|
|
48505
|
+
async resumeClock() {
|
|
48506
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48507
|
+
return null;
|
|
48508
|
+
return new Promise((resolve) => {
|
|
48509
|
+
const reqId = this.makeReqId();
|
|
48510
|
+
this.clockStatusResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48511
|
+
if (this.clockStatusResolvers.delete(reqId))
|
|
48512
|
+
resolve(null);
|
|
48513
|
+
}, 5000) });
|
|
48514
|
+
this.ws.send(JSON.stringify({ type: "resume_clock", _reqId: reqId }));
|
|
48515
|
+
});
|
|
48516
|
+
}
|
|
48517
|
+
async getClock() {
|
|
48518
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48519
|
+
return null;
|
|
48520
|
+
return new Promise((resolve) => {
|
|
48521
|
+
const reqId = this.makeReqId();
|
|
48522
|
+
this.clockStatusResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48523
|
+
if (this.clockStatusResolvers.delete(reqId))
|
|
48524
|
+
resolve(null);
|
|
48525
|
+
}, 5000) });
|
|
48526
|
+
this.ws.send(JSON.stringify({ type: "get_clock", _reqId: reqId }));
|
|
48527
|
+
});
|
|
48528
|
+
}
|
|
48138
48529
|
async meshInfo() {
|
|
48139
48530
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48140
48531
|
return null;
|
|
@@ -48147,8 +48538,132 @@ class BrokerClient {
|
|
|
48147
48538
|
this.ws.send(JSON.stringify({ type: "mesh_info", _reqId: reqId }));
|
|
48148
48539
|
});
|
|
48149
48540
|
}
|
|
48541
|
+
skillAckResolvers = new Map;
|
|
48542
|
+
skillDataResolvers = new Map;
|
|
48543
|
+
skillListResolvers = new Map;
|
|
48544
|
+
async shareSkill(name, description, instructions, tags) {
|
|
48545
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48546
|
+
return null;
|
|
48547
|
+
return new Promise((resolve) => {
|
|
48548
|
+
const reqId = this.makeReqId();
|
|
48549
|
+
this.skillAckResolvers.set(reqId, { resolve: (result) => {
|
|
48550
|
+
resolve(result ? { ok: true, action: result.action } : null);
|
|
48551
|
+
}, timer: setTimeout(() => {
|
|
48552
|
+
if (this.skillAckResolvers.delete(reqId))
|
|
48553
|
+
resolve(null);
|
|
48554
|
+
}, 5000) });
|
|
48555
|
+
this.ws.send(JSON.stringify({ type: "share_skill", name, description, instructions, tags, _reqId: reqId }));
|
|
48556
|
+
});
|
|
48557
|
+
}
|
|
48558
|
+
async getSkill(name) {
|
|
48559
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48560
|
+
return null;
|
|
48561
|
+
return new Promise((resolve) => {
|
|
48562
|
+
const reqId = this.makeReqId();
|
|
48563
|
+
this.skillDataResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48564
|
+
if (this.skillDataResolvers.delete(reqId))
|
|
48565
|
+
resolve(null);
|
|
48566
|
+
}, 5000) });
|
|
48567
|
+
this.ws.send(JSON.stringify({ type: "get_skill", name, _reqId: reqId }));
|
|
48568
|
+
});
|
|
48569
|
+
}
|
|
48570
|
+
async listSkills(query) {
|
|
48571
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48572
|
+
return [];
|
|
48573
|
+
return new Promise((resolve) => {
|
|
48574
|
+
const reqId = this.makeReqId();
|
|
48575
|
+
this.skillListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48576
|
+
if (this.skillListResolvers.delete(reqId))
|
|
48577
|
+
resolve([]);
|
|
48578
|
+
}, 5000) });
|
|
48579
|
+
this.ws.send(JSON.stringify({ type: "list_skills", query, _reqId: reqId }));
|
|
48580
|
+
});
|
|
48581
|
+
}
|
|
48582
|
+
async removeSkill(name) {
|
|
48583
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48584
|
+
return false;
|
|
48585
|
+
return new Promise((resolve) => {
|
|
48586
|
+
const reqId = this.makeReqId();
|
|
48587
|
+
this.skillAckResolvers.set(reqId, { resolve: (result) => {
|
|
48588
|
+
resolve(result?.action === "removed");
|
|
48589
|
+
}, timer: setTimeout(() => {
|
|
48590
|
+
if (this.skillAckResolvers.delete(reqId))
|
|
48591
|
+
resolve(false);
|
|
48592
|
+
}, 5000) });
|
|
48593
|
+
this.ws.send(JSON.stringify({ type: "remove_skill", name, _reqId: reqId }));
|
|
48594
|
+
});
|
|
48595
|
+
}
|
|
48596
|
+
webhookAckResolvers = new Map;
|
|
48597
|
+
webhookListResolvers = new Map;
|
|
48598
|
+
async createWebhook(name) {
|
|
48599
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48600
|
+
return null;
|
|
48601
|
+
return new Promise((resolve) => {
|
|
48602
|
+
const reqId = this.makeReqId();
|
|
48603
|
+
this.webhookAckResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48604
|
+
if (this.webhookAckResolvers.delete(reqId))
|
|
48605
|
+
resolve(null);
|
|
48606
|
+
}, 5000) });
|
|
48607
|
+
this.ws.send(JSON.stringify({ type: "create_webhook", name, _reqId: reqId }));
|
|
48608
|
+
});
|
|
48609
|
+
}
|
|
48610
|
+
async listWebhooks() {
|
|
48611
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48612
|
+
return [];
|
|
48613
|
+
return new Promise((resolve) => {
|
|
48614
|
+
const reqId = this.makeReqId();
|
|
48615
|
+
this.webhookListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48616
|
+
if (this.webhookListResolvers.delete(reqId))
|
|
48617
|
+
resolve([]);
|
|
48618
|
+
}, 5000) });
|
|
48619
|
+
this.ws.send(JSON.stringify({ type: "list_webhooks", _reqId: reqId }));
|
|
48620
|
+
});
|
|
48621
|
+
}
|
|
48622
|
+
async deleteWebhook(name) {
|
|
48623
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48624
|
+
return false;
|
|
48625
|
+
return new Promise((resolve) => {
|
|
48626
|
+
const reqId = this.makeReqId();
|
|
48627
|
+
this.webhookAckResolvers.set(reqId, { resolve: () => resolve(true), timer: setTimeout(() => {
|
|
48628
|
+
if (this.webhookAckResolvers.delete(reqId))
|
|
48629
|
+
resolve(false);
|
|
48630
|
+
}, 5000) });
|
|
48631
|
+
this.ws.send(JSON.stringify({ type: "delete_webhook", name, _reqId: reqId }));
|
|
48632
|
+
});
|
|
48633
|
+
}
|
|
48634
|
+
setSharedDirs(dirs) {
|
|
48635
|
+
this.sharedDirs = dirs.map((d) => {
|
|
48636
|
+
const { resolve } = __require("node:path");
|
|
48637
|
+
return resolve(d);
|
|
48638
|
+
});
|
|
48639
|
+
}
|
|
48640
|
+
async requestFile(targetPubkey, filePath) {
|
|
48641
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48642
|
+
return { error: "not connected" };
|
|
48643
|
+
return new Promise((resolve) => {
|
|
48644
|
+
const reqId = this.makeReqId();
|
|
48645
|
+
this.peerFileResponseResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48646
|
+
if (this.peerFileResponseResolvers.delete(reqId))
|
|
48647
|
+
resolve({ error: "timeout waiting for peer response" });
|
|
48648
|
+
}, 15000) });
|
|
48649
|
+
this.ws.send(JSON.stringify({ type: "peer_file_request", targetPubkey, filePath, _reqId: reqId }));
|
|
48650
|
+
});
|
|
48651
|
+
}
|
|
48652
|
+
async requestDir(targetPubkey, dirPath, pattern) {
|
|
48653
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48654
|
+
return { error: "not connected" };
|
|
48655
|
+
return new Promise((resolve) => {
|
|
48656
|
+
const reqId = this.makeReqId();
|
|
48657
|
+
this.peerDirResponseResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
48658
|
+
if (this.peerDirResponseResolvers.delete(reqId))
|
|
48659
|
+
resolve({ error: "timeout waiting for peer response" });
|
|
48660
|
+
}, 15000) });
|
|
48661
|
+
this.ws.send(JSON.stringify({ type: "peer_dir_request", targetPubkey, dirPath, ...pattern ? { pattern } : {}, _reqId: reqId }));
|
|
48662
|
+
});
|
|
48663
|
+
}
|
|
48150
48664
|
close() {
|
|
48151
48665
|
this.closed = true;
|
|
48666
|
+
this.stopStatsReporting();
|
|
48152
48667
|
if (this.helloTimer)
|
|
48153
48668
|
clearTimeout(this.helloTimer);
|
|
48154
48669
|
if (this.reconnectTimer)
|
|
@@ -48160,6 +48675,141 @@ class BrokerClient {
|
|
|
48160
48675
|
}
|
|
48161
48676
|
this.setConnStatus("closed");
|
|
48162
48677
|
}
|
|
48678
|
+
static MAX_FILE_SIZE = 1048576;
|
|
48679
|
+
async handlePeerFileRequest(msg) {
|
|
48680
|
+
const { resolve, join: join2, normalize } = await import("node:path");
|
|
48681
|
+
const { readFileSync: readFileSync2, statSync } = await import("node:fs");
|
|
48682
|
+
const reqId = msg._reqId;
|
|
48683
|
+
const sendResponse = (content, error2) => {
|
|
48684
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48685
|
+
return;
|
|
48686
|
+
this.ws.send(JSON.stringify({
|
|
48687
|
+
type: "peer_file_response",
|
|
48688
|
+
requesterPubkey: msg.requesterPubkey,
|
|
48689
|
+
filePath: msg.filePath,
|
|
48690
|
+
...content !== undefined ? { content } : {},
|
|
48691
|
+
...error2 ? { error: error2 } : {},
|
|
48692
|
+
...reqId ? { _reqId: reqId } : {}
|
|
48693
|
+
}));
|
|
48694
|
+
};
|
|
48695
|
+
if (msg.filePath.includes("..")) {
|
|
48696
|
+
sendResponse(undefined, "path traversal not allowed");
|
|
48697
|
+
return;
|
|
48698
|
+
}
|
|
48699
|
+
let resolvedPath = null;
|
|
48700
|
+
for (const dir of this.sharedDirs) {
|
|
48701
|
+
const candidate = resolve(join2(dir, msg.filePath));
|
|
48702
|
+
const normalizedCandidate = normalize(candidate);
|
|
48703
|
+
const normalizedDir = normalize(dir);
|
|
48704
|
+
if (normalizedCandidate.startsWith(normalizedDir + "/") || normalizedCandidate === normalizedDir) {
|
|
48705
|
+
resolvedPath = candidate;
|
|
48706
|
+
break;
|
|
48707
|
+
}
|
|
48708
|
+
}
|
|
48709
|
+
if (!resolvedPath) {
|
|
48710
|
+
sendResponse(undefined, "file outside shared directories");
|
|
48711
|
+
return;
|
|
48712
|
+
}
|
|
48713
|
+
try {
|
|
48714
|
+
const stat = statSync(resolvedPath);
|
|
48715
|
+
if (!stat.isFile()) {
|
|
48716
|
+
sendResponse(undefined, "not a file");
|
|
48717
|
+
return;
|
|
48718
|
+
}
|
|
48719
|
+
if (stat.size > BrokerClient.MAX_FILE_SIZE) {
|
|
48720
|
+
sendResponse(undefined, `file too large (${stat.size} bytes, max ${BrokerClient.MAX_FILE_SIZE})`);
|
|
48721
|
+
return;
|
|
48722
|
+
}
|
|
48723
|
+
const content = readFileSync2(resolvedPath);
|
|
48724
|
+
sendResponse(content.toString("base64"));
|
|
48725
|
+
} catch (e) {
|
|
48726
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
48727
|
+
if (errMsg.includes("ENOENT")) {
|
|
48728
|
+
sendResponse(undefined, "file not found");
|
|
48729
|
+
} else {
|
|
48730
|
+
sendResponse(undefined, `read error: ${errMsg}`);
|
|
48731
|
+
}
|
|
48732
|
+
}
|
|
48733
|
+
}
|
|
48734
|
+
async handlePeerDirRequest(msg) {
|
|
48735
|
+
const { resolve, join: join2, normalize, relative } = await import("node:path");
|
|
48736
|
+
const { readdirSync, statSync } = await import("node:fs");
|
|
48737
|
+
const reqId = msg._reqId;
|
|
48738
|
+
const sendResponse = (entries, error2) => {
|
|
48739
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
48740
|
+
return;
|
|
48741
|
+
this.ws.send(JSON.stringify({
|
|
48742
|
+
type: "peer_dir_response",
|
|
48743
|
+
requesterPubkey: msg.requesterPubkey,
|
|
48744
|
+
dirPath: msg.dirPath,
|
|
48745
|
+
...entries ? { entries } : {},
|
|
48746
|
+
...error2 ? { error: error2 } : {},
|
|
48747
|
+
...reqId ? { _reqId: reqId } : {}
|
|
48748
|
+
}));
|
|
48749
|
+
};
|
|
48750
|
+
const dirPath = msg.dirPath || ".";
|
|
48751
|
+
if (dirPath.includes("..")) {
|
|
48752
|
+
sendResponse(undefined, "path traversal not allowed");
|
|
48753
|
+
return;
|
|
48754
|
+
}
|
|
48755
|
+
let resolvedPath = null;
|
|
48756
|
+
for (const dir of this.sharedDirs) {
|
|
48757
|
+
const candidate = resolve(join2(dir, dirPath));
|
|
48758
|
+
const normalizedCandidate = normalize(candidate);
|
|
48759
|
+
const normalizedDir = normalize(dir);
|
|
48760
|
+
if (normalizedCandidate.startsWith(normalizedDir + "/") || normalizedCandidate === normalizedDir) {
|
|
48761
|
+
resolvedPath = candidate;
|
|
48762
|
+
break;
|
|
48763
|
+
}
|
|
48764
|
+
}
|
|
48765
|
+
if (!resolvedPath) {
|
|
48766
|
+
sendResponse(undefined, "directory outside shared directories");
|
|
48767
|
+
return;
|
|
48768
|
+
}
|
|
48769
|
+
try {
|
|
48770
|
+
const stat = statSync(resolvedPath);
|
|
48771
|
+
if (!stat.isDirectory()) {
|
|
48772
|
+
sendResponse(undefined, "not a directory");
|
|
48773
|
+
return;
|
|
48774
|
+
}
|
|
48775
|
+
const entries = [];
|
|
48776
|
+
const MAX_ENTRIES = 500;
|
|
48777
|
+
const MAX_DEPTH = 2;
|
|
48778
|
+
const pattern = msg.pattern ? new RegExp(msg.pattern.replace(/\*/g, ".*").replace(/\?/g, "."), "i") : null;
|
|
48779
|
+
const walk = (dir, depth) => {
|
|
48780
|
+
if (entries.length >= MAX_ENTRIES || depth > MAX_DEPTH)
|
|
48781
|
+
return;
|
|
48782
|
+
try {
|
|
48783
|
+
const items = readdirSync(dir, { withFileTypes: true });
|
|
48784
|
+
for (const item of items) {
|
|
48785
|
+
if (entries.length >= MAX_ENTRIES)
|
|
48786
|
+
break;
|
|
48787
|
+
if (item.name.startsWith("."))
|
|
48788
|
+
continue;
|
|
48789
|
+
const relPath = relative(resolvedPath, join2(dir, item.name));
|
|
48790
|
+
const label = item.isDirectory() ? relPath + "/" : relPath;
|
|
48791
|
+
if (pattern && !pattern.test(item.name)) {
|
|
48792
|
+
if (item.isDirectory())
|
|
48793
|
+
walk(join2(dir, item.name), depth + 1);
|
|
48794
|
+
continue;
|
|
48795
|
+
}
|
|
48796
|
+
entries.push(label);
|
|
48797
|
+
if (item.isDirectory())
|
|
48798
|
+
walk(join2(dir, item.name), depth + 1);
|
|
48799
|
+
}
|
|
48800
|
+
} catch {}
|
|
48801
|
+
};
|
|
48802
|
+
walk(resolvedPath, 0);
|
|
48803
|
+
sendResponse(entries.sort());
|
|
48804
|
+
} catch (e) {
|
|
48805
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
48806
|
+
if (errMsg.includes("ENOENT")) {
|
|
48807
|
+
sendResponse(undefined, "directory not found");
|
|
48808
|
+
} else {
|
|
48809
|
+
sendResponse(undefined, `read error: ${errMsg}`);
|
|
48810
|
+
}
|
|
48811
|
+
}
|
|
48812
|
+
}
|
|
48163
48813
|
resolveFromMap(map2, reqId, value) {
|
|
48164
48814
|
let entry = reqId ? map2.get(reqId) : undefined;
|
|
48165
48815
|
if (!entry) {
|
|
@@ -48197,6 +48847,7 @@ class BrokerClient {
|
|
|
48197
48847
|
return;
|
|
48198
48848
|
}
|
|
48199
48849
|
if (msg.type === "push") {
|
|
48850
|
+
this._statsCounters.messagesIn++;
|
|
48200
48851
|
const nonce = String(msg.nonce ?? "");
|
|
48201
48852
|
const ciphertext = String(msg.ciphertext ?? "");
|
|
48202
48853
|
const senderPubkey = String(msg.senderPubkey ?? "");
|
|
@@ -48234,7 +48885,9 @@ class BrokerClient {
|
|
|
48234
48885
|
receivedAt: new Date().toISOString(),
|
|
48235
48886
|
plaintext,
|
|
48236
48887
|
kind,
|
|
48237
|
-
...msg.subtype ? { subtype: msg.subtype } : {}
|
|
48888
|
+
...msg.subtype ? { subtype: msg.subtype } : {},
|
|
48889
|
+
...msg.event ? { event: String(msg.event) } : {},
|
|
48890
|
+
...msg.eventData ? { eventData: msg.eventData } : {}
|
|
48238
48891
|
};
|
|
48239
48892
|
this.pushBuffer.push(push);
|
|
48240
48893
|
if (this.pushBuffer.length > 500)
|
|
@@ -48388,31 +49041,125 @@ class BrokerClient {
|
|
|
48388
49041
|
data: msg.data,
|
|
48389
49042
|
publishedBy: String(msg.publishedBy ?? "")
|
|
48390
49043
|
};
|
|
48391
|
-
for (const h of this.streamDataHandlers) {
|
|
48392
|
-
try {
|
|
48393
|
-
h(evt);
|
|
48394
|
-
} catch {}
|
|
49044
|
+
for (const h of this.streamDataHandlers) {
|
|
49045
|
+
try {
|
|
49046
|
+
h(evt);
|
|
49047
|
+
} catch {}
|
|
49048
|
+
}
|
|
49049
|
+
return;
|
|
49050
|
+
}
|
|
49051
|
+
if (msg.type === "clock_status") {
|
|
49052
|
+
this.resolveFromMap(this.clockStatusResolvers, msgReqId, {
|
|
49053
|
+
speed: Number(msg.speed ?? 0),
|
|
49054
|
+
paused: Boolean(msg.paused),
|
|
49055
|
+
tick: Number(msg.tick ?? 0),
|
|
49056
|
+
simTime: String(msg.simTime ?? ""),
|
|
49057
|
+
startedAt: String(msg.startedAt ?? "")
|
|
49058
|
+
});
|
|
49059
|
+
return;
|
|
49060
|
+
}
|
|
49061
|
+
if (msg.type === "mesh_info_result") {
|
|
49062
|
+
this.resolveFromMap(this.meshInfoResolvers, msgReqId, msg);
|
|
49063
|
+
return;
|
|
49064
|
+
}
|
|
49065
|
+
if (msg.type === "skill_ack") {
|
|
49066
|
+
this.resolveFromMap(this.skillAckResolvers, msgReqId, { name: String(msg.name ?? ""), action: String(msg.action ?? "") });
|
|
49067
|
+
return;
|
|
49068
|
+
}
|
|
49069
|
+
if (msg.type === "skill_data") {
|
|
49070
|
+
const skill = msg.skill;
|
|
49071
|
+
this.resolveFromMap(this.skillDataResolvers, msgReqId, skill ?? null);
|
|
49072
|
+
return;
|
|
49073
|
+
}
|
|
49074
|
+
if (msg.type === "skill_list") {
|
|
49075
|
+
const skills = msg.skills ?? [];
|
|
49076
|
+
this.resolveFromMap(this.skillListResolvers, msgReqId, skills);
|
|
49077
|
+
return;
|
|
49078
|
+
}
|
|
49079
|
+
if (msg.type === "scheduled_ack") {
|
|
49080
|
+
this.resolveFromMap(this.scheduledAckResolvers, msgReqId, {
|
|
49081
|
+
scheduledId: String(msg.scheduledId ?? ""),
|
|
49082
|
+
deliverAt: Number(msg.deliverAt ?? 0),
|
|
49083
|
+
...msg.cron ? { cron: String(msg.cron) } : {}
|
|
49084
|
+
});
|
|
49085
|
+
return;
|
|
49086
|
+
}
|
|
49087
|
+
if (msg.type === "scheduled_list") {
|
|
49088
|
+
const messages = msg.messages ?? [];
|
|
49089
|
+
this.resolveFromMap(this.scheduledListResolvers, msgReqId, messages);
|
|
49090
|
+
return;
|
|
49091
|
+
}
|
|
49092
|
+
if (msg.type === "cancel_scheduled_ack") {
|
|
49093
|
+
this.resolveFromMap(this.cancelScheduledResolvers, msgReqId, Boolean(msg.ok));
|
|
49094
|
+
return;
|
|
49095
|
+
}
|
|
49096
|
+
if (msg.type === "mcp_register_ack") {
|
|
49097
|
+
this.resolveFromMap(this.mcpRegisterResolvers, msgReqId, {
|
|
49098
|
+
serverName: String(msg.serverName ?? ""),
|
|
49099
|
+
toolCount: Number(msg.toolCount ?? 0)
|
|
49100
|
+
});
|
|
49101
|
+
return;
|
|
49102
|
+
}
|
|
49103
|
+
if (msg.type === "mcp_list_result") {
|
|
49104
|
+
const servers = msg.servers ?? [];
|
|
49105
|
+
this.resolveFromMap(this.mcpListResolvers, msgReqId, servers);
|
|
49106
|
+
return;
|
|
49107
|
+
}
|
|
49108
|
+
if (msg.type === "mcp_call_result") {
|
|
49109
|
+
this.resolveFromMap(this.mcpCallResolvers, msgReqId, {
|
|
49110
|
+
...msg.result !== undefined ? { result: msg.result } : {},
|
|
49111
|
+
...msg.error ? { error: String(msg.error) } : {}
|
|
49112
|
+
});
|
|
49113
|
+
return;
|
|
49114
|
+
}
|
|
49115
|
+
if (msg.type === "mcp_call_forward") {
|
|
49116
|
+
const forward = {
|
|
49117
|
+
callId: String(msg.callId ?? ""),
|
|
49118
|
+
serverName: String(msg.serverName ?? ""),
|
|
49119
|
+
toolName: String(msg.toolName ?? ""),
|
|
49120
|
+
args: msg.args ?? {},
|
|
49121
|
+
callerName: String(msg.callerName ?? "")
|
|
49122
|
+
};
|
|
49123
|
+
if (this.mcpCallForwardHandler) {
|
|
49124
|
+
this.mcpCallForwardHandler(forward).then((res) => this.sendMcpCallResponse(forward.callId, res.result, res.error)).catch((e) => this.sendMcpCallResponse(forward.callId, undefined, e instanceof Error ? e.message : String(e)));
|
|
49125
|
+
} else {
|
|
49126
|
+
this.sendMcpCallResponse(forward.callId, undefined, "No MCP call handler registered on this peer");
|
|
48395
49127
|
}
|
|
48396
49128
|
return;
|
|
48397
49129
|
}
|
|
48398
|
-
if (msg.type === "
|
|
48399
|
-
this.
|
|
49130
|
+
if (msg.type === "peer_file_request_forward") {
|
|
49131
|
+
this.handlePeerFileRequest(msg);
|
|
48400
49132
|
return;
|
|
48401
49133
|
}
|
|
48402
|
-
if (msg.type === "
|
|
48403
|
-
this.resolveFromMap(this.
|
|
48404
|
-
|
|
48405
|
-
|
|
49134
|
+
if (msg.type === "peer_file_response_forward") {
|
|
49135
|
+
this.resolveFromMap(this.peerFileResponseResolvers, msgReqId, {
|
|
49136
|
+
content: msg.content ? String(msg.content) : undefined,
|
|
49137
|
+
error: msg.error ? String(msg.error) : undefined
|
|
48406
49138
|
});
|
|
48407
49139
|
return;
|
|
48408
49140
|
}
|
|
48409
|
-
if (msg.type === "
|
|
48410
|
-
|
|
48411
|
-
this.resolveFromMap(this.scheduledListResolvers, msgReqId, messages);
|
|
49141
|
+
if (msg.type === "peer_dir_request_forward") {
|
|
49142
|
+
this.handlePeerDirRequest(msg);
|
|
48412
49143
|
return;
|
|
48413
49144
|
}
|
|
48414
|
-
if (msg.type === "
|
|
48415
|
-
this.resolveFromMap(this.
|
|
49145
|
+
if (msg.type === "peer_dir_response_forward") {
|
|
49146
|
+
this.resolveFromMap(this.peerDirResponseResolvers, msgReqId, {
|
|
49147
|
+
entries: msg.entries ?? undefined,
|
|
49148
|
+
error: msg.error ? String(msg.error) : undefined
|
|
49149
|
+
});
|
|
49150
|
+
return;
|
|
49151
|
+
}
|
|
49152
|
+
if (msg.type === "webhook_ack") {
|
|
49153
|
+
this.resolveFromMap(this.webhookAckResolvers, msgReqId, {
|
|
49154
|
+
name: String(msg.name ?? ""),
|
|
49155
|
+
url: String(msg.url ?? ""),
|
|
49156
|
+
secret: String(msg.secret ?? "")
|
|
49157
|
+
});
|
|
49158
|
+
return;
|
|
49159
|
+
}
|
|
49160
|
+
if (msg.type === "webhook_list") {
|
|
49161
|
+
const webhooks = msg.webhooks ?? [];
|
|
49162
|
+
this.resolveFromMap(this.webhookListResolvers, msgReqId, webhooks);
|
|
48416
49163
|
return;
|
|
48417
49164
|
}
|
|
48418
49165
|
if (msg.type === "error") {
|
|
@@ -48457,7 +49204,18 @@ class BrokerClient {
|
|
|
48457
49204
|
[this.taskCreatedResolvers, null],
|
|
48458
49205
|
[this.streamCreatedResolvers, null],
|
|
48459
49206
|
[this.listPeersResolvers, []],
|
|
48460
|
-
[this.meshInfoResolvers, null]
|
|
49207
|
+
[this.meshInfoResolvers, null],
|
|
49208
|
+
[this.clockStatusResolvers, null],
|
|
49209
|
+
[this.mcpRegisterResolvers, null],
|
|
49210
|
+
[this.mcpListResolvers, []],
|
|
49211
|
+
[this.mcpCallResolvers, { error: "broker error" }],
|
|
49212
|
+
[this.skillAckResolvers, null],
|
|
49213
|
+
[this.skillDataResolvers, null],
|
|
49214
|
+
[this.skillListResolvers, []],
|
|
49215
|
+
[this.peerFileResponseResolvers, { error: "broker error" }],
|
|
49216
|
+
[this.peerDirResponseResolvers, { error: "broker error" }],
|
|
49217
|
+
[this.webhookAckResolvers, null],
|
|
49218
|
+
[this.webhookListResolvers, []]
|
|
48461
49219
|
];
|
|
48462
49220
|
for (const [map2, defaultVal] of allMaps) {
|
|
48463
49221
|
const first = map2.entries().next().value;
|
|
@@ -48660,6 +49418,8 @@ If the channel meta contains \`subtype: reminder\`, this is a scheduled reminder
|
|
|
48660
49418
|
| check_messages() | Drain buffered inbound messages (auto-pushed in most cases, use as fallback). |
|
|
48661
49419
|
| set_summary(summary) | Set 1-2 sentence description of your current work, visible to all peers. |
|
|
48662
49420
|
| set_status(status) | Override status: idle, working, or dnd. |
|
|
49421
|
+
| set_visible(visible) | Toggle visibility. Hidden peers skip list_peers and broadcasts; direct messages still arrive. |
|
|
49422
|
+
| set_profile(avatar?, title?, bio?, capabilities?) | Set public profile: emoji avatar, short title, bio, capabilities list. |
|
|
48663
49423
|
| join_group(name, role?) | Join a @group with optional role (lead, member, observer, or any string). |
|
|
48664
49424
|
| leave_group(name) | Leave a @group. |
|
|
48665
49425
|
| set_state(key, value) | Write shared state; pushes change to all peers. |
|
|
@@ -48696,6 +49456,12 @@ If the channel meta contains \`subtype: reminder\`, this is a scheduled reminder
|
|
|
48696
49456
|
| schedule_reminder(message, in_seconds?, deliver_at?, to?) | Schedule a reminder to yourself (no \`to\`) or a delayed message to a peer/group. Delivered as a push with \`subtype: reminder\` in the channel meta. |
|
|
48697
49457
|
| list_scheduled() | List pending scheduled reminders and messages. |
|
|
48698
49458
|
| cancel_scheduled(id) | Cancel a pending scheduled item. |
|
|
49459
|
+
| read_peer_file(peer, path) | Read a file from another peer's project (max 1MB). |
|
|
49460
|
+
| list_peer_files(peer, path?, pattern?) | List files in a peer's shared directory. |
|
|
49461
|
+
| mesh_mcp_register(server_name, description, tools) | Register an MCP server with the mesh. Other peers can call its tools. |
|
|
49462
|
+
| mesh_mcp_list() | List MCP servers available in the mesh with their tools. |
|
|
49463
|
+
| mesh_tool_call(server_name, tool_name, args?) | Call a tool on a mesh-registered MCP server (30s timeout). |
|
|
49464
|
+
| mesh_mcp_remove(server_name) | Unregister an MCP server you registered. |
|
|
48699
49465
|
|
|
48700
49466
|
If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
|
|
48701
49467
|
|
|
@@ -48759,6 +49525,9 @@ Your message mode is "${messageMode}".
|
|
|
48759
49525
|
}));
|
|
48760
49526
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
48761
49527
|
const { name, arguments: args } = req.params;
|
|
49528
|
+
for (const c of allClients()) {
|
|
49529
|
+
c.incrementToolCalls();
|
|
49530
|
+
}
|
|
48762
49531
|
if (config2.meshes.length === 0) {
|
|
48763
49532
|
return text("No meshes joined. Run `claudemesh join https://claudemesh.com/join/<token>` first.", true);
|
|
48764
49533
|
}
|
|
@@ -48805,7 +49574,19 @@ No peers connected.`);
|
|
|
48805
49574
|
const peerLines = peers.map((p) => {
|
|
48806
49575
|
const summary = p.summary ? ` — "${p.summary}"` : "";
|
|
48807
49576
|
const groupsStr = p.groups?.length ? ` [${p.groups.map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ")}]` : "";
|
|
48808
|
-
|
|
49577
|
+
const meta2 = [];
|
|
49578
|
+
if (p.peerType)
|
|
49579
|
+
meta2.push(`type:${p.peerType}`);
|
|
49580
|
+
if (p.channel)
|
|
49581
|
+
meta2.push(`channel:${p.channel}`);
|
|
49582
|
+
if (p.model)
|
|
49583
|
+
meta2.push(`model:${p.model}`);
|
|
49584
|
+
const metaStr = meta2.length ? ` {${meta2.join(", ")}}` : "";
|
|
49585
|
+
const cwdStr = p.cwd ? ` cwd:${p.cwd}` : "";
|
|
49586
|
+
const profileAvatar = p.profile?.avatar ? `${p.profile.avatar} ` : "";
|
|
49587
|
+
const profileTitle = p.profile?.title ? ` (${p.profile.title})` : "";
|
|
49588
|
+
const hiddenTag = p.visible === false ? " [hidden]" : "";
|
|
49589
|
+
return `- ${profileAvatar}**${p.displayName}**${profileTitle} [${p.status}]${hiddenTag}${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`;
|
|
48809
49590
|
});
|
|
48810
49591
|
sections.push(`${header}
|
|
48811
49592
|
${peerLines.join(`
|
|
@@ -48872,6 +49653,32 @@ ${drained.join(`
|
|
|
48872
49653
|
await c.setStatus(s);
|
|
48873
49654
|
return text(`Status set to ${s} across ${allClients().length} mesh(es).`);
|
|
48874
49655
|
}
|
|
49656
|
+
case "set_visible": {
|
|
49657
|
+
const { visible } = args ?? {};
|
|
49658
|
+
if (visible === undefined)
|
|
49659
|
+
return text("set_visible: `visible` required", true);
|
|
49660
|
+
for (const c of allClients())
|
|
49661
|
+
await c.setVisible(visible);
|
|
49662
|
+
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.");
|
|
49663
|
+
}
|
|
49664
|
+
case "set_profile": {
|
|
49665
|
+
const { avatar, title, bio, capabilities } = args ?? {};
|
|
49666
|
+
const profile = { avatar, title, bio, capabilities };
|
|
49667
|
+
for (const c of allClients())
|
|
49668
|
+
await c.setProfile(profile);
|
|
49669
|
+
const parts = [];
|
|
49670
|
+
if (avatar)
|
|
49671
|
+
parts.push(`Avatar: ${avatar}`);
|
|
49672
|
+
if (title)
|
|
49673
|
+
parts.push(`Title: ${title}`);
|
|
49674
|
+
if (bio)
|
|
49675
|
+
parts.push(`Bio: ${bio}`);
|
|
49676
|
+
if (capabilities?.length)
|
|
49677
|
+
parts.push(`Capabilities: ${capabilities.join(", ")}`);
|
|
49678
|
+
return text(parts.length > 0 ? `Profile updated:
|
|
49679
|
+
${parts.join(`
|
|
49680
|
+
`)}` : "Profile cleared.");
|
|
49681
|
+
}
|
|
48875
49682
|
case "join_group": {
|
|
48876
49683
|
const { name: groupName, role } = args ?? {};
|
|
48877
49684
|
if (!groupName)
|
|
@@ -48958,13 +49765,16 @@ ${lines.join(`
|
|
|
48958
49765
|
const sArgs = args ?? {};
|
|
48959
49766
|
if (!sArgs.message)
|
|
48960
49767
|
return text("schedule_reminder: `message` required", true);
|
|
49768
|
+
const isCron = !!sArgs.cron;
|
|
48961
49769
|
let deliverAt;
|
|
48962
|
-
if (
|
|
49770
|
+
if (isCron) {
|
|
49771
|
+
deliverAt = 0;
|
|
49772
|
+
} else if (sArgs.deliver_at) {
|
|
48963
49773
|
deliverAt = Number(sArgs.deliver_at);
|
|
48964
49774
|
} else if (sArgs.in_seconds) {
|
|
48965
49775
|
deliverAt = Date.now() + Number(sArgs.in_seconds) * 1000;
|
|
48966
49776
|
} else {
|
|
48967
|
-
return text("schedule_reminder: provide `deliver_at` (ms timestamp) or `
|
|
49777
|
+
return text("schedule_reminder: provide `deliver_at` (ms timestamp), `in_seconds`, or `cron` expression", true);
|
|
48968
49778
|
}
|
|
48969
49779
|
const isSelf = !sArgs.to;
|
|
48970
49780
|
let targetSpec;
|
|
@@ -48984,9 +49794,13 @@ ${lines.join(`
|
|
|
48984
49794
|
targetSpec = to;
|
|
48985
49795
|
}
|
|
48986
49796
|
}
|
|
48987
|
-
const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true);
|
|
49797
|
+
const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true, sArgs.cron);
|
|
48988
49798
|
if (!result)
|
|
48989
49799
|
return text("schedule_reminder: broker did not acknowledge — check connection", true);
|
|
49800
|
+
if (isCron) {
|
|
49801
|
+
const nextFire = new Date(result.deliverAt).toISOString();
|
|
49802
|
+
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}`);
|
|
49803
|
+
}
|
|
48990
49804
|
const when = new Date(result.deliverAt).toISOString();
|
|
48991
49805
|
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}`);
|
|
48992
49806
|
}
|
|
@@ -49398,6 +50212,72 @@ ${rows.join(`
|
|
|
49398
50212
|
return text("No active streams.");
|
|
49399
50213
|
const lines = streams.map((s) => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`);
|
|
49400
50214
|
return text(lines.join(`
|
|
50215
|
+
`));
|
|
50216
|
+
}
|
|
50217
|
+
case "mesh_set_clock": {
|
|
50218
|
+
const { speed } = args ?? {};
|
|
50219
|
+
if (!speed || speed < 1 || speed > 100)
|
|
50220
|
+
return text("mesh_set_clock: speed must be 1-100", true);
|
|
50221
|
+
const client2 = allClients()[0];
|
|
50222
|
+
if (!client2)
|
|
50223
|
+
return text("mesh_set_clock: not connected", true);
|
|
50224
|
+
const result = await client2.setClock(speed);
|
|
50225
|
+
if (!result)
|
|
50226
|
+
return text("mesh_set_clock: timed out", true);
|
|
50227
|
+
return text([
|
|
50228
|
+
`**Clock set to x${result.speed}**`,
|
|
50229
|
+
`Paused: ${result.paused}`,
|
|
50230
|
+
`Tick: ${result.tick}`,
|
|
50231
|
+
`Sim time: ${result.simTime}`,
|
|
50232
|
+
`Started at: ${result.startedAt}`
|
|
50233
|
+
].join(`
|
|
50234
|
+
`));
|
|
50235
|
+
}
|
|
50236
|
+
case "mesh_pause_clock": {
|
|
50237
|
+
const client2 = allClients()[0];
|
|
50238
|
+
if (!client2)
|
|
50239
|
+
return text("mesh_pause_clock: not connected", true);
|
|
50240
|
+
const result = await client2.pauseClock();
|
|
50241
|
+
if (!result)
|
|
50242
|
+
return text("mesh_pause_clock: timed out", true);
|
|
50243
|
+
return text([
|
|
50244
|
+
"**Clock paused**",
|
|
50245
|
+
`Speed: x${result.speed}`,
|
|
50246
|
+
`Tick: ${result.tick}`,
|
|
50247
|
+
`Sim time: ${result.simTime}`
|
|
50248
|
+
].join(`
|
|
50249
|
+
`));
|
|
50250
|
+
}
|
|
50251
|
+
case "mesh_resume_clock": {
|
|
50252
|
+
const client2 = allClients()[0];
|
|
50253
|
+
if (!client2)
|
|
50254
|
+
return text("mesh_resume_clock: not connected", true);
|
|
50255
|
+
const result = await client2.resumeClock();
|
|
50256
|
+
if (!result)
|
|
50257
|
+
return text("mesh_resume_clock: timed out", true);
|
|
50258
|
+
return text([
|
|
50259
|
+
"**Clock resumed**",
|
|
50260
|
+
`Speed: x${result.speed}`,
|
|
50261
|
+
`Tick: ${result.tick}`,
|
|
50262
|
+
`Sim time: ${result.simTime}`
|
|
50263
|
+
].join(`
|
|
50264
|
+
`));
|
|
50265
|
+
}
|
|
50266
|
+
case "mesh_clock": {
|
|
50267
|
+
const client2 = allClients()[0];
|
|
50268
|
+
if (!client2)
|
|
50269
|
+
return text("mesh_clock: not connected", true);
|
|
50270
|
+
const result = await client2.getClock();
|
|
50271
|
+
if (!result)
|
|
50272
|
+
return text("mesh_clock: timed out", true);
|
|
50273
|
+
const statusLabel = result.speed === 0 ? "not started" : result.paused ? "paused" : "running";
|
|
50274
|
+
return text([
|
|
50275
|
+
`**Clock status: ${statusLabel}**`,
|
|
50276
|
+
`Speed: x${result.speed}`,
|
|
50277
|
+
`Tick: ${result.tick}`,
|
|
50278
|
+
`Sim time: ${result.simTime}`,
|
|
50279
|
+
`Started at: ${result.startedAt}`
|
|
50280
|
+
].join(`
|
|
49401
50281
|
`));
|
|
49402
50282
|
}
|
|
49403
50283
|
case "mesh_info": {
|
|
@@ -49423,6 +50303,89 @@ ${rows.join(`
|
|
|
49423
50303
|
return text(lines.join(`
|
|
49424
50304
|
`));
|
|
49425
50305
|
}
|
|
50306
|
+
case "mesh_stats": {
|
|
50307
|
+
const clients2 = allClients();
|
|
50308
|
+
if (clients2.length === 0)
|
|
50309
|
+
return text("mesh_stats: no joined meshes", true);
|
|
50310
|
+
const sections = [];
|
|
50311
|
+
for (const c of clients2) {
|
|
50312
|
+
const peers = await c.listPeers();
|
|
50313
|
+
const header = `## ${c.meshSlug}`;
|
|
50314
|
+
const rows = peers.map((p) => {
|
|
50315
|
+
const s = p.stats;
|
|
50316
|
+
if (!s)
|
|
50317
|
+
return `| ${p.displayName} | - | - | - | - | - |`;
|
|
50318
|
+
const up = s.uptime != null ? `${Math.floor(s.uptime / 60)}m` : "-";
|
|
50319
|
+
return `| ${p.displayName} | ${s.messagesIn ?? 0} | ${s.messagesOut ?? 0} | ${s.toolCalls ?? 0} | ${up} | ${s.errors ?? 0} |`;
|
|
50320
|
+
});
|
|
50321
|
+
sections.push(`${header}
|
|
50322
|
+
| Peer | Msgs In | Msgs Out | Tool Calls | Uptime | Errors |
|
|
50323
|
+
|------|---------|----------|------------|--------|--------|
|
|
50324
|
+
${rows.join(`
|
|
50325
|
+
`)}`);
|
|
50326
|
+
}
|
|
50327
|
+
return text(sections.join(`
|
|
50328
|
+
|
|
50329
|
+
`));
|
|
50330
|
+
}
|
|
50331
|
+
case "share_skill": {
|
|
50332
|
+
const { name: skillName, description: skillDesc, instructions: skillInstr, tags: skillTags } = args ?? {};
|
|
50333
|
+
if (!skillName || !skillDesc || !skillInstr)
|
|
50334
|
+
return text("share_skill: `name`, `description`, and `instructions` required", true);
|
|
50335
|
+
const client2 = allClients()[0];
|
|
50336
|
+
if (!client2)
|
|
50337
|
+
return text("share_skill: not connected", true);
|
|
50338
|
+
const result = await client2.shareSkill(skillName, skillDesc, skillInstr, skillTags);
|
|
50339
|
+
if (!result)
|
|
50340
|
+
return text("share_skill: broker did not acknowledge", true);
|
|
50341
|
+
return text(`Skill "${skillName}" published to the mesh.`);
|
|
50342
|
+
}
|
|
50343
|
+
case "get_skill": {
|
|
50344
|
+
const { name: gsName } = args ?? {};
|
|
50345
|
+
if (!gsName)
|
|
50346
|
+
return text("get_skill: `name` required", true);
|
|
50347
|
+
const client2 = allClients()[0];
|
|
50348
|
+
if (!client2)
|
|
50349
|
+
return text("get_skill: not connected", true);
|
|
50350
|
+
const skill = await client2.getSkill(gsName);
|
|
50351
|
+
if (!skill)
|
|
50352
|
+
return text(`Skill "${gsName}" not found in the mesh.`);
|
|
50353
|
+
return text(`# Skill: ${skill.name}
|
|
50354
|
+
|
|
50355
|
+
**Description:** ${skill.description}
|
|
50356
|
+
**Author:** ${skill.author}
|
|
50357
|
+
**Tags:** ${skill.tags.length ? skill.tags.join(", ") : "none"}
|
|
50358
|
+
**Created:** ${skill.createdAt}
|
|
50359
|
+
|
|
50360
|
+
---
|
|
50361
|
+
|
|
50362
|
+
## Instructions
|
|
50363
|
+
|
|
50364
|
+
${skill.instructions}`);
|
|
50365
|
+
}
|
|
50366
|
+
case "list_skills": {
|
|
50367
|
+
const { query: skillQuery } = args ?? {};
|
|
50368
|
+
const client2 = allClients()[0];
|
|
50369
|
+
if (!client2)
|
|
50370
|
+
return text("list_skills: not connected", true);
|
|
50371
|
+
const skills = await client2.listSkills(skillQuery);
|
|
50372
|
+
if (skills.length === 0)
|
|
50373
|
+
return text(skillQuery ? `No skills found for "${skillQuery}".` : "No skills in the mesh yet.");
|
|
50374
|
+
const lines = skills.map((s) => `- **${s.name}**: ${s.description}${s.tags.length ? ` [${s.tags.join(", ")}]` : ""} (by ${s.author})`);
|
|
50375
|
+
return text(`${skills.length} skill(s):
|
|
50376
|
+
${lines.join(`
|
|
50377
|
+
`)}`);
|
|
50378
|
+
}
|
|
50379
|
+
case "remove_skill": {
|
|
50380
|
+
const { name: rsName } = args ?? {};
|
|
50381
|
+
if (!rsName)
|
|
50382
|
+
return text("remove_skill: `name` required", true);
|
|
50383
|
+
const client2 = allClients()[0];
|
|
50384
|
+
if (!client2)
|
|
50385
|
+
return text("remove_skill: not connected", true);
|
|
50386
|
+
const removed = await client2.removeSkill(rsName);
|
|
50387
|
+
return text(removed ? `Skill "${rsName}" removed.` : `Skill "${rsName}" not found.`, !removed);
|
|
50388
|
+
}
|
|
49426
50389
|
case "ping_mesh": {
|
|
49427
50390
|
const { priorities: pingPriorities } = args ?? {};
|
|
49428
50391
|
const toTest = pingPriorities ?? ["now", "next"];
|
|
@@ -49459,6 +50422,57 @@ ${rows.join(`
|
|
|
49459
50422
|
return text(results.join(`
|
|
49460
50423
|
`));
|
|
49461
50424
|
}
|
|
50425
|
+
case "mesh_mcp_register": {
|
|
50426
|
+
const { server_name, description, tools: regTools } = args ?? {};
|
|
50427
|
+
if (!server_name || !description || !regTools?.length)
|
|
50428
|
+
return text("mesh_mcp_register: `server_name`, `description`, and `tools` required", true);
|
|
50429
|
+
const client2 = allClients()[0];
|
|
50430
|
+
if (!client2)
|
|
50431
|
+
return text("mesh_mcp_register: not connected", true);
|
|
50432
|
+
const result = await client2.mcpRegister(server_name, description, regTools);
|
|
50433
|
+
if (!result)
|
|
50434
|
+
return text("mesh_mcp_register: broker did not acknowledge", true);
|
|
50435
|
+
return text(`Registered MCP server "${result.serverName}" with ${result.toolCount} tool(s). Other peers can now call its tools via mesh_tool_call.`);
|
|
50436
|
+
}
|
|
50437
|
+
case "mesh_mcp_list": {
|
|
50438
|
+
const client2 = allClients()[0];
|
|
50439
|
+
if (!client2)
|
|
50440
|
+
return text("mesh_mcp_list: not connected", true);
|
|
50441
|
+
const servers = await client2.mcpList();
|
|
50442
|
+
if (servers.length === 0)
|
|
50443
|
+
return text("No MCP servers registered in the mesh.");
|
|
50444
|
+
const lines = servers.map((s) => {
|
|
50445
|
+
const toolList = s.tools.map((t) => ` - **${t.name}**: ${t.description}`).join(`
|
|
50446
|
+
`);
|
|
50447
|
+
return `- **${s.name}** (hosted by ${s.hostedBy}): ${s.description}
|
|
50448
|
+
${toolList}`;
|
|
50449
|
+
});
|
|
50450
|
+
return text(`${servers.length} MCP server(s) in mesh:
|
|
50451
|
+
${lines.join(`
|
|
50452
|
+
`)}`);
|
|
50453
|
+
}
|
|
50454
|
+
case "mesh_tool_call": {
|
|
50455
|
+
const { server_name: callServer, tool_name: callTool, args: callArgs } = args ?? {};
|
|
50456
|
+
if (!callServer || !callTool)
|
|
50457
|
+
return text("mesh_tool_call: `server_name` and `tool_name` required", true);
|
|
50458
|
+
const client2 = allClients()[0];
|
|
50459
|
+
if (!client2)
|
|
50460
|
+
return text("mesh_tool_call: not connected", true);
|
|
50461
|
+
const callResult = await client2.mcpCall(callServer, callTool, callArgs ?? {});
|
|
50462
|
+
if (callResult.error)
|
|
50463
|
+
return text(`mesh_tool_call error: ${callResult.error}`, true);
|
|
50464
|
+
return text(typeof callResult.result === "string" ? callResult.result : JSON.stringify(callResult.result, null, 2));
|
|
50465
|
+
}
|
|
50466
|
+
case "mesh_mcp_remove": {
|
|
50467
|
+
const { server_name: rmServer } = args ?? {};
|
|
50468
|
+
if (!rmServer)
|
|
50469
|
+
return text("mesh_mcp_remove: `server_name` required", true);
|
|
50470
|
+
const client2 = allClients()[0];
|
|
50471
|
+
if (!client2)
|
|
50472
|
+
return text("mesh_mcp_remove: not connected", true);
|
|
50473
|
+
await client2.mcpUnregister(rmServer);
|
|
50474
|
+
return text(`Unregistered MCP server "${rmServer}" from the mesh.`);
|
|
50475
|
+
}
|
|
49462
50476
|
case "grant_file_access": {
|
|
49463
50477
|
const { fileId, to: grantTo } = args ?? {};
|
|
49464
50478
|
if (!fileId || !grantTo)
|
|
@@ -49491,6 +50505,117 @@ ${rows.join(`
|
|
|
49491
50505
|
return text("grant_file_access: broker did not confirm", true);
|
|
49492
50506
|
return text(`Access granted: ${targetPeer.displayName} can now download file ${fileId}`);
|
|
49493
50507
|
}
|
|
50508
|
+
case "read_peer_file": {
|
|
50509
|
+
const { peer: peerName, path: filePath } = args ?? {};
|
|
50510
|
+
if (!peerName || !filePath)
|
|
50511
|
+
return text("read_peer_file: `peer` and `path` required", true);
|
|
50512
|
+
const client2 = allClients()[0];
|
|
50513
|
+
if (!client2)
|
|
50514
|
+
return text("read_peer_file: not connected", true);
|
|
50515
|
+
const peers = await client2.listPeers();
|
|
50516
|
+
const nameLower = peerName.toLowerCase();
|
|
50517
|
+
let targetPubkey = null;
|
|
50518
|
+
if (/^[0-9a-f]{64}$/.test(peerName)) {
|
|
50519
|
+
targetPubkey = peerName;
|
|
50520
|
+
} else {
|
|
50521
|
+
const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
|
|
50522
|
+
if (!match) {
|
|
50523
|
+
const partials = peers.filter((p) => p.displayName.toLowerCase().includes(nameLower));
|
|
50524
|
+
if (partials.length === 1) {
|
|
50525
|
+
targetPubkey = partials[0].pubkey;
|
|
50526
|
+
} else {
|
|
50527
|
+
const names = peers.map((p) => p.displayName).join(", ");
|
|
50528
|
+
return text(`read_peer_file: peer "${peerName}" not found. Online: ${names || "(none)"}`, true);
|
|
50529
|
+
}
|
|
50530
|
+
} else {
|
|
50531
|
+
targetPubkey = match.pubkey;
|
|
50532
|
+
}
|
|
50533
|
+
}
|
|
50534
|
+
const result = await client2.requestFile(targetPubkey, filePath);
|
|
50535
|
+
if (result.error)
|
|
50536
|
+
return text(`read_peer_file: ${result.error}`, true);
|
|
50537
|
+
if (!result.content)
|
|
50538
|
+
return text("read_peer_file: empty response from peer", true);
|
|
50539
|
+
try {
|
|
50540
|
+
const decoded = Buffer.from(result.content, "base64").toString("utf-8");
|
|
50541
|
+
return text(decoded);
|
|
50542
|
+
} catch {
|
|
50543
|
+
return text("read_peer_file: failed to decode file content (binary file?)", true);
|
|
50544
|
+
}
|
|
50545
|
+
}
|
|
50546
|
+
case "list_peer_files": {
|
|
50547
|
+
const { peer: peerName, path: dirPath, pattern } = args ?? {};
|
|
50548
|
+
if (!peerName)
|
|
50549
|
+
return text("list_peer_files: `peer` required", true);
|
|
50550
|
+
const client2 = allClients()[0];
|
|
50551
|
+
if (!client2)
|
|
50552
|
+
return text("list_peer_files: not connected", true);
|
|
50553
|
+
const peers = await client2.listPeers();
|
|
50554
|
+
const nameLower = peerName.toLowerCase();
|
|
50555
|
+
let targetPubkey = null;
|
|
50556
|
+
if (/^[0-9a-f]{64}$/.test(peerName)) {
|
|
50557
|
+
targetPubkey = peerName;
|
|
50558
|
+
} else {
|
|
50559
|
+
const match = peers.find((p) => p.displayName.toLowerCase() === nameLower);
|
|
50560
|
+
if (!match) {
|
|
50561
|
+
const partials = peers.filter((p) => p.displayName.toLowerCase().includes(nameLower));
|
|
50562
|
+
if (partials.length === 1) {
|
|
50563
|
+
targetPubkey = partials[0].pubkey;
|
|
50564
|
+
} else {
|
|
50565
|
+
const names = peers.map((p) => p.displayName).join(", ");
|
|
50566
|
+
return text(`list_peer_files: peer "${peerName}" not found. Online: ${names || "(none)"}`, true);
|
|
50567
|
+
}
|
|
50568
|
+
} else {
|
|
50569
|
+
targetPubkey = match.pubkey;
|
|
50570
|
+
}
|
|
50571
|
+
}
|
|
50572
|
+
const result = await client2.requestDir(targetPubkey, dirPath ?? ".", pattern);
|
|
50573
|
+
if (result.error)
|
|
50574
|
+
return text(`list_peer_files: ${result.error}`, true);
|
|
50575
|
+
if (!result.entries || result.entries.length === 0)
|
|
50576
|
+
return text("No files found.");
|
|
50577
|
+
return text(result.entries.join(`
|
|
50578
|
+
`));
|
|
50579
|
+
}
|
|
50580
|
+
case "create_webhook": {
|
|
50581
|
+
const { name: whName } = args ?? {};
|
|
50582
|
+
if (!whName)
|
|
50583
|
+
return text("create_webhook: `name` required", true);
|
|
50584
|
+
const client2 = allClients()[0];
|
|
50585
|
+
if (!client2)
|
|
50586
|
+
return text("create_webhook: not connected", true);
|
|
50587
|
+
const wh = await client2.createWebhook(whName);
|
|
50588
|
+
if (!wh)
|
|
50589
|
+
return text("create_webhook: broker did not acknowledge — check connection", true);
|
|
50590
|
+
return text(`Webhook **${wh.name}** created.
|
|
50591
|
+
|
|
50592
|
+
URL: ${wh.url}
|
|
50593
|
+
Secret: ${wh.secret}
|
|
50594
|
+
|
|
50595
|
+
External services can POST JSON to this URL. The payload will be pushed to all connected mesh peers.`);
|
|
50596
|
+
}
|
|
50597
|
+
case "list_webhooks": {
|
|
50598
|
+
const client2 = allClients()[0];
|
|
50599
|
+
if (!client2)
|
|
50600
|
+
return text("list_webhooks: not connected", true);
|
|
50601
|
+
const webhooks = await client2.listWebhooks();
|
|
50602
|
+
if (webhooks.length === 0)
|
|
50603
|
+
return text("No active webhooks.");
|
|
50604
|
+
const lines = webhooks.map((w) => `- **${w.name}** — ${w.url} (created ${w.createdAt})`);
|
|
50605
|
+
return text(`${webhooks.length} webhook(s):
|
|
50606
|
+
${lines.join(`
|
|
50607
|
+
`)}`);
|
|
50608
|
+
}
|
|
50609
|
+
case "delete_webhook": {
|
|
50610
|
+
const { name: delName } = args ?? {};
|
|
50611
|
+
if (!delName)
|
|
50612
|
+
return text("delete_webhook: `name` required", true);
|
|
50613
|
+
const client2 = allClients()[0];
|
|
50614
|
+
if (!client2)
|
|
50615
|
+
return text("delete_webhook: not connected", true);
|
|
50616
|
+
const ok = await client2.deleteWebhook(delName);
|
|
50617
|
+
return text(ok ? `Webhook "${delName}" deactivated.` : `Failed to deactivate webhook "${delName}".`, !ok);
|
|
50618
|
+
}
|
|
49494
50619
|
default:
|
|
49495
50620
|
return text(`Unknown tool: ${name}`, true);
|
|
49496
50621
|
}
|
|
@@ -49502,6 +50627,44 @@ ${rows.join(`
|
|
|
49502
50627
|
client2.onPush(async (msg) => {
|
|
49503
50628
|
if (messageMode === "off")
|
|
49504
50629
|
return;
|
|
50630
|
+
if (msg.subtype === "system" && msg.event) {
|
|
50631
|
+
const eventName = msg.event;
|
|
50632
|
+
const data = msg.eventData ?? {};
|
|
50633
|
+
let content2;
|
|
50634
|
+
if (eventName === "tick") {
|
|
50635
|
+
const tick = data.tick ?? 0;
|
|
50636
|
+
const simTime = String(data.simTime ?? "").replace("T", " ").replace(/\..*/, "");
|
|
50637
|
+
const speed = data.speed ?? 1;
|
|
50638
|
+
content2 = `[heartbeat] tick ${tick} | sim time: ${simTime} | speed: x${speed}`;
|
|
50639
|
+
} else if (eventName === "peer_joined") {
|
|
50640
|
+
content2 = `[system] Peer "${data.name ?? "unknown"}" joined the mesh`;
|
|
50641
|
+
} else if (eventName === "peer_left") {
|
|
50642
|
+
content2 = `[system] Peer "${data.name ?? "unknown"}" left the mesh`;
|
|
50643
|
+
} else {
|
|
50644
|
+
content2 = `[system] ${eventName}: ${JSON.stringify(data)}`;
|
|
50645
|
+
}
|
|
50646
|
+
try {
|
|
50647
|
+
await server.notification({
|
|
50648
|
+
method: "notifications/claude/channel",
|
|
50649
|
+
params: {
|
|
50650
|
+
content: content2,
|
|
50651
|
+
meta: {
|
|
50652
|
+
kind: "system",
|
|
50653
|
+
event: eventName,
|
|
50654
|
+
mesh_slug: client2.meshSlug,
|
|
50655
|
+
mesh_id: client2.meshId,
|
|
50656
|
+
...Object.keys(data).length > 0 ? { eventData: data } : {}
|
|
50657
|
+
}
|
|
50658
|
+
}
|
|
50659
|
+
});
|
|
50660
|
+
process.stderr.write(`[claudemesh] system: ${content2}
|
|
50661
|
+
`);
|
|
50662
|
+
} catch (pushErr) {
|
|
50663
|
+
process.stderr.write(`[claudemesh] system push FAILED: ${pushErr}
|
|
50664
|
+
`);
|
|
50665
|
+
}
|
|
50666
|
+
return;
|
|
50667
|
+
}
|
|
49505
50668
|
const fromPubkey = msg.senderPubkey || "";
|
|
49506
50669
|
const fromName = fromPubkey ? await resolvePeerName(client2, fromPubkey) : "unknown";
|
|
49507
50670
|
if (messageMode === "inbox") {
|
|
@@ -49600,6 +50763,7 @@ ${rows.join(`
|
|
|
49600
50763
|
}
|
|
49601
50764
|
|
|
49602
50765
|
// src/commands/install.ts
|
|
50766
|
+
init_config();
|
|
49603
50767
|
import {
|
|
49604
50768
|
chmodSync as chmodSync2,
|
|
49605
50769
|
copyFileSync,
|
|
@@ -49725,47 +50889,51 @@ function writeClaudeSettings(obj) {
|
|
|
49725
50889
|
`, "utf-8");
|
|
49726
50890
|
}
|
|
49727
50891
|
var CLAUDEMESH_TOOLS = [
|
|
49728
|
-
"
|
|
49729
|
-
"mcp__claudemesh__list_peers",
|
|
50892
|
+
"mcp__claudemesh__cancel_scheduled",
|
|
49730
50893
|
"mcp__claudemesh__check_messages",
|
|
49731
|
-
"
|
|
49732
|
-
"
|
|
49733
|
-
"
|
|
49734
|
-
"
|
|
49735
|
-
"
|
|
49736
|
-
"
|
|
49737
|
-
"mcp__claudemesh__list_state",
|
|
49738
|
-
"mcp__claudemesh__remember",
|
|
49739
|
-
"mcp__claudemesh__recall",
|
|
50894
|
+
"mcp__claudemesh__claim_task",
|
|
50895
|
+
"mcp__claudemesh__complete_task",
|
|
50896
|
+
"mcp__claudemesh__create_stream",
|
|
50897
|
+
"mcp__claudemesh__create_task",
|
|
50898
|
+
"mcp__claudemesh__delete_file",
|
|
50899
|
+
"mcp__claudemesh__file_status",
|
|
49740
50900
|
"mcp__claudemesh__forget",
|
|
49741
|
-
"
|
|
50901
|
+
"mcp__claudemesh__get_context",
|
|
49742
50902
|
"mcp__claudemesh__get_file",
|
|
49743
|
-
"
|
|
49744
|
-
"
|
|
49745
|
-
"mcp__claudemesh__delete_file",
|
|
49746
|
-
"mcp__claudemesh__vector_store",
|
|
49747
|
-
"mcp__claudemesh__vector_search",
|
|
49748
|
-
"mcp__claudemesh__vector_delete",
|
|
49749
|
-
"mcp__claudemesh__list_collections",
|
|
49750
|
-
"mcp__claudemesh__graph_query",
|
|
50903
|
+
"mcp__claudemesh__get_state",
|
|
50904
|
+
"mcp__claudemesh__grant_file_access",
|
|
49751
50905
|
"mcp__claudemesh__graph_execute",
|
|
49752
|
-
"
|
|
49753
|
-
"
|
|
49754
|
-
"
|
|
49755
|
-
"
|
|
49756
|
-
"mcp__claudemesh__get_context",
|
|
50906
|
+
"mcp__claudemesh__graph_query",
|
|
50907
|
+
"mcp__claudemesh__join_group",
|
|
50908
|
+
"mcp__claudemesh__leave_group",
|
|
50909
|
+
"mcp__claudemesh__list_collections",
|
|
49757
50910
|
"mcp__claudemesh__list_contexts",
|
|
49758
|
-
"
|
|
49759
|
-
"
|
|
49760
|
-
"
|
|
49761
|
-
"
|
|
49762
|
-
"mcp__claudemesh__create_stream",
|
|
49763
|
-
"mcp__claudemesh__publish",
|
|
49764
|
-
"mcp__claudemesh__subscribe",
|
|
50911
|
+
"mcp__claudemesh__list_files",
|
|
50912
|
+
"mcp__claudemesh__list_peers",
|
|
50913
|
+
"mcp__claudemesh__list_scheduled",
|
|
50914
|
+
"mcp__claudemesh__list_state",
|
|
49765
50915
|
"mcp__claudemesh__list_streams",
|
|
50916
|
+
"mcp__claudemesh__list_tasks",
|
|
49766
50917
|
"mcp__claudemesh__mesh_execute",
|
|
50918
|
+
"mcp__claudemesh__mesh_info",
|
|
49767
50919
|
"mcp__claudemesh__mesh_query",
|
|
49768
|
-
"mcp__claudemesh__mesh_schema"
|
|
50920
|
+
"mcp__claudemesh__mesh_schema",
|
|
50921
|
+
"mcp__claudemesh__message_status",
|
|
50922
|
+
"mcp__claudemesh__ping_mesh",
|
|
50923
|
+
"mcp__claudemesh__publish",
|
|
50924
|
+
"mcp__claudemesh__recall",
|
|
50925
|
+
"mcp__claudemesh__remember",
|
|
50926
|
+
"mcp__claudemesh__schedule_reminder",
|
|
50927
|
+
"mcp__claudemesh__send_message",
|
|
50928
|
+
"mcp__claudemesh__set_state",
|
|
50929
|
+
"mcp__claudemesh__set_status",
|
|
50930
|
+
"mcp__claudemesh__set_summary",
|
|
50931
|
+
"mcp__claudemesh__share_context",
|
|
50932
|
+
"mcp__claudemesh__share_file",
|
|
50933
|
+
"mcp__claudemesh__subscribe",
|
|
50934
|
+
"mcp__claudemesh__vector_delete",
|
|
50935
|
+
"mcp__claudemesh__vector_search",
|
|
50936
|
+
"mcp__claudemesh__vector_store"
|
|
49769
50937
|
];
|
|
49770
50938
|
function installAllowedTools() {
|
|
49771
50939
|
const settings = readClaudeSettings();
|
|
@@ -49896,10 +51064,22 @@ function runInstall(args = []) {
|
|
|
49896
51064
|
} else {
|
|
49897
51065
|
console.log(dim("· Hooks skipped (--no-hooks)"));
|
|
49898
51066
|
}
|
|
51067
|
+
let hasMeshes = false;
|
|
51068
|
+
try {
|
|
51069
|
+
const meshConfig = loadConfig();
|
|
51070
|
+
hasMeshes = meshConfig.meshes.length > 0;
|
|
51071
|
+
} catch {}
|
|
49899
51072
|
console.log("");
|
|
49900
51073
|
console.log(yellow(bold2("⚠ RESTART CLAUDE CODE")) + yellow(" for MCP tools to appear."));
|
|
49901
|
-
|
|
49902
|
-
|
|
51074
|
+
if (!hasMeshes) {
|
|
51075
|
+
console.log("");
|
|
51076
|
+
console.log(yellow("No meshes joined.") + " To connect with peers:");
|
|
51077
|
+
console.log(` ${bold2("claudemesh join <invite-url>")}` + dim(" — join an existing mesh"));
|
|
51078
|
+
console.log(` ${dim("Create one at")} ${bold2("https://claudemesh.com/dashboard")}`);
|
|
51079
|
+
} else {
|
|
51080
|
+
console.log("");
|
|
51081
|
+
console.log(`Next: ${bold2("claudemesh join https://claudemesh.com/join/<token>")}`);
|
|
51082
|
+
}
|
|
49903
51083
|
console.log("");
|
|
49904
51084
|
console.log(yellow("⚠ For real-time push messages from peers, launch with:"));
|
|
49905
51085
|
console.log(` ${bold2("claudemesh launch")}` + dim(" (or: claude --dangerously-load-development-channels server:claudemesh)"));
|
|
@@ -50559,7 +51739,7 @@ init_config();
|
|
|
50559
51739
|
// package.json
|
|
50560
51740
|
var package_default = {
|
|
50561
51741
|
name: "claudemesh-cli",
|
|
50562
|
-
version: "0.
|
|
51742
|
+
version: "0.7.0",
|
|
50563
51743
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
50564
51744
|
keywords: [
|
|
50565
51745
|
"claude-code",
|
|
@@ -51025,8 +52205,19 @@ async function runPeers(flags) {
|
|
|
51025
52205
|
const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
|
|
51026
52206
|
const statusIcon = p.status === "working" ? yellow("●") : green("●");
|
|
51027
52207
|
const name = bold2(p.displayName);
|
|
52208
|
+
const meta2 = [];
|
|
52209
|
+
if (p.peerType)
|
|
52210
|
+
meta2.push(p.peerType);
|
|
52211
|
+
if (p.channel)
|
|
52212
|
+
meta2.push(p.channel);
|
|
52213
|
+
if (p.model)
|
|
52214
|
+
meta2.push(p.model);
|
|
52215
|
+
const metaStr = meta2.length ? dim(` (${meta2.join(", ")})`) : "";
|
|
52216
|
+
const cwdStr = p.cwd ? dim(` cwd: ${p.cwd}`) : "";
|
|
51028
52217
|
const summary = p.summary ? dim(` ${p.summary}`) : "";
|
|
51029
|
-
console.log(` ${statusIcon} ${name}${groups}${summary}`);
|
|
52218
|
+
console.log(` ${statusIcon} ${name}${groups}${metaStr}${summary}`);
|
|
52219
|
+
if (cwdStr)
|
|
52220
|
+
console.log(` ${cwdStr}`);
|
|
51030
52221
|
}
|
|
51031
52222
|
console.log("");
|
|
51032
52223
|
});
|
|
@@ -51313,13 +52504,15 @@ async function runRemind(flags, positional) {
|
|
|
51313
52504
|
if (!message) {
|
|
51314
52505
|
console.error("Usage: claudemesh remind <message> --in <duration>");
|
|
51315
52506
|
console.error(" claudemesh remind <message> --at <time>");
|
|
52507
|
+
console.error(' claudemesh remind <message> --cron "0 */2 * * *"');
|
|
51316
52508
|
console.error(" claudemesh remind list");
|
|
51317
52509
|
console.error(" claudemesh remind cancel <id>");
|
|
51318
52510
|
process.exit(1);
|
|
51319
52511
|
}
|
|
51320
|
-
const
|
|
51321
|
-
|
|
51322
|
-
|
|
52512
|
+
const isCron = !!flags.cron;
|
|
52513
|
+
const deliverAt = isCron ? 0 : parseDeliverAt(flags);
|
|
52514
|
+
if (!isCron && deliverAt === null) {
|
|
52515
|
+
console.error('Specify when: --in <duration> (e.g. "2h", "30m"), --at <time> (e.g. "15:00"), or --cron <expression>');
|
|
51323
52516
|
process.exit(1);
|
|
51324
52517
|
}
|
|
51325
52518
|
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
@@ -51339,7 +52532,7 @@ async function runRemind(flags, positional) {
|
|
|
51339
52532
|
} else {
|
|
51340
52533
|
targetSpec = client2.getSessionPubkey() ?? "*";
|
|
51341
52534
|
}
|
|
51342
|
-
const result = await client2.scheduleMessage(targetSpec, message, deliverAt);
|
|
52535
|
+
const result = await client2.scheduleMessage(targetSpec, message, deliverAt ?? 0, false, flags.cron);
|
|
51343
52536
|
if (!result) {
|
|
51344
52537
|
console.error("✗ Broker did not acknowledge — check connection");
|
|
51345
52538
|
process.exit(1);
|
|
@@ -51348,34 +52541,170 @@ async function runRemind(flags, positional) {
|
|
|
51348
52541
|
console.log(JSON.stringify(result));
|
|
51349
52542
|
return;
|
|
51350
52543
|
}
|
|
51351
|
-
const when = new Date(result.deliverAt).toLocaleString();
|
|
51352
52544
|
const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
|
|
51353
|
-
|
|
52545
|
+
if (isCron) {
|
|
52546
|
+
const nextFire = new Date(result.deliverAt).toLocaleString();
|
|
52547
|
+
console.log(`✓ Recurring reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} — cron: ${flags.cron}, next fire: ${nextFire}`);
|
|
52548
|
+
} else {
|
|
52549
|
+
const when = new Date(result.deliverAt).toLocaleString();
|
|
52550
|
+
console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
|
|
52551
|
+
}
|
|
51354
52552
|
});
|
|
51355
52553
|
}
|
|
52554
|
+
// src/templates/dev-team.json
|
|
52555
|
+
var dev_team_default = {
|
|
52556
|
+
name: "dev-team",
|
|
52557
|
+
description: "Software development team with frontend, backend, and devops groups",
|
|
52558
|
+
groups: [
|
|
52559
|
+
{ name: "frontend", roles: ["lead", "member"] },
|
|
52560
|
+
{ name: "backend", roles: ["lead", "member"] },
|
|
52561
|
+
{ name: "devops", roles: ["lead", "member"] },
|
|
52562
|
+
{ name: "qa", roles: ["lead", "member"] }
|
|
52563
|
+
],
|
|
52564
|
+
stateKeys: {
|
|
52565
|
+
sprint: "current",
|
|
52566
|
+
"deploy-frozen": "false",
|
|
52567
|
+
"pr-queue": "[]"
|
|
52568
|
+
},
|
|
52569
|
+
suggestedRoles: ["lead", "member", "reviewer"],
|
|
52570
|
+
systemPromptHint: "You are part of a dev team. Coordinate with @frontend, @backend, @devops groups. Check state keys for sprint status and deploy freezes before making changes."
|
|
52571
|
+
};
|
|
52572
|
+
// src/templates/research.json
|
|
52573
|
+
var research_default = {
|
|
52574
|
+
name: "research",
|
|
52575
|
+
description: "Research and analysis team focused on deep investigation and knowledge sharing",
|
|
52576
|
+
groups: [
|
|
52577
|
+
{ name: "analysis", roles: ["lead", "analyst"] },
|
|
52578
|
+
{ name: "writing", roles: ["lead", "writer", "reviewer"] },
|
|
52579
|
+
{ name: "data", roles: ["engineer", "analyst"] }
|
|
52580
|
+
],
|
|
52581
|
+
stateKeys: {
|
|
52582
|
+
"research-topic": "",
|
|
52583
|
+
phase: "exploration",
|
|
52584
|
+
"findings-count": "0"
|
|
52585
|
+
},
|
|
52586
|
+
suggestedRoles: ["lead", "analyst", "writer", "reviewer"],
|
|
52587
|
+
systemPromptHint: "You are part of a research team. Share findings via remember(), use recall() before starting new analysis. Coordinate phases through state keys."
|
|
52588
|
+
};
|
|
52589
|
+
// src/templates/ops-incident.json
|
|
52590
|
+
var ops_incident_default = {
|
|
52591
|
+
name: "ops-incident",
|
|
52592
|
+
description: "Incident response team with oncall, comms, and engineering groups",
|
|
52593
|
+
groups: [
|
|
52594
|
+
{ name: "oncall", roles: ["primary", "secondary"] },
|
|
52595
|
+
{ name: "comms", roles: ["lead", "scribe"] },
|
|
52596
|
+
{ name: "engineering", roles: ["lead", "responder"] }
|
|
52597
|
+
],
|
|
52598
|
+
stateKeys: {
|
|
52599
|
+
"incident-status": "investigating",
|
|
52600
|
+
severity: "unknown",
|
|
52601
|
+
commander: "",
|
|
52602
|
+
timeline: "[]"
|
|
52603
|
+
},
|
|
52604
|
+
suggestedRoles: ["commander", "primary-oncall", "scribe", "responder"],
|
|
52605
|
+
systemPromptHint: "INCIDENT MODE. Priority: now for all messages. Update incident-status state. Commander coordinates. Scribe maintains timeline. Engineering fixes."
|
|
52606
|
+
};
|
|
52607
|
+
// src/templates/simulation.json
|
|
52608
|
+
var simulation_default = {
|
|
52609
|
+
name: "simulation",
|
|
52610
|
+
description: "Load testing simulation with configurable time multiplier and user personas",
|
|
52611
|
+
groups: [
|
|
52612
|
+
{ name: "personas", roles: ["admin", "user", "customer"] },
|
|
52613
|
+
{ name: "observers", roles: ["monitor", "analyst"] },
|
|
52614
|
+
{ name: "control", roles: ["orchestrator"] }
|
|
52615
|
+
],
|
|
52616
|
+
stateKeys: {
|
|
52617
|
+
"clock-speed": "x1",
|
|
52618
|
+
"sim-status": "paused",
|
|
52619
|
+
"tick-count": "0",
|
|
52620
|
+
scenario: ""
|
|
52621
|
+
},
|
|
52622
|
+
suggestedRoles: ["orchestrator", "persona", "monitor"],
|
|
52623
|
+
systemPromptHint: "SIMULATION MODE. Follow the clock-speed state for time multiplier. Act according to your persona role and the simulated time. Report actions to @observers."
|
|
52624
|
+
};
|
|
52625
|
+
// src/templates/personal.json
|
|
52626
|
+
var personal_default = {
|
|
52627
|
+
name: "personal",
|
|
52628
|
+
description: "Private mesh for a single user — all sessions auto-join",
|
|
52629
|
+
groups: [],
|
|
52630
|
+
stateKeys: {
|
|
52631
|
+
focus: "",
|
|
52632
|
+
todos: "[]"
|
|
52633
|
+
},
|
|
52634
|
+
suggestedRoles: [],
|
|
52635
|
+
systemPromptHint: "Personal workspace. All your Claude Code sessions share this mesh. Use state keys to track focus and todos across sessions."
|
|
52636
|
+
};
|
|
52637
|
+
|
|
52638
|
+
// src/templates/index.ts
|
|
52639
|
+
var TEMPLATES = {
|
|
52640
|
+
"dev-team": dev_team_default,
|
|
52641
|
+
research: research_default,
|
|
52642
|
+
"ops-incident": ops_incident_default,
|
|
52643
|
+
simulation: simulation_default,
|
|
52644
|
+
personal: personal_default
|
|
52645
|
+
};
|
|
52646
|
+
function listTemplates() {
|
|
52647
|
+
return Object.values(TEMPLATES);
|
|
52648
|
+
}
|
|
52649
|
+
function getTemplate(name) {
|
|
52650
|
+
return TEMPLATES[name];
|
|
52651
|
+
}
|
|
52652
|
+
|
|
52653
|
+
// src/commands/create.ts
|
|
52654
|
+
function runCreate(args) {
|
|
52655
|
+
if (args["list-templates"]) {
|
|
52656
|
+
console.log(`Available mesh templates:
|
|
52657
|
+
`);
|
|
52658
|
+
for (const t of listTemplates()) {
|
|
52659
|
+
console.log(` ${t.name}`);
|
|
52660
|
+
console.log(` ${t.description}`);
|
|
52661
|
+
console.log(` Groups: ${t.groups.map((g) => g.name).join(", ") || "(none)"}`);
|
|
52662
|
+
console.log(` State keys: ${Object.keys(t.stateKeys).join(", ") || "(none)"}`);
|
|
52663
|
+
console.log();
|
|
52664
|
+
}
|
|
52665
|
+
return;
|
|
52666
|
+
}
|
|
52667
|
+
const templateName = args.template;
|
|
52668
|
+
if (templateName) {
|
|
52669
|
+
const template = getTemplate(templateName);
|
|
52670
|
+
if (!template) {
|
|
52671
|
+
console.error(`Unknown template "${templateName}". Use --list-templates to see available options.`);
|
|
52672
|
+
process.exit(1);
|
|
52673
|
+
}
|
|
52674
|
+
console.log(`Template "${template.name}" loaded:`);
|
|
52675
|
+
console.log(` Groups: ${template.groups.map((g) => `@${g.name}`).join(", ")}`);
|
|
52676
|
+
console.log(` State keys: ${Object.keys(template.stateKeys).join(", ")}`);
|
|
52677
|
+
console.log(` Hint: ${template.systemPromptHint.slice(0, 80)}...`);
|
|
52678
|
+
console.log();
|
|
52679
|
+
console.log("Template applied. Use `claudemesh launch` with --groups to join the predefined groups.");
|
|
52680
|
+
return;
|
|
52681
|
+
}
|
|
52682
|
+
console.log("Usage: claudemesh create --template <name>");
|
|
52683
|
+
console.log(" claudemesh create --list-templates");
|
|
52684
|
+
}
|
|
51356
52685
|
|
|
51357
52686
|
// src/index.ts
|
|
51358
52687
|
var launch = defineCommand({
|
|
51359
52688
|
meta: {
|
|
51360
52689
|
name: "launch",
|
|
51361
|
-
description: "
|
|
52690
|
+
description: "Spawn a Claude Code session with mesh connectivity and MCP tools"
|
|
51362
52691
|
},
|
|
51363
52692
|
args: {
|
|
51364
52693
|
name: {
|
|
51365
52694
|
type: "string",
|
|
51366
|
-
description: "Display name
|
|
52695
|
+
description: "Display name visible to other peers"
|
|
51367
52696
|
},
|
|
51368
52697
|
role: {
|
|
51369
52698
|
type: "string",
|
|
51370
|
-
description: "
|
|
52699
|
+
description: "Free-form role tag: `dev`, `lead`, `analyst`, etc"
|
|
51371
52700
|
},
|
|
51372
52701
|
groups: {
|
|
51373
52702
|
type: "string",
|
|
51374
|
-
description: 'Groups to join
|
|
52703
|
+
description: 'Groups to join as `group:role,...` — e.g. `"eng/frontend:lead,qa:member"`'
|
|
51375
52704
|
},
|
|
51376
52705
|
mesh: {
|
|
51377
52706
|
type: "string",
|
|
51378
|
-
description: "
|
|
52707
|
+
description: "Mesh slug (interactive picker if omitted and >1 joined)"
|
|
51379
52708
|
},
|
|
51380
52709
|
join: {
|
|
51381
52710
|
type: "string",
|
|
@@ -51383,21 +52712,21 @@ var launch = defineCommand({
|
|
|
51383
52712
|
},
|
|
51384
52713
|
"message-mode": {
|
|
51385
52714
|
type: "string",
|
|
51386
|
-
description: "push (default) | inbox | off —
|
|
52715
|
+
description: '`"push"` (default) | `"inbox"` | `"off"` — how peer messages arrive'
|
|
51387
52716
|
},
|
|
51388
52717
|
"system-prompt": {
|
|
51389
52718
|
type: "string",
|
|
51390
|
-
description: "
|
|
52719
|
+
description: "Custom system prompt for this Claude session"
|
|
51391
52720
|
},
|
|
51392
52721
|
yes: {
|
|
51393
52722
|
type: "boolean",
|
|
51394
52723
|
alias: "y",
|
|
51395
|
-
description: "Skip
|
|
52724
|
+
description: "Skip the --dangerously-skip-permissions confirmation",
|
|
51396
52725
|
default: false
|
|
51397
52726
|
},
|
|
51398
52727
|
quiet: {
|
|
51399
52728
|
type: "boolean",
|
|
51400
|
-
description: "
|
|
52729
|
+
description: "Suppress banner and interactive prompts",
|
|
51401
52730
|
default: false
|
|
51402
52731
|
}
|
|
51403
52732
|
},
|
|
@@ -51408,7 +52737,7 @@ var launch = defineCommand({
|
|
|
51408
52737
|
var install = defineCommand({
|
|
51409
52738
|
meta: {
|
|
51410
52739
|
name: "install",
|
|
51411
|
-
description: "Register MCP server
|
|
52740
|
+
description: "Register MCP server and status hooks with Claude Code"
|
|
51412
52741
|
},
|
|
51413
52742
|
args: {
|
|
51414
52743
|
"no-hooks": {
|
|
@@ -51424,12 +52753,12 @@ var install = defineCommand({
|
|
|
51424
52753
|
var join7 = defineCommand({
|
|
51425
52754
|
meta: {
|
|
51426
52755
|
name: "join",
|
|
51427
|
-
description: "Join a mesh via invite URL"
|
|
52756
|
+
description: "Join a mesh via invite URL or token"
|
|
51428
52757
|
},
|
|
51429
52758
|
args: {
|
|
51430
52759
|
url: {
|
|
51431
52760
|
type: "positional",
|
|
51432
|
-
description: "Invite URL (https://claudemesh.com/join
|
|
52761
|
+
description: "Invite URL (`https://claudemesh.com/join/...`) or token",
|
|
51433
52762
|
required: true
|
|
51434
52763
|
}
|
|
51435
52764
|
},
|
|
@@ -51440,12 +52769,12 @@ var join7 = defineCommand({
|
|
|
51440
52769
|
var leave = defineCommand({
|
|
51441
52770
|
meta: {
|
|
51442
52771
|
name: "leave",
|
|
51443
|
-
description: "Leave a joined mesh"
|
|
52772
|
+
description: "Leave a joined mesh and remove its local keypair"
|
|
51444
52773
|
},
|
|
51445
52774
|
args: {
|
|
51446
52775
|
slug: {
|
|
51447
52776
|
type: "positional",
|
|
51448
|
-
description: "Mesh slug to leave",
|
|
52777
|
+
description: "Mesh slug to leave (see `claudemesh list`)",
|
|
51449
52778
|
required: true
|
|
51450
52779
|
}
|
|
51451
52780
|
},
|
|
@@ -51461,23 +52790,33 @@ var main = defineCommand({
|
|
|
51461
52790
|
},
|
|
51462
52791
|
subCommands: {
|
|
51463
52792
|
launch,
|
|
52793
|
+
create: defineCommand({
|
|
52794
|
+
meta: { name: "create", description: "Create a new mesh from a template" },
|
|
52795
|
+
args: {
|
|
52796
|
+
template: { type: "string", description: "Template name: `dev-team`, `research`, `ops-incident`, `simulation`, `personal`" },
|
|
52797
|
+
"list-templates": { type: "boolean", description: "List available templates and exit", default: false }
|
|
52798
|
+
},
|
|
52799
|
+
run({ args }) {
|
|
52800
|
+
runCreate(args);
|
|
52801
|
+
}
|
|
52802
|
+
}),
|
|
51464
52803
|
install,
|
|
51465
52804
|
uninstall: defineCommand({
|
|
51466
|
-
meta: { name: "uninstall", description: "Remove MCP server and hooks" },
|
|
52805
|
+
meta: { name: "uninstall", description: "Remove MCP server and hooks from Claude Code config" },
|
|
51467
52806
|
run() {
|
|
51468
52807
|
runUninstall();
|
|
51469
52808
|
}
|
|
51470
52809
|
}),
|
|
51471
52810
|
join: join7,
|
|
51472
52811
|
list: defineCommand({
|
|
51473
|
-
meta: { name: "list", description: "Show joined meshes and identities" },
|
|
52812
|
+
meta: { name: "list", description: "Show joined meshes, slugs, and local identities" },
|
|
51474
52813
|
run() {
|
|
51475
52814
|
runList();
|
|
51476
52815
|
}
|
|
51477
52816
|
}),
|
|
51478
52817
|
leave,
|
|
51479
52818
|
peers: defineCommand({
|
|
51480
|
-
meta: { name: "peers", description: "List
|
|
52819
|
+
meta: { name: "peers", description: "List online peers with status, summary, and groups" },
|
|
51481
52820
|
args: {
|
|
51482
52821
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51483
52822
|
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
@@ -51487,34 +52826,34 @@ var main = defineCommand({
|
|
|
51487
52826
|
}
|
|
51488
52827
|
}),
|
|
51489
52828
|
send: defineCommand({
|
|
51490
|
-
meta: { name: "send", description: "Send a message to a peer, group, or
|
|
52829
|
+
meta: { name: "send", description: "Send a message to a peer, group, or all peers" },
|
|
51491
52830
|
args: {
|
|
51492
|
-
to: { type: "positional", description: "Recipient: display name,
|
|
52831
|
+
to: { type: "positional", description: "Recipient: display name, `@group`, `*` (broadcast), or pubkey hex", required: true },
|
|
51493
52832
|
message: { type: "positional", description: "Message text", required: true },
|
|
51494
52833
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51495
|
-
priority: { type: "string", description: "now | next (default) | low" }
|
|
52834
|
+
priority: { type: "string", description: '`"now"` | `"next"` (default) | `"low"`' }
|
|
51496
52835
|
},
|
|
51497
52836
|
async run({ args }) {
|
|
51498
52837
|
await runSend(args, args.to, args.message);
|
|
51499
52838
|
}
|
|
51500
52839
|
}),
|
|
51501
52840
|
inbox: defineCommand({
|
|
51502
|
-
meta: { name: "inbox", description: "
|
|
52841
|
+
meta: { name: "inbox", description: "Drain pending inbound messages" },
|
|
51503
52842
|
args: {
|
|
51504
52843
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51505
52844
|
json: { type: "boolean", description: "Output as JSON", default: false },
|
|
51506
|
-
wait: { type: "string", description: "Seconds to wait for broker delivery (default: 1)" }
|
|
52845
|
+
wait: { type: "string", description: "Seconds to wait for broker delivery (default: `1`)" }
|
|
51507
52846
|
},
|
|
51508
52847
|
async run({ args }) {
|
|
51509
52848
|
await runInbox({ ...args, wait: args.wait ? parseInt(args.wait, 10) : undefined });
|
|
51510
52849
|
}
|
|
51511
52850
|
}),
|
|
51512
52851
|
state: defineCommand({
|
|
51513
|
-
meta: { name: "state", description: "
|
|
52852
|
+
meta: { name: "state", description: "Get, set, or list shared key-value state in the mesh" },
|
|
51514
52853
|
args: {
|
|
51515
|
-
action: { type: "positional", description: "get | set | list", required: true },
|
|
51516
|
-
key: { type: "positional", description: "State key (required for get
|
|
51517
|
-
value: { type: "positional", description: "Value to
|
|
52854
|
+
action: { type: "positional", description: "`get <key>` | `set <key> <value>` | `list`", required: true },
|
|
52855
|
+
key: { type: "positional", description: "State key (required for `get` and `set`)" },
|
|
52856
|
+
value: { type: "positional", description: "Value to store (required for `set`)" },
|
|
51518
52857
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51519
52858
|
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51520
52859
|
},
|
|
@@ -51550,11 +52889,11 @@ var main = defineCommand({
|
|
|
51550
52889
|
}
|
|
51551
52890
|
}),
|
|
51552
52891
|
remember: defineCommand({
|
|
51553
|
-
meta: { name: "remember", description: "Store a memory
|
|
52892
|
+
meta: { name: "remember", description: "Store a persistent memory visible to all peers" },
|
|
51554
52893
|
args: {
|
|
51555
|
-
content: { type: "positional", description: "Text to
|
|
52894
|
+
content: { type: "positional", description: "Text to store", required: true },
|
|
51556
52895
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51557
|
-
tags: { type: "string", description: "Comma-separated tags
|
|
52896
|
+
tags: { type: "string", description: "Comma-separated tags, e.g. `task,context`" },
|
|
51558
52897
|
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51559
52898
|
},
|
|
51560
52899
|
async run({ args }) {
|
|
@@ -51562,9 +52901,9 @@ var main = defineCommand({
|
|
|
51562
52901
|
}
|
|
51563
52902
|
}),
|
|
51564
52903
|
recall: defineCommand({
|
|
51565
|
-
meta: { name: "recall", description: "Search mesh
|
|
52904
|
+
meta: { name: "recall", description: "Search mesh memories by keyword or phrase" },
|
|
51566
52905
|
args: {
|
|
51567
|
-
query: { type: "positional", description: "
|
|
52906
|
+
query: { type: "positional", description: "Full-text search query", required: true },
|
|
51568
52907
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51569
52908
|
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51570
52909
|
},
|
|
@@ -51573,13 +52912,14 @@ var main = defineCommand({
|
|
|
51573
52912
|
}
|
|
51574
52913
|
}),
|
|
51575
52914
|
remind: defineCommand({
|
|
51576
|
-
meta: { name: "remind", description: "Schedule a
|
|
52915
|
+
meta: { name: "remind", description: "Schedule a delayed message. Also: `remind list`, `remind cancel <id>`" },
|
|
51577
52916
|
args: {
|
|
51578
|
-
message: { type: "positional", description: "Message text
|
|
51579
|
-
extra: { type: "positional", description: "
|
|
51580
|
-
in: { type: "string", description: 'Deliver after duration: "2h"
|
|
51581
|
-
at: { type: "string", description: 'Deliver at time: "15:00" or ISO timestamp' },
|
|
51582
|
-
|
|
52917
|
+
message: { type: "positional", description: "Message text — or `list` / `cancel <id>` to manage reminders", required: false },
|
|
52918
|
+
extra: { type: "positional", description: "Reminder ID for `cancel`", required: false },
|
|
52919
|
+
in: { type: "string", description: 'Deliver after duration: `"2h"`, `"30m"`, `"90s"`' },
|
|
52920
|
+
at: { type: "string", description: 'Deliver at time: `"15:00"` or ISO timestamp' },
|
|
52921
|
+
cron: { type: "string", description: 'Recurring cron expression: `"0 */2 * * *"` (every 2h), `"30 9 * * 1-5"` (9:30 weekdays)' },
|
|
52922
|
+
to: { type: "string", description: "Recipient (default: self). Name, `@group`, `*`, or pubkey" },
|
|
51583
52923
|
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51584
52924
|
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51585
52925
|
},
|
|
@@ -51589,31 +52929,31 @@ var main = defineCommand({
|
|
|
51589
52929
|
}
|
|
51590
52930
|
}),
|
|
51591
52931
|
status: defineCommand({
|
|
51592
|
-
meta: { name: "status", description: "Check broker
|
|
52932
|
+
meta: { name: "status", description: "Check broker connectivity for each joined mesh" },
|
|
51593
52933
|
async run() {
|
|
51594
52934
|
await runStatus();
|
|
51595
52935
|
}
|
|
51596
52936
|
}),
|
|
51597
52937
|
doctor: defineCommand({
|
|
51598
|
-
meta: { name: "doctor", description: "Diagnose install, config, keypairs, and PATH" },
|
|
52938
|
+
meta: { name: "doctor", description: "Diagnose install, config, keypairs, and PATH issues" },
|
|
51599
52939
|
async run() {
|
|
51600
52940
|
await runDoctor();
|
|
51601
52941
|
}
|
|
51602
52942
|
}),
|
|
51603
52943
|
mcp: defineCommand({
|
|
51604
|
-
meta: { name: "mcp", description: "Start MCP server
|
|
52944
|
+
meta: { name: "mcp", description: "Start MCP server on stdio (called by Claude Code, not users)" },
|
|
51605
52945
|
async run() {
|
|
51606
52946
|
await startMcpServer();
|
|
51607
52947
|
}
|
|
51608
52948
|
}),
|
|
51609
52949
|
"seed-test-mesh": defineCommand({
|
|
51610
|
-
meta: { name: "seed-test-mesh", description: "Dev
|
|
52950
|
+
meta: { name: "seed-test-mesh", description: "Dev: inject a mesh into local config, skip invite flow" },
|
|
51611
52951
|
run({ rawArgs }) {
|
|
51612
52952
|
runSeedTestMesh(rawArgs);
|
|
51613
52953
|
}
|
|
51614
52954
|
}),
|
|
51615
52955
|
hook: defineCommand({
|
|
51616
|
-
meta: { name: "hook", description: "Internal
|
|
52956
|
+
meta: { name: "hook", description: "Internal: handle Claude Code hook events" },
|
|
51617
52957
|
async run({ rawArgs }) {
|
|
51618
52958
|
await runHook(rawArgs);
|
|
51619
52959
|
}
|