claudemesh-cli 0.6.6 → 0.6.8
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/LICENSE.md +37 -0
- package/dist/index.js +570 -155
- package/package.json +20 -21
package/LICENSE.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 alezmad (claudemesh)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Attribution
|
|
26
|
+
|
|
27
|
+
This project was originally scaffolded using TurboStarter (https://turbostarter.dev),
|
|
28
|
+
a proprietary SaaS starter kit. The TurboStarter scaffold code is covered by
|
|
29
|
+
your separate purchase agreement with TurboStarter and is NOT re-licensed by
|
|
30
|
+
this MIT license. The MIT license above covers claudemesh-specific additions,
|
|
31
|
+
modifications, and original code written on top of that scaffold — including
|
|
32
|
+
but not limited to: apps/broker, apps/cli, apps/web/src/modules/marketing/home,
|
|
33
|
+
packages/db/src/schema/mesh.ts, the protocol, and the documentation.
|
|
34
|
+
|
|
35
|
+
If you are redistributing this repository, you are responsible for compliance
|
|
36
|
+
with BOTH the TurboStarter EULA (for scaffold components) and this MIT license
|
|
37
|
+
(for claudemesh code).
|
package/dist/index.js
CHANGED
|
@@ -47374,6 +47374,39 @@ var TOOLS = [
|
|
|
47374
47374
|
}
|
|
47375
47375
|
}
|
|
47376
47376
|
},
|
|
47377
|
+
{
|
|
47378
|
+
name: "schedule_reminder",
|
|
47379
|
+
description: "Schedule a message for future delivery. Without `to`, it fires back to yourself (a self-reminder). With `to`, it delivers to a peer, @group, or * broadcast. The broker holds it and delivers when the time arrives. Receivers see `subtype: reminder` in the push envelope.",
|
|
47380
|
+
inputSchema: {
|
|
47381
|
+
type: "object",
|
|
47382
|
+
properties: {
|
|
47383
|
+
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" },
|
|
47386
|
+
to: {
|
|
47387
|
+
type: "string",
|
|
47388
|
+
description: "Recipient: display name, pubkey hex, @group, or * (omit for self-reminder)"
|
|
47389
|
+
}
|
|
47390
|
+
},
|
|
47391
|
+
required: ["message"]
|
|
47392
|
+
}
|
|
47393
|
+
},
|
|
47394
|
+
{
|
|
47395
|
+
name: "list_scheduled",
|
|
47396
|
+
description: "List all your pending scheduled messages: id, recipient, preview, and delivery time.",
|
|
47397
|
+
inputSchema: { type: "object", properties: {} }
|
|
47398
|
+
},
|
|
47399
|
+
{
|
|
47400
|
+
name: "cancel_scheduled",
|
|
47401
|
+
description: "Cancel a pending scheduled message before it fires.",
|
|
47402
|
+
inputSchema: {
|
|
47403
|
+
type: "object",
|
|
47404
|
+
properties: {
|
|
47405
|
+
id: { type: "string", description: "Scheduled message ID" }
|
|
47406
|
+
},
|
|
47407
|
+
required: ["id"]
|
|
47408
|
+
}
|
|
47409
|
+
},
|
|
47377
47410
|
{
|
|
47378
47411
|
name: "mesh_info",
|
|
47379
47412
|
description: "Get a complete overview of the mesh: peers, groups, state, memory, files, tasks, streams, tables. Call on session start for full situational awareness.",
|
|
@@ -47740,6 +47773,49 @@ class BrokerClient {
|
|
|
47740
47773
|
return;
|
|
47741
47774
|
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
47742
47775
|
}
|
|
47776
|
+
async scheduleMessage(to, message, deliverAt, isReminder = false) {
|
|
47777
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47778
|
+
return null;
|
|
47779
|
+
return new Promise((resolve) => {
|
|
47780
|
+
const reqId = this.makeReqId();
|
|
47781
|
+
this.scheduledAckResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
47782
|
+
if (this.scheduledAckResolvers.delete(reqId))
|
|
47783
|
+
resolve(null);
|
|
47784
|
+
}, 8000) });
|
|
47785
|
+
this.ws.send(JSON.stringify({
|
|
47786
|
+
type: "schedule",
|
|
47787
|
+
to,
|
|
47788
|
+
message,
|
|
47789
|
+
deliverAt,
|
|
47790
|
+
...isReminder ? { subtype: "reminder" } : {},
|
|
47791
|
+
_reqId: reqId
|
|
47792
|
+
}));
|
|
47793
|
+
});
|
|
47794
|
+
}
|
|
47795
|
+
async listScheduled() {
|
|
47796
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47797
|
+
return [];
|
|
47798
|
+
return new Promise((resolve) => {
|
|
47799
|
+
const reqId = this.makeReqId();
|
|
47800
|
+
this.scheduledListResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
47801
|
+
if (this.scheduledListResolvers.delete(reqId))
|
|
47802
|
+
resolve([]);
|
|
47803
|
+
}, 5000) });
|
|
47804
|
+
this.ws.send(JSON.stringify({ type: "list_scheduled", _reqId: reqId }));
|
|
47805
|
+
});
|
|
47806
|
+
}
|
|
47807
|
+
async cancelScheduled(scheduledId) {
|
|
47808
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47809
|
+
return false;
|
|
47810
|
+
return new Promise((resolve) => {
|
|
47811
|
+
const reqId = this.makeReqId();
|
|
47812
|
+
this.cancelScheduledResolvers.set(reqId, { resolve, timer: setTimeout(() => {
|
|
47813
|
+
if (this.cancelScheduledResolvers.delete(reqId))
|
|
47814
|
+
resolve(false);
|
|
47815
|
+
}, 5000) });
|
|
47816
|
+
this.ws.send(JSON.stringify({ type: "cancel_scheduled", scheduledId, _reqId: reqId }));
|
|
47817
|
+
});
|
|
47818
|
+
}
|
|
47743
47819
|
messageStatusResolvers = new Map;
|
|
47744
47820
|
fileUrlResolvers = new Map;
|
|
47745
47821
|
fileListResolvers = new Map;
|
|
@@ -47757,6 +47833,9 @@ class BrokerClient {
|
|
|
47757
47833
|
streamCreatedResolvers = new Map;
|
|
47758
47834
|
streamListResolvers = new Map;
|
|
47759
47835
|
streamDataHandlers = new Set;
|
|
47836
|
+
scheduledAckResolvers = new Map;
|
|
47837
|
+
scheduledListResolvers = new Map;
|
|
47838
|
+
cancelScheduledResolvers = new Map;
|
|
47760
47839
|
async messageStatus(messageId) {
|
|
47761
47840
|
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
47762
47841
|
return null;
|
|
@@ -48154,7 +48233,8 @@ class BrokerClient {
|
|
|
48154
48233
|
createdAt: String(msg.createdAt ?? ""),
|
|
48155
48234
|
receivedAt: new Date().toISOString(),
|
|
48156
48235
|
plaintext,
|
|
48157
|
-
kind
|
|
48236
|
+
kind,
|
|
48237
|
+
...msg.subtype ? { subtype: msg.subtype } : {}
|
|
48158
48238
|
};
|
|
48159
48239
|
this.pushBuffer.push(push);
|
|
48160
48240
|
if (this.pushBuffer.length > 500)
|
|
@@ -48319,6 +48399,22 @@ class BrokerClient {
|
|
|
48319
48399
|
this.resolveFromMap(this.meshInfoResolvers, msgReqId, msg);
|
|
48320
48400
|
return;
|
|
48321
48401
|
}
|
|
48402
|
+
if (msg.type === "scheduled_ack") {
|
|
48403
|
+
this.resolveFromMap(this.scheduledAckResolvers, msgReqId, {
|
|
48404
|
+
scheduledId: String(msg.scheduledId ?? ""),
|
|
48405
|
+
deliverAt: Number(msg.deliverAt ?? 0)
|
|
48406
|
+
});
|
|
48407
|
+
return;
|
|
48408
|
+
}
|
|
48409
|
+
if (msg.type === "scheduled_list") {
|
|
48410
|
+
const messages = msg.messages ?? [];
|
|
48411
|
+
this.resolveFromMap(this.scheduledListResolvers, msgReqId, messages);
|
|
48412
|
+
return;
|
|
48413
|
+
}
|
|
48414
|
+
if (msg.type === "cancel_scheduled_ack") {
|
|
48415
|
+
this.resolveFromMap(this.cancelScheduledResolvers, msgReqId, Boolean(msg.ok));
|
|
48416
|
+
return;
|
|
48417
|
+
}
|
|
48322
48418
|
if (msg.type === "error") {
|
|
48323
48419
|
this.debug(`broker error: ${msg.code} ${msg.message}`);
|
|
48324
48420
|
const id = msg.id ? String(msg.id) : null;
|
|
@@ -48351,6 +48447,9 @@ class BrokerClient {
|
|
|
48351
48447
|
[this.contextResultsResolvers, []],
|
|
48352
48448
|
[this.contextListResolvers, []],
|
|
48353
48449
|
[this.streamListResolvers, []],
|
|
48450
|
+
[this.scheduledAckResolvers, null],
|
|
48451
|
+
[this.scheduledListResolvers, []],
|
|
48452
|
+
[this.cancelScheduledResolvers, false],
|
|
48354
48453
|
[this.messageStatusResolvers, null],
|
|
48355
48454
|
[this.grantFileAccessResolvers, false],
|
|
48356
48455
|
[this.collectionListResolvers, []],
|
|
@@ -48418,17 +48517,17 @@ async function ensureClient(mesh) {
|
|
|
48418
48517
|
const existing = clients.get(mesh.meshId);
|
|
48419
48518
|
if (existing)
|
|
48420
48519
|
return existing;
|
|
48421
|
-
const
|
|
48422
|
-
clients.set(mesh.meshId,
|
|
48520
|
+
const client2 = new BrokerClient(mesh, { debug: env.CLAUDEMESH_DEBUG, displayName: configDisplayName });
|
|
48521
|
+
clients.set(mesh.meshId, client2);
|
|
48423
48522
|
try {
|
|
48424
|
-
await
|
|
48523
|
+
await client2.connect();
|
|
48425
48524
|
for (const g of configGroups ?? []) {
|
|
48426
48525
|
try {
|
|
48427
|
-
await
|
|
48526
|
+
await client2.joinGroup(g.name, g.role);
|
|
48428
48527
|
} catch {}
|
|
48429
48528
|
}
|
|
48430
48529
|
} catch {}
|
|
48431
|
-
return
|
|
48530
|
+
return client2;
|
|
48432
48531
|
}
|
|
48433
48532
|
async function startClients(config2) {
|
|
48434
48533
|
configDisplayName = config2.displayName;
|
|
@@ -48511,12 +48610,12 @@ async function resolveClient(to) {
|
|
|
48511
48610
|
var peerNameCache = new Map;
|
|
48512
48611
|
var peerNameCacheAge = 0;
|
|
48513
48612
|
var CACHE_TTL_MS = 30000;
|
|
48514
|
-
async function resolvePeerName(
|
|
48613
|
+
async function resolvePeerName(client2, pubkey) {
|
|
48515
48614
|
const now = Date.now();
|
|
48516
48615
|
if (now - peerNameCacheAge > CACHE_TTL_MS) {
|
|
48517
48616
|
peerNameCache.clear();
|
|
48518
48617
|
try {
|
|
48519
|
-
const peers = await
|
|
48618
|
+
const peers = await client2.listPeers();
|
|
48520
48619
|
for (const p of peers)
|
|
48521
48620
|
peerNameCache.set(p.pubkey, p.displayName);
|
|
48522
48621
|
} catch {}
|
|
@@ -48530,7 +48629,8 @@ function decryptFailedWarning(senderPubkey) {
|
|
|
48530
48629
|
}
|
|
48531
48630
|
function formatPush(p, meshSlug) {
|
|
48532
48631
|
const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
|
|
48533
|
-
|
|
48632
|
+
const tag = p.subtype === "reminder" ? " [REMINDER]" : "";
|
|
48633
|
+
return `[${meshSlug}]${tag} from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
|
|
48534
48634
|
${body}`;
|
|
48535
48635
|
}
|
|
48536
48636
|
async function startMcpServer() {
|
|
@@ -48550,6 +48650,8 @@ You are "${myName}"${myRole ? ` (${myRole})` : ""} — a peer in the claudemesh
|
|
|
48550
48650
|
## Responding to messages
|
|
48551
48651
|
When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Pause your current task, reply via send_message, then resume. Read from_name, mesh_slug, and priority from the channel attributes. Reply by setting \`to\` to the sender's from_name (display name). Stay in character per your system prompt. Do not ignore low-priority messages — acknowledge them briefly even if you defer action.
|
|
48552
48652
|
|
|
48653
|
+
If the channel meta contains \`subtype: reminder\`, this is a scheduled reminder you set for yourself — act on it immediately (no reply needed).
|
|
48654
|
+
|
|
48553
48655
|
## Tools
|
|
48554
48656
|
| Tool | Description |
|
|
48555
48657
|
|------|-------------|
|
|
@@ -48591,6 +48693,9 @@ When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATEL
|
|
|
48591
48693
|
| claim_task(id) | Claim an unclaimed task. |
|
|
48592
48694
|
| complete_task(id, result?) | Mark task done with optional result. |
|
|
48593
48695
|
| list_tasks(status?, assignee?) | List tasks filtered by status/assignee. |
|
|
48696
|
+
| 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
|
+
| list_scheduled() | List pending scheduled reminders and messages. |
|
|
48698
|
+
| cancel_scheduled(id) | Cancel a pending scheduled item. |
|
|
48594
48699
|
|
|
48595
48700
|
If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
|
|
48596
48701
|
|
|
@@ -48666,15 +48771,15 @@ Your message mode is "${messageMode}".
|
|
|
48666
48771
|
const results = [];
|
|
48667
48772
|
const seen = new Set;
|
|
48668
48773
|
for (const target of targets) {
|
|
48669
|
-
const { client, targetSpec, error: error2 } = await resolveClient(target);
|
|
48670
|
-
if (!
|
|
48774
|
+
const { client: client2, targetSpec, error: error2 } = await resolveClient(target);
|
|
48775
|
+
if (!client2) {
|
|
48671
48776
|
results.push(`✗ ${target}: ${error2 ?? "no client resolved"}`);
|
|
48672
48777
|
continue;
|
|
48673
48778
|
}
|
|
48674
48779
|
if (seen.has(targetSpec))
|
|
48675
48780
|
continue;
|
|
48676
48781
|
seen.add(targetSpec);
|
|
48677
|
-
const result = await
|
|
48782
|
+
const result = await client2.send(targetSpec, message, priority ?? "next");
|
|
48678
48783
|
if (!result.ok) {
|
|
48679
48784
|
results.push(`✗ ${target}: ${result.error}`);
|
|
48680
48785
|
} else {
|
|
@@ -48795,19 +48900,19 @@ ${drained.join(`
|
|
|
48795
48900
|
const { key } = args ?? {};
|
|
48796
48901
|
if (!key)
|
|
48797
48902
|
return text("get_state: `key` required", true);
|
|
48798
|
-
const
|
|
48799
|
-
if (!
|
|
48903
|
+
const client2 = allClients()[0];
|
|
48904
|
+
if (!client2)
|
|
48800
48905
|
return text("get_state: not connected", true);
|
|
48801
|
-
const result = await
|
|
48906
|
+
const result = await client2.getState(key);
|
|
48802
48907
|
if (!result)
|
|
48803
48908
|
return text(`State "${key}" not found.`);
|
|
48804
48909
|
return text(`${key} = ${JSON.stringify(result.value)} (set by ${result.updatedBy} at ${result.updatedAt})`);
|
|
48805
48910
|
}
|
|
48806
48911
|
case "list_state": {
|
|
48807
|
-
const
|
|
48808
|
-
if (!
|
|
48912
|
+
const client2 = allClients()[0];
|
|
48913
|
+
if (!client2)
|
|
48809
48914
|
return text("list_state: not connected", true);
|
|
48810
|
-
const entries = await
|
|
48915
|
+
const entries = await client2.listState();
|
|
48811
48916
|
if (entries.length === 0)
|
|
48812
48917
|
return text("No shared state set.");
|
|
48813
48918
|
const lines = entries.map((e) => `- **${e.key}** = ${JSON.stringify(e.value)} (by ${e.updatedBy})`);
|
|
@@ -48818,20 +48923,20 @@ ${drained.join(`
|
|
|
48818
48923
|
const { content, tags } = args ?? {};
|
|
48819
48924
|
if (!content)
|
|
48820
48925
|
return text("remember: `content` required", true);
|
|
48821
|
-
const
|
|
48822
|
-
if (!
|
|
48926
|
+
const client2 = allClients()[0];
|
|
48927
|
+
if (!client2)
|
|
48823
48928
|
return text("remember: not connected", true);
|
|
48824
|
-
const id = await
|
|
48929
|
+
const id = await client2.remember(content, tags);
|
|
48825
48930
|
return text(`Remembered${id ? ` (${id})` : ""}: "${content.slice(0, 80)}${content.length > 80 ? "..." : ""}"`);
|
|
48826
48931
|
}
|
|
48827
48932
|
case "recall": {
|
|
48828
48933
|
const { query } = args ?? {};
|
|
48829
48934
|
if (!query)
|
|
48830
48935
|
return text("recall: `query` required", true);
|
|
48831
|
-
const
|
|
48832
|
-
if (!
|
|
48936
|
+
const client2 = allClients()[0];
|
|
48937
|
+
if (!client2)
|
|
48833
48938
|
return text("recall: not connected", true);
|
|
48834
|
-
const memories = await
|
|
48939
|
+
const memories = await client2.recall(query);
|
|
48835
48940
|
if (memories.length === 0)
|
|
48836
48941
|
return text(`No memories found for "${query}".`);
|
|
48837
48942
|
const lines = memories.map((m) => `- [${m.id.slice(0, 8)}] ${m.content} (by ${m.rememberedBy}, ${m.rememberedAt})`);
|
|
@@ -48843,12 +48948,64 @@ ${lines.join(`
|
|
|
48843
48948
|
const { id } = args ?? {};
|
|
48844
48949
|
if (!id)
|
|
48845
48950
|
return text("forget: `id` required", true);
|
|
48846
|
-
const
|
|
48847
|
-
if (!
|
|
48951
|
+
const client2 = allClients()[0];
|
|
48952
|
+
if (!client2)
|
|
48848
48953
|
return text("forget: not connected", true);
|
|
48849
|
-
await
|
|
48954
|
+
await client2.forget(id);
|
|
48850
48955
|
return text(`Forgotten: ${id}`);
|
|
48851
48956
|
}
|
|
48957
|
+
case "schedule_reminder": {
|
|
48958
|
+
const sArgs = args ?? {};
|
|
48959
|
+
if (!sArgs.message)
|
|
48960
|
+
return text("schedule_reminder: `message` required", true);
|
|
48961
|
+
let deliverAt;
|
|
48962
|
+
if (sArgs.deliver_at) {
|
|
48963
|
+
deliverAt = Number(sArgs.deliver_at);
|
|
48964
|
+
} else if (sArgs.in_seconds) {
|
|
48965
|
+
deliverAt = Date.now() + Number(sArgs.in_seconds) * 1000;
|
|
48966
|
+
} else {
|
|
48967
|
+
return text("schedule_reminder: provide `deliver_at` (ms timestamp) or `in_seconds`", true);
|
|
48968
|
+
}
|
|
48969
|
+
const isSelf = !sArgs.to;
|
|
48970
|
+
let targetSpec;
|
|
48971
|
+
if (isSelf) {
|
|
48972
|
+
targetSpec = client.getSessionPubkey() ?? "*";
|
|
48973
|
+
} else {
|
|
48974
|
+
const to = sArgs.to;
|
|
48975
|
+
if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
|
|
48976
|
+
const peers = await client.listPeers();
|
|
48977
|
+
const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
|
|
48978
|
+
if (!match) {
|
|
48979
|
+
const names = peers.map((p) => p.displayName).join(", ");
|
|
48980
|
+
return text(`schedule_reminder: peer "${to}" not found. Online: ${names || "(none)"}`, true);
|
|
48981
|
+
}
|
|
48982
|
+
targetSpec = match.pubkey;
|
|
48983
|
+
} else {
|
|
48984
|
+
targetSpec = to;
|
|
48985
|
+
}
|
|
48986
|
+
}
|
|
48987
|
+
const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true);
|
|
48988
|
+
if (!result)
|
|
48989
|
+
return text("schedule_reminder: broker did not acknowledge — check connection", true);
|
|
48990
|
+
const when = new Date(result.deliverAt).toISOString();
|
|
48991
|
+
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
|
+
}
|
|
48993
|
+
case "list_scheduled": {
|
|
48994
|
+
const scheduled = await client.listScheduled();
|
|
48995
|
+
if (scheduled.length === 0)
|
|
48996
|
+
return text("No pending scheduled messages.");
|
|
48997
|
+
const lines = scheduled.map((m) => `- [${m.id.slice(0, 8)}] → ${m.to === client.getSessionPubkey() ? "self (reminder)" : m.to} at ${new Date(m.deliverAt).toISOString()}: "${m.message.slice(0, 60)}${m.message.length > 60 ? "…" : ""}"`);
|
|
48998
|
+
return text(`${scheduled.length} scheduled:
|
|
48999
|
+
${lines.join(`
|
|
49000
|
+
`)}`);
|
|
49001
|
+
}
|
|
49002
|
+
case "cancel_scheduled": {
|
|
49003
|
+
const { id: schedId } = args ?? {};
|
|
49004
|
+
if (!schedId)
|
|
49005
|
+
return text("cancel_scheduled: `id` required", true);
|
|
49006
|
+
const ok = await client.cancelScheduled(schedId);
|
|
49007
|
+
return text(ok ? `Cancelled: ${schedId}` : `Not found or already fired: ${schedId}`, !ok);
|
|
49008
|
+
}
|
|
48852
49009
|
case "share_file": {
|
|
48853
49010
|
const { path: filePath, name: fileName, tags, to: fileTo } = args ?? {};
|
|
48854
49011
|
if (!filePath)
|
|
@@ -48856,15 +49013,15 @@ ${lines.join(`
|
|
|
48856
49013
|
const { existsSync: existsSync2 } = await import("node:fs");
|
|
48857
49014
|
if (!existsSync2(filePath))
|
|
48858
49015
|
return text(`share_file: file not found: ${filePath}`, true);
|
|
48859
|
-
const
|
|
48860
|
-
if (!
|
|
49016
|
+
const client2 = allClients()[0];
|
|
49017
|
+
if (!client2)
|
|
48861
49018
|
return text("share_file: not connected", true);
|
|
48862
49019
|
if (fileTo) {
|
|
48863
49020
|
const { encryptFile: encryptFile2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
48864
49021
|
const { readFileSync: readFileSync2, writeFileSync: writeFileSync2, mkdtempSync, unlinkSync, rmdirSync } = await import("node:fs");
|
|
48865
49022
|
const { tmpdir } = await import("node:os");
|
|
48866
49023
|
const { join: join2, basename } = await import("node:path");
|
|
48867
|
-
const peers = await
|
|
49024
|
+
const peers = await client2.listPeers();
|
|
48868
49025
|
const targetPeer = peers.find((p) => p.pubkey === fileTo || p.displayName === fileTo);
|
|
48869
49026
|
if (!targetPeer) {
|
|
48870
49027
|
return text(`share_file: peer not found: ${fileTo}`, true);
|
|
@@ -48872,7 +49029,7 @@ ${lines.join(`
|
|
|
48872
49029
|
const plaintext = readFileSync2(filePath);
|
|
48873
49030
|
const { ciphertext, nonce, key } = await encryptFile2(new Uint8Array(plaintext));
|
|
48874
49031
|
const sealedForTarget = await sealKeyForPeer2(key, targetPeer.pubkey);
|
|
48875
|
-
const myPubkey =
|
|
49032
|
+
const myPubkey = client2.getSessionPubkey();
|
|
48876
49033
|
const sealedForSelf = myPubkey ? await sealKeyForPeer2(key, myPubkey) : null;
|
|
48877
49034
|
const fileKeys = [
|
|
48878
49035
|
{ peerPubkey: targetPeer.pubkey, sealedKey: sealedForTarget },
|
|
@@ -48889,7 +49046,7 @@ ${lines.join(`
|
|
|
48889
49046
|
const tmpPath = join2(tmpDir, baseName);
|
|
48890
49047
|
writeFileSync2(tmpPath, combined);
|
|
48891
49048
|
try {
|
|
48892
|
-
const fileId = await
|
|
49049
|
+
const fileId = await client2.uploadFile(tmpPath, client2.meshId, client2.meshSlug, {
|
|
48893
49050
|
name: baseName,
|
|
48894
49051
|
tags,
|
|
48895
49052
|
persistent: true,
|
|
@@ -48910,7 +49067,7 @@ ${lines.join(`
|
|
|
48910
49067
|
}
|
|
48911
49068
|
}
|
|
48912
49069
|
try {
|
|
48913
|
-
const fileId = await
|
|
49070
|
+
const fileId = await client2.uploadFile(filePath, client2.meshId, client2.meshSlug, {
|
|
48914
49071
|
name: fileName,
|
|
48915
49072
|
tags,
|
|
48916
49073
|
persistent: true
|
|
@@ -48924,10 +49081,10 @@ ${lines.join(`
|
|
|
48924
49081
|
const { id, save_to } = args ?? {};
|
|
48925
49082
|
if (!id || !save_to)
|
|
48926
49083
|
return text("get_file: `id` and `save_to` required", true);
|
|
48927
|
-
const
|
|
48928
|
-
if (!
|
|
49084
|
+
const client2 = allClients()[0];
|
|
49085
|
+
if (!client2)
|
|
48929
49086
|
return text("get_file: not connected", true);
|
|
48930
|
-
const result = await
|
|
49087
|
+
const result = await client2.getFile(id);
|
|
48931
49088
|
if (!result)
|
|
48932
49089
|
return text(`get_file: file ${id} not found`, true);
|
|
48933
49090
|
if (result.encrypted) {
|
|
@@ -48935,8 +49092,8 @@ ${lines.join(`
|
|
|
48935
49092
|
return text("get_file: encrypted file — no decryption key available for your session", true);
|
|
48936
49093
|
const { openSealedKey: openSealedKey2, decryptFile: decryptFile2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
48937
49094
|
const { ensureSodium: ensureSodium2 } = await Promise.resolve().then(() => (init_keypair(), exports_keypair));
|
|
48938
|
-
const myPubkey =
|
|
48939
|
-
const mySecret =
|
|
49095
|
+
const myPubkey = client2.getSessionPubkey();
|
|
49096
|
+
const mySecret = client2.getSessionSecretKey();
|
|
48940
49097
|
if (!myPubkey || !mySecret) {
|
|
48941
49098
|
return text("get_file: no session keypair — cannot decrypt", true);
|
|
48942
49099
|
}
|
|
@@ -48971,10 +49128,10 @@ ${lines.join(`
|
|
|
48971
49128
|
}
|
|
48972
49129
|
case "list_files": {
|
|
48973
49130
|
const { query, from } = args ?? {};
|
|
48974
|
-
const
|
|
48975
|
-
if (!
|
|
49131
|
+
const client2 = allClients()[0];
|
|
49132
|
+
if (!client2)
|
|
48976
49133
|
return text("list_files: not connected", true);
|
|
48977
|
-
const files = await
|
|
49134
|
+
const files = await client2.listFiles(query, from);
|
|
48978
49135
|
if (files.length === 0)
|
|
48979
49136
|
return text("No files found.");
|
|
48980
49137
|
const lines = files.map((f) => `- **${f.name}** (${f.id.slice(0, 8)}…, ${f.size} bytes) by ${f.uploadedBy}${f.tags.length ? ` [${f.tags.join(", ")}]` : ""}`);
|
|
@@ -48985,10 +49142,10 @@ ${lines.join(`
|
|
|
48985
49142
|
const { id } = args ?? {};
|
|
48986
49143
|
if (!id)
|
|
48987
49144
|
return text("file_status: `id` required", true);
|
|
48988
|
-
const
|
|
48989
|
-
if (!
|
|
49145
|
+
const client2 = allClients()[0];
|
|
49146
|
+
if (!client2)
|
|
48990
49147
|
return text("file_status: not connected", true);
|
|
48991
|
-
const accesses = await
|
|
49148
|
+
const accesses = await client2.fileStatus(id);
|
|
48992
49149
|
if (accesses.length === 0)
|
|
48993
49150
|
return text("No one has accessed this file yet.");
|
|
48994
49151
|
const lines = accesses.map((a) => `- ${a.peerName} at ${a.accessedAt}`);
|
|
@@ -49000,30 +49157,30 @@ ${lines.join(`
|
|
|
49000
49157
|
const { id } = args ?? {};
|
|
49001
49158
|
if (!id)
|
|
49002
49159
|
return text("delete_file: `id` required", true);
|
|
49003
|
-
const
|
|
49004
|
-
if (!
|
|
49160
|
+
const client2 = allClients()[0];
|
|
49161
|
+
if (!client2)
|
|
49005
49162
|
return text("delete_file: not connected", true);
|
|
49006
|
-
await
|
|
49163
|
+
await client2.deleteFile(id);
|
|
49007
49164
|
return text(`Deleted: ${id}`);
|
|
49008
49165
|
}
|
|
49009
49166
|
case "vector_store": {
|
|
49010
49167
|
const { collection, text: storeText, metadata } = args ?? {};
|
|
49011
49168
|
if (!collection || !storeText)
|
|
49012
49169
|
return text("vector_store: `collection` and `text` required", true);
|
|
49013
|
-
const
|
|
49014
|
-
if (!
|
|
49170
|
+
const client2 = allClients()[0];
|
|
49171
|
+
if (!client2)
|
|
49015
49172
|
return text("vector_store: not connected", true);
|
|
49016
|
-
const id = await
|
|
49173
|
+
const id = await client2.vectorStore(collection, storeText, metadata);
|
|
49017
49174
|
return text(`Stored in ${collection}${id ? ` (${id})` : ""}`);
|
|
49018
49175
|
}
|
|
49019
49176
|
case "vector_search": {
|
|
49020
49177
|
const { collection, query, limit } = args ?? {};
|
|
49021
49178
|
if (!collection || !query)
|
|
49022
49179
|
return text("vector_search: `collection` and `query` required", true);
|
|
49023
|
-
const
|
|
49024
|
-
if (!
|
|
49180
|
+
const client2 = allClients()[0];
|
|
49181
|
+
if (!client2)
|
|
49025
49182
|
return text("vector_search: not connected", true);
|
|
49026
|
-
const results = await
|
|
49183
|
+
const results = await client2.vectorSearch(collection, query, limit);
|
|
49027
49184
|
if (results.length === 0)
|
|
49028
49185
|
return text(`No results in ${collection} for "${query}".`);
|
|
49029
49186
|
const lines = results.map((r) => `- [${r.id.slice(0, 8)}…] (score: ${r.score.toFixed(3)}) ${r.text.slice(0, 120)}${r.text.length > 120 ? "…" : ""}`);
|
|
@@ -49035,17 +49192,17 @@ ${lines.join(`
|
|
|
49035
49192
|
const { collection, id } = args ?? {};
|
|
49036
49193
|
if (!collection || !id)
|
|
49037
49194
|
return text("vector_delete: `collection` and `id` required", true);
|
|
49038
|
-
const
|
|
49039
|
-
if (!
|
|
49195
|
+
const client2 = allClients()[0];
|
|
49196
|
+
if (!client2)
|
|
49040
49197
|
return text("vector_delete: not connected", true);
|
|
49041
|
-
await
|
|
49198
|
+
await client2.vectorDelete(collection, id);
|
|
49042
49199
|
return text(`Deleted ${id} from ${collection}`);
|
|
49043
49200
|
}
|
|
49044
49201
|
case "list_collections": {
|
|
49045
|
-
const
|
|
49046
|
-
if (!
|
|
49202
|
+
const client2 = allClients()[0];
|
|
49203
|
+
if (!client2)
|
|
49047
49204
|
return text("list_collections: not connected", true);
|
|
49048
|
-
const collections = await
|
|
49205
|
+
const collections = await client2.listCollections();
|
|
49049
49206
|
if (collections.length === 0)
|
|
49050
49207
|
return text("No vector collections.");
|
|
49051
49208
|
return text(`Collections:
|
|
@@ -49056,10 +49213,10 @@ ${collections.map((c) => `- ${c}`).join(`
|
|
|
49056
49213
|
const { cypher } = args ?? {};
|
|
49057
49214
|
if (!cypher)
|
|
49058
49215
|
return text("graph_query: `cypher` required", true);
|
|
49059
|
-
const
|
|
49060
|
-
if (!
|
|
49216
|
+
const client2 = allClients()[0];
|
|
49217
|
+
if (!client2)
|
|
49061
49218
|
return text("graph_query: not connected", true);
|
|
49062
|
-
const rows = await
|
|
49219
|
+
const rows = await client2.graphQuery(cypher);
|
|
49063
49220
|
if (rows.length === 0)
|
|
49064
49221
|
return text("No results.");
|
|
49065
49222
|
return text(JSON.stringify(rows, null, 2));
|
|
@@ -49068,30 +49225,30 @@ ${collections.map((c) => `- ${c}`).join(`
|
|
|
49068
49225
|
const { cypher } = args ?? {};
|
|
49069
49226
|
if (!cypher)
|
|
49070
49227
|
return text("graph_execute: `cypher` required", true);
|
|
49071
|
-
const
|
|
49072
|
-
if (!
|
|
49228
|
+
const client2 = allClients()[0];
|
|
49229
|
+
if (!client2)
|
|
49073
49230
|
return text("graph_execute: not connected", true);
|
|
49074
|
-
const rows = await
|
|
49231
|
+
const rows = await client2.graphExecute(cypher);
|
|
49075
49232
|
return text(rows.length > 0 ? JSON.stringify(rows, null, 2) : "Executed successfully.");
|
|
49076
49233
|
}
|
|
49077
49234
|
case "share_context": {
|
|
49078
49235
|
const { summary, files_read, key_findings, tags } = args ?? {};
|
|
49079
49236
|
if (!summary)
|
|
49080
49237
|
return text("share_context: `summary` required", true);
|
|
49081
|
-
const
|
|
49082
|
-
if (!
|
|
49238
|
+
const client2 = allClients()[0];
|
|
49239
|
+
if (!client2)
|
|
49083
49240
|
return text("share_context: not connected", true);
|
|
49084
|
-
await
|
|
49241
|
+
await client2.shareContext(summary, files_read, key_findings, tags);
|
|
49085
49242
|
return text(`Context shared: "${summary.slice(0, 80)}${summary.length > 80 ? "…" : ""}"`);
|
|
49086
49243
|
}
|
|
49087
49244
|
case "get_context": {
|
|
49088
49245
|
const { query } = args ?? {};
|
|
49089
49246
|
if (!query)
|
|
49090
49247
|
return text("get_context: `query` required", true);
|
|
49091
|
-
const
|
|
49092
|
-
if (!
|
|
49248
|
+
const client2 = allClients()[0];
|
|
49249
|
+
if (!client2)
|
|
49093
49250
|
return text("get_context: not connected", true);
|
|
49094
|
-
const contexts = await
|
|
49251
|
+
const contexts = await client2.getContext(query);
|
|
49095
49252
|
if (contexts.length === 0)
|
|
49096
49253
|
return text(`No context found for "${query}".`);
|
|
49097
49254
|
const lines = contexts.map((c) => {
|
|
@@ -49106,10 +49263,10 @@ ${lines.join(`
|
|
|
49106
49263
|
`)}`);
|
|
49107
49264
|
}
|
|
49108
49265
|
case "list_contexts": {
|
|
49109
|
-
const
|
|
49110
|
-
if (!
|
|
49266
|
+
const client2 = allClients()[0];
|
|
49267
|
+
if (!client2)
|
|
49111
49268
|
return text("list_contexts: not connected", true);
|
|
49112
|
-
const contexts = await
|
|
49269
|
+
const contexts = await client2.listContexts();
|
|
49113
49270
|
if (contexts.length === 0)
|
|
49114
49271
|
return text("No peer contexts shared yet.");
|
|
49115
49272
|
const lines = contexts.map((c) => `- **${c.peerName}**: ${c.summary}${c.tags.length ? ` [${c.tags.join(", ")}]` : ""}`);
|
|
@@ -49121,38 +49278,38 @@ ${lines.join(`
|
|
|
49121
49278
|
const { title, assignee, priority, tags } = args ?? {};
|
|
49122
49279
|
if (!title)
|
|
49123
49280
|
return text("create_task: `title` required", true);
|
|
49124
|
-
const
|
|
49125
|
-
if (!
|
|
49281
|
+
const client2 = allClients()[0];
|
|
49282
|
+
if (!client2)
|
|
49126
49283
|
return text("create_task: not connected", true);
|
|
49127
|
-
const id = await
|
|
49284
|
+
const id = await client2.createTask(title, assignee, priority, tags);
|
|
49128
49285
|
return text(`Task created${id ? ` (${id})` : ""}: "${title}"${assignee ? ` → ${assignee}` : ""}`);
|
|
49129
49286
|
}
|
|
49130
49287
|
case "claim_task": {
|
|
49131
49288
|
const { id } = args ?? {};
|
|
49132
49289
|
if (!id)
|
|
49133
49290
|
return text("claim_task: `id` required", true);
|
|
49134
|
-
const
|
|
49135
|
-
if (!
|
|
49291
|
+
const client2 = allClients()[0];
|
|
49292
|
+
if (!client2)
|
|
49136
49293
|
return text("claim_task: not connected", true);
|
|
49137
|
-
await
|
|
49294
|
+
await client2.claimTask(id);
|
|
49138
49295
|
return text(`Claimed task: ${id}`);
|
|
49139
49296
|
}
|
|
49140
49297
|
case "complete_task": {
|
|
49141
49298
|
const { id, result } = args ?? {};
|
|
49142
49299
|
if (!id)
|
|
49143
49300
|
return text("complete_task: `id` required", true);
|
|
49144
|
-
const
|
|
49145
|
-
if (!
|
|
49301
|
+
const client2 = allClients()[0];
|
|
49302
|
+
if (!client2)
|
|
49146
49303
|
return text("complete_task: not connected", true);
|
|
49147
|
-
await
|
|
49304
|
+
await client2.completeTask(id, result);
|
|
49148
49305
|
return text(`Completed task: ${id}${result ? ` — ${result}` : ""}`);
|
|
49149
49306
|
}
|
|
49150
49307
|
case "list_tasks": {
|
|
49151
49308
|
const { status, assignee } = args ?? {};
|
|
49152
|
-
const
|
|
49153
|
-
if (!
|
|
49309
|
+
const client2 = allClients()[0];
|
|
49310
|
+
if (!client2)
|
|
49154
49311
|
return text("list_tasks: not connected", true);
|
|
49155
|
-
const tasks = await
|
|
49312
|
+
const tasks = await client2.listTasks(status, assignee);
|
|
49156
49313
|
if (tasks.length === 0)
|
|
49157
49314
|
return text("No tasks found.");
|
|
49158
49315
|
const lines = tasks.map((t) => `- [${t.id.slice(0, 8)}…] **${t.title}** (${t.status}, ${t.priority}) ${t.assignee ? `→ ${t.assignee}` : "unassigned"} (by ${t.createdBy})`);
|
|
@@ -49164,10 +49321,10 @@ ${lines.join(`
|
|
|
49164
49321
|
const { sql: querySql } = args ?? {};
|
|
49165
49322
|
if (!querySql)
|
|
49166
49323
|
return text("mesh_query: `sql` required", true);
|
|
49167
|
-
const
|
|
49168
|
-
if (!
|
|
49324
|
+
const client2 = allClients()[0];
|
|
49325
|
+
if (!client2)
|
|
49169
49326
|
return text("mesh_query: not connected", true);
|
|
49170
|
-
const result = await
|
|
49327
|
+
const result = await client2.meshQuery(querySql);
|
|
49171
49328
|
if (!result)
|
|
49172
49329
|
return text("mesh_query: query failed or timed out", true);
|
|
49173
49330
|
if (result.rows.length === 0)
|
|
@@ -49185,17 +49342,17 @@ ${rows.join(`
|
|
|
49185
49342
|
const { sql: execSql } = args ?? {};
|
|
49186
49343
|
if (!execSql)
|
|
49187
49344
|
return text("mesh_execute: `sql` required", true);
|
|
49188
|
-
const
|
|
49189
|
-
if (!
|
|
49345
|
+
const client2 = allClients()[0];
|
|
49346
|
+
if (!client2)
|
|
49190
49347
|
return text("mesh_execute: not connected", true);
|
|
49191
|
-
await
|
|
49348
|
+
await client2.meshExecute(execSql);
|
|
49192
49349
|
return text(`Executed.`);
|
|
49193
49350
|
}
|
|
49194
49351
|
case "mesh_schema": {
|
|
49195
|
-
const
|
|
49196
|
-
if (!
|
|
49352
|
+
const client2 = allClients()[0];
|
|
49353
|
+
if (!client2)
|
|
49197
49354
|
return text("mesh_schema: not connected", true);
|
|
49198
|
-
const tables = await
|
|
49355
|
+
const tables = await client2.meshSchema();
|
|
49199
49356
|
if (!tables || tables.length === 0)
|
|
49200
49357
|
return text("No tables in mesh database.");
|
|
49201
49358
|
const lines = tables.map((t) => `**${t.name}**: ${t.columns.map((c) => `${c.name} (${c.type}${c.nullable ? ", nullable" : ""})`).join(", ")}`);
|
|
@@ -49206,37 +49363,37 @@ ${rows.join(`
|
|
|
49206
49363
|
const { name: streamName } = args ?? {};
|
|
49207
49364
|
if (!streamName)
|
|
49208
49365
|
return text("create_stream: `name` required", true);
|
|
49209
|
-
const
|
|
49210
|
-
if (!
|
|
49366
|
+
const client2 = allClients()[0];
|
|
49367
|
+
if (!client2)
|
|
49211
49368
|
return text("create_stream: not connected", true);
|
|
49212
|
-
const streamId = await
|
|
49369
|
+
const streamId = await client2.createStream(streamName);
|
|
49213
49370
|
return text(`Stream created: ${streamName}${streamId ? ` (${streamId})` : ""}`);
|
|
49214
49371
|
}
|
|
49215
49372
|
case "publish": {
|
|
49216
49373
|
const { stream: pubStream, data: pubData } = args ?? {};
|
|
49217
49374
|
if (!pubStream)
|
|
49218
49375
|
return text("publish: `stream` required", true);
|
|
49219
|
-
const
|
|
49220
|
-
if (!
|
|
49376
|
+
const client2 = allClients()[0];
|
|
49377
|
+
if (!client2)
|
|
49221
49378
|
return text("publish: not connected", true);
|
|
49222
|
-
await
|
|
49379
|
+
await client2.publish(pubStream, pubData);
|
|
49223
49380
|
return text(`Published to ${pubStream}.`);
|
|
49224
49381
|
}
|
|
49225
49382
|
case "subscribe": {
|
|
49226
49383
|
const { stream: subStream } = args ?? {};
|
|
49227
49384
|
if (!subStream)
|
|
49228
49385
|
return text("subscribe: `stream` required", true);
|
|
49229
|
-
const
|
|
49230
|
-
if (!
|
|
49386
|
+
const client2 = allClients()[0];
|
|
49387
|
+
if (!client2)
|
|
49231
49388
|
return text("subscribe: not connected", true);
|
|
49232
|
-
await
|
|
49389
|
+
await client2.subscribe(subStream);
|
|
49233
49390
|
return text(`Subscribed to ${subStream}. Data pushes will arrive as channel notifications.`);
|
|
49234
49391
|
}
|
|
49235
49392
|
case "list_streams": {
|
|
49236
|
-
const
|
|
49237
|
-
if (!
|
|
49393
|
+
const client2 = allClients()[0];
|
|
49394
|
+
if (!client2)
|
|
49238
49395
|
return text("list_streams: not connected", true);
|
|
49239
|
-
const streams = await
|
|
49396
|
+
const streams = await client2.listStreams();
|
|
49240
49397
|
if (streams.length === 0)
|
|
49241
49398
|
return text("No active streams.");
|
|
49242
49399
|
const lines = streams.map((s) => `- **${s.name}** (${s.id.slice(0, 8)}…) by ${s.createdBy}, ${s.subscriberCount} subscriber(s)`);
|
|
@@ -49244,10 +49401,10 @@ ${rows.join(`
|
|
|
49244
49401
|
`));
|
|
49245
49402
|
}
|
|
49246
49403
|
case "mesh_info": {
|
|
49247
|
-
const
|
|
49248
|
-
if (!
|
|
49404
|
+
const client2 = allClients()[0];
|
|
49405
|
+
if (!client2)
|
|
49249
49406
|
return text("mesh_info: not connected", true);
|
|
49250
|
-
const info = await
|
|
49407
|
+
const info = await client2.meshInfo();
|
|
49251
49408
|
if (!info)
|
|
49252
49409
|
return text("mesh_info: timed out", true);
|
|
49253
49410
|
const lines = [
|
|
@@ -49269,21 +49426,21 @@ ${rows.join(`
|
|
|
49269
49426
|
case "ping_mesh": {
|
|
49270
49427
|
const { priorities: pingPriorities } = args ?? {};
|
|
49271
49428
|
const toTest = pingPriorities ?? ["now", "next"];
|
|
49272
|
-
const
|
|
49273
|
-
if (!
|
|
49429
|
+
const client2 = allClients()[0];
|
|
49430
|
+
if (!client2)
|
|
49274
49431
|
return text("ping_mesh: not connected", true);
|
|
49275
49432
|
const results = [];
|
|
49276
|
-
results.push(`WS status: ${
|
|
49277
|
-
results.push(`Mesh: ${
|
|
49278
|
-
const peers = await
|
|
49433
|
+
results.push(`WS status: ${client2.status}`);
|
|
49434
|
+
results.push(`Mesh: ${client2.meshSlug}`);
|
|
49435
|
+
const peers = await client2.listPeers();
|
|
49279
49436
|
const selfPeer = peers.find((p) => p.displayName === myName);
|
|
49280
49437
|
results.push(`Your status: ${selfPeer?.status ?? "not found in peer list"}`);
|
|
49281
49438
|
results.push(`Peers online: ${peers.length}`);
|
|
49282
|
-
results.push(`Push buffer: ${
|
|
49439
|
+
results.push(`Push buffer: ${client2.pushHistory.length} buffered`);
|
|
49283
49440
|
for (const prio of toTest) {
|
|
49284
49441
|
const sendTime = Date.now();
|
|
49285
49442
|
const target = peers.find((p) => p.displayName !== myName);
|
|
49286
|
-
const sendResult = await
|
|
49443
|
+
const sendResult = await client2.send(target?.pubkey ?? "*", `__ping__ ${prio} from ${myName} at ${new Date().toISOString()}`, prio);
|
|
49287
49444
|
const ackTime = Date.now();
|
|
49288
49445
|
if (!sendResult.ok) {
|
|
49289
49446
|
results.push(`[${prio}] SEND FAILED: ${sendResult.error}`);
|
|
@@ -49306,14 +49463,14 @@ ${rows.join(`
|
|
|
49306
49463
|
const { fileId, to: grantTo } = args ?? {};
|
|
49307
49464
|
if (!fileId || !grantTo)
|
|
49308
49465
|
return text("grant_file_access: `fileId` and `to` required", true);
|
|
49309
|
-
const
|
|
49310
|
-
if (!
|
|
49466
|
+
const client2 = allClients()[0];
|
|
49467
|
+
if (!client2)
|
|
49311
49468
|
return text("grant_file_access: not connected", true);
|
|
49312
|
-
const peers = await
|
|
49469
|
+
const peers = await client2.listPeers();
|
|
49313
49470
|
const targetPeer = peers.find((p) => p.pubkey === grantTo || p.displayName === grantTo);
|
|
49314
49471
|
if (!targetPeer)
|
|
49315
49472
|
return text(`grant_file_access: peer not found: ${grantTo}`, true);
|
|
49316
|
-
const result = await
|
|
49473
|
+
const result = await client2.getFile(fileId);
|
|
49317
49474
|
if (!result)
|
|
49318
49475
|
return text("grant_file_access: file not found", true);
|
|
49319
49476
|
if (!result.encrypted)
|
|
@@ -49321,15 +49478,15 @@ ${rows.join(`
|
|
|
49321
49478
|
if (!result.sealedKey)
|
|
49322
49479
|
return text("grant_file_access: no key available (are you the owner?)", true);
|
|
49323
49480
|
const { openSealedKey: openSealedKey2, sealKeyForPeer: sealKeyForPeer2 } = await Promise.resolve().then(() => (init_file_crypto(), exports_file_crypto));
|
|
49324
|
-
const myPubkey =
|
|
49325
|
-
const mySecret =
|
|
49481
|
+
const myPubkey = client2.getSessionPubkey();
|
|
49482
|
+
const mySecret = client2.getSessionSecretKey();
|
|
49326
49483
|
if (!myPubkey || !mySecret)
|
|
49327
49484
|
return text("grant_file_access: no session keypair", true);
|
|
49328
49485
|
const kf = await openSealedKey2(result.sealedKey, myPubkey, mySecret);
|
|
49329
49486
|
if (!kf)
|
|
49330
49487
|
return text("grant_file_access: cannot decrypt your own key", true);
|
|
49331
49488
|
const sealedForPeer = await sealKeyForPeer2(kf, targetPeer.pubkey);
|
|
49332
|
-
const ok = await
|
|
49489
|
+
const ok = await client2.grantFileAccess(fileId, targetPeer.pubkey, sealedForPeer);
|
|
49333
49490
|
if (!ok)
|
|
49334
49491
|
return text("grant_file_access: broker did not confirm", true);
|
|
49335
49492
|
return text(`Access granted: ${targetPeer.displayName} can now download file ${fileId}`);
|
|
@@ -49341,12 +49498,12 @@ ${rows.join(`
|
|
|
49341
49498
|
await startClients(config2);
|
|
49342
49499
|
const transport = new StdioServerTransport;
|
|
49343
49500
|
await server.connect(transport);
|
|
49344
|
-
for (const
|
|
49345
|
-
|
|
49501
|
+
for (const client2 of allClients()) {
|
|
49502
|
+
client2.onPush(async (msg) => {
|
|
49346
49503
|
if (messageMode === "off")
|
|
49347
49504
|
return;
|
|
49348
49505
|
const fromPubkey = msg.senderPubkey || "";
|
|
49349
|
-
const fromName = fromPubkey ? await resolvePeerName(
|
|
49506
|
+
const fromName = fromPubkey ? await resolvePeerName(client2, fromPubkey) : "unknown";
|
|
49350
49507
|
if (messageMode === "inbox") {
|
|
49351
49508
|
try {
|
|
49352
49509
|
await server.notification({
|
|
@@ -49368,12 +49525,13 @@ ${rows.join(`
|
|
|
49368
49525
|
meta: {
|
|
49369
49526
|
from_id: fromPubkey,
|
|
49370
49527
|
from_name: fromName,
|
|
49371
|
-
mesh_slug:
|
|
49372
|
-
mesh_id:
|
|
49528
|
+
mesh_slug: client2.meshSlug,
|
|
49529
|
+
mesh_id: client2.meshId,
|
|
49373
49530
|
priority: msg.priority,
|
|
49374
49531
|
sent_at: msg.createdAt,
|
|
49375
49532
|
delivered_at: msg.receivedAt,
|
|
49376
|
-
kind: msg.kind
|
|
49533
|
+
kind: msg.kind,
|
|
49534
|
+
...msg.subtype ? { subtype: msg.subtype } : {}
|
|
49377
49535
|
}
|
|
49378
49536
|
}
|
|
49379
49537
|
});
|
|
@@ -49384,7 +49542,7 @@ ${rows.join(`
|
|
|
49384
49542
|
`);
|
|
49385
49543
|
}
|
|
49386
49544
|
});
|
|
49387
|
-
|
|
49545
|
+
client2.onStreamData(async (evt) => {
|
|
49388
49546
|
try {
|
|
49389
49547
|
await server.notification({
|
|
49390
49548
|
method: "notifications/claude/channel",
|
|
@@ -49399,7 +49557,7 @@ ${rows.join(`
|
|
|
49399
49557
|
});
|
|
49400
49558
|
} catch {}
|
|
49401
49559
|
});
|
|
49402
|
-
|
|
49560
|
+
client2.onStateChange(async (change) => {
|
|
49403
49561
|
try {
|
|
49404
49562
|
await server.notification({
|
|
49405
49563
|
method: "notifications/claude/channel",
|
|
@@ -49416,17 +49574,17 @@ ${rows.join(`
|
|
|
49416
49574
|
});
|
|
49417
49575
|
}
|
|
49418
49576
|
setTimeout(async () => {
|
|
49419
|
-
const
|
|
49420
|
-
if (!
|
|
49577
|
+
const client2 = allClients()[0];
|
|
49578
|
+
if (!client2 || client2.status !== "open")
|
|
49421
49579
|
return;
|
|
49422
49580
|
try {
|
|
49423
|
-
const peers = await
|
|
49581
|
+
const peers = await client2.listPeers();
|
|
49424
49582
|
const peerNames = peers.filter((p) => p.displayName !== myName).map((p) => p.displayName).join(", ") || "none";
|
|
49425
49583
|
await server.notification({
|
|
49426
49584
|
method: "notifications/claude/channel",
|
|
49427
49585
|
params: {
|
|
49428
|
-
content: `[system] Connected as ${myName} to mesh ${
|
|
49429
|
-
meta: { kind: "welcome", mesh_slug:
|
|
49586
|
+
content: `[system] Connected as ${myName} to mesh ${client2.meshSlug}. ${peers.length} peer(s) online: ${peerNames}. Call mesh_info for full details or set_summary to announce yourself.`,
|
|
49587
|
+
meta: { kind: "welcome", mesh_slug: client2.meshSlug }
|
|
49430
49588
|
}
|
|
49431
49589
|
});
|
|
49432
49590
|
} catch {}
|
|
@@ -50401,7 +50559,7 @@ init_config();
|
|
|
50401
50559
|
// package.json
|
|
50402
50560
|
var package_default = {
|
|
50403
50561
|
name: "claudemesh-cli",
|
|
50404
|
-
version: "0.6.
|
|
50562
|
+
version: "0.6.8",
|
|
50405
50563
|
description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
50406
50564
|
keywords: [
|
|
50407
50565
|
"claude-code",
|
|
@@ -50834,13 +50992,13 @@ Joined: ${config2.meshes.map((m) => m.slug).join(", ")}`);
|
|
|
50834
50992
|
process.exit(1);
|
|
50835
50993
|
}
|
|
50836
50994
|
const displayName = opts.displayName ?? config2.displayName ?? `${hostname3()}-${process.pid}`;
|
|
50837
|
-
const
|
|
50995
|
+
const client2 = new BrokerClient(mesh, { displayName });
|
|
50838
50996
|
try {
|
|
50839
|
-
await
|
|
50840
|
-
const result = await fn(
|
|
50997
|
+
await client2.connect();
|
|
50998
|
+
const result = await fn(client2, mesh);
|
|
50841
50999
|
return result;
|
|
50842
51000
|
} finally {
|
|
50843
|
-
|
|
51001
|
+
client2.close();
|
|
50844
51002
|
}
|
|
50845
51003
|
}
|
|
50846
51004
|
|
|
@@ -50851,8 +51009,8 @@ async function runPeers(flags) {
|
|
|
50851
51009
|
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
50852
51010
|
const green = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
|
|
50853
51011
|
const yellow = (s) => useColor ? `\x1B[33m${s}\x1B[39m` : s;
|
|
50854
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
50855
|
-
const peers = await
|
|
51012
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
|
|
51013
|
+
const peers = await client2.listPeers();
|
|
50856
51014
|
if (flags.json) {
|
|
50857
51015
|
console.log(JSON.stringify(peers, null, 2));
|
|
50858
51016
|
return;
|
|
@@ -50877,10 +51035,10 @@ async function runPeers(flags) {
|
|
|
50877
51035
|
// src/commands/send.ts
|
|
50878
51036
|
async function runSend(flags, to, message) {
|
|
50879
51037
|
const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
|
|
50880
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
51038
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
50881
51039
|
let targetSpec = to;
|
|
50882
51040
|
if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
|
|
50883
|
-
const peers = await
|
|
51041
|
+
const peers = await client2.listPeers();
|
|
50884
51042
|
const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
|
|
50885
51043
|
if (!match) {
|
|
50886
51044
|
const names = peers.map((p) => p.displayName).join(", ");
|
|
@@ -50889,7 +51047,7 @@ async function runSend(flags, to, message) {
|
|
|
50889
51047
|
}
|
|
50890
51048
|
targetSpec = match.pubkey;
|
|
50891
51049
|
}
|
|
50892
|
-
const result = await
|
|
51050
|
+
const result = await client2.send(targetSpec, message, priority);
|
|
50893
51051
|
if (result.ok) {
|
|
50894
51052
|
console.log(`✓ Sent to ${to}${result.messageId ? ` (${result.messageId.slice(0, 8)})` : ""}`);
|
|
50895
51053
|
} else {
|
|
@@ -50915,9 +51073,9 @@ async function runInbox(flags) {
|
|
|
50915
51073
|
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
50916
51074
|
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
50917
51075
|
const waitMs = (flags.wait ?? 1) * 1000;
|
|
50918
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
51076
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
|
|
50919
51077
|
await new Promise((resolve2) => setTimeout(resolve2, waitMs));
|
|
50920
|
-
const messages =
|
|
51078
|
+
const messages = client2.drainPushBuffer();
|
|
50921
51079
|
if (flags.json) {
|
|
50922
51080
|
console.log(JSON.stringify(messages, null, 2));
|
|
50923
51081
|
return;
|
|
@@ -50939,8 +51097,8 @@ async function runInbox(flags) {
|
|
|
50939
51097
|
async function runStateGet(flags, key) {
|
|
50940
51098
|
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
50941
51099
|
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
50942
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
50943
|
-
const entry = await
|
|
51100
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51101
|
+
const entry = await client2.getState(key);
|
|
50944
51102
|
if (!entry) {
|
|
50945
51103
|
console.log(dim(`(not set)`));
|
|
50946
51104
|
return;
|
|
@@ -50961,8 +51119,8 @@ async function runStateSet(flags, key, value) {
|
|
|
50961
51119
|
} catch {
|
|
50962
51120
|
parsed = value;
|
|
50963
51121
|
}
|
|
50964
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
50965
|
-
await
|
|
51122
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51123
|
+
await client2.setState(key, parsed);
|
|
50966
51124
|
console.log(`✓ ${key} = ${JSON.stringify(parsed)}`);
|
|
50967
51125
|
});
|
|
50968
51126
|
}
|
|
@@ -50970,8 +51128,8 @@ async function runStateList(flags) {
|
|
|
50970
51128
|
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
50971
51129
|
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
50972
51130
|
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
50973
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (
|
|
50974
|
-
const entries = await
|
|
51131
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
|
|
51132
|
+
const entries = await client2.listState();
|
|
50975
51133
|
if (flags.json) {
|
|
50976
51134
|
console.log(JSON.stringify(entries, null, 2));
|
|
50977
51135
|
return;
|
|
@@ -50988,6 +51146,214 @@ async function runStateList(flags) {
|
|
|
50988
51146
|
});
|
|
50989
51147
|
}
|
|
50990
51148
|
|
|
51149
|
+
// src/commands/memory.ts
|
|
51150
|
+
async function runRemember(flags, content) {
|
|
51151
|
+
const tags = flags.tags ? flags.tags.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
51152
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51153
|
+
const id = await client2.remember(content, tags);
|
|
51154
|
+
if (flags.json) {
|
|
51155
|
+
console.log(JSON.stringify({ id, content, tags }));
|
|
51156
|
+
return;
|
|
51157
|
+
}
|
|
51158
|
+
if (id) {
|
|
51159
|
+
console.log(`✓ Remembered (${id.slice(0, 8)})`);
|
|
51160
|
+
} else {
|
|
51161
|
+
console.error("✗ Failed to store memory");
|
|
51162
|
+
process.exit(1);
|
|
51163
|
+
}
|
|
51164
|
+
});
|
|
51165
|
+
}
|
|
51166
|
+
async function runRecall(flags, query) {
|
|
51167
|
+
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
51168
|
+
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
51169
|
+
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
51170
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51171
|
+
const memories = await client2.recall(query);
|
|
51172
|
+
if (flags.json) {
|
|
51173
|
+
console.log(JSON.stringify(memories, null, 2));
|
|
51174
|
+
return;
|
|
51175
|
+
}
|
|
51176
|
+
if (memories.length === 0) {
|
|
51177
|
+
console.log(dim("No memories found."));
|
|
51178
|
+
return;
|
|
51179
|
+
}
|
|
51180
|
+
for (const m of memories) {
|
|
51181
|
+
const tags = m.tags.length ? dim(` [${m.tags.join(", ")}]`) : "";
|
|
51182
|
+
console.log(`${bold2(m.id.slice(0, 8))}${tags}`);
|
|
51183
|
+
console.log(` ${m.content}`);
|
|
51184
|
+
console.log(dim(` ${m.rememberedBy} · ${new Date(m.rememberedAt).toLocaleString()}`));
|
|
51185
|
+
console.log("");
|
|
51186
|
+
}
|
|
51187
|
+
});
|
|
51188
|
+
}
|
|
51189
|
+
|
|
51190
|
+
// src/commands/info.ts
|
|
51191
|
+
init_config();
|
|
51192
|
+
async function runInfo(flags) {
|
|
51193
|
+
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
51194
|
+
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
51195
|
+
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
51196
|
+
const config2 = loadConfig();
|
|
51197
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2, mesh) => {
|
|
51198
|
+
const [brokerInfo, peers, state] = await Promise.all([
|
|
51199
|
+
client2.meshInfo(),
|
|
51200
|
+
client2.listPeers(),
|
|
51201
|
+
client2.listState()
|
|
51202
|
+
]);
|
|
51203
|
+
const output = {
|
|
51204
|
+
slug: mesh.slug,
|
|
51205
|
+
meshId: mesh.meshId,
|
|
51206
|
+
memberId: mesh.memberId,
|
|
51207
|
+
brokerUrl: mesh.brokerUrl,
|
|
51208
|
+
displayName: config2.displayName ?? null,
|
|
51209
|
+
peerCount: peers.length,
|
|
51210
|
+
stateCount: state.length,
|
|
51211
|
+
...brokerInfo ?? {}
|
|
51212
|
+
};
|
|
51213
|
+
if (flags.json) {
|
|
51214
|
+
console.log(JSON.stringify(output, null, 2));
|
|
51215
|
+
return;
|
|
51216
|
+
}
|
|
51217
|
+
console.log(bold2(mesh.slug) + dim(` · ${mesh.brokerUrl}`));
|
|
51218
|
+
console.log(dim(` mesh: ${mesh.meshId}`));
|
|
51219
|
+
console.log(dim(` member: ${mesh.memberId}`));
|
|
51220
|
+
console.log(` peers: ${peers.length} connected`);
|
|
51221
|
+
console.log(` state: ${state.length} keys`);
|
|
51222
|
+
if (brokerInfo && typeof brokerInfo === "object") {
|
|
51223
|
+
for (const [k, v] of Object.entries(brokerInfo)) {
|
|
51224
|
+
if (["slug", "meshId", "brokerUrl"].includes(k))
|
|
51225
|
+
continue;
|
|
51226
|
+
console.log(dim(` ${k}: ${JSON.stringify(v)}`));
|
|
51227
|
+
}
|
|
51228
|
+
}
|
|
51229
|
+
});
|
|
51230
|
+
}
|
|
51231
|
+
|
|
51232
|
+
// src/commands/remind.ts
|
|
51233
|
+
function parseDuration(raw) {
|
|
51234
|
+
const m = raw.trim().match(/^(\d+(?:\.\d+)?)\s*(s|sec|m|min|h|hr|d|day)?$/i);
|
|
51235
|
+
if (!m)
|
|
51236
|
+
return null;
|
|
51237
|
+
const n = parseFloat(m[1]);
|
|
51238
|
+
const unit = (m[2] ?? "s").toLowerCase();
|
|
51239
|
+
if (unit.startsWith("d"))
|
|
51240
|
+
return n * 86400000;
|
|
51241
|
+
if (unit.startsWith("h"))
|
|
51242
|
+
return n * 3600000;
|
|
51243
|
+
if (unit.startsWith("m"))
|
|
51244
|
+
return n * 60000;
|
|
51245
|
+
return n * 1000;
|
|
51246
|
+
}
|
|
51247
|
+
function parseDeliverAt(flags) {
|
|
51248
|
+
if (flags.in) {
|
|
51249
|
+
const ms = parseDuration(flags.in);
|
|
51250
|
+
if (ms === null)
|
|
51251
|
+
return null;
|
|
51252
|
+
return Date.now() + ms;
|
|
51253
|
+
}
|
|
51254
|
+
if (flags.at) {
|
|
51255
|
+
const hm = flags.at.match(/^(\d{1,2}):(\d{2})$/);
|
|
51256
|
+
if (hm) {
|
|
51257
|
+
const now = new Date;
|
|
51258
|
+
const target = new Date(now);
|
|
51259
|
+
target.setHours(parseInt(hm[1], 10), parseInt(hm[2], 10), 0, 0);
|
|
51260
|
+
if (target <= now)
|
|
51261
|
+
target.setDate(target.getDate() + 1);
|
|
51262
|
+
return target.getTime();
|
|
51263
|
+
}
|
|
51264
|
+
const ts = Date.parse(flags.at);
|
|
51265
|
+
return isNaN(ts) ? null : ts;
|
|
51266
|
+
}
|
|
51267
|
+
return null;
|
|
51268
|
+
}
|
|
51269
|
+
async function runRemind(flags, positional) {
|
|
51270
|
+
const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
|
|
51271
|
+
const dim = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
|
|
51272
|
+
const bold2 = (s) => useColor ? `\x1B[1m${s}\x1B[22m` : s;
|
|
51273
|
+
const action = positional[0];
|
|
51274
|
+
if (action === "list") {
|
|
51275
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51276
|
+
const scheduled = await client2.listScheduled();
|
|
51277
|
+
if (flags.json) {
|
|
51278
|
+
console.log(JSON.stringify(scheduled, null, 2));
|
|
51279
|
+
return;
|
|
51280
|
+
}
|
|
51281
|
+
if (scheduled.length === 0) {
|
|
51282
|
+
console.log(dim("No pending reminders."));
|
|
51283
|
+
return;
|
|
51284
|
+
}
|
|
51285
|
+
for (const m of scheduled) {
|
|
51286
|
+
const when = new Date(m.deliverAt).toLocaleString();
|
|
51287
|
+
const to = m.to === client2.getSessionPubkey() ? dim("(self)") : m.to;
|
|
51288
|
+
console.log(` ${bold2(m.id.slice(0, 8))} → ${to} at ${when}`);
|
|
51289
|
+
console.log(` ${dim(m.message.slice(0, 80))}`);
|
|
51290
|
+
console.log("");
|
|
51291
|
+
}
|
|
51292
|
+
});
|
|
51293
|
+
return;
|
|
51294
|
+
}
|
|
51295
|
+
if (action === "cancel") {
|
|
51296
|
+
const id = positional[1];
|
|
51297
|
+
if (!id) {
|
|
51298
|
+
console.error("Usage: claudemesh remind cancel <id>");
|
|
51299
|
+
process.exit(1);
|
|
51300
|
+
}
|
|
51301
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51302
|
+
const ok = await client2.cancelScheduled(id);
|
|
51303
|
+
if (ok)
|
|
51304
|
+
console.log(`✓ Cancelled ${id}`);
|
|
51305
|
+
else {
|
|
51306
|
+
console.error(`✗ Not found or already fired: ${id}`);
|
|
51307
|
+
process.exit(1);
|
|
51308
|
+
}
|
|
51309
|
+
});
|
|
51310
|
+
return;
|
|
51311
|
+
}
|
|
51312
|
+
const message = action ?? positional.join(" ");
|
|
51313
|
+
if (!message) {
|
|
51314
|
+
console.error("Usage: claudemesh remind <message> --in <duration>");
|
|
51315
|
+
console.error(" claudemesh remind <message> --at <time>");
|
|
51316
|
+
console.error(" claudemesh remind list");
|
|
51317
|
+
console.error(" claudemesh remind cancel <id>");
|
|
51318
|
+
process.exit(1);
|
|
51319
|
+
}
|
|
51320
|
+
const deliverAt = parseDeliverAt(flags);
|
|
51321
|
+
if (deliverAt === null) {
|
|
51322
|
+
console.error('Specify when: --in <duration> (e.g. "2h", "30m") or --at <time> (e.g. "15:00")');
|
|
51323
|
+
process.exit(1);
|
|
51324
|
+
}
|
|
51325
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
|
|
51326
|
+
let targetSpec;
|
|
51327
|
+
if (flags.to && flags.to !== "self") {
|
|
51328
|
+
if (flags.to.startsWith("@") || flags.to === "*" || /^[0-9a-f]{64}$/i.test(flags.to)) {
|
|
51329
|
+
targetSpec = flags.to;
|
|
51330
|
+
} else {
|
|
51331
|
+
const peers = await client2.listPeers();
|
|
51332
|
+
const match = peers.find((p) => p.displayName.toLowerCase() === flags.to.toLowerCase());
|
|
51333
|
+
if (!match) {
|
|
51334
|
+
console.error(`Peer "${flags.to}" not found. Online: ${peers.map((p) => p.displayName).join(", ") || "(none)"}`);
|
|
51335
|
+
process.exit(1);
|
|
51336
|
+
}
|
|
51337
|
+
targetSpec = match.pubkey;
|
|
51338
|
+
}
|
|
51339
|
+
} else {
|
|
51340
|
+
targetSpec = client2.getSessionPubkey() ?? "*";
|
|
51341
|
+
}
|
|
51342
|
+
const result = await client2.scheduleMessage(targetSpec, message, deliverAt);
|
|
51343
|
+
if (!result) {
|
|
51344
|
+
console.error("✗ Broker did not acknowledge — check connection");
|
|
51345
|
+
process.exit(1);
|
|
51346
|
+
}
|
|
51347
|
+
if (flags.json) {
|
|
51348
|
+
console.log(JSON.stringify(result));
|
|
51349
|
+
return;
|
|
51350
|
+
}
|
|
51351
|
+
const when = new Date(result.deliverAt).toLocaleString();
|
|
51352
|
+
const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
|
|
51353
|
+
console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
|
|
51354
|
+
});
|
|
51355
|
+
}
|
|
51356
|
+
|
|
50991
51357
|
// src/index.ts
|
|
50992
51358
|
var launch = defineCommand({
|
|
50993
51359
|
meta: {
|
|
@@ -51173,6 +51539,55 @@ var main = defineCommand({
|
|
|
51173
51539
|
}
|
|
51174
51540
|
}
|
|
51175
51541
|
}),
|
|
51542
|
+
info: defineCommand({
|
|
51543
|
+
meta: { name: "info", description: "Show mesh overview: slug, broker, peer count, state keys" },
|
|
51544
|
+
args: {
|
|
51545
|
+
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51546
|
+
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51547
|
+
},
|
|
51548
|
+
async run({ args }) {
|
|
51549
|
+
await runInfo(args);
|
|
51550
|
+
}
|
|
51551
|
+
}),
|
|
51552
|
+
remember: defineCommand({
|
|
51553
|
+
meta: { name: "remember", description: "Store a memory in the mesh (accessible to all peers)" },
|
|
51554
|
+
args: {
|
|
51555
|
+
content: { type: "positional", description: "Text to remember", required: true },
|
|
51556
|
+
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51557
|
+
tags: { type: "string", description: "Comma-separated tags (e.g. task,context)" },
|
|
51558
|
+
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51559
|
+
},
|
|
51560
|
+
async run({ args }) {
|
|
51561
|
+
await runRemember(args, args.content);
|
|
51562
|
+
}
|
|
51563
|
+
}),
|
|
51564
|
+
recall: defineCommand({
|
|
51565
|
+
meta: { name: "recall", description: "Search mesh memory by keyword or phrase" },
|
|
51566
|
+
args: {
|
|
51567
|
+
query: { type: "positional", description: "Search query", required: true },
|
|
51568
|
+
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51569
|
+
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51570
|
+
},
|
|
51571
|
+
async run({ args }) {
|
|
51572
|
+
await runRecall(args, args.query);
|
|
51573
|
+
}
|
|
51574
|
+
}),
|
|
51575
|
+
remind: defineCommand({
|
|
51576
|
+
meta: { name: "remind", description: "Schedule a reminder or delayed message via the broker" },
|
|
51577
|
+
args: {
|
|
51578
|
+
message: { type: "positional", description: "Message text, or: list | cancel <id>", required: false },
|
|
51579
|
+
extra: { type: "positional", description: "Additional positional args", required: false },
|
|
51580
|
+
in: { type: "string", description: 'Deliver after duration: "2h", "30m", "90s"' },
|
|
51581
|
+
at: { type: "string", description: 'Deliver at time: "15:00" or ISO timestamp' },
|
|
51582
|
+
to: { type: "string", description: "Recipient (default: self). Name, @group, pubkey, or *" },
|
|
51583
|
+
mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
|
|
51584
|
+
json: { type: "boolean", description: "Output as JSON", default: false }
|
|
51585
|
+
},
|
|
51586
|
+
async run({ args, rawArgs }) {
|
|
51587
|
+
const positionals = rawArgs.filter((a) => !a.startsWith("-"));
|
|
51588
|
+
await runRemind(args, positionals);
|
|
51589
|
+
}
|
|
51590
|
+
}),
|
|
51176
51591
|
status: defineCommand({
|
|
51177
51592
|
meta: { name: "status", description: "Check broker reachability for each joined mesh" },
|
|
51178
51593
|
async run() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemesh-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -30,17 +30,6 @@
|
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
|
|
35
|
-
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
36
|
-
"dev": "bun --hot src/index.ts",
|
|
37
|
-
"start": "bun src/index.ts",
|
|
38
|
-
"format": "prettier --check . --ignore-path ../../.gitignore",
|
|
39
|
-
"lint": "eslint",
|
|
40
|
-
"prepublishOnly": "bun run build",
|
|
41
|
-
"test": "vitest run",
|
|
42
|
-
"typecheck": "tsc --noEmit"
|
|
43
|
-
},
|
|
44
33
|
"prettier": "@turbostarter/prettier-config",
|
|
45
34
|
"engines": {
|
|
46
35
|
"node": ">=20"
|
|
@@ -53,15 +42,25 @@
|
|
|
53
42
|
"zod": "4.1.13"
|
|
54
43
|
},
|
|
55
44
|
"devDependencies": {
|
|
56
|
-
"@turbostarter/eslint-config": "workspace:*",
|
|
57
|
-
"@turbostarter/prettier-config": "workspace:*",
|
|
58
|
-
"@turbostarter/tsconfig": "workspace:*",
|
|
59
|
-
"@turbostarter/vitest-config": "workspace:*",
|
|
60
45
|
"@types/libsodium-wrappers": "0.7.14",
|
|
61
46
|
"@types/ws": "8.5.13",
|
|
62
|
-
"eslint": "
|
|
63
|
-
"prettier": "
|
|
64
|
-
"typescript": "
|
|
65
|
-
"vitest": "
|
|
47
|
+
"eslint": "9.39.0",
|
|
48
|
+
"prettier": "3.6.2",
|
|
49
|
+
"typescript": "5.9.3",
|
|
50
|
+
"vitest": "4.0.14",
|
|
51
|
+
"@turbostarter/prettier-config": "0.1.0",
|
|
52
|
+
"@turbostarter/eslint-config": "0.1.0",
|
|
53
|
+
"@turbostarter/vitest-config": "0.1.0",
|
|
54
|
+
"@turbostarter/tsconfig": "0.1.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
|
|
58
|
+
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
59
|
+
"dev": "bun --hot src/index.ts",
|
|
60
|
+
"start": "bun src/index.ts",
|
|
61
|
+
"format": "prettier --check . --ignore-path ../../.gitignore",
|
|
62
|
+
"lint": "eslint",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"typecheck": "tsc --noEmit"
|
|
66
65
|
}
|
|
67
|
-
}
|
|
66
|
+
}
|