@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.
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +23 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/identity.d.ts +2 -0
- package/dist/commands/identity.d.ts.map +1 -0
- package/dist/commands/identity.js +32 -0
- package/dist/commands/identity.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/join.d.ts +2 -0
- package/dist/commands/join.d.ts.map +1 -0
- package/dist/commands/join.js +52 -0
- package/dist/commands/join.js.map +1 -0
- package/dist/commands/peers.d.ts +2 -0
- package/dist/commands/peers.d.ts.map +1 -0
- package/dist/commands/peers.js +25 -0
- package/dist/commands/peers.js.map +1 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +26 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/util/config.d.ts +6 -0
- package/dist/util/config.d.ts.map +1 -0
- package/dist/util/config.js +40 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/format.d.ts +5 -0
- package/dist/util/format.d.ts.map +1 -0
- package/dist/util/format.js +15 -0
- package/dist/util/format.js.map +1 -0
- package/dist/util/index.d.ts +4 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +4 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/peer-factory.d.ts +5 -0
- package/dist/util/peer-factory.d.ts.map +1 -0
- package/dist/util/peer-factory.js +28 -0
- package/dist/util/peer-factory.js.map +1 -0
- package/package.json +30 -0
- package/src/commands/history.ts +25 -0
- package/src/commands/identity.ts +35 -0
- package/src/commands/index.ts +5 -0
- package/src/commands/join.ts +60 -0
- package/src/commands/peers.ts +30 -0
- package/src/commands/publish.ts +27 -0
- package/src/index.ts +38 -0
- package/src/util/config.ts +44 -0
- package/src/util/format.ts +18 -0
- package/src/util/index.ts +3 -0
- package/src/util/peer-factory.ts +30 -0
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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,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,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
|
+
}
|