@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.
Files changed (39) hide show
  1. package/dist/chunk-2FYYTIJQ.mjs +993 -0
  2. package/dist/chunk-6FXRLPLR.mjs +436 -0
  3. package/dist/chunk-CC4O7GSQ.mjs +978 -0
  4. package/dist/chunk-CQ5TV7CQ.mjs +989 -0
  5. package/dist/chunk-GPKQUVAV.mjs +987 -0
  6. package/dist/chunk-LJQEO3CY.mjs +150 -0
  7. package/dist/chunk-S7MOQCV4.mjs +137 -0
  8. package/dist/chunk-XHPFY5MH.mjs +132 -0
  9. package/dist/cli/index.js +1128 -49
  10. package/dist/cli/index.mjs +370 -30
  11. package/dist/daemon/index.d.mts +3 -3
  12. package/dist/daemon/index.d.ts +3 -3
  13. package/dist/daemon/index.js +416 -130
  14. package/dist/daemon/index.mjs +2 -1
  15. package/dist/display-KKJPO6UA.mjs +14 -0
  16. package/dist/errors-CY6HW2I5.mjs +24 -0
  17. package/dist/index.d.mts +66 -4
  18. package/dist/index.d.ts +66 -4
  19. package/dist/index.js +896 -113
  20. package/dist/index.mjs +18 -6
  21. package/dist/listeners-BBNBsJCk.d.ts +372 -0
  22. package/dist/listeners-BCEypw1u.d.ts +372 -0
  23. package/dist/listeners-BGdrWpkP.d.mts +372 -0
  24. package/dist/listeners-CMUKjEkb.d.mts +372 -0
  25. package/dist/listeners-CadPNUHd.d.ts +372 -0
  26. package/dist/listeners-Ckdj6D8T.d.mts +372 -0
  27. package/dist/mcp.d.mts +2 -2
  28. package/dist/mcp.d.ts +2 -2
  29. package/dist/mcp.js +410 -102
  30. package/dist/mcp.mjs +4 -2
  31. package/dist/param-parser-JVKB5FQK.mjs +12 -0
  32. package/dist/param-parser-PAKCNDBX.mjs +136 -0
  33. package/dist/sdk-BUVNdBc7.d.mts +1167 -0
  34. package/dist/sdk-BUVNdBc7.d.ts +1167 -0
  35. package/dist/sdk-Cl5Qzt4I.d.mts +1165 -0
  36. package/dist/sdk-Cl5Qzt4I.d.ts +1165 -0
  37. package/dist/sdk-DQKNbBce.d.mts +1162 -0
  38. package/dist/sdk-DQKNbBce.d.ts +1162 -0
  39. package/package.json +1 -1
@@ -1,10 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- createEdgeNodeSecurity
4
- } from "../chunk-NCQVOLS4.mjs";
3
+ createEdgeNodeSecurity,
4
+ globalHandlerRegistry
5
+ } from "../chunk-6FXRLPLR.mjs";
5
6
  import {
6
7
  Rainfall
7
- } from "../chunk-VDPKDC3R.mjs";
8
+ } from "../chunk-2FYYTIJQ.mjs";
9
+ import "../chunk-LJQEO3CY.mjs";
10
+ import {
11
+ formatResult
12
+ } from "../chunk-XHPFY5MH.mjs";
8
13
  import {
9
14
  getConfigDir,
10
15
  loadConfig,
@@ -49,6 +54,7 @@ Commands:
49
54
  config llm Show LLM configuration
50
55
 
51
56
  edge generate-keys Generate key pair for edge node encryption
57
+ edge register <proc-node-id> Register a proc node for edge execution
52
58
  edge status Show edge node security status
53
59
 
54
60
  version Show version information
@@ -66,7 +72,12 @@ Options for 'run':
66
72
  --params, -p <json> Tool parameters as JSON
67
73
  --file, -f <path> Read parameters from file
68
74
  --raw Output raw JSON
75
+ --table Output as table (if applicable)
76
+ --terminal Output for terminal consumption (minimal formatting)
69
77
  --<key> <value> Pass individual parameters (e.g., --query "AI news")
78
+ Arrays: --tickers AAPL,GOOGL (comma-separated)
79
+ Numbers: --count 42
80
+ Booleans: --enabled true
70
81
 
71
82
  Options for 'daemon start':
72
83
  --port <port> WebSocket port (default: 8765)
@@ -82,6 +93,7 @@ Examples:
82
93
  rainfall tools describe github-create-issue
83
94
  rainfall run exa-web-search -p '{"query": "AI news"}'
84
95
  rainfall run exa-web-search --query "AI news"
96
+ rainfall run finviz-quotes --tickers AAPL,GOOGL,MSFT
85
97
  rainfall run github-create-issue --owner facebook --repo react --title "Bug"
86
98
  rainfall run article-summarize -f ./article.json
87
99
  rainfall daemon start
@@ -99,6 +111,24 @@ function getRainfall() {
99
111
  baseUrl: config.baseUrl
100
112
  });
101
113
  }
114
+ async function fetchAllNodeIds(rainfall) {
115
+ try {
116
+ const client = rainfall.getClient();
117
+ const subscriberId = await client.ensureSubscriberId();
118
+ const result = await client.request(
119
+ `/olympic/subscribers/${subscriberId}/nodes/_utils/node-list`
120
+ );
121
+ if (result.keys && Array.isArray(result.keys)) {
122
+ return result.keys;
123
+ }
124
+ if (result.nodes && Array.isArray(result.nodes)) {
125
+ return result.nodes.map((n) => n.id);
126
+ }
127
+ return [];
128
+ } catch {
129
+ return [];
130
+ }
131
+ }
102
132
  async function authLogin(args) {
103
133
  const apiKey = args[0] || process.env.RAINFALL_API_KEY;
104
134
  if (!apiKey) {
@@ -185,6 +215,100 @@ function formatSchema(obj, indent = 0) {
185
215
  }
186
216
  return lines.join("\n");
187
217
  }
218
+ function levenshteinDistance(a, b) {
219
+ const matrix = [];
220
+ for (let i = 0; i <= b.length; i++) {
221
+ matrix[i] = [i];
222
+ }
223
+ for (let j = 0; j <= a.length; j++) {
224
+ matrix[0][j] = j;
225
+ }
226
+ for (let i = 1; i <= b.length; i++) {
227
+ for (let j = 1; j <= a.length; j++) {
228
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
229
+ matrix[i][j] = matrix[i - 1][j - 1];
230
+ } else {
231
+ matrix[i][j] = Math.min(
232
+ matrix[i - 1][j - 1] + 1,
233
+ // substitution
234
+ matrix[i][j - 1] + 1,
235
+ // insertion
236
+ matrix[i - 1][j] + 1
237
+ // deletion
238
+ );
239
+ }
240
+ }
241
+ }
242
+ return matrix[b.length][a.length];
243
+ }
244
+ function jaroWinklerSimilarity(a, b) {
245
+ if (a === b) return 1;
246
+ if (a.length === 0 || b.length === 0) return 0;
247
+ const matchDistance = Math.floor(Math.max(a.length, b.length) / 2) - 1;
248
+ const aMatches = new Array(a.length).fill(false);
249
+ const bMatches = new Array(b.length).fill(false);
250
+ let matches = 0;
251
+ let transpositions = 0;
252
+ for (let i = 0; i < a.length; i++) {
253
+ const start = Math.max(0, i - matchDistance);
254
+ const end = Math.min(i + matchDistance + 1, b.length);
255
+ for (let j = start; j < end; j++) {
256
+ if (bMatches[j] || a.charAt(i) !== b.charAt(j)) continue;
257
+ aMatches[i] = true;
258
+ bMatches[j] = true;
259
+ matches++;
260
+ break;
261
+ }
262
+ }
263
+ if (matches === 0) return 0;
264
+ let k = 0;
265
+ for (let i = 0; i < a.length; i++) {
266
+ if (!aMatches[i]) continue;
267
+ while (!bMatches[k]) k++;
268
+ if (a.charAt(i) !== b.charAt(k)) transpositions++;
269
+ k++;
270
+ }
271
+ const jaro = (matches / a.length + matches / b.length + (matches - transpositions / 2) / matches) / 3;
272
+ let prefixLength = 0;
273
+ for (let i = 0; i < Math.min(a.length, b.length); i++) {
274
+ if (a.charAt(i) === b.charAt(i)) {
275
+ prefixLength++;
276
+ } else {
277
+ break;
278
+ }
279
+ }
280
+ const scalingFactor = 0.1;
281
+ return jaro + prefixLength * scalingFactor * (1 - jaro);
282
+ }
283
+ function calculateSimilarity(toolId, candidateId, description = "") {
284
+ const lowerToolId = toolId.toLowerCase();
285
+ const lowerCandidate = candidateId.toLowerCase();
286
+ const prefix = lowerToolId.split("-")[0];
287
+ const hasPrefix = lowerToolId.includes("-");
288
+ const jwScore = jaroWinklerSimilarity(lowerToolId, lowerCandidate);
289
+ const maxLen = Math.max(lowerToolId.length, lowerCandidate.length);
290
+ const lvScore = maxLen === 0 ? 1 : 1 - levenshteinDistance(lowerToolId, lowerCandidate) / maxLen;
291
+ let substringBoost = 0;
292
+ if (lowerCandidate.includes(lowerToolId) || lowerToolId.includes(lowerCandidate)) {
293
+ substringBoost = 0.4;
294
+ }
295
+ let prefixBoost = 0;
296
+ if (hasPrefix && lowerCandidate === prefix) {
297
+ prefixBoost = 0.5;
298
+ }
299
+ if (hasPrefix && lowerCandidate.startsWith(prefix + "-")) {
300
+ prefixBoost = 0.35;
301
+ }
302
+ const descMatch = description.toLowerCase().includes(lowerToolId) ? 0.1 : 0;
303
+ return jwScore * 0.4 + lvScore * 0.25 + substringBoost + prefixBoost + descMatch;
304
+ }
305
+ function findSimilarToolIds(toolId, toolIds) {
306
+ const scored = toolIds.map((id) => ({
307
+ id,
308
+ score: calculateSimilarity(toolId, id)
309
+ }));
310
+ return scored.filter((item) => item.score > 0.35).sort((a, b) => b.score - a.score).slice(0, 5).map((item) => item.id);
311
+ }
188
312
  async function describeTool(args) {
189
313
  const toolId = args[0];
190
314
  if (!toolId) {
@@ -220,6 +344,17 @@ async function describeTool(args) {
220
344
  console.log();
221
345
  } catch (error) {
222
346
  console.error(`Error: Tool '${toolId}' not found`);
347
+ try {
348
+ const allNodeIds = await fetchAllNodeIds(rainfall);
349
+ const suggestions = findSimilarToolIds(toolId, allNodeIds);
350
+ if (suggestions.length > 0) {
351
+ console.error("\nDid you mean:");
352
+ for (const suggestion of suggestions) {
353
+ console.error(` \u2022 ${suggestion}`);
354
+ }
355
+ }
356
+ } catch {
357
+ }
223
358
  process.exit(1);
224
359
  }
225
360
  }
@@ -263,20 +398,30 @@ Options:
263
398
  -p, --params <json> Tool parameters as JSON string
264
399
  -f, --file <path> Read parameters from JSON file
265
400
  --raw Output raw JSON (no formatting)
401
+ --table Output as table (if applicable)
402
+ --terminal Output for terminal consumption (minimal formatting)
403
+ --target-edge <id> Execute on specific edge node (for cross-node jobs)
266
404
  --<key> <value> Pass individual parameters (e.g., --query "AI news")
405
+ Arrays: --tickers AAPL,GOOGL (comma-separated)
406
+ Numbers: --count 42
407
+ Booleans: --enabled true
267
408
 
268
409
  Examples:
269
410
  rainfall run figma-users-getMe
270
411
  rainfall run exa-web-search -p '{"query": "AI news"}'
271
412
  rainfall run exa-web-search --query "AI news"
413
+ rainfall run finviz-quotes --tickers AAPL,GOOGL,MSFT
272
414
  rainfall run github-create-issue --owner facebook --repo react --title "Bug"
273
415
  rainfall run github-create-issue -f ./issue.json
416
+ rainfall run exa-web-search --query "latest AI" --target-edge <edge-id>
274
417
  echo '{"query": "hello"}' | rainfall run exa-web-search
275
418
  `);
276
419
  return;
277
420
  }
278
421
  let params = {};
279
422
  const rawArgs = [];
423
+ let displayMode = "pretty";
424
+ let targetEdge;
280
425
  for (let i = 1; i < args.length; i++) {
281
426
  const arg = args[i];
282
427
  if (arg === "--params" || arg === "-p") {
@@ -304,16 +449,23 @@ Examples:
304
449
  process.exit(1);
305
450
  }
306
451
  } else if (arg === "--raw") {
452
+ displayMode = "raw";
453
+ } else if (arg === "--table") {
454
+ displayMode = "table";
455
+ } else if (arg === "--terminal") {
456
+ displayMode = "terminal";
457
+ } else if (arg === "--target-edge") {
458
+ targetEdge = args[++i];
459
+ if (!targetEdge) {
460
+ console.error("Error: --target-edge requires an edge node ID");
461
+ process.exit(1);
462
+ }
307
463
  } else if (arg.startsWith("--")) {
308
464
  const key = arg.slice(2);
309
465
  const value = args[++i];
310
466
  if (value === void 0) {
311
- console.error(`Error: ${arg} requires a value`);
312
- process.exit(1);
313
- }
314
- try {
315
- params[key] = JSON.parse(value);
316
- } catch {
467
+ params[key] = true;
468
+ } else {
317
469
  params[key] = value;
318
470
  }
319
471
  } else {
@@ -355,30 +507,110 @@ Examples:
355
507
  }
356
508
  }
357
509
  const rainfall = getRainfall();
358
- if (rawArgs.length === 1 && Object.keys(params).length === 0) {
359
- try {
360
- const schema = await rainfall.getToolSchema(toolId);
361
- if (schema.parameters && typeof schema.parameters === "object") {
362
- const paramEntries = Object.entries(schema.parameters);
363
- const requiredParams = paramEntries.filter(([, p]) => !p.optional);
364
- if (requiredParams.length === 1) {
365
- const [paramName] = requiredParams[0];
366
- params = { [paramName]: rawArgs[0] };
367
- }
510
+ let toolSchema;
511
+ try {
512
+ const fullSchema = await rainfall.getToolSchema(toolId);
513
+ toolSchema = {
514
+ parameters: fullSchema.parameters
515
+ };
516
+ } catch {
517
+ }
518
+ const cliFlags = /* @__PURE__ */ new Set(["--params", "-p", "--file", "-f", "--raw", "--table", "--terminal", "--target-edge"]);
519
+ const toolArgs = args.slice(1).filter((arg, i, arr) => {
520
+ if (cliFlags.has(arg)) {
521
+ return false;
522
+ }
523
+ if (i > 0 && cliFlags.has(arr[i - 1])) {
524
+ return false;
525
+ }
526
+ return true;
527
+ });
528
+ if (toolSchema?.parameters) {
529
+ const { parseCliArgs } = await import("../param-parser-PAKCNDBX.mjs");
530
+ const parsedParams = parseCliArgs(
531
+ toolArgs,
532
+ {
533
+ name: toolId,
534
+ description: "",
535
+ category: "",
536
+ parameters: toolSchema.parameters
368
537
  }
369
- } catch {
538
+ );
539
+ params = { ...parsedParams, ...params };
540
+ }
541
+ if (rawArgs.length === 1 && Object.keys(params).length === 0 && toolSchema?.parameters) {
542
+ const paramEntries = Object.entries(toolSchema.parameters);
543
+ const requiredParams = paramEntries.filter(([, p]) => !p.optional);
544
+ if (requiredParams.length === 1) {
545
+ const [paramName, paramSchema] = requiredParams[0];
546
+ const { parseValue } = await import("../param-parser-PAKCNDBX.mjs");
547
+ params = { [paramName]: parseValue(rawArgs[0], paramSchema) };
370
548
  }
371
549
  }
550
+ const handler = globalHandlerRegistry.findHandler(toolId);
551
+ const toolContext = {
552
+ rainfall,
553
+ toolId,
554
+ params,
555
+ args: rawArgs,
556
+ flags: { raw: displayMode === "raw" }
557
+ };
372
558
  try {
373
- const result = await rainfall.executeTool(toolId, params);
374
- if (args.includes("--raw")) {
375
- console.log(JSON.stringify(result));
559
+ let executionParams = params;
560
+ let preflightContext;
561
+ let skipExecution;
562
+ if (handler?.preflight) {
563
+ const preflightResult = await handler.preflight(toolContext);
564
+ if (preflightResult) {
565
+ if (preflightResult.skipExecution !== void 0) {
566
+ skipExecution = preflightResult.skipExecution;
567
+ }
568
+ if (preflightResult.params) {
569
+ executionParams = preflightResult.params;
570
+ }
571
+ preflightContext = preflightResult.context;
572
+ }
573
+ }
574
+ let result;
575
+ if (skipExecution !== void 0) {
576
+ result = skipExecution;
577
+ } else if (targetEdge) {
578
+ result = await rainfall.executeTool(toolId, executionParams, { targetEdge });
376
579
  } else {
377
- console.log(JSON.stringify(result, null, 2));
580
+ result = await rainfall.executeTool(toolId, executionParams);
581
+ }
582
+ const postflightContext = {
583
+ ...toolContext,
584
+ result,
585
+ preflightContext
586
+ };
587
+ if (handler?.postflight) {
588
+ await handler.postflight(postflightContext);
589
+ }
590
+ let displayed = false;
591
+ if (handler?.display) {
592
+ displayed = await handler.display({ ...postflightContext, flags: { ...toolContext.flags, mode: displayMode } });
593
+ }
594
+ if (!displayed) {
595
+ const output = await formatResult(result, { mode: displayMode });
596
+ console.log(output);
378
597
  }
379
598
  } catch (error) {
380
599
  const message = error instanceof Error ? error.message : String(error);
381
600
  console.error(`Error: ${message}`);
601
+ if (message.toLowerCase().includes("not found") || message.toLowerCase().includes("not found")) {
602
+ try {
603
+ const allNodeIds = await fetchAllNodeIds(rainfall);
604
+ const suggestions = findSimilarToolIds(toolId, allNodeIds);
605
+ if (suggestions.length > 0) {
606
+ console.error("\nDid you mean:");
607
+ for (const suggestion of suggestions) {
608
+ console.error(` \u2022 ${suggestion}`);
609
+ }
610
+ }
611
+ } catch {
612
+ }
613
+ }
382
614
  process.exit(1);
383
615
  }
384
616
  }
@@ -671,7 +903,7 @@ async function edgeGenerateKeys() {
671
903
  console.log(" Private:", privateKeyPath);
672
904
  console.log("\n\u{1F4CB} To register this edge node:");
673
905
  console.log(" 1. Copy the public key above");
674
- console.log(" 2. Register with: rainfall edge register <public-key>");
906
+ console.log(" 2. Register proc node with: rainfall edge register <proc-node-id> --public-key <key>");
675
907
  console.log(" 3. The backend will return an edgeNodeSecret (JWT)");
676
908
  console.log(" 4. Store the secret securely - it expires in 30 days");
677
909
  } catch (error) {
@@ -679,6 +911,98 @@ async function edgeGenerateKeys() {
679
911
  process.exit(1);
680
912
  }
681
913
  }
914
+ async function edgeRegister(args) {
915
+ const procNodeId = args[0];
916
+ if (!procNodeId) {
917
+ console.error("Error: Proc node ID required");
918
+ console.error("\nUsage: rainfall edge register <proc-node-id> [options]");
919
+ console.error("\nOptions:");
920
+ console.error(" --public-key <key> Public key for encryption (optional)");
921
+ console.error(" --list <id1,id2,...> Register multiple proc nodes (comma-separated)");
922
+ console.error("\nExamples:");
923
+ console.error(" rainfall edge register exa-web-search");
924
+ console.error(' rainfall edge register exa-web-search --public-key "base64key..."');
925
+ console.error(' rainfall edge register --list "exa-web-search,github-create-issue"');
926
+ process.exit(1);
927
+ }
928
+ const rainfall = getRainfall();
929
+ const config = loadConfig();
930
+ let publicKey;
931
+ let procNodeIds = [procNodeId];
932
+ for (let i = 1; i < args.length; i++) {
933
+ const arg = args[i];
934
+ if (arg === "--public-key" || arg === "-k") {
935
+ publicKey = args[++i];
936
+ } else if (arg === "--list" || arg === "-l") {
937
+ const list = args[++i];
938
+ if (list) {
939
+ procNodeIds = list.split(",").map((id) => id.trim());
940
+ }
941
+ }
942
+ }
943
+ if (!publicKey) {
944
+ const configDir = getConfigDir();
945
+ const keysDir = join(configDir, "keys");
946
+ const publicKeyPath = join(keysDir, "edge-node.pub");
947
+ if (existsSync(publicKeyPath)) {
948
+ publicKey = readFileSync(publicKeyPath, "utf-8");
949
+ }
950
+ }
951
+ console.log(`\u{1F310} Registering ${procNodeIds.length} proc node(s) for edge execution...
952
+ `);
953
+ try {
954
+ let edgeNodeId = config.edgeNodeId;
955
+ if (!edgeNodeId) {
956
+ console.log("\u{1F4E1} Registering edge node with backend...");
957
+ const registerResult = await rainfall.executeTool("register-edge-node", {
958
+ hostname: process.env.HOSTNAME || "local-edge",
959
+ capabilities: procNodeIds,
960
+ version: "1.0.0",
961
+ metadata: {
962
+ publicKey: publicKey || void 0,
963
+ source: "rainfall-devkit-cli"
964
+ }
965
+ });
966
+ edgeNodeId = registerResult.edgeNodeId;
967
+ console.log(` Edge node registered: ${edgeNodeId}`);
968
+ } else {
969
+ console.log(` Using existing edge node: ${edgeNodeId}`);
970
+ }
971
+ console.log("\n\u{1F4E1} Registering proc nodes...");
972
+ const result = await rainfall.executeTool("register-proc-edge-nodes", {
973
+ edgeNodeId,
974
+ procNodeIds,
975
+ publicKey,
976
+ hostname: process.env.HOSTNAME || "local-edge"
977
+ });
978
+ if (!result.success) {
979
+ console.error("\u274C Registration failed");
980
+ process.exit(1);
981
+ }
982
+ config.edgeNodeId = result.edgeNodeId;
983
+ config.edgeNodeSecret = result.edgeNodeSecret;
984
+ config.edgeNodeKeysPath = join(getConfigDir(), "keys");
985
+ saveConfig(config);
986
+ console.log("\u2705 Proc node(s) registered successfully!\n");
987
+ console.log("Edge Node ID:", result.edgeNodeId);
988
+ console.log("Proc Nodes Registered:");
989
+ for (const nodeId of result.registeredProcNodes) {
990
+ console.log(` \u2022 ${nodeId}`);
991
+ }
992
+ console.log("\n\u{1F510} Edge node secret stored in config.");
993
+ console.log(" This secret is used for authentication with the backend.");
994
+ console.log("\n\u{1F4CB} You can now run tools on this edge node:");
995
+ console.log(` rainfall run ${procNodeIds[0]} --target-edge ${result.edgeNodeId}`);
996
+ } catch (error) {
997
+ const message = error instanceof Error ? error.message : String(error);
998
+ console.error("\u274C Failed to register proc node:", message);
999
+ if (message.includes("not found") || message.includes("does not exist")) {
1000
+ console.error("\n\u{1F4A1} The backend may not have the registration tools yet.");
1001
+ console.error(" Make sure you are running the latest version of Rainyday.");
1002
+ }
1003
+ process.exit(1);
1004
+ }
1005
+ }
682
1006
  async function edgeStatus() {
683
1007
  const configDir = getConfigDir();
684
1008
  const keysDir = join(configDir, "keys");
@@ -696,23 +1020,36 @@ async function edgeStatus() {
696
1020
  console.log(" " + publicKey.substring(0, 50) + "...");
697
1021
  }
698
1022
  const config = loadConfig();
1023
+ console.log("\nRegistration:");
699
1024
  if (config.edgeNodeId) {
700
- console.log("\nRegistration:");
701
1025
  console.log(" Edge Node ID:", config.edgeNodeId);
1026
+ } else {
1027
+ console.log(" Edge Node ID: \u274C Not registered");
702
1028
  }
703
1029
  if (config.edgeNodeSecret) {
704
- console.log(" JWT Secret: \u2705 Present (expires: check with backend)");
1030
+ console.log(" JWT Secret: \u2705 Present");
1031
+ const masked = config.edgeNodeSecret.substring(0, 10) + "..." + config.edgeNodeSecret.substring(config.edgeNodeSecret.length - 4);
1032
+ console.log(" (" + masked + ")");
705
1033
  } else {
706
1034
  console.log(" JWT Secret: \u274C Not configured");
707
1035
  }
1036
+ if (config.procNodeIds && config.procNodeIds.length > 0) {
1037
+ console.log("\nRegistered Proc Nodes:");
1038
+ for (const nodeId of config.procNodeIds) {
1039
+ console.log(` \u2022 ${nodeId}`);
1040
+ }
1041
+ }
708
1042
  console.log("\n\u{1F4DA} Next steps:");
709
1043
  if (!hasPublicKey) {
710
1044
  console.log(" 1. Run: rainfall edge generate-keys");
1045
+ console.log(" 2. Run: rainfall edge register <proc-node-id>");
711
1046
  } else if (!config.edgeNodeSecret) {
712
- console.log(" 1. Register your edge node with the backend");
713
- console.log(" 2. Store the returned edgeNodeSecret in config");
1047
+ console.log(" 1. Register your proc node:");
1048
+ console.log(" rainfall edge register exa-web-search");
714
1049
  } else {
715
1050
  console.log(" Edge node is configured and ready for secure operation");
1051
+ console.log(" Run tools on this edge node:");
1052
+ console.log(` rainfall run <tool> --target-edge ${config.edgeNodeId}`);
716
1053
  }
717
1054
  }
718
1055
  async function main() {
@@ -825,12 +1162,15 @@ async function main() {
825
1162
  case "generate-keys":
826
1163
  await edgeGenerateKeys();
827
1164
  break;
1165
+ case "register":
1166
+ await edgeRegister(rest);
1167
+ break;
828
1168
  case "status":
829
1169
  await edgeStatus();
830
1170
  break;
831
1171
  default:
832
1172
  console.error("Error: Unknown edge subcommand");
833
- console.error("\nUsage: rainfall edge <generate-keys|status>");
1173
+ console.error("\nUsage: rainfall edge <generate-keys|register|status>");
834
1174
  process.exit(1);
835
1175
  }
836
1176
  break;
@@ -1,6 +1,6 @@
1
- import { R as RainfallConfig, g as MCPClientConfig, h as MCPProxyHub } from '../sdk-4OvXPr8E.mjs';
2
- export { i as MCPClientInfo, j as MCPToolInfo, k as MCPTransportType } from '../sdk-4OvXPr8E.mjs';
3
- import { N as NetworkedExecutorOptions, C as ContextOptions, R as RainfallNetworkedExecutor, a as RainfallDaemonContext, b as RainfallListenerRegistry } from '../listeners-MNAnpZj-.mjs';
1
+ import { R as RainfallConfig, g as MCPClientConfig, h as MCPProxyHub } from '../sdk-BUVNdBc7.mjs';
2
+ export { i as MCPClientInfo, j as MCPToolInfo, k as MCPTransportType } from '../sdk-BUVNdBc7.mjs';
3
+ import { N as NetworkedExecutorOptions, C as ContextOptions, R as RainfallNetworkedExecutor, a as RainfallDaemonContext, b as RainfallListenerRegistry } from '../listeners-BGdrWpkP.mjs';
4
4
  import 'ws';
5
5
  import '@modelcontextprotocol/sdk/client/index.js';
6
6
  import '@modelcontextprotocol/sdk/client/stdio.js';
@@ -1,6 +1,6 @@
1
- import { R as RainfallConfig, g as MCPClientConfig, h as MCPProxyHub } from '../sdk-4OvXPr8E.js';
2
- export { i as MCPClientInfo, j as MCPToolInfo, k as MCPTransportType } from '../sdk-4OvXPr8E.js';
3
- import { N as NetworkedExecutorOptions, C as ContextOptions, R as RainfallNetworkedExecutor, a as RainfallDaemonContext, b as RainfallListenerRegistry } from '../listeners-B5Vy9Ao5.js';
1
+ import { R as RainfallConfig, g as MCPClientConfig, h as MCPProxyHub } from '../sdk-BUVNdBc7.js';
2
+ export { i as MCPClientInfo, j as MCPToolInfo, k as MCPTransportType } from '../sdk-BUVNdBc7.js';
3
+ import { N as NetworkedExecutorOptions, C as ContextOptions, R as RainfallNetworkedExecutor, a as RainfallDaemonContext, b as RainfallListenerRegistry } from '../listeners-BCEypw1u.js';
4
4
  import 'ws';
5
5
  import '@modelcontextprotocol/sdk/client/index.js';
6
6
  import '@modelcontextprotocol/sdk/client/stdio.js';