claudemesh-cli 1.25.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entrypoints/cli.js +1751 -1172
- package/dist/entrypoints/cli.js.map +16 -16
- package/dist/entrypoints/mcp.js +2 -2
- package/dist/entrypoints/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/entrypoints/cli.js
CHANGED
|
@@ -103,7 +103,7 @@ __export(exports_urls, {
|
|
|
103
103
|
VERSION: () => VERSION,
|
|
104
104
|
URLS: () => URLS
|
|
105
105
|
});
|
|
106
|
-
var URLS, VERSION = "1.
|
|
106
|
+
var URLS, VERSION = "1.27.0", env;
|
|
107
107
|
var init_urls = __esm(() => {
|
|
108
108
|
URLS = {
|
|
109
109
|
BROKER: process.env.CLAUDEMESH_BROKER_URL ?? "wss://ic.claudemesh.com/ws",
|
|
@@ -6524,1226 +6524,1369 @@ var init_connect = __esm(() => {
|
|
|
6524
6524
|
init_facade();
|
|
6525
6525
|
});
|
|
6526
6526
|
|
|
6527
|
-
// src/commands/
|
|
6528
|
-
var
|
|
6529
|
-
__export(
|
|
6530
|
-
|
|
6531
|
-
runDisconnect: () => runDisconnect
|
|
6527
|
+
// src/commands/info.ts
|
|
6528
|
+
var exports_info = {};
|
|
6529
|
+
__export(exports_info, {
|
|
6530
|
+
runInfo: () => runInfo
|
|
6532
6531
|
});
|
|
6533
|
-
function
|
|
6534
|
-
const m = input.match(/^(\d+)(s|m|h)$/);
|
|
6535
|
-
if (!m)
|
|
6536
|
-
return null;
|
|
6537
|
-
const val = parseInt(m[1], 10);
|
|
6538
|
-
const unit = m[2];
|
|
6539
|
-
if (unit === "s")
|
|
6540
|
-
return val * 1000;
|
|
6541
|
-
if (unit === "m")
|
|
6542
|
-
return val * 60000;
|
|
6543
|
-
if (unit === "h")
|
|
6544
|
-
return val * 3600000;
|
|
6545
|
-
return null;
|
|
6546
|
-
}
|
|
6547
|
-
function buildPayload(kind, target, opts) {
|
|
6548
|
-
if (opts.all)
|
|
6549
|
-
return { type: kind, all: true };
|
|
6550
|
-
if (opts.stale) {
|
|
6551
|
-
const ms = parseStaleMs(opts.stale);
|
|
6552
|
-
if (!ms)
|
|
6553
|
-
return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
|
|
6554
|
-
return { type: kind, stale: ms };
|
|
6555
|
-
}
|
|
6556
|
-
if (target)
|
|
6557
|
-
return { type: kind, target };
|
|
6558
|
-
return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
|
|
6559
|
-
}
|
|
6560
|
-
async function runDisconnect(target, opts = {}) {
|
|
6532
|
+
async function runInfo(flags) {
|
|
6561
6533
|
const config = readConfig();
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6534
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
|
|
6535
|
+
const [brokerInfo, peers, state] = await Promise.all([
|
|
6536
|
+
client.meshInfo(),
|
|
6537
|
+
client.listPeers(),
|
|
6538
|
+
client.listState()
|
|
6539
|
+
]);
|
|
6540
|
+
const output = {
|
|
6541
|
+
slug: mesh.slug,
|
|
6542
|
+
meshId: mesh.meshId,
|
|
6543
|
+
memberId: mesh.memberId,
|
|
6544
|
+
brokerUrl: mesh.brokerUrl,
|
|
6545
|
+
displayName: config.displayName ?? null,
|
|
6546
|
+
peerCount: peers.length,
|
|
6547
|
+
stateCount: state.length,
|
|
6548
|
+
...brokerInfo ?? {}
|
|
6549
|
+
};
|
|
6550
|
+
if (flags.json) {
|
|
6551
|
+
process.stdout.write(JSON.stringify(output, null, 2) + `
|
|
6552
|
+
`);
|
|
6553
|
+
return;
|
|
6580
6554
|
}
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
const result = await client.sendAndWait(built);
|
|
6598
|
-
const peers = result?.affected ?? result?.kicked ?? [];
|
|
6599
|
-
if (peers.length === 0)
|
|
6600
|
-
render.info("No peers matched.");
|
|
6601
|
-
else {
|
|
6602
|
-
render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
6603
|
-
render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
|
|
6555
|
+
render.section(`${mesh.slug} · ${mesh.brokerUrl}`);
|
|
6556
|
+
render.kv([
|
|
6557
|
+
["mesh", mesh.meshId],
|
|
6558
|
+
["member", mesh.memberId],
|
|
6559
|
+
["peers", `${peers.length} connected`],
|
|
6560
|
+
["state", `${state.length} keys`]
|
|
6561
|
+
]);
|
|
6562
|
+
if (brokerInfo && typeof brokerInfo === "object") {
|
|
6563
|
+
const extras = [];
|
|
6564
|
+
for (const [k, v] of Object.entries(brokerInfo)) {
|
|
6565
|
+
if (["slug", "meshId", "brokerUrl"].includes(k))
|
|
6566
|
+
continue;
|
|
6567
|
+
extras.push([k, JSON.stringify(v)]);
|
|
6568
|
+
}
|
|
6569
|
+
if (extras.length)
|
|
6570
|
+
render.kv(extras);
|
|
6604
6571
|
}
|
|
6605
|
-
return EXIT.SUCCESS;
|
|
6606
6572
|
});
|
|
6607
6573
|
}
|
|
6608
|
-
var
|
|
6574
|
+
var init_info2 = __esm(() => {
|
|
6609
6575
|
init_connect();
|
|
6610
6576
|
init_facade();
|
|
6611
6577
|
init_render();
|
|
6612
|
-
init_exit_codes();
|
|
6613
6578
|
});
|
|
6614
6579
|
|
|
6615
|
-
// src/
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
}
|
|
6627
|
-
const config = readConfig();
|
|
6628
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
6629
|
-
if (!meshSlug) {
|
|
6630
|
-
render.err("No mesh joined.");
|
|
6631
|
-
return EXIT.NOT_FOUND;
|
|
6632
|
-
}
|
|
6633
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
6634
|
-
const result = await client.sendAndWait({ type: "ban", target });
|
|
6635
|
-
if (result?.banned) {
|
|
6636
|
-
render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
|
|
6637
|
-
render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
|
|
6638
|
-
} else {
|
|
6639
|
-
render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
|
|
6640
|
-
}
|
|
6641
|
-
return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
6642
|
-
});
|
|
6643
|
-
}
|
|
6644
|
-
async function runUnban(target, opts = {}) {
|
|
6645
|
-
if (!target) {
|
|
6646
|
-
render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
|
|
6647
|
-
return EXIT.INVALID_ARGS;
|
|
6648
|
-
}
|
|
6649
|
-
const config = readConfig();
|
|
6650
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
6651
|
-
if (!meshSlug) {
|
|
6652
|
-
render.err("No mesh joined.");
|
|
6653
|
-
return EXIT.NOT_FOUND;
|
|
6654
|
-
}
|
|
6655
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
6656
|
-
const result = await client.sendAndWait({ type: "unban", target });
|
|
6657
|
-
if (result?.unbanned) {
|
|
6658
|
-
render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
|
|
6659
|
-
} else {
|
|
6660
|
-
render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
|
|
6661
|
-
}
|
|
6662
|
-
return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
6663
|
-
});
|
|
6664
|
-
}
|
|
6665
|
-
async function runBans(opts = {}) {
|
|
6666
|
-
const config = readConfig();
|
|
6667
|
-
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
6668
|
-
if (!meshSlug) {
|
|
6669
|
-
render.err("No mesh joined.");
|
|
6670
|
-
return EXIT.NOT_FOUND;
|
|
6671
|
-
}
|
|
6672
|
-
return await withMesh({ meshSlug }, async (client) => {
|
|
6673
|
-
const result = await client.sendAndWait({ type: "list_bans" });
|
|
6674
|
-
const bans = result?.bans ?? [];
|
|
6675
|
-
if (opts.json) {
|
|
6676
|
-
process.stdout.write(JSON.stringify(bans, null, 2) + `
|
|
6677
|
-
`);
|
|
6678
|
-
return EXIT.SUCCESS;
|
|
6679
|
-
}
|
|
6680
|
-
if (bans.length === 0) {
|
|
6681
|
-
render.info("No banned members.");
|
|
6682
|
-
return EXIT.SUCCESS;
|
|
6580
|
+
// src/services/api/with-rest-key.ts
|
|
6581
|
+
async function withRestKey(opts, fn) {
|
|
6582
|
+
return withMesh({ meshSlug: opts.meshSlug ?? null }, async (client, mesh) => {
|
|
6583
|
+
const result = await client.apiKeyCreate({
|
|
6584
|
+
label: `cli-${opts.purpose ?? "rest"}-${process.pid}`,
|
|
6585
|
+
capabilities: opts.capabilities ?? ["read"],
|
|
6586
|
+
topicScopes: opts.topicScopes ?? undefined,
|
|
6587
|
+
expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString()
|
|
6588
|
+
});
|
|
6589
|
+
if (!result || !result.secret) {
|
|
6590
|
+
throw new Error("apikey mint failed — broker did not return a secret");
|
|
6683
6591
|
}
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6592
|
+
try {
|
|
6593
|
+
return await fn({
|
|
6594
|
+
secret: result.secret,
|
|
6595
|
+
meshId: mesh.meshId,
|
|
6596
|
+
meshSlug: mesh.slug,
|
|
6597
|
+
client,
|
|
6598
|
+
mesh
|
|
6599
|
+
});
|
|
6600
|
+
} finally {
|
|
6601
|
+
try {
|
|
6602
|
+
await client.apiKeyRevoke(result.id);
|
|
6603
|
+
} catch {}
|
|
6687
6604
|
}
|
|
6688
|
-
return EXIT.SUCCESS;
|
|
6689
6605
|
});
|
|
6690
6606
|
}
|
|
6691
|
-
var
|
|
6607
|
+
var init_with_rest_key = __esm(() => {
|
|
6692
6608
|
init_connect();
|
|
6693
|
-
init_facade();
|
|
6694
|
-
init_render();
|
|
6695
|
-
init_exit_codes();
|
|
6696
6609
|
});
|
|
6697
6610
|
|
|
6698
|
-
// src/
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6611
|
+
// src/commands/me.ts
|
|
6612
|
+
var exports_me = {};
|
|
6613
|
+
__export(exports_me, {
|
|
6614
|
+
runMeTopics: () => runMeTopics,
|
|
6615
|
+
runMeTasks: () => runMeTasks,
|
|
6616
|
+
runMeState: () => runMeState,
|
|
6617
|
+
runMeSearch: () => runMeSearch,
|
|
6618
|
+
runMeNotifications: () => runMeNotifications,
|
|
6619
|
+
runMeMemory: () => runMeMemory,
|
|
6620
|
+
runMeActivity: () => runMeActivity,
|
|
6621
|
+
runMe: () => runMe
|
|
6622
|
+
});
|
|
6623
|
+
function resolveMeshForMint(explicit) {
|
|
6624
|
+
if (explicit)
|
|
6625
|
+
return explicit;
|
|
6626
|
+
const cfg = readConfig();
|
|
6627
|
+
return cfg.meshes[0]?.slug ?? null;
|
|
6707
6628
|
}
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6629
|
+
async function runMe(flags) {
|
|
6630
|
+
return withRestKey({
|
|
6631
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6632
|
+
purpose: "workspace-overview",
|
|
6633
|
+
capabilities: ["read"]
|
|
6634
|
+
}, async ({ secret }) => {
|
|
6635
|
+
const ws = await request({
|
|
6636
|
+
path: "/api/v1/me/workspace",
|
|
6637
|
+
token: secret
|
|
6638
|
+
});
|
|
6639
|
+
if (flags.json) {
|
|
6640
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6641
|
+
return EXIT.SUCCESS;
|
|
6642
|
+
}
|
|
6643
|
+
render.section(`${clay("workspace")} — ${bold(ws.userId.slice(0, 8))} ${dim(`· ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
|
|
6644
|
+
const totalsLine = [
|
|
6645
|
+
`${green(String(ws.totals.online))}/${ws.totals.peers} online`,
|
|
6646
|
+
`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}`,
|
|
6647
|
+
ws.totals.unreadMentions > 0 ? yellow(`${ws.totals.unreadMentions} unread @you`) : dim("0 unread @you")
|
|
6648
|
+
].join(dim(" · "));
|
|
6649
|
+
process.stdout.write(" " + totalsLine + `
|
|
6650
|
+
|
|
6720
6651
|
`);
|
|
6652
|
+
if (ws.meshes.length === 0) {
|
|
6653
|
+
process.stdout.write(dim(" no meshes joined — run `claudemesh new` or accept an invite\n"));
|
|
6654
|
+
return EXIT.SUCCESS;
|
|
6721
6655
|
}
|
|
6722
|
-
|
|
6723
|
-
|
|
6656
|
+
const slugWidth = Math.max(...ws.meshes.map((m) => m.slug.length), 8);
|
|
6657
|
+
for (const m of ws.meshes) {
|
|
6658
|
+
const slug = cyan(m.slug.padEnd(slugWidth));
|
|
6659
|
+
const peers = `${m.online}/${m.peers}`;
|
|
6660
|
+
const role = dim(m.myRole);
|
|
6661
|
+
const unread = m.unreadMentions > 0 ? " " + yellow(`${m.unreadMentions} @you`) : "";
|
|
6662
|
+
process.stdout.write(` ${slug} ${peers.padStart(5)} online ${dim(String(m.topics).padStart(2) + " topics")} ${role}${unread}
|
|
6663
|
+
`);
|
|
6664
|
+
}
|
|
6665
|
+
return EXIT.SUCCESS;
|
|
6666
|
+
});
|
|
6724
6667
|
}
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
return null;
|
|
6735
|
-
return new Promise((resolve) => {
|
|
6736
|
-
const id = randomUUID2();
|
|
6737
|
-
const req = { id, verb, args };
|
|
6738
|
-
const parser = new LineParser;
|
|
6739
|
-
let settled = false;
|
|
6740
|
-
const finish = (value) => {
|
|
6741
|
-
if (settled)
|
|
6742
|
-
return;
|
|
6743
|
-
settled = true;
|
|
6744
|
-
try {
|
|
6745
|
-
socket.destroy();
|
|
6746
|
-
} catch {}
|
|
6747
|
-
clearTimeout(timer);
|
|
6748
|
-
resolve(value);
|
|
6749
|
-
};
|
|
6750
|
-
const socket = createConnection({ path });
|
|
6751
|
-
const timer = setTimeout(() => {
|
|
6752
|
-
finish(null);
|
|
6753
|
-
}, timeoutMs);
|
|
6754
|
-
socket.on("connect", () => {
|
|
6755
|
-
try {
|
|
6756
|
-
socket.write(frame(req));
|
|
6757
|
-
} catch {
|
|
6758
|
-
finish(null);
|
|
6759
|
-
}
|
|
6760
|
-
});
|
|
6761
|
-
socket.on("data", (chunk) => {
|
|
6762
|
-
const lines = parser.feed(chunk);
|
|
6763
|
-
for (const line of lines) {
|
|
6764
|
-
if (!line.trim())
|
|
6765
|
-
continue;
|
|
6766
|
-
let res;
|
|
6767
|
-
try {
|
|
6768
|
-
res = JSON.parse(line);
|
|
6769
|
-
} catch {
|
|
6770
|
-
continue;
|
|
6771
|
-
}
|
|
6772
|
-
if (res.id !== id)
|
|
6773
|
-
continue;
|
|
6774
|
-
if (res.ok)
|
|
6775
|
-
finish({ ok: true, result: res.result });
|
|
6776
|
-
else
|
|
6777
|
-
finish({ ok: false, error: res.error });
|
|
6778
|
-
return;
|
|
6779
|
-
}
|
|
6780
|
-
});
|
|
6781
|
-
socket.on("error", (err) => {
|
|
6782
|
-
const code = err.code;
|
|
6783
|
-
if (code === "ECONNREFUSED" || code === "ENOENT" || code === "EPERM") {
|
|
6784
|
-
finish(null);
|
|
6785
|
-
} else {
|
|
6786
|
-
finish(null);
|
|
6787
|
-
}
|
|
6788
|
-
});
|
|
6789
|
-
socket.on("close", () => {
|
|
6790
|
-
finish(null);
|
|
6668
|
+
async function runMeTopics(flags) {
|
|
6669
|
+
return withRestKey({
|
|
6670
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6671
|
+
purpose: "workspace-topics",
|
|
6672
|
+
capabilities: ["read"]
|
|
6673
|
+
}, async ({ secret }) => {
|
|
6674
|
+
const ws = await request({
|
|
6675
|
+
path: "/api/v1/me/topics",
|
|
6676
|
+
token: secret
|
|
6791
6677
|
});
|
|
6678
|
+
const visible = flags.unread ? ws.topics.filter((t) => t.unread > 0) : ws.topics;
|
|
6679
|
+
if (flags.json) {
|
|
6680
|
+
console.log(JSON.stringify({ topics: visible, totals: ws.totals }, null, 2));
|
|
6681
|
+
return EXIT.SUCCESS;
|
|
6682
|
+
}
|
|
6683
|
+
render.section(`${clay("topics")} — ${ws.totals.topics} across all meshes ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· all read")}`);
|
|
6684
|
+
if (visible.length === 0) {
|
|
6685
|
+
process.stdout.write(dim(flags.unread ? ` no unread topics
|
|
6686
|
+
` : " no topics — run `claudemesh topic create #general`\n"));
|
|
6687
|
+
return EXIT.SUCCESS;
|
|
6688
|
+
}
|
|
6689
|
+
const slugWidth = Math.max(...visible.map((t) => t.meshSlug.length), 6);
|
|
6690
|
+
const nameWidth = Math.max(...visible.map((t) => t.name.length), 8);
|
|
6691
|
+
for (const t of visible) {
|
|
6692
|
+
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
6693
|
+
const name = cyan(t.name.padEnd(nameWidth));
|
|
6694
|
+
const unread = t.unread > 0 ? yellow(`${t.unread} unread`.padStart(10)) : dim("·".padStart(10));
|
|
6695
|
+
const last = t.lastMessageAt ? dim(formatRelativeTime(t.lastMessageAt)) : dim("never");
|
|
6696
|
+
process.stdout.write(` ${slug} ${name} ${unread} ${last}
|
|
6697
|
+
`);
|
|
6698
|
+
}
|
|
6699
|
+
return EXIT.SUCCESS;
|
|
6792
6700
|
});
|
|
6793
6701
|
}
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6702
|
+
async function runMeNotifications(flags) {
|
|
6703
|
+
return withRestKey({
|
|
6704
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6705
|
+
purpose: "workspace-notifications",
|
|
6706
|
+
capabilities: ["read"]
|
|
6707
|
+
}, async ({ secret }) => {
|
|
6708
|
+
const params = new URLSearchParams;
|
|
6709
|
+
if (flags.all)
|
|
6710
|
+
params.set("include", "all");
|
|
6711
|
+
if (flags.since)
|
|
6712
|
+
params.set("since", flags.since);
|
|
6713
|
+
const path = "/api/v1/me/notifications" + (params.toString() ? `?${params.toString()}` : "");
|
|
6714
|
+
const ws = await request({
|
|
6715
|
+
path,
|
|
6716
|
+
token: secret
|
|
6717
|
+
});
|
|
6718
|
+
if (flags.json) {
|
|
6719
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6720
|
+
return EXIT.SUCCESS;
|
|
6721
|
+
}
|
|
6722
|
+
const headerLabel = flags.all ? "@-mentions (all)" : "@-mentions (unread)";
|
|
6723
|
+
render.section(`${clay(headerLabel)} — ${ws.totals.total} ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· nothing pending")}`);
|
|
6724
|
+
if (ws.notifications.length === 0) {
|
|
6725
|
+
process.stdout.write(dim(flags.all ? ` no @-mentions in window
|
|
6726
|
+
` : ` inbox zero — nothing waiting
|
|
6727
|
+
`));
|
|
6728
|
+
return EXIT.SUCCESS;
|
|
6729
|
+
}
|
|
6730
|
+
const slugWidth = Math.max(...ws.notifications.map((n) => n.meshSlug.length), 6);
|
|
6731
|
+
for (const n of ws.notifications) {
|
|
6732
|
+
const slug = dim(n.meshSlug.padEnd(slugWidth));
|
|
6733
|
+
const topic = cyan(`#${n.topicName}`);
|
|
6734
|
+
const sender = n.senderName ? `from ${n.senderName}` : "from ?";
|
|
6735
|
+
const ago = formatRelativeTime(n.createdAt);
|
|
6736
|
+
const dot = n.read ? dim("·") : yellow("●");
|
|
6737
|
+
const snippet = n.snippet ?? (n.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
6738
|
+
process.stdout.write(` ${dot} ${slug} ${topic} ${dim(sender)} ${dim(ago)}
|
|
6739
|
+
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
6740
|
+
`);
|
|
6741
|
+
}
|
|
6742
|
+
return EXIT.SUCCESS;
|
|
6743
|
+
});
|
|
6809
6744
|
}
|
|
6810
|
-
function
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6745
|
+
async function runMeActivity(flags) {
|
|
6746
|
+
return withRestKey({
|
|
6747
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6748
|
+
purpose: "workspace-activity",
|
|
6749
|
+
capabilities: ["read"]
|
|
6750
|
+
}, async ({ secret }) => {
|
|
6751
|
+
const params = new URLSearchParams;
|
|
6752
|
+
if (flags.since)
|
|
6753
|
+
params.set("since", flags.since);
|
|
6754
|
+
const path = "/api/v1/me/activity" + (params.toString() ? `?${params.toString()}` : "");
|
|
6755
|
+
const ws = await request({
|
|
6756
|
+
path,
|
|
6757
|
+
token: secret
|
|
6758
|
+
});
|
|
6759
|
+
if (flags.json) {
|
|
6760
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6761
|
+
return EXIT.SUCCESS;
|
|
6762
|
+
}
|
|
6763
|
+
render.section(`${clay("activity")} — ${ws.totals.events} ${dim(flags.since ? `since ${flags.since}` : "in the last 24h")}`);
|
|
6764
|
+
if (ws.activity.length === 0) {
|
|
6765
|
+
process.stdout.write(dim(` quiet — no activity in window
|
|
6766
|
+
`));
|
|
6767
|
+
return EXIT.SUCCESS;
|
|
6768
|
+
}
|
|
6769
|
+
const slugWidth = Math.max(...ws.activity.map((a) => a.meshSlug.length), 6);
|
|
6770
|
+
for (const a of ws.activity) {
|
|
6771
|
+
const slug = dim(a.meshSlug.padEnd(slugWidth));
|
|
6772
|
+
const topic = cyan(`#${a.topicName}`);
|
|
6773
|
+
const sender = a.senderName ?? "?";
|
|
6774
|
+
const ago = formatRelativeTime(a.createdAt);
|
|
6775
|
+
const snippet = a.snippet ?? (a.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
6776
|
+
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
6777
|
+
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
6778
|
+
`);
|
|
6779
|
+
}
|
|
6780
|
+
return EXIT.SUCCESS;
|
|
6781
|
+
});
|
|
6819
6782
|
}
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
import { request as httpRequest } from "node:http";
|
|
6826
|
-
async function ipc(opts) {
|
|
6827
|
-
const useTcp = !!opts.preferTcp;
|
|
6828
|
-
const headers = {
|
|
6829
|
-
accept: "application/json",
|
|
6830
|
-
host: "localhost"
|
|
6831
|
-
};
|
|
6832
|
-
let bodyBuf;
|
|
6833
|
-
if (opts.body !== undefined) {
|
|
6834
|
-
bodyBuf = Buffer.from(JSON.stringify(opts.body), "utf8");
|
|
6835
|
-
headers["content-type"] = "application/json";
|
|
6836
|
-
headers["content-length"] = String(bodyBuf.length);
|
|
6837
|
-
}
|
|
6838
|
-
if (useTcp) {
|
|
6839
|
-
const tok = readLocalToken();
|
|
6840
|
-
if (!tok)
|
|
6841
|
-
throw new IpcError(0, null, "daemon local token not found; is the daemon running?");
|
|
6842
|
-
headers.authorization = `Bearer ${tok}`;
|
|
6783
|
+
async function runMeSearch(flags) {
|
|
6784
|
+
if (!flags.query || flags.query.length < 2) {
|
|
6785
|
+
process.stderr.write(`Usage: claudemesh me search <query> (min 2 chars)
|
|
6786
|
+
`);
|
|
6787
|
+
return EXIT.INVALID_ARGS;
|
|
6843
6788
|
}
|
|
6844
|
-
return
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
} catch {}
|
|
6854
|
-
resolve({ status: res.statusCode ?? 0, body: parsed });
|
|
6855
|
-
});
|
|
6789
|
+
return withRestKey({
|
|
6790
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6791
|
+
purpose: "workspace-search",
|
|
6792
|
+
capabilities: ["read"]
|
|
6793
|
+
}, async ({ secret }) => {
|
|
6794
|
+
const params = new URLSearchParams({ q: flags.query });
|
|
6795
|
+
const ws = await request({
|
|
6796
|
+
path: `/api/v1/me/search?${params.toString()}`,
|
|
6797
|
+
token: secret
|
|
6856
6798
|
});
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6799
|
+
if (flags.json) {
|
|
6800
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6801
|
+
return EXIT.SUCCESS;
|
|
6802
|
+
}
|
|
6803
|
+
render.section(`${clay("search")} — "${flags.query}" ${dim(`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}, ` + `${ws.totals.messages} message${ws.totals.messages === 1 ? "" : "s"}`)}`);
|
|
6804
|
+
if (ws.topics.length === 0 && ws.messages.length === 0) {
|
|
6805
|
+
process.stdout.write(dim(` no matches
|
|
6806
|
+
`));
|
|
6807
|
+
return EXIT.SUCCESS;
|
|
6808
|
+
}
|
|
6809
|
+
if (ws.topics.length > 0) {
|
|
6810
|
+
process.stdout.write(dim(`
|
|
6811
|
+
topics
|
|
6812
|
+
`));
|
|
6813
|
+
const slugWidth = Math.max(...ws.topics.map((t) => t.meshSlug.length), 6);
|
|
6814
|
+
for (const t of ws.topics) {
|
|
6815
|
+
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
6816
|
+
const name = cyan(`#${t.name}`);
|
|
6817
|
+
const desc = t.description ? dim(` — ${t.description}`) : "";
|
|
6818
|
+
process.stdout.write(` ${slug} ${name}${desc}
|
|
6819
|
+
`);
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
if (ws.messages.length > 0) {
|
|
6823
|
+
process.stdout.write(dim(`
|
|
6824
|
+
messages
|
|
6825
|
+
`));
|
|
6826
|
+
const slugWidth = Math.max(...ws.messages.map((m) => m.meshSlug.length), 6);
|
|
6827
|
+
for (const m of ws.messages) {
|
|
6828
|
+
const slug = dim(m.meshSlug.padEnd(slugWidth));
|
|
6829
|
+
const topic = cyan(`#${m.topicName}`);
|
|
6830
|
+
const sender = m.senderName;
|
|
6831
|
+
const ago = formatRelativeTime(m.createdAt);
|
|
6832
|
+
const snippet = m.snippet ?? (m.bodyVersion === 2 ? dim("[encrypted — open the topic to decrypt]") : dim("[empty]"));
|
|
6833
|
+
const highlighted = m.snippet ? highlightMatch(snippet, flags.query) : snippet;
|
|
6834
|
+
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
6835
|
+
` + ` ${highlighted}
|
|
6836
|
+
`);
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
return EXIT.SUCCESS;
|
|
6862
6840
|
});
|
|
6863
6841
|
}
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
this.payload = payload;
|
|
6875
|
-
}
|
|
6876
|
-
};
|
|
6877
|
-
});
|
|
6878
|
-
|
|
6879
|
-
// src/services/bridge/daemon-route.ts
|
|
6880
|
-
var exports_daemon_route = {};
|
|
6881
|
-
__export(exports_daemon_route, {
|
|
6882
|
-
trySendViaDaemon: () => trySendViaDaemon,
|
|
6883
|
-
tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
|
|
6884
|
-
tryListPeersViaDaemon: () => tryListPeersViaDaemon,
|
|
6885
|
-
tryGetSkillViaDaemon: () => tryGetSkillViaDaemon
|
|
6886
|
-
});
|
|
6887
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
6888
|
-
async function tryListPeersViaDaemon() {
|
|
6889
|
-
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
6890
|
-
return null;
|
|
6891
|
-
try {
|
|
6892
|
-
const res = await ipc({ path: "/v1/peers", timeoutMs: 3000 });
|
|
6893
|
-
if (res.status !== 200)
|
|
6894
|
-
return null;
|
|
6895
|
-
return Array.isArray(res.body.peers) ? res.body.peers : [];
|
|
6896
|
-
} catch (err) {
|
|
6897
|
-
const msg = String(err);
|
|
6898
|
-
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
6899
|
-
return null;
|
|
6900
|
-
return null;
|
|
6901
|
-
}
|
|
6842
|
+
function highlightMatch(text, query) {
|
|
6843
|
+
if (!query)
|
|
6844
|
+
return text;
|
|
6845
|
+
const idx = text.toLowerCase().indexOf(query.toLowerCase());
|
|
6846
|
+
if (idx === -1)
|
|
6847
|
+
return text;
|
|
6848
|
+
const before = text.slice(0, idx);
|
|
6849
|
+
const match = text.slice(idx, idx + query.length);
|
|
6850
|
+
const after = text.slice(idx + query.length);
|
|
6851
|
+
return `${before}${yellow(match)}${after}`;
|
|
6902
6852
|
}
|
|
6903
|
-
async function
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
const
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6853
|
+
async function runMeTasks(flags) {
|
|
6854
|
+
return withRestKey({
|
|
6855
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6856
|
+
purpose: "workspace-tasks",
|
|
6857
|
+
capabilities: ["read"]
|
|
6858
|
+
}, async ({ secret }) => {
|
|
6859
|
+
const params = new URLSearchParams;
|
|
6860
|
+
if (flags.status)
|
|
6861
|
+
params.set("status", flags.status);
|
|
6862
|
+
const path = "/api/v1/me/tasks" + (params.toString() ? `?${params.toString()}` : "");
|
|
6863
|
+
const ws = await request({
|
|
6864
|
+
path,
|
|
6865
|
+
token: secret
|
|
6866
|
+
});
|
|
6867
|
+
if (flags.json) {
|
|
6868
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6869
|
+
return EXIT.SUCCESS;
|
|
6870
|
+
}
|
|
6871
|
+
render.section(`${clay("tasks")} — ${dim(`${ws.totals.open} open · ${ws.totals.claimed} in-flight · ${ws.totals.completed} done`)}`);
|
|
6872
|
+
if (ws.tasks.length === 0) {
|
|
6873
|
+
process.stdout.write(dim(` no tasks in window
|
|
6874
|
+
`));
|
|
6875
|
+
return EXIT.SUCCESS;
|
|
6876
|
+
}
|
|
6877
|
+
const slugWidth = Math.max(...ws.tasks.map((t) => t.meshSlug.length), 6);
|
|
6878
|
+
for (const t of ws.tasks) {
|
|
6879
|
+
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
6880
|
+
const status = t.status === "open" ? yellow("open ") : t.status === "claimed" ? cyan("working ") : green("done ");
|
|
6881
|
+
const prio = t.priority === "urgent" ? yellow("!") : t.priority === "low" ? dim("·") : " ";
|
|
6882
|
+
const claimer = t.claimedByName ? dim(` ← ${t.claimedByName}`) : "";
|
|
6883
|
+
process.stdout.write(` ${slug} ${prio} ${status} ${t.title}${claimer}
|
|
6884
|
+
`);
|
|
6885
|
+
}
|
|
6886
|
+
return EXIT.SUCCESS;
|
|
6887
|
+
});
|
|
6917
6888
|
}
|
|
6918
|
-
async function
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6889
|
+
async function runMeState(flags) {
|
|
6890
|
+
return withRestKey({
|
|
6891
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6892
|
+
purpose: "workspace-state",
|
|
6893
|
+
capabilities: ["read"]
|
|
6894
|
+
}, async ({ secret }) => {
|
|
6895
|
+
const params = new URLSearchParams;
|
|
6896
|
+
if (flags.key)
|
|
6897
|
+
params.set("key", flags.key);
|
|
6898
|
+
const path = "/api/v1/me/state" + (params.toString() ? `?${params.toString()}` : "");
|
|
6899
|
+
const ws = await request({
|
|
6900
|
+
path,
|
|
6901
|
+
token: secret
|
|
6925
6902
|
});
|
|
6926
|
-
if (
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6903
|
+
if (flags.json) {
|
|
6904
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6905
|
+
return EXIT.SUCCESS;
|
|
6906
|
+
}
|
|
6907
|
+
render.section(`${clay("state")} — ${ws.totals.entries} entr${ws.totals.entries === 1 ? "y" : "ies"} ${dim(`across ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
|
|
6908
|
+
if (ws.entries.length === 0) {
|
|
6909
|
+
process.stdout.write(dim(` no state entries
|
|
6910
|
+
`));
|
|
6911
|
+
return EXIT.SUCCESS;
|
|
6912
|
+
}
|
|
6913
|
+
const slugWidth = Math.max(...ws.entries.map((e) => e.meshSlug.length), 6);
|
|
6914
|
+
const keyWidth = Math.max(...ws.entries.map((e) => e.key.length), 8);
|
|
6915
|
+
for (const e of ws.entries) {
|
|
6916
|
+
const slug = dim(e.meshSlug.padEnd(slugWidth));
|
|
6917
|
+
const key = cyan(e.key.padEnd(keyWidth));
|
|
6918
|
+
const valueStr = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
|
|
6919
|
+
const trimmed = valueStr.length > 80 ? valueStr.slice(0, 80) + "…" : valueStr;
|
|
6920
|
+
const ago = dim(formatRelativeTime(e.updatedAt));
|
|
6921
|
+
process.stdout.write(` ${slug} ${key} ${trimmed} ${ago}
|
|
6922
|
+
`);
|
|
6923
|
+
}
|
|
6924
|
+
return EXIT.SUCCESS;
|
|
6925
|
+
});
|
|
6934
6926
|
}
|
|
6935
|
-
async function
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
}
|
|
6927
|
+
async function runMeMemory(flags) {
|
|
6928
|
+
return withRestKey({
|
|
6929
|
+
meshSlug: resolveMeshForMint(flags.mesh),
|
|
6930
|
+
purpose: "workspace-memory",
|
|
6931
|
+
capabilities: ["read"]
|
|
6932
|
+
}, async ({ secret }) => {
|
|
6933
|
+
const params = new URLSearchParams;
|
|
6934
|
+
if (flags.query)
|
|
6935
|
+
params.set("q", flags.query);
|
|
6936
|
+
const path = "/api/v1/me/memory" + (params.toString() ? `?${params.toString()}` : "");
|
|
6937
|
+
const ws = await request({
|
|
6938
|
+
path,
|
|
6939
|
+
token: secret
|
|
6949
6940
|
});
|
|
6950
|
-
if (
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
|
|
6954
|
-
duplicate: res.body.duplicate,
|
|
6955
|
-
status: res.body.status
|
|
6956
|
-
};
|
|
6941
|
+
if (flags.json) {
|
|
6942
|
+
console.log(JSON.stringify(ws, null, 2));
|
|
6943
|
+
return EXIT.SUCCESS;
|
|
6957
6944
|
}
|
|
6958
|
-
|
|
6959
|
-
}
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6945
|
+
const headerLabel = flags.query ? `recall — "${flags.query}"` : "recall — last 30 days";
|
|
6946
|
+
render.section(`${clay(headerLabel)} ${dim(`${ws.totals.entries} match${ws.totals.entries === 1 ? "" : "es"}`)}`);
|
|
6947
|
+
if (ws.memories.length === 0) {
|
|
6948
|
+
process.stdout.write(dim(` no memories
|
|
6949
|
+
`));
|
|
6950
|
+
return EXIT.SUCCESS;
|
|
6951
|
+
}
|
|
6952
|
+
const slugWidth = Math.max(...ws.memories.map((m) => m.meshSlug.length), 6);
|
|
6953
|
+
for (const m of ws.memories) {
|
|
6954
|
+
const slug = dim(m.meshSlug.padEnd(slugWidth));
|
|
6955
|
+
const ago = dim(formatRelativeTime(m.rememberedAt));
|
|
6956
|
+
const tags = m.tags.length > 0 ? " " + dim("[" + m.tags.join(", ") + "]") : "";
|
|
6957
|
+
const content = m.content.length > 240 ? m.content.slice(0, 240) + "…" : m.content;
|
|
6958
|
+
process.stdout.write(` ${slug} ${ago}${tags}
|
|
6959
|
+
${content}
|
|
6960
|
+
`);
|
|
6961
|
+
}
|
|
6962
|
+
return EXIT.SUCCESS;
|
|
6963
|
+
});
|
|
6965
6964
|
}
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6965
|
+
function formatRelativeTime(iso) {
|
|
6966
|
+
const then = new Date(iso).getTime();
|
|
6967
|
+
const now = Date.now();
|
|
6968
|
+
const sec = Math.max(0, Math.floor((now - then) / 1000));
|
|
6969
|
+
if (sec < 60)
|
|
6970
|
+
return `${sec}s ago`;
|
|
6971
|
+
if (sec < 3600)
|
|
6972
|
+
return `${Math.floor(sec / 60)}m ago`;
|
|
6973
|
+
if (sec < 86400)
|
|
6974
|
+
return `${Math.floor(sec / 3600)}h ago`;
|
|
6975
|
+
if (sec < 86400 * 30)
|
|
6976
|
+
return `${Math.floor(sec / 86400)}d ago`;
|
|
6977
|
+
if (sec < 86400 * 365)
|
|
6978
|
+
return `${Math.floor(sec / (86400 * 30))}mo ago`;
|
|
6979
|
+
return `${Math.floor(sec / (86400 * 365))}y ago`;
|
|
6980
|
+
}
|
|
6981
|
+
var init_me = __esm(() => {
|
|
6982
|
+
init_with_rest_key();
|
|
6983
|
+
init_client();
|
|
6984
|
+
init_facade();
|
|
6985
|
+
init_render();
|
|
6986
|
+
init_styles();
|
|
6987
|
+
init_exit_codes();
|
|
6969
6988
|
});
|
|
6970
6989
|
|
|
6971
|
-
// src/commands/
|
|
6972
|
-
var
|
|
6973
|
-
__export(
|
|
6974
|
-
|
|
6990
|
+
// src/commands/kick.ts
|
|
6991
|
+
var exports_kick = {};
|
|
6992
|
+
__export(exports_kick, {
|
|
6993
|
+
runKick: () => runKick,
|
|
6994
|
+
runDisconnect: () => runDisconnect
|
|
6975
6995
|
});
|
|
6976
|
-
function
|
|
6977
|
-
const
|
|
6978
|
-
|
|
6979
|
-
|
|
6980
|
-
|
|
6996
|
+
function parseStaleMs(input) {
|
|
6997
|
+
const m = input.match(/^(\d+)(s|m|h)$/);
|
|
6998
|
+
if (!m)
|
|
6999
|
+
return null;
|
|
7000
|
+
const val = parseInt(m[1], 10);
|
|
7001
|
+
const unit = m[2];
|
|
7002
|
+
if (unit === "s")
|
|
7003
|
+
return val * 1000;
|
|
7004
|
+
if (unit === "m")
|
|
7005
|
+
return val * 60000;
|
|
7006
|
+
if (unit === "h")
|
|
7007
|
+
return val * 3600000;
|
|
7008
|
+
return null;
|
|
7009
|
+
}
|
|
7010
|
+
function buildPayload(kind, target, opts) {
|
|
7011
|
+
if (opts.all)
|
|
7012
|
+
return { type: kind, all: true };
|
|
7013
|
+
if (opts.stale) {
|
|
7014
|
+
const ms = parseStaleMs(opts.stale);
|
|
7015
|
+
if (!ms)
|
|
7016
|
+
return { error: `Invalid stale duration: "${opts.stale}". Use e.g. 30m, 1h, 300s.` };
|
|
7017
|
+
return { type: kind, stale: ms };
|
|
6981
7018
|
}
|
|
6982
|
-
|
|
7019
|
+
if (target)
|
|
7020
|
+
return { type: kind, target };
|
|
7021
|
+
return { error: `Usage: claudemesh ${kind} <peer> | --stale 30m | --all` };
|
|
6983
7022
|
}
|
|
6984
|
-
async function
|
|
7023
|
+
async function runDisconnect(target, opts = {}) {
|
|
6985
7024
|
const config = readConfig();
|
|
6986
|
-
const
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
const dr = await tryListPeersViaDaemon2();
|
|
6991
|
-
if (dr !== null) {
|
|
6992
|
-
return dr.map((p) => annotateSelf(p, selfMemberPubkey, null));
|
|
6993
|
-
}
|
|
6994
|
-
} catch {}
|
|
6995
|
-
const bridged = await tryBridge(slug, "peers");
|
|
6996
|
-
if (bridged && bridged.ok) {
|
|
6997
|
-
const peers = bridged.result;
|
|
6998
|
-
return peers.map((p) => annotateSelf(p, selfMemberPubkey, null));
|
|
7025
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7026
|
+
if (!meshSlug) {
|
|
7027
|
+
render.err("No mesh joined.");
|
|
7028
|
+
return EXIT.NOT_FOUND;
|
|
6999
7029
|
}
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7030
|
+
const built = buildPayload("disconnect", target, opts);
|
|
7031
|
+
if ("error" in built) {
|
|
7032
|
+
render.err(String(built.error));
|
|
7033
|
+
return EXIT.INVALID_ARGS;
|
|
7034
|
+
}
|
|
7035
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7036
|
+
const result = await client.sendAndWait(built);
|
|
7037
|
+
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7038
|
+
if (peers.length === 0)
|
|
7039
|
+
render.info("No peers matched.");
|
|
7040
|
+
else {
|
|
7041
|
+
render.ok(`Disconnected ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7042
|
+
render.hint("They will auto-reconnect within seconds. For a session-ending kick, use `claudemesh kick`.");
|
|
7043
|
+
}
|
|
7044
|
+
return EXIT.SUCCESS;
|
|
7005
7045
|
});
|
|
7006
|
-
return result;
|
|
7007
|
-
}
|
|
7008
|
-
function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
|
|
7009
|
-
const isSelf = !!(selfMemberPubkey && peer.memberPubkey && peer.memberPubkey === selfMemberPubkey);
|
|
7010
|
-
const isThisSession = !!(isSelf && selfSessionPubkey && peer.pubkey === selfSessionPubkey);
|
|
7011
|
-
return { ...peer, isSelf, isThisSession };
|
|
7012
7046
|
}
|
|
7013
|
-
async function
|
|
7047
|
+
async function runKick(target, opts = {}) {
|
|
7014
7048
|
const config = readConfig();
|
|
7015
|
-
const
|
|
7016
|
-
if (
|
|
7017
|
-
render.err("No
|
|
7018
|
-
|
|
7019
|
-
process.exit(1);
|
|
7049
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7050
|
+
if (!meshSlug) {
|
|
7051
|
+
render.err("No mesh joined.");
|
|
7052
|
+
return EXIT.NOT_FOUND;
|
|
7020
7053
|
}
|
|
7021
|
-
const
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
render.
|
|
7033
|
-
|
|
7034
|
-
render.info(dim(" (no peers connected)"));
|
|
7035
|
-
continue;
|
|
7036
|
-
}
|
|
7037
|
-
for (const p of peers) {
|
|
7038
|
-
const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
|
|
7039
|
-
const statusDot = p.status === "working" ? yellow("●") : green("●");
|
|
7040
|
-
const name = bold(p.displayName);
|
|
7041
|
-
const meta = [];
|
|
7042
|
-
if (p.peerType)
|
|
7043
|
-
meta.push(p.peerType);
|
|
7044
|
-
if (p.channel)
|
|
7045
|
-
meta.push(p.channel);
|
|
7046
|
-
if (p.model)
|
|
7047
|
-
meta.push(p.model);
|
|
7048
|
-
const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
|
|
7049
|
-
const summary = p.summary ? dim(` — ${p.summary}`) : "";
|
|
7050
|
-
const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
|
|
7051
|
-
const selfTag = p.isThisSession ? dim(" ") + yellow("(this session)") : p.isSelf ? dim(" ") + yellow("(your other session)") : "";
|
|
7052
|
-
render.info(`${statusDot} ${name}${selfTag}${groups}${metaStr}${pubkeyTag}${summary}`);
|
|
7053
|
-
if (p.cwd)
|
|
7054
|
-
render.info(dim(` cwd: ${p.cwd}`));
|
|
7055
|
-
}
|
|
7056
|
-
} catch (e) {
|
|
7057
|
-
render.err(`${slug}: ${e instanceof Error ? e.message : String(e)}`);
|
|
7054
|
+
const built = buildPayload("kick", target, opts);
|
|
7055
|
+
if ("error" in built) {
|
|
7056
|
+
render.err(String(built.error));
|
|
7057
|
+
return EXIT.INVALID_ARGS;
|
|
7058
|
+
}
|
|
7059
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7060
|
+
const result = await client.sendAndWait(built);
|
|
7061
|
+
const peers = result?.affected ?? result?.kicked ?? [];
|
|
7062
|
+
if (peers.length === 0)
|
|
7063
|
+
render.info("No peers matched.");
|
|
7064
|
+
else {
|
|
7065
|
+
render.ok(`Kicked ${peers.length} peer(s): ${peers.join(", ")}`);
|
|
7066
|
+
render.hint("Their Claude Code session ended. They can rejoin anytime by running `claudemesh`.");
|
|
7058
7067
|
}
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
process.stdout.write(JSON.stringify(slugs.length === 1 ? allJson[0]?.peers : allJson, null, 2) + `
|
|
7062
|
-
`);
|
|
7063
|
-
}
|
|
7068
|
+
return EXIT.SUCCESS;
|
|
7069
|
+
});
|
|
7064
7070
|
}
|
|
7065
|
-
var
|
|
7066
|
-
var init_peers = __esm(() => {
|
|
7071
|
+
var init_kick = __esm(() => {
|
|
7067
7072
|
init_connect();
|
|
7068
7073
|
init_facade();
|
|
7069
|
-
init_client3();
|
|
7070
7074
|
init_render();
|
|
7071
|
-
|
|
7072
|
-
FIELD_ALIAS = {
|
|
7073
|
-
name: "displayName"
|
|
7074
|
-
};
|
|
7075
|
+
init_exit_codes();
|
|
7075
7076
|
});
|
|
7076
7077
|
|
|
7077
|
-
// src/commands/
|
|
7078
|
-
var
|
|
7079
|
-
__export(
|
|
7080
|
-
|
|
7078
|
+
// src/commands/ban.ts
|
|
7079
|
+
var exports_ban = {};
|
|
7080
|
+
__export(exports_ban, {
|
|
7081
|
+
runUnban: () => runUnban,
|
|
7082
|
+
runBans: () => runBans,
|
|
7083
|
+
runBan: () => runBan
|
|
7081
7084
|
});
|
|
7082
|
-
async function
|
|
7083
|
-
if (!
|
|
7084
|
-
render.err("Usage: claudemesh
|
|
7085
|
-
|
|
7085
|
+
async function runBan(target, opts = {}) {
|
|
7086
|
+
if (!target) {
|
|
7087
|
+
render.err("Usage: claudemesh ban <peer-name-or-pubkey>");
|
|
7088
|
+
return EXIT.INVALID_ARGS;
|
|
7086
7089
|
}
|
|
7087
|
-
const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
|
|
7088
7090
|
const config = readConfig();
|
|
7089
|
-
const meshSlug =
|
|
7090
|
-
if (!
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7091
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7092
|
+
if (!meshSlug) {
|
|
7093
|
+
render.err("No mesh joined.");
|
|
7094
|
+
return EXIT.NOT_FOUND;
|
|
7095
|
+
}
|
|
7096
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7097
|
+
const result = await client.sendAndWait({ type: "ban", target });
|
|
7098
|
+
if (result?.banned) {
|
|
7099
|
+
render.ok(`Banned ${result.banned} from ${meshSlug}. They cannot reconnect until unbanned.`);
|
|
7100
|
+
render.hint(`Undo: claudemesh unban ${result.banned} --mesh ${meshSlug}`);
|
|
7101
|
+
} else {
|
|
7102
|
+
render.err(result?.message ?? result?.error ?? result?.code ?? "ban failed");
|
|
7096
7103
|
}
|
|
7104
|
+
return result?.banned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7105
|
+
});
|
|
7106
|
+
}
|
|
7107
|
+
async function runUnban(target, opts = {}) {
|
|
7108
|
+
if (!target) {
|
|
7109
|
+
render.err("Usage: claudemesh unban <peer-name-or-pubkey>");
|
|
7110
|
+
return EXIT.INVALID_ARGS;
|
|
7097
7111
|
}
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
}
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
else
|
|
7111
|
-
render.err(`send failed (daemon): ${dr.error}`);
|
|
7112
|
-
process.exit(1);
|
|
7112
|
+
const config = readConfig();
|
|
7113
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7114
|
+
if (!meshSlug) {
|
|
7115
|
+
render.err("No mesh joined.");
|
|
7116
|
+
return EXIT.NOT_FOUND;
|
|
7117
|
+
}
|
|
7118
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7119
|
+
const result = await client.sendAndWait({ type: "unban", target });
|
|
7120
|
+
if (result?.unbanned) {
|
|
7121
|
+
render.ok(`Unbanned ${result.unbanned} from ${meshSlug}. They can rejoin.`);
|
|
7122
|
+
} else {
|
|
7123
|
+
render.err(result?.message ?? result?.error ?? result?.code ?? "unban failed");
|
|
7113
7124
|
}
|
|
7125
|
+
return result?.unbanned ? EXIT.SUCCESS : EXIT.INTERNAL_ERROR;
|
|
7126
|
+
});
|
|
7127
|
+
}
|
|
7128
|
+
async function runBans(opts = {}) {
|
|
7129
|
+
const config = readConfig();
|
|
7130
|
+
const meshSlug = opts.mesh ?? config.meshes[0]?.slug;
|
|
7131
|
+
if (!meshSlug) {
|
|
7132
|
+
render.err("No mesh joined.");
|
|
7133
|
+
return EXIT.NOT_FOUND;
|
|
7114
7134
|
}
|
|
7115
|
-
|
|
7116
|
-
const
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
}
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7135
|
+
return await withMesh({ meshSlug }, async (client) => {
|
|
7136
|
+
const result = await client.sendAndWait({ type: "list_bans" });
|
|
7137
|
+
const bans = result?.bans ?? [];
|
|
7138
|
+
if (opts.json) {
|
|
7139
|
+
process.stdout.write(JSON.stringify(bans, null, 2) + `
|
|
7140
|
+
`);
|
|
7141
|
+
return EXIT.SUCCESS;
|
|
7142
|
+
}
|
|
7143
|
+
if (bans.length === 0) {
|
|
7144
|
+
render.info("No banned members.");
|
|
7145
|
+
return EXIT.SUCCESS;
|
|
7146
|
+
}
|
|
7147
|
+
render.section(`banned members on ${meshSlug}`);
|
|
7148
|
+
for (const b of bans) {
|
|
7149
|
+
render.kv([[b.name, `${b.pubkey.slice(0, 16)}… · banned ${new Date(b.revokedAt).toLocaleDateString()}`]]);
|
|
7150
|
+
}
|
|
7151
|
+
return EXIT.SUCCESS;
|
|
7152
|
+
});
|
|
7153
|
+
}
|
|
7154
|
+
var init_ban = __esm(() => {
|
|
7155
|
+
init_connect();
|
|
7156
|
+
init_facade();
|
|
7157
|
+
init_render();
|
|
7158
|
+
init_exit_codes();
|
|
7159
|
+
});
|
|
7160
|
+
|
|
7161
|
+
// src/services/bridge/protocol.ts
|
|
7162
|
+
import { homedir as homedir5 } from "node:os";
|
|
7163
|
+
import { join as join6 } from "node:path";
|
|
7164
|
+
function socketPath(meshSlug) {
|
|
7165
|
+
return join6(homedir5(), ".claudemesh", "sockets", `${meshSlug}.sock`);
|
|
7166
|
+
}
|
|
7167
|
+
function frame(obj) {
|
|
7168
|
+
return JSON.stringify(obj) + `
|
|
7169
|
+
`;
|
|
7170
|
+
}
|
|
7171
|
+
|
|
7172
|
+
class LineParser {
|
|
7173
|
+
buf = "";
|
|
7174
|
+
feed(chunk) {
|
|
7175
|
+
this.buf += typeof chunk === "string" ? chunk : chunk.toString("utf-8");
|
|
7176
|
+
const lines = [];
|
|
7177
|
+
let nl = this.buf.indexOf(`
|
|
7178
|
+
`);
|
|
7179
|
+
while (nl !== -1) {
|
|
7180
|
+
lines.push(this.buf.slice(0, nl));
|
|
7181
|
+
this.buf = this.buf.slice(nl + 1);
|
|
7182
|
+
nl = this.buf.indexOf(`
|
|
7183
|
+
`);
|
|
7133
7184
|
}
|
|
7185
|
+
return lines;
|
|
7134
7186
|
}
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7187
|
+
}
|
|
7188
|
+
var init_protocol = () => {};
|
|
7189
|
+
|
|
7190
|
+
// src/services/bridge/client.ts
|
|
7191
|
+
import { createConnection } from "node:net";
|
|
7192
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
7193
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
7194
|
+
async function tryBridge(meshSlug, verb, args = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
7195
|
+
const path = socketPath(meshSlug);
|
|
7196
|
+
if (!existsSync6(path))
|
|
7197
|
+
return null;
|
|
7198
|
+
return new Promise((resolve) => {
|
|
7199
|
+
const id = randomUUID2();
|
|
7200
|
+
const req = { id, verb, args };
|
|
7201
|
+
const parser = new LineParser;
|
|
7202
|
+
let settled = false;
|
|
7203
|
+
const finish = (value) => {
|
|
7204
|
+
if (settled)
|
|
7205
|
+
return;
|
|
7206
|
+
settled = true;
|
|
7207
|
+
try {
|
|
7208
|
+
socket.destroy();
|
|
7209
|
+
} catch {}
|
|
7210
|
+
clearTimeout(timer);
|
|
7211
|
+
resolve(value);
|
|
7212
|
+
};
|
|
7213
|
+
const socket = createConnection({ path });
|
|
7214
|
+
const timer = setTimeout(() => {
|
|
7215
|
+
finish(null);
|
|
7216
|
+
}, timeoutMs);
|
|
7217
|
+
socket.on("connect", () => {
|
|
7218
|
+
try {
|
|
7219
|
+
socket.write(frame(req));
|
|
7220
|
+
} catch {
|
|
7221
|
+
finish(null);
|
|
7154
7222
|
}
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7223
|
+
});
|
|
7224
|
+
socket.on("data", (chunk) => {
|
|
7225
|
+
const lines = parser.feed(chunk);
|
|
7226
|
+
for (const line of lines) {
|
|
7227
|
+
if (!line.trim())
|
|
7228
|
+
continue;
|
|
7229
|
+
let res;
|
|
7230
|
+
try {
|
|
7231
|
+
res = JSON.parse(line);
|
|
7232
|
+
} catch {
|
|
7233
|
+
continue;
|
|
7234
|
+
}
|
|
7235
|
+
if (res.id !== id)
|
|
7236
|
+
continue;
|
|
7237
|
+
if (res.ok)
|
|
7238
|
+
finish({ ok: true, result: res.result });
|
|
7239
|
+
else
|
|
7240
|
+
finish({ ok: false, error: res.error });
|
|
7241
|
+
return;
|
|
7163
7242
|
}
|
|
7164
|
-
}
|
|
7165
|
-
|
|
7166
|
-
|
|
7243
|
+
});
|
|
7244
|
+
socket.on("error", (err) => {
|
|
7245
|
+
const code = err.code;
|
|
7246
|
+
if (code === "ECONNREFUSED" || code === "ENOENT" || code === "EPERM") {
|
|
7247
|
+
finish(null);
|
|
7167
7248
|
} else {
|
|
7168
|
-
|
|
7249
|
+
finish(null);
|
|
7169
7250
|
}
|
|
7170
|
-
|
|
7171
|
-
|
|
7251
|
+
});
|
|
7252
|
+
socket.on("close", () => {
|
|
7253
|
+
finish(null);
|
|
7254
|
+
});
|
|
7172
7255
|
});
|
|
7173
7256
|
}
|
|
7174
|
-
var
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
init_client3();
|
|
7178
|
-
init_daemon_route();
|
|
7179
|
-
init_render();
|
|
7180
|
-
init_styles();
|
|
7257
|
+
var DEFAULT_TIMEOUT_MS = 5000;
|
|
7258
|
+
var init_client3 = __esm(() => {
|
|
7259
|
+
init_protocol();
|
|
7181
7260
|
});
|
|
7182
7261
|
|
|
7183
|
-
// src/
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
return ` ${bold(from)} ${dim(`[${kindTag}] ${time}`)}
|
|
7194
|
-
${text}`;
|
|
7262
|
+
// src/daemon/local-token.ts
|
|
7263
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
7264
|
+
import { dirname as dirname3 } from "node:path";
|
|
7265
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
7266
|
+
function readLocalToken() {
|
|
7267
|
+
try {
|
|
7268
|
+
return readFileSync5(DAEMON_PATHS.TOKEN_FILE, "utf8").trim();
|
|
7269
|
+
} catch {
|
|
7270
|
+
return null;
|
|
7271
|
+
}
|
|
7195
7272
|
}
|
|
7196
|
-
|
|
7197
|
-
const
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
}
|
|
7210
|
-
render.section(`inbox — ${mesh.slug} (${messages.length} message${messages.length === 1 ? "" : "s"})`);
|
|
7211
|
-
for (const msg of messages) {
|
|
7212
|
-
process.stdout.write(formatMessage(msg) + `
|
|
7273
|
+
function ensureLocalToken() {
|
|
7274
|
+
const existing = readLocalToken();
|
|
7275
|
+
if (existing)
|
|
7276
|
+
return existing;
|
|
7277
|
+
mkdirSync4(dirname3(DAEMON_PATHS.TOKEN_FILE), { recursive: true, mode: 448 });
|
|
7278
|
+
const tok = randomBytes4(32).toString("base64url");
|
|
7279
|
+
writeFileSync6(DAEMON_PATHS.TOKEN_FILE, tok + `
|
|
7280
|
+
`, { mode: 384 });
|
|
7281
|
+
return tok;
|
|
7282
|
+
}
|
|
7283
|
+
var init_local_token = __esm(() => {
|
|
7284
|
+
init_paths2();
|
|
7285
|
+
});
|
|
7213
7286
|
|
|
7214
|
-
|
|
7215
|
-
|
|
7287
|
+
// src/daemon/ipc/client.ts
|
|
7288
|
+
import { request as httpRequest } from "node:http";
|
|
7289
|
+
async function ipc(opts) {
|
|
7290
|
+
const useTcp = !!opts.preferTcp;
|
|
7291
|
+
const headers = {
|
|
7292
|
+
accept: "application/json",
|
|
7293
|
+
host: "localhost"
|
|
7294
|
+
};
|
|
7295
|
+
let bodyBuf;
|
|
7296
|
+
if (opts.body !== undefined) {
|
|
7297
|
+
bodyBuf = Buffer.from(JSON.stringify(opts.body), "utf8");
|
|
7298
|
+
headers["content-type"] = "application/json";
|
|
7299
|
+
headers["content-length"] = String(bodyBuf.length);
|
|
7300
|
+
}
|
|
7301
|
+
if (useTcp) {
|
|
7302
|
+
const tok = readLocalToken();
|
|
7303
|
+
if (!tok)
|
|
7304
|
+
throw new IpcError(0, null, "daemon local token not found; is the daemon running?");
|
|
7305
|
+
headers.authorization = `Bearer ${tok}`;
|
|
7306
|
+
}
|
|
7307
|
+
return new Promise((resolve, reject) => {
|
|
7308
|
+
const req = httpRequest(useTcp ? { host: DAEMON_TCP_HOST, port: DAEMON_TCP_DEFAULT_PORT, path: opts.path, method: opts.method ?? "GET", headers } : { socketPath: DAEMON_PATHS.SOCK_FILE, path: opts.path, method: opts.method ?? "GET", headers }, (res) => {
|
|
7309
|
+
const chunks = [];
|
|
7310
|
+
res.on("data", (c) => chunks.push(c));
|
|
7311
|
+
res.on("end", () => {
|
|
7312
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
7313
|
+
let parsed = raw;
|
|
7314
|
+
try {
|
|
7315
|
+
parsed = raw.length > 0 ? JSON.parse(raw) : null;
|
|
7316
|
+
} catch {}
|
|
7317
|
+
resolve({ status: res.statusCode ?? 0, body: parsed });
|
|
7318
|
+
});
|
|
7319
|
+
});
|
|
7320
|
+
req.setTimeout(opts.timeoutMs ?? 5000, () => req.destroy(new Error("ipc_timeout")));
|
|
7321
|
+
req.on("error", (err) => reject(err));
|
|
7322
|
+
if (bodyBuf)
|
|
7323
|
+
req.write(bodyBuf);
|
|
7324
|
+
req.end();
|
|
7216
7325
|
});
|
|
7217
7326
|
}
|
|
7218
|
-
var
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7327
|
+
var IpcError;
|
|
7328
|
+
var init_client4 = __esm(() => {
|
|
7329
|
+
init_paths2();
|
|
7330
|
+
init_local_token();
|
|
7331
|
+
IpcError = class IpcError extends Error {
|
|
7332
|
+
status;
|
|
7333
|
+
payload;
|
|
7334
|
+
constructor(status, payload, msg) {
|
|
7335
|
+
super(msg);
|
|
7336
|
+
this.status = status;
|
|
7337
|
+
this.payload = payload;
|
|
7338
|
+
}
|
|
7339
|
+
};
|
|
7222
7340
|
});
|
|
7223
7341
|
|
|
7224
|
-
// src/
|
|
7225
|
-
var
|
|
7226
|
-
__export(
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7342
|
+
// src/services/bridge/daemon-route.ts
|
|
7343
|
+
var exports_daemon_route = {};
|
|
7344
|
+
__export(exports_daemon_route, {
|
|
7345
|
+
trySetStateViaDaemon: () => trySetStateViaDaemon,
|
|
7346
|
+
trySendViaDaemon: () => trySendViaDaemon,
|
|
7347
|
+
tryRememberViaDaemon: () => tryRememberViaDaemon,
|
|
7348
|
+
tryRecallViaDaemon: () => tryRecallViaDaemon,
|
|
7349
|
+
tryListStateViaDaemon: () => tryListStateViaDaemon,
|
|
7350
|
+
tryListSkillsViaDaemon: () => tryListSkillsViaDaemon,
|
|
7351
|
+
tryListPeersViaDaemon: () => tryListPeersViaDaemon,
|
|
7352
|
+
tryGetStateViaDaemon: () => tryGetStateViaDaemon,
|
|
7353
|
+
tryGetSkillViaDaemon: () => tryGetSkillViaDaemon,
|
|
7354
|
+
tryForgetViaDaemon: () => tryForgetViaDaemon
|
|
7230
7355
|
});
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
if (!entry) {
|
|
7235
|
-
render.info(dim("(not set)"));
|
|
7236
|
-
return;
|
|
7237
|
-
}
|
|
7238
|
-
if (flags.json) {
|
|
7239
|
-
console.log(JSON.stringify(entry, null, 2));
|
|
7240
|
-
return;
|
|
7241
|
-
}
|
|
7242
|
-
const val = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
7243
|
-
render.info(val);
|
|
7244
|
-
render.info(dim(` set by ${entry.updatedBy} at ${new Date(entry.updatedAt).toLocaleString()}`));
|
|
7245
|
-
});
|
|
7356
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
7357
|
+
function meshQuery(mesh) {
|
|
7358
|
+
return mesh ? `?mesh=${encodeURIComponent(mesh)}` : "";
|
|
7246
7359
|
}
|
|
7247
|
-
async function
|
|
7248
|
-
|
|
7360
|
+
async function tryListPeersViaDaemon(mesh) {
|
|
7361
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7362
|
+
return null;
|
|
7249
7363
|
try {
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7364
|
+
const res = await ipc({ path: `/v1/peers${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7365
|
+
if (res.status !== 200)
|
|
7366
|
+
return null;
|
|
7367
|
+
return Array.isArray(res.body.peers) ? res.body.peers : [];
|
|
7368
|
+
} catch (err) {
|
|
7369
|
+
const msg = String(err);
|
|
7370
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7371
|
+
return null;
|
|
7372
|
+
return null;
|
|
7253
7373
|
}
|
|
7254
|
-
await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
|
|
7255
|
-
await client.setState(key, parsed);
|
|
7256
|
-
render.ok(`${bold(key)} = ${JSON.stringify(parsed)}`);
|
|
7257
|
-
});
|
|
7258
7374
|
}
|
|
7259
|
-
async function
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
process.stdout.write(` ${bold(e.key)}: ${val}
|
|
7274
|
-
`);
|
|
7275
|
-
process.stdout.write(` ${dim(e.updatedBy + " · " + new Date(e.updatedAt).toLocaleString())}
|
|
7276
|
-
`);
|
|
7277
|
-
}
|
|
7278
|
-
});
|
|
7375
|
+
async function tryListSkillsViaDaemon(mesh) {
|
|
7376
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7377
|
+
return null;
|
|
7378
|
+
try {
|
|
7379
|
+
const res = await ipc({ path: `/v1/skills${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7380
|
+
if (res.status !== 200)
|
|
7381
|
+
return null;
|
|
7382
|
+
return Array.isArray(res.body.skills) ? res.body.skills : [];
|
|
7383
|
+
} catch (err) {
|
|
7384
|
+
const msg = String(err);
|
|
7385
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7386
|
+
return null;
|
|
7387
|
+
return null;
|
|
7388
|
+
}
|
|
7279
7389
|
}
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
async function withRestKey(opts, fn) {
|
|
7288
|
-
return withMesh({ meshSlug: opts.meshSlug ?? null }, async (client, mesh) => {
|
|
7289
|
-
const result = await client.apiKeyCreate({
|
|
7290
|
-
label: `cli-${opts.purpose ?? "rest"}-${process.pid}`,
|
|
7291
|
-
capabilities: opts.capabilities ?? ["read"],
|
|
7292
|
-
topicScopes: opts.topicScopes ?? undefined,
|
|
7293
|
-
expiresAt: new Date(Date.now() + 5 * 60 * 1000).toISOString()
|
|
7390
|
+
async function tryGetSkillViaDaemon(name, mesh) {
|
|
7391
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7392
|
+
return null;
|
|
7393
|
+
try {
|
|
7394
|
+
const res = await ipc({
|
|
7395
|
+
path: `/v1/skills/${encodeURIComponent(name)}${meshQuery(mesh)}`,
|
|
7396
|
+
timeoutMs: 3000
|
|
7294
7397
|
});
|
|
7295
|
-
if (
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7398
|
+
if (res.status === 404)
|
|
7399
|
+
return null;
|
|
7400
|
+
if (res.status !== 200)
|
|
7401
|
+
return null;
|
|
7402
|
+
return res.body.skill ?? null;
|
|
7403
|
+
} catch {
|
|
7404
|
+
return null;
|
|
7405
|
+
}
|
|
7406
|
+
}
|
|
7407
|
+
async function tryGetStateViaDaemon(key, mesh) {
|
|
7408
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7409
|
+
return null;
|
|
7410
|
+
try {
|
|
7411
|
+
const path = `/v1/state?key=${encodeURIComponent(key)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
|
|
7412
|
+
const res = await ipc({ path, timeoutMs: 3000 });
|
|
7413
|
+
if (res.status === 404)
|
|
7414
|
+
return;
|
|
7415
|
+
if (res.status !== 200)
|
|
7416
|
+
return null;
|
|
7417
|
+
return res.body.state ?? undefined;
|
|
7418
|
+
} catch (err) {
|
|
7419
|
+
const msg = String(err);
|
|
7420
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7421
|
+
return null;
|
|
7422
|
+
return null;
|
|
7423
|
+
}
|
|
7312
7424
|
}
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
runMe: () => runMe
|
|
7328
|
-
});
|
|
7329
|
-
function resolveMeshForMint(explicit) {
|
|
7330
|
-
if (explicit)
|
|
7331
|
-
return explicit;
|
|
7332
|
-
const cfg = readConfig();
|
|
7333
|
-
return cfg.meshes[0]?.slug ?? null;
|
|
7425
|
+
async function tryListStateViaDaemon(mesh) {
|
|
7426
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7427
|
+
return null;
|
|
7428
|
+
try {
|
|
7429
|
+
const res = await ipc({ path: `/v1/state${meshQuery(mesh)}`, timeoutMs: 3000 });
|
|
7430
|
+
if (res.status !== 200)
|
|
7431
|
+
return null;
|
|
7432
|
+
return Array.isArray(res.body.entries) ? res.body.entries : [];
|
|
7433
|
+
} catch (err) {
|
|
7434
|
+
const msg = String(err);
|
|
7435
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7436
|
+
return null;
|
|
7437
|
+
return null;
|
|
7438
|
+
}
|
|
7334
7439
|
}
|
|
7335
|
-
async function
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7440
|
+
async function trySetStateViaDaemon(key, value, mesh) {
|
|
7441
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7442
|
+
return false;
|
|
7443
|
+
try {
|
|
7444
|
+
const res = await ipc({
|
|
7445
|
+
method: "POST",
|
|
7446
|
+
path: "/v1/state",
|
|
7447
|
+
timeoutMs: 3000,
|
|
7448
|
+
body: { key, value, ...mesh ? { mesh } : {} }
|
|
7344
7449
|
});
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
render.section(`${clay("workspace")} — ${bold(ws.userId.slice(0, 8))} ${dim(`· ${ws.totals.meshes} mesh${ws.totals.meshes === 1 ? "" : "es"}`)}`);
|
|
7350
|
-
const totalsLine = [
|
|
7351
|
-
`${green(String(ws.totals.online))}/${ws.totals.peers} online`,
|
|
7352
|
-
`${ws.totals.topics} topic${ws.totals.topics === 1 ? "" : "s"}`,
|
|
7353
|
-
ws.totals.unreadMentions > 0 ? yellow(`${ws.totals.unreadMentions} unread @you`) : dim("0 unread @you")
|
|
7354
|
-
].join(dim(" · "));
|
|
7355
|
-
process.stdout.write(" " + totalsLine + `
|
|
7356
|
-
|
|
7357
|
-
`);
|
|
7358
|
-
if (ws.meshes.length === 0) {
|
|
7359
|
-
process.stdout.write(dim(" no meshes joined — run `claudemesh new` or accept an invite\n"));
|
|
7360
|
-
return EXIT.SUCCESS;
|
|
7361
|
-
}
|
|
7362
|
-
const slugWidth = Math.max(...ws.meshes.map((m) => m.slug.length), 8);
|
|
7363
|
-
for (const m of ws.meshes) {
|
|
7364
|
-
const slug = cyan(m.slug.padEnd(slugWidth));
|
|
7365
|
-
const peers = `${m.online}/${m.peers}`;
|
|
7366
|
-
const role = dim(m.myRole);
|
|
7367
|
-
const unread = m.unreadMentions > 0 ? " " + yellow(`${m.unreadMentions} @you`) : "";
|
|
7368
|
-
process.stdout.write(` ${slug} ${peers.padStart(5)} online ${dim(String(m.topics).padStart(2) + " topics")} ${role}${unread}
|
|
7369
|
-
`);
|
|
7370
|
-
}
|
|
7371
|
-
return EXIT.SUCCESS;
|
|
7372
|
-
});
|
|
7450
|
+
return res.status === 200 && res.body.ok === true;
|
|
7451
|
+
} catch {
|
|
7452
|
+
return false;
|
|
7453
|
+
}
|
|
7373
7454
|
}
|
|
7374
|
-
async function
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7455
|
+
async function tryRememberViaDaemon(content, tags, mesh) {
|
|
7456
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7457
|
+
return null;
|
|
7458
|
+
try {
|
|
7459
|
+
const res = await ipc({
|
|
7460
|
+
method: "POST",
|
|
7461
|
+
path: "/v1/memory",
|
|
7462
|
+
timeoutMs: 5000,
|
|
7463
|
+
body: { content, ...tags?.length ? { tags } : {}, ...mesh ? { mesh } : {} }
|
|
7383
7464
|
});
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
if (visible.length === 0) {
|
|
7391
|
-
process.stdout.write(dim(flags.unread ? ` no unread topics
|
|
7392
|
-
` : " no topics — run `claudemesh topic create #general`\n"));
|
|
7393
|
-
return EXIT.SUCCESS;
|
|
7394
|
-
}
|
|
7395
|
-
const slugWidth = Math.max(...visible.map((t) => t.meshSlug.length), 6);
|
|
7396
|
-
const nameWidth = Math.max(...visible.map((t) => t.name.length), 8);
|
|
7397
|
-
for (const t of visible) {
|
|
7398
|
-
const slug = dim(t.meshSlug.padEnd(slugWidth));
|
|
7399
|
-
const name = cyan(t.name.padEnd(nameWidth));
|
|
7400
|
-
const unread = t.unread > 0 ? yellow(`${t.unread} unread`.padStart(10)) : dim("·".padStart(10));
|
|
7401
|
-
const last = t.lastMessageAt ? dim(formatRelativeTime(t.lastMessageAt)) : dim("never");
|
|
7402
|
-
process.stdout.write(` ${slug} ${name} ${unread} ${last}
|
|
7403
|
-
`);
|
|
7404
|
-
}
|
|
7405
|
-
return EXIT.SUCCESS;
|
|
7406
|
-
});
|
|
7465
|
+
if (res.status !== 200 || !res.body.id)
|
|
7466
|
+
return null;
|
|
7467
|
+
return { id: res.body.id, mesh: res.body.mesh };
|
|
7468
|
+
} catch {
|
|
7469
|
+
return null;
|
|
7470
|
+
}
|
|
7407
7471
|
}
|
|
7408
|
-
async function
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
});
|
|
7424
|
-
if (flags.json) {
|
|
7425
|
-
console.log(JSON.stringify(ws, null, 2));
|
|
7426
|
-
return EXIT.SUCCESS;
|
|
7427
|
-
}
|
|
7428
|
-
const headerLabel = flags.all ? "@-mentions (all)" : "@-mentions (unread)";
|
|
7429
|
-
render.section(`${clay(headerLabel)} — ${ws.totals.total} ${dim(ws.totals.unread > 0 ? `· ${ws.totals.unread} unread` : "· nothing pending")}`);
|
|
7430
|
-
if (ws.notifications.length === 0) {
|
|
7431
|
-
process.stdout.write(dim(flags.all ? ` no @-mentions in window
|
|
7432
|
-
` : ` inbox zero — nothing waiting
|
|
7433
|
-
`));
|
|
7434
|
-
return EXIT.SUCCESS;
|
|
7435
|
-
}
|
|
7436
|
-
const slugWidth = Math.max(...ws.notifications.map((n) => n.meshSlug.length), 6);
|
|
7437
|
-
for (const n of ws.notifications) {
|
|
7438
|
-
const slug = dim(n.meshSlug.padEnd(slugWidth));
|
|
7439
|
-
const topic = cyan(`#${n.topicName}`);
|
|
7440
|
-
const sender = n.senderName ? `from ${n.senderName}` : "from ?";
|
|
7441
|
-
const ago = formatRelativeTime(n.createdAt);
|
|
7442
|
-
const dot = n.read ? dim("·") : yellow("●");
|
|
7443
|
-
const snippet = n.snippet ?? (n.ciphertext ? dim("[encrypted]") : dim("[empty]"));
|
|
7444
|
-
process.stdout.write(` ${dot} ${slug} ${topic} ${dim(sender)} ${dim(ago)}
|
|
7445
|
-
` + ` ${snippet.length > 200 ? snippet.slice(0, 200) + "…" : snippet}
|
|
7446
|
-
`);
|
|
7447
|
-
}
|
|
7448
|
-
return EXIT.SUCCESS;
|
|
7449
|
-
});
|
|
7472
|
+
async function tryRecallViaDaemon(query, mesh) {
|
|
7473
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7474
|
+
return null;
|
|
7475
|
+
try {
|
|
7476
|
+
const path = `/v1/memory?q=${encodeURIComponent(query)}${mesh ? `&mesh=${encodeURIComponent(mesh)}` : ""}`;
|
|
7477
|
+
const res = await ipc({ path, timeoutMs: 5000 });
|
|
7478
|
+
if (res.status !== 200)
|
|
7479
|
+
return null;
|
|
7480
|
+
return Array.isArray(res.body.matches) ? res.body.matches : [];
|
|
7481
|
+
} catch (err) {
|
|
7482
|
+
const msg = String(err);
|
|
7483
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7484
|
+
return null;
|
|
7485
|
+
return null;
|
|
7486
|
+
}
|
|
7450
7487
|
}
|
|
7451
|
-
async function
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7488
|
+
async function tryForgetViaDaemon(id, mesh) {
|
|
7489
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7490
|
+
return false;
|
|
7491
|
+
try {
|
|
7492
|
+
const path = `/v1/memory/${encodeURIComponent(id)}${meshQuery(mesh)}`;
|
|
7493
|
+
const res = await ipc({ method: "DELETE", path, timeoutMs: 3000 });
|
|
7494
|
+
return res.status === 200 && res.body.ok === true;
|
|
7495
|
+
} catch {
|
|
7496
|
+
return false;
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
async function trySendViaDaemon(args) {
|
|
7500
|
+
if (!existsSync7(DAEMON_PATHS.SOCK_FILE))
|
|
7501
|
+
return null;
|
|
7502
|
+
try {
|
|
7503
|
+
const res = await ipc({
|
|
7504
|
+
method: "POST",
|
|
7505
|
+
path: "/v1/send",
|
|
7506
|
+
timeoutMs: 3000,
|
|
7507
|
+
body: {
|
|
7508
|
+
to: args.to,
|
|
7509
|
+
message: args.message,
|
|
7510
|
+
priority: args.priority,
|
|
7511
|
+
...args.idempotencyKey ? { client_message_id: args.idempotencyKey } : {},
|
|
7512
|
+
...args.expectedMesh ? { mesh: args.expectedMesh } : {}
|
|
7513
|
+
}
|
|
7464
7514
|
});
|
|
7465
|
-
if (
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
`));
|
|
7473
|
-
return EXIT.SUCCESS;
|
|
7515
|
+
if (res.status === 202 || res.status === 200) {
|
|
7516
|
+
return {
|
|
7517
|
+
ok: true,
|
|
7518
|
+
messageId: res.body.broker_message_id ?? res.body.client_message_id ?? "",
|
|
7519
|
+
duplicate: res.body.duplicate,
|
|
7520
|
+
status: res.body.status
|
|
7521
|
+
};
|
|
7474
7522
|
}
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7523
|
+
return { ok: false, error: res.body.error ?? `daemon http ${res.status}` };
|
|
7524
|
+
} catch (err) {
|
|
7525
|
+
const msg = String(err);
|
|
7526
|
+
if (/ENOENT|ECONNREFUSED|ipc_timeout/.test(msg))
|
|
7527
|
+
return null;
|
|
7528
|
+
return { ok: false, error: msg };
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
var init_daemon_route = __esm(() => {
|
|
7532
|
+
init_client4();
|
|
7533
|
+
init_paths2();
|
|
7534
|
+
});
|
|
7535
|
+
|
|
7536
|
+
// src/commands/peers.ts
|
|
7537
|
+
var exports_peers = {};
|
|
7538
|
+
__export(exports_peers, {
|
|
7539
|
+
runPeers: () => runPeers
|
|
7540
|
+
});
|
|
7541
|
+
function projectFields(record, fields) {
|
|
7542
|
+
const out = {};
|
|
7543
|
+
for (const f of fields) {
|
|
7544
|
+
const sourceKey = FIELD_ALIAS[f] ?? f;
|
|
7545
|
+
out[f] = record[sourceKey];
|
|
7546
|
+
}
|
|
7547
|
+
return out;
|
|
7548
|
+
}
|
|
7549
|
+
async function listPeersForMesh(slug) {
|
|
7550
|
+
const config = readConfig();
|
|
7551
|
+
const joined = config.meshes.find((m) => m.slug === slug);
|
|
7552
|
+
const selfMemberPubkey = joined?.pubkey ?? null;
|
|
7553
|
+
try {
|
|
7554
|
+
const { tryListPeersViaDaemon: tryListPeersViaDaemon2 } = await Promise.resolve().then(() => (init_daemon_route(), exports_daemon_route));
|
|
7555
|
+
const dr = await tryListPeersViaDaemon2();
|
|
7556
|
+
if (dr !== null) {
|
|
7557
|
+
return dr.map((p) => annotateSelf(p, selfMemberPubkey, null));
|
|
7485
7558
|
}
|
|
7486
|
-
|
|
7559
|
+
} catch {}
|
|
7560
|
+
const bridged = await tryBridge(slug, "peers");
|
|
7561
|
+
if (bridged && bridged.ok) {
|
|
7562
|
+
const peers = bridged.result;
|
|
7563
|
+
return peers.map((p) => annotateSelf(p, selfMemberPubkey, null));
|
|
7564
|
+
}
|
|
7565
|
+
let result = [];
|
|
7566
|
+
await withMesh({ meshSlug: slug }, async (client) => {
|
|
7567
|
+
const all = await client.listPeers();
|
|
7568
|
+
const selfSessionPubkey = client.getSessionPubkey();
|
|
7569
|
+
result = all.map((p) => annotateSelf(p, selfMemberPubkey, selfSessionPubkey));
|
|
7487
7570
|
});
|
|
7571
|
+
return result;
|
|
7488
7572
|
}
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7573
|
+
function annotateSelf(peer, selfMemberPubkey, selfSessionPubkey) {
|
|
7574
|
+
const isSelf = !!(selfMemberPubkey && peer.memberPubkey && peer.memberPubkey === selfMemberPubkey);
|
|
7575
|
+
const isThisSession = !!(isSelf && selfSessionPubkey && peer.pubkey === selfSessionPubkey);
|
|
7576
|
+
return { ...peer, isSelf, isThisSession };
|
|
7577
|
+
}
|
|
7578
|
+
async function runPeers(flags) {
|
|
7579
|
+
const config = readConfig();
|
|
7580
|
+
const slugs = flags.mesh ? [flags.mesh] : config.meshes.map((m) => m.slug);
|
|
7581
|
+
if (slugs.length === 0) {
|
|
7582
|
+
render.err("No meshes joined.");
|
|
7583
|
+
render.hint("claudemesh <invite-url> # join + launch");
|
|
7584
|
+
process.exit(1);
|
|
7585
|
+
}
|
|
7586
|
+
const fieldList = typeof flags.json === "string" && flags.json.length > 0 ? flags.json.split(",").map((s) => s.trim()).filter(Boolean) : null;
|
|
7587
|
+
const wantsJson = flags.json !== undefined && flags.json !== false;
|
|
7588
|
+
const allJson = [];
|
|
7589
|
+
for (const slug of slugs) {
|
|
7590
|
+
try {
|
|
7591
|
+
const peers = await listPeersForMesh(slug);
|
|
7592
|
+
if (wantsJson) {
|
|
7593
|
+
const projected = fieldList ? peers.map((p) => projectFields(p, fieldList)) : peers;
|
|
7594
|
+
allJson.push({ mesh: slug, peers: projected });
|
|
7595
|
+
continue;
|
|
7596
|
+
}
|
|
7597
|
+
render.section(`peers on ${slug} (${peers.length})`);
|
|
7598
|
+
if (peers.length === 0) {
|
|
7599
|
+
render.info(dim(" (no peers connected)"));
|
|
7600
|
+
continue;
|
|
7601
|
+
}
|
|
7602
|
+
for (const p of peers) {
|
|
7603
|
+
const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
|
|
7604
|
+
const statusDot = p.status === "working" ? yellow("●") : green("●");
|
|
7605
|
+
const name = bold(p.displayName);
|
|
7606
|
+
const meta = [];
|
|
7607
|
+
if (p.peerType)
|
|
7608
|
+
meta.push(p.peerType);
|
|
7609
|
+
if (p.channel)
|
|
7610
|
+
meta.push(p.channel);
|
|
7611
|
+
if (p.model)
|
|
7612
|
+
meta.push(p.model);
|
|
7613
|
+
const metaStr = meta.length ? dim(` (${meta.join(", ")})`) : "";
|
|
7614
|
+
const summary = p.summary ? dim(` — ${p.summary}`) : "";
|
|
7615
|
+
const pubkeyTag = dim(` · ${p.pubkey.slice(0, 16)}…`);
|
|
7616
|
+
const selfTag = p.isThisSession ? dim(" ") + yellow("(this session)") : p.isSelf ? dim(" ") + yellow("(your other session)") : "";
|
|
7617
|
+
render.info(`${statusDot} ${name}${selfTag}${groups}${metaStr}${pubkeyTag}${summary}`);
|
|
7618
|
+
if (p.cwd)
|
|
7619
|
+
render.info(dim(` cwd: ${p.cwd}`));
|
|
7620
|
+
}
|
|
7621
|
+
} catch (e) {
|
|
7622
|
+
render.err(`${slug}: ${e instanceof Error ? e.message : String(e)}`);
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
if (wantsJson) {
|
|
7626
|
+
process.stdout.write(JSON.stringify(slugs.length === 1 ? allJson[0]?.peers : allJson, null, 2) + `
|
|
7492
7627
|
`);
|
|
7493
|
-
return EXIT.INVALID_ARGS;
|
|
7494
7628
|
}
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7629
|
+
}
|
|
7630
|
+
var FIELD_ALIAS;
|
|
7631
|
+
var init_peers = __esm(() => {
|
|
7632
|
+
init_connect();
|
|
7633
|
+
init_facade();
|
|
7634
|
+
init_client3();
|
|
7635
|
+
init_render();
|
|
7636
|
+
init_styles();
|
|
7637
|
+
FIELD_ALIAS = {
|
|
7638
|
+
name: "displayName"
|
|
7639
|
+
};
|
|
7640
|
+
});
|
|
7641
|
+
|
|
7642
|
+
// src/commands/send.ts
|
|
7643
|
+
var exports_send = {};
|
|
7644
|
+
__export(exports_send, {
|
|
7645
|
+
runSend: () => runSend
|
|
7646
|
+
});
|
|
7647
|
+
async function runSend(flags, to, message) {
|
|
7648
|
+
if (!to || !message) {
|
|
7649
|
+
render.err("Usage: claudemesh send <to> <message>");
|
|
7650
|
+
process.exit(1);
|
|
7651
|
+
}
|
|
7652
|
+
const priority = flags.priority === "now" ? "now" : flags.priority === "low" ? "low" : "next";
|
|
7653
|
+
const config = readConfig();
|
|
7654
|
+
const meshSlug = flags.mesh ?? (config.meshes.length === 1 ? config.meshes[0].slug : null);
|
|
7655
|
+
if (!flags.self && meshSlug) {
|
|
7656
|
+
const joined = config.meshes.find((m) => m.slug === meshSlug);
|
|
7657
|
+
if (joined && /^[0-9a-f]{64}$/i.test(to) && to.toLowerCase() === joined.pubkey.toLowerCase()) {
|
|
7658
|
+
render.err(`Target "${to.slice(0, 16)}…" is your own member pubkey on mesh "${meshSlug}".`);
|
|
7659
|
+
render.hint("Pass --self to message a sibling session of your own member, or pick a different peer's pubkey.");
|
|
7660
|
+
process.exit(1);
|
|
7508
7661
|
}
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7662
|
+
}
|
|
7663
|
+
{
|
|
7664
|
+
const dr = await trySendViaDaemon({ to, message, priority, expectedMesh: meshSlug ?? undefined });
|
|
7665
|
+
if (dr !== null) {
|
|
7666
|
+
if (dr.ok) {
|
|
7667
|
+
if (flags.json)
|
|
7668
|
+
console.log(JSON.stringify({ ok: true, messageId: dr.messageId, target: to, via: "daemon", duplicate: !!dr.duplicate }));
|
|
7669
|
+
else
|
|
7670
|
+
render.ok(`sent to ${to} (daemon)`, dr.messageId ? dim(dr.messageId.slice(0, 8)) : undefined);
|
|
7671
|
+
return;
|
|
7672
|
+
}
|
|
7673
|
+
if (flags.json)
|
|
7674
|
+
console.log(JSON.stringify({ ok: false, error: dr.error, via: "daemon" }));
|
|
7675
|
+
else
|
|
7676
|
+
render.err(`send failed (daemon): ${dr.error}`);
|
|
7677
|
+
process.exit(1);
|
|
7514
7678
|
}
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7679
|
+
}
|
|
7680
|
+
if (meshSlug) {
|
|
7681
|
+
const bridged = await tryBridge(meshSlug, "send", { to, message, priority });
|
|
7682
|
+
if (bridged !== null) {
|
|
7683
|
+
if (bridged.ok) {
|
|
7684
|
+
const r = bridged.result;
|
|
7685
|
+
if (flags.json) {
|
|
7686
|
+
console.log(JSON.stringify({ ok: true, messageId: r.messageId, target: to }));
|
|
7687
|
+
} else {
|
|
7688
|
+
render.ok(`sent to ${to}`, r.messageId ? dim(r.messageId.slice(0, 8)) : undefined);
|
|
7689
|
+
}
|
|
7690
|
+
return;
|
|
7526
7691
|
}
|
|
7692
|
+
if (flags.json) {
|
|
7693
|
+
console.log(JSON.stringify({ ok: false, error: bridged.error }));
|
|
7694
|
+
} else {
|
|
7695
|
+
render.err(`send failed: ${bridged.error}`);
|
|
7696
|
+
}
|
|
7697
|
+
process.exit(1);
|
|
7527
7698
|
}
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
const
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
const
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
const highlighted = m.snippet ? highlightMatch(snippet, flags.query) : snippet;
|
|
7540
|
-
process.stdout.write(` ${slug} ${topic} ${dim(sender + " ·")} ${dim(ago)}
|
|
7541
|
-
` + ` ${highlighted}
|
|
7542
|
-
`);
|
|
7699
|
+
}
|
|
7700
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
|
|
7701
|
+
let targetSpec = to;
|
|
7702
|
+
if (to.startsWith("#") && !/^#[0-9a-z_-]{20,}$/i.test(to)) {
|
|
7703
|
+
const name = to.slice(1);
|
|
7704
|
+
const topics = await client.topicList();
|
|
7705
|
+
const match = topics.find((t) => t.name === name);
|
|
7706
|
+
if (!match) {
|
|
7707
|
+
const names = topics.map((t) => "#" + t.name).join(", ");
|
|
7708
|
+
render.err(`Topic "${to}" not found.`, `topics: ${names || "(none)"}`);
|
|
7709
|
+
process.exit(1);
|
|
7543
7710
|
}
|
|
7711
|
+
targetSpec = "#" + match.id;
|
|
7712
|
+
} else if (!to.startsWith("@") && !to.startsWith("#") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
|
|
7713
|
+
const peers = await client.listPeers();
|
|
7714
|
+
const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
|
|
7715
|
+
if (!match) {
|
|
7716
|
+
const names = peers.map((p) => p.displayName).join(", ");
|
|
7717
|
+
render.err(`Peer "${to}" not found.`, `online: ${names || "(none)"}`);
|
|
7718
|
+
process.exit(1);
|
|
7719
|
+
}
|
|
7720
|
+
targetSpec = match.pubkey;
|
|
7721
|
+
}
|
|
7722
|
+
const result = await client.send(targetSpec, message, priority);
|
|
7723
|
+
if (result.ok) {
|
|
7724
|
+
if (flags.json) {
|
|
7725
|
+
console.log(JSON.stringify({ ok: true, messageId: result.messageId, target: to }));
|
|
7726
|
+
} else {
|
|
7727
|
+
render.ok(`sent to ${to}`, result.messageId ? dim(result.messageId.slice(0, 8)) : undefined);
|
|
7728
|
+
}
|
|
7729
|
+
} else {
|
|
7730
|
+
if (flags.json) {
|
|
7731
|
+
console.log(JSON.stringify({ ok: false, error: result.error ?? "unknown" }));
|
|
7732
|
+
} else {
|
|
7733
|
+
render.err(`send failed: ${result.error ?? "unknown error"}`);
|
|
7734
|
+
}
|
|
7735
|
+
process.exit(1);
|
|
7544
7736
|
}
|
|
7545
|
-
return EXIT.SUCCESS;
|
|
7546
7737
|
});
|
|
7547
7738
|
}
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7739
|
+
var init_send = __esm(() => {
|
|
7740
|
+
init_connect();
|
|
7741
|
+
init_facade();
|
|
7742
|
+
init_client3();
|
|
7743
|
+
init_daemon_route();
|
|
7744
|
+
init_render();
|
|
7745
|
+
init_styles();
|
|
7746
|
+
});
|
|
7747
|
+
|
|
7748
|
+
// src/commands/inbox.ts
|
|
7749
|
+
var exports_inbox = {};
|
|
7750
|
+
__export(exports_inbox, {
|
|
7751
|
+
runInbox: () => runInbox
|
|
7752
|
+
});
|
|
7753
|
+
function formatMessage(msg) {
|
|
7754
|
+
const text = msg.plaintext ?? `[encrypted: ${msg.ciphertext.slice(0, 32)}…]`;
|
|
7755
|
+
const from = msg.senderPubkey.slice(0, 8);
|
|
7756
|
+
const time = new Date(msg.createdAt).toLocaleTimeString();
|
|
7757
|
+
const kindTag = msg.kind === "direct" ? "→ direct" : msg.kind;
|
|
7758
|
+
return ` ${bold(from)} ${dim(`[${kindTag}] ${time}`)}
|
|
7759
|
+
${text}`;
|
|
7558
7760
|
}
|
|
7559
|
-
async function
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
}, async ({ secret }) => {
|
|
7565
|
-
const params = new URLSearchParams;
|
|
7566
|
-
if (flags.status)
|
|
7567
|
-
params.set("status", flags.status);
|
|
7568
|
-
const path = "/api/v1/me/tasks" + (params.toString() ? `?${params.toString()}` : "");
|
|
7569
|
-
const ws = await request({
|
|
7570
|
-
path,
|
|
7571
|
-
token: secret
|
|
7572
|
-
});
|
|
7761
|
+
async function runInbox(flags) {
|
|
7762
|
+
const waitMs = (flags.wait ?? 1) * 1000;
|
|
7763
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
|
|
7764
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
7765
|
+
const messages = client.drainPushBuffer();
|
|
7573
7766
|
if (flags.json) {
|
|
7574
|
-
|
|
7575
|
-
|
|
7767
|
+
process.stdout.write(JSON.stringify(messages, null, 2) + `
|
|
7768
|
+
`);
|
|
7769
|
+
return;
|
|
7576
7770
|
}
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
`));
|
|
7581
|
-
return EXIT.SUCCESS;
|
|
7771
|
+
if (messages.length === 0) {
|
|
7772
|
+
render.info(dim(`No messages on mesh "${mesh.slug}".`));
|
|
7773
|
+
return;
|
|
7582
7774
|
}
|
|
7583
|
-
|
|
7584
|
-
for (const
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
const prio = t.priority === "urgent" ? yellow("!") : t.priority === "low" ? dim("·") : " ";
|
|
7588
|
-
const claimer = t.claimedByName ? dim(` ← ${t.claimedByName}`) : "";
|
|
7589
|
-
process.stdout.write(` ${slug} ${prio} ${status} ${t.title}${claimer}
|
|
7775
|
+
render.section(`inbox — ${mesh.slug} (${messages.length} message${messages.length === 1 ? "" : "s"})`);
|
|
7776
|
+
for (const msg of messages) {
|
|
7777
|
+
process.stdout.write(formatMessage(msg) + `
|
|
7778
|
+
|
|
7590
7779
|
`);
|
|
7591
7780
|
}
|
|
7592
|
-
return EXIT.SUCCESS;
|
|
7593
7781
|
});
|
|
7594
7782
|
}
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7783
|
+
var init_inbox = __esm(() => {
|
|
7784
|
+
init_connect();
|
|
7785
|
+
init_render();
|
|
7786
|
+
init_styles();
|
|
7787
|
+
});
|
|
7788
|
+
|
|
7789
|
+
// src/commands/state.ts
|
|
7790
|
+
var exports_state = {};
|
|
7791
|
+
__export(exports_state, {
|
|
7792
|
+
runStateSet: () => runStateSet,
|
|
7793
|
+
runStateList: () => runStateList,
|
|
7794
|
+
runStateGet: () => runStateGet
|
|
7795
|
+
});
|
|
7796
|
+
async function runStateGet(flags, key) {
|
|
7797
|
+
const daemonEntry = await tryGetStateViaDaemon(key, flags.mesh);
|
|
7798
|
+
if (daemonEntry !== null) {
|
|
7799
|
+
if (!daemonEntry) {
|
|
7800
|
+
render.info(dim("(not set)"));
|
|
7801
|
+
return;
|
|
7802
|
+
}
|
|
7609
7803
|
if (flags.json) {
|
|
7610
|
-
console.log(JSON.stringify(
|
|
7611
|
-
return
|
|
7804
|
+
console.log(JSON.stringify(daemonEntry, null, 2));
|
|
7805
|
+
return;
|
|
7612
7806
|
}
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7807
|
+
const val = typeof daemonEntry.value === "string" ? daemonEntry.value : JSON.stringify(daemonEntry.value);
|
|
7808
|
+
render.info(val);
|
|
7809
|
+
render.info(dim(` set by ${daemonEntry.updatedBy} at ${new Date(daemonEntry.updatedAt).toLocaleString()}`));
|
|
7810
|
+
return;
|
|
7811
|
+
}
|
|
7812
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
|
|
7813
|
+
const entry = await client.getState(key);
|
|
7814
|
+
if (!entry) {
|
|
7815
|
+
render.info(dim("(not set)"));
|
|
7816
|
+
return;
|
|
7618
7817
|
}
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
const slug = dim(e.meshSlug.padEnd(slugWidth));
|
|
7623
|
-
const key = cyan(e.key.padEnd(keyWidth));
|
|
7624
|
-
const valueStr = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
|
|
7625
|
-
const trimmed = valueStr.length > 80 ? valueStr.slice(0, 80) + "…" : valueStr;
|
|
7626
|
-
const ago = dim(formatRelativeTime(e.updatedAt));
|
|
7627
|
-
process.stdout.write(` ${slug} ${key} ${trimmed} ${ago}
|
|
7628
|
-
`);
|
|
7818
|
+
if (flags.json) {
|
|
7819
|
+
console.log(JSON.stringify(entry, null, 2));
|
|
7820
|
+
return;
|
|
7629
7821
|
}
|
|
7630
|
-
|
|
7822
|
+
const val = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
7823
|
+
render.info(val);
|
|
7824
|
+
render.info(dim(` set by ${entry.updatedBy} at ${new Date(entry.updatedAt).toLocaleString()}`));
|
|
7631
7825
|
});
|
|
7632
7826
|
}
|
|
7633
|
-
async function
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7827
|
+
async function runStateSet(flags, key, value) {
|
|
7828
|
+
let parsed;
|
|
7829
|
+
try {
|
|
7830
|
+
parsed = JSON.parse(value);
|
|
7831
|
+
} catch {
|
|
7832
|
+
parsed = value;
|
|
7833
|
+
}
|
|
7834
|
+
const daemonOk = await trySetStateViaDaemon(key, parsed, flags.mesh);
|
|
7835
|
+
if (daemonOk) {
|
|
7836
|
+
render.ok(`${bold(key)} = ${JSON.stringify(parsed)}`);
|
|
7837
|
+
return;
|
|
7838
|
+
}
|
|
7839
|
+
await withMesh({ meshSlug: flags.mesh ?? null }, async (client) => {
|
|
7840
|
+
await client.setState(key, parsed);
|
|
7841
|
+
render.ok(`${bold(key)} = ${JSON.stringify(parsed)}`);
|
|
7842
|
+
});
|
|
7843
|
+
}
|
|
7844
|
+
async function runStateList(flags) {
|
|
7845
|
+
const daemonRows = await tryListStateViaDaemon(flags.mesh);
|
|
7846
|
+
if (daemonRows !== null) {
|
|
7647
7847
|
if (flags.json) {
|
|
7648
|
-
console.log(JSON.stringify(
|
|
7649
|
-
return
|
|
7848
|
+
console.log(JSON.stringify(daemonRows, null, 2));
|
|
7849
|
+
return;
|
|
7650
7850
|
}
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
process.stdout.write(dim(` no memories
|
|
7655
|
-
`));
|
|
7656
|
-
return EXIT.SUCCESS;
|
|
7851
|
+
if (daemonRows.length === 0) {
|
|
7852
|
+
render.info(dim("(no state)"));
|
|
7853
|
+
return;
|
|
7657
7854
|
}
|
|
7658
|
-
|
|
7659
|
-
for (const
|
|
7660
|
-
const
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
process.stdout.write(` ${slug} ${ago}${tags}
|
|
7665
|
-
${content}
|
|
7855
|
+
render.section(`state (${daemonRows.length})`);
|
|
7856
|
+
for (const e of daemonRows) {
|
|
7857
|
+
const val = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
|
|
7858
|
+
process.stdout.write(` ${bold(e.key)}: ${val}
|
|
7859
|
+
`);
|
|
7860
|
+
process.stdout.write(` ${dim(e.updatedBy + " · " + new Date(e.updatedAt).toLocaleString())}
|
|
7666
7861
|
`);
|
|
7667
7862
|
}
|
|
7668
|
-
return
|
|
7669
|
-
}
|
|
7670
|
-
}
|
|
7671
|
-
function formatRelativeTime(iso) {
|
|
7672
|
-
const then = new Date(iso).getTime();
|
|
7673
|
-
const now = Date.now();
|
|
7674
|
-
const sec = Math.max(0, Math.floor((now - then) / 1000));
|
|
7675
|
-
if (sec < 60)
|
|
7676
|
-
return `${sec}s ago`;
|
|
7677
|
-
if (sec < 3600)
|
|
7678
|
-
return `${Math.floor(sec / 60)}m ago`;
|
|
7679
|
-
if (sec < 86400)
|
|
7680
|
-
return `${Math.floor(sec / 3600)}h ago`;
|
|
7681
|
-
if (sec < 86400 * 30)
|
|
7682
|
-
return `${Math.floor(sec / 86400)}d ago`;
|
|
7683
|
-
if (sec < 86400 * 365)
|
|
7684
|
-
return `${Math.floor(sec / (86400 * 30))}mo ago`;
|
|
7685
|
-
return `${Math.floor(sec / (86400 * 365))}y ago`;
|
|
7686
|
-
}
|
|
7687
|
-
var init_me = __esm(() => {
|
|
7688
|
-
init_with_rest_key();
|
|
7689
|
-
init_client();
|
|
7690
|
-
init_facade();
|
|
7691
|
-
init_render();
|
|
7692
|
-
init_styles();
|
|
7693
|
-
init_exit_codes();
|
|
7694
|
-
});
|
|
7695
|
-
|
|
7696
|
-
// src/commands/info.ts
|
|
7697
|
-
var exports_info = {};
|
|
7698
|
-
__export(exports_info, {
|
|
7699
|
-
runInfo: () => runInfo
|
|
7700
|
-
});
|
|
7701
|
-
async function runInfo(flags) {
|
|
7702
|
-
const config = readConfig();
|
|
7863
|
+
return;
|
|
7864
|
+
}
|
|
7703
7865
|
await withMesh({ meshSlug: flags.mesh ?? null }, async (client, mesh) => {
|
|
7704
|
-
const
|
|
7705
|
-
client.meshInfo(),
|
|
7706
|
-
client.listPeers(),
|
|
7707
|
-
client.listState()
|
|
7708
|
-
]);
|
|
7709
|
-
const output = {
|
|
7710
|
-
slug: mesh.slug,
|
|
7711
|
-
meshId: mesh.meshId,
|
|
7712
|
-
memberId: mesh.memberId,
|
|
7713
|
-
brokerUrl: mesh.brokerUrl,
|
|
7714
|
-
displayName: config.displayName ?? null,
|
|
7715
|
-
peerCount: peers.length,
|
|
7716
|
-
stateCount: state.length,
|
|
7717
|
-
...brokerInfo ?? {}
|
|
7718
|
-
};
|
|
7866
|
+
const entries = await client.listState();
|
|
7719
7867
|
if (flags.json) {
|
|
7720
|
-
|
|
7721
|
-
`);
|
|
7868
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
7722
7869
|
return;
|
|
7723
7870
|
}
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
continue;
|
|
7736
|
-
extras.push([k, JSON.stringify(v)]);
|
|
7737
|
-
}
|
|
7738
|
-
if (extras.length)
|
|
7739
|
-
render.kv(extras);
|
|
7871
|
+
if (entries.length === 0) {
|
|
7872
|
+
render.info(dim(`No state on mesh "${mesh.slug}".`));
|
|
7873
|
+
return;
|
|
7874
|
+
}
|
|
7875
|
+
render.section(`state (${entries.length})`);
|
|
7876
|
+
for (const e of entries) {
|
|
7877
|
+
const val = typeof e.value === "string" ? e.value : JSON.stringify(e.value);
|
|
7878
|
+
process.stdout.write(` ${bold(e.key)}: ${val}
|
|
7879
|
+
`);
|
|
7880
|
+
process.stdout.write(` ${dim(e.updatedBy + " · " + new Date(e.updatedAt).toLocaleString())}
|
|
7881
|
+
`);
|
|
7740
7882
|
}
|
|
7741
7883
|
});
|
|
7742
7884
|
}
|
|
7743
|
-
var
|
|
7885
|
+
var init_state = __esm(() => {
|
|
7744
7886
|
init_connect();
|
|
7745
|
-
|
|
7887
|
+
init_daemon_route();
|
|
7746
7888
|
init_render();
|
|
7889
|
+
init_styles();
|
|
7747
7890
|
});
|
|
7748
7891
|
|
|
7749
7892
|
// src/commands/remember.ts
|
|
@@ -7757,6 +7900,15 @@ async function remember(content, opts = {}) {
|
|
|
7757
7900
|
return EXIT.INVALID_ARGS;
|
|
7758
7901
|
}
|
|
7759
7902
|
const tags = opts.tags?.split(",").map((t) => t.trim()).filter(Boolean);
|
|
7903
|
+
const daemonRes = await tryRememberViaDaemon(content, tags, opts.mesh);
|
|
7904
|
+
if (daemonRes) {
|
|
7905
|
+
if (opts.json) {
|
|
7906
|
+
console.log(JSON.stringify({ id: daemonRes.id, content, tags, mesh: daemonRes.mesh }));
|
|
7907
|
+
return EXIT.SUCCESS;
|
|
7908
|
+
}
|
|
7909
|
+
render.ok("remembered", dim(daemonRes.id.slice(0, 8)));
|
|
7910
|
+
return EXIT.SUCCESS;
|
|
7911
|
+
}
|
|
7760
7912
|
return await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
|
|
7761
7913
|
const id = await client.remember(content, tags);
|
|
7762
7914
|
if (opts.json) {
|
|
@@ -7773,6 +7925,7 @@ async function remember(content, opts = {}) {
|
|
|
7773
7925
|
}
|
|
7774
7926
|
var init_remember = __esm(() => {
|
|
7775
7927
|
init_connect();
|
|
7928
|
+
init_daemon_route();
|
|
7776
7929
|
init_render();
|
|
7777
7930
|
init_styles();
|
|
7778
7931
|
init_exit_codes();
|
|
@@ -7788,6 +7941,29 @@ async function recall(query, opts = {}) {
|
|
|
7788
7941
|
render.err("Usage: claudemesh recall <query>");
|
|
7789
7942
|
return EXIT.INVALID_ARGS;
|
|
7790
7943
|
}
|
|
7944
|
+
const daemonMatches = await tryRecallViaDaemon(query, opts.mesh);
|
|
7945
|
+
if (daemonMatches !== null) {
|
|
7946
|
+
if (opts.json) {
|
|
7947
|
+
console.log(JSON.stringify(daemonMatches, null, 2));
|
|
7948
|
+
return EXIT.SUCCESS;
|
|
7949
|
+
}
|
|
7950
|
+
if (daemonMatches.length === 0) {
|
|
7951
|
+
render.info(dim("no memories found."));
|
|
7952
|
+
return EXIT.SUCCESS;
|
|
7953
|
+
}
|
|
7954
|
+
render.section(`memories (${daemonMatches.length})`);
|
|
7955
|
+
for (const m of daemonMatches) {
|
|
7956
|
+
const tags = m.tags.length ? dim(` [${m.tags.map((t) => clay(t)).join(dim(", "))}]`) : "";
|
|
7957
|
+
process.stdout.write(` ${bold(m.id.slice(0, 8))}${tags}
|
|
7958
|
+
`);
|
|
7959
|
+
process.stdout.write(` ${m.content}
|
|
7960
|
+
`);
|
|
7961
|
+
process.stdout.write(` ${dim(m.rememberedBy + " · " + new Date(m.rememberedAt).toLocaleString())}
|
|
7962
|
+
|
|
7963
|
+
`);
|
|
7964
|
+
}
|
|
7965
|
+
return EXIT.SUCCESS;
|
|
7966
|
+
}
|
|
7791
7967
|
return await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
|
|
7792
7968
|
const memories = await client.recall(query);
|
|
7793
7969
|
if (opts.json) {
|
|
@@ -7814,6 +7990,7 @@ async function recall(query, opts = {}) {
|
|
|
7814
7990
|
}
|
|
7815
7991
|
var init_recall = __esm(() => {
|
|
7816
7992
|
init_connect();
|
|
7993
|
+
init_daemon_route();
|
|
7817
7994
|
init_render();
|
|
7818
7995
|
init_styles();
|
|
7819
7996
|
init_exit_codes();
|
|
@@ -8026,6 +8203,14 @@ async function runForget(id, opts) {
|
|
|
8026
8203
|
render.err("Usage: claudemesh forget <memory-id>");
|
|
8027
8204
|
return EXIT.INVALID_ARGS;
|
|
8028
8205
|
}
|
|
8206
|
+
if (await tryForgetViaDaemon(id, opts.mesh)) {
|
|
8207
|
+
if (opts.json) {
|
|
8208
|
+
console.log(JSON.stringify({ id, forgotten: true }));
|
|
8209
|
+
return EXIT.SUCCESS;
|
|
8210
|
+
}
|
|
8211
|
+
render.ok(`forgot ${dim(id.slice(0, 8))}`);
|
|
8212
|
+
return EXIT.SUCCESS;
|
|
8213
|
+
}
|
|
8029
8214
|
await withMesh({ meshSlug: opts.mesh ?? null }, async (client) => {
|
|
8030
8215
|
await client.forget(id);
|
|
8031
8216
|
});
|
|
@@ -8203,6 +8388,7 @@ var init_broker_actions = __esm(() => {
|
|
|
8203
8388
|
init_connect();
|
|
8204
8389
|
init_facade();
|
|
8205
8390
|
init_client3();
|
|
8391
|
+
init_daemon_route();
|
|
8206
8392
|
init_render();
|
|
8207
8393
|
init_styles();
|
|
8208
8394
|
init_exit_codes();
|
|
@@ -8908,10 +9094,9 @@ function startIpcServer(opts) {
|
|
|
8908
9094
|
outboxDb: opts.outboxDb,
|
|
8909
9095
|
inboxDb: opts.inboxDb,
|
|
8910
9096
|
bus: opts.bus,
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
meshSlug: opts.meshSlug
|
|
9097
|
+
brokers: opts.brokers,
|
|
9098
|
+
meshConfigs: opts.meshConfigs,
|
|
9099
|
+
onPendingInserted: opts.onPendingInserted
|
|
8915
9100
|
});
|
|
8916
9101
|
if (existsSync9(DAEMON_PATHS.SOCK_FILE)) {
|
|
8917
9102
|
try {
|
|
@@ -9003,7 +9188,7 @@ function makeHandler(opts) {
|
|
|
9003
9188
|
respond(res, 200, {
|
|
9004
9189
|
daemon_version: VERSION,
|
|
9005
9190
|
ipc_api: "v1",
|
|
9006
|
-
ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills"],
|
|
9191
|
+
ipc_features: ["version", "health", "send", "inbox", "events", "peers", "profile", "skills", "state", "memory"],
|
|
9007
9192
|
schema_version: 1
|
|
9008
9193
|
});
|
|
9009
9194
|
return;
|
|
@@ -9021,34 +9206,209 @@ function makeHandler(opts) {
|
|
|
9021
9206
|
return;
|
|
9022
9207
|
}
|
|
9023
9208
|
if (req.method === "GET" && url.pathname === "/v1/peers") {
|
|
9024
|
-
if (!opts.
|
|
9209
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9210
|
+
respond(res, 503, { error: "broker not initialised" });
|
|
9211
|
+
return;
|
|
9212
|
+
}
|
|
9213
|
+
const filterMesh = url.searchParams.get("mesh") ?? undefined;
|
|
9214
|
+
try {
|
|
9215
|
+
const all = [];
|
|
9216
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9217
|
+
if (filterMesh && filterMesh !== slug)
|
|
9218
|
+
continue;
|
|
9219
|
+
try {
|
|
9220
|
+
const peers = await b.listPeers();
|
|
9221
|
+
for (const p of peers)
|
|
9222
|
+
all.push({ ...p, mesh: slug });
|
|
9223
|
+
} catch (e) {
|
|
9224
|
+
opts.log("warn", "ipc_peers_broker_failed", { mesh: slug, err: String(e) });
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
respond(res, 200, { peers: all });
|
|
9228
|
+
} catch (e) {
|
|
9229
|
+
respond(res, 502, { error: "broker_unreachable", detail: String(e) });
|
|
9230
|
+
}
|
|
9231
|
+
return;
|
|
9232
|
+
}
|
|
9233
|
+
if (req.method === "GET" && url.pathname === "/v1/state") {
|
|
9234
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9235
|
+
respond(res, 503, { error: "broker not initialised" });
|
|
9236
|
+
return;
|
|
9237
|
+
}
|
|
9238
|
+
const filterMesh = url.searchParams.get("mesh") ?? undefined;
|
|
9239
|
+
const key = url.searchParams.get("key");
|
|
9240
|
+
try {
|
|
9241
|
+
if (key) {
|
|
9242
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9243
|
+
if (filterMesh && filterMesh !== slug)
|
|
9244
|
+
continue;
|
|
9245
|
+
const row = await b.getState(key).catch(() => null);
|
|
9246
|
+
if (row) {
|
|
9247
|
+
respond(res, 200, { state: { ...row, mesh: slug } });
|
|
9248
|
+
return;
|
|
9249
|
+
}
|
|
9250
|
+
}
|
|
9251
|
+
respond(res, 404, { error: "state_not_found", key });
|
|
9252
|
+
return;
|
|
9253
|
+
}
|
|
9254
|
+
const all = [];
|
|
9255
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9256
|
+
if (filterMesh && filterMesh !== slug)
|
|
9257
|
+
continue;
|
|
9258
|
+
const rows = await b.listState().catch(() => []);
|
|
9259
|
+
for (const r of rows)
|
|
9260
|
+
all.push({ ...r, mesh: slug });
|
|
9261
|
+
}
|
|
9262
|
+
respond(res, 200, { entries: all });
|
|
9263
|
+
} catch (e) {
|
|
9264
|
+
respond(res, 502, { error: "broker_unreachable", detail: String(e) });
|
|
9265
|
+
}
|
|
9266
|
+
return;
|
|
9267
|
+
}
|
|
9268
|
+
if (req.method === "POST" && url.pathname === "/v1/state") {
|
|
9269
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9270
|
+
respond(res, 503, { error: "broker not initialised" });
|
|
9271
|
+
return;
|
|
9272
|
+
}
|
|
9273
|
+
try {
|
|
9274
|
+
const body = await readJsonBody(req, 256 * 1024);
|
|
9275
|
+
if (!body || typeof body.key !== "string") {
|
|
9276
|
+
respond(res, 400, { error: "missing 'key' (string)" });
|
|
9277
|
+
return;
|
|
9278
|
+
}
|
|
9279
|
+
const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
|
|
9280
|
+
let chosen = requested;
|
|
9281
|
+
if (!chosen && opts.brokers.size === 1)
|
|
9282
|
+
chosen = opts.brokers.keys().next().value;
|
|
9283
|
+
if (!chosen) {
|
|
9284
|
+
respond(res, 400, { error: "mesh_required", attached: [...opts.brokers.keys()] });
|
|
9285
|
+
return;
|
|
9286
|
+
}
|
|
9287
|
+
const broker = opts.brokers.get(chosen);
|
|
9288
|
+
if (!broker) {
|
|
9289
|
+
respond(res, 404, { error: "mesh_not_attached", mesh: chosen });
|
|
9290
|
+
return;
|
|
9291
|
+
}
|
|
9292
|
+
broker.setState(body.key, body.value);
|
|
9293
|
+
respond(res, 200, { ok: true, key: body.key, mesh: chosen });
|
|
9294
|
+
} catch (e) {
|
|
9295
|
+
respond(res, 400, { error: String(e) });
|
|
9296
|
+
}
|
|
9297
|
+
return;
|
|
9298
|
+
}
|
|
9299
|
+
if (req.method === "GET" && url.pathname === "/v1/memory") {
|
|
9300
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9025
9301
|
respond(res, 503, { error: "broker not initialised" });
|
|
9026
9302
|
return;
|
|
9027
9303
|
}
|
|
9304
|
+
const query = url.searchParams.get("q") ?? "";
|
|
9305
|
+
const filterMesh = url.searchParams.get("mesh") ?? undefined;
|
|
9028
9306
|
try {
|
|
9029
|
-
const
|
|
9030
|
-
|
|
9307
|
+
const all = [];
|
|
9308
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9309
|
+
if (filterMesh && filterMesh !== slug)
|
|
9310
|
+
continue;
|
|
9311
|
+
const rows = await b.recall(query).catch(() => []);
|
|
9312
|
+
for (const r of rows)
|
|
9313
|
+
all.push({ ...r, mesh: slug });
|
|
9314
|
+
}
|
|
9315
|
+
respond(res, 200, { matches: all });
|
|
9031
9316
|
} catch (e) {
|
|
9032
9317
|
respond(res, 502, { error: "broker_unreachable", detail: String(e) });
|
|
9033
9318
|
}
|
|
9034
9319
|
return;
|
|
9035
9320
|
}
|
|
9321
|
+
if (req.method === "POST" && url.pathname === "/v1/memory") {
|
|
9322
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9323
|
+
respond(res, 503, { error: "broker not initialised" });
|
|
9324
|
+
return;
|
|
9325
|
+
}
|
|
9326
|
+
try {
|
|
9327
|
+
const body = await readJsonBody(req, 256 * 1024);
|
|
9328
|
+
if (!body || typeof body.content !== "string") {
|
|
9329
|
+
respond(res, 400, { error: "missing 'content' (string)" });
|
|
9330
|
+
return;
|
|
9331
|
+
}
|
|
9332
|
+
const requested = (typeof body.mesh === "string" ? body.mesh : null) || null;
|
|
9333
|
+
let chosen = requested;
|
|
9334
|
+
if (!chosen && opts.brokers.size === 1)
|
|
9335
|
+
chosen = opts.brokers.keys().next().value;
|
|
9336
|
+
if (!chosen) {
|
|
9337
|
+
respond(res, 400, { error: "mesh_required", attached: [...opts.brokers.keys()] });
|
|
9338
|
+
return;
|
|
9339
|
+
}
|
|
9340
|
+
const broker = opts.brokers.get(chosen);
|
|
9341
|
+
if (!broker) {
|
|
9342
|
+
respond(res, 404, { error: "mesh_not_attached", mesh: chosen });
|
|
9343
|
+
return;
|
|
9344
|
+
}
|
|
9345
|
+
const tags = Array.isArray(body.tags) ? body.tags.filter((t) => typeof t === "string") : undefined;
|
|
9346
|
+
const id = await broker.remember(body.content, tags);
|
|
9347
|
+
if (!id) {
|
|
9348
|
+
respond(res, 502, { error: "remember_timeout" });
|
|
9349
|
+
return;
|
|
9350
|
+
}
|
|
9351
|
+
respond(res, 200, { id, mesh: chosen });
|
|
9352
|
+
} catch (e) {
|
|
9353
|
+
respond(res, 400, { error: String(e) });
|
|
9354
|
+
}
|
|
9355
|
+
return;
|
|
9356
|
+
}
|
|
9357
|
+
if (req.method === "DELETE" && url.pathname.startsWith("/v1/memory/")) {
|
|
9358
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9359
|
+
respond(res, 503, { error: "broker not initialised" });
|
|
9360
|
+
return;
|
|
9361
|
+
}
|
|
9362
|
+
const id = decodeURIComponent(url.pathname.slice("/v1/memory/".length));
|
|
9363
|
+
if (!id) {
|
|
9364
|
+
respond(res, 400, { error: "missing memory id" });
|
|
9365
|
+
return;
|
|
9366
|
+
}
|
|
9367
|
+
const requested = url.searchParams.get("mesh");
|
|
9368
|
+
let chosen = requested;
|
|
9369
|
+
if (!chosen && opts.brokers.size === 1)
|
|
9370
|
+
chosen = opts.brokers.keys().next().value;
|
|
9371
|
+
if (!chosen) {
|
|
9372
|
+
respond(res, 400, { error: "mesh_required", attached: [...opts.brokers.keys()] });
|
|
9373
|
+
return;
|
|
9374
|
+
}
|
|
9375
|
+
const broker = opts.brokers.get(chosen);
|
|
9376
|
+
if (!broker) {
|
|
9377
|
+
respond(res, 404, { error: "mesh_not_attached", mesh: chosen });
|
|
9378
|
+
return;
|
|
9379
|
+
}
|
|
9380
|
+
broker.forget(id);
|
|
9381
|
+
respond(res, 200, { ok: true, id, mesh: chosen });
|
|
9382
|
+
return;
|
|
9383
|
+
}
|
|
9036
9384
|
if (req.method === "GET" && url.pathname === "/v1/skills") {
|
|
9037
|
-
if (!opts.
|
|
9385
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9038
9386
|
respond(res, 503, { error: "broker not initialised" });
|
|
9039
9387
|
return;
|
|
9040
9388
|
}
|
|
9041
9389
|
const query = url.searchParams.get("query") ?? undefined;
|
|
9390
|
+
const filterMesh = url.searchParams.get("mesh") ?? undefined;
|
|
9042
9391
|
try {
|
|
9043
|
-
const
|
|
9044
|
-
|
|
9392
|
+
const all = [];
|
|
9393
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9394
|
+
if (filterMesh && filterMesh !== slug)
|
|
9395
|
+
continue;
|
|
9396
|
+
try {
|
|
9397
|
+
const skills = await b.listSkills(query);
|
|
9398
|
+
for (const s of skills)
|
|
9399
|
+
all.push({ ...s, mesh: slug });
|
|
9400
|
+
} catch (e) {
|
|
9401
|
+
opts.log("warn", "ipc_skills_broker_failed", { mesh: slug, err: String(e) });
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
respond(res, 200, { skills: all });
|
|
9045
9405
|
} catch (e) {
|
|
9046
9406
|
respond(res, 502, { error: "broker_unreachable", detail: String(e) });
|
|
9047
9407
|
}
|
|
9048
9408
|
return;
|
|
9049
9409
|
}
|
|
9050
9410
|
if (req.method === "GET" && url.pathname.startsWith("/v1/skills/")) {
|
|
9051
|
-
if (!opts.
|
|
9411
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9052
9412
|
respond(res, 503, { error: "broker not initialised" });
|
|
9053
9413
|
return;
|
|
9054
9414
|
}
|
|
@@ -9057,20 +9417,25 @@ function makeHandler(opts) {
|
|
|
9057
9417
|
respond(res, 400, { error: "missing skill name" });
|
|
9058
9418
|
return;
|
|
9059
9419
|
}
|
|
9420
|
+
const filterMesh = url.searchParams.get("mesh") ?? undefined;
|
|
9060
9421
|
try {
|
|
9061
|
-
const
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9422
|
+
for (const [slug, b] of opts.brokers.entries()) {
|
|
9423
|
+
if (filterMesh && filterMesh !== slug)
|
|
9424
|
+
continue;
|
|
9425
|
+
const skill = await b.getSkill(name).catch(() => null);
|
|
9426
|
+
if (skill) {
|
|
9427
|
+
respond(res, 200, { skill: { ...skill, mesh: slug } });
|
|
9428
|
+
return;
|
|
9429
|
+
}
|
|
9065
9430
|
}
|
|
9066
|
-
respond(res,
|
|
9431
|
+
respond(res, 404, { error: "skill_not_found", name });
|
|
9067
9432
|
} catch (e) {
|
|
9068
9433
|
respond(res, 502, { error: "broker_unreachable", detail: String(e) });
|
|
9069
9434
|
}
|
|
9070
9435
|
return;
|
|
9071
9436
|
}
|
|
9072
9437
|
if (req.method === "POST" && url.pathname === "/v1/profile") {
|
|
9073
|
-
if (!opts.
|
|
9438
|
+
if (!opts.brokers || opts.brokers.size === 0) {
|
|
9074
9439
|
respond(res, 503, { error: "broker not initialised" });
|
|
9075
9440
|
return;
|
|
9076
9441
|
}
|
|
@@ -9080,26 +9445,34 @@ function makeHandler(opts) {
|
|
|
9080
9445
|
respond(res, 400, { error: "expected JSON object" });
|
|
9081
9446
|
return;
|
|
9082
9447
|
}
|
|
9448
|
+
const requested = (typeof body.mesh === "string" ? body.mesh : url.searchParams.get("mesh")) || null;
|
|
9449
|
+
const targets = requested ? [opts.brokers.get(requested)].filter(Boolean) : [...opts.brokers.values()];
|
|
9450
|
+
if (targets.length === 0) {
|
|
9451
|
+
respond(res, 404, { error: "mesh_not_attached", mesh: requested });
|
|
9452
|
+
return;
|
|
9453
|
+
}
|
|
9083
9454
|
const updates = {};
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9455
|
+
for (const b of targets) {
|
|
9456
|
+
if (typeof body.summary === "string")
|
|
9457
|
+
b.setSummary(body.summary);
|
|
9458
|
+
if (body.status === "idle" || body.status === "working" || body.status === "dnd")
|
|
9459
|
+
b.setStatus(body.status);
|
|
9460
|
+
if (typeof body.visible === "boolean")
|
|
9461
|
+
b.setVisible(body.visible);
|
|
9462
|
+
const profile = {};
|
|
9463
|
+
if (typeof body.avatar === "string")
|
|
9464
|
+
profile.avatar = body.avatar;
|
|
9465
|
+
if (typeof body.title === "string")
|
|
9466
|
+
profile.title = body.title;
|
|
9467
|
+
if (typeof body.bio === "string")
|
|
9468
|
+
profile.bio = body.bio;
|
|
9469
|
+
if (Array.isArray(body.capabilities))
|
|
9470
|
+
profile.capabilities = body.capabilities.filter((c) => typeof c === "string");
|
|
9471
|
+
if (Object.keys(profile).length > 0)
|
|
9472
|
+
b.setProfile(profile);
|
|
9473
|
+
}
|
|
9101
9474
|
Object.assign(updates, body);
|
|
9102
|
-
respond(res, 200, { ok: true, applied: Object.keys(updates) });
|
|
9475
|
+
respond(res, 200, { ok: true, applied: Object.keys(updates), meshes: requested ? [requested] : [...opts.brokers.keys()] });
|
|
9103
9476
|
} catch (e) {
|
|
9104
9477
|
respond(res, 400, { error: String(e) });
|
|
9105
9478
|
}
|
|
@@ -9217,9 +9590,27 @@ function makeHandler(opts) {
|
|
|
9217
9590
|
respond(res, 400, { error: parsed.error });
|
|
9218
9591
|
return;
|
|
9219
9592
|
}
|
|
9220
|
-
if (opts.
|
|
9593
|
+
if (opts.brokers && opts.brokers.size > 0 && opts.meshConfigs) {
|
|
9594
|
+
let chosenSlug = parsed.req.mesh ?? null;
|
|
9595
|
+
if (!chosenSlug && opts.brokers.size === 1) {
|
|
9596
|
+
chosenSlug = opts.brokers.keys().next().value;
|
|
9597
|
+
}
|
|
9598
|
+
if (!chosenSlug) {
|
|
9599
|
+
respond(res, 400, {
|
|
9600
|
+
error: "mesh_required",
|
|
9601
|
+
detail: `daemon attached to ${opts.brokers.size} meshes; pass 'mesh' in request body`,
|
|
9602
|
+
attached: [...opts.brokers.keys()]
|
|
9603
|
+
});
|
|
9604
|
+
return;
|
|
9605
|
+
}
|
|
9606
|
+
const broker = opts.brokers.get(chosenSlug);
|
|
9607
|
+
const meshCfg = opts.meshConfigs.get(chosenSlug);
|
|
9608
|
+
if (!broker || !meshCfg) {
|
|
9609
|
+
respond(res, 404, { error: "mesh_not_attached", mesh: chosenSlug });
|
|
9610
|
+
return;
|
|
9611
|
+
}
|
|
9221
9612
|
try {
|
|
9222
|
-
const routed = await resolveAndEncrypt(parsed.req,
|
|
9613
|
+
const routed = await resolveAndEncrypt(parsed.req, broker, meshCfg.secretKey, chosenSlug);
|
|
9223
9614
|
parsed.req.target_spec = routed.target_spec;
|
|
9224
9615
|
parsed.req.ciphertext = routed.ciphertext;
|
|
9225
9616
|
parsed.req.nonce = routed.nonce;
|
|
@@ -9321,6 +9712,7 @@ function parseSendRequest(body, idempotencyHeader) {
|
|
|
9321
9712
|
const headerId = Array.isArray(idempotencyHeader) ? idempotencyHeader[0] : idempotencyHeader;
|
|
9322
9713
|
const client_message_id = typeof b.client_message_id === "string" && b.client_message_id.trim() ? b.client_message_id.trim() : typeof headerId === "string" && headerId.trim() ? headerId.trim() : undefined;
|
|
9323
9714
|
const reply_to_id = typeof b.reply_to_id === "string" ? b.reply_to_id : undefined;
|
|
9715
|
+
const mesh = typeof b.mesh === "string" ? b.mesh.trim() : undefined;
|
|
9324
9716
|
return {
|
|
9325
9717
|
req: {
|
|
9326
9718
|
to,
|
|
@@ -9330,7 +9722,8 @@ function parseSendRequest(body, idempotencyHeader) {
|
|
|
9330
9722
|
reply_to_id,
|
|
9331
9723
|
client_message_id,
|
|
9332
9724
|
destination_kind,
|
|
9333
|
-
destination_ref
|
|
9725
|
+
destination_ref,
|
|
9726
|
+
mesh
|
|
9334
9727
|
}
|
|
9335
9728
|
};
|
|
9336
9729
|
}
|
|
@@ -9405,6 +9798,10 @@ class DaemonBrokerClient {
|
|
|
9405
9798
|
peerListResolvers = new Map;
|
|
9406
9799
|
skillListResolvers = new Map;
|
|
9407
9800
|
skillDataResolvers = new Map;
|
|
9801
|
+
stateGetResolvers = new Map;
|
|
9802
|
+
stateListResolvers = new Map;
|
|
9803
|
+
memoryStoreResolvers = new Map;
|
|
9804
|
+
memoryRecallResolvers = new Map;
|
|
9408
9805
|
sessionPubkey = null;
|
|
9409
9806
|
sessionSecretKey = null;
|
|
9410
9807
|
opens = [];
|
|
@@ -9545,6 +9942,46 @@ class DaemonBrokerClient {
|
|
|
9545
9942
|
}
|
|
9546
9943
|
return;
|
|
9547
9944
|
}
|
|
9945
|
+
if (msg.type === "state_value" || msg.type === "state_data") {
|
|
9946
|
+
const reqId = String(msg._reqId ?? "");
|
|
9947
|
+
const pending = this.stateGetResolvers.get(reqId);
|
|
9948
|
+
if (pending) {
|
|
9949
|
+
this.stateGetResolvers.delete(reqId);
|
|
9950
|
+
clearTimeout(pending.timer);
|
|
9951
|
+
pending.resolve(msg.state ?? msg.row ?? null);
|
|
9952
|
+
}
|
|
9953
|
+
return;
|
|
9954
|
+
}
|
|
9955
|
+
if (msg.type === "state_list") {
|
|
9956
|
+
const reqId = String(msg._reqId ?? "");
|
|
9957
|
+
const pending = this.stateListResolvers.get(reqId);
|
|
9958
|
+
if (pending) {
|
|
9959
|
+
this.stateListResolvers.delete(reqId);
|
|
9960
|
+
clearTimeout(pending.timer);
|
|
9961
|
+
pending.resolve(Array.isArray(msg.entries) ? msg.entries : []);
|
|
9962
|
+
}
|
|
9963
|
+
return;
|
|
9964
|
+
}
|
|
9965
|
+
if (msg.type === "memory_stored") {
|
|
9966
|
+
const reqId = String(msg._reqId ?? "");
|
|
9967
|
+
const pending = this.memoryStoreResolvers.get(reqId);
|
|
9968
|
+
if (pending) {
|
|
9969
|
+
this.memoryStoreResolvers.delete(reqId);
|
|
9970
|
+
clearTimeout(pending.timer);
|
|
9971
|
+
pending.resolve(typeof msg.memoryId === "string" ? msg.memoryId : null);
|
|
9972
|
+
}
|
|
9973
|
+
return;
|
|
9974
|
+
}
|
|
9975
|
+
if (msg.type === "memory_recall_result") {
|
|
9976
|
+
const reqId = String(msg._reqId ?? "");
|
|
9977
|
+
const pending = this.memoryRecallResolvers.get(reqId);
|
|
9978
|
+
if (pending) {
|
|
9979
|
+
this.memoryRecallResolvers.delete(reqId);
|
|
9980
|
+
clearTimeout(pending.timer);
|
|
9981
|
+
pending.resolve(Array.isArray(msg.matches) ? msg.matches : []);
|
|
9982
|
+
}
|
|
9983
|
+
return;
|
|
9984
|
+
}
|
|
9548
9985
|
if (msg.type === "push" || msg.type === "inbound") {
|
|
9549
9986
|
this.opts.onPush?.(msg);
|
|
9550
9987
|
return;
|
|
@@ -9665,6 +10102,96 @@ class DaemonBrokerClient {
|
|
|
9665
10102
|
}
|
|
9666
10103
|
});
|
|
9667
10104
|
}
|
|
10105
|
+
async getState(key, timeoutMs = 5000) {
|
|
10106
|
+
if (this._status !== "open" || !this.ws)
|
|
10107
|
+
return null;
|
|
10108
|
+
return new Promise((resolve) => {
|
|
10109
|
+
const reqId = `sg-${++this.reqCounter}`;
|
|
10110
|
+
const timer = setTimeout(() => {
|
|
10111
|
+
if (this.stateGetResolvers.delete(reqId))
|
|
10112
|
+
resolve(null);
|
|
10113
|
+
}, timeoutMs);
|
|
10114
|
+
this.stateGetResolvers.set(reqId, { resolve, timer });
|
|
10115
|
+
try {
|
|
10116
|
+
this.ws.send(JSON.stringify({ type: "get_state", key, _reqId: reqId }));
|
|
10117
|
+
} catch {
|
|
10118
|
+
this.stateGetResolvers.delete(reqId);
|
|
10119
|
+
clearTimeout(timer);
|
|
10120
|
+
resolve(null);
|
|
10121
|
+
}
|
|
10122
|
+
});
|
|
10123
|
+
}
|
|
10124
|
+
async listState(timeoutMs = 5000) {
|
|
10125
|
+
if (this._status !== "open" || !this.ws)
|
|
10126
|
+
return [];
|
|
10127
|
+
return new Promise((resolve) => {
|
|
10128
|
+
const reqId = `sl-${++this.reqCounter}`;
|
|
10129
|
+
const timer = setTimeout(() => {
|
|
10130
|
+
if (this.stateListResolvers.delete(reqId))
|
|
10131
|
+
resolve([]);
|
|
10132
|
+
}, timeoutMs);
|
|
10133
|
+
this.stateListResolvers.set(reqId, { resolve, timer });
|
|
10134
|
+
try {
|
|
10135
|
+
this.ws.send(JSON.stringify({ type: "list_state", _reqId: reqId }));
|
|
10136
|
+
} catch {
|
|
10137
|
+
this.stateListResolvers.delete(reqId);
|
|
10138
|
+
clearTimeout(timer);
|
|
10139
|
+
resolve([]);
|
|
10140
|
+
}
|
|
10141
|
+
});
|
|
10142
|
+
}
|
|
10143
|
+
setState(key, value) {
|
|
10144
|
+
if (this._status !== "open" || !this.ws)
|
|
10145
|
+
return;
|
|
10146
|
+
try {
|
|
10147
|
+
this.ws.send(JSON.stringify({ type: "set_state", key, value }));
|
|
10148
|
+
} catch {}
|
|
10149
|
+
}
|
|
10150
|
+
async remember(content, tags, timeoutMs = 5000) {
|
|
10151
|
+
if (this._status !== "open" || !this.ws)
|
|
10152
|
+
return null;
|
|
10153
|
+
return new Promise((resolve) => {
|
|
10154
|
+
const reqId = `mr-${++this.reqCounter}`;
|
|
10155
|
+
const timer = setTimeout(() => {
|
|
10156
|
+
if (this.memoryStoreResolvers.delete(reqId))
|
|
10157
|
+
resolve(null);
|
|
10158
|
+
}, timeoutMs);
|
|
10159
|
+
this.memoryStoreResolvers.set(reqId, { resolve, timer });
|
|
10160
|
+
try {
|
|
10161
|
+
this.ws.send(JSON.stringify({ type: "remember", content, tags, _reqId: reqId }));
|
|
10162
|
+
} catch {
|
|
10163
|
+
this.memoryStoreResolvers.delete(reqId);
|
|
10164
|
+
clearTimeout(timer);
|
|
10165
|
+
resolve(null);
|
|
10166
|
+
}
|
|
10167
|
+
});
|
|
10168
|
+
}
|
|
10169
|
+
async recall(query, timeoutMs = 5000) {
|
|
10170
|
+
if (this._status !== "open" || !this.ws)
|
|
10171
|
+
return [];
|
|
10172
|
+
return new Promise((resolve) => {
|
|
10173
|
+
const reqId = `mc-${++this.reqCounter}`;
|
|
10174
|
+
const timer = setTimeout(() => {
|
|
10175
|
+
if (this.memoryRecallResolvers.delete(reqId))
|
|
10176
|
+
resolve([]);
|
|
10177
|
+
}, timeoutMs);
|
|
10178
|
+
this.memoryRecallResolvers.set(reqId, { resolve, timer });
|
|
10179
|
+
try {
|
|
10180
|
+
this.ws.send(JSON.stringify({ type: "recall", query, _reqId: reqId }));
|
|
10181
|
+
} catch {
|
|
10182
|
+
this.memoryRecallResolvers.delete(reqId);
|
|
10183
|
+
clearTimeout(timer);
|
|
10184
|
+
resolve([]);
|
|
10185
|
+
}
|
|
10186
|
+
});
|
|
10187
|
+
}
|
|
10188
|
+
forget(memoryId) {
|
|
10189
|
+
if (this._status !== "open" || !this.ws)
|
|
10190
|
+
return;
|
|
10191
|
+
try {
|
|
10192
|
+
this.ws.send(JSON.stringify({ type: "forget", memoryId }));
|
|
10193
|
+
} catch {}
|
|
10194
|
+
}
|
|
9668
10195
|
setProfile(profile) {
|
|
9669
10196
|
if (this._status !== "open" || !this.ws)
|
|
9670
10197
|
return;
|
|
@@ -9796,6 +10323,17 @@ async function drainOnce(opts, log2) {
|
|
|
9796
10323
|
if (markInflight(opts.db, row.id, now) === 0)
|
|
9797
10324
|
continue;
|
|
9798
10325
|
const fpHex = bufferToHex(row.request_fingerprint);
|
|
10326
|
+
let broker;
|
|
10327
|
+
if (row.mesh) {
|
|
10328
|
+
broker = opts.brokers.get(row.mesh);
|
|
10329
|
+
} else if (opts.brokers.size === 1) {
|
|
10330
|
+
broker = opts.brokers.values().next().value;
|
|
10331
|
+
}
|
|
10332
|
+
if (!broker) {
|
|
10333
|
+
log2("warn", "drain_no_broker_for_mesh", { id: row.id, mesh: row.mesh ?? "(null)" });
|
|
10334
|
+
markDead(opts.db, row.id, `no_broker_for_mesh:${row.mesh ?? "null"}`);
|
|
10335
|
+
continue;
|
|
10336
|
+
}
|
|
9799
10337
|
let targetSpec;
|
|
9800
10338
|
let nonce;
|
|
9801
10339
|
let ciphertext;
|
|
@@ -9813,7 +10351,7 @@ async function drainOnce(opts, log2) {
|
|
|
9813
10351
|
}
|
|
9814
10352
|
let res;
|
|
9815
10353
|
try {
|
|
9816
|
-
res = await
|
|
10354
|
+
res = await broker.send({
|
|
9817
10355
|
targetSpec,
|
|
9818
10356
|
priority,
|
|
9819
10357
|
nonce,
|
|
@@ -10159,10 +10697,10 @@ async function runDaemon(opts = {}) {
|
|
|
10159
10697
|
}
|
|
10160
10698
|
const bus = new EventBus;
|
|
10161
10699
|
const cfg = readConfig();
|
|
10162
|
-
let
|
|
10700
|
+
let meshes;
|
|
10163
10701
|
if (opts.mesh) {
|
|
10164
|
-
|
|
10165
|
-
if (!
|
|
10702
|
+
const found = cfg.meshes.find((m) => m.slug === opts.mesh);
|
|
10703
|
+
if (!found) {
|
|
10166
10704
|
process.stderr.write(`mesh not found: ${opts.mesh}
|
|
10167
10705
|
`);
|
|
10168
10706
|
process.stderr.write(`joined meshes: ${cfg.meshes.map((m) => m.slug).join(", ") || "(none)"}
|
|
@@ -10173,8 +10711,7 @@ async function runDaemon(opts = {}) {
|
|
|
10173
10711
|
} catch {}
|
|
10174
10712
|
return 2;
|
|
10175
10713
|
}
|
|
10176
|
-
|
|
10177
|
-
mesh = cfg.meshes[0];
|
|
10714
|
+
meshes = [found];
|
|
10178
10715
|
} else if (cfg.meshes.length === 0) {
|
|
10179
10716
|
process.stderr.write(`no mesh joined; run \`claudemesh join <invite-url>\` first
|
|
10180
10717
|
`);
|
|
@@ -10184,43 +10721,41 @@ async function runDaemon(opts = {}) {
|
|
|
10184
10721
|
} catch {}
|
|
10185
10722
|
return 2;
|
|
10186
10723
|
} else {
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
status: s,
|
|
10203
|
-
mesh: mesh.slug,
|
|
10204
|
-
ts: new Date().toISOString()
|
|
10205
|
-
}) + `
|
|
10724
|
+
meshes = cfg.meshes;
|
|
10725
|
+
}
|
|
10726
|
+
const brokers = new Map;
|
|
10727
|
+
const meshConfigs = new Map;
|
|
10728
|
+
for (const mesh of meshes) {
|
|
10729
|
+
meshConfigs.set(mesh.slug, mesh);
|
|
10730
|
+
const broker = new DaemonBrokerClient(mesh, {
|
|
10731
|
+
displayName: opts.displayName,
|
|
10732
|
+
onStatusChange: (s) => {
|
|
10733
|
+
process.stdout.write(JSON.stringify({
|
|
10734
|
+
msg: "broker_status",
|
|
10735
|
+
status: s,
|
|
10736
|
+
mesh: mesh.slug,
|
|
10737
|
+
ts: new Date().toISOString()
|
|
10738
|
+
}) + `
|
|
10206
10739
|
`);
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10740
|
+
bus.publish("broker_status", { mesh: mesh.slug, status: s });
|
|
10741
|
+
},
|
|
10742
|
+
onPush: (m) => {
|
|
10743
|
+
const sessionKeys = broker.getSessionKeys();
|
|
10744
|
+
handleBrokerPush(m, {
|
|
10745
|
+
db: inboxDb,
|
|
10746
|
+
bus,
|
|
10747
|
+
meshSlug: mesh.slug,
|
|
10748
|
+
recipientSecretKeyHex: mesh.secretKey,
|
|
10749
|
+
sessionSecretKeyHex: sessionKeys?.sessionSecretKey
|
|
10750
|
+
});
|
|
10751
|
+
}
|
|
10752
|
+
});
|
|
10753
|
+
broker.connect().catch((err) => process.stderr.write(`broker connect failed for ${mesh.slug}: ${String(err)}
|
|
10221
10754
|
`));
|
|
10755
|
+
brokers.set(mesh.slug, broker);
|
|
10756
|
+
}
|
|
10222
10757
|
let drain = null;
|
|
10223
|
-
drain = startDrainWorker({ db: outboxDb,
|
|
10758
|
+
drain = startDrainWorker({ db: outboxDb, brokers });
|
|
10224
10759
|
const ipc2 = startIpcServer({
|
|
10225
10760
|
localToken,
|
|
10226
10761
|
tcpEnabled,
|
|
@@ -10228,10 +10763,9 @@ async function runDaemon(opts = {}) {
|
|
|
10228
10763
|
outboxDb,
|
|
10229
10764
|
inboxDb,
|
|
10230
10765
|
bus,
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
meshSlug: mesh.slug
|
|
10766
|
+
brokers,
|
|
10767
|
+
meshConfigs,
|
|
10768
|
+
onPendingInserted: () => drain?.wake()
|
|
10235
10769
|
});
|
|
10236
10770
|
try {
|
|
10237
10771
|
await ipc2.ready;
|
|
@@ -10246,7 +10780,7 @@ async function runDaemon(opts = {}) {
|
|
|
10246
10780
|
pid: process.pid,
|
|
10247
10781
|
sock: DAEMON_PATHS.SOCK_FILE,
|
|
10248
10782
|
tcp: tcpEnabled ? `127.0.0.1:47823` : null,
|
|
10249
|
-
|
|
10783
|
+
meshes: meshes.map((m) => m.slug),
|
|
10250
10784
|
ts: new Date().toISOString()
|
|
10251
10785
|
}) + `
|
|
10252
10786
|
`);
|
|
@@ -10259,7 +10793,11 @@ async function runDaemon(opts = {}) {
|
|
|
10259
10793
|
`);
|
|
10260
10794
|
if (drain)
|
|
10261
10795
|
await drain.close();
|
|
10262
|
-
|
|
10796
|
+
for (const b of brokers.values()) {
|
|
10797
|
+
try {
|
|
10798
|
+
await b.close();
|
|
10799
|
+
} catch {}
|
|
10800
|
+
}
|
|
10263
10801
|
await ipc2.close();
|
|
10264
10802
|
try {
|
|
10265
10803
|
outboxDb.close();
|
|
@@ -17475,7 +18013,7 @@ USAGE
|
|
|
17475
18013
|
claudemesh <invite-url> join a mesh, then launch
|
|
17476
18014
|
claudemesh launch --name <n> --join <url> join + launch in one step
|
|
17477
18015
|
|
|
17478
|
-
Mesh
|
|
18016
|
+
Mesh (alias: "workspace" — claudemesh workspace <verb> mirrors each)
|
|
17479
18017
|
claudemesh create <name> create a new mesh
|
|
17480
18018
|
claudemesh join <url> join a mesh (accepts short /i/ or long /join/ link)
|
|
17481
18019
|
claudemesh launch [slug] launch Claude Code on a mesh (alias: connect)
|
|
@@ -17749,6 +18287,47 @@ async function main() {
|
|
|
17749
18287
|
process.exit(await invite2(positionals[0], { mesh: flags.mesh, json: !!flags.json }));
|
|
17750
18288
|
break;
|
|
17751
18289
|
}
|
|
18290
|
+
case "workspace": {
|
|
18291
|
+
const sub = positionals[0];
|
|
18292
|
+
if (!sub || sub === "launch" || sub === "connect" || sub === "open") {
|
|
18293
|
+
const { runLaunch: runLaunch2 } = await Promise.resolve().then(() => (init_launch(), exports_launch));
|
|
18294
|
+
await runLaunch2({
|
|
18295
|
+
mesh: positionals[1] ?? flags.mesh,
|
|
18296
|
+
name: flags.name,
|
|
18297
|
+
join: flags.join,
|
|
18298
|
+
yes: !!flags.y || !!flags.yes,
|
|
18299
|
+
resume: flags.resume
|
|
18300
|
+
}, process.argv.slice(2));
|
|
18301
|
+
} else if (sub === "list" || sub === "ls") {
|
|
18302
|
+
const { runList: runList2 } = await Promise.resolve().then(() => (init_list2(), exports_list));
|
|
18303
|
+
await runList2();
|
|
18304
|
+
} else if (sub === "info") {
|
|
18305
|
+
const { runInfo: runInfo2 } = await Promise.resolve().then(() => (init_info2(), exports_info));
|
|
18306
|
+
await runInfo2({});
|
|
18307
|
+
} else if (sub === "create" || sub === "new") {
|
|
18308
|
+
const { newMesh: newMesh2 } = await Promise.resolve().then(() => (init_new(), exports_new));
|
|
18309
|
+
process.exit(await newMesh2(positionals[1] ?? "", { json: !!flags.json }));
|
|
18310
|
+
} else if (sub === "join" || sub === "add") {
|
|
18311
|
+
const { runJoin: runJoin2 } = await Promise.resolve().then(() => (init_join2(), exports_join));
|
|
18312
|
+
await runJoin2(positionals.slice(1));
|
|
18313
|
+
} else if (sub === "delete" || sub === "rm") {
|
|
18314
|
+
const { deleteMesh: deleteMesh2 } = await Promise.resolve().then(() => (init_delete_mesh(), exports_delete_mesh));
|
|
18315
|
+
process.exit(await deleteMesh2(positionals[1] ?? "", { yes: !!flags.y || !!flags.yes }));
|
|
18316
|
+
} else if (sub === "rename") {
|
|
18317
|
+
const { rename: rename2 } = await Promise.resolve().then(() => (init_rename2(), exports_rename));
|
|
18318
|
+
process.exit(await rename2(positionals[1] ?? "", positionals[2] ?? ""));
|
|
18319
|
+
} else if (sub === "share" || sub === "invite") {
|
|
18320
|
+
const { invite: invite2 } = await Promise.resolve().then(() => (init_invite(), exports_invite));
|
|
18321
|
+
process.exit(await invite2(positionals[1], { mesh: flags.mesh, json: !!flags.json }));
|
|
18322
|
+
} else if (sub === "overview") {
|
|
18323
|
+
const { runMe: runMe2 } = await Promise.resolve().then(() => (init_me(), exports_me));
|
|
18324
|
+
process.exit(await runMe2({ mesh: flags.mesh, json: !!flags.json }));
|
|
18325
|
+
} else {
|
|
18326
|
+
console.error("Usage: claudemesh workspace <list|info|create|join|delete|rename|share|launch|overview>");
|
|
18327
|
+
process.exit(EXIT.INVALID_ARGS);
|
|
18328
|
+
}
|
|
18329
|
+
break;
|
|
18330
|
+
}
|
|
17752
18331
|
case "disconnect": {
|
|
17753
18332
|
const { runDisconnect: runDisconnect2 } = await Promise.resolve().then(() => (init_kick(), exports_kick));
|
|
17754
18333
|
process.exit(await runDisconnect2(positionals[0], { mesh: flags.mesh, stale: flags.stale, all: !!flags.all }));
|
|
@@ -18594,4 +19173,4 @@ main().catch((err) => {
|
|
|
18594
19173
|
process.exit(EXIT.INTERNAL_ERROR);
|
|
18595
19174
|
});
|
|
18596
19175
|
|
|
18597
|
-
//# debugId=
|
|
19176
|
+
//# debugId=5CE8252722B33A8D64756E2164756E21
|