@a-company/paradigm 3.44.0 → 3.46.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/chunk-KVDYJLTC.js +121 -0
- package/dist/{chunk-SOBTKFSP.js → chunk-S2HO5MLR.js} +5 -0
- package/dist/index.js +40 -19
- package/dist/mcp.js +71 -1
- package/dist/peers-RFQCWVLV.js +82 -0
- package/dist/{platform-server-KHL6ZPPN.js → platform-server-H7Y6Q7O4.js} +1 -1
- package/dist/{serve-JVXSRSUB.js → serve-KKEHE44G.js} +1 -1
- package/dist/{symphony-EYRGGVNE.js → symphony-6K3HD7AW.js} +349 -28
- package/dist/{symphony-QWOEKZMC.js → symphony-YCHBYN3E.js} +19 -1
- package/dist/symphony-peers-APOGJPF4.js +120 -0
- package/dist/symphony-peers-HSY3RI3S.js +34 -0
- package/dist/symphony-relay-GTAJRCVF.js +683 -0
- package/dist/university-content/courses/para-501.json +84 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../paradigm-mcp/src/utils/symphony-peers.ts
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
import * as crypto from "crypto";
|
|
8
|
+
var SCORE_DIR = path.join(os.homedir(), ".paradigm", "score");
|
|
9
|
+
var PEERS_FILE = path.join(SCORE_DIR, "peers.json");
|
|
10
|
+
var PAIRING_TTL_MS = 5 * 60 * 1e3;
|
|
11
|
+
function loadPeers() {
|
|
12
|
+
try {
|
|
13
|
+
if (!fs.existsSync(PEERS_FILE)) return [];
|
|
14
|
+
const content = fs.readFileSync(PEERS_FILE, "utf-8");
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (!Array.isArray(parsed)) return [];
|
|
17
|
+
return parsed;
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function savePeers(peers) {
|
|
23
|
+
if (!fs.existsSync(SCORE_DIR)) {
|
|
24
|
+
fs.mkdirSync(SCORE_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
fs.writeFileSync(PEERS_FILE, JSON.stringify(peers, null, 2), { mode: 384 });
|
|
27
|
+
}
|
|
28
|
+
function findPeer(id) {
|
|
29
|
+
const peers = loadPeers();
|
|
30
|
+
return peers.find((p) => p.id === id) ?? null;
|
|
31
|
+
}
|
|
32
|
+
function addPeer(peer) {
|
|
33
|
+
const peers = loadPeers();
|
|
34
|
+
const idx = peers.findIndex((p) => p.id === peer.id);
|
|
35
|
+
if (idx >= 0) {
|
|
36
|
+
peers[idx] = peer;
|
|
37
|
+
} else {
|
|
38
|
+
peers.push(peer);
|
|
39
|
+
}
|
|
40
|
+
savePeers(peers);
|
|
41
|
+
}
|
|
42
|
+
function revokePeer(id) {
|
|
43
|
+
const peers = loadPeers();
|
|
44
|
+
const peer = peers.find((p) => p.id === id);
|
|
45
|
+
if (!peer) return false;
|
|
46
|
+
peer.revoked = true;
|
|
47
|
+
savePeers(peers);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function forgetAllPeers() {
|
|
51
|
+
const peers = loadPeers();
|
|
52
|
+
const count = peers.length;
|
|
53
|
+
if (fs.existsSync(PEERS_FILE)) {
|
|
54
|
+
fs.unlinkSync(PEERS_FILE);
|
|
55
|
+
}
|
|
56
|
+
return count;
|
|
57
|
+
}
|
|
58
|
+
function updatePeerLastSeen(id) {
|
|
59
|
+
const peers = loadPeers();
|
|
60
|
+
const peer = peers.find((p) => p.id === id);
|
|
61
|
+
if (!peer) return;
|
|
62
|
+
peer.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
63
|
+
savePeers(peers);
|
|
64
|
+
}
|
|
65
|
+
function updatePeerAgents(id, agents) {
|
|
66
|
+
const peers = loadPeers();
|
|
67
|
+
const peer = peers.find((p) => p.id === id);
|
|
68
|
+
if (!peer) return;
|
|
69
|
+
peer.agents = agents;
|
|
70
|
+
savePeers(peers);
|
|
71
|
+
}
|
|
72
|
+
function generatePairing() {
|
|
73
|
+
const sharedSecret = crypto.randomBytes(32).toString("hex");
|
|
74
|
+
const code = (parseInt(crypto.randomBytes(3).toString("hex"), 16) % 1e6).toString().padStart(6, "0");
|
|
75
|
+
const codeHash = crypto.createHash("sha256").update(code).digest("hex");
|
|
76
|
+
return {
|
|
77
|
+
code,
|
|
78
|
+
codeHash,
|
|
79
|
+
sharedSecret,
|
|
80
|
+
createdAt: Date.now()
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function verifyPairingCode(state, code) {
|
|
84
|
+
if (Date.now() - state.createdAt > PAIRING_TTL_MS) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const hash = crypto.createHash("sha256").update(code).digest("hex");
|
|
88
|
+
return hash === state.codeHash;
|
|
89
|
+
}
|
|
90
|
+
function computeHmacProof(challenge, codeHash) {
|
|
91
|
+
return crypto.createHmac("sha256", codeHash).update(challenge).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
function verifyHmacProof(challenge, codeHash, proof) {
|
|
94
|
+
const expected = computeHmacProof(challenge, codeHash);
|
|
95
|
+
if (expected.length !== proof.length) return false;
|
|
96
|
+
try {
|
|
97
|
+
return crypto.timingSafeEqual(
|
|
98
|
+
Buffer.from(expected, "hex"),
|
|
99
|
+
Buffer.from(proof, "hex")
|
|
100
|
+
);
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export {
|
|
107
|
+
PEERS_FILE,
|
|
108
|
+
PAIRING_TTL_MS,
|
|
109
|
+
loadPeers,
|
|
110
|
+
savePeers,
|
|
111
|
+
findPeer,
|
|
112
|
+
addPeer,
|
|
113
|
+
revokePeer,
|
|
114
|
+
forgetAllPeers,
|
|
115
|
+
updatePeerLastSeen,
|
|
116
|
+
updatePeerAgents,
|
|
117
|
+
generatePairing,
|
|
118
|
+
verifyPairingCode,
|
|
119
|
+
computeHmacProof,
|
|
120
|
+
verifyHmacProof
|
|
121
|
+
};
|
|
@@ -246,6 +246,9 @@ function appendToOutbox(agentId, message) {
|
|
|
246
246
|
ensureAgentDir(agentId);
|
|
247
247
|
appendJsonlLine(outboxPath(agentId), message);
|
|
248
248
|
}
|
|
249
|
+
function readOutbox(agentId) {
|
|
250
|
+
return readJsonlFile(outboxPath(agentId));
|
|
251
|
+
}
|
|
249
252
|
function acknowledgeMessages(agentId, lastMessageId) {
|
|
250
253
|
const filePath = ackPath(agentId);
|
|
251
254
|
ensureAgentDir(agentId);
|
|
@@ -596,7 +599,9 @@ export {
|
|
|
596
599
|
markAgentPollTime,
|
|
597
600
|
isAgentAsleep,
|
|
598
601
|
discoverClaudeCodeSessions,
|
|
602
|
+
appendToInbox,
|
|
599
603
|
readInbox,
|
|
604
|
+
readOutbox,
|
|
600
605
|
acknowledgeMessages,
|
|
601
606
|
garbageCollect,
|
|
602
607
|
createThread,
|
package/dist/index.js
CHANGED
|
@@ -710,7 +710,7 @@ loreCmd.option("-p, --port <port>", "Port to run on", "3840").option("--no-open"
|
|
|
710
710
|
await loreServeCommand(void 0, options);
|
|
711
711
|
});
|
|
712
712
|
program.command("serve").description("Launch Paradigm Platform \u2014 unified development management UI").option("-p, --port <port>", "Port to run on", "3850").option("--no-open", "Don't open browser automatically").option("--sections <list>", "Comma-separated sections to enable (e.g., lore,graph,git)").action(async (options) => {
|
|
713
|
-
const { serveCommand } = await import("./serve-
|
|
713
|
+
const { serveCommand } = await import("./serve-KKEHE44G.js");
|
|
714
714
|
await serveCommand(options);
|
|
715
715
|
});
|
|
716
716
|
var graphCmd = program.command("graph").description("Interactive symbol relationship graph").argument("[path]", "Project directory", void 0).option("-p, --port <port>", "Port to run on", "3841").option("--no-open", "Don't open browser automatically").action(async (path2, options) => {
|
|
@@ -835,71 +835,92 @@ pipelineCmd.action(async () => {
|
|
|
835
835
|
});
|
|
836
836
|
var symphonyCmd = program.command("symphony").description("Symphony \u2014 agent-to-agent messaging for multi-session collaboration");
|
|
837
837
|
symphonyCmd.command("join").description("Join this session to the Symphony network").option("--remote <ip>", "Connect to remote Symphony server").action(async (options) => {
|
|
838
|
-
const { symphonyJoinCommand } = await import("./symphony-
|
|
838
|
+
const { symphonyJoinCommand } = await import("./symphony-6K3HD7AW.js");
|
|
839
839
|
await symphonyJoinCommand(options);
|
|
840
840
|
});
|
|
841
841
|
symphonyCmd.command("leave").description("Remove this session from the Symphony network").action(async () => {
|
|
842
|
-
const { symphonyLeaveCommand } = await import("./symphony-
|
|
842
|
+
const { symphonyLeaveCommand } = await import("./symphony-6K3HD7AW.js");
|
|
843
843
|
await symphonyLeaveCommand();
|
|
844
844
|
});
|
|
845
845
|
symphonyCmd.command("whoami").description("Show this agent's identity and linked peers").action(async () => {
|
|
846
|
-
const { symphonyWhoamiCommand } = await import("./symphony-
|
|
846
|
+
const { symphonyWhoamiCommand } = await import("./symphony-6K3HD7AW.js");
|
|
847
847
|
await symphonyWhoamiCommand();
|
|
848
848
|
});
|
|
849
849
|
symphonyCmd.command("list").alias("ls").description("List all joined agents").option("--json", "Output as JSON").action(async (options) => {
|
|
850
|
-
const { symphonyListCommand } = await import("./symphony-
|
|
850
|
+
const { symphonyListCommand } = await import("./symphony-6K3HD7AW.js");
|
|
851
851
|
await symphonyListCommand(options);
|
|
852
852
|
});
|
|
853
853
|
symphonyCmd.command("send <message>").description("Send a note to agents").option("--to <agent>", "Send to specific agent (omit for broadcast)").option("--thread <id>", "Reply to existing thread").action(async (message, options) => {
|
|
854
|
-
const { symphonySendCommand } = await import("./symphony-
|
|
854
|
+
const { symphonySendCommand } = await import("./symphony-6K3HD7AW.js");
|
|
855
855
|
await symphonySendCommand(message, options);
|
|
856
856
|
});
|
|
857
857
|
symphonyCmd.command("read").description("Show unread notes").action(async () => {
|
|
858
|
-
const { symphonyReadCommand } = await import("./symphony-
|
|
858
|
+
const { symphonyReadCommand } = await import("./symphony-6K3HD7AW.js");
|
|
859
859
|
await symphonyReadCommand();
|
|
860
860
|
});
|
|
861
861
|
symphonyCmd.command("inbox").description("Show unread notes (alias for read)").action(async () => {
|
|
862
|
-
const { symphonyReadCommand } = await import("./symphony-
|
|
862
|
+
const { symphonyReadCommand } = await import("./symphony-6K3HD7AW.js");
|
|
863
863
|
await symphonyReadCommand();
|
|
864
864
|
});
|
|
865
865
|
symphonyCmd.command("threads").description("List all threads").option("--json", "Output as JSON").action(async (options) => {
|
|
866
|
-
const { symphonyThreadsCommand } = await import("./symphony-
|
|
866
|
+
const { symphonyThreadsCommand } = await import("./symphony-6K3HD7AW.js");
|
|
867
867
|
await symphonyThreadsCommand(options);
|
|
868
868
|
});
|
|
869
869
|
symphonyCmd.command("thread <id>").description("Show full thread conversation").action(async (id) => {
|
|
870
|
-
const { symphonyThreadCommand } = await import("./symphony-
|
|
870
|
+
const { symphonyThreadCommand } = await import("./symphony-6K3HD7AW.js");
|
|
871
871
|
await symphonyThreadCommand(id);
|
|
872
872
|
});
|
|
873
873
|
symphonyCmd.command("resolve <id>").description("Mark a thread as resolved").option("--decision <text>", "Decision text to record").action(async (id, options) => {
|
|
874
|
-
const { symphonyResolveCommand } = await import("./symphony-
|
|
874
|
+
const { symphonyResolveCommand } = await import("./symphony-6K3HD7AW.js");
|
|
875
875
|
await symphonyResolveCommand(id, options);
|
|
876
876
|
});
|
|
877
877
|
symphonyCmd.command("status").description("Show Symphony network status").option("--json", "Output as JSON").action(async (options) => {
|
|
878
|
-
const { symphonyStatusCommand } = await import("./symphony-
|
|
878
|
+
const { symphonyStatusCommand } = await import("./symphony-6K3HD7AW.js");
|
|
879
879
|
await symphonyStatusCommand(options);
|
|
880
880
|
});
|
|
881
|
-
symphonyCmd.command("serve").description("Start
|
|
882
|
-
const { symphonyServeCommand } = await import("./symphony-
|
|
881
|
+
symphonyCmd.command("serve").description("Start Symphony relay server for cross-machine networking").option("--port <port>", "Port to listen on", "3939").option("--public", "Show connection string for internet access").action(async (options) => {
|
|
882
|
+
const { symphonyServeCommand } = await import("./symphony-6K3HD7AW.js");
|
|
883
883
|
await symphonyServeCommand(options);
|
|
884
884
|
});
|
|
885
|
+
var peersCmd = symphonyCmd.command("peers").description("Manage trusted remote peers");
|
|
886
|
+
peersCmd.command("list", { isDefault: true }).description("List trusted peers and their agents").option("--json", "Output as JSON").action(async (options) => {
|
|
887
|
+
const { symphonyPeersCommand } = await import("./peers-RFQCWVLV.js");
|
|
888
|
+
await symphonyPeersCommand(options);
|
|
889
|
+
});
|
|
890
|
+
peersCmd.command("revoke <id>").description("Revoke trust for a peer (disconnects immediately)").action(async (id) => {
|
|
891
|
+
const { symphonyPeersRevokeCommand } = await import("./peers-RFQCWVLV.js");
|
|
892
|
+
await symphonyPeersRevokeCommand(id);
|
|
893
|
+
});
|
|
894
|
+
peersCmd.command("forget").description("Clear all peer trust records").option("--force", "Skip confirmation").action(async (options) => {
|
|
895
|
+
const { symphonyPeersForgetCommand } = await import("./peers-RFQCWVLV.js");
|
|
896
|
+
await symphonyPeersForgetCommand(options);
|
|
897
|
+
});
|
|
898
|
+
peersCmd.action(async () => {
|
|
899
|
+
const { symphonyPeersCommand } = await import("./peers-RFQCWVLV.js");
|
|
900
|
+
await symphonyPeersCommand({});
|
|
901
|
+
});
|
|
885
902
|
symphonyCmd.command("request <file>").description("Request a file from another agent").option("--from <agent>", "Agent to request from").option("--reason <text>", "Why this file is needed").action(async (file, options) => {
|
|
886
|
-
const { symphonyRequestCommand } = await import("./symphony-
|
|
903
|
+
const { symphonyRequestCommand } = await import("./symphony-6K3HD7AW.js");
|
|
887
904
|
await symphonyRequestCommand(file, options);
|
|
888
905
|
});
|
|
889
906
|
symphonyCmd.command("requests").description("List pending file requests").action(async () => {
|
|
890
|
-
const { symphonyRequestsCommand } = await import("./symphony-
|
|
907
|
+
const { symphonyRequestsCommand } = await import("./symphony-6K3HD7AW.js");
|
|
891
908
|
await symphonyRequestsCommand();
|
|
892
909
|
});
|
|
893
910
|
symphonyCmd.command("approve <id>").description("Approve a file request").option("--redact", "Strip sensitive lines before sending").action(async (id, options) => {
|
|
894
|
-
const { symphonyApproveCommand } = await import("./symphony-
|
|
911
|
+
const { symphonyApproveCommand } = await import("./symphony-6K3HD7AW.js");
|
|
895
912
|
await symphonyApproveCommand(id, options);
|
|
896
913
|
});
|
|
897
914
|
symphonyCmd.command("deny <id>").description("Deny a file request").option("--reason <text>", "Reason for denial").action(async (id, options) => {
|
|
898
|
-
const { symphonyDenyCommand } = await import("./symphony-
|
|
915
|
+
const { symphonyDenyCommand } = await import("./symphony-6K3HD7AW.js");
|
|
899
916
|
await symphonyDenyCommand(id, options);
|
|
900
917
|
});
|
|
918
|
+
symphonyCmd.command("watch").description("Watch inbox in real-time \u2014 zero AI tokens, pure file monitoring").option("--interval <ms>", "Poll interval in milliseconds (default: 2000)").option("--thread <id>", "Only show messages from this thread").option("--quiet", "Minimal output \u2014 messages only, no header").action(async (options) => {
|
|
919
|
+
const { symphonyWatchCommand } = await import("./symphony-6K3HD7AW.js");
|
|
920
|
+
await symphonyWatchCommand(options);
|
|
921
|
+
});
|
|
901
922
|
symphonyCmd.action(async () => {
|
|
902
|
-
const { symphonyStatusCommand } = await import("./symphony-
|
|
923
|
+
const { symphonyStatusCommand } = await import("./symphony-6K3HD7AW.js");
|
|
903
924
|
await symphonyStatusCommand({});
|
|
904
925
|
});
|
|
905
926
|
program.parse();
|
package/dist/mcp.js
CHANGED
|
@@ -14584,6 +14584,35 @@ function outboxPath(agentId) {
|
|
|
14584
14584
|
function ackPath(agentId) {
|
|
14585
14585
|
return path28.join(getAgentDir(agentId), "ack.json");
|
|
14586
14586
|
}
|
|
14587
|
+
function peekInbox(agentId) {
|
|
14588
|
+
const filePath = inboxPath(agentId);
|
|
14589
|
+
if (!fs26.existsSync(filePath)) return { hasNew: false, inboxSize: 0 };
|
|
14590
|
+
const stat = fs26.statSync(filePath);
|
|
14591
|
+
const inboxSize = stat.size;
|
|
14592
|
+
const ack = readAck(agentId);
|
|
14593
|
+
if (!ack) {
|
|
14594
|
+
return { hasNew: inboxSize > 0, inboxSize };
|
|
14595
|
+
}
|
|
14596
|
+
const ackSizePath = path28.join(getAgentDir(agentId), "ack-size.json");
|
|
14597
|
+
if (fs26.existsSync(ackSizePath)) {
|
|
14598
|
+
try {
|
|
14599
|
+
const ackSize = JSON.parse(fs26.readFileSync(ackSizePath, "utf-8"));
|
|
14600
|
+
return { hasNew: inboxSize > (ackSize.size || 0), inboxSize };
|
|
14601
|
+
} catch {
|
|
14602
|
+
}
|
|
14603
|
+
}
|
|
14604
|
+
return { hasNew: inboxSize > 0, inboxSize };
|
|
14605
|
+
}
|
|
14606
|
+
function recordAckSize(agentId) {
|
|
14607
|
+
const filePath = inboxPath(agentId);
|
|
14608
|
+
const ackSizePath = path28.join(getAgentDir(agentId), "ack-size.json");
|
|
14609
|
+
try {
|
|
14610
|
+
const size = fs26.existsSync(filePath) ? fs26.statSync(filePath).size : 0;
|
|
14611
|
+
ensureAgentDir(agentId);
|
|
14612
|
+
fs26.writeFileSync(ackSizePath, JSON.stringify({ size }), "utf-8");
|
|
14613
|
+
} catch {
|
|
14614
|
+
}
|
|
14615
|
+
}
|
|
14587
14616
|
function appendToInbox(agentId, message) {
|
|
14588
14617
|
ensureAgentDir(agentId);
|
|
14589
14618
|
appendJsonlLine(inboxPath(agentId), message);
|
|
@@ -14942,9 +14971,26 @@ function isProcessAlive2(pid) {
|
|
|
14942
14971
|
// ../paradigm-mcp/src/tools/symphony.ts
|
|
14943
14972
|
function getSymphonyToolsList() {
|
|
14944
14973
|
return [
|
|
14974
|
+
{
|
|
14975
|
+
name: "paradigm_symphony_peek",
|
|
14976
|
+
description: "Ultra-cheap inbox check \u2014 file stat only, no parsing. Returns { hasNew: true/false }. Use with /loop 10s for near-free monitoring. When hasNew is true, call paradigm_symphony_poll to read messages. ~15 tokens.",
|
|
14977
|
+
inputSchema: {
|
|
14978
|
+
type: "object",
|
|
14979
|
+
properties: {
|
|
14980
|
+
status: {
|
|
14981
|
+
type: "string",
|
|
14982
|
+
description: "Short status blurb (same as poll). Updates heartbeat."
|
|
14983
|
+
}
|
|
14984
|
+
}
|
|
14985
|
+
},
|
|
14986
|
+
annotations: {
|
|
14987
|
+
readOnlyHint: true,
|
|
14988
|
+
destructiveHint: false
|
|
14989
|
+
}
|
|
14990
|
+
},
|
|
14945
14991
|
{
|
|
14946
14992
|
name: "paradigm_symphony_poll",
|
|
14947
|
-
description: "
|
|
14993
|
+
description: "Read inbox notes and process them. Call when paradigm_symphony_peek returns hasNew: true, or directly via /loop for continuous messaging. Returns unread notes formatted as markdown with thread context and suggested actions. Updates heartbeat and optional status blurb. ~200 tokens.",
|
|
14948
14994
|
inputSchema: {
|
|
14949
14995
|
type: "object",
|
|
14950
14996
|
properties: {
|
|
@@ -15121,6 +15167,16 @@ async function handleSymphonyTool(name, args, ctx) {
|
|
|
15121
15167
|
identity = registerAgent(ctx.rootDir);
|
|
15122
15168
|
}
|
|
15123
15169
|
switch (name) {
|
|
15170
|
+
case "paradigm_symphony_peek": {
|
|
15171
|
+
const peekStatus = args.status;
|
|
15172
|
+
markAgentPollTime(identity.id, peekStatus);
|
|
15173
|
+
const { hasNew } = peekInbox(identity.id);
|
|
15174
|
+
const pendingRequests = hasNew ? listFileRequests("pending").length : 0;
|
|
15175
|
+
return {
|
|
15176
|
+
handled: true,
|
|
15177
|
+
text: JSON.stringify(hasNew ? { hasNew: true, pendingFileRequests: pendingRequests, action: "call paradigm_symphony_poll to read" } : { hasNew: false })
|
|
15178
|
+
};
|
|
15179
|
+
}
|
|
15124
15180
|
case "paradigm_symphony_poll": {
|
|
15125
15181
|
cleanStaleAgents();
|
|
15126
15182
|
expireOldRequests();
|
|
@@ -15130,6 +15186,7 @@ async function handleSymphonyTool(name, args, ctx) {
|
|
|
15130
15186
|
if (messages.length > 0) {
|
|
15131
15187
|
const lastId = messages[messages.length - 1].id;
|
|
15132
15188
|
acknowledgeMessages(identity.id, lastId);
|
|
15189
|
+
recordAckSize(identity.id);
|
|
15133
15190
|
}
|
|
15134
15191
|
garbageCollect(identity.id);
|
|
15135
15192
|
const pendingRequests = listFileRequests("pending");
|
|
@@ -15236,6 +15293,18 @@ async function handleSymphonyTool(name, args, ctx) {
|
|
|
15236
15293
|
const threads = listThreads("active");
|
|
15237
15294
|
const unread = readInbox(identity.id);
|
|
15238
15295
|
const pendingRequests = listFileRequests("pending");
|
|
15296
|
+
let peers = [];
|
|
15297
|
+
try {
|
|
15298
|
+
const { loadPeers } = await import("./symphony-peers-APOGJPF4.js");
|
|
15299
|
+
const allPeers = loadPeers();
|
|
15300
|
+
peers = allPeers.filter((p) => !p.revoked).map((p) => ({
|
|
15301
|
+
id: p.id,
|
|
15302
|
+
address: p.address,
|
|
15303
|
+
agents: p.agents?.length ?? 0,
|
|
15304
|
+
lastSeen: p.lastSeen
|
|
15305
|
+
}));
|
|
15306
|
+
} catch {
|
|
15307
|
+
}
|
|
15239
15308
|
return {
|
|
15240
15309
|
handled: true,
|
|
15241
15310
|
text: JSON.stringify({
|
|
@@ -15253,6 +15322,7 @@ async function handleSymphonyTool(name, args, ctx) {
|
|
|
15253
15322
|
lastPoll: a.lastPoll,
|
|
15254
15323
|
statusBlurb: a.statusBlurb
|
|
15255
15324
|
})),
|
|
15325
|
+
peers,
|
|
15256
15326
|
activeThreads: threads.map((t) => ({
|
|
15257
15327
|
id: t.id,
|
|
15258
15328
|
topic: t.topic,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
forgetAllPeers,
|
|
4
|
+
loadPeers,
|
|
5
|
+
revokePeer
|
|
6
|
+
} from "./chunk-KVDYJLTC.js";
|
|
7
|
+
import "./chunk-ZXMDA7VB.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/symphony/peers.ts
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
async function symphonyPeersCommand(options) {
|
|
12
|
+
const peers = loadPeers();
|
|
13
|
+
if (options.json) {
|
|
14
|
+
console.log(JSON.stringify(peers, null, 2));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (peers.length === 0) {
|
|
18
|
+
console.log(chalk.yellow('No trusted peers. Run "paradigm symphony serve" to accept connections.'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
console.log(chalk.cyan(`
|
|
22
|
+
Trusted Peers (${peers.length})
|
|
23
|
+
`));
|
|
24
|
+
console.log(chalk.gray(` ${"PEER ID".padEnd(20)} ${"ADDRESS".padEnd(22)} ${"STATUS".padEnd(10)} ${"AGENTS".padEnd(8)} LAST SEEN`));
|
|
25
|
+
console.log(chalk.gray(` ${"\u2500".repeat(20)} ${"\u2500".repeat(22)} ${"\u2500".repeat(10)} ${"\u2500".repeat(8)} ${"\u2500".repeat(20)}`));
|
|
26
|
+
for (const peer of peers) {
|
|
27
|
+
const status = peer.revoked ? chalk.red("revoked") : chalk.green("trusted");
|
|
28
|
+
const agentCount = (peer.agents || []).length.toString();
|
|
29
|
+
const lastSeen = peer.lastSeen ? formatRelativeTime(peer.lastSeen) : chalk.gray("never");
|
|
30
|
+
console.log(` ${chalk.white(peer.id.padEnd(20))} ${peer.address.padEnd(22)} ${status.padEnd(10)} ${agentCount.padEnd(8)} ${lastSeen}`);
|
|
31
|
+
if (peer.agents && peer.agents.length > 0) {
|
|
32
|
+
for (const agent of peer.agents) {
|
|
33
|
+
const agentStatus = agent.status === "awake" ? chalk.green("awake") : chalk.yellow("asleep");
|
|
34
|
+
console.log(chalk.gray(` \u2514 ${agent.id} [${agentStatus}]`));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log();
|
|
39
|
+
}
|
|
40
|
+
async function symphonyPeersRevokeCommand(peerId) {
|
|
41
|
+
const success = revokePeer(peerId);
|
|
42
|
+
if (success) {
|
|
43
|
+
console.log(chalk.green(`\u2713 Revoked peer ${chalk.bold(peerId)}`));
|
|
44
|
+
console.log(chalk.gray(" Peer will be disconnected and cannot reconnect until re-paired."));
|
|
45
|
+
} else {
|
|
46
|
+
console.log(chalk.red(`Peer "${peerId}" not found.`));
|
|
47
|
+
const peers = loadPeers();
|
|
48
|
+
if (peers.length > 0) {
|
|
49
|
+
console.log(chalk.gray("\n Available peers:"));
|
|
50
|
+
for (const p of peers) {
|
|
51
|
+
console.log(chalk.gray(` ${p.id} (${p.address})`));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function symphonyPeersForgetCommand(options) {
|
|
57
|
+
const peers = loadPeers();
|
|
58
|
+
if (peers.length === 0) {
|
|
59
|
+
console.log(chalk.yellow("No peers to forget."));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!options.force) {
|
|
63
|
+
console.log(chalk.yellow(`This will remove all ${peers.length} trusted peer(s). Use --force to confirm.`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const count = forgetAllPeers();
|
|
67
|
+
console.log(chalk.green(`\u2713 Forgot ${count} peer${count !== 1 ? "s" : ""}`));
|
|
68
|
+
console.log(chalk.gray(" All peer trust records deleted. Re-pairing required for remote connections."));
|
|
69
|
+
}
|
|
70
|
+
function formatRelativeTime(isoDate) {
|
|
71
|
+
const diff = Date.now() - new Date(isoDate).getTime();
|
|
72
|
+
const seconds = Math.floor(diff / 1e3);
|
|
73
|
+
if (seconds < 60) return "just now";
|
|
74
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
75
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
76
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
symphonyPeersCommand,
|
|
80
|
+
symphonyPeersForgetCommand,
|
|
81
|
+
symphonyPeersRevokeCommand
|
|
82
|
+
};
|
|
@@ -857,7 +857,7 @@ async function startPlatformServer(options) {
|
|
|
857
857
|
}
|
|
858
858
|
if (sections.has("symphony")) {
|
|
859
859
|
try {
|
|
860
|
-
const { createSymphonyRouter } = await import("./symphony-
|
|
860
|
+
const { createSymphonyRouter } = await import("./symphony-YCHBYN3E.js");
|
|
861
861
|
app.use("/api/symphony", createSymphonyRouter(options.projectDir, wsContext.broadcast));
|
|
862
862
|
log.component("platform-server").success("Symphony routes mounted");
|
|
863
863
|
} catch (err) {
|
|
@@ -10,7 +10,7 @@ async function serveCommand(options) {
|
|
|
10
10
|
const sections = options.sections ? options.sections.split(",").map((s) => s.trim()) : void 0;
|
|
11
11
|
console.log(chalk.cyan("\n Starting Paradigm Platform...\n"));
|
|
12
12
|
try {
|
|
13
|
-
const { startPlatformServer } = await import("./platform-server-
|
|
13
|
+
const { startPlatformServer } = await import("./platform-server-H7Y6Q7O4.js");
|
|
14
14
|
await startPlatformServer({ port, projectDir, open: shouldOpen, sections });
|
|
15
15
|
console.log(chalk.green(` Platform running at ${chalk.bold(`http://localhost:${port}`)}`));
|
|
16
16
|
console.log(chalk.gray(" Press Ctrl+C to stop\n"));
|