@agent-p2p/cli 0.0.1

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.
Files changed (56) hide show
  1. package/dist/commands/history.d.ts +2 -0
  2. package/dist/commands/history.d.ts.map +1 -0
  3. package/dist/commands/history.js +23 -0
  4. package/dist/commands/history.js.map +1 -0
  5. package/dist/commands/identity.d.ts +2 -0
  6. package/dist/commands/identity.d.ts.map +1 -0
  7. package/dist/commands/identity.js +32 -0
  8. package/dist/commands/identity.js.map +1 -0
  9. package/dist/commands/index.d.ts +6 -0
  10. package/dist/commands/index.d.ts.map +1 -0
  11. package/dist/commands/index.js +6 -0
  12. package/dist/commands/index.js.map +1 -0
  13. package/dist/commands/join.d.ts +2 -0
  14. package/dist/commands/join.d.ts.map +1 -0
  15. package/dist/commands/join.js +52 -0
  16. package/dist/commands/join.js.map +1 -0
  17. package/dist/commands/peers.d.ts +2 -0
  18. package/dist/commands/peers.d.ts.map +1 -0
  19. package/dist/commands/peers.js +25 -0
  20. package/dist/commands/peers.js.map +1 -0
  21. package/dist/commands/publish.d.ts +2 -0
  22. package/dist/commands/publish.d.ts.map +1 -0
  23. package/dist/commands/publish.js +26 -0
  24. package/dist/commands/publish.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +34 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/util/config.d.ts +6 -0
  30. package/dist/util/config.d.ts.map +1 -0
  31. package/dist/util/config.js +40 -0
  32. package/dist/util/config.js.map +1 -0
  33. package/dist/util/format.d.ts +5 -0
  34. package/dist/util/format.d.ts.map +1 -0
  35. package/dist/util/format.js +15 -0
  36. package/dist/util/format.js.map +1 -0
  37. package/dist/util/index.d.ts +4 -0
  38. package/dist/util/index.d.ts.map +1 -0
  39. package/dist/util/index.js +4 -0
  40. package/dist/util/index.js.map +1 -0
  41. package/dist/util/peer-factory.d.ts +5 -0
  42. package/dist/util/peer-factory.d.ts.map +1 -0
  43. package/dist/util/peer-factory.js +28 -0
  44. package/dist/util/peer-factory.js.map +1 -0
  45. package/package.json +30 -0
  46. package/src/commands/history.ts +25 -0
  47. package/src/commands/identity.ts +35 -0
  48. package/src/commands/index.ts +5 -0
  49. package/src/commands/join.ts +60 -0
  50. package/src/commands/peers.ts +30 -0
  51. package/src/commands/publish.ts +27 -0
  52. package/src/index.ts +38 -0
  53. package/src/util/config.ts +44 -0
  54. package/src/util/format.ts +18 -0
  55. package/src/util/index.ts +3 -0
  56. package/src/util/peer-factory.ts +30 -0
@@ -0,0 +1,2 @@
1
+ export declare function historyCommand(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/commands/history.ts"],"names":[],"mappings":"AAEA,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBlE"}
@@ -0,0 +1,23 @@
1
+ import { createShortLivedPeer, formatSignal } from "../util/index.js";
2
+ export async function historyCommand(args) {
3
+ const [stationId, topic] = args;
4
+ if (!stationId || !topic) {
5
+ console.error("Usage: agent-p2p history <station-id> <topic>");
6
+ process.exit(1);
7
+ }
8
+ console.log(`Fetching history for "${topic}" on station "${stationId}"...`);
9
+ const peer = await createShortLivedPeer(stationId, 3000);
10
+ const signals = peer.getHistory(topic);
11
+ if (signals.length === 0) {
12
+ console.log("No signals found.");
13
+ }
14
+ else {
15
+ console.log(`\n${signals.length} signal(s):\n`);
16
+ for (const s of signals) {
17
+ console.log(formatSignal(s));
18
+ }
19
+ }
20
+ peer.disconnect();
21
+ process.exit(0);
22
+ }
23
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/commands/history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAW,MAAM,kBAAkB,CAAC;AAE/E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAc;IAClD,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;IAChC,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,yBAAyB,KAAK,iBAAiB,SAAS,MAAM,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAEzD,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,eAAe,CAAC,CAAC;QAChD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;IACF,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function identityCommand(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/commands/identity.ts"],"names":[],"mappings":"AAIA,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BnE"}
@@ -0,0 +1,32 @@
1
+ import { peerId } from "@agent-p2p/core";
2
+ import { loadOrCreateKeypair, generateAndSaveKeypair, loadExportedKeypair, identityExists } from "../util/config.js";
3
+ import { shortId } from "../util/format.js";
4
+ export async function identityCommand(args) {
5
+ const generate = args.includes("--generate");
6
+ if (generate) {
7
+ const kp = await generateAndSaveKeypair();
8
+ const pid = peerId(kp.publicKey);
9
+ console.log(`Generated new identity`);
10
+ console.log(` Peer ID: ${pid}`);
11
+ console.log(` Short: ${shortId(pid)}`);
12
+ console.log(` Saved to ~/.agent-p2p/identity.json`);
13
+ return;
14
+ }
15
+ if (!identityExists()) {
16
+ const kp = await loadOrCreateKeypair();
17
+ const pid = peerId(kp.publicKey);
18
+ console.log(`Created new identity (first run)`);
19
+ console.log(` Peer ID: ${pid}`);
20
+ console.log(` Short: ${shortId(pid)}`);
21
+ console.log(` Saved to ~/.agent-p2p/identity.json`);
22
+ return;
23
+ }
24
+ const exported = loadExportedKeypair();
25
+ if (!exported) {
26
+ console.error("No identity found. Run: agent-p2p identity --generate");
27
+ process.exit(1);
28
+ }
29
+ console.log(` Peer ID: ${exported.publicKeyHex}`);
30
+ console.log(` Short: ${shortId(exported.publicKeyHex)}`);
31
+ }
32
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/commands/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAc;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAE7C,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,EAAE,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACrD,OAAO;IACR,CAAC;IAED,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,MAAM,mBAAmB,EAAE,CAAC;QACvC,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACrD,OAAO;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,CAAC,YAAY,EAAE,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { joinCommand } from "./join.js";
2
+ export { publishCommand } from "./publish.js";
3
+ export { peersCommand } from "./peers.js";
4
+ export { historyCommand } from "./history.js";
5
+ export { identityCommand } from "./identity.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { joinCommand } from "./join.js";
2
+ export { publishCommand } from "./publish.js";
3
+ export { peersCommand } from "./peers.js";
4
+ export { historyCommand } from "./history.js";
5
+ export { identityCommand } from "./identity.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function joinCommand(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=join.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join.d.ts","sourceRoot":"","sources":["../../src/commands/join.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwD/D"}
@@ -0,0 +1,52 @@
1
+ import * as readline from "node:readline";
2
+ import { createPeer, setupCleanup, formatSignal, shortId } from "../util/index.js";
3
+ export async function joinCommand(args) {
4
+ const stationId = args[0];
5
+ if (!stationId) {
6
+ console.error("Usage: agent-p2p join <station-id> [--topic <topic>]");
7
+ process.exit(1);
8
+ }
9
+ const topicIdx = args.indexOf("--topic");
10
+ const topic = topicIdx !== -1 ? args[topicIdx + 1] : "general.chat";
11
+ if (!topic) {
12
+ console.error("Missing topic value after --topic");
13
+ process.exit(1);
14
+ }
15
+ console.log(`Joining station "${stationId}" on topic "${topic}"...`);
16
+ const peer = await createPeer(stationId);
17
+ setupCleanup(peer);
18
+ console.log(`Connected as ${shortId(peer.peerId)}`);
19
+ console.log(`Role: ${peer.permissions.getRole(peer.peerId)}`);
20
+ console.log("Type a message and press Enter to publish. Ctrl+C to quit.\n");
21
+ peer.subscribe(topic, (signal) => {
22
+ if (signal.provenance.peer_id !== peer.peerId) {
23
+ console.log(formatSignal(signal));
24
+ }
25
+ });
26
+ peer.on("peer:join", (p) => {
27
+ console.log(`> ${shortId(p.peerId)} joined`);
28
+ });
29
+ peer.on("peer:leave", (p) => {
30
+ console.log(`> ${shortId(p.peerId)} left`);
31
+ });
32
+ peer.on("roles:changed", ({ peerId: pid, role }) => {
33
+ console.log(`> ${shortId(pid)} role changed to ${role}`);
34
+ });
35
+ const rl = readline.createInterface({ input: process.stdin });
36
+ rl.on("line", async (line) => {
37
+ const msg = line.trim();
38
+ if (!msg)
39
+ return;
40
+ try {
41
+ await peer.publish(topic, msg);
42
+ }
43
+ catch (err) {
44
+ console.error(`Failed to publish: ${err.message}`);
45
+ }
46
+ });
47
+ rl.on("close", () => {
48
+ peer.disconnect();
49
+ process.exit(0);
50
+ });
51
+ }
52
+ //# sourceMappingURL=join.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join.js","sourceRoot":"","sources":["../../src/commands/join.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function peersCommand(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=peers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.d.ts","sourceRoot":"","sources":["../../src/commands/peers.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BhE"}
@@ -0,0 +1,25 @@
1
+ import { createShortLivedPeer, formatPeer, shortId } from "../util/index.js";
2
+ export async function peersCommand(args) {
3
+ const stationId = args[0];
4
+ if (!stationId) {
5
+ console.error("Usage: agent-p2p peers <station-id>");
6
+ process.exit(1);
7
+ }
8
+ console.log(`Connecting to station "${stationId}"...`);
9
+ const peer = await createShortLivedPeer(stationId, 3000);
10
+ const peers = peer.getPeers();
11
+ const roles = peer.permissions.getAllRoles();
12
+ console.log(`\nPeers online (${peers.length}):`);
13
+ for (const p of peers) {
14
+ const role = roles.get(p.peerId) ?? "viewer";
15
+ console.log(formatPeer(p, role));
16
+ }
17
+ console.log(`\nAll known roles:`);
18
+ for (const [pid, role] of roles) {
19
+ const online = peers.some((p) => p.peerId === pid) ? "online" : "offline";
20
+ console.log(` ${shortId(pid)} — ${role} (${online})`);
21
+ }
22
+ peer.disconnect();
23
+ process.exit(0);
24
+ }
25
+ //# sourceMappingURL=peers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peers.js","sourceRoot":"","sources":["../../src/commands/peers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE7E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAc;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,0BAA0B,SAAS,MAAM,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAEzD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAC,UAAU,EAAE,CAAC;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function publishCommand(args: string[]): Promise<void>;
2
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAEA,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlE"}
@@ -0,0 +1,26 @@
1
+ import { createShortLivedPeer, shortId } from "../util/index.js";
2
+ export async function publishCommand(args) {
3
+ const [stationId, topic, ...rest] = args;
4
+ const message = rest.join(" ");
5
+ if (!stationId || !topic || !message) {
6
+ console.error("Usage: agent-p2p publish <station-id> <topic> <message>");
7
+ process.exit(1);
8
+ }
9
+ const peer = await createShortLivedPeer(stationId);
10
+ try {
11
+ const signal = await peer.publish(topic, message);
12
+ console.log(`Published to ${topic} (${shortId(signal.id)})`);
13
+ // Wait for Yjs to propagate the update over WebRTC
14
+ await new Promise((r) => setTimeout(r, 3000));
15
+ }
16
+ catch (err) {
17
+ console.error(`Failed: ${err.message}`);
18
+ process.exit(1);
19
+ }
20
+ finally {
21
+ peer.disconnect();
22
+ // Trystero keeps WebSocket connections to trackers open; force exit for CLI
23
+ process.exit(0);
24
+ }
25
+ }
26
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAc;IAClD,MAAM,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE/B,IAAI,CAAC,SAAS,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtC,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAEnD,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,gBAAgB,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;QAC7D,mDAAmD;QACnD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,WAAY,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;YAAS,CAAC;QACV,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,4EAA4E;QAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bun
2
+ // No WebRTC polyfill needed — trystero bundles its own via @thaunknown/simple-peer + webrtc-polyfill
3
+ import { joinCommand, publishCommand, peersCommand, historyCommand, identityCommand } from "./commands/index.js";
4
+ const USAGE = `agent-p2p — P2P signal streaming CLI
5
+
6
+ Usage:
7
+ agent-p2p join <station-id> [--topic <topic>] Join a station (interactive)
8
+ agent-p2p publish <station-id> <topic> <msg> Publish a one-shot signal
9
+ agent-p2p peers <station-id> List peers and roles
10
+ agent-p2p history <station-id> <topic> Show channel history
11
+ agent-p2p identity [--generate] Show or create keypair
12
+ `;
13
+ const [command, ...args] = process.argv.slice(2);
14
+ switch (command) {
15
+ case "join":
16
+ await joinCommand(args);
17
+ break;
18
+ case "publish":
19
+ await publishCommand(args);
20
+ break;
21
+ case "peers":
22
+ await peersCommand(args);
23
+ break;
24
+ case "history":
25
+ await historyCommand(args);
26
+ break;
27
+ case "identity":
28
+ await identityCommand(args);
29
+ break;
30
+ default:
31
+ console.log(USAGE);
32
+ process.exit(command ? 1 : 0);
33
+ }
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,qGAAqG;AAErG,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjH,MAAM,KAAK,GAAG;;;;;;;;CAQb,CAAC;AAEF,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEjD,QAAQ,OAAO,EAAE,CAAC;IACjB,KAAK,MAAM;QACV,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM;IACP,KAAK,SAAS;QACb,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM;IACP,KAAK,OAAO;QACX,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACzB,MAAM;IACP,KAAK,SAAS;QACb,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM;IACP,KAAK,UAAU;QACd,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM;IACP;QACC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Keypair, type ExportedKeypair } from "@agent-p2p/core";
2
+ export declare function identityExists(): boolean;
3
+ export declare function loadOrCreateKeypair(): Promise<Keypair>;
4
+ export declare function generateAndSaveKeypair(): Promise<Keypair>;
5
+ export declare function loadExportedKeypair(): ExportedKeypair | null;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/util/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiD,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWpH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAW5D;AAED,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,CAAC,CAM/D;AAED,wBAAgB,mBAAmB,IAAI,eAAe,GAAG,IAAI,CAI5D"}
@@ -0,0 +1,40 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { generateKeypair, exportKeypair, importKeypair } from "@agent-p2p/core";
5
+ const CONFIG_DIR = join(homedir(), ".agent-p2p");
6
+ const IDENTITY_PATH = join(CONFIG_DIR, "identity.json");
7
+ function ensureConfigDir() {
8
+ if (!existsSync(CONFIG_DIR)) {
9
+ mkdirSync(CONFIG_DIR, { recursive: true });
10
+ }
11
+ }
12
+ export function identityExists() {
13
+ return existsSync(IDENTITY_PATH);
14
+ }
15
+ export async function loadOrCreateKeypair() {
16
+ ensureConfigDir();
17
+ if (existsSync(IDENTITY_PATH)) {
18
+ const raw = readFileSync(IDENTITY_PATH, "utf-8");
19
+ const exported = JSON.parse(raw);
20
+ return importKeypair(exported);
21
+ }
22
+ const kp = await generateKeypair();
23
+ const exported = exportKeypair(kp);
24
+ writeFileSync(IDENTITY_PATH, JSON.stringify(exported, null, 2) + "\n");
25
+ return kp;
26
+ }
27
+ export async function generateAndSaveKeypair() {
28
+ ensureConfigDir();
29
+ const kp = await generateKeypair();
30
+ const exported = exportKeypair(kp);
31
+ writeFileSync(IDENTITY_PATH, JSON.stringify(exported, null, 2) + "\n");
32
+ return kp;
33
+ }
34
+ export function loadExportedKeypair() {
35
+ if (!existsSync(IDENTITY_PATH))
36
+ return null;
37
+ const raw = readFileSync(IDENTITY_PATH, "utf-8");
38
+ return JSON.parse(raw);
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/util/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAsC,MAAM,iBAAiB,CAAC;AAEpH,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACjD,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAExD,SAAS,eAAe;IACvB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;AACF,CAAC;AAED,MAAM,UAAU,cAAc;IAC7B,OAAO,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACxC,eAAe,EAAE,CAAC;IAClB,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAoB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAClD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC;AACX,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAC3C,eAAe,EAAE,CAAC;IAClB,MAAM,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC;AACX,CAAC;AAED,MAAM,UAAU,mBAAmB;IAClC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { SignalEnvelope, PeerInfo } from "@agent-p2p/peer";
2
+ export declare function shortId(id: string): string;
3
+ export declare function formatSignal(signal: SignalEnvelope): string;
4
+ export declare function formatPeer(peer: PeerInfo, role?: string): string;
5
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/util/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAK3D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAIhE"}
@@ -0,0 +1,15 @@
1
+ export function shortId(id) {
2
+ return id.slice(0, 8);
3
+ }
4
+ export function formatSignal(signal) {
5
+ const time = new Date(signal.timestamp).toLocaleTimeString();
6
+ const from = shortId(signal.provenance.peer_id);
7
+ const body = typeof signal.payload.body === "string" ? signal.payload.body : JSON.stringify(signal.payload.body);
8
+ return `[${time}] ${from}@${signal.channel_topic}: ${body}`;
9
+ }
10
+ export function formatPeer(peer, role) {
11
+ const joined = new Date(peer.joinedAt).toLocaleTimeString();
12
+ const roleStr = role ? ` (${role})` : "";
13
+ return ` ${shortId(peer.peerId)}${roleStr} joined ${joined}`;
14
+ }
15
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/util/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CAAC,EAAU;IACjC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAsB;IAClD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,kBAAkB,EAAE,CAAC;IAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjH,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAc,EAAE,IAAa;IACvD,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,kBAAkB,EAAE,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,OAAO,YAAY,MAAM,EAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { loadOrCreateKeypair, generateAndSaveKeypair, loadExportedKeypair, identityExists } from "./config.js";
2
+ export { createPeer, setupCleanup, createShortLivedPeer } from "./peer-factory.js";
3
+ export { shortId, formatSignal, formatPeer } from "./format.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { loadOrCreateKeypair, generateAndSaveKeypair, loadExportedKeypair, identityExists } from "./config.js";
2
+ export { createPeer, setupCleanup, createShortLivedPeer } from "./peer-factory.js";
3
+ export { shortId, formatSignal, formatPeer } from "./format.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { RadioPeer, type RadioPeerOptions } from "@agent-p2p/peer";
2
+ export declare function createPeer(stationId: string, opts?: Partial<RadioPeerOptions>): Promise<RadioPeer>;
3
+ export declare function setupCleanup(peer: RadioPeer): void;
4
+ export declare function createShortLivedPeer(stationId: string, syncMs?: number): Promise<RadioPeer>;
5
+ //# sourceMappingURL=peer-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-factory.d.ts","sourceRoot":"","sources":["../../src/util/peer-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnE,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,OAAO,CAAC,gBAAgB,CAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAU5G;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAOlD;AAED,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,SAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAK/F"}
@@ -0,0 +1,28 @@
1
+ import { RadioPeer } from "@agent-p2p/peer";
2
+ import { loadOrCreateKeypair } from "./config.js";
3
+ export async function createPeer(stationId, opts = {}) {
4
+ const keypair = await loadOrCreateKeypair();
5
+ const peer = new RadioPeer(stationId, {
6
+ enablePersistence: false,
7
+ keypair,
8
+ transport: { backend: "torrent" },
9
+ ...opts,
10
+ });
11
+ await peer.ready();
12
+ return peer;
13
+ }
14
+ export function setupCleanup(peer) {
15
+ const cleanup = () => {
16
+ peer.disconnect();
17
+ process.exit(0);
18
+ };
19
+ process.on("SIGINT", cleanup);
20
+ process.on("SIGTERM", cleanup);
21
+ }
22
+ export async function createShortLivedPeer(stationId, syncMs = 5000) {
23
+ const peer = await createPeer(stationId);
24
+ // Wait for CRDT sync
25
+ await new Promise((r) => setTimeout(r, syncMs));
26
+ return peer;
27
+ }
28
+ //# sourceMappingURL=peer-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer-factory.js","sourceRoot":"","sources":["../../src/util/peer-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAAiB,EAAE,OAAkC,EAAE;IACvF,MAAM,OAAO,GAAG,MAAM,mBAAmB,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,SAAS,EAAE;QACrC,iBAAiB,EAAE,KAAK;QACxB,OAAO;QACP,SAAS,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;QACjC,GAAG,IAAI;KACP,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACnB,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAe;IAC3C,MAAM,OAAO,GAAG,GAAG,EAAE;QACpB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB,EAAE,MAAM,GAAG,IAAI;IAC1E,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IACzC,qBAAqB;IACrB,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC;AACb,CAAC"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@agent-p2p/cli",
3
+ "version": "0.0.1",
4
+ "description": "CLI for agent-p2p — join stations, publish signals, and manage identity",
5
+ "type": "module",
6
+ "bin": {
7
+ "agent-p2p": "./src/index.ts"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "bun": "./src/index.ts",
12
+ "import": "./dist/index.js",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": ["src", "dist"],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json"
19
+ },
20
+ "dependencies": {
21
+ "@agent-p2p/core": "^0.0.1",
22
+ "@agent-p2p/peer": "^0.0.1"
23
+ },
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/luisfreitas/radio.ag",
28
+ "directory": "packages/cli"
29
+ }
30
+ }
@@ -0,0 +1,25 @@
1
+ import { createShortLivedPeer, formatSignal, shortId } from "../util/index.js";
2
+
3
+ export async function historyCommand(args: string[]): Promise<void> {
4
+ const [stationId, topic] = args;
5
+ if (!stationId || !topic) {
6
+ console.error("Usage: agent-p2p history <station-id> <topic>");
7
+ process.exit(1);
8
+ }
9
+
10
+ console.log(`Fetching history for "${topic}" on station "${stationId}"...`);
11
+ const peer = await createShortLivedPeer(stationId, 3000);
12
+
13
+ const signals = peer.getHistory(topic);
14
+ if (signals.length === 0) {
15
+ console.log("No signals found.");
16
+ } else {
17
+ console.log(`\n${signals.length} signal(s):\n`);
18
+ for (const s of signals) {
19
+ console.log(formatSignal(s));
20
+ }
21
+ }
22
+
23
+ peer.disconnect();
24
+ process.exit(0);
25
+ }
@@ -0,0 +1,35 @@
1
+ import { peerId } from "@agent-p2p/core";
2
+ import { loadOrCreateKeypair, generateAndSaveKeypair, loadExportedKeypair, identityExists } from "../util/config.js";
3
+ import { shortId } from "../util/format.js";
4
+
5
+ export async function identityCommand(args: string[]): Promise<void> {
6
+ const generate = args.includes("--generate");
7
+
8
+ if (generate) {
9
+ const kp = await generateAndSaveKeypair();
10
+ const pid = peerId(kp.publicKey);
11
+ console.log(`Generated new identity`);
12
+ console.log(` Peer ID: ${pid}`);
13
+ console.log(` Short: ${shortId(pid)}`);
14
+ console.log(` Saved to ~/.agent-p2p/identity.json`);
15
+ return;
16
+ }
17
+
18
+ if (!identityExists()) {
19
+ const kp = await loadOrCreateKeypair();
20
+ const pid = peerId(kp.publicKey);
21
+ console.log(`Created new identity (first run)`);
22
+ console.log(` Peer ID: ${pid}`);
23
+ console.log(` Short: ${shortId(pid)}`);
24
+ console.log(` Saved to ~/.agent-p2p/identity.json`);
25
+ return;
26
+ }
27
+
28
+ const exported = loadExportedKeypair();
29
+ if (!exported) {
30
+ console.error("No identity found. Run: agent-p2p identity --generate");
31
+ process.exit(1);
32
+ }
33
+ console.log(` Peer ID: ${exported.publicKeyHex}`);
34
+ console.log(` Short: ${shortId(exported.publicKeyHex)}`);
35
+ }
@@ -0,0 +1,5 @@
1
+ export { joinCommand } from "./join.js";
2
+ export { publishCommand } from "./publish.js";
3
+ export { peersCommand } from "./peers.js";
4
+ export { historyCommand } from "./history.js";
5
+ export { identityCommand } from "./identity.js";
@@ -0,0 +1,60 @@
1
+ import * as readline from "node:readline";
2
+ import { createPeer, setupCleanup, formatSignal, shortId } from "../util/index.js";
3
+
4
+ export async function joinCommand(args: string[]): Promise<void> {
5
+ const stationId = args[0];
6
+ if (!stationId) {
7
+ console.error("Usage: agent-p2p join <station-id> [--topic <topic>]");
8
+ process.exit(1);
9
+ }
10
+
11
+ const topicIdx = args.indexOf("--topic");
12
+ const topic = topicIdx !== -1 ? args[topicIdx + 1] : "general.chat";
13
+
14
+ if (!topic) {
15
+ console.error("Missing topic value after --topic");
16
+ process.exit(1);
17
+ }
18
+
19
+ console.log(`Joining station "${stationId}" on topic "${topic}"...`);
20
+
21
+ const peer = await createPeer(stationId);
22
+ setupCleanup(peer);
23
+
24
+ console.log(`Connected as ${shortId(peer.peerId)}`);
25
+ console.log(`Role: ${peer.permissions.getRole(peer.peerId)}`);
26
+ console.log("Type a message and press Enter to publish. Ctrl+C to quit.\n");
27
+
28
+ peer.subscribe(topic, (signal) => {
29
+ if (signal.provenance.peer_id !== peer.peerId) {
30
+ console.log(formatSignal(signal));
31
+ }
32
+ });
33
+
34
+ peer.on("peer:join", (p) => {
35
+ console.log(`> ${shortId(p.peerId)} joined`);
36
+ });
37
+
38
+ peer.on("peer:leave", (p) => {
39
+ console.log(`> ${shortId(p.peerId)} left`);
40
+ });
41
+
42
+ peer.on("roles:changed", ({ peerId: pid, role }) => {
43
+ console.log(`> ${shortId(pid)} role changed to ${role}`);
44
+ });
45
+
46
+ const rl = readline.createInterface({ input: process.stdin });
47
+ rl.on("line", async (line) => {
48
+ const msg = line.trim();
49
+ if (!msg) return;
50
+ try {
51
+ await peer.publish(topic, msg);
52
+ } catch (err) {
53
+ console.error(`Failed to publish: ${(err as Error).message}`);
54
+ }
55
+ });
56
+ rl.on("close", () => {
57
+ peer.disconnect();
58
+ process.exit(0);
59
+ });
60
+ }
@@ -0,0 +1,30 @@
1
+ import { createShortLivedPeer, formatPeer, shortId } from "../util/index.js";
2
+
3
+ export async function peersCommand(args: string[]): Promise<void> {
4
+ const stationId = args[0];
5
+ if (!stationId) {
6
+ console.error("Usage: agent-p2p peers <station-id>");
7
+ process.exit(1);
8
+ }
9
+
10
+ console.log(`Connecting to station "${stationId}"...`);
11
+ const peer = await createShortLivedPeer(stationId, 3000);
12
+
13
+ const peers = peer.getPeers();
14
+ const roles = peer.permissions.getAllRoles();
15
+
16
+ console.log(`\nPeers online (${peers.length}):`);
17
+ for (const p of peers) {
18
+ const role = roles.get(p.peerId) ?? "viewer";
19
+ console.log(formatPeer(p, role));
20
+ }
21
+
22
+ console.log(`\nAll known roles:`);
23
+ for (const [pid, role] of roles) {
24
+ const online = peers.some((p) => p.peerId === pid) ? "online" : "offline";
25
+ console.log(` ${shortId(pid)} — ${role} (${online})`);
26
+ }
27
+
28
+ peer.disconnect();
29
+ process.exit(0);
30
+ }
@@ -0,0 +1,27 @@
1
+ import { createShortLivedPeer, shortId } from "../util/index.js";
2
+
3
+ export async function publishCommand(args: string[]): Promise<void> {
4
+ const [stationId, topic, ...rest] = args;
5
+ const message = rest.join(" ");
6
+
7
+ if (!stationId || !topic || !message) {
8
+ console.error("Usage: agent-p2p publish <station-id> <topic> <message>");
9
+ process.exit(1);
10
+ }
11
+
12
+ const peer = await createShortLivedPeer(stationId);
13
+
14
+ try {
15
+ const signal = await peer.publish(topic, message);
16
+ console.log(`Published to ${topic} (${shortId(signal.id)})`);
17
+ // Wait for Yjs to propagate the update over WebRTC
18
+ await new Promise((r) => setTimeout(r, 3000));
19
+ } catch (err) {
20
+ console.error(`Failed: ${(err as Error).message}`);
21
+ process.exit(1);
22
+ } finally {
23
+ peer.disconnect();
24
+ // Trystero keeps WebSocket connections to trackers open; force exit for CLI
25
+ process.exit(0);
26
+ }
27
+ }
package/src/index.ts ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bun
2
+
3
+ // No WebRTC polyfill needed — trystero bundles its own via @thaunknown/simple-peer + webrtc-polyfill
4
+
5
+ import { joinCommand, publishCommand, peersCommand, historyCommand, identityCommand } from "./commands/index.js";
6
+
7
+ const USAGE = `agent-p2p — P2P signal streaming CLI
8
+
9
+ Usage:
10
+ agent-p2p join <station-id> [--topic <topic>] Join a station (interactive)
11
+ agent-p2p publish <station-id> <topic> <msg> Publish a one-shot signal
12
+ agent-p2p peers <station-id> List peers and roles
13
+ agent-p2p history <station-id> <topic> Show channel history
14
+ agent-p2p identity [--generate] Show or create keypair
15
+ `;
16
+
17
+ const [command, ...args] = process.argv.slice(2);
18
+
19
+ switch (command) {
20
+ case "join":
21
+ await joinCommand(args);
22
+ break;
23
+ case "publish":
24
+ await publishCommand(args);
25
+ break;
26
+ case "peers":
27
+ await peersCommand(args);
28
+ break;
29
+ case "history":
30
+ await historyCommand(args);
31
+ break;
32
+ case "identity":
33
+ await identityCommand(args);
34
+ break;
35
+ default:
36
+ console.log(USAGE);
37
+ process.exit(command ? 1 : 0);
38
+ }
@@ -0,0 +1,44 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import { generateKeypair, exportKeypair, importKeypair, type Keypair, type ExportedKeypair } from "@agent-p2p/core";
5
+
6
+ const CONFIG_DIR = join(homedir(), ".agent-p2p");
7
+ const IDENTITY_PATH = join(CONFIG_DIR, "identity.json");
8
+
9
+ function ensureConfigDir(): void {
10
+ if (!existsSync(CONFIG_DIR)) {
11
+ mkdirSync(CONFIG_DIR, { recursive: true });
12
+ }
13
+ }
14
+
15
+ export function identityExists(): boolean {
16
+ return existsSync(IDENTITY_PATH);
17
+ }
18
+
19
+ export async function loadOrCreateKeypair(): Promise<Keypair> {
20
+ ensureConfigDir();
21
+ if (existsSync(IDENTITY_PATH)) {
22
+ const raw = readFileSync(IDENTITY_PATH, "utf-8");
23
+ const exported: ExportedKeypair = JSON.parse(raw);
24
+ return importKeypair(exported);
25
+ }
26
+ const kp = await generateKeypair();
27
+ const exported = exportKeypair(kp);
28
+ writeFileSync(IDENTITY_PATH, JSON.stringify(exported, null, 2) + "\n");
29
+ return kp;
30
+ }
31
+
32
+ export async function generateAndSaveKeypair(): Promise<Keypair> {
33
+ ensureConfigDir();
34
+ const kp = await generateKeypair();
35
+ const exported = exportKeypair(kp);
36
+ writeFileSync(IDENTITY_PATH, JSON.stringify(exported, null, 2) + "\n");
37
+ return kp;
38
+ }
39
+
40
+ export function loadExportedKeypair(): ExportedKeypair | null {
41
+ if (!existsSync(IDENTITY_PATH)) return null;
42
+ const raw = readFileSync(IDENTITY_PATH, "utf-8");
43
+ return JSON.parse(raw);
44
+ }
@@ -0,0 +1,18 @@
1
+ import type { SignalEnvelope, PeerInfo } from "@agent-p2p/peer";
2
+
3
+ export function shortId(id: string): string {
4
+ return id.slice(0, 8);
5
+ }
6
+
7
+ export function formatSignal(signal: SignalEnvelope): string {
8
+ const time = new Date(signal.timestamp).toLocaleTimeString();
9
+ const from = shortId(signal.provenance.peer_id);
10
+ const body = typeof signal.payload.body === "string" ? signal.payload.body : JSON.stringify(signal.payload.body);
11
+ return `[${time}] ${from}@${signal.channel_topic}: ${body}`;
12
+ }
13
+
14
+ export function formatPeer(peer: PeerInfo, role?: string): string {
15
+ const joined = new Date(peer.joinedAt).toLocaleTimeString();
16
+ const roleStr = role ? ` (${role})` : "";
17
+ return ` ${shortId(peer.peerId)}${roleStr} joined ${joined}`;
18
+ }
@@ -0,0 +1,3 @@
1
+ export { loadOrCreateKeypair, generateAndSaveKeypair, loadExportedKeypair, identityExists } from "./config.js";
2
+ export { createPeer, setupCleanup, createShortLivedPeer } from "./peer-factory.js";
3
+ export { shortId, formatSignal, formatPeer } from "./format.js";
@@ -0,0 +1,30 @@
1
+ import { RadioPeer, type RadioPeerOptions } from "@agent-p2p/peer";
2
+ import { loadOrCreateKeypair } from "./config.js";
3
+
4
+ export async function createPeer(stationId: string, opts: Partial<RadioPeerOptions> = {}): Promise<RadioPeer> {
5
+ const keypair = await loadOrCreateKeypair();
6
+ const peer = new RadioPeer(stationId, {
7
+ enablePersistence: false,
8
+ keypair,
9
+ transport: { backend: "torrent" },
10
+ ...opts,
11
+ });
12
+ await peer.ready();
13
+ return peer;
14
+ }
15
+
16
+ export function setupCleanup(peer: RadioPeer): void {
17
+ const cleanup = () => {
18
+ peer.disconnect();
19
+ process.exit(0);
20
+ };
21
+ process.on("SIGINT", cleanup);
22
+ process.on("SIGTERM", cleanup);
23
+ }
24
+
25
+ export async function createShortLivedPeer(stationId: string, syncMs = 5000): Promise<RadioPeer> {
26
+ const peer = await createPeer(stationId);
27
+ // Wait for CRDT sync
28
+ await new Promise((r) => setTimeout(r, syncMs));
29
+ return peer;
30
+ }