@rainfall-devkit/sdk 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-6FXRLPLR.mjs +436 -0
- package/dist/chunk-CC4O7GSQ.mjs +978 -0
- package/dist/chunk-CQ5TV7CQ.mjs +989 -0
- package/dist/chunk-GPKQUVAV.mjs +987 -0
- package/dist/chunk-KOCCGNEQ.mjs +269 -0
- package/dist/chunk-LJQEO3CY.mjs +150 -0
- package/dist/chunk-NCQVOLS4.mjs +269 -0
- package/dist/chunk-S7MOQCV4.mjs +137 -0
- package/dist/chunk-XHPFY5MH.mjs +132 -0
- package/dist/cli/index.js +1009 -54
- package/dist/cli/index.mjs +244 -24
- package/dist/daemon/index.d.mts +3 -3
- package/dist/daemon/index.d.ts +3 -3
- package/dist/daemon/index.js +411 -129
- package/dist/daemon/index.mjs +2 -1
- package/dist/display-KKJPO6UA.mjs +14 -0
- package/dist/errors-CY6HW2I5.mjs +24 -0
- package/dist/index.d.mts +66 -4
- package/dist/index.d.ts +66 -4
- package/dist/index.js +903 -124
- package/dist/index.mjs +18 -6
- package/dist/listeners-BBNBsJCk.d.ts +372 -0
- package/dist/listeners-CMUKjEkb.d.mts +372 -0
- package/dist/listeners-CadPNUHd.d.ts +372 -0
- package/dist/listeners-Ckdj6D8T.d.mts +372 -0
- package/dist/mcp.d.mts +2 -2
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +405 -101
- package/dist/mcp.mjs +4 -2
- package/dist/param-parser-JVKB5FQK.mjs +12 -0
- package/dist/param-parser-PAKCNDBX.mjs +136 -0
- package/dist/sdk-Cl5Qzt4I.d.mts +1165 -0
- package/dist/sdk-Cl5Qzt4I.d.ts +1165 -0
- package/dist/sdk-DQKNbBce.d.mts +1162 -0
- package/dist/sdk-DQKNbBce.d.ts +1162 -0
- package/package.json +2 -2
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// src/security/edge-node.ts
|
|
2
|
+
import sodium from "libsodium-wrappers-sumo";
|
|
3
|
+
var EdgeNodeSecurity = class {
|
|
4
|
+
sodiumReady;
|
|
5
|
+
backendSecret;
|
|
6
|
+
keyPair;
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.sodiumReady = sodium.ready;
|
|
9
|
+
this.backendSecret = options.backendSecret;
|
|
10
|
+
this.keyPair = options.keyPair;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Initialize libsodium
|
|
14
|
+
*/
|
|
15
|
+
async initialize() {
|
|
16
|
+
await this.sodiumReady;
|
|
17
|
+
}
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// JWT Token Management
|
|
20
|
+
// ============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Generate a JWT token for an edge node
|
|
23
|
+
* Note: In production, this is done by the backend. This is for testing.
|
|
24
|
+
*/
|
|
25
|
+
generateJWT(edgeNodeId, subscriberId, expiresInDays = 30) {
|
|
26
|
+
if (!this.backendSecret) {
|
|
27
|
+
throw new Error("Backend secret not configured");
|
|
28
|
+
}
|
|
29
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
30
|
+
const exp = now + expiresInDays * 24 * 60 * 60;
|
|
31
|
+
const jti = this.generateTokenId();
|
|
32
|
+
const payload = {
|
|
33
|
+
sub: edgeNodeId,
|
|
34
|
+
iss: "rainfall-backend",
|
|
35
|
+
iat: now,
|
|
36
|
+
exp,
|
|
37
|
+
jti,
|
|
38
|
+
scope: ["edge:heartbeat", "edge:claim", "edge:submit", "edge:queue"]
|
|
39
|
+
};
|
|
40
|
+
const header = { alg: "HS256", typ: "JWT" };
|
|
41
|
+
const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
|
|
42
|
+
const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
|
|
43
|
+
const signature = this.hmacSha256(
|
|
44
|
+
`${encodedHeader}.${encodedPayload}`,
|
|
45
|
+
this.backendSecret
|
|
46
|
+
);
|
|
47
|
+
const encodedSignature = this.base64UrlEncode(signature);
|
|
48
|
+
return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate a JWT token
|
|
52
|
+
*/
|
|
53
|
+
validateJWT(token) {
|
|
54
|
+
const parts = token.split(".");
|
|
55
|
+
if (parts.length !== 3) {
|
|
56
|
+
throw new Error("Invalid JWT format");
|
|
57
|
+
}
|
|
58
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
59
|
+
if (this.backendSecret) {
|
|
60
|
+
const expectedSignature = this.hmacSha256(
|
|
61
|
+
`${encodedHeader}.${encodedPayload}`,
|
|
62
|
+
this.backendSecret
|
|
63
|
+
);
|
|
64
|
+
const expectedEncoded = this.base64UrlEncode(expectedSignature);
|
|
65
|
+
if (!this.timingSafeEqual(encodedSignature, expectedEncoded)) {
|
|
66
|
+
throw new Error("Invalid JWT signature");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const payload = JSON.parse(this.base64UrlDecode(encodedPayload));
|
|
70
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
71
|
+
if (payload.exp < now) {
|
|
72
|
+
throw new Error("JWT token expired");
|
|
73
|
+
}
|
|
74
|
+
if (payload.iss !== "rainfall-backend") {
|
|
75
|
+
throw new Error("Invalid JWT issuer");
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
edgeNodeId: payload.sub,
|
|
79
|
+
subscriberId: payload.sub,
|
|
80
|
+
// Same as edge node ID for now
|
|
81
|
+
scopes: payload.scope,
|
|
82
|
+
expiresAt: payload.exp
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extract bearer token from Authorization header
|
|
87
|
+
*/
|
|
88
|
+
extractBearerToken(authHeader) {
|
|
89
|
+
if (!authHeader) return null;
|
|
90
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
91
|
+
return match ? match[1] : null;
|
|
92
|
+
}
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// ACL Enforcement
|
|
95
|
+
// ============================================================================
|
|
96
|
+
/**
|
|
97
|
+
* Check if an edge node is allowed to perform an action on a job
|
|
98
|
+
* Rule: Edge nodes can only access jobs for their own subscriber
|
|
99
|
+
*/
|
|
100
|
+
checkACL(check) {
|
|
101
|
+
if (check.subscriberId !== check.jobSubscriberId) {
|
|
102
|
+
return {
|
|
103
|
+
allowed: false,
|
|
104
|
+
reason: `Edge node ${check.edgeNodeId} cannot access jobs from subscriber ${check.jobSubscriberId}`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const allowedActions = ["heartbeat", "claim", "submit", "queue"];
|
|
108
|
+
if (!allowedActions.includes(check.action)) {
|
|
109
|
+
return {
|
|
110
|
+
allowed: false,
|
|
111
|
+
reason: `Unknown action: ${check.action}`
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return { allowed: true };
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Middleware-style ACL check for job operations
|
|
118
|
+
*/
|
|
119
|
+
requireSameSubscriber(edgeNodeSubscriberId, jobSubscriberId, operation) {
|
|
120
|
+
const result = this.checkACL({
|
|
121
|
+
edgeNodeId: edgeNodeSubscriberId,
|
|
122
|
+
subscriberId: edgeNodeSubscriberId,
|
|
123
|
+
jobSubscriberId,
|
|
124
|
+
action: operation
|
|
125
|
+
});
|
|
126
|
+
if (!result.allowed) {
|
|
127
|
+
throw new Error(result.reason);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Encryption (Libsodium)
|
|
132
|
+
// ============================================================================
|
|
133
|
+
/**
|
|
134
|
+
* Generate a new Ed25519 key pair for an edge node
|
|
135
|
+
*/
|
|
136
|
+
async generateKeyPair() {
|
|
137
|
+
await this.sodiumReady;
|
|
138
|
+
const keyPair = sodium.crypto_box_keypair();
|
|
139
|
+
return {
|
|
140
|
+
publicKey: this.bytesToBase64(keyPair.publicKey),
|
|
141
|
+
privateKey: this.bytesToBase64(keyPair.privateKey)
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Encrypt job parameters for a target edge node using its public key
|
|
146
|
+
*/
|
|
147
|
+
async encryptForEdgeNode(plaintext, targetPublicKeyBase64) {
|
|
148
|
+
await this.sodiumReady;
|
|
149
|
+
if (!this.keyPair) {
|
|
150
|
+
throw new Error("Local key pair not configured");
|
|
151
|
+
}
|
|
152
|
+
const targetPublicKey = this.base64ToBytes(targetPublicKeyBase64);
|
|
153
|
+
const ephemeralKeyPair = sodium.crypto_box_keypair();
|
|
154
|
+
const nonce = sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES);
|
|
155
|
+
const message = new TextEncoder().encode(plaintext);
|
|
156
|
+
const ciphertext = sodium.crypto_box_easy(
|
|
157
|
+
message,
|
|
158
|
+
nonce,
|
|
159
|
+
targetPublicKey,
|
|
160
|
+
ephemeralKeyPair.privateKey
|
|
161
|
+
);
|
|
162
|
+
return {
|
|
163
|
+
ciphertext: this.bytesToBase64(ciphertext),
|
|
164
|
+
nonce: this.bytesToBase64(nonce),
|
|
165
|
+
ephemeralPublicKey: this.bytesToBase64(ephemeralKeyPair.publicKey)
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Decrypt job parameters received from the backend
|
|
170
|
+
*/
|
|
171
|
+
async decryptFromBackend(encrypted) {
|
|
172
|
+
await this.sodiumReady;
|
|
173
|
+
if (!this.keyPair) {
|
|
174
|
+
throw new Error("Local key pair not configured");
|
|
175
|
+
}
|
|
176
|
+
const privateKey = this.base64ToBytes(this.keyPair.privateKey);
|
|
177
|
+
const ephemeralPublicKey = this.base64ToBytes(encrypted.ephemeralPublicKey);
|
|
178
|
+
const nonce = this.base64ToBytes(encrypted.nonce);
|
|
179
|
+
const ciphertext = this.base64ToBytes(encrypted.ciphertext);
|
|
180
|
+
const decrypted = sodium.crypto_box_open_easy(
|
|
181
|
+
ciphertext,
|
|
182
|
+
nonce,
|
|
183
|
+
ephemeralPublicKey,
|
|
184
|
+
privateKey
|
|
185
|
+
);
|
|
186
|
+
if (!decrypted) {
|
|
187
|
+
throw new Error("Decryption failed - invalid ciphertext or keys");
|
|
188
|
+
}
|
|
189
|
+
return new TextDecoder().decode(decrypted);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Encrypt job parameters for local storage (using secretbox)
|
|
193
|
+
*/
|
|
194
|
+
async encryptLocal(plaintext, key) {
|
|
195
|
+
await this.sodiumReady;
|
|
196
|
+
const keyBytes = this.deriveKey(key);
|
|
197
|
+
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
|
|
198
|
+
const message = new TextEncoder().encode(plaintext);
|
|
199
|
+
const ciphertext = sodium.crypto_secretbox_easy(message, nonce, keyBytes);
|
|
200
|
+
return {
|
|
201
|
+
ciphertext: this.bytesToBase64(ciphertext),
|
|
202
|
+
nonce: this.bytesToBase64(nonce)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Decrypt locally stored job parameters
|
|
207
|
+
*/
|
|
208
|
+
async decryptLocal(encrypted, key) {
|
|
209
|
+
await this.sodiumReady;
|
|
210
|
+
const keyBytes = this.deriveKey(key);
|
|
211
|
+
const nonce = this.base64ToBytes(encrypted.nonce);
|
|
212
|
+
const ciphertext = this.base64ToBytes(encrypted.ciphertext);
|
|
213
|
+
const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, keyBytes);
|
|
214
|
+
if (!decrypted) {
|
|
215
|
+
throw new Error("Local decryption failed");
|
|
216
|
+
}
|
|
217
|
+
return new TextDecoder().decode(decrypted);
|
|
218
|
+
}
|
|
219
|
+
// ============================================================================
|
|
220
|
+
// Utility Methods
|
|
221
|
+
// ============================================================================
|
|
222
|
+
generateTokenId() {
|
|
223
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
224
|
+
}
|
|
225
|
+
base64UrlEncode(str) {
|
|
226
|
+
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
227
|
+
}
|
|
228
|
+
base64UrlDecode(str) {
|
|
229
|
+
const padding = "=".repeat((4 - str.length % 4) % 4);
|
|
230
|
+
const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + padding;
|
|
231
|
+
return atob(base64);
|
|
232
|
+
}
|
|
233
|
+
hmacSha256(message, secret) {
|
|
234
|
+
const key = new TextEncoder().encode(secret);
|
|
235
|
+
const msg = new TextEncoder().encode(message);
|
|
236
|
+
const hash = sodium.crypto_auth(msg, key);
|
|
237
|
+
return this.bytesToBase64(hash);
|
|
238
|
+
}
|
|
239
|
+
timingSafeEqual(a, b) {
|
|
240
|
+
if (a.length !== b.length) return false;
|
|
241
|
+
let result = 0;
|
|
242
|
+
for (let i = 0; i < a.length; i++) {
|
|
243
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
244
|
+
}
|
|
245
|
+
return result === 0;
|
|
246
|
+
}
|
|
247
|
+
bytesToBase64(bytes) {
|
|
248
|
+
const binString = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
|
|
249
|
+
return btoa(binString);
|
|
250
|
+
}
|
|
251
|
+
base64ToBytes(base64) {
|
|
252
|
+
const binString = atob(base64);
|
|
253
|
+
return Uint8Array.from(binString, (m) => m.charCodeAt(0));
|
|
254
|
+
}
|
|
255
|
+
deriveKey(password) {
|
|
256
|
+
const passwordBytes = new TextEncoder().encode(password);
|
|
257
|
+
return sodium.crypto_generichash(32, passwordBytes, null);
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
async function createEdgeNodeSecurity(options = {}) {
|
|
261
|
+
const security = new EdgeNodeSecurity(options);
|
|
262
|
+
await security.initialize();
|
|
263
|
+
return security;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/cli/core/types.ts
|
|
267
|
+
var ToolHandlerRegistry = class {
|
|
268
|
+
handlers = [];
|
|
269
|
+
register(handler) {
|
|
270
|
+
this.handlers.push(handler);
|
|
271
|
+
}
|
|
272
|
+
findHandler(toolId) {
|
|
273
|
+
return this.handlers.find((h) => {
|
|
274
|
+
if (typeof h.toolId === "string") {
|
|
275
|
+
return h.toolId === toolId;
|
|
276
|
+
}
|
|
277
|
+
return h.toolId.test(toolId);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
getAllHandlers() {
|
|
281
|
+
return [...this.handlers];
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var globalHandlerRegistry = new ToolHandlerRegistry();
|
|
285
|
+
|
|
286
|
+
// src/cli/handlers/_registry.ts
|
|
287
|
+
var imageGenerationHandler = {
|
|
288
|
+
toolId: /image-generation|generate-image/,
|
|
289
|
+
async display(context) {
|
|
290
|
+
const { detectImageData, displayImage } = await import("./display-KKJPO6UA.mjs");
|
|
291
|
+
const { result, flags } = context;
|
|
292
|
+
const imageInfo = detectImageData(result);
|
|
293
|
+
if (imageInfo.hasImage && !flags.raw) {
|
|
294
|
+
try {
|
|
295
|
+
if (imageInfo.imageData) {
|
|
296
|
+
await displayImage(imageInfo.imageData);
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.warn("Failed to display image:", error instanceof Error ? error.message : error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
var finvizQuotesHandler = {
|
|
307
|
+
toolId: "finviz-quotes",
|
|
308
|
+
async preflight(context) {
|
|
309
|
+
const { parseValue } = await import("./param-parser-PAKCNDBX.mjs");
|
|
310
|
+
const params = { ...context.params };
|
|
311
|
+
if (params.tickers && typeof params.tickers === "string") {
|
|
312
|
+
params.tickers = parseValue(params.tickers, { type: "array", items: { type: "string" } });
|
|
313
|
+
}
|
|
314
|
+
return { params };
|
|
315
|
+
},
|
|
316
|
+
async display(context) {
|
|
317
|
+
const { result, flags } = context;
|
|
318
|
+
if (flags.raw) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
const obj = result;
|
|
322
|
+
const quotes = obj?.quotes;
|
|
323
|
+
if (Array.isArray(quotes) && quotes.length > 0) {
|
|
324
|
+
const { formatAsTable } = await import("./display-KKJPO6UA.mjs");
|
|
325
|
+
const tableData = quotes.map((q) => {
|
|
326
|
+
const quote = q;
|
|
327
|
+
const data = quote.data || {};
|
|
328
|
+
return {
|
|
329
|
+
Ticker: quote.ticker || data.Ticker || "-",
|
|
330
|
+
Price: data.Price || data.Close || "-",
|
|
331
|
+
Change: data.Change || "-",
|
|
332
|
+
Volume: data.Volume || "-",
|
|
333
|
+
"Market Cap": data.MarketCap || "-"
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
console.log(formatAsTable(tableData));
|
|
337
|
+
const summary = obj?.summary;
|
|
338
|
+
if (summary && typeof summary === "string") {
|
|
339
|
+
console.log(`
|
|
340
|
+
${summary}`);
|
|
341
|
+
}
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var csvQueryHandler = {
|
|
348
|
+
toolId: /query-csv|csv-query/,
|
|
349
|
+
async display(context) {
|
|
350
|
+
const { result, flags } = context;
|
|
351
|
+
if (flags.raw) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
355
|
+
const { formatAsTable } = await import("./display-KKJPO6UA.mjs");
|
|
356
|
+
console.log(formatAsTable(result));
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var webSearchHandler = {
|
|
363
|
+
toolId: /web-search|exa-web-search|perplexity/,
|
|
364
|
+
async display(context) {
|
|
365
|
+
const { result, flags } = context;
|
|
366
|
+
if (flags.raw) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
const obj = result;
|
|
370
|
+
if (obj.results && typeof obj.results === "string") {
|
|
371
|
+
console.log(obj.results);
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
if (obj.answer || obj.summary) {
|
|
375
|
+
console.log(obj.answer || obj.summary);
|
|
376
|
+
if (obj.sources && Array.isArray(obj.sources)) {
|
|
377
|
+
console.log("\n--- Sources ---");
|
|
378
|
+
obj.sources.forEach((source, i) => {
|
|
379
|
+
if (typeof source === "string") {
|
|
380
|
+
console.log(` ${i + 1}. ${source}`);
|
|
381
|
+
} else if (source && typeof source === "object") {
|
|
382
|
+
const s = source;
|
|
383
|
+
console.log(` ${i + 1}. ${s.title || s.url || JSON.stringify(source)}`);
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var memoryRecallHandler = {
|
|
393
|
+
toolId: /memory-recall|recall/,
|
|
394
|
+
async display(context) {
|
|
395
|
+
const { result, flags } = context;
|
|
396
|
+
if (flags.raw) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
if (Array.isArray(result)) {
|
|
400
|
+
if (result.length === 0) {
|
|
401
|
+
console.log("No memories found.");
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
console.log(`Found ${result.length} memory(s):
|
|
405
|
+
`);
|
|
406
|
+
result.forEach((mem, i) => {
|
|
407
|
+
const memory = mem;
|
|
408
|
+
console.log(`\u2500`.repeat(60));
|
|
409
|
+
console.log(` ${i + 1}. ${memory.content || memory.text || JSON.stringify(memory).slice(0, 100)}`);
|
|
410
|
+
if (memory.similarity) {
|
|
411
|
+
console.log(` Similarity: ${(Number(memory.similarity) * 100).toFixed(1)}%`);
|
|
412
|
+
}
|
|
413
|
+
if (memory.keywords && Array.isArray(memory.keywords)) {
|
|
414
|
+
console.log(` Keywords: ${memory.keywords.join(", ")}`);
|
|
415
|
+
}
|
|
416
|
+
console.log();
|
|
417
|
+
});
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
function registerBuiltInHandlers(registry = globalHandlerRegistry) {
|
|
424
|
+
registry.register(imageGenerationHandler);
|
|
425
|
+
registry.register(finvizQuotesHandler);
|
|
426
|
+
registry.register(csvQueryHandler);
|
|
427
|
+
registry.register(webSearchHandler);
|
|
428
|
+
registry.register(memoryRecallHandler);
|
|
429
|
+
}
|
|
430
|
+
registerBuiltInHandlers();
|
|
431
|
+
|
|
432
|
+
export {
|
|
433
|
+
EdgeNodeSecurity,
|
|
434
|
+
createEdgeNodeSecurity,
|
|
435
|
+
globalHandlerRegistry
|
|
436
|
+
};
|