@rainfall-devkit/sdk 0.2.2 → 0.2.4
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-2FYYTIJQ.mjs +993 -0
- 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-LJQEO3CY.mjs +150 -0
- package/dist/chunk-S7MOQCV4.mjs +137 -0
- package/dist/chunk-XHPFY5MH.mjs +132 -0
- package/dist/cli/index.js +1128 -49
- package/dist/cli/index.mjs +370 -30
- package/dist/daemon/index.d.mts +3 -3
- package/dist/daemon/index.d.ts +3 -3
- package/dist/daemon/index.js +416 -130
- 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 +896 -113
- package/dist/index.mjs +18 -6
- package/dist/listeners-BBNBsJCk.d.ts +372 -0
- package/dist/listeners-BCEypw1u.d.ts +372 -0
- package/dist/listeners-BGdrWpkP.d.mts +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 +410 -102
- 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-BUVNdBc7.d.mts +1167 -0
- package/dist/sdk-BUVNdBc7.d.ts +1167 -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 +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -41,6 +41,19 @@ var init_cjs_shims = __esm({
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
// src/errors.ts
|
|
44
|
+
var errors_exports = {};
|
|
45
|
+
__export(errors_exports, {
|
|
46
|
+
AuthenticationError: () => AuthenticationError,
|
|
47
|
+
NetworkError: () => NetworkError,
|
|
48
|
+
NotFoundError: () => NotFoundError,
|
|
49
|
+
RainfallError: () => RainfallError,
|
|
50
|
+
RateLimitError: () => RateLimitError,
|
|
51
|
+
ServerError: () => ServerError,
|
|
52
|
+
TimeoutError: () => TimeoutError,
|
|
53
|
+
ToolNotFoundError: () => ToolNotFoundError,
|
|
54
|
+
ValidationError: () => ValidationError,
|
|
55
|
+
parseErrorResponse: () => parseErrorResponse
|
|
56
|
+
});
|
|
44
57
|
function parseErrorResponse(response, data) {
|
|
45
58
|
const statusCode = response.status;
|
|
46
59
|
if (statusCode === 429) {
|
|
@@ -89,7 +102,7 @@ function parseErrorResponse(response, data) {
|
|
|
89
102
|
);
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
|
-
var RainfallError, AuthenticationError, RateLimitError, ValidationError, NotFoundError, ServerError, TimeoutError, NetworkError;
|
|
105
|
+
var RainfallError, AuthenticationError, RateLimitError, ValidationError, NotFoundError, ServerError, TimeoutError, NetworkError, ToolNotFoundError;
|
|
93
106
|
var init_errors = __esm({
|
|
94
107
|
"src/errors.ts"() {
|
|
95
108
|
"use strict";
|
|
@@ -175,6 +188,179 @@ var init_errors = __esm({
|
|
|
175
188
|
Object.setPrototypeOf(this, _NetworkError.prototype);
|
|
176
189
|
}
|
|
177
190
|
};
|
|
191
|
+
ToolNotFoundError = class _ToolNotFoundError extends RainfallError {
|
|
192
|
+
constructor(toolId) {
|
|
193
|
+
super(`Tool '${toolId}' not found`, "TOOL_NOT_FOUND", 404, { toolId });
|
|
194
|
+
this.name = "ToolNotFoundError";
|
|
195
|
+
Object.setPrototypeOf(this, _ToolNotFoundError.prototype);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// src/validation.ts
|
|
202
|
+
async function fetchToolSchema(client, toolId) {
|
|
203
|
+
const cached = schemaCache.get(toolId);
|
|
204
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
205
|
+
return cached.schema;
|
|
206
|
+
}
|
|
207
|
+
const response = await client.request(
|
|
208
|
+
`/olympic/subscribers/me/nodes/${toolId}/params`
|
|
209
|
+
);
|
|
210
|
+
if (!response.success || !response.params) {
|
|
211
|
+
throw new ValidationError(`Failed to fetch schema for tool '${toolId}'`);
|
|
212
|
+
}
|
|
213
|
+
schemaCache.set(toolId, { schema: response.params, timestamp: Date.now() });
|
|
214
|
+
return response.params;
|
|
215
|
+
}
|
|
216
|
+
function validateParams(schema, params, toolId) {
|
|
217
|
+
const errors = [];
|
|
218
|
+
const parameters = schema.parameters || {};
|
|
219
|
+
for (const [key, paramSchema] of Object.entries(parameters)) {
|
|
220
|
+
if (paramSchema.optional !== true && !(key in (params || {}))) {
|
|
221
|
+
errors.push({
|
|
222
|
+
path: key,
|
|
223
|
+
message: `Missing required parameter '${key}'`,
|
|
224
|
+
expected: paramSchema.type
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (params) {
|
|
229
|
+
for (const [key, value] of Object.entries(params)) {
|
|
230
|
+
const paramSchema = parameters[key];
|
|
231
|
+
if (!paramSchema) {
|
|
232
|
+
errors.push({
|
|
233
|
+
path: key,
|
|
234
|
+
message: `Unknown parameter '${key}'`,
|
|
235
|
+
received: value
|
|
236
|
+
});
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const typeError = validateType(key, value, paramSchema);
|
|
240
|
+
if (typeError) {
|
|
241
|
+
errors.push(typeError);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
valid: errors.length === 0,
|
|
247
|
+
errors
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function validateType(path, value, schema) {
|
|
251
|
+
if (value === null || value === void 0) {
|
|
252
|
+
if (schema.optional === true) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
path,
|
|
257
|
+
message: `Parameter '${path}' is required but received ${value}`,
|
|
258
|
+
received: value,
|
|
259
|
+
expected: schema.type
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const expectedType = schema.type;
|
|
263
|
+
const actualType = getJsType(value);
|
|
264
|
+
switch (expectedType) {
|
|
265
|
+
case "string":
|
|
266
|
+
if (typeof value !== "string") {
|
|
267
|
+
return {
|
|
268
|
+
path,
|
|
269
|
+
message: `Parameter '${path}' must be a string, received ${actualType}`,
|
|
270
|
+
received: value,
|
|
271
|
+
expected: "string"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
break;
|
|
275
|
+
case "number":
|
|
276
|
+
if (typeof value !== "number" || isNaN(value)) {
|
|
277
|
+
return {
|
|
278
|
+
path,
|
|
279
|
+
message: `Parameter '${path}' must be a number, received ${actualType}`,
|
|
280
|
+
received: value,
|
|
281
|
+
expected: "number"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
case "boolean":
|
|
286
|
+
if (typeof value !== "boolean") {
|
|
287
|
+
return {
|
|
288
|
+
path,
|
|
289
|
+
message: `Parameter '${path}' must be a boolean, received ${actualType}`,
|
|
290
|
+
received: value,
|
|
291
|
+
expected: "boolean"
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
break;
|
|
295
|
+
case "array":
|
|
296
|
+
if (!Array.isArray(value)) {
|
|
297
|
+
return {
|
|
298
|
+
path,
|
|
299
|
+
message: `Parameter '${path}' must be an array, received ${actualType}`,
|
|
300
|
+
received: value,
|
|
301
|
+
expected: "array"
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (schema.items) {
|
|
305
|
+
for (let i = 0; i < value.length; i++) {
|
|
306
|
+
const itemError = validateType(`${path}[${i}]`, value[i], schema.items);
|
|
307
|
+
if (itemError) {
|
|
308
|
+
return itemError;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
break;
|
|
313
|
+
case "object":
|
|
314
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
315
|
+
return {
|
|
316
|
+
path,
|
|
317
|
+
message: `Parameter '${path}' must be an object, received ${actualType}`,
|
|
318
|
+
received: value,
|
|
319
|
+
expected: "object"
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (schema.properties) {
|
|
323
|
+
const objValue = value;
|
|
324
|
+
for (const [propKey, propSchema] of Object.entries(schema.properties)) {
|
|
325
|
+
if (objValue[propKey] !== void 0) {
|
|
326
|
+
const propError = validateType(`${path}.${propKey}`, objValue[propKey], propSchema);
|
|
327
|
+
if (propError) {
|
|
328
|
+
return propError;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
break;
|
|
334
|
+
default:
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
function getJsType(value) {
|
|
340
|
+
if (value === null) return "null";
|
|
341
|
+
if (Array.isArray(value)) return "array";
|
|
342
|
+
return typeof value;
|
|
343
|
+
}
|
|
344
|
+
function formatValidationErrors(result) {
|
|
345
|
+
if (result.valid) return "No validation errors";
|
|
346
|
+
const lines = result.errors.map((err) => {
|
|
347
|
+
let line = ` - ${err.message}`;
|
|
348
|
+
if (err.received !== void 0) {
|
|
349
|
+
line += ` (received: ${JSON.stringify(err.received).slice(0, 50)})`;
|
|
350
|
+
}
|
|
351
|
+
return line;
|
|
352
|
+
});
|
|
353
|
+
return `Validation failed with ${result.errors.length} error(s):
|
|
354
|
+
${lines.join("\n")}`;
|
|
355
|
+
}
|
|
356
|
+
var schemaCache, CACHE_TTL_MS;
|
|
357
|
+
var init_validation = __esm({
|
|
358
|
+
"src/validation.ts"() {
|
|
359
|
+
"use strict";
|
|
360
|
+
init_cjs_shims();
|
|
361
|
+
init_errors();
|
|
362
|
+
schemaCache = /* @__PURE__ */ new Map();
|
|
363
|
+
CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
178
364
|
}
|
|
179
365
|
});
|
|
180
366
|
|
|
@@ -185,6 +371,7 @@ var init_client = __esm({
|
|
|
185
371
|
"use strict";
|
|
186
372
|
init_cjs_shims();
|
|
187
373
|
init_errors();
|
|
374
|
+
init_validation();
|
|
188
375
|
DEFAULT_BASE_URL = "https://olympic-api.pragma-digital.org/v1";
|
|
189
376
|
DEFAULT_TIMEOUT = 3e4;
|
|
190
377
|
DEFAULT_RETRIES = 3;
|
|
@@ -195,6 +382,7 @@ var init_client = __esm({
|
|
|
195
382
|
defaultTimeout;
|
|
196
383
|
defaultRetries;
|
|
197
384
|
defaultRetryDelay;
|
|
385
|
+
disableValidation;
|
|
198
386
|
lastRateLimitInfo;
|
|
199
387
|
subscriberId;
|
|
200
388
|
constructor(config) {
|
|
@@ -203,6 +391,7 @@ var init_client = __esm({
|
|
|
203
391
|
this.defaultTimeout = config.timeout || DEFAULT_TIMEOUT;
|
|
204
392
|
this.defaultRetries = config.retries ?? DEFAULT_RETRIES;
|
|
205
393
|
this.defaultRetryDelay = config.retryDelay || DEFAULT_RETRY_DELAY;
|
|
394
|
+
this.disableValidation = config.disableValidation ?? false;
|
|
206
395
|
}
|
|
207
396
|
/**
|
|
208
397
|
* Get the last rate limit info from the API
|
|
@@ -285,15 +474,72 @@ var init_client = __esm({
|
|
|
285
474
|
}
|
|
286
475
|
/**
|
|
287
476
|
* Execute a tool/node by ID
|
|
477
|
+
*
|
|
478
|
+
* @param toolId - The ID of the tool/node to execute
|
|
479
|
+
* @param params - Parameters to pass to the tool
|
|
480
|
+
* @param options - Request options including skipValidation to bypass param validation
|
|
288
481
|
*/
|
|
289
482
|
async executeTool(toolId, params, options) {
|
|
483
|
+
if (!this.disableValidation && !options?.skipValidation) {
|
|
484
|
+
const validation = await this.validateToolParams(toolId, params);
|
|
485
|
+
if (!validation.valid) {
|
|
486
|
+
const { ValidationError: ValidationError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
487
|
+
throw new ValidationError2(
|
|
488
|
+
`Parameter validation failed for tool '${toolId}': ${formatValidationErrors(validation)}`,
|
|
489
|
+
{ toolId, errors: validation.errors }
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
290
493
|
const subscriberId = await this.ensureSubscriberId();
|
|
494
|
+
const body = params || {};
|
|
495
|
+
if (options?.targetEdge) {
|
|
496
|
+
body._targetEdge = options.targetEdge;
|
|
497
|
+
}
|
|
291
498
|
const response = await this.request(`/olympic/subscribers/${subscriberId}/nodes/${toolId}`, {
|
|
292
499
|
method: "POST",
|
|
293
|
-
body
|
|
500
|
+
body
|
|
294
501
|
}, options);
|
|
502
|
+
if (response.success === false) {
|
|
503
|
+
const errorMessage = typeof response.error === "string" ? response.error : JSON.stringify(response.error);
|
|
504
|
+
throw new RainfallError(
|
|
505
|
+
`Tool execution failed: ${errorMessage}`,
|
|
506
|
+
"TOOL_EXECUTION_ERROR",
|
|
507
|
+
400,
|
|
508
|
+
{ toolId, error: response.error }
|
|
509
|
+
);
|
|
510
|
+
}
|
|
295
511
|
return response.result;
|
|
296
512
|
}
|
|
513
|
+
/**
|
|
514
|
+
* Validate parameters for a tool without executing it
|
|
515
|
+
* Fetches the tool schema and validates the provided params
|
|
516
|
+
*
|
|
517
|
+
* @param toolId - The ID of the tool to validate params for
|
|
518
|
+
* @param params - Parameters to validate
|
|
519
|
+
* @returns Validation result with detailed error information
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```typescript
|
|
523
|
+
* const result = await client.validateToolParams('finviz-quotes', { tickers: ['AAPL'] });
|
|
524
|
+
* if (!result.valid) {
|
|
525
|
+
* console.log('Validation errors:', result.errors);
|
|
526
|
+
* }
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
async validateToolParams(toolId, params) {
|
|
530
|
+
try {
|
|
531
|
+
const schema = await fetchToolSchema(this, toolId);
|
|
532
|
+
return validateParams(schema, params, toolId);
|
|
533
|
+
} catch (error) {
|
|
534
|
+
if (error instanceof RainfallError && error.statusCode === 404) {
|
|
535
|
+
return {
|
|
536
|
+
valid: false,
|
|
537
|
+
errors: [{ path: toolId, message: `Tool '${toolId}' not found` }]
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
return { valid: true, errors: [] };
|
|
541
|
+
}
|
|
542
|
+
}
|
|
297
543
|
/**
|
|
298
544
|
* List all available tools
|
|
299
545
|
*/
|
|
@@ -316,11 +562,20 @@ var init_client = __esm({
|
|
|
316
562
|
}
|
|
317
563
|
/**
|
|
318
564
|
* Get tool schema/parameters
|
|
565
|
+
*
|
|
566
|
+
* @param toolId - The ID of the tool to get schema for
|
|
567
|
+
* @returns Tool schema including parameters and output definitions
|
|
319
568
|
*/
|
|
320
569
|
async getToolSchema(toolId) {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
570
|
+
const schema = await fetchToolSchema(this, toolId);
|
|
571
|
+
return {
|
|
572
|
+
name: schema.name,
|
|
573
|
+
description: schema.description,
|
|
574
|
+
category: schema.category,
|
|
575
|
+
parameters: schema.parameters,
|
|
576
|
+
output: schema.output,
|
|
577
|
+
metadata: schema.metadata || {}
|
|
578
|
+
};
|
|
324
579
|
}
|
|
325
580
|
/**
|
|
326
581
|
* Get subscriber info
|
|
@@ -898,9 +1153,40 @@ var init_sdk = __esm({
|
|
|
898
1153
|
}
|
|
899
1154
|
/**
|
|
900
1155
|
* Execute any tool by ID (low-level access)
|
|
1156
|
+
*
|
|
1157
|
+
* @param toolId - The ID of the tool to execute
|
|
1158
|
+
* @param params - Parameters to pass to the tool
|
|
1159
|
+
* @param options - Execution options including skipValidation to bypass param validation
|
|
1160
|
+
*
|
|
1161
|
+
* @example
|
|
1162
|
+
* ```typescript
|
|
1163
|
+
* // Execute with validation (default)
|
|
1164
|
+
* const result = await rainfall.executeTool('finviz-quotes', { tickers: ['AAPL'] });
|
|
1165
|
+
*
|
|
1166
|
+
* // Execute without validation
|
|
1167
|
+
* const result = await rainfall.executeTool('finviz-quotes', { tickers: ['AAPL'] }, { skipValidation: true });
|
|
1168
|
+
* ```
|
|
901
1169
|
*/
|
|
902
|
-
async executeTool(toolId, params) {
|
|
903
|
-
return this.client.executeTool(toolId, params);
|
|
1170
|
+
async executeTool(toolId, params, options) {
|
|
1171
|
+
return this.client.executeTool(toolId, params, options);
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Validate parameters for a tool without executing it
|
|
1175
|
+
*
|
|
1176
|
+
* @param toolId - The ID of the tool to validate params for
|
|
1177
|
+
* @param params - Parameters to validate
|
|
1178
|
+
* @returns Validation result with detailed error information
|
|
1179
|
+
*
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```typescript
|
|
1182
|
+
* const result = await rainfall.validateToolParams('finviz-quotes', { tickers: ['AAPL'] });
|
|
1183
|
+
* if (!result.valid) {
|
|
1184
|
+
* console.log('Validation errors:', result.errors);
|
|
1185
|
+
* }
|
|
1186
|
+
* ```
|
|
1187
|
+
*/
|
|
1188
|
+
async validateToolParams(toolId, params) {
|
|
1189
|
+
return this.client.validateToolParams(toolId, params);
|
|
904
1190
|
}
|
|
905
1191
|
/**
|
|
906
1192
|
* Get current subscriber info and usage
|
|
@@ -1058,6 +1344,291 @@ var init_config = __esm({
|
|
|
1058
1344
|
}
|
|
1059
1345
|
});
|
|
1060
1346
|
|
|
1347
|
+
// src/cli/core/display.ts
|
|
1348
|
+
var display_exports = {};
|
|
1349
|
+
__export(display_exports, {
|
|
1350
|
+
detectImageData: () => detectImageData,
|
|
1351
|
+
detectImageSupport: () => detectImageSupport,
|
|
1352
|
+
displayImage: () => displayImage,
|
|
1353
|
+
formatAsTable: () => formatAsTable,
|
|
1354
|
+
formatResult: () => formatResult
|
|
1355
|
+
});
|
|
1356
|
+
function detectImageSupport() {
|
|
1357
|
+
if (process.env.TERM_PROGRAM === "iTerm.app") {
|
|
1358
|
+
return { supported: true, command: "imgcat" };
|
|
1359
|
+
}
|
|
1360
|
+
if (process.env.KITTY_WINDOW_ID) {
|
|
1361
|
+
return { supported: true, command: "kitty +kitten icat" };
|
|
1362
|
+
}
|
|
1363
|
+
try {
|
|
1364
|
+
return { supported: true, command: "xan" };
|
|
1365
|
+
} catch {
|
|
1366
|
+
}
|
|
1367
|
+
try {
|
|
1368
|
+
return { supported: true, command: "catimg" };
|
|
1369
|
+
} catch {
|
|
1370
|
+
}
|
|
1371
|
+
return { supported: false };
|
|
1372
|
+
}
|
|
1373
|
+
async function displayImage(imageData, options = {}) {
|
|
1374
|
+
const imageCommand = options.imageCommand || detectImageSupport().command;
|
|
1375
|
+
if (!imageCommand) {
|
|
1376
|
+
const { writeFileSync: writeFileSync3 } = await import("fs");
|
|
1377
|
+
const { tmpdir } = await import("os");
|
|
1378
|
+
const { join: join3 } = await import("path");
|
|
1379
|
+
const tempPath = join3(tmpdir(), `rainfall-image-${Date.now()}.png`);
|
|
1380
|
+
const buffer = typeof imageData === "string" ? Buffer.from(imageData, "base64") : imageData;
|
|
1381
|
+
writeFileSync3(tempPath, buffer);
|
|
1382
|
+
console.log(`Image saved to: ${tempPath}`);
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
return new Promise((resolve, reject) => {
|
|
1386
|
+
const buffer = typeof imageData === "string" ? Buffer.from(imageData, "base64") : imageData;
|
|
1387
|
+
const child = (0, import_child_process.spawn)(imageCommand, [], {
|
|
1388
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
1389
|
+
shell: true
|
|
1390
|
+
});
|
|
1391
|
+
child.stdin.write(buffer);
|
|
1392
|
+
child.stdin.end();
|
|
1393
|
+
child.on("close", (code) => {
|
|
1394
|
+
if (code === 0) {
|
|
1395
|
+
resolve();
|
|
1396
|
+
} else {
|
|
1397
|
+
reject(new Error(`Image display failed with code ${code}`));
|
|
1398
|
+
}
|
|
1399
|
+
});
|
|
1400
|
+
child.on("error", reject);
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
function formatAsTable(data, columns) {
|
|
1404
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
1405
|
+
return "No data";
|
|
1406
|
+
}
|
|
1407
|
+
const cols = columns || Object.keys(data[0]);
|
|
1408
|
+
const widths = {};
|
|
1409
|
+
for (const col of cols) {
|
|
1410
|
+
widths[col] = Math.max(
|
|
1411
|
+
col.length,
|
|
1412
|
+
...data.map((row) => {
|
|
1413
|
+
const val = row?.[col];
|
|
1414
|
+
return String(val ?? "").slice(0, 50).length;
|
|
1415
|
+
})
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
const lines = [];
|
|
1419
|
+
const header = cols.map((col) => col.padEnd(widths[col])).join(" ");
|
|
1420
|
+
lines.push(header);
|
|
1421
|
+
lines.push(cols.map((col) => "-".repeat(widths[col])).join(" "));
|
|
1422
|
+
for (const row of data) {
|
|
1423
|
+
const line = cols.map((col) => {
|
|
1424
|
+
const val = row?.[col];
|
|
1425
|
+
const str = String(val ?? "").slice(0, 50);
|
|
1426
|
+
return str.padEnd(widths[col]);
|
|
1427
|
+
}).join(" ");
|
|
1428
|
+
lines.push(line);
|
|
1429
|
+
}
|
|
1430
|
+
return lines.join("\n");
|
|
1431
|
+
}
|
|
1432
|
+
async function formatResult(result, options = {}) {
|
|
1433
|
+
const mode = options.mode || "pretty";
|
|
1434
|
+
switch (mode) {
|
|
1435
|
+
case "raw":
|
|
1436
|
+
return JSON.stringify(result);
|
|
1437
|
+
case "pretty":
|
|
1438
|
+
return JSON.stringify(result, null, 2);
|
|
1439
|
+
case "table":
|
|
1440
|
+
if (Array.isArray(result)) {
|
|
1441
|
+
return formatAsTable(result, options.columns);
|
|
1442
|
+
}
|
|
1443
|
+
return JSON.stringify(result, null, 2);
|
|
1444
|
+
case "terminal":
|
|
1445
|
+
if (typeof result === "string") {
|
|
1446
|
+
return result;
|
|
1447
|
+
}
|
|
1448
|
+
if (Array.isArray(result) && result.every((r) => typeof r === "string")) {
|
|
1449
|
+
return result.join("\n");
|
|
1450
|
+
}
|
|
1451
|
+
return JSON.stringify(result);
|
|
1452
|
+
default:
|
|
1453
|
+
return JSON.stringify(result, null, 2);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
function detectImageData(result) {
|
|
1457
|
+
if (!result || typeof result !== "object") {
|
|
1458
|
+
return { hasImage: false };
|
|
1459
|
+
}
|
|
1460
|
+
const obj = result;
|
|
1461
|
+
const imageFields = ["image", "imageData", "imageBase64", "png", "jpeg", "data"];
|
|
1462
|
+
for (const field of imageFields) {
|
|
1463
|
+
if (obj[field] && typeof obj[field] === "string") {
|
|
1464
|
+
const value = obj[field];
|
|
1465
|
+
if (value.startsWith("data:image/") || value.length > 100) {
|
|
1466
|
+
return {
|
|
1467
|
+
hasImage: true,
|
|
1468
|
+
imageData: value.startsWith("data:image/") ? value.split(",")[1] : value
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
if (obj.url && typeof obj.url === "string" && (obj.url.endsWith(".png") || obj.url.endsWith(".jpg") || obj.url.endsWith(".jpeg"))) {
|
|
1474
|
+
return { hasImage: true, imagePath: obj.url };
|
|
1475
|
+
}
|
|
1476
|
+
return { hasImage: false };
|
|
1477
|
+
}
|
|
1478
|
+
var import_child_process;
|
|
1479
|
+
var init_display = __esm({
|
|
1480
|
+
"src/cli/core/display.ts"() {
|
|
1481
|
+
"use strict";
|
|
1482
|
+
init_cjs_shims();
|
|
1483
|
+
import_child_process = require("child_process");
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
// src/cli/core/param-parser.ts
|
|
1488
|
+
var param_parser_exports = {};
|
|
1489
|
+
__export(param_parser_exports, {
|
|
1490
|
+
formatValueForDisplay: () => formatValueForDisplay,
|
|
1491
|
+
generateParamExample: () => generateParamExample,
|
|
1492
|
+
parseCliArgs: () => parseCliArgs,
|
|
1493
|
+
parseValue: () => parseValue
|
|
1494
|
+
});
|
|
1495
|
+
function parseValue(value, schema, options = {}) {
|
|
1496
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1497
|
+
if (!schema) {
|
|
1498
|
+
try {
|
|
1499
|
+
return JSON.parse(value);
|
|
1500
|
+
} catch {
|
|
1501
|
+
return value;
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
const expectedType = schema.type;
|
|
1505
|
+
switch (expectedType) {
|
|
1506
|
+
case "array":
|
|
1507
|
+
return parseArrayValue(value, schema, opts);
|
|
1508
|
+
case "number":
|
|
1509
|
+
return parseNumberValue(value, opts);
|
|
1510
|
+
case "boolean":
|
|
1511
|
+
return parseBooleanValue(value, opts);
|
|
1512
|
+
case "string":
|
|
1513
|
+
return value;
|
|
1514
|
+
case "object":
|
|
1515
|
+
try {
|
|
1516
|
+
return JSON.parse(value);
|
|
1517
|
+
} catch {
|
|
1518
|
+
return value;
|
|
1519
|
+
}
|
|
1520
|
+
default:
|
|
1521
|
+
try {
|
|
1522
|
+
return JSON.parse(value);
|
|
1523
|
+
} catch {
|
|
1524
|
+
return value;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
function parseArrayValue(value, schema, options) {
|
|
1529
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
1530
|
+
try {
|
|
1531
|
+
return JSON.parse(value);
|
|
1532
|
+
} catch {
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (options.arrayInterpolation && value.includes(options.arraySeparator)) {
|
|
1536
|
+
const items = value.split(options.arraySeparator).map((s) => s.trim()).filter(Boolean);
|
|
1537
|
+
if (schema.items) {
|
|
1538
|
+
return items.map((item) => parseValue(item, schema.items, { ...options, arrayInterpolation: false }));
|
|
1539
|
+
}
|
|
1540
|
+
return items;
|
|
1541
|
+
}
|
|
1542
|
+
if (schema.items) {
|
|
1543
|
+
return [parseValue(value, schema.items, { ...options, arrayInterpolation: false })];
|
|
1544
|
+
}
|
|
1545
|
+
return [value];
|
|
1546
|
+
}
|
|
1547
|
+
function parseNumberValue(value, options) {
|
|
1548
|
+
if (!options.numberParsing) {
|
|
1549
|
+
return value;
|
|
1550
|
+
}
|
|
1551
|
+
const num = Number(value);
|
|
1552
|
+
if (!isNaN(num) && isFinite(num)) {
|
|
1553
|
+
return num;
|
|
1554
|
+
}
|
|
1555
|
+
return value;
|
|
1556
|
+
}
|
|
1557
|
+
function parseBooleanValue(value, options) {
|
|
1558
|
+
if (!options.booleanParsing) {
|
|
1559
|
+
return value;
|
|
1560
|
+
}
|
|
1561
|
+
const lower = value.toLowerCase();
|
|
1562
|
+
if (lower === "true" || lower === "yes" || lower === "1" || lower === "on") {
|
|
1563
|
+
return true;
|
|
1564
|
+
}
|
|
1565
|
+
if (lower === "false" || lower === "no" || lower === "0" || lower === "off") {
|
|
1566
|
+
return false;
|
|
1567
|
+
}
|
|
1568
|
+
return value;
|
|
1569
|
+
}
|
|
1570
|
+
function parseCliArgs(args, schema, options = {}) {
|
|
1571
|
+
const params = {};
|
|
1572
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
1573
|
+
const parameters = schema?.parameters || {};
|
|
1574
|
+
for (let i = 0; i < args.length; i++) {
|
|
1575
|
+
const arg = args[i];
|
|
1576
|
+
if (arg.startsWith("--")) {
|
|
1577
|
+
const key = arg.slice(2);
|
|
1578
|
+
const value = args[++i];
|
|
1579
|
+
if (value === void 0) {
|
|
1580
|
+
params[key] = true;
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
const paramSchema = parameters[key];
|
|
1584
|
+
params[key] = parseValue(value, paramSchema, opts);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
return params;
|
|
1588
|
+
}
|
|
1589
|
+
function formatValueForDisplay(value) {
|
|
1590
|
+
if (value === null) return "null";
|
|
1591
|
+
if (value === void 0) return "undefined";
|
|
1592
|
+
if (typeof value === "string") return value;
|
|
1593
|
+
if (typeof value === "number") return String(value);
|
|
1594
|
+
if (typeof value === "boolean") return String(value);
|
|
1595
|
+
if (Array.isArray(value)) {
|
|
1596
|
+
return value.map(formatValueForDisplay).join(",");
|
|
1597
|
+
}
|
|
1598
|
+
return JSON.stringify(value);
|
|
1599
|
+
}
|
|
1600
|
+
function generateParamExample(key, schema) {
|
|
1601
|
+
const type = schema.type || "string";
|
|
1602
|
+
switch (type) {
|
|
1603
|
+
case "array":
|
|
1604
|
+
if (schema.items?.type === "string") {
|
|
1605
|
+
return `--${key} item1,item2,item3`;
|
|
1606
|
+
}
|
|
1607
|
+
return `--${key} '["item1", "item2"]'`;
|
|
1608
|
+
case "number":
|
|
1609
|
+
return `--${key} 42`;
|
|
1610
|
+
case "boolean":
|
|
1611
|
+
return `--${key} true`;
|
|
1612
|
+
case "object":
|
|
1613
|
+
return `--${key} '{"key": "value"}'`;
|
|
1614
|
+
default:
|
|
1615
|
+
return `--${key} "value"`;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
var DEFAULT_OPTIONS;
|
|
1619
|
+
var init_param_parser = __esm({
|
|
1620
|
+
"src/cli/core/param-parser.ts"() {
|
|
1621
|
+
"use strict";
|
|
1622
|
+
init_cjs_shims();
|
|
1623
|
+
DEFAULT_OPTIONS = {
|
|
1624
|
+
arrayInterpolation: true,
|
|
1625
|
+
numberParsing: true,
|
|
1626
|
+
booleanParsing: true,
|
|
1627
|
+
arraySeparator: ","
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1061
1632
|
// src/services/networked.ts
|
|
1062
1633
|
var RainfallNetworkedExecutor;
|
|
1063
1634
|
var init_networked = __esm({
|
|
@@ -1717,7 +2288,7 @@ var init_listeners = __esm({
|
|
|
1717
2288
|
});
|
|
1718
2289
|
|
|
1719
2290
|
// src/services/mcp-proxy.ts
|
|
1720
|
-
var import_ws, import_client2, import_stdio, import_streamableHttp,
|
|
2291
|
+
var import_ws, import_client2, import_stdio, import_streamableHttp, import_types2, MCPProxyHub;
|
|
1721
2292
|
var init_mcp_proxy = __esm({
|
|
1722
2293
|
"src/services/mcp-proxy.ts"() {
|
|
1723
2294
|
"use strict";
|
|
@@ -1726,7 +2297,7 @@ var init_mcp_proxy = __esm({
|
|
|
1726
2297
|
import_client2 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
1727
2298
|
import_stdio = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
1728
2299
|
import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
1729
|
-
|
|
2300
|
+
import_types2 = require("@modelcontextprotocol/sdk/types.js");
|
|
1730
2301
|
MCPProxyHub = class {
|
|
1731
2302
|
clients = /* @__PURE__ */ new Map();
|
|
1732
2303
|
options;
|
|
@@ -1845,7 +2416,7 @@ var init_mcp_proxy = __esm({
|
|
|
1845
2416
|
method: "tools/list",
|
|
1846
2417
|
params: {}
|
|
1847
2418
|
},
|
|
1848
|
-
|
|
2419
|
+
import_types2.ListToolsResultSchema
|
|
1849
2420
|
);
|
|
1850
2421
|
const tools = toolsResult.tools.map((tool) => ({
|
|
1851
2422
|
name: tool.name,
|
|
@@ -1959,7 +2530,7 @@ var init_mcp_proxy = __esm({
|
|
|
1959
2530
|
arguments: args
|
|
1960
2531
|
}
|
|
1961
2532
|
},
|
|
1962
|
-
|
|
2533
|
+
import_types2.CallToolResultSchema
|
|
1963
2534
|
),
|
|
1964
2535
|
new Promise(
|
|
1965
2536
|
(_, reject) => setTimeout(() => reject(new Error(`Tool call timeout after ${timeout}ms`)), timeout)
|
|
@@ -1969,7 +2540,7 @@ var init_mcp_proxy = __esm({
|
|
|
1969
2540
|
return this.formatToolResult(result);
|
|
1970
2541
|
} catch (error) {
|
|
1971
2542
|
this.log(`[${requestId}] Failed:`, error instanceof Error ? error.message : error);
|
|
1972
|
-
if (error instanceof
|
|
2543
|
+
if (error instanceof import_types2.McpError) {
|
|
1973
2544
|
throw new Error(`MCP Error (${toolName}): ${error.message} (code: ${error.code})`);
|
|
1974
2545
|
}
|
|
1975
2546
|
throw error;
|
|
@@ -2053,7 +2624,7 @@ var init_mcp_proxy = __esm({
|
|
|
2053
2624
|
method: "tools/list",
|
|
2054
2625
|
params: {}
|
|
2055
2626
|
},
|
|
2056
|
-
|
|
2627
|
+
import_types2.ListToolsResultSchema
|
|
2057
2628
|
);
|
|
2058
2629
|
client.tools = toolsResult.tools.map((tool) => ({
|
|
2059
2630
|
name: tool.name,
|
|
@@ -2084,7 +2655,7 @@ var init_mcp_proxy = __esm({
|
|
|
2084
2655
|
method: "resources/list",
|
|
2085
2656
|
params: {}
|
|
2086
2657
|
},
|
|
2087
|
-
|
|
2658
|
+
import_types2.ListResourcesResultSchema
|
|
2088
2659
|
);
|
|
2089
2660
|
results.push({
|
|
2090
2661
|
clientName: name,
|
|
@@ -2110,7 +2681,7 @@ var init_mcp_proxy = __esm({
|
|
|
2110
2681
|
method: "resources/read",
|
|
2111
2682
|
params: { uri }
|
|
2112
2683
|
},
|
|
2113
|
-
|
|
2684
|
+
import_types2.ReadResourceResultSchema
|
|
2114
2685
|
);
|
|
2115
2686
|
return result;
|
|
2116
2687
|
} else {
|
|
@@ -2121,7 +2692,7 @@ var init_mcp_proxy = __esm({
|
|
|
2121
2692
|
method: "resources/read",
|
|
2122
2693
|
params: { uri }
|
|
2123
2694
|
},
|
|
2124
|
-
|
|
2695
|
+
import_types2.ReadResourceResultSchema
|
|
2125
2696
|
);
|
|
2126
2697
|
return { clientName: name, ...result };
|
|
2127
2698
|
} catch {
|
|
@@ -2145,7 +2716,7 @@ var init_mcp_proxy = __esm({
|
|
|
2145
2716
|
method: "prompts/list",
|
|
2146
2717
|
params: {}
|
|
2147
2718
|
},
|
|
2148
|
-
|
|
2719
|
+
import_types2.ListPromptsResultSchema
|
|
2149
2720
|
);
|
|
2150
2721
|
results.push({
|
|
2151
2722
|
clientName: name,
|
|
@@ -2171,7 +2742,7 @@ var init_mcp_proxy = __esm({
|
|
|
2171
2742
|
method: "prompts/get",
|
|
2172
2743
|
params: { name, arguments: args }
|
|
2173
2744
|
},
|
|
2174
|
-
|
|
2745
|
+
import_types2.GetPromptResultSchema
|
|
2175
2746
|
);
|
|
2176
2747
|
return result;
|
|
2177
2748
|
} else {
|
|
@@ -2182,7 +2753,7 @@ var init_mcp_proxy = __esm({
|
|
|
2182
2753
|
method: "prompts/get",
|
|
2183
2754
|
params: { name, arguments: args }
|
|
2184
2755
|
},
|
|
2185
|
-
|
|
2756
|
+
import_types2.GetPromptResultSchema
|
|
2186
2757
|
);
|
|
2187
2758
|
return { clientName: cName, ...result };
|
|
2188
2759
|
} catch {
|
|
@@ -2204,7 +2775,7 @@ var init_mcp_proxy = __esm({
|
|
|
2204
2775
|
method: "tools/list",
|
|
2205
2776
|
params: {}
|
|
2206
2777
|
},
|
|
2207
|
-
|
|
2778
|
+
import_types2.ListToolsResultSchema
|
|
2208
2779
|
);
|
|
2209
2780
|
results.set(name, {
|
|
2210
2781
|
status: "healthy",
|
|
@@ -3274,7 +3845,7 @@ var import_path2 = require("path");
|
|
|
3274
3845
|
var import_url = require("url");
|
|
3275
3846
|
init_sdk();
|
|
3276
3847
|
init_config();
|
|
3277
|
-
var
|
|
3848
|
+
var import_child_process2 = require("child_process");
|
|
3278
3849
|
|
|
3279
3850
|
// src/security/edge-node.ts
|
|
3280
3851
|
init_cjs_shims();
|
|
@@ -3542,6 +4113,179 @@ async function createEdgeNodeSecurity(options = {}) {
|
|
|
3542
4113
|
return security;
|
|
3543
4114
|
}
|
|
3544
4115
|
|
|
4116
|
+
// src/cli/index.ts
|
|
4117
|
+
init_display();
|
|
4118
|
+
|
|
4119
|
+
// src/cli/handlers/_registry.ts
|
|
4120
|
+
init_cjs_shims();
|
|
4121
|
+
|
|
4122
|
+
// src/cli/core/types.ts
|
|
4123
|
+
init_cjs_shims();
|
|
4124
|
+
var ToolHandlerRegistry = class {
|
|
4125
|
+
handlers = [];
|
|
4126
|
+
register(handler) {
|
|
4127
|
+
this.handlers.push(handler);
|
|
4128
|
+
}
|
|
4129
|
+
findHandler(toolId) {
|
|
4130
|
+
return this.handlers.find((h) => {
|
|
4131
|
+
if (typeof h.toolId === "string") {
|
|
4132
|
+
return h.toolId === toolId;
|
|
4133
|
+
}
|
|
4134
|
+
return h.toolId.test(toolId);
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
getAllHandlers() {
|
|
4138
|
+
return [...this.handlers];
|
|
4139
|
+
}
|
|
4140
|
+
};
|
|
4141
|
+
var globalHandlerRegistry = new ToolHandlerRegistry();
|
|
4142
|
+
|
|
4143
|
+
// src/cli/handlers/_registry.ts
|
|
4144
|
+
var imageGenerationHandler = {
|
|
4145
|
+
toolId: /image-generation|generate-image/,
|
|
4146
|
+
async display(context) {
|
|
4147
|
+
const { detectImageData: detectImageData2, displayImage: displayImage2 } = await Promise.resolve().then(() => (init_display(), display_exports));
|
|
4148
|
+
const { result, flags } = context;
|
|
4149
|
+
const imageInfo = detectImageData2(result);
|
|
4150
|
+
if (imageInfo.hasImage && !flags.raw) {
|
|
4151
|
+
try {
|
|
4152
|
+
if (imageInfo.imageData) {
|
|
4153
|
+
await displayImage2(imageInfo.imageData);
|
|
4154
|
+
return true;
|
|
4155
|
+
}
|
|
4156
|
+
} catch (error) {
|
|
4157
|
+
console.warn("Failed to display image:", error instanceof Error ? error.message : error);
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
return false;
|
|
4161
|
+
}
|
|
4162
|
+
};
|
|
4163
|
+
var finvizQuotesHandler = {
|
|
4164
|
+
toolId: "finviz-quotes",
|
|
4165
|
+
async preflight(context) {
|
|
4166
|
+
const { parseValue: parseValue2 } = await Promise.resolve().then(() => (init_param_parser(), param_parser_exports));
|
|
4167
|
+
const params = { ...context.params };
|
|
4168
|
+
if (params.tickers && typeof params.tickers === "string") {
|
|
4169
|
+
params.tickers = parseValue2(params.tickers, { type: "array", items: { type: "string" } });
|
|
4170
|
+
}
|
|
4171
|
+
return { params };
|
|
4172
|
+
},
|
|
4173
|
+
async display(context) {
|
|
4174
|
+
const { result, flags } = context;
|
|
4175
|
+
if (flags.raw) {
|
|
4176
|
+
return false;
|
|
4177
|
+
}
|
|
4178
|
+
const obj = result;
|
|
4179
|
+
const quotes = obj?.quotes;
|
|
4180
|
+
if (Array.isArray(quotes) && quotes.length > 0) {
|
|
4181
|
+
const { formatAsTable: formatAsTable2 } = await Promise.resolve().then(() => (init_display(), display_exports));
|
|
4182
|
+
const tableData = quotes.map((q) => {
|
|
4183
|
+
const quote = q;
|
|
4184
|
+
const data = quote.data || {};
|
|
4185
|
+
return {
|
|
4186
|
+
Ticker: quote.ticker || data.Ticker || "-",
|
|
4187
|
+
Price: data.Price || data.Close || "-",
|
|
4188
|
+
Change: data.Change || "-",
|
|
4189
|
+
Volume: data.Volume || "-",
|
|
4190
|
+
"Market Cap": data.MarketCap || "-"
|
|
4191
|
+
};
|
|
4192
|
+
});
|
|
4193
|
+
console.log(formatAsTable2(tableData));
|
|
4194
|
+
const summary = obj?.summary;
|
|
4195
|
+
if (summary && typeof summary === "string") {
|
|
4196
|
+
console.log(`
|
|
4197
|
+
${summary}`);
|
|
4198
|
+
}
|
|
4199
|
+
return true;
|
|
4200
|
+
}
|
|
4201
|
+
return false;
|
|
4202
|
+
}
|
|
4203
|
+
};
|
|
4204
|
+
var csvQueryHandler = {
|
|
4205
|
+
toolId: /query-csv|csv-query/,
|
|
4206
|
+
async display(context) {
|
|
4207
|
+
const { result, flags } = context;
|
|
4208
|
+
if (flags.raw) {
|
|
4209
|
+
return false;
|
|
4210
|
+
}
|
|
4211
|
+
if (Array.isArray(result) && result.length > 0) {
|
|
4212
|
+
const { formatAsTable: formatAsTable2 } = await Promise.resolve().then(() => (init_display(), display_exports));
|
|
4213
|
+
console.log(formatAsTable2(result));
|
|
4214
|
+
return true;
|
|
4215
|
+
}
|
|
4216
|
+
return false;
|
|
4217
|
+
}
|
|
4218
|
+
};
|
|
4219
|
+
var webSearchHandler = {
|
|
4220
|
+
toolId: /web-search|exa-web-search|perplexity/,
|
|
4221
|
+
async display(context) {
|
|
4222
|
+
const { result, flags } = context;
|
|
4223
|
+
if (flags.raw) {
|
|
4224
|
+
return false;
|
|
4225
|
+
}
|
|
4226
|
+
const obj = result;
|
|
4227
|
+
if (obj.results && typeof obj.results === "string") {
|
|
4228
|
+
console.log(obj.results);
|
|
4229
|
+
return true;
|
|
4230
|
+
}
|
|
4231
|
+
if (obj.answer || obj.summary) {
|
|
4232
|
+
console.log(obj.answer || obj.summary);
|
|
4233
|
+
if (obj.sources && Array.isArray(obj.sources)) {
|
|
4234
|
+
console.log("\n--- Sources ---");
|
|
4235
|
+
obj.sources.forEach((source, i) => {
|
|
4236
|
+
if (typeof source === "string") {
|
|
4237
|
+
console.log(` ${i + 1}. ${source}`);
|
|
4238
|
+
} else if (source && typeof source === "object") {
|
|
4239
|
+
const s = source;
|
|
4240
|
+
console.log(` ${i + 1}. ${s.title || s.url || JSON.stringify(source)}`);
|
|
4241
|
+
}
|
|
4242
|
+
});
|
|
4243
|
+
}
|
|
4244
|
+
return true;
|
|
4245
|
+
}
|
|
4246
|
+
return false;
|
|
4247
|
+
}
|
|
4248
|
+
};
|
|
4249
|
+
var memoryRecallHandler = {
|
|
4250
|
+
toolId: /memory-recall|recall/,
|
|
4251
|
+
async display(context) {
|
|
4252
|
+
const { result, flags } = context;
|
|
4253
|
+
if (flags.raw) {
|
|
4254
|
+
return false;
|
|
4255
|
+
}
|
|
4256
|
+
if (Array.isArray(result)) {
|
|
4257
|
+
if (result.length === 0) {
|
|
4258
|
+
console.log("No memories found.");
|
|
4259
|
+
return true;
|
|
4260
|
+
}
|
|
4261
|
+
console.log(`Found ${result.length} memory(s):
|
|
4262
|
+
`);
|
|
4263
|
+
result.forEach((mem, i) => {
|
|
4264
|
+
const memory = mem;
|
|
4265
|
+
console.log(`\u2500`.repeat(60));
|
|
4266
|
+
console.log(` ${i + 1}. ${memory.content || memory.text || JSON.stringify(memory).slice(0, 100)}`);
|
|
4267
|
+
if (memory.similarity) {
|
|
4268
|
+
console.log(` Similarity: ${(Number(memory.similarity) * 100).toFixed(1)}%`);
|
|
4269
|
+
}
|
|
4270
|
+
if (memory.keywords && Array.isArray(memory.keywords)) {
|
|
4271
|
+
console.log(` Keywords: ${memory.keywords.join(", ")}`);
|
|
4272
|
+
}
|
|
4273
|
+
console.log();
|
|
4274
|
+
});
|
|
4275
|
+
return true;
|
|
4276
|
+
}
|
|
4277
|
+
return false;
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
function registerBuiltInHandlers(registry = globalHandlerRegistry) {
|
|
4281
|
+
registry.register(imageGenerationHandler);
|
|
4282
|
+
registry.register(finvizQuotesHandler);
|
|
4283
|
+
registry.register(csvQueryHandler);
|
|
4284
|
+
registry.register(webSearchHandler);
|
|
4285
|
+
registry.register(memoryRecallHandler);
|
|
4286
|
+
}
|
|
4287
|
+
registerBuiltInHandlers();
|
|
4288
|
+
|
|
3545
4289
|
// src/cli/index.ts
|
|
3546
4290
|
function printHelp() {
|
|
3547
4291
|
console.log(`
|
|
@@ -3576,6 +4320,7 @@ Commands:
|
|
|
3576
4320
|
config llm Show LLM configuration
|
|
3577
4321
|
|
|
3578
4322
|
edge generate-keys Generate key pair for edge node encryption
|
|
4323
|
+
edge register <proc-node-id> Register a proc node for edge execution
|
|
3579
4324
|
edge status Show edge node security status
|
|
3580
4325
|
|
|
3581
4326
|
version Show version information
|
|
@@ -3593,7 +4338,12 @@ Options for 'run':
|
|
|
3593
4338
|
--params, -p <json> Tool parameters as JSON
|
|
3594
4339
|
--file, -f <path> Read parameters from file
|
|
3595
4340
|
--raw Output raw JSON
|
|
4341
|
+
--table Output as table (if applicable)
|
|
4342
|
+
--terminal Output for terminal consumption (minimal formatting)
|
|
3596
4343
|
--<key> <value> Pass individual parameters (e.g., --query "AI news")
|
|
4344
|
+
Arrays: --tickers AAPL,GOOGL (comma-separated)
|
|
4345
|
+
Numbers: --count 42
|
|
4346
|
+
Booleans: --enabled true
|
|
3597
4347
|
|
|
3598
4348
|
Options for 'daemon start':
|
|
3599
4349
|
--port <port> WebSocket port (default: 8765)
|
|
@@ -3609,6 +4359,7 @@ Examples:
|
|
|
3609
4359
|
rainfall tools describe github-create-issue
|
|
3610
4360
|
rainfall run exa-web-search -p '{"query": "AI news"}'
|
|
3611
4361
|
rainfall run exa-web-search --query "AI news"
|
|
4362
|
+
rainfall run finviz-quotes --tickers AAPL,GOOGL,MSFT
|
|
3612
4363
|
rainfall run github-create-issue --owner facebook --repo react --title "Bug"
|
|
3613
4364
|
rainfall run article-summarize -f ./article.json
|
|
3614
4365
|
rainfall daemon start
|
|
@@ -3626,6 +4377,24 @@ function getRainfall() {
|
|
|
3626
4377
|
baseUrl: config.baseUrl
|
|
3627
4378
|
});
|
|
3628
4379
|
}
|
|
4380
|
+
async function fetchAllNodeIds(rainfall) {
|
|
4381
|
+
try {
|
|
4382
|
+
const client = rainfall.getClient();
|
|
4383
|
+
const subscriberId = await client.ensureSubscriberId();
|
|
4384
|
+
const result = await client.request(
|
|
4385
|
+
`/olympic/subscribers/${subscriberId}/nodes/_utils/node-list`
|
|
4386
|
+
);
|
|
4387
|
+
if (result.keys && Array.isArray(result.keys)) {
|
|
4388
|
+
return result.keys;
|
|
4389
|
+
}
|
|
4390
|
+
if (result.nodes && Array.isArray(result.nodes)) {
|
|
4391
|
+
return result.nodes.map((n) => n.id);
|
|
4392
|
+
}
|
|
4393
|
+
return [];
|
|
4394
|
+
} catch {
|
|
4395
|
+
return [];
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
3629
4398
|
async function authLogin(args) {
|
|
3630
4399
|
const apiKey = args[0] || process.env.RAINFALL_API_KEY;
|
|
3631
4400
|
if (!apiKey) {
|
|
@@ -3712,6 +4481,100 @@ function formatSchema(obj, indent = 0) {
|
|
|
3712
4481
|
}
|
|
3713
4482
|
return lines.join("\n");
|
|
3714
4483
|
}
|
|
4484
|
+
function levenshteinDistance(a, b) {
|
|
4485
|
+
const matrix = [];
|
|
4486
|
+
for (let i = 0; i <= b.length; i++) {
|
|
4487
|
+
matrix[i] = [i];
|
|
4488
|
+
}
|
|
4489
|
+
for (let j = 0; j <= a.length; j++) {
|
|
4490
|
+
matrix[0][j] = j;
|
|
4491
|
+
}
|
|
4492
|
+
for (let i = 1; i <= b.length; i++) {
|
|
4493
|
+
for (let j = 1; j <= a.length; j++) {
|
|
4494
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
4495
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
4496
|
+
} else {
|
|
4497
|
+
matrix[i][j] = Math.min(
|
|
4498
|
+
matrix[i - 1][j - 1] + 1,
|
|
4499
|
+
// substitution
|
|
4500
|
+
matrix[i][j - 1] + 1,
|
|
4501
|
+
// insertion
|
|
4502
|
+
matrix[i - 1][j] + 1
|
|
4503
|
+
// deletion
|
|
4504
|
+
);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
return matrix[b.length][a.length];
|
|
4509
|
+
}
|
|
4510
|
+
function jaroWinklerSimilarity(a, b) {
|
|
4511
|
+
if (a === b) return 1;
|
|
4512
|
+
if (a.length === 0 || b.length === 0) return 0;
|
|
4513
|
+
const matchDistance = Math.floor(Math.max(a.length, b.length) / 2) - 1;
|
|
4514
|
+
const aMatches = new Array(a.length).fill(false);
|
|
4515
|
+
const bMatches = new Array(b.length).fill(false);
|
|
4516
|
+
let matches = 0;
|
|
4517
|
+
let transpositions = 0;
|
|
4518
|
+
for (let i = 0; i < a.length; i++) {
|
|
4519
|
+
const start = Math.max(0, i - matchDistance);
|
|
4520
|
+
const end = Math.min(i + matchDistance + 1, b.length);
|
|
4521
|
+
for (let j = start; j < end; j++) {
|
|
4522
|
+
if (bMatches[j] || a.charAt(i) !== b.charAt(j)) continue;
|
|
4523
|
+
aMatches[i] = true;
|
|
4524
|
+
bMatches[j] = true;
|
|
4525
|
+
matches++;
|
|
4526
|
+
break;
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
if (matches === 0) return 0;
|
|
4530
|
+
let k = 0;
|
|
4531
|
+
for (let i = 0; i < a.length; i++) {
|
|
4532
|
+
if (!aMatches[i]) continue;
|
|
4533
|
+
while (!bMatches[k]) k++;
|
|
4534
|
+
if (a.charAt(i) !== b.charAt(k)) transpositions++;
|
|
4535
|
+
k++;
|
|
4536
|
+
}
|
|
4537
|
+
const jaro = (matches / a.length + matches / b.length + (matches - transpositions / 2) / matches) / 3;
|
|
4538
|
+
let prefixLength = 0;
|
|
4539
|
+
for (let i = 0; i < Math.min(a.length, b.length); i++) {
|
|
4540
|
+
if (a.charAt(i) === b.charAt(i)) {
|
|
4541
|
+
prefixLength++;
|
|
4542
|
+
} else {
|
|
4543
|
+
break;
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
const scalingFactor = 0.1;
|
|
4547
|
+
return jaro + prefixLength * scalingFactor * (1 - jaro);
|
|
4548
|
+
}
|
|
4549
|
+
function calculateSimilarity(toolId, candidateId, description = "") {
|
|
4550
|
+
const lowerToolId = toolId.toLowerCase();
|
|
4551
|
+
const lowerCandidate = candidateId.toLowerCase();
|
|
4552
|
+
const prefix = lowerToolId.split("-")[0];
|
|
4553
|
+
const hasPrefix = lowerToolId.includes("-");
|
|
4554
|
+
const jwScore = jaroWinklerSimilarity(lowerToolId, lowerCandidate);
|
|
4555
|
+
const maxLen = Math.max(lowerToolId.length, lowerCandidate.length);
|
|
4556
|
+
const lvScore = maxLen === 0 ? 1 : 1 - levenshteinDistance(lowerToolId, lowerCandidate) / maxLen;
|
|
4557
|
+
let substringBoost = 0;
|
|
4558
|
+
if (lowerCandidate.includes(lowerToolId) || lowerToolId.includes(lowerCandidate)) {
|
|
4559
|
+
substringBoost = 0.4;
|
|
4560
|
+
}
|
|
4561
|
+
let prefixBoost = 0;
|
|
4562
|
+
if (hasPrefix && lowerCandidate === prefix) {
|
|
4563
|
+
prefixBoost = 0.5;
|
|
4564
|
+
}
|
|
4565
|
+
if (hasPrefix && lowerCandidate.startsWith(prefix + "-")) {
|
|
4566
|
+
prefixBoost = 0.35;
|
|
4567
|
+
}
|
|
4568
|
+
const descMatch = description.toLowerCase().includes(lowerToolId) ? 0.1 : 0;
|
|
4569
|
+
return jwScore * 0.4 + lvScore * 0.25 + substringBoost + prefixBoost + descMatch;
|
|
4570
|
+
}
|
|
4571
|
+
function findSimilarToolIds(toolId, toolIds) {
|
|
4572
|
+
const scored = toolIds.map((id) => ({
|
|
4573
|
+
id,
|
|
4574
|
+
score: calculateSimilarity(toolId, id)
|
|
4575
|
+
}));
|
|
4576
|
+
return scored.filter((item) => item.score > 0.35).sort((a, b) => b.score - a.score).slice(0, 5).map((item) => item.id);
|
|
4577
|
+
}
|
|
3715
4578
|
async function describeTool(args) {
|
|
3716
4579
|
const toolId = args[0];
|
|
3717
4580
|
if (!toolId) {
|
|
@@ -3747,6 +4610,17 @@ async function describeTool(args) {
|
|
|
3747
4610
|
console.log();
|
|
3748
4611
|
} catch (error) {
|
|
3749
4612
|
console.error(`Error: Tool '${toolId}' not found`);
|
|
4613
|
+
try {
|
|
4614
|
+
const allNodeIds = await fetchAllNodeIds(rainfall);
|
|
4615
|
+
const suggestions = findSimilarToolIds(toolId, allNodeIds);
|
|
4616
|
+
if (suggestions.length > 0) {
|
|
4617
|
+
console.error("\nDid you mean:");
|
|
4618
|
+
for (const suggestion of suggestions) {
|
|
4619
|
+
console.error(` \u2022 ${suggestion}`);
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
} catch {
|
|
4623
|
+
}
|
|
3750
4624
|
process.exit(1);
|
|
3751
4625
|
}
|
|
3752
4626
|
}
|
|
@@ -3790,20 +4664,30 @@ Options:
|
|
|
3790
4664
|
-p, --params <json> Tool parameters as JSON string
|
|
3791
4665
|
-f, --file <path> Read parameters from JSON file
|
|
3792
4666
|
--raw Output raw JSON (no formatting)
|
|
4667
|
+
--table Output as table (if applicable)
|
|
4668
|
+
--terminal Output for terminal consumption (minimal formatting)
|
|
4669
|
+
--target-edge <id> Execute on specific edge node (for cross-node jobs)
|
|
3793
4670
|
--<key> <value> Pass individual parameters (e.g., --query "AI news")
|
|
4671
|
+
Arrays: --tickers AAPL,GOOGL (comma-separated)
|
|
4672
|
+
Numbers: --count 42
|
|
4673
|
+
Booleans: --enabled true
|
|
3794
4674
|
|
|
3795
4675
|
Examples:
|
|
3796
4676
|
rainfall run figma-users-getMe
|
|
3797
4677
|
rainfall run exa-web-search -p '{"query": "AI news"}'
|
|
3798
4678
|
rainfall run exa-web-search --query "AI news"
|
|
4679
|
+
rainfall run finviz-quotes --tickers AAPL,GOOGL,MSFT
|
|
3799
4680
|
rainfall run github-create-issue --owner facebook --repo react --title "Bug"
|
|
3800
4681
|
rainfall run github-create-issue -f ./issue.json
|
|
4682
|
+
rainfall run exa-web-search --query "latest AI" --target-edge <edge-id>
|
|
3801
4683
|
echo '{"query": "hello"}' | rainfall run exa-web-search
|
|
3802
4684
|
`);
|
|
3803
4685
|
return;
|
|
3804
4686
|
}
|
|
3805
4687
|
let params = {};
|
|
3806
4688
|
const rawArgs = [];
|
|
4689
|
+
let displayMode = "pretty";
|
|
4690
|
+
let targetEdge;
|
|
3807
4691
|
for (let i = 1; i < args.length; i++) {
|
|
3808
4692
|
const arg = args[i];
|
|
3809
4693
|
if (arg === "--params" || arg === "-p") {
|
|
@@ -3831,16 +4715,23 @@ Examples:
|
|
|
3831
4715
|
process.exit(1);
|
|
3832
4716
|
}
|
|
3833
4717
|
} else if (arg === "--raw") {
|
|
4718
|
+
displayMode = "raw";
|
|
4719
|
+
} else if (arg === "--table") {
|
|
4720
|
+
displayMode = "table";
|
|
4721
|
+
} else if (arg === "--terminal") {
|
|
4722
|
+
displayMode = "terminal";
|
|
4723
|
+
} else if (arg === "--target-edge") {
|
|
4724
|
+
targetEdge = args[++i];
|
|
4725
|
+
if (!targetEdge) {
|
|
4726
|
+
console.error("Error: --target-edge requires an edge node ID");
|
|
4727
|
+
process.exit(1);
|
|
4728
|
+
}
|
|
3834
4729
|
} else if (arg.startsWith("--")) {
|
|
3835
4730
|
const key = arg.slice(2);
|
|
3836
4731
|
const value = args[++i];
|
|
3837
4732
|
if (value === void 0) {
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
}
|
|
3841
|
-
try {
|
|
3842
|
-
params[key] = JSON.parse(value);
|
|
3843
|
-
} catch {
|
|
4733
|
+
params[key] = true;
|
|
4734
|
+
} else {
|
|
3844
4735
|
params[key] = value;
|
|
3845
4736
|
}
|
|
3846
4737
|
} else {
|
|
@@ -3882,30 +4773,110 @@ Examples:
|
|
|
3882
4773
|
}
|
|
3883
4774
|
}
|
|
3884
4775
|
const rainfall = getRainfall();
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
4776
|
+
let toolSchema;
|
|
4777
|
+
try {
|
|
4778
|
+
const fullSchema = await rainfall.getToolSchema(toolId);
|
|
4779
|
+
toolSchema = {
|
|
4780
|
+
parameters: fullSchema.parameters
|
|
4781
|
+
};
|
|
4782
|
+
} catch {
|
|
4783
|
+
}
|
|
4784
|
+
const cliFlags = /* @__PURE__ */ new Set(["--params", "-p", "--file", "-f", "--raw", "--table", "--terminal", "--target-edge"]);
|
|
4785
|
+
const toolArgs = args.slice(1).filter((arg, i, arr) => {
|
|
4786
|
+
if (cliFlags.has(arg)) {
|
|
4787
|
+
return false;
|
|
4788
|
+
}
|
|
4789
|
+
if (i > 0 && cliFlags.has(arr[i - 1])) {
|
|
4790
|
+
return false;
|
|
4791
|
+
}
|
|
4792
|
+
return true;
|
|
4793
|
+
});
|
|
4794
|
+
if (toolSchema?.parameters) {
|
|
4795
|
+
const { parseCliArgs: parseCliArgs2 } = await Promise.resolve().then(() => (init_param_parser(), param_parser_exports));
|
|
4796
|
+
const parsedParams = parseCliArgs2(
|
|
4797
|
+
toolArgs,
|
|
4798
|
+
{
|
|
4799
|
+
name: toolId,
|
|
4800
|
+
description: "",
|
|
4801
|
+
category: "",
|
|
4802
|
+
parameters: toolSchema.parameters
|
|
3895
4803
|
}
|
|
3896
|
-
|
|
4804
|
+
);
|
|
4805
|
+
params = { ...parsedParams, ...params };
|
|
4806
|
+
}
|
|
4807
|
+
if (rawArgs.length === 1 && Object.keys(params).length === 0 && toolSchema?.parameters) {
|
|
4808
|
+
const paramEntries = Object.entries(toolSchema.parameters);
|
|
4809
|
+
const requiredParams = paramEntries.filter(([, p]) => !p.optional);
|
|
4810
|
+
if (requiredParams.length === 1) {
|
|
4811
|
+
const [paramName, paramSchema] = requiredParams[0];
|
|
4812
|
+
const { parseValue: parseValue2 } = await Promise.resolve().then(() => (init_param_parser(), param_parser_exports));
|
|
4813
|
+
params = { [paramName]: parseValue2(rawArgs[0], paramSchema) };
|
|
3897
4814
|
}
|
|
3898
4815
|
}
|
|
4816
|
+
const handler = globalHandlerRegistry.findHandler(toolId);
|
|
4817
|
+
const toolContext = {
|
|
4818
|
+
rainfall,
|
|
4819
|
+
toolId,
|
|
4820
|
+
params,
|
|
4821
|
+
args: rawArgs,
|
|
4822
|
+
flags: { raw: displayMode === "raw" }
|
|
4823
|
+
};
|
|
3899
4824
|
try {
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
4825
|
+
let executionParams = params;
|
|
4826
|
+
let preflightContext;
|
|
4827
|
+
let skipExecution;
|
|
4828
|
+
if (handler?.preflight) {
|
|
4829
|
+
const preflightResult = await handler.preflight(toolContext);
|
|
4830
|
+
if (preflightResult) {
|
|
4831
|
+
if (preflightResult.skipExecution !== void 0) {
|
|
4832
|
+
skipExecution = preflightResult.skipExecution;
|
|
4833
|
+
}
|
|
4834
|
+
if (preflightResult.params) {
|
|
4835
|
+
executionParams = preflightResult.params;
|
|
4836
|
+
}
|
|
4837
|
+
preflightContext = preflightResult.context;
|
|
4838
|
+
}
|
|
4839
|
+
}
|
|
4840
|
+
let result;
|
|
4841
|
+
if (skipExecution !== void 0) {
|
|
4842
|
+
result = skipExecution;
|
|
4843
|
+
} else if (targetEdge) {
|
|
4844
|
+
result = await rainfall.executeTool(toolId, executionParams, { targetEdge });
|
|
3903
4845
|
} else {
|
|
3904
|
-
|
|
4846
|
+
result = await rainfall.executeTool(toolId, executionParams);
|
|
4847
|
+
}
|
|
4848
|
+
const postflightContext = {
|
|
4849
|
+
...toolContext,
|
|
4850
|
+
result,
|
|
4851
|
+
preflightContext
|
|
4852
|
+
};
|
|
4853
|
+
if (handler?.postflight) {
|
|
4854
|
+
await handler.postflight(postflightContext);
|
|
4855
|
+
}
|
|
4856
|
+
let displayed = false;
|
|
4857
|
+
if (handler?.display) {
|
|
4858
|
+
displayed = await handler.display({ ...postflightContext, flags: { ...toolContext.flags, mode: displayMode } });
|
|
4859
|
+
}
|
|
4860
|
+
if (!displayed) {
|
|
4861
|
+
const output = await formatResult(result, { mode: displayMode });
|
|
4862
|
+
console.log(output);
|
|
3905
4863
|
}
|
|
3906
4864
|
} catch (error) {
|
|
3907
4865
|
const message = error instanceof Error ? error.message : String(error);
|
|
3908
4866
|
console.error(`Error: ${message}`);
|
|
4867
|
+
if (message.toLowerCase().includes("not found") || message.toLowerCase().includes("not found")) {
|
|
4868
|
+
try {
|
|
4869
|
+
const allNodeIds = await fetchAllNodeIds(rainfall);
|
|
4870
|
+
const suggestions = findSimilarToolIds(toolId, allNodeIds);
|
|
4871
|
+
if (suggestions.length > 0) {
|
|
4872
|
+
console.error("\nDid you mean:");
|
|
4873
|
+
for (const suggestion of suggestions) {
|
|
4874
|
+
console.error(` \u2022 ${suggestion}`);
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
} catch {
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
3909
4880
|
process.exit(1);
|
|
3910
4881
|
}
|
|
3911
4882
|
}
|
|
@@ -4022,7 +4993,7 @@ async function upgrade() {
|
|
|
4022
4993
|
console.log(`Running: ${command} ${args.join(" ")}`);
|
|
4023
4994
|
console.log();
|
|
4024
4995
|
return new Promise((resolve, reject) => {
|
|
4025
|
-
const child = (0,
|
|
4996
|
+
const child = (0, import_child_process2.spawn)(command, args, {
|
|
4026
4997
|
stdio: "inherit",
|
|
4027
4998
|
shell: true
|
|
4028
4999
|
});
|
|
@@ -4198,7 +5169,7 @@ async function edgeGenerateKeys() {
|
|
|
4198
5169
|
console.log(" Private:", privateKeyPath);
|
|
4199
5170
|
console.log("\n\u{1F4CB} To register this edge node:");
|
|
4200
5171
|
console.log(" 1. Copy the public key above");
|
|
4201
|
-
console.log(" 2. Register with: rainfall edge register <public-key>");
|
|
5172
|
+
console.log(" 2. Register proc node with: rainfall edge register <proc-node-id> --public-key <key>");
|
|
4202
5173
|
console.log(" 3. The backend will return an edgeNodeSecret (JWT)");
|
|
4203
5174
|
console.log(" 4. Store the secret securely - it expires in 30 days");
|
|
4204
5175
|
} catch (error) {
|
|
@@ -4206,6 +5177,98 @@ async function edgeGenerateKeys() {
|
|
|
4206
5177
|
process.exit(1);
|
|
4207
5178
|
}
|
|
4208
5179
|
}
|
|
5180
|
+
async function edgeRegister(args) {
|
|
5181
|
+
const procNodeId = args[0];
|
|
5182
|
+
if (!procNodeId) {
|
|
5183
|
+
console.error("Error: Proc node ID required");
|
|
5184
|
+
console.error("\nUsage: rainfall edge register <proc-node-id> [options]");
|
|
5185
|
+
console.error("\nOptions:");
|
|
5186
|
+
console.error(" --public-key <key> Public key for encryption (optional)");
|
|
5187
|
+
console.error(" --list <id1,id2,...> Register multiple proc nodes (comma-separated)");
|
|
5188
|
+
console.error("\nExamples:");
|
|
5189
|
+
console.error(" rainfall edge register exa-web-search");
|
|
5190
|
+
console.error(' rainfall edge register exa-web-search --public-key "base64key..."');
|
|
5191
|
+
console.error(' rainfall edge register --list "exa-web-search,github-create-issue"');
|
|
5192
|
+
process.exit(1);
|
|
5193
|
+
}
|
|
5194
|
+
const rainfall = getRainfall();
|
|
5195
|
+
const config = loadConfig();
|
|
5196
|
+
let publicKey;
|
|
5197
|
+
let procNodeIds = [procNodeId];
|
|
5198
|
+
for (let i = 1; i < args.length; i++) {
|
|
5199
|
+
const arg = args[i];
|
|
5200
|
+
if (arg === "--public-key" || arg === "-k") {
|
|
5201
|
+
publicKey = args[++i];
|
|
5202
|
+
} else if (arg === "--list" || arg === "-l") {
|
|
5203
|
+
const list = args[++i];
|
|
5204
|
+
if (list) {
|
|
5205
|
+
procNodeIds = list.split(",").map((id) => id.trim());
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
if (!publicKey) {
|
|
5210
|
+
const configDir = getConfigDir();
|
|
5211
|
+
const keysDir = (0, import_path2.join)(configDir, "keys");
|
|
5212
|
+
const publicKeyPath = (0, import_path2.join)(keysDir, "edge-node.pub");
|
|
5213
|
+
if ((0, import_fs2.existsSync)(publicKeyPath)) {
|
|
5214
|
+
publicKey = (0, import_fs2.readFileSync)(publicKeyPath, "utf-8");
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
console.log(`\u{1F310} Registering ${procNodeIds.length} proc node(s) for edge execution...
|
|
5218
|
+
`);
|
|
5219
|
+
try {
|
|
5220
|
+
let edgeNodeId = config.edgeNodeId;
|
|
5221
|
+
if (!edgeNodeId) {
|
|
5222
|
+
console.log("\u{1F4E1} Registering edge node with backend...");
|
|
5223
|
+
const registerResult = await rainfall.executeTool("register-edge-node", {
|
|
5224
|
+
hostname: process.env.HOSTNAME || "local-edge",
|
|
5225
|
+
capabilities: procNodeIds,
|
|
5226
|
+
version: "1.0.0",
|
|
5227
|
+
metadata: {
|
|
5228
|
+
publicKey: publicKey || void 0,
|
|
5229
|
+
source: "rainfall-devkit-cli"
|
|
5230
|
+
}
|
|
5231
|
+
});
|
|
5232
|
+
edgeNodeId = registerResult.edgeNodeId;
|
|
5233
|
+
console.log(` Edge node registered: ${edgeNodeId}`);
|
|
5234
|
+
} else {
|
|
5235
|
+
console.log(` Using existing edge node: ${edgeNodeId}`);
|
|
5236
|
+
}
|
|
5237
|
+
console.log("\n\u{1F4E1} Registering proc nodes...");
|
|
5238
|
+
const result = await rainfall.executeTool("register-proc-edge-nodes", {
|
|
5239
|
+
edgeNodeId,
|
|
5240
|
+
procNodeIds,
|
|
5241
|
+
publicKey,
|
|
5242
|
+
hostname: process.env.HOSTNAME || "local-edge"
|
|
5243
|
+
});
|
|
5244
|
+
if (!result.success) {
|
|
5245
|
+
console.error("\u274C Registration failed");
|
|
5246
|
+
process.exit(1);
|
|
5247
|
+
}
|
|
5248
|
+
config.edgeNodeId = result.edgeNodeId;
|
|
5249
|
+
config.edgeNodeSecret = result.edgeNodeSecret;
|
|
5250
|
+
config.edgeNodeKeysPath = (0, import_path2.join)(getConfigDir(), "keys");
|
|
5251
|
+
saveConfig(config);
|
|
5252
|
+
console.log("\u2705 Proc node(s) registered successfully!\n");
|
|
5253
|
+
console.log("Edge Node ID:", result.edgeNodeId);
|
|
5254
|
+
console.log("Proc Nodes Registered:");
|
|
5255
|
+
for (const nodeId of result.registeredProcNodes) {
|
|
5256
|
+
console.log(` \u2022 ${nodeId}`);
|
|
5257
|
+
}
|
|
5258
|
+
console.log("\n\u{1F510} Edge node secret stored in config.");
|
|
5259
|
+
console.log(" This secret is used for authentication with the backend.");
|
|
5260
|
+
console.log("\n\u{1F4CB} You can now run tools on this edge node:");
|
|
5261
|
+
console.log(` rainfall run ${procNodeIds[0]} --target-edge ${result.edgeNodeId}`);
|
|
5262
|
+
} catch (error) {
|
|
5263
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5264
|
+
console.error("\u274C Failed to register proc node:", message);
|
|
5265
|
+
if (message.includes("not found") || message.includes("does not exist")) {
|
|
5266
|
+
console.error("\n\u{1F4A1} The backend may not have the registration tools yet.");
|
|
5267
|
+
console.error(" Make sure you are running the latest version of Rainyday.");
|
|
5268
|
+
}
|
|
5269
|
+
process.exit(1);
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
4209
5272
|
async function edgeStatus() {
|
|
4210
5273
|
const configDir = getConfigDir();
|
|
4211
5274
|
const keysDir = (0, import_path2.join)(configDir, "keys");
|
|
@@ -4223,23 +5286,36 @@ async function edgeStatus() {
|
|
|
4223
5286
|
console.log(" " + publicKey.substring(0, 50) + "...");
|
|
4224
5287
|
}
|
|
4225
5288
|
const config = loadConfig();
|
|
5289
|
+
console.log("\nRegistration:");
|
|
4226
5290
|
if (config.edgeNodeId) {
|
|
4227
|
-
console.log("\nRegistration:");
|
|
4228
5291
|
console.log(" Edge Node ID:", config.edgeNodeId);
|
|
5292
|
+
} else {
|
|
5293
|
+
console.log(" Edge Node ID: \u274C Not registered");
|
|
4229
5294
|
}
|
|
4230
5295
|
if (config.edgeNodeSecret) {
|
|
4231
|
-
console.log(" JWT Secret: \u2705 Present
|
|
5296
|
+
console.log(" JWT Secret: \u2705 Present");
|
|
5297
|
+
const masked = config.edgeNodeSecret.substring(0, 10) + "..." + config.edgeNodeSecret.substring(config.edgeNodeSecret.length - 4);
|
|
5298
|
+
console.log(" (" + masked + ")");
|
|
4232
5299
|
} else {
|
|
4233
5300
|
console.log(" JWT Secret: \u274C Not configured");
|
|
4234
5301
|
}
|
|
5302
|
+
if (config.procNodeIds && config.procNodeIds.length > 0) {
|
|
5303
|
+
console.log("\nRegistered Proc Nodes:");
|
|
5304
|
+
for (const nodeId of config.procNodeIds) {
|
|
5305
|
+
console.log(` \u2022 ${nodeId}`);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
4235
5308
|
console.log("\n\u{1F4DA} Next steps:");
|
|
4236
5309
|
if (!hasPublicKey) {
|
|
4237
5310
|
console.log(" 1. Run: rainfall edge generate-keys");
|
|
5311
|
+
console.log(" 2. Run: rainfall edge register <proc-node-id>");
|
|
4238
5312
|
} else if (!config.edgeNodeSecret) {
|
|
4239
|
-
console.log(" 1. Register your
|
|
4240
|
-
console.log("
|
|
5313
|
+
console.log(" 1. Register your proc node:");
|
|
5314
|
+
console.log(" rainfall edge register exa-web-search");
|
|
4241
5315
|
} else {
|
|
4242
5316
|
console.log(" Edge node is configured and ready for secure operation");
|
|
5317
|
+
console.log(" Run tools on this edge node:");
|
|
5318
|
+
console.log(` rainfall run <tool> --target-edge ${config.edgeNodeId}`);
|
|
4243
5319
|
}
|
|
4244
5320
|
}
|
|
4245
5321
|
async function main() {
|
|
@@ -4352,12 +5428,15 @@ async function main() {
|
|
|
4352
5428
|
case "generate-keys":
|
|
4353
5429
|
await edgeGenerateKeys();
|
|
4354
5430
|
break;
|
|
5431
|
+
case "register":
|
|
5432
|
+
await edgeRegister(rest);
|
|
5433
|
+
break;
|
|
4355
5434
|
case "status":
|
|
4356
5435
|
await edgeStatus();
|
|
4357
5436
|
break;
|
|
4358
5437
|
default:
|
|
4359
5438
|
console.error("Error: Unknown edge subcommand");
|
|
4360
|
-
console.error("\nUsage: rainfall edge <generate-keys|status>");
|
|
5439
|
+
console.error("\nUsage: rainfall edge <generate-keys|register|status>");
|
|
4361
5440
|
process.exit(1);
|
|
4362
5441
|
}
|
|
4363
5442
|
break;
|