@perkos/perkos-a2a 0.3.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +207 -536
- package/bin/relay.ts +88 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -9
- package/dist/index.js.map +1 -1
- package/dist/relay-client.d.ts +51 -0
- package/dist/relay-client.d.ts.map +1 -0
- package/dist/relay-client.js +222 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/relay.d.ts +49 -0
- package/dist/relay.d.ts.map +1 -0
- package/dist/relay.js +261 -0
- package/dist/relay.js.map +1 -0
- package/dist/server.d.ts +29 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +288 -65
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -1
- package/openclaw.plugin.json +66 -30
- package/package.json +9 -3
- package/skills/perkos-a2a/SKILL.md +68 -0
package/dist/relay.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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 type { RelayAgentEntry } from "./types.js";
|
|
11
|
+
export interface RelayHubConfig {
|
|
12
|
+
port: number;
|
|
13
|
+
/** Accepted API keys. If empty, auth is disabled. */
|
|
14
|
+
apiKeys: string[];
|
|
15
|
+
/** Maximum queued messages per offline agent */
|
|
16
|
+
maxQueuePerAgent: number;
|
|
17
|
+
/** Rate limit: max messages per agent per minute */
|
|
18
|
+
rateLimitPerMinute: number;
|
|
19
|
+
/** Heartbeat interval in milliseconds */
|
|
20
|
+
heartbeatIntervalMs: number;
|
|
21
|
+
/** Consider agent dead after this many missed heartbeats */
|
|
22
|
+
heartbeatTimeoutMs: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class RelayHub {
|
|
25
|
+
private wss;
|
|
26
|
+
private agents;
|
|
27
|
+
private offlineQueue;
|
|
28
|
+
private rateCounts;
|
|
29
|
+
private config;
|
|
30
|
+
private heartbeatTimer;
|
|
31
|
+
private logger;
|
|
32
|
+
constructor(config?: Partial<RelayHubConfig>, logger?: {
|
|
33
|
+
info: (...args: unknown[]) => void;
|
|
34
|
+
error: (...args: unknown[]) => void;
|
|
35
|
+
});
|
|
36
|
+
start(): void;
|
|
37
|
+
stop(): void;
|
|
38
|
+
getRegistry(): RelayAgentEntry[];
|
|
39
|
+
private handleConnection;
|
|
40
|
+
private handleRegister;
|
|
41
|
+
private routeMessage;
|
|
42
|
+
private drainQueue;
|
|
43
|
+
private handleDiscover;
|
|
44
|
+
private handleHeartbeat;
|
|
45
|
+
private checkHeartbeats;
|
|
46
|
+
private checkRateLimit;
|
|
47
|
+
private sendError;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAgB,eAAe,EAAa,MAAM,YAAY,CAAC;AAgB3E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gDAAgD;IAChD,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAWD,qBAAa,QAAQ;IACnB,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,UAAU,CAAkE;IACpF,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,MAAM,CAA8E;gBAG1F,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,EAChC,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE;IAMtF,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAeZ,WAAW,IAAI,eAAe,EAAE;IAahC,OAAO,CAAC,gBAAgB;IAuDxB,OAAO,CAAC,cAAc;IAgDtB,OAAO,CAAC,YAAY;IAgCpB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,SAAS;CAYlB"}
|
package/dist/relay.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
maxQueuePerAgent: 200,
|
|
16
|
+
rateLimitPerMinute: 60,
|
|
17
|
+
heartbeatIntervalMs: 30_000,
|
|
18
|
+
heartbeatTimeoutMs: 90_000,
|
|
19
|
+
};
|
|
20
|
+
export class RelayHub {
|
|
21
|
+
wss = null;
|
|
22
|
+
agents = new Map();
|
|
23
|
+
offlineQueue = new Map();
|
|
24
|
+
rateCounts = new Map();
|
|
25
|
+
config;
|
|
26
|
+
heartbeatTimer = null;
|
|
27
|
+
logger;
|
|
28
|
+
constructor(config, logger) {
|
|
29
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
30
|
+
this.logger = logger || { info: console.log, error: console.error };
|
|
31
|
+
}
|
|
32
|
+
start() {
|
|
33
|
+
this.wss = new WebSocketServer({ port: this.config.port });
|
|
34
|
+
this.logger.info(`[perkos-a2a] Relay hub listening on port ${this.config.port}`);
|
|
35
|
+
this.wss.on("connection", (ws, req) => {
|
|
36
|
+
const remoteAddr = req.socket.remoteAddress || "unknown";
|
|
37
|
+
this.logger.info(`[perkos-a2a] Relay connection from ${remoteAddr}`);
|
|
38
|
+
this.handleConnection(ws);
|
|
39
|
+
});
|
|
40
|
+
this.heartbeatTimer = setInterval(() => this.checkHeartbeats(), this.config.heartbeatIntervalMs);
|
|
41
|
+
}
|
|
42
|
+
stop() {
|
|
43
|
+
if (this.heartbeatTimer) {
|
|
44
|
+
clearInterval(this.heartbeatTimer);
|
|
45
|
+
this.heartbeatTimer = null;
|
|
46
|
+
}
|
|
47
|
+
if (this.wss) {
|
|
48
|
+
for (const agent of this.agents.values()) {
|
|
49
|
+
agent.ws.close(1001, "Relay shutting down");
|
|
50
|
+
}
|
|
51
|
+
this.wss.close();
|
|
52
|
+
this.wss = null;
|
|
53
|
+
this.logger.info("[perkos-a2a] Relay hub stopped");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getRegistry() {
|
|
57
|
+
const entries = [];
|
|
58
|
+
for (const agent of this.agents.values()) {
|
|
59
|
+
entries.push({
|
|
60
|
+
name: agent.name,
|
|
61
|
+
connectedAt: agent.connectedAt,
|
|
62
|
+
lastHeartbeat: agent.lastHeartbeat,
|
|
63
|
+
card: agent.card,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return entries;
|
|
67
|
+
}
|
|
68
|
+
handleConnection(ws) {
|
|
69
|
+
let agentName = null;
|
|
70
|
+
ws.on("message", (data) => {
|
|
71
|
+
let msg;
|
|
72
|
+
try {
|
|
73
|
+
msg = JSON.parse(data.toString());
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
this.sendError(ws, "invalid_json", "Failed to parse message");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (msg.type === "register") {
|
|
80
|
+
agentName = this.handleRegister(ws, msg);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!agentName) {
|
|
84
|
+
this.sendError(ws, "not_registered", "Must register before sending messages");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!this.checkRateLimit(agentName)) {
|
|
88
|
+
this.sendError(ws, "rate_limited", "Rate limit exceeded");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
switch (msg.type) {
|
|
92
|
+
case "task":
|
|
93
|
+
case "task_response":
|
|
94
|
+
this.routeMessage(agentName, msg);
|
|
95
|
+
break;
|
|
96
|
+
case "discover":
|
|
97
|
+
this.handleDiscover(ws, msg);
|
|
98
|
+
break;
|
|
99
|
+
case "heartbeat":
|
|
100
|
+
this.handleHeartbeat(agentName, msg);
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
this.sendError(ws, "unknown_type", `Unknown message type: ${msg.type}`);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
ws.on("close", () => {
|
|
107
|
+
if (agentName) {
|
|
108
|
+
this.agents.delete(agentName);
|
|
109
|
+
this.logger.info(`[perkos-a2a] Agent disconnected: ${agentName}`);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
ws.on("error", (err) => {
|
|
113
|
+
this.logger.error(`[perkos-a2a] WebSocket error for ${agentName || "unknown"}: ${err.message}`);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
handleRegister(ws, msg) {
|
|
117
|
+
const name = msg.payload.agentName;
|
|
118
|
+
const apiKey = msg.payload.apiKey;
|
|
119
|
+
if (!name) {
|
|
120
|
+
this.sendError(ws, "missing_name", "agentName is required for registration");
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
if (this.config.apiKeys.length > 0 && !this.config.apiKeys.includes(apiKey)) {
|
|
124
|
+
this.sendError(ws, "auth_failed", "Invalid API key");
|
|
125
|
+
ws.close(4001, "Authentication failed");
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
// Disconnect existing connection for same agent name
|
|
129
|
+
const existing = this.agents.get(name);
|
|
130
|
+
if (existing) {
|
|
131
|
+
existing.ws.close(4002, "Replaced by new connection");
|
|
132
|
+
}
|
|
133
|
+
const now = new Date().toISOString();
|
|
134
|
+
this.agents.set(name, {
|
|
135
|
+
name,
|
|
136
|
+
ws,
|
|
137
|
+
apiKey: apiKey || "",
|
|
138
|
+
card: msg.payload.card,
|
|
139
|
+
connectedAt: now,
|
|
140
|
+
lastHeartbeat: now,
|
|
141
|
+
});
|
|
142
|
+
this.logger.info(`[perkos-a2a] Agent registered: ${name}`);
|
|
143
|
+
const ack = {
|
|
144
|
+
type: "register_ack",
|
|
145
|
+
id: msg.id,
|
|
146
|
+
from: "relay",
|
|
147
|
+
payload: { status: "ok", agentName: name },
|
|
148
|
+
timestamp: now,
|
|
149
|
+
};
|
|
150
|
+
ws.send(JSON.stringify(ack));
|
|
151
|
+
// Deliver queued messages
|
|
152
|
+
this.drainQueue(name, ws);
|
|
153
|
+
return name;
|
|
154
|
+
}
|
|
155
|
+
routeMessage(fromAgent, msg) {
|
|
156
|
+
const target = msg.to;
|
|
157
|
+
if (!target) {
|
|
158
|
+
this.sendError(this.agents.get(fromAgent).ws, "missing_target", "Message requires 'to' field");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
msg.from = fromAgent;
|
|
162
|
+
msg.timestamp = new Date().toISOString();
|
|
163
|
+
const targetAgent = this.agents.get(target);
|
|
164
|
+
if (targetAgent && targetAgent.ws.readyState === WebSocket.OPEN) {
|
|
165
|
+
targetAgent.ws.send(JSON.stringify(msg));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Queue for offline delivery
|
|
169
|
+
if (!this.offlineQueue.has(target)) {
|
|
170
|
+
this.offlineQueue.set(target, []);
|
|
171
|
+
}
|
|
172
|
+
const queue = this.offlineQueue.get(target);
|
|
173
|
+
if (queue.length < this.config.maxQueuePerAgent) {
|
|
174
|
+
queue.push({ message: msg, queuedAt: new Date().toISOString() });
|
|
175
|
+
this.logger.info(`[perkos-a2a] Queued message for offline agent: ${target} (${queue.length} pending)`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.logger.info(`[perkos-a2a] Queue full for agent: ${target}, dropping message`);
|
|
179
|
+
const sender = this.agents.get(fromAgent);
|
|
180
|
+
if (sender) {
|
|
181
|
+
this.sendError(sender.ws, "queue_full", `Target agent ${target} queue is full`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
drainQueue(agentName, ws) {
|
|
187
|
+
const queue = this.offlineQueue.get(agentName);
|
|
188
|
+
if (!queue || queue.length === 0)
|
|
189
|
+
return;
|
|
190
|
+
this.logger.info(`[perkos-a2a] Delivering ${queue.length} queued messages to ${agentName}`);
|
|
191
|
+
for (const item of queue) {
|
|
192
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
193
|
+
ws.send(JSON.stringify(item.message));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.offlineQueue.delete(agentName);
|
|
197
|
+
}
|
|
198
|
+
handleDiscover(ws, msg) {
|
|
199
|
+
const agents = this.getRegistry();
|
|
200
|
+
const response = {
|
|
201
|
+
type: "discover_response",
|
|
202
|
+
id: msg.id,
|
|
203
|
+
from: "relay",
|
|
204
|
+
payload: { agents },
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
};
|
|
207
|
+
ws.send(JSON.stringify(response));
|
|
208
|
+
}
|
|
209
|
+
handleHeartbeat(agentName, msg) {
|
|
210
|
+
const agent = this.agents.get(agentName);
|
|
211
|
+
if (agent) {
|
|
212
|
+
agent.lastHeartbeat = new Date().toISOString();
|
|
213
|
+
}
|
|
214
|
+
const ack = {
|
|
215
|
+
type: "heartbeat_ack",
|
|
216
|
+
id: msg.id,
|
|
217
|
+
from: "relay",
|
|
218
|
+
payload: {},
|
|
219
|
+
timestamp: new Date().toISOString(),
|
|
220
|
+
};
|
|
221
|
+
const ws = agent?.ws;
|
|
222
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
223
|
+
ws.send(JSON.stringify(ack));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
checkHeartbeats() {
|
|
227
|
+
const now = Date.now();
|
|
228
|
+
for (const [name, agent] of this.agents) {
|
|
229
|
+
const lastBeat = new Date(agent.lastHeartbeat).getTime();
|
|
230
|
+
if (now - lastBeat > this.config.heartbeatTimeoutMs) {
|
|
231
|
+
this.logger.info(`[perkos-a2a] Agent ${name} timed out (no heartbeat)`);
|
|
232
|
+
agent.ws.close(4003, "Heartbeat timeout");
|
|
233
|
+
this.agents.delete(name);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
checkRateLimit(agentName) {
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
const window = 60_000;
|
|
240
|
+
let entry = this.rateCounts.get(agentName);
|
|
241
|
+
if (!entry || now - entry.windowStart > window) {
|
|
242
|
+
entry = { count: 0, windowStart: now };
|
|
243
|
+
this.rateCounts.set(agentName, entry);
|
|
244
|
+
}
|
|
245
|
+
entry.count++;
|
|
246
|
+
return entry.count <= this.config.rateLimitPerMinute;
|
|
247
|
+
}
|
|
248
|
+
sendError(ws, code, message) {
|
|
249
|
+
const errorMsg = {
|
|
250
|
+
type: "error",
|
|
251
|
+
id: randomUUID(),
|
|
252
|
+
from: "relay",
|
|
253
|
+
payload: { code, message },
|
|
254
|
+
timestamp: new Date().toISOString(),
|
|
255
|
+
};
|
|
256
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
257
|
+
ws.send(JSON.stringify(errorMsg));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,35 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A2A Protocol Server
|
|
3
|
-
* Express-based JSON-RPC 2.0 server with Agent Card discovery
|
|
3
|
+
* Express-based JSON-RPC 2.0 server with Agent Card discovery.
|
|
4
|
+
* Supports full, client-only, relay, and auto modes.
|
|
5
|
+
* Integrates with the relay hub for NAT traversal when configured.
|
|
4
6
|
*/
|
|
5
7
|
import express from "express";
|
|
6
8
|
import type { AgentCard, JsonRpcResponse, A2APluginConfig } from "./types.js";
|
|
9
|
+
/** Detect if the host is behind NAT by comparing public IP to local interfaces */
|
|
10
|
+
export declare function detectNetworking(): Promise<{
|
|
11
|
+
isBehindNat: boolean;
|
|
12
|
+
publicIp: string | null;
|
|
13
|
+
localIps: string[];
|
|
14
|
+
hasTailscale: boolean;
|
|
15
|
+
tailscaleIp: string | null;
|
|
16
|
+
}>;
|
|
7
17
|
export declare class A2AServer {
|
|
8
18
|
private app;
|
|
9
19
|
private tasks;
|
|
10
20
|
private agentCard;
|
|
11
21
|
private config;
|
|
12
22
|
private logger;
|
|
23
|
+
private clientOnly;
|
|
24
|
+
private relayClient;
|
|
25
|
+
private relayHub;
|
|
26
|
+
private messageInjector;
|
|
13
27
|
constructor(config: A2APluginConfig, logger?: {
|
|
14
28
|
info: (...args: unknown[]) => void;
|
|
15
29
|
error: (...args: unknown[]) => void;
|
|
16
30
|
});
|
|
31
|
+
/** Set the message injector for delivering tasks into the agent session */
|
|
32
|
+
setMessageInjector(injector: (text: string, metadata?: Record<string, unknown>) => void): void;
|
|
33
|
+
isClientOnly(): boolean;
|
|
34
|
+
isRelayConnected(): boolean;
|
|
35
|
+
private authMiddleware;
|
|
17
36
|
private setupRoutes;
|
|
18
37
|
private handleSendMessage;
|
|
19
38
|
private processTask;
|
|
20
39
|
private handleGetTask;
|
|
21
40
|
private handleListTasks;
|
|
22
41
|
private handleCancelTask;
|
|
23
|
-
/** Send a task to a peer agent via A2A protocol */
|
|
42
|
+
/** Send a task to a peer agent via A2A protocol (direct HTTP or relay) */
|
|
24
43
|
sendTask(targetAgent: string, messageText: string): Promise<JsonRpcResponse>;
|
|
25
|
-
|
|
44
|
+
private sendTaskDirect;
|
|
45
|
+
private sendTaskViaRelay;
|
|
46
|
+
/** Discover all peer agents (direct + relay) */
|
|
26
47
|
discoverPeers(): Promise<Record<string, {
|
|
27
48
|
status: string;
|
|
28
49
|
card?: AgentCard;
|
|
29
50
|
}>>;
|
|
30
51
|
private success;
|
|
31
52
|
private error;
|
|
32
|
-
|
|
53
|
+
/** Handle an inbound task received via the relay */
|
|
54
|
+
private handleRelayTask;
|
|
55
|
+
private startRelayClient;
|
|
56
|
+
private startRelayHub;
|
|
57
|
+
start(): Promise<void>;
|
|
33
58
|
private tryListen;
|
|
34
59
|
getExpressApp(): express.Express;
|
|
35
60
|
}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,OAAO,KAAK,EACV,SAAS,EAGT,eAAe,EACf,eAAe,EAGhB,MAAM,YAAY,CAAC;AAEpB,kFAAkF;AAClF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC;IAChD,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC,CAwCD;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAA8E;IAC5F,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,eAAe,CAA6E;gBAGlG,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE;IAsBtF,2EAA2E;IAC3E,kBAAkB,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,IAAI;IAI9F,YAAY,IAAI,OAAO;IAIvB,gBAAgB,IAAI,OAAO;IAI3B,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,WAAW;YAqEL,iBAAiB;YA6BjB,WAAW;IA0EzB,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,gBAAgB;IAaxB,0EAA0E;IACpE,QAAQ,CACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC;YA6Bb,cAAc;YAqCd,gBAAgB;IAuB9B,gDAAgD;IAC1C,aAAa,IAAI,OAAO,CAC5B,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,CAAC,CACrD;IAsCD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,KAAK;IAIb,oDAAoD;IACpD,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,aAAa;IAgBf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyD5B,OAAO,CAAC,SAAS;IA6BjB,aAAa,IAAI,OAAO,CAAC,OAAO;CAGjC"}
|