@perkos/perkos-a2a 0.8.35 → 0.9.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 (50) hide show
  1. package/README.md +99 -21
  2. package/dist/agent.d.ts +14 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +363 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/agentic-actions.js +472 -0
  7. package/dist/agentic-actions.js.map +1 -0
  8. package/dist/chat-client.d.ts +77 -0
  9. package/dist/chat-client.d.ts.map +1 -0
  10. package/dist/chat-client.js +364 -0
  11. package/dist/chat-client.js.map +1 -0
  12. package/dist/chat-store.d.ts +87 -0
  13. package/dist/chat-store.d.ts.map +1 -0
  14. package/dist/chat-store.js +233 -0
  15. package/dist/chat-store.js.map +1 -0
  16. package/dist/chat-types.d.ts +166 -0
  17. package/dist/chat-types.d.ts.map +1 -0
  18. package/dist/chat-types.js +13 -0
  19. package/dist/chat-types.js.map +1 -0
  20. package/dist/hermes-cli.d.ts +30 -0
  21. package/dist/hermes-cli.d.ts.map +1 -0
  22. package/dist/hermes-cli.js +120 -0
  23. package/dist/hermes-cli.js.map +1 -0
  24. package/dist/hermes-plugin.d.ts +91 -0
  25. package/dist/hermes-plugin.d.ts.map +1 -0
  26. package/dist/hermes-plugin.js +196 -0
  27. package/dist/hermes-plugin.js.map +1 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1022 -26
  31. package/dist/index.js.map +4 -4
  32. package/dist/pairing.js +66 -0
  33. package/dist/pairing.js.map +1 -0
  34. package/dist/relay-client.js +222 -0
  35. package/dist/relay-client.js.map +1 -0
  36. package/dist/relay.js +274 -0
  37. package/dist/relay.js.map +1 -0
  38. package/dist/runtime-reply.js +137 -0
  39. package/dist/runtime-reply.js.map +1 -0
  40. package/dist/server.js +611 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/types.d.ts +36 -1
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js +4 -1
  45. package/dist/types.js.map +1 -1
  46. package/docs/chat-client.md +190 -0
  47. package/docs/demo-setup.md +273 -0
  48. package/openclaw.plugin.json +38 -1
  49. package/package.json +25 -21
  50. package/scripts/hermes/install.mjs +211 -0
@@ -0,0 +1,66 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { dirname, join } from "path";
4
+ import { generateKeyPairSync } from "crypto";
5
+ export function defaultPairingPath(agentName) {
6
+ const safeName = agentName.replace(/[^a-zA-Z0-9_.-]/g, "_");
7
+ return join(homedir(), ".perkos", "a2a", "agents", `${safeName}.json`);
8
+ }
9
+ export function loadOrCreateIdentity(agentName, path = defaultPairingPath(agentName)) {
10
+ if (existsSync(path)) {
11
+ const raw = JSON.parse(readFileSync(path, "utf8"));
12
+ if (raw.identity?.agentName && raw.identity?.publicKey && raw.identity?.privateKey)
13
+ return raw.identity;
14
+ }
15
+ const pair = generateKeyPairSync("ed25519", {
16
+ publicKeyEncoding: { type: "spki", format: "pem" },
17
+ privateKeyEncoding: { type: "pkcs8", format: "pem" },
18
+ });
19
+ return {
20
+ agentName,
21
+ publicKey: pair.publicKey,
22
+ privateKey: pair.privateKey,
23
+ keyType: "ed25519",
24
+ createdAt: new Date().toISOString(),
25
+ };
26
+ }
27
+ export function savePairingProfile(profile, path = defaultPairingPath(profile.identity.agentName)) {
28
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
29
+ writeFileSync(path, `${JSON.stringify(profile, null, 2)}\n`, { mode: 0o600 });
30
+ }
31
+ export function buildClaimRequest(input) {
32
+ return {
33
+ agentName: input.identity.agentName,
34
+ publicKey: input.identity.publicKey,
35
+ keyType: input.identity.keyType,
36
+ runtime: input.runtime,
37
+ capabilities: input.capabilities?.length ? input.capabilities : ["chat", "tasks:receive", "messages:send"],
38
+ transport: { relay: true, publicUrl: input.publicUrl },
39
+ metadata: input.metadata,
40
+ };
41
+ }
42
+ export function claimUrlFromInvite(inviteUrl) {
43
+ const trimmed = inviteUrl.replace(/\/+$/, "");
44
+ return `${trimmed}/claim`;
45
+ }
46
+ export async function claimPairingInvite(inviteUrl, request) {
47
+ const response = await fetch(claimUrlFromInvite(inviteUrl), {
48
+ method: "POST",
49
+ headers: { "content-type": "application/json" },
50
+ body: JSON.stringify(request),
51
+ });
52
+ const body = await response.text();
53
+ let parsed;
54
+ try {
55
+ parsed = body ? JSON.parse(body) : {};
56
+ }
57
+ catch {
58
+ throw new Error(`Pairing server returned non-JSON response (${response.status}): ${body}`);
59
+ }
60
+ if (!response.ok) {
61
+ const message = typeof parsed === "object" && parsed && "error" in parsed ? String(parsed.error) : body;
62
+ throw new Error(`Pairing claim failed (${response.status}): ${message}`);
63
+ }
64
+ return parsed;
65
+ }
66
+ //# sourceMappingURL=pairing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing.js","sourceRoot":"","sources":["../src/pairing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * A2A Relay Client
3
+ *
4
+ * WebSocket client that connects to an A2A Relay Hub for NAT-friendly
5
+ * bidirectional communication. Handles registration, auto-reconnect
6
+ * with exponential backoff, heartbeats, and message routing.
7
+ */
8
+ import WebSocket from "ws";
9
+ import { randomUUID } from "crypto";
10
+ const MIN_RECONNECT_MS = 1_000;
11
+ const MAX_RECONNECT_MS = 60_000;
12
+ const HEARTBEAT_INTERVAL_MS = 25_000;
13
+ export class RelayClient {
14
+ ws = null;
15
+ options;
16
+ logger;
17
+ reconnectMs = MIN_RECONNECT_MS;
18
+ reconnectTimer = null;
19
+ heartbeatTimer = null;
20
+ connected = false;
21
+ stopped = false;
22
+ pendingCallbacks = new Map();
23
+ constructor(options) {
24
+ this.options = options;
25
+ this.logger = options.logger || { info: console.log, error: console.error };
26
+ }
27
+ isConnected() {
28
+ return this.connected;
29
+ }
30
+ start() {
31
+ this.stopped = false;
32
+ this.connect();
33
+ }
34
+ stop() {
35
+ this.stopped = true;
36
+ this.clearTimers();
37
+ if (this.ws) {
38
+ this.ws.close(1000, "Client shutting down");
39
+ this.ws = null;
40
+ }
41
+ this.connected = false;
42
+ }
43
+ /** Send a task to another agent via the relay */
44
+ async sendTask(targetAgent, payload) {
45
+ return this.sendAndWait({
46
+ type: "task",
47
+ to: targetAgent,
48
+ id: randomUUID(),
49
+ from: this.options.agentName,
50
+ payload,
51
+ timestamp: new Date().toISOString(),
52
+ }, "task_response", 60_000);
53
+ }
54
+ /** Discover agents connected to the relay */
55
+ async discover() {
56
+ const response = await this.sendAndWait({
57
+ type: "discover",
58
+ id: randomUUID(),
59
+ from: this.options.agentName,
60
+ payload: {},
61
+ timestamp: new Date().toISOString(),
62
+ }, "discover_response", 10_000);
63
+ return response.payload.agents || [];
64
+ }
65
+ /** Send a task response back through the relay */
66
+ sendTaskResponse(originalMsg, result) {
67
+ const response = {
68
+ type: "task_response",
69
+ id: originalMsg.id,
70
+ from: this.options.agentName,
71
+ to: originalMsg.from,
72
+ payload: result,
73
+ timestamp: new Date().toISOString(),
74
+ };
75
+ this.send(response);
76
+ }
77
+ connect() {
78
+ if (this.stopped)
79
+ return;
80
+ const url = this.options.relay.url;
81
+ this.logger.info(`[perkos-a2a] Connecting to relay: ${url}`);
82
+ try {
83
+ this.ws = new WebSocket(url);
84
+ }
85
+ catch (err) {
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ this.logger.error(`[perkos-a2a] Failed to create WebSocket: ${msg}`);
88
+ this.scheduleReconnect();
89
+ return;
90
+ }
91
+ this.ws.on("open", () => {
92
+ this.connected = true;
93
+ this.reconnectMs = MIN_RECONNECT_MS;
94
+ this.logger.info("[perkos-a2a] Connected to relay hub");
95
+ this.register();
96
+ this.startHeartbeat();
97
+ });
98
+ this.ws.on("message", (data) => {
99
+ let msg;
100
+ try {
101
+ msg = JSON.parse(data.toString());
102
+ }
103
+ catch {
104
+ this.logger.error("[perkos-a2a] Failed to parse relay message");
105
+ return;
106
+ }
107
+ this.handleMessage(msg);
108
+ });
109
+ this.ws.on("close", (code, reason) => {
110
+ this.connected = false;
111
+ this.clearTimers();
112
+ if (!this.stopped) {
113
+ this.logger.info(`[perkos-a2a] Relay connection closed (${code}: ${reason || "no reason"}). Reconnecting...`);
114
+ this.scheduleReconnect();
115
+ }
116
+ });
117
+ this.ws.on("error", (err) => {
118
+ this.logger.error(`[perkos-a2a] Relay WebSocket error: ${err.message}`);
119
+ });
120
+ }
121
+ register() {
122
+ const msg = {
123
+ type: "register",
124
+ id: randomUUID(),
125
+ from: this.options.agentName,
126
+ payload: {
127
+ agentName: this.options.agentName,
128
+ apiKey: this.options.relay.apiKey,
129
+ card: this.options.card,
130
+ },
131
+ timestamp: new Date().toISOString(),
132
+ };
133
+ this.send(msg);
134
+ }
135
+ handleMessage(msg) {
136
+ // Check for pending request/response callbacks
137
+ const callback = this.pendingCallbacks.get(msg.id);
138
+ if (callback) {
139
+ this.pendingCallbacks.delete(msg.id);
140
+ callback(msg);
141
+ return;
142
+ }
143
+ switch (msg.type) {
144
+ case "register_ack":
145
+ this.logger.info("[perkos-a2a] Registered with relay hub");
146
+ break;
147
+ case "task":
148
+ this.options.onTask(msg);
149
+ break;
150
+ case "task_response":
151
+ // Late response with no pending callback; log and discard
152
+ this.logger.info(`[perkos-a2a] Received unmatched task response: ${msg.id}`);
153
+ break;
154
+ case "discover_response":
155
+ if (this.options.onDiscoverResponse) {
156
+ this.options.onDiscoverResponse(msg.payload.agents);
157
+ }
158
+ break;
159
+ case "heartbeat_ack":
160
+ break;
161
+ case "error":
162
+ this.logger.error(`[perkos-a2a] Relay error: ${msg.payload.code} - ${msg.payload.message}`);
163
+ break;
164
+ default:
165
+ this.logger.info(`[perkos-a2a] Unknown relay message type: ${msg.type}`);
166
+ }
167
+ }
168
+ send(msg) {
169
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
170
+ this.ws.send(JSON.stringify(msg));
171
+ }
172
+ }
173
+ async sendAndWait(msg, expectedType, timeoutMs) {
174
+ return new Promise((resolve, reject) => {
175
+ const timer = setTimeout(() => {
176
+ this.pendingCallbacks.delete(msg.id);
177
+ reject(new Error(`Relay request timed out after ${timeoutMs}ms`));
178
+ }, timeoutMs);
179
+ this.pendingCallbacks.set(msg.id, (response) => {
180
+ clearTimeout(timer);
181
+ if (response.type === "error") {
182
+ reject(new Error(`Relay error: ${response.payload.message}`));
183
+ }
184
+ else {
185
+ resolve(response);
186
+ }
187
+ });
188
+ this.send(msg);
189
+ });
190
+ }
191
+ startHeartbeat() {
192
+ this.heartbeatTimer = setInterval(() => {
193
+ const msg = {
194
+ type: "heartbeat",
195
+ id: randomUUID(),
196
+ from: this.options.agentName,
197
+ payload: {},
198
+ timestamp: new Date().toISOString(),
199
+ };
200
+ this.send(msg);
201
+ }, HEARTBEAT_INTERVAL_MS);
202
+ }
203
+ scheduleReconnect() {
204
+ if (this.stopped)
205
+ return;
206
+ this.reconnectTimer = setTimeout(() => {
207
+ this.connect();
208
+ }, this.reconnectMs);
209
+ this.reconnectMs = Math.min(this.reconnectMs * 2, MAX_RECONNECT_MS);
210
+ }
211
+ clearTimers() {
212
+ if (this.heartbeatTimer) {
213
+ clearInterval(this.heartbeatTimer);
214
+ this.heartbeatTimer = null;
215
+ }
216
+ if (this.reconnectTimer) {
217
+ clearTimeout(this.reconnectTimer);
218
+ this.reconnectTimer = null;
219
+ }
220
+ }
221
+ }
222
+ //# sourceMappingURL=relay-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAcpC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC,MAAM,OAAO,WAAW;IACd,EAAE,GAAqB,IAAI,CAAC;IAC5B,OAAO,CAAqB;IAC5B,MAAM,CAA8E;IACpF,WAAW,GAAG,gBAAgB,CAAC;IAC/B,cAAc,GAAyC,IAAI,CAAC;IAC5D,cAAc,GAA0C,IAAI,CAAC;IAC7D,SAAS,GAAG,KAAK,CAAC;IAClB,OAAO,GAAG,KAAK,CAAC;IAChB,gBAAgB,GAA6C,IAAI,GAAG,EAAE,CAAC;IAE/E,YAAY,OAA2B;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;IAC9E,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,WAAW,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;YAC5C,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,QAAQ,CAAC,WAAmB,EAAE,OAAgC;QAClE,OAAO,IAAI,CAAC,WAAW,CAAC;YACtB,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,WAAW;YACf,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC5B,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,QAAQ;QACZ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC;YACtC,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE,EAAE;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAChC,OAAQ,QAAQ,CAAC,OAAO,CAAC,MAA4B,IAAI,EAAE,CAAC;IAC9D,CAAC;IAED,kDAAkD;IAClD,gBAAgB,CAAC,WAAyB,EAAE,MAA+B;QACzE,MAAM,QAAQ,GAAiB;YAC7B,IAAI,EAAE,eAAe;YACrB,EAAE,EAAE,WAAW,CAAC,EAAE;YAClB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC5B,EAAE,EAAE,WAAW,CAAC,IAAI;YACpB,OAAO,EAAE,MAAM;YACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,GAAG,EAAE,CAAC,CAAC;QAE7D,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,GAAG,EAAE,CAAC,CAAC;YACrE,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACtB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;YACxD,IAAI,CAAC,QAAQ,EAAE,CAAC;YAChB,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YAC7B,IAAI,GAAiB,CAAC;YACtB,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;gBAChE,OAAO;YACT,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,IAAI,KAAK,MAAM,IAAI,WAAW,oBAAoB,CAAC,CAAC;gBAC9G,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAC1B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,QAAQ;QACd,MAAM,GAAG,GAAiB;YACxB,IAAI,EAAE,UAAU;YAChB,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;YAC5B,OAAO,EAAE;gBACP,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;gBACjC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM;gBACjC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;aACxB;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IAEO,aAAa,CAAC,GAAiB;QACrC,+CAA+C;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,cAAc;gBACjB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;gBAC3D,MAAM;YACR,KAAK,MAAM;gBACT,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACzB,MAAM;YACR,KAAK,eAAe;gBAClB,0DAA0D;gBAC1D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kDAAkD,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC7E,MAAM;YACR,KAAK,mBAAmB;gBACtB,IAAI,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;oBACpC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,MAA2B,CAAC,CAAC;gBAC3E,CAAC;gBACD,MAAM;YACR,KAAK,eAAe;gBAClB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC5F,MAAM;YACR;gBACE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4CAA4C,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,GAAiB;QAC5B,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACrD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CACvB,GAAiB,EACjB,YAAoB,EACpB,SAAiB;QAEjB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACrC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,SAAS,IAAI,CAAC,CAAC,CAAC;YACpE,CAAC,EAAE,SAAS,CAAC,CAAC;YAEd,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAE;gBAC7C,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAChE,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,MAAM,GAAG,GAAiB;gBACxB,IAAI,EAAE,WAAW;gBACjB,EAAE,EAAE,UAAU,EAAE;gBAChB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;gBAC5B,OAAO,EAAE,EAAE;gBACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC5B,CAAC;IAEO,iBAAiB;QACvB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACtE,CAAC;IAEO,WAAW;QACjB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;CACF"}
package/dist/relay.js ADDED
@@ -0,0 +1,274 @@
1
+ /**
2
+ * A2A Relay Hub
3
+ *
4
+ * Lightweight WebSocket broker for NAT traversal. Agents connect outbound
5
+ * to the relay, which routes messages between them. Supports API key auth,
6
+ * rate limiting, agent presence registry, and offline message queuing.
7
+ *
8
+ * Can run standalone via bin/relay.ts or embedded in full mode.
9
+ */
10
+ import { WebSocketServer, WebSocket } from "ws";
11
+ import { randomUUID } from "crypto";
12
+ const DEFAULT_CONFIG = {
13
+ port: 6060,
14
+ apiKeys: [],
15
+ registeredAgents: {},
16
+ maxQueuePerAgent: 200,
17
+ rateLimitPerMinute: 60,
18
+ heartbeatIntervalMs: 30_000,
19
+ heartbeatTimeoutMs: 90_000,
20
+ };
21
+ export class RelayHub {
22
+ wss = null;
23
+ agents = new Map();
24
+ offlineQueue = new Map();
25
+ rateCounts = new Map();
26
+ config;
27
+ heartbeatTimer = null;
28
+ logger;
29
+ constructor(config, logger) {
30
+ this.config = { ...DEFAULT_CONFIG, ...config };
31
+ this.logger = logger || { info: console.log, error: console.error };
32
+ }
33
+ start() {
34
+ this.wss = new WebSocketServer({ port: this.config.port });
35
+ this.logger.info(`[perkos-a2a] Relay hub listening on port ${this.config.port}`);
36
+ this.wss.on("connection", (ws, req) => {
37
+ const remoteAddr = req.socket.remoteAddress || "unknown";
38
+ this.logger.info(`[perkos-a2a] Relay connection from ${remoteAddr}`);
39
+ this.handleConnection(ws);
40
+ });
41
+ this.heartbeatTimer = setInterval(() => this.checkHeartbeats(), this.config.heartbeatIntervalMs);
42
+ }
43
+ stop() {
44
+ if (this.heartbeatTimer) {
45
+ clearInterval(this.heartbeatTimer);
46
+ this.heartbeatTimer = null;
47
+ }
48
+ if (this.wss) {
49
+ for (const agent of this.agents.values()) {
50
+ agent.ws.close(1001, "Relay shutting down");
51
+ }
52
+ this.wss.close();
53
+ this.wss = null;
54
+ this.logger.info("[perkos-a2a] Relay hub stopped");
55
+ }
56
+ }
57
+ getRegistry() {
58
+ const entries = [];
59
+ for (const agent of this.agents.values()) {
60
+ entries.push({
61
+ name: agent.name,
62
+ connectedAt: agent.connectedAt,
63
+ lastHeartbeat: agent.lastHeartbeat,
64
+ card: agent.card,
65
+ });
66
+ }
67
+ return entries;
68
+ }
69
+ handleConnection(ws) {
70
+ let agentName = null;
71
+ ws.on("message", (data) => {
72
+ let msg;
73
+ try {
74
+ msg = JSON.parse(data.toString());
75
+ }
76
+ catch {
77
+ this.sendError(ws, "invalid_json", "Failed to parse message");
78
+ return;
79
+ }
80
+ if (msg.type === "register") {
81
+ agentName = this.handleRegister(ws, msg);
82
+ return;
83
+ }
84
+ if (!agentName) {
85
+ this.sendError(ws, "not_registered", "Must register before sending messages");
86
+ return;
87
+ }
88
+ if (!this.checkRateLimit(agentName)) {
89
+ this.sendError(ws, "rate_limited", "Rate limit exceeded");
90
+ return;
91
+ }
92
+ switch (msg.type) {
93
+ case "task":
94
+ case "task_response":
95
+ this.routeMessage(agentName, msg);
96
+ break;
97
+ case "discover":
98
+ this.handleDiscover(ws, msg);
99
+ break;
100
+ case "heartbeat":
101
+ this.handleHeartbeat(agentName, msg);
102
+ break;
103
+ default:
104
+ this.sendError(ws, "unknown_type", `Unknown message type: ${msg.type}`);
105
+ }
106
+ });
107
+ ws.on("close", () => {
108
+ if (agentName) {
109
+ this.agents.delete(agentName);
110
+ this.logger.info(`[perkos-a2a] Agent disconnected: ${agentName}`);
111
+ }
112
+ });
113
+ ws.on("error", (err) => {
114
+ this.logger.error(`[perkos-a2a] WebSocket error for ${agentName || "unknown"}: ${err.message}`);
115
+ });
116
+ }
117
+ handleRegister(ws, msg) {
118
+ const name = msg.payload.agentName;
119
+ const apiKey = msg.payload.apiKey;
120
+ if (!name) {
121
+ this.sendError(ws, "missing_name", "agentName is required for registration");
122
+ return null;
123
+ }
124
+ const approvedKey = this.config.registeredAgents[name];
125
+ if (Object.keys(this.config.registeredAgents).length > 0) {
126
+ if (!approvedKey || approvedKey !== apiKey) {
127
+ this.sendError(ws, "not_approved", "Agent is not approved for this relay or API key does not match");
128
+ ws.close(4001, "Agent not approved");
129
+ return null;
130
+ }
131
+ }
132
+ else if (this.config.apiKeys.length > 0 && !this.config.apiKeys.includes(apiKey)) {
133
+ this.sendError(ws, "auth_failed", "Invalid API key");
134
+ ws.close(4001, "Authentication failed");
135
+ return null;
136
+ }
137
+ // Disconnect existing connection for same agent name
138
+ const existing = this.agents.get(name);
139
+ if (existing) {
140
+ existing.ws.close(4002, "Replaced by new connection");
141
+ }
142
+ const now = new Date().toISOString();
143
+ this.agents.set(name, {
144
+ name,
145
+ ws,
146
+ apiKey: apiKey || "",
147
+ card: msg.payload.card,
148
+ connectedAt: now,
149
+ lastHeartbeat: now,
150
+ });
151
+ this.logger.info(`[perkos-a2a] Agent registered: ${name}`);
152
+ const ack = {
153
+ type: "register_ack",
154
+ id: msg.id,
155
+ from: "relay",
156
+ payload: { status: "ok", agentName: name },
157
+ timestamp: now,
158
+ };
159
+ ws.send(JSON.stringify(ack));
160
+ // Deliver queued messages
161
+ this.drainQueue(name, ws);
162
+ return name;
163
+ }
164
+ routeMessage(fromAgent, msg) {
165
+ const target = msg.to;
166
+ if (!target) {
167
+ this.sendError(this.agents.get(fromAgent).ws, "missing_target", "Message requires 'to' field");
168
+ return;
169
+ }
170
+ msg.from = fromAgent;
171
+ msg.timestamp = new Date().toISOString();
172
+ if (Object.keys(this.config.registeredAgents).length > 0 && !(target in this.config.registeredAgents)) {
173
+ this.sendError(this.agents.get(fromAgent).ws, "target_not_approved", `Target agent ${target} is not approved on this relay`);
174
+ return;
175
+ }
176
+ const targetAgent = this.agents.get(target);
177
+ if (targetAgent && targetAgent.ws.readyState === WebSocket.OPEN) {
178
+ targetAgent.ws.send(JSON.stringify(msg));
179
+ }
180
+ else {
181
+ // Queue for offline delivery
182
+ if (!this.offlineQueue.has(target)) {
183
+ this.offlineQueue.set(target, []);
184
+ }
185
+ const queue = this.offlineQueue.get(target);
186
+ if (queue.length < this.config.maxQueuePerAgent) {
187
+ queue.push({ message: msg, queuedAt: new Date().toISOString() });
188
+ this.logger.info(`[perkos-a2a] Queued message for offline agent: ${target} (${queue.length} pending)`);
189
+ }
190
+ else {
191
+ this.logger.info(`[perkos-a2a] Queue full for agent: ${target}, dropping message`);
192
+ const sender = this.agents.get(fromAgent);
193
+ if (sender) {
194
+ this.sendError(sender.ws, "queue_full", `Target agent ${target} queue is full`);
195
+ }
196
+ }
197
+ }
198
+ }
199
+ drainQueue(agentName, ws) {
200
+ const queue = this.offlineQueue.get(agentName);
201
+ if (!queue || queue.length === 0)
202
+ return;
203
+ this.logger.info(`[perkos-a2a] Delivering ${queue.length} queued messages to ${agentName}`);
204
+ for (const item of queue) {
205
+ if (ws.readyState === WebSocket.OPEN) {
206
+ ws.send(JSON.stringify(item.message));
207
+ }
208
+ }
209
+ this.offlineQueue.delete(agentName);
210
+ }
211
+ handleDiscover(ws, msg) {
212
+ const agents = this.getRegistry();
213
+ const response = {
214
+ type: "discover_response",
215
+ id: msg.id,
216
+ from: "relay",
217
+ payload: { agents },
218
+ timestamp: new Date().toISOString(),
219
+ };
220
+ ws.send(JSON.stringify(response));
221
+ }
222
+ handleHeartbeat(agentName, msg) {
223
+ const agent = this.agents.get(agentName);
224
+ if (agent) {
225
+ agent.lastHeartbeat = new Date().toISOString();
226
+ }
227
+ const ack = {
228
+ type: "heartbeat_ack",
229
+ id: msg.id,
230
+ from: "relay",
231
+ payload: {},
232
+ timestamp: new Date().toISOString(),
233
+ };
234
+ const ws = agent?.ws;
235
+ if (ws && ws.readyState === WebSocket.OPEN) {
236
+ ws.send(JSON.stringify(ack));
237
+ }
238
+ }
239
+ checkHeartbeats() {
240
+ const now = Date.now();
241
+ for (const [name, agent] of this.agents) {
242
+ const lastBeat = new Date(agent.lastHeartbeat).getTime();
243
+ if (now - lastBeat > this.config.heartbeatTimeoutMs) {
244
+ this.logger.info(`[perkos-a2a] Agent ${name} timed out (no heartbeat)`);
245
+ agent.ws.close(4003, "Heartbeat timeout");
246
+ this.agents.delete(name);
247
+ }
248
+ }
249
+ }
250
+ checkRateLimit(agentName) {
251
+ const now = Date.now();
252
+ const window = 60_000;
253
+ let entry = this.rateCounts.get(agentName);
254
+ if (!entry || now - entry.windowStart > window) {
255
+ entry = { count: 0, windowStart: now };
256
+ this.rateCounts.set(agentName, entry);
257
+ }
258
+ entry.count++;
259
+ return entry.count <= this.config.rateLimitPerMinute;
260
+ }
261
+ sendError(ws, code, message) {
262
+ const errorMsg = {
263
+ type: "error",
264
+ id: randomUUID(),
265
+ from: "relay",
266
+ payload: { code, message },
267
+ timestamp: new Date().toISOString(),
268
+ };
269
+ if (ws.readyState === WebSocket.OPEN) {
270
+ ws.send(JSON.stringify(errorMsg));
271
+ }
272
+ }
273
+ }
274
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}