@rainfall-devkit/sdk 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +51 -0
  2. package/dist/chunk-7MRE4ZVI.mjs +662 -0
  3. package/dist/chunk-AQFC7YAX.mjs +27 -0
  4. package/dist/chunk-V5QWJVLC.mjs +662 -0
  5. package/dist/chunk-VDPKDC3R.mjs +869 -0
  6. package/dist/chunk-WOITG5TG.mjs +84 -0
  7. package/dist/cli/index.js +2799 -713
  8. package/dist/cli/index.mjs +354 -36
  9. package/dist/config-DDTQQBN7.mjs +14 -0
  10. package/dist/config-ZKNHII2A.mjs +8 -0
  11. package/dist/daemon/index.d.mts +136 -0
  12. package/dist/daemon/index.d.ts +136 -0
  13. package/dist/daemon/index.js +2473 -0
  14. package/dist/daemon/index.mjs +836 -0
  15. package/dist/errors-BMPseAnM.d.mts +47 -0
  16. package/dist/errors-BMPseAnM.d.ts +47 -0
  17. package/dist/errors-CZdRoYyw.d.ts +332 -0
  18. package/dist/errors-Chjq1Mev.d.mts +332 -0
  19. package/dist/index.d.mts +3 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.js +759 -3
  22. package/dist/index.mjs +14 -2
  23. package/dist/listeners-BbYIaNCs.d.mts +372 -0
  24. package/dist/listeners-CP2A9J_2.d.ts +372 -0
  25. package/dist/listeners-CTRSofnm.d.mts +372 -0
  26. package/dist/listeners-CYI-YwIF.d.mts +372 -0
  27. package/dist/listeners-QJeEtLbV.d.ts +372 -0
  28. package/dist/listeners-hp0Ib2Ox.d.ts +372 -0
  29. package/dist/mcp.d.mts +3 -2
  30. package/dist/mcp.d.ts +3 -2
  31. package/dist/mcp.js +92 -1
  32. package/dist/mcp.mjs +1 -1
  33. package/dist/sdk-CJ9g5lFo.d.mts +772 -0
  34. package/dist/sdk-CJ9g5lFo.d.ts +772 -0
  35. package/dist/sdk-DD1OeGRJ.d.mts +871 -0
  36. package/dist/sdk-DD1OeGRJ.d.ts +871 -0
  37. package/dist/types-GnRAfH-h.d.mts +489 -0
  38. package/dist/types-GnRAfH-h.d.ts +489 -0
  39. package/package.json +14 -5
package/dist/cli/index.js CHANGED
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
9
16
  var __copyProps = (to, from, except, desc) => {
10
17
  if (from && typeof from === "object" || typeof from === "function") {
11
18
  for (let key of __getOwnPropNames(from))
@@ -23,93 +30,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
30
  mod
24
31
  ));
25
32
 
26
- // src/cli/index.ts
27
- var import_fs = require("fs");
28
- var import_path = require("path");
29
- var import_os = require("os");
33
+ // node_modules/tsup/assets/cjs_shims.js
34
+ var getImportMetaUrl, importMetaUrl;
35
+ var init_cjs_shims = __esm({
36
+ "node_modules/tsup/assets/cjs_shims.js"() {
37
+ "use strict";
38
+ getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
39
+ importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
40
+ }
41
+ });
30
42
 
31
43
  // src/errors.ts
32
- var RainfallError = class _RainfallError extends Error {
33
- constructor(message, code, statusCode, details) {
34
- super(message);
35
- this.code = code;
36
- this.statusCode = statusCode;
37
- this.details = details;
38
- this.name = "RainfallError";
39
- Object.setPrototypeOf(this, _RainfallError.prototype);
40
- }
41
- toJSON() {
42
- return {
43
- name: this.name,
44
- code: this.code,
45
- message: this.message,
46
- statusCode: this.statusCode,
47
- details: this.details
48
- };
49
- }
50
- };
51
- var AuthenticationError = class _AuthenticationError extends RainfallError {
52
- constructor(message = "Invalid API key", details) {
53
- super(message, "AUTHENTICATION_ERROR", 401, details);
54
- this.name = "AuthenticationError";
55
- Object.setPrototypeOf(this, _AuthenticationError.prototype);
56
- }
57
- };
58
- var RateLimitError = class _RateLimitError extends RainfallError {
59
- retryAfter;
60
- limit;
61
- remaining;
62
- resetAt;
63
- constructor(message = "Rate limit exceeded", retryAfter = 60, limit = 0, remaining = 0, resetAt) {
64
- super(message, "RATE_LIMIT_ERROR", 429, { retryAfter, limit, remaining });
65
- this.name = "RateLimitError";
66
- this.retryAfter = retryAfter;
67
- this.limit = limit;
68
- this.remaining = remaining;
69
- this.resetAt = resetAt || new Date(Date.now() + retryAfter * 1e3);
70
- Object.setPrototypeOf(this, _RateLimitError.prototype);
71
- }
72
- };
73
- var ValidationError = class _ValidationError extends RainfallError {
74
- constructor(message, details) {
75
- super(message, "VALIDATION_ERROR", 400, details);
76
- this.name = "ValidationError";
77
- Object.setPrototypeOf(this, _ValidationError.prototype);
78
- }
79
- };
80
- var NotFoundError = class _NotFoundError extends RainfallError {
81
- constructor(resource, identifier) {
82
- super(
83
- `${resource}${identifier ? ` '${identifier}'` : ""} not found`,
84
- "NOT_FOUND_ERROR",
85
- 404,
86
- { resource, identifier }
87
- );
88
- this.name = "NotFoundError";
89
- Object.setPrototypeOf(this, _NotFoundError.prototype);
90
- }
91
- };
92
- var ServerError = class _ServerError extends RainfallError {
93
- constructor(message = "Internal server error", statusCode = 500) {
94
- super(message, "SERVER_ERROR", statusCode);
95
- this.name = "ServerError";
96
- Object.setPrototypeOf(this, _ServerError.prototype);
97
- }
98
- };
99
- var TimeoutError = class _TimeoutError extends RainfallError {
100
- constructor(timeoutMs) {
101
- super(`Request timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR", 408);
102
- this.name = "TimeoutError";
103
- Object.setPrototypeOf(this, _TimeoutError.prototype);
104
- }
105
- };
106
- var NetworkError = class _NetworkError extends RainfallError {
107
- constructor(message = "Network error", details) {
108
- super(message, "NETWORK_ERROR", void 0, details);
109
- this.name = "NetworkError";
110
- Object.setPrototypeOf(this, _NetworkError.prototype);
111
- }
112
- };
113
44
  function parseErrorResponse(response, data) {
114
45
  const statusCode = response.status;
115
46
  if (statusCode === 429) {
@@ -158,663 +89,2476 @@ function parseErrorResponse(response, data) {
158
89
  );
159
90
  }
160
91
  }
92
+ var RainfallError, AuthenticationError, RateLimitError, ValidationError, NotFoundError, ServerError, TimeoutError, NetworkError;
93
+ var init_errors = __esm({
94
+ "src/errors.ts"() {
95
+ "use strict";
96
+ init_cjs_shims();
97
+ RainfallError = class _RainfallError extends Error {
98
+ constructor(message, code, statusCode, details) {
99
+ super(message);
100
+ this.code = code;
101
+ this.statusCode = statusCode;
102
+ this.details = details;
103
+ this.name = "RainfallError";
104
+ Object.setPrototypeOf(this, _RainfallError.prototype);
105
+ }
106
+ toJSON() {
107
+ return {
108
+ name: this.name,
109
+ code: this.code,
110
+ message: this.message,
111
+ statusCode: this.statusCode,
112
+ details: this.details
113
+ };
114
+ }
115
+ };
116
+ AuthenticationError = class _AuthenticationError extends RainfallError {
117
+ constructor(message = "Invalid API key", details) {
118
+ super(message, "AUTHENTICATION_ERROR", 401, details);
119
+ this.name = "AuthenticationError";
120
+ Object.setPrototypeOf(this, _AuthenticationError.prototype);
121
+ }
122
+ };
123
+ RateLimitError = class _RateLimitError extends RainfallError {
124
+ retryAfter;
125
+ limit;
126
+ remaining;
127
+ resetAt;
128
+ constructor(message = "Rate limit exceeded", retryAfter = 60, limit = 0, remaining = 0, resetAt) {
129
+ super(message, "RATE_LIMIT_ERROR", 429, { retryAfter, limit, remaining });
130
+ this.name = "RateLimitError";
131
+ this.retryAfter = retryAfter;
132
+ this.limit = limit;
133
+ this.remaining = remaining;
134
+ this.resetAt = resetAt || new Date(Date.now() + retryAfter * 1e3);
135
+ Object.setPrototypeOf(this, _RateLimitError.prototype);
136
+ }
137
+ };
138
+ ValidationError = class _ValidationError extends RainfallError {
139
+ constructor(message, details) {
140
+ super(message, "VALIDATION_ERROR", 400, details);
141
+ this.name = "ValidationError";
142
+ Object.setPrototypeOf(this, _ValidationError.prototype);
143
+ }
144
+ };
145
+ NotFoundError = class _NotFoundError extends RainfallError {
146
+ constructor(resource, identifier) {
147
+ super(
148
+ `${resource}${identifier ? ` '${identifier}'` : ""} not found`,
149
+ "NOT_FOUND_ERROR",
150
+ 404,
151
+ { resource, identifier }
152
+ );
153
+ this.name = "NotFoundError";
154
+ Object.setPrototypeOf(this, _NotFoundError.prototype);
155
+ }
156
+ };
157
+ ServerError = class _ServerError extends RainfallError {
158
+ constructor(message = "Internal server error", statusCode = 500) {
159
+ super(message, "SERVER_ERROR", statusCode);
160
+ this.name = "ServerError";
161
+ Object.setPrototypeOf(this, _ServerError.prototype);
162
+ }
163
+ };
164
+ TimeoutError = class _TimeoutError extends RainfallError {
165
+ constructor(timeoutMs) {
166
+ super(`Request timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR", 408);
167
+ this.name = "TimeoutError";
168
+ Object.setPrototypeOf(this, _TimeoutError.prototype);
169
+ }
170
+ };
171
+ NetworkError = class _NetworkError extends RainfallError {
172
+ constructor(message = "Network error", details) {
173
+ super(message, "NETWORK_ERROR", void 0, details);
174
+ this.name = "NetworkError";
175
+ Object.setPrototypeOf(this, _NetworkError.prototype);
176
+ }
177
+ };
178
+ }
179
+ });
161
180
 
162
181
  // src/client.ts
163
- var DEFAULT_BASE_URL = "https://olympic-api.pragma-digital.org/v1";
164
- var DEFAULT_TIMEOUT = 3e4;
165
- var DEFAULT_RETRIES = 3;
166
- var DEFAULT_RETRY_DELAY = 1e3;
167
- var RainfallClient = class {
168
- apiKey;
169
- baseUrl;
170
- defaultTimeout;
171
- defaultRetries;
172
- defaultRetryDelay;
173
- lastRateLimitInfo;
174
- subscriberId;
175
- constructor(config) {
176
- this.apiKey = config.apiKey;
177
- this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
178
- this.defaultTimeout = config.timeout || DEFAULT_TIMEOUT;
179
- this.defaultRetries = config.retries ?? DEFAULT_RETRIES;
180
- this.defaultRetryDelay = config.retryDelay || DEFAULT_RETRY_DELAY;
181
- }
182
- /**
183
- * Get the last rate limit info from the API
184
- */
185
- getRateLimitInfo() {
186
- return this.lastRateLimitInfo;
187
- }
188
- /**
189
- * Make an authenticated request to the Rainfall API
190
- */
191
- async request(path, options = {}, requestOptions) {
192
- const timeout = requestOptions?.timeout ?? this.defaultTimeout;
193
- const maxRetries = requestOptions?.retries ?? this.defaultRetries;
194
- const retryDelay = requestOptions?.retryDelay ?? this.defaultRetryDelay;
195
- const url = `${this.baseUrl}${path}`;
196
- const method = options.method || "GET";
197
- let lastError;
198
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
199
- try {
200
- const controller = new AbortController();
201
- const timeoutId = setTimeout(() => controller.abort(), timeout);
202
- const response = await fetch(url, {
203
- method,
204
- headers: {
205
- "x-api-key": this.apiKey,
206
- "Content-Type": "application/json",
207
- "Accept": "application/json",
208
- "X-Rainfall-SDK-Version": "0.1.0",
209
- ...options.headers
210
- },
211
- body: options.body ? JSON.stringify(options.body) : void 0,
212
- signal: controller.signal
213
- });
214
- clearTimeout(timeoutId);
215
- const limit = response.headers.get("x-ratelimit-limit");
216
- const remaining = response.headers.get("x-ratelimit-remaining");
217
- const reset = response.headers.get("x-ratelimit-reset");
218
- if (limit && remaining && reset) {
219
- this.lastRateLimitInfo = {
220
- limit: parseInt(limit, 10),
221
- remaining: parseInt(remaining, 10),
222
- resetAt: new Date(parseInt(reset, 10) * 1e3)
223
- };
224
- }
225
- let data;
226
- const contentType = response.headers.get("content-type");
227
- if (contentType?.includes("application/json")) {
228
- data = await response.json();
229
- } else {
230
- data = await response.text();
182
+ var DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_RETRIES, DEFAULT_RETRY_DELAY, RainfallClient;
183
+ var init_client = __esm({
184
+ "src/client.ts"() {
185
+ "use strict";
186
+ init_cjs_shims();
187
+ init_errors();
188
+ DEFAULT_BASE_URL = "https://olympic-api.pragma-digital.org/v1";
189
+ DEFAULT_TIMEOUT = 3e4;
190
+ DEFAULT_RETRIES = 3;
191
+ DEFAULT_RETRY_DELAY = 1e3;
192
+ RainfallClient = class {
193
+ apiKey;
194
+ baseUrl;
195
+ defaultTimeout;
196
+ defaultRetries;
197
+ defaultRetryDelay;
198
+ lastRateLimitInfo;
199
+ subscriberId;
200
+ constructor(config) {
201
+ this.apiKey = config.apiKey;
202
+ this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
203
+ this.defaultTimeout = config.timeout || DEFAULT_TIMEOUT;
204
+ this.defaultRetries = config.retries ?? DEFAULT_RETRIES;
205
+ this.defaultRetryDelay = config.retryDelay || DEFAULT_RETRY_DELAY;
206
+ }
207
+ /**
208
+ * Get the last rate limit info from the API
209
+ */
210
+ getRateLimitInfo() {
211
+ return this.lastRateLimitInfo;
212
+ }
213
+ /**
214
+ * Make an authenticated request to the Rainfall API
215
+ */
216
+ async request(path, options = {}, requestOptions) {
217
+ const timeout = requestOptions?.timeout ?? this.defaultTimeout;
218
+ const maxRetries = requestOptions?.retries ?? this.defaultRetries;
219
+ const retryDelay = requestOptions?.retryDelay ?? this.defaultRetryDelay;
220
+ const url = `${this.baseUrl}${path}`;
221
+ const method = options.method || "GET";
222
+ let lastError;
223
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
224
+ try {
225
+ const controller = new AbortController();
226
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
227
+ const response = await fetch(url, {
228
+ method,
229
+ headers: {
230
+ "x-api-key": this.apiKey,
231
+ "Content-Type": "application/json",
232
+ "Accept": "application/json",
233
+ "X-Rainfall-SDK-Version": "0.1.0",
234
+ ...options.headers
235
+ },
236
+ body: options.body ? JSON.stringify(options.body) : void 0,
237
+ signal: controller.signal
238
+ });
239
+ clearTimeout(timeoutId);
240
+ const limit = response.headers.get("x-ratelimit-limit");
241
+ const remaining = response.headers.get("x-ratelimit-remaining");
242
+ const reset = response.headers.get("x-ratelimit-reset");
243
+ if (limit && remaining && reset) {
244
+ this.lastRateLimitInfo = {
245
+ limit: parseInt(limit, 10),
246
+ remaining: parseInt(remaining, 10),
247
+ resetAt: new Date(parseInt(reset, 10) * 1e3)
248
+ };
249
+ }
250
+ let data;
251
+ const contentType = response.headers.get("content-type");
252
+ if (contentType?.includes("application/json")) {
253
+ data = await response.json();
254
+ } else {
255
+ data = await response.text();
256
+ }
257
+ if (!response.ok) {
258
+ throw parseErrorResponse(response, data);
259
+ }
260
+ return data;
261
+ } catch (error) {
262
+ if (error instanceof RainfallError) {
263
+ if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {
264
+ throw error;
265
+ }
266
+ if (error.statusCode === 401) {
267
+ throw error;
268
+ }
269
+ }
270
+ if (error instanceof Error && error.name === "AbortError") {
271
+ lastError = new TimeoutError(timeout);
272
+ } else if (error instanceof TypeError) {
273
+ lastError = new NetworkError(error.message);
274
+ } else {
275
+ lastError = error instanceof Error ? error : new Error(String(error));
276
+ }
277
+ if (attempt >= maxRetries) {
278
+ break;
279
+ }
280
+ const delay = retryDelay * Math.pow(2, attempt) + Math.random() * 1e3;
281
+ await this.sleep(delay);
282
+ }
231
283
  }
232
- if (!response.ok) {
233
- throw parseErrorResponse(response, data);
284
+ throw lastError || new RainfallError("Request failed", "REQUEST_FAILED");
285
+ }
286
+ /**
287
+ * Execute a tool/node by ID
288
+ */
289
+ async executeTool(toolId, params, options) {
290
+ const subscriberId = await this.ensureSubscriberId();
291
+ const response = await this.request(`/olympic/subscribers/${subscriberId}/nodes/${toolId}`, {
292
+ method: "POST",
293
+ body: params || {}
294
+ }, options);
295
+ return response.result;
296
+ }
297
+ /**
298
+ * List all available tools
299
+ */
300
+ async listTools() {
301
+ const subscriberId = await this.ensureSubscriberId();
302
+ const result = await this.request(`/olympic/subscribers/${subscriberId}/nodes/_utils/node-descriptions`);
303
+ if (result.success && result.nodes) {
304
+ return Object.values(result.nodes);
234
305
  }
235
- return data;
236
- } catch (error) {
237
- if (error instanceof RainfallError) {
238
- if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {
239
- throw error;
240
- }
241
- if (error.statusCode === 401) {
242
- throw error;
306
+ const legacyResult = await this.request(`/olympic/subscribers/${subscriberId}/nodes/_utils/node-list`);
307
+ if (legacyResult.keys && Array.isArray(legacyResult.keys)) {
308
+ return legacyResult.keys.map((key) => ({
309
+ id: key,
310
+ name: key,
311
+ description: "",
312
+ category: "general"
313
+ }));
314
+ }
315
+ return legacyResult.nodes || [];
316
+ }
317
+ /**
318
+ * Get tool schema/parameters
319
+ */
320
+ async getToolSchema(toolId) {
321
+ const subscriberId = await this.ensureSubscriberId();
322
+ const response = await this.request(`/olympic/subscribers/${subscriberId}/nodes/${toolId}/params`);
323
+ return response.params;
324
+ }
325
+ /**
326
+ * Get subscriber info
327
+ */
328
+ async getMe() {
329
+ const result = await this.request("/olympic/subscribers/me");
330
+ if (result.subscriber?.id) {
331
+ this.subscriberId = result.subscriber.id;
332
+ }
333
+ const subscriber = result.subscriber;
334
+ return {
335
+ id: subscriber.id,
336
+ name: subscriber.name,
337
+ email: subscriber.google_id,
338
+ billingStatus: subscriber.billing_status,
339
+ plan: subscriber.billing_status,
340
+ usage: {
341
+ callsThisMonth: subscriber.metadata?.usage?.callsThisMonth ?? 0,
342
+ callsLimit: subscriber.metadata?.usage?.callsLimit ?? 5e3
243
343
  }
344
+ };
345
+ }
346
+ /**
347
+ * Ensure we have a subscriber ID, fetching it if necessary
348
+ */
349
+ async ensureSubscriberId() {
350
+ if (this.subscriberId) {
351
+ return this.subscriberId;
244
352
  }
245
- if (error instanceof Error && error.name === "AbortError") {
246
- lastError = new TimeoutError(timeout);
247
- } else if (error instanceof TypeError) {
248
- lastError = new NetworkError(error.message);
249
- } else {
250
- lastError = error instanceof Error ? error : new Error(String(error));
353
+ const me = await this.getMe();
354
+ if (!me.id) {
355
+ throw new RainfallError("Failed to get subscriber ID", "NO_SUBSCRIBER_ID");
251
356
  }
252
- if (attempt >= maxRetries) {
253
- break;
357
+ return me.id;
358
+ }
359
+ sleep(ms) {
360
+ return new Promise((resolve) => setTimeout(resolve, ms));
361
+ }
362
+ /**
363
+ * OpenAI-compatible chat completions with tool support
364
+ */
365
+ async chatCompletions(params) {
366
+ const { subscriber_id, ...body } = params;
367
+ if (body.stream) {
368
+ const url = `${this.baseUrl}/olympic/subscribers/${subscriber_id}/v1/chat/completions`;
369
+ const response = await fetch(url, {
370
+ method: "POST",
371
+ headers: {
372
+ "x-api-key": this.apiKey,
373
+ "Content-Type": "application/json",
374
+ "Accept": "text/event-stream"
375
+ },
376
+ body: JSON.stringify(body)
377
+ });
378
+ if (!response.ok) {
379
+ const error = await response.text();
380
+ throw new RainfallError(`Chat completions failed: ${error}`, "CHAT_ERROR");
381
+ }
382
+ if (!response.body) {
383
+ throw new RainfallError("No response body", "CHAT_ERROR");
384
+ }
385
+ return response.body;
254
386
  }
255
- const delay = retryDelay * Math.pow(2, attempt) + Math.random() * 1e3;
256
- await this.sleep(delay);
387
+ return this.request(
388
+ `/olympic/subscribers/${subscriber_id}/v1/chat/completions`,
389
+ {
390
+ method: "POST",
391
+ body
392
+ }
393
+ );
257
394
  }
258
- }
259
- throw lastError || new RainfallError("Request failed", "REQUEST_FAILED");
395
+ /**
396
+ * List available models (OpenAI-compatible format)
397
+ */
398
+ async listModels(subscriberId) {
399
+ const sid = subscriberId || this.subscriberId || await this.ensureSubscriberId();
400
+ const result = await this.request(
401
+ `/olympic/subscribers/${sid}/v1/models`
402
+ );
403
+ return result.data || [];
404
+ }
405
+ };
260
406
  }
261
- /**
262
- * Execute a tool/node by ID
263
- */
264
- async executeTool(toolId, params, options) {
265
- const subscriberId = await this.ensureSubscriberId();
266
- const response = await this.request(`/olympic/subscribers/${subscriberId}/nodes/${toolId}`, {
267
- method: "POST",
268
- body: params || {}
269
- }, options);
270
- return response.result;
407
+ });
408
+
409
+ // src/namespaces/integrations.ts
410
+ function createIntegrations(client) {
411
+ return new IntegrationsNamespace(client);
412
+ }
413
+ var IntegrationsNamespace;
414
+ var init_integrations = __esm({
415
+ "src/namespaces/integrations.ts"() {
416
+ "use strict";
417
+ init_cjs_shims();
418
+ IntegrationsNamespace = class {
419
+ constructor(client) {
420
+ this.client = client;
421
+ }
422
+ get github() {
423
+ return {
424
+ issues: {
425
+ create: (params) => this.client.executeTool("github-create-issue", params),
426
+ list: (params) => this.client.executeTool("github-list-issues", params),
427
+ get: (params) => this.client.executeTool("github-get-issue", params),
428
+ update: (params) => this.client.executeTool("github-update-issue", params),
429
+ addComment: (params) => this.client.executeTool("github-add-issue-comment", params)
430
+ },
431
+ repos: {
432
+ get: (params) => this.client.executeTool("github-get-repository", params),
433
+ listBranches: (params) => this.client.executeTool("github-list-branches", params)
434
+ },
435
+ pullRequests: {
436
+ list: (params) => this.client.executeTool("github-list-pull-requests", params),
437
+ get: (params) => this.client.executeTool("github-get-pull-request", params)
438
+ }
439
+ };
440
+ }
441
+ get notion() {
442
+ return {
443
+ pages: {
444
+ create: (params) => this.client.executeTool("notion-pages-create", params),
445
+ retrieve: (params) => this.client.executeTool("notion-pages-retrieve", params),
446
+ update: (params) => this.client.executeTool("notion-pages-update", params)
447
+ },
448
+ databases: {
449
+ query: (params) => this.client.executeTool("notion-databases-query", params),
450
+ retrieve: (params) => this.client.executeTool("notion-databases-retrieve", params)
451
+ },
452
+ blocks: {
453
+ appendChildren: (params) => this.client.executeTool("notion-blocks-append-children", params),
454
+ retrieveChildren: (params) => this.client.executeTool("notion-blocks-retrieve-children", params)
455
+ }
456
+ };
457
+ }
458
+ get linear() {
459
+ return {
460
+ issues: {
461
+ create: (params) => this.client.executeTool("linear-core-issueCreate", params),
462
+ list: (params) => this.client.executeTool("linear-core-issues", params),
463
+ get: (params) => this.client.executeTool("linear-core-issue", params),
464
+ update: (params) => this.client.executeTool("linear-core-issueUpdate", params),
465
+ archive: (params) => this.client.executeTool("linear-core-issueArchive", params)
466
+ },
467
+ teams: {
468
+ list: () => this.client.executeTool("linear-core-teams", {})
469
+ }
470
+ };
471
+ }
472
+ get slack() {
473
+ return {
474
+ messages: {
475
+ send: (params) => this.client.executeTool("slack-core-postMessage", params),
476
+ list: (params) => this.client.executeTool("slack-core-listMessages", params)
477
+ },
478
+ channels: {
479
+ list: () => this.client.executeTool("slack-core-listChannels", {})
480
+ },
481
+ users: {
482
+ list: () => this.client.executeTool("slack-core-listUsers", {})
483
+ },
484
+ reactions: {
485
+ add: (params) => this.client.executeTool("slack-core-addReaction", params)
486
+ }
487
+ };
488
+ }
489
+ get figma() {
490
+ return {
491
+ files: {
492
+ get: (params) => this.client.executeTool("figma-files-getFile", { fileKey: params.fileKey }),
493
+ getNodes: (params) => this.client.executeTool("figma-files-getFileNodes", { fileKey: params.fileKey, nodeIds: params.nodeIds }),
494
+ getImages: (params) => this.client.executeTool("figma-files-getFileImage", { fileKey: params.fileKey, nodeIds: params.nodeIds, format: params.format }),
495
+ getComments: (params) => this.client.executeTool("figma-comments-getFileComments", { fileKey: params.fileKey }),
496
+ postComment: (params) => this.client.executeTool("figma-comments-postComment", { fileKey: params.fileKey, message: params.message, nodeId: params.nodeId })
497
+ },
498
+ projects: {
499
+ list: (params) => this.client.executeTool("figma-projects-getTeamProjects", { teamId: params.teamId }),
500
+ getFiles: (params) => this.client.executeTool("figma-projects-getProjectFiles", { projectId: params.projectId })
501
+ }
502
+ };
503
+ }
504
+ get stripe() {
505
+ return {
506
+ customers: {
507
+ create: (params) => this.client.executeTool("stripe-customers-create", params),
508
+ retrieve: (params) => this.client.executeTool("stripe-customers-retrieve", { customerId: params.customerId }),
509
+ update: (params) => this.client.executeTool("stripe-customers-update", params),
510
+ listPaymentMethods: (params) => this.client.executeTool("stripe-customers-list-payment-methods", { customerId: params.customerId })
511
+ },
512
+ paymentIntents: {
513
+ create: (params) => this.client.executeTool("stripe-payment-intents-create", params),
514
+ retrieve: (params) => this.client.executeTool("stripe-payment-intents-retrieve", { paymentIntentId: params.paymentIntentId }),
515
+ confirm: (params) => this.client.executeTool("stripe-payment-intents-confirm", { paymentIntentId: params.paymentIntentId })
516
+ },
517
+ subscriptions: {
518
+ create: (params) => this.client.executeTool("stripe-subscriptions-create", params),
519
+ retrieve: (params) => this.client.executeTool("stripe-subscriptions-retrieve", { subscriptionId: params.subscriptionId }),
520
+ cancel: (params) => this.client.executeTool("stripe-subscriptions-cancel", { subscriptionId: params.subscriptionId })
521
+ }
522
+ };
523
+ }
524
+ };
271
525
  }
272
- /**
273
- * List all available tools
274
- */
275
- async listTools() {
276
- const subscriberId = await this.ensureSubscriberId();
277
- const result = await this.request(`/olympic/subscribers/${subscriberId}/nodes/_utils/node-descriptions`);
278
- if (result.success && result.nodes) {
279
- return Object.values(result.nodes);
280
- }
281
- const legacyResult = await this.request(`/olympic/subscribers/${subscriberId}/nodes/_utils/node-list`);
282
- if (legacyResult.keys && Array.isArray(legacyResult.keys)) {
283
- return legacyResult.keys.map((key) => ({
284
- id: key,
285
- name: key,
286
- description: "",
287
- category: "general"
288
- }));
289
- }
290
- return legacyResult.nodes || [];
526
+ });
527
+
528
+ // src/namespaces/memory.ts
529
+ function createMemory(client) {
530
+ return {
531
+ create: (params) => client.executeTool("memory-create", params),
532
+ get: (params) => client.executeTool("memory-get", { memoryId: params.memoryId }),
533
+ recall: (params) => client.executeTool("memory-recall", params),
534
+ list: (params) => client.executeTool("memory-list", params ?? {}),
535
+ update: (params) => client.executeTool("memory-update", params),
536
+ delete: (params) => client.executeTool("memory-delete", { memoryId: params.memoryId })
537
+ };
538
+ }
539
+ var init_memory = __esm({
540
+ "src/namespaces/memory.ts"() {
541
+ "use strict";
542
+ init_cjs_shims();
291
543
  }
292
- /**
293
- * Get tool schema/parameters
294
- */
295
- async getToolSchema(toolId) {
296
- const subscriberId = await this.ensureSubscriberId();
297
- const response = await this.request(`/olympic/subscribers/${subscriberId}/nodes/${toolId}/params`);
298
- return response.params;
544
+ });
545
+
546
+ // src/namespaces/articles.ts
547
+ function createArticles(client) {
548
+ return {
549
+ search: (params) => client.executeTool("article-search", params),
550
+ create: (params) => client.executeTool("article-create", params),
551
+ createFromUrl: (params) => client.executeTool("article-create-from-url", params),
552
+ fetch: (params) => client.executeTool("article-fetch", params),
553
+ recent: (params) => client.executeTool("article-recent", params ?? {}),
554
+ relevant: (params) => client.executeTool("article-relevant-news", params),
555
+ summarize: (params) => client.executeTool("article-summarize", params),
556
+ extractTopics: (params) => client.executeTool("article-topic-extractor", params)
557
+ };
558
+ }
559
+ var init_articles = __esm({
560
+ "src/namespaces/articles.ts"() {
561
+ "use strict";
562
+ init_cjs_shims();
563
+ }
564
+ });
565
+
566
+ // src/namespaces/web.ts
567
+ function createWeb(client) {
568
+ return {
569
+ search: {
570
+ exa: (params) => client.executeTool("exa-web-search", params),
571
+ perplexity: (params) => client.executeTool("perplexity-search", params)
572
+ },
573
+ fetch: (params) => client.executeTool("web-fetch", params),
574
+ htmlToMarkdown: (params) => client.executeTool("html-to-markdown-converter", params),
575
+ extractHtml: (params) => client.executeTool("extract-html-selector", params)
576
+ };
577
+ }
578
+ var init_web = __esm({
579
+ "src/namespaces/web.ts"() {
580
+ "use strict";
581
+ init_cjs_shims();
582
+ }
583
+ });
584
+
585
+ // src/namespaces/ai.ts
586
+ function createAI(client) {
587
+ return {
588
+ embeddings: {
589
+ document: (params) => client.executeTool("jina-document-embedding", params),
590
+ query: (params) => client.executeTool("jina-query-embedding", params),
591
+ image: (params) => client.executeTool("jina-image-embedding", { image: params.imageBase64 })
592
+ },
593
+ image: {
594
+ generate: (params) => client.executeTool("image-generation", params)
595
+ },
596
+ ocr: (params) => client.executeTool("ocr-text-extraction", { image: params.imageBase64 }),
597
+ vision: (params) => client.executeTool("llama-scout-vision", { image: params.imageBase64, prompt: params.prompt }),
598
+ chat: (params) => client.executeTool("xai-chat-completions", params),
599
+ complete: (params) => client.executeTool("fim", params),
600
+ classify: (params) => client.executeTool("jina-document-classifier", params),
601
+ segment: (params) => client.executeTool("jina-text-segmenter", params),
602
+ /**
603
+ * OpenAI-compatible chat completions with full tool support
604
+ * This is the recommended method for multi-turn conversations with tools
605
+ */
606
+ chatCompletions: (params) => client.chatCompletions(params)
607
+ };
608
+ }
609
+ var init_ai = __esm({
610
+ "src/namespaces/ai.ts"() {
611
+ "use strict";
612
+ init_cjs_shims();
299
613
  }
300
- /**
301
- * Get subscriber info
302
- */
303
- async getMe() {
304
- const result = await this.request("/olympic/subscribers/me");
305
- if (result.subscriber?.id) {
306
- this.subscriberId = result.subscriber.id;
614
+ });
615
+
616
+ // src/namespaces/data.ts
617
+ function createData(client) {
618
+ return {
619
+ csv: {
620
+ query: (params) => client.executeTool("query-csv", params),
621
+ convert: (params) => client.executeTool("csv-convert", params)
622
+ },
623
+ scripts: {
624
+ create: (params) => client.executeTool("create-saved-script", params),
625
+ execute: (params) => client.executeTool("execute-saved-script", params),
626
+ list: () => client.executeTool("list-saved-scripts", {}),
627
+ update: (params) => client.executeTool("update-saved-script", params),
628
+ delete: (params) => client.executeTool("delete-saved-script", params)
629
+ },
630
+ similarity: {
631
+ search: (params) => client.executeTool("duck-db-similarity-search", params),
632
+ duckDbSearch: (params) => client.executeTool("duck-db-similarity-search", params)
307
633
  }
308
- const subscriber = result.subscriber;
309
- return {
310
- id: subscriber.id,
311
- name: subscriber.name,
312
- email: subscriber.google_id,
313
- billingStatus: subscriber.billing_status,
314
- plan: subscriber.billing_status,
315
- usage: {
316
- callsThisMonth: subscriber.metadata?.usage?.callsThisMonth ?? 0,
317
- callsLimit: subscriber.metadata?.usage?.callsLimit ?? 5e3
634
+ };
635
+ }
636
+ var init_data = __esm({
637
+ "src/namespaces/data.ts"() {
638
+ "use strict";
639
+ init_cjs_shims();
640
+ }
641
+ });
642
+
643
+ // src/namespaces/utils.ts
644
+ function createUtils(client) {
645
+ return {
646
+ mermaid: (params) => client.executeTool("mermaid-diagram-generator", { mermaid: params.diagram }),
647
+ documentConvert: (params) => client.executeTool("document-format-converter", {
648
+ base64: `data:${params.mimeType};base64,${Buffer.from(params.document).toString("base64")}`,
649
+ format: params.format
650
+ }),
651
+ regex: {
652
+ match: (params) => client.executeTool("regex-match", params),
653
+ replace: (params) => client.executeTool("regex-replace", params)
654
+ },
655
+ jsonExtract: (params) => client.executeTool("json-extract", params),
656
+ digest: (params) => client.executeTool("digest-generator", { text: params.data }),
657
+ monteCarlo: (params) => client.executeTool("monte-carlo-simulation", params)
658
+ };
659
+ }
660
+ var init_utils = __esm({
661
+ "src/namespaces/utils.ts"() {
662
+ "use strict";
663
+ init_cjs_shims();
664
+ }
665
+ });
666
+
667
+ // src/sdk.ts
668
+ var Rainfall;
669
+ var init_sdk = __esm({
670
+ "src/sdk.ts"() {
671
+ "use strict";
672
+ init_cjs_shims();
673
+ init_client();
674
+ init_integrations();
675
+ init_memory();
676
+ init_articles();
677
+ init_web();
678
+ init_ai();
679
+ init_data();
680
+ init_utils();
681
+ Rainfall = class {
682
+ client;
683
+ _integrations;
684
+ _memory;
685
+ _articles;
686
+ _web;
687
+ _ai;
688
+ _data;
689
+ _utils;
690
+ constructor(config) {
691
+ this.client = new RainfallClient(config);
692
+ }
693
+ /**
694
+ * Integrations namespace - GitHub, Notion, Linear, Slack, Figma, Stripe
695
+ *
696
+ * @example
697
+ * ```typescript
698
+ * // GitHub
699
+ * await rainfall.integrations.github.issues.create({
700
+ * owner: 'facebook',
701
+ * repo: 'react',
702
+ * title: 'Bug report'
703
+ * });
704
+ *
705
+ * // Slack
706
+ * await rainfall.integrations.slack.messages.send({
707
+ * channelId: 'C123456',
708
+ * text: 'Hello team!'
709
+ * });
710
+ *
711
+ * // Linear
712
+ * const issues = await rainfall.integrations.linear.issues.list();
713
+ * ```
714
+ */
715
+ get integrations() {
716
+ if (!this._integrations) {
717
+ this._integrations = createIntegrations(this.client);
718
+ }
719
+ return this._integrations;
720
+ }
721
+ /**
722
+ * Memory namespace - Semantic memory storage and retrieval
723
+ *
724
+ * @example
725
+ * ```typescript
726
+ * // Store a memory
727
+ * await rainfall.memory.create({
728
+ * content: 'User prefers dark mode',
729
+ * keywords: ['preference', 'ui']
730
+ * });
731
+ *
732
+ * // Recall similar memories
733
+ * const memories = await rainfall.memory.recall({
734
+ * query: 'user preferences',
735
+ * topK: 5
736
+ * });
737
+ * ```
738
+ */
739
+ get memory() {
740
+ if (!this._memory) {
741
+ this._memory = createMemory(this.client);
742
+ }
743
+ return this._memory;
744
+ }
745
+ /**
746
+ * Articles namespace - News aggregation and article management
747
+ *
748
+ * @example
749
+ * ```typescript
750
+ * // Search news
751
+ * const articles = await rainfall.articles.search({
752
+ * query: 'artificial intelligence'
753
+ * });
754
+ *
755
+ * // Create from URL
756
+ * const article = await rainfall.articles.createFromUrl({
757
+ * url: 'https://example.com/article'
758
+ * });
759
+ *
760
+ * // Summarize
761
+ * const summary = await rainfall.articles.summarize({
762
+ * text: article.content
763
+ * });
764
+ * ```
765
+ */
766
+ get articles() {
767
+ if (!this._articles) {
768
+ this._articles = createArticles(this.client);
769
+ }
770
+ return this._articles;
771
+ }
772
+ /**
773
+ * Web namespace - Web search, scraping, and content extraction
774
+ *
775
+ * @example
776
+ * ```typescript
777
+ * // Search with Exa
778
+ * const results = await rainfall.web.search.exa({
779
+ * query: 'latest AI research'
780
+ * });
781
+ *
782
+ * // Fetch and convert
783
+ * const html = await rainfall.web.fetch({ url: 'https://example.com' });
784
+ * const markdown = await rainfall.web.htmlToMarkdown({ html });
785
+ *
786
+ * // Extract specific elements
787
+ * const links = await rainfall.web.extractHtml({
788
+ * html,
789
+ * selector: 'a[href]'
790
+ * });
791
+ * ```
792
+ */
793
+ get web() {
794
+ if (!this._web) {
795
+ this._web = createWeb(this.client);
796
+ }
797
+ return this._web;
798
+ }
799
+ /**
800
+ * AI namespace - Embeddings, image generation, OCR, vision, chat
801
+ *
802
+ * @example
803
+ * ```typescript
804
+ * // Generate embeddings
805
+ * const embedding = await rainfall.ai.embeddings.document({
806
+ * text: 'Hello world'
807
+ * });
808
+ *
809
+ * // Generate image
810
+ * const image = await rainfall.ai.image.generate({
811
+ * prompt: 'A serene mountain landscape'
812
+ * });
813
+ *
814
+ * // OCR
815
+ * const text = await rainfall.ai.ocr({ imageBase64: '...' });
816
+ *
817
+ * // Chat
818
+ * const response = await rainfall.ai.chat({
819
+ * messages: [{ role: 'user', content: 'Hello!' }]
820
+ * });
821
+ * ```
822
+ */
823
+ get ai() {
824
+ if (!this._ai) {
825
+ this._ai = createAI(this.client);
826
+ }
827
+ return this._ai;
828
+ }
829
+ /**
830
+ * Data namespace - CSV processing, scripts, similarity search
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * // Query CSV with SQL
835
+ * const results = await rainfall.data.csv.query({
836
+ * sql: 'SELECT * FROM data WHERE value > 100'
837
+ * });
838
+ *
839
+ * // Execute saved script
840
+ * const result = await rainfall.data.scripts.execute({
841
+ * name: 'my-script',
842
+ * params: { input: 'data' }
843
+ * });
844
+ * ```
845
+ */
846
+ get data() {
847
+ if (!this._data) {
848
+ this._data = createData(this.client);
849
+ }
850
+ return this._data;
851
+ }
852
+ /**
853
+ * Utils namespace - Mermaid diagrams, document conversion, regex, JSON extraction
854
+ *
855
+ * @example
856
+ * ```typescript
857
+ * // Generate diagram
858
+ * const diagram = await rainfall.utils.mermaid({
859
+ * diagram: 'graph TD; A-->B;'
860
+ * });
861
+ *
862
+ * // Convert document
863
+ * const pdf = await rainfall.utils.documentConvert({
864
+ * document: markdownContent,
865
+ * mimeType: 'text/markdown',
866
+ * format: 'pdf'
867
+ * });
868
+ *
869
+ * // Extract JSON from text
870
+ * const json = await rainfall.utils.jsonExtract({
871
+ * text: 'Here is some data: {"key": "value"}'
872
+ * });
873
+ * ```
874
+ */
875
+ get utils() {
876
+ if (!this._utils) {
877
+ this._utils = createUtils(this.client);
878
+ }
879
+ return this._utils;
880
+ }
881
+ /**
882
+ * Get the underlying HTTP client for advanced usage
883
+ */
884
+ getClient() {
885
+ return this.client;
886
+ }
887
+ /**
888
+ * List all available tools
889
+ */
890
+ async listTools() {
891
+ return this.client.listTools();
892
+ }
893
+ /**
894
+ * Get schema for a specific tool
895
+ */
896
+ async getToolSchema(toolId) {
897
+ return this.client.getToolSchema(toolId);
898
+ }
899
+ /**
900
+ * Execute any tool by ID (low-level access)
901
+ */
902
+ async executeTool(toolId, params) {
903
+ return this.client.executeTool(toolId, params);
904
+ }
905
+ /**
906
+ * Get current subscriber info and usage
907
+ */
908
+ async getMe() {
909
+ return this.client.getMe();
910
+ }
911
+ /**
912
+ * Get current rate limit info
913
+ */
914
+ getRateLimitInfo() {
915
+ return this.client.getRateLimitInfo();
916
+ }
917
+ /**
918
+ * OpenAI-compatible chat completions with tool support
919
+ *
920
+ * @example
921
+ * ```typescript
922
+ * // Simple chat
923
+ * const response = await rainfall.chatCompletions({
924
+ * subscriber_id: 'my-subscriber',
925
+ * messages: [{ role: 'user', content: 'Hello!' }],
926
+ * model: 'llama-3.3-70b-versatile'
927
+ * });
928
+ *
929
+ * // With tools
930
+ * const response = await rainfall.chatCompletions({
931
+ * subscriber_id: 'my-subscriber',
932
+ * messages: [{ role: 'user', content: 'Search for AI news' }],
933
+ * tools: [{ type: 'function', function: { name: 'web-search' } }],
934
+ * enable_stacked: true
935
+ * });
936
+ *
937
+ * // Streaming
938
+ * const stream = await rainfall.chatCompletions({
939
+ * subscriber_id: 'my-subscriber',
940
+ * messages: [{ role: 'user', content: 'Tell me a story' }],
941
+ * stream: true
942
+ * });
943
+ * ```
944
+ */
945
+ async chatCompletions(params) {
946
+ return this.client.chatCompletions(params);
947
+ }
948
+ /**
949
+ * List available models (OpenAI-compatible format)
950
+ *
951
+ * @example
952
+ * ```typescript
953
+ * const models = await rainfall.listModels();
954
+ * console.log(models); // [{ id: 'llama-3.3-70b-versatile', ... }]
955
+ * ```
956
+ */
957
+ async listModels(subscriberId) {
958
+ return this.client.listModels(subscriberId);
318
959
  }
319
960
  };
320
961
  }
321
- /**
322
- * Ensure we have a subscriber ID, fetching it if necessary
323
- */
324
- async ensureSubscriberId() {
325
- if (this.subscriberId) {
326
- return this.subscriberId;
327
- }
328
- const me = await this.getMe();
329
- if (!me.id) {
330
- throw new RainfallError("Failed to get subscriber ID", "NO_SUBSCRIBER_ID");
962
+ });
963
+
964
+ // src/cli/config.ts
965
+ var config_exports = {};
966
+ __export(config_exports, {
967
+ getLLMConfig: () => getLLMConfig,
968
+ getProviderBaseUrl: () => getProviderBaseUrl,
969
+ isLocalProvider: () => isLocalProvider,
970
+ loadConfig: () => loadConfig,
971
+ saveConfig: () => saveConfig
972
+ });
973
+ function loadConfig() {
974
+ let config = {};
975
+ if ((0, import_fs.existsSync)(CONFIG_FILE)) {
976
+ try {
977
+ config = JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf8"));
978
+ } catch {
979
+ config = {};
331
980
  }
332
- return me.id;
333
981
  }
334
- sleep(ms) {
335
- return new Promise((resolve) => setTimeout(resolve, ms));
982
+ if (process.env.RAINFALL_API_KEY) {
983
+ config.apiKey = process.env.RAINFALL_API_KEY;
336
984
  }
337
- };
985
+ if (process.env.RAINFALL_BASE_URL) {
986
+ config.baseUrl = process.env.RAINFALL_BASE_URL;
987
+ }
988
+ if (!config.llm) {
989
+ config.llm = { provider: "rainfall" };
990
+ }
991
+ if (process.env.OPENAI_API_KEY) {
992
+ config.llm.provider = config.llm.provider || "openai";
993
+ config.llm.apiKey = process.env.OPENAI_API_KEY;
994
+ }
995
+ if (process.env.ANTHROPIC_API_KEY) {
996
+ config.llm.provider = "anthropic";
997
+ config.llm.apiKey = process.env.ANTHROPIC_API_KEY;
998
+ }
999
+ if (process.env.OLLAMA_HOST || process.env.OLLAMA_URL) {
1000
+ config.llm.provider = "ollama";
1001
+ config.llm.baseUrl = process.env.OLLAMA_HOST || process.env.OLLAMA_URL;
1002
+ }
1003
+ if (process.env.LLM_MODEL) {
1004
+ config.llm.model = process.env.LLM_MODEL;
1005
+ }
1006
+ return config;
1007
+ }
1008
+ function saveConfig(config) {
1009
+ if (!(0, import_fs.existsSync)(CONFIG_DIR)) {
1010
+ (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
1011
+ }
1012
+ (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
1013
+ }
1014
+ function getLLMConfig(config) {
1015
+ const defaults = {
1016
+ provider: "rainfall",
1017
+ apiKey: config.apiKey || "",
1018
+ baseUrl: config.baseUrl || "https://api.rainfall.com",
1019
+ model: "llama-3.3-70b-versatile",
1020
+ options: {}
1021
+ };
1022
+ return { ...defaults, ...config.llm };
1023
+ }
1024
+ function isLocalProvider(config) {
1025
+ return config.llm?.provider === "ollama" || config.llm?.provider === "local";
1026
+ }
1027
+ function getProviderBaseUrl(config) {
1028
+ const provider = config.llm?.provider || "rainfall";
1029
+ switch (provider) {
1030
+ case "openai":
1031
+ return config.llm?.baseUrl || "https://api.openai.com/v1";
1032
+ case "anthropic":
1033
+ return config.llm?.baseUrl || "https://api.anthropic.com/v1";
1034
+ case "ollama":
1035
+ return config.llm?.baseUrl || "http://localhost:11434/v1";
1036
+ case "local":
1037
+ return config.llm?.baseUrl || "http://localhost:1234/v1";
1038
+ case "rainfall":
1039
+ default:
1040
+ return config.baseUrl || "https://api.rainfall.com";
1041
+ }
1042
+ }
1043
+ var import_fs, import_path, import_os, CONFIG_DIR, CONFIG_FILE;
1044
+ var init_config = __esm({
1045
+ "src/cli/config.ts"() {
1046
+ "use strict";
1047
+ init_cjs_shims();
1048
+ import_fs = require("fs");
1049
+ import_path = require("path");
1050
+ import_os = require("os");
1051
+ CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".rainfall");
1052
+ CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
1053
+ }
1054
+ });
338
1055
 
339
- // src/namespaces/integrations.ts
340
- function createIntegrations(client) {
341
- return new IntegrationsNamespace(client);
1056
+ // src/services/networked.ts
1057
+ var RainfallNetworkedExecutor;
1058
+ var init_networked = __esm({
1059
+ "src/services/networked.ts"() {
1060
+ "use strict";
1061
+ init_cjs_shims();
1062
+ RainfallNetworkedExecutor = class {
1063
+ rainfall;
1064
+ options;
1065
+ edgeNodeId;
1066
+ jobCallbacks = /* @__PURE__ */ new Map();
1067
+ resultPollingInterval;
1068
+ constructor(rainfall, options = {}) {
1069
+ this.rainfall = rainfall;
1070
+ this.options = {
1071
+ wsPort: 8765,
1072
+ httpPort: 8787,
1073
+ hostname: process.env.HOSTNAME || "local-daemon",
1074
+ capabilities: {
1075
+ localExec: true,
1076
+ fileWatch: true,
1077
+ passiveListen: true
1078
+ },
1079
+ ...options
1080
+ };
1081
+ }
1082
+ /**
1083
+ * Register this edge node with the Rainfall backend
1084
+ */
1085
+ async registerEdgeNode() {
1086
+ const capabilities = this.buildCapabilitiesList();
1087
+ try {
1088
+ const result = await this.rainfall.executeTool("register-edge-node", {
1089
+ hostname: this.options.hostname,
1090
+ capabilities,
1091
+ wsPort: this.options.wsPort,
1092
+ httpPort: this.options.httpPort,
1093
+ version: "0.1.0"
1094
+ });
1095
+ this.edgeNodeId = result.edgeNodeId;
1096
+ console.log(`\u{1F310} Edge node registered with Rainfall as ${this.edgeNodeId}`);
1097
+ return this.edgeNodeId;
1098
+ } catch (error) {
1099
+ this.edgeNodeId = `edge-${this.options.hostname}-${Date.now()}`;
1100
+ console.log(`\u{1F310} Edge node running in local mode (ID: ${this.edgeNodeId})`);
1101
+ return this.edgeNodeId;
1102
+ }
1103
+ }
1104
+ /**
1105
+ * Unregister this edge node on shutdown
1106
+ */
1107
+ async unregisterEdgeNode() {
1108
+ if (!this.edgeNodeId) return;
1109
+ try {
1110
+ await this.rainfall.executeTool("unregister-edge-node", {
1111
+ edgeNodeId: this.edgeNodeId
1112
+ });
1113
+ console.log(`\u{1F310} Edge node ${this.edgeNodeId} unregistered`);
1114
+ } catch {
1115
+ }
1116
+ if (this.resultPollingInterval) {
1117
+ clearInterval(this.resultPollingInterval);
1118
+ }
1119
+ }
1120
+ /**
1121
+ * Queue a tool execution for distributed processing
1122
+ * Non-blocking - returns immediately with a job ID
1123
+ */
1124
+ async queueToolExecution(toolId, params, options = {}) {
1125
+ const executionMode = options.executionMode || "any";
1126
+ try {
1127
+ const result = await this.rainfall.executeTool("queue-job", {
1128
+ toolId,
1129
+ params,
1130
+ executionMode,
1131
+ requesterEdgeNodeId: this.edgeNodeId
1132
+ });
1133
+ if (options.callback) {
1134
+ this.jobCallbacks.set(result.jobId, options.callback);
1135
+ this.startResultPolling();
1136
+ }
1137
+ return result.jobId;
1138
+ } catch (error) {
1139
+ if (executionMode === "local-only" || executionMode === "any") {
1140
+ try {
1141
+ const result = await this.rainfall.executeTool(toolId, params);
1142
+ if (options.callback) {
1143
+ options.callback(result);
1144
+ }
1145
+ return `local-${Date.now()}`;
1146
+ } catch (execError) {
1147
+ if (options.callback) {
1148
+ options.callback(null, String(execError));
1149
+ }
1150
+ throw execError;
1151
+ }
1152
+ }
1153
+ throw error;
1154
+ }
1155
+ }
1156
+ /**
1157
+ * Get status of a queued job
1158
+ */
1159
+ async getJobStatus(jobId) {
1160
+ try {
1161
+ const result = await this.rainfall.executeTool("get-job-status", {
1162
+ jobId
1163
+ });
1164
+ return result.job;
1165
+ } catch {
1166
+ return null;
1167
+ }
1168
+ }
1169
+ /**
1170
+ * Subscribe to job results via polling (WebSocket fallback)
1171
+ * In the future, this will use WebSocket push from ApresMoi
1172
+ */
1173
+ async subscribeToResults(callback) {
1174
+ console.log("\u{1F4E1} Subscribed to job results via Rainfall (polling mode)");
1175
+ this.onResultReceived = callback;
1176
+ }
1177
+ onResultReceived;
1178
+ /**
1179
+ * Start polling for job results (fallback until WebSocket push is ready)
1180
+ */
1181
+ startResultPolling() {
1182
+ if (this.resultPollingInterval) return;
1183
+ this.resultPollingInterval = setInterval(async () => {
1184
+ for (const [jobId, callback] of this.jobCallbacks) {
1185
+ try {
1186
+ const job = await this.getJobStatus(jobId);
1187
+ if (job?.status === "completed" || job?.status === "failed") {
1188
+ callback(job.result, job.error);
1189
+ this.jobCallbacks.delete(jobId);
1190
+ if (this.onResultReceived) {
1191
+ this.onResultReceived(jobId, job.result, job.error);
1192
+ }
1193
+ }
1194
+ } catch {
1195
+ }
1196
+ }
1197
+ if (this.jobCallbacks.size === 0 && this.resultPollingInterval) {
1198
+ clearInterval(this.resultPollingInterval);
1199
+ this.resultPollingInterval = void 0;
1200
+ }
1201
+ }, 2e3);
1202
+ }
1203
+ /**
1204
+ * Claim a job for execution on this edge node
1205
+ */
1206
+ async claimJob() {
1207
+ try {
1208
+ const result = await this.rainfall.executeTool("claim-job", {
1209
+ edgeNodeId: this.edgeNodeId,
1210
+ capabilities: this.buildCapabilitiesList()
1211
+ });
1212
+ return result.job;
1213
+ } catch {
1214
+ return null;
1215
+ }
1216
+ }
1217
+ /**
1218
+ * Submit job result after execution
1219
+ */
1220
+ async submitJobResult(jobId, result, error) {
1221
+ try {
1222
+ await this.rainfall.executeTool("submit-job-result", {
1223
+ jobId,
1224
+ edgeNodeId: this.edgeNodeId,
1225
+ result,
1226
+ error
1227
+ });
1228
+ } catch {
1229
+ }
1230
+ }
1231
+ /**
1232
+ * Get this edge node's ID
1233
+ */
1234
+ getEdgeNodeId() {
1235
+ return this.edgeNodeId;
1236
+ }
1237
+ /**
1238
+ * Build capabilities list from options
1239
+ */
1240
+ buildCapabilitiesList() {
1241
+ const caps = this.options.capabilities || {};
1242
+ const list = [];
1243
+ if (caps.localExec) list.push("local-exec");
1244
+ if (caps.fileWatch) list.push("file-watch");
1245
+ if (caps.passiveListen) list.push("passive-listen");
1246
+ if (caps.browser) list.push("browser");
1247
+ if (caps.custom) list.push(...caps.custom);
1248
+ return list;
1249
+ }
1250
+ };
1251
+ }
1252
+ });
1253
+
1254
+ // src/services/context.ts
1255
+ var RainfallDaemonContext;
1256
+ var init_context = __esm({
1257
+ "src/services/context.ts"() {
1258
+ "use strict";
1259
+ init_cjs_shims();
1260
+ RainfallDaemonContext = class {
1261
+ rainfall;
1262
+ options;
1263
+ localMemories = /* @__PURE__ */ new Map();
1264
+ sessions = /* @__PURE__ */ new Map();
1265
+ executionHistory = [];
1266
+ currentSessionId;
1267
+ constructor(rainfall, options = {}) {
1268
+ this.rainfall = rainfall;
1269
+ this.options = {
1270
+ maxLocalMemories: 1e3,
1271
+ maxMessageHistory: 100,
1272
+ maxExecutionHistory: 500,
1273
+ sessionTtl: 24 * 60 * 60 * 1e3,
1274
+ // 24 hours
1275
+ ...options
1276
+ };
1277
+ }
1278
+ /**
1279
+ * Initialize the context - load recent memories from cloud
1280
+ */
1281
+ async initialize() {
1282
+ try {
1283
+ const recentMemories = await this.rainfall.memory.recall({
1284
+ query: "daemon:context",
1285
+ topK: this.options.maxLocalMemories
1286
+ });
1287
+ for (const memory of recentMemories) {
1288
+ this.localMemories.set(memory.id, {
1289
+ id: memory.id,
1290
+ content: memory.content,
1291
+ keywords: memory.keywords || [],
1292
+ timestamp: memory.timestamp,
1293
+ source: memory.source,
1294
+ metadata: memory.metadata
1295
+ });
1296
+ }
1297
+ console.log(`\u{1F9E0} Loaded ${this.localMemories.size} memories into context`);
1298
+ } catch (error) {
1299
+ console.warn("\u26A0\uFE0F Could not sync memories:", error instanceof Error ? error.message : error);
1300
+ }
1301
+ }
1302
+ /**
1303
+ * Create or get a session
1304
+ */
1305
+ getSession(sessionId) {
1306
+ if (sessionId && this.sessions.has(sessionId)) {
1307
+ const session = this.sessions.get(sessionId);
1308
+ session.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
1309
+ return session;
1310
+ }
1311
+ const newSession = {
1312
+ id: sessionId || `session-${Date.now()}`,
1313
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1314
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
1315
+ variables: {},
1316
+ messageHistory: []
1317
+ };
1318
+ this.sessions.set(newSession.id, newSession);
1319
+ this.currentSessionId = newSession.id;
1320
+ return newSession;
1321
+ }
1322
+ /**
1323
+ * Get the current active session
1324
+ */
1325
+ getCurrentSession() {
1326
+ if (this.currentSessionId) {
1327
+ return this.sessions.get(this.currentSessionId);
1328
+ }
1329
+ return void 0;
1330
+ }
1331
+ /**
1332
+ * Set the current active session
1333
+ */
1334
+ setCurrentSession(sessionId) {
1335
+ if (this.sessions.has(sessionId)) {
1336
+ this.currentSessionId = sessionId;
1337
+ }
1338
+ }
1339
+ /**
1340
+ * Add a message to the current session history
1341
+ */
1342
+ addMessage(role, content) {
1343
+ const session = this.getCurrentSession();
1344
+ if (!session) return;
1345
+ session.messageHistory.push({
1346
+ role,
1347
+ content,
1348
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1349
+ });
1350
+ if (session.messageHistory.length > this.options.maxMessageHistory) {
1351
+ session.messageHistory = session.messageHistory.slice(-this.options.maxMessageHistory);
1352
+ }
1353
+ }
1354
+ /**
1355
+ * Store a memory (local + cloud sync)
1356
+ */
1357
+ async storeMemory(content, options = {}) {
1358
+ const id = `mem-${Date.now()}-${Math.random().toString(36).slice(2)}`;
1359
+ const entry = {
1360
+ id,
1361
+ content,
1362
+ keywords: options.keywords || [],
1363
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1364
+ source: options.source || "daemon",
1365
+ metadata: options.metadata
1366
+ };
1367
+ this.localMemories.set(id, entry);
1368
+ try {
1369
+ await this.rainfall.memory.create({
1370
+ content,
1371
+ keywords: [...options.keywords || [], "daemon:context"],
1372
+ metadata: {
1373
+ ...options.metadata,
1374
+ daemonMemoryId: id,
1375
+ source: options.source || "daemon"
1376
+ }
1377
+ });
1378
+ } catch (error) {
1379
+ console.warn("\u26A0\uFE0F Could not sync memory to cloud:", error instanceof Error ? error.message : error);
1380
+ }
1381
+ this.trimLocalMemories();
1382
+ return id;
1383
+ }
1384
+ /**
1385
+ * Recall memories by query
1386
+ */
1387
+ async recallMemories(query, topK = 5) {
1388
+ const localResults = Array.from(this.localMemories.values()).filter(
1389
+ (m) => m.content.toLowerCase().includes(query.toLowerCase()) || m.keywords.some((k) => k.toLowerCase().includes(query.toLowerCase()))
1390
+ ).slice(0, topK);
1391
+ try {
1392
+ const cloudResults = await this.rainfall.memory.recall({ query, topK });
1393
+ const seen = new Set(localResults.map((r) => r.id));
1394
+ for (const mem of cloudResults) {
1395
+ if (!seen.has(mem.id)) {
1396
+ localResults.push({
1397
+ id: mem.id,
1398
+ content: mem.content,
1399
+ keywords: mem.keywords || [],
1400
+ timestamp: mem.timestamp,
1401
+ source: mem.source,
1402
+ metadata: mem.metadata
1403
+ });
1404
+ }
1405
+ }
1406
+ } catch {
1407
+ }
1408
+ return localResults.slice(0, topK);
1409
+ }
1410
+ /**
1411
+ * Set a session variable
1412
+ */
1413
+ setVariable(key, value) {
1414
+ const session = this.getCurrentSession();
1415
+ if (session) {
1416
+ session.variables[key] = value;
1417
+ }
1418
+ }
1419
+ /**
1420
+ * Get a session variable
1421
+ */
1422
+ getVariable(key) {
1423
+ const session = this.getCurrentSession();
1424
+ return session?.variables[key];
1425
+ }
1426
+ /**
1427
+ * Record a tool execution
1428
+ */
1429
+ recordExecution(toolId, params, result, options = { duration: 0 }) {
1430
+ const record = {
1431
+ id: `exec-${Date.now()}-${Math.random().toString(36).slice(2)}`,
1432
+ toolId,
1433
+ params,
1434
+ result,
1435
+ error: options.error,
1436
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1437
+ duration: options.duration,
1438
+ edgeNodeId: options.edgeNodeId
1439
+ };
1440
+ this.executionHistory.push(record);
1441
+ if (this.executionHistory.length > this.options.maxExecutionHistory) {
1442
+ this.executionHistory = this.executionHistory.slice(-this.options.maxExecutionHistory);
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Get recent execution history
1447
+ */
1448
+ getExecutionHistory(limit = 10) {
1449
+ return this.executionHistory.slice(-limit).reverse();
1450
+ }
1451
+ /**
1452
+ * Get execution statistics
1453
+ */
1454
+ getExecutionStats() {
1455
+ const stats = {
1456
+ total: this.executionHistory.length,
1457
+ successful: 0,
1458
+ failed: 0,
1459
+ averageDuration: 0,
1460
+ byTool: {}
1461
+ };
1462
+ let totalDuration = 0;
1463
+ for (const exec of this.executionHistory) {
1464
+ if (exec.error) {
1465
+ stats.failed++;
1466
+ } else {
1467
+ stats.successful++;
1468
+ }
1469
+ totalDuration += exec.duration;
1470
+ stats.byTool[exec.toolId] = (stats.byTool[exec.toolId] || 0) + 1;
1471
+ }
1472
+ stats.averageDuration = stats.total > 0 ? totalDuration / stats.total : 0;
1473
+ return stats;
1474
+ }
1475
+ /**
1476
+ * Clear old sessions based on TTL
1477
+ */
1478
+ cleanupSessions() {
1479
+ const now = Date.now();
1480
+ const ttl = this.options.sessionTtl;
1481
+ for (const [id, session] of this.sessions) {
1482
+ const lastActivity = new Date(session.lastActivity).getTime();
1483
+ if (now - lastActivity > ttl) {
1484
+ this.sessions.delete(id);
1485
+ if (this.currentSessionId === id) {
1486
+ this.currentSessionId = void 0;
1487
+ }
1488
+ }
1489
+ }
1490
+ }
1491
+ /**
1492
+ * Get context summary for debugging
1493
+ */
1494
+ getStatus() {
1495
+ return {
1496
+ memoriesCached: this.localMemories.size,
1497
+ activeSessions: this.sessions.size,
1498
+ currentSession: this.currentSessionId,
1499
+ executionHistorySize: this.executionHistory.length
1500
+ };
1501
+ }
1502
+ trimLocalMemories() {
1503
+ if (this.localMemories.size <= this.options.maxLocalMemories) return;
1504
+ const entries = Array.from(this.localMemories.entries()).sort((a, b) => new Date(a[1].timestamp).getTime() - new Date(b[1].timestamp).getTime());
1505
+ const toRemove = entries.slice(0, entries.length - this.options.maxLocalMemories);
1506
+ for (const [id] of toRemove) {
1507
+ this.localMemories.delete(id);
1508
+ }
1509
+ }
1510
+ };
1511
+ }
1512
+ });
1513
+
1514
+ // src/services/listeners.ts
1515
+ var RainfallListenerRegistry;
1516
+ var init_listeners = __esm({
1517
+ "src/services/listeners.ts"() {
1518
+ "use strict";
1519
+ init_cjs_shims();
1520
+ RainfallListenerRegistry = class {
1521
+ rainfall;
1522
+ context;
1523
+ executor;
1524
+ watchers = /* @__PURE__ */ new Map();
1525
+ cronIntervals = /* @__PURE__ */ new Map();
1526
+ eventHistory = [];
1527
+ maxEventHistory = 100;
1528
+ constructor(rainfall, context, executor) {
1529
+ this.rainfall = rainfall;
1530
+ this.context = context;
1531
+ this.executor = executor;
1532
+ }
1533
+ /**
1534
+ * Register a file watcher
1535
+ * Note: Actual file watching requires fs.watch or chokidar
1536
+ * This is the registry - actual watching is done by the daemon
1537
+ */
1538
+ async registerFileWatcher(config) {
1539
+ console.log(`\u{1F441}\uFE0F Registering file watcher: ${config.name} (${config.watchPath})`);
1540
+ const existing = Array.from(this.watchers.keys());
1541
+ if (existing.includes(config.id)) {
1542
+ await this.unregisterFileWatcher(config.id);
1543
+ }
1544
+ this.watchers.set(config.id, {
1545
+ stop: () => {
1546
+ console.log(`\u{1F441}\uFE0F Stopped file watcher: ${config.name}`);
1547
+ }
1548
+ });
1549
+ await this.context.storeMemory(`File watcher registered: ${config.name}`, {
1550
+ keywords: ["listener", "file-watcher", config.name],
1551
+ metadata: { config }
1552
+ });
1553
+ }
1554
+ /**
1555
+ * Unregister a file watcher
1556
+ */
1557
+ async unregisterFileWatcher(id) {
1558
+ const watcher = this.watchers.get(id);
1559
+ if (watcher) {
1560
+ watcher.stop();
1561
+ this.watchers.delete(id);
1562
+ }
1563
+ }
1564
+ /**
1565
+ * Register a cron trigger
1566
+ */
1567
+ async registerCronTrigger(config) {
1568
+ console.log(`\u23F0 Registering cron trigger: ${config.name} (${config.cron})`);
1569
+ if (this.cronIntervals.has(config.id)) {
1570
+ clearInterval(this.cronIntervals.get(config.id));
1571
+ this.cronIntervals.delete(config.id);
1572
+ }
1573
+ const interval = this.parseCronToMs(config.cron);
1574
+ if (interval) {
1575
+ const intervalId = setInterval(async () => {
1576
+ await this.handleCronTick(config);
1577
+ }, interval);
1578
+ this.cronIntervals.set(config.id, intervalId);
1579
+ }
1580
+ await this.context.storeMemory(`Cron trigger registered: ${config.name}`, {
1581
+ keywords: ["listener", "cron", config.name],
1582
+ metadata: { config }
1583
+ });
1584
+ }
1585
+ /**
1586
+ * Unregister a cron trigger
1587
+ */
1588
+ unregisterCronTrigger(id) {
1589
+ const interval = this.cronIntervals.get(id);
1590
+ if (interval) {
1591
+ clearInterval(interval);
1592
+ this.cronIntervals.delete(id);
1593
+ }
1594
+ }
1595
+ /**
1596
+ * Handle a file event
1597
+ */
1598
+ async handleFileEvent(watcherId, eventType, filePath) {
1599
+ const event = {
1600
+ id: `evt-${Date.now()}`,
1601
+ type: "file",
1602
+ source: watcherId,
1603
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1604
+ data: { eventType, filePath }
1605
+ };
1606
+ this.recordEvent(event);
1607
+ console.log(`\u{1F4C1} File event: ${eventType} ${filePath}`);
1608
+ }
1609
+ /**
1610
+ * Handle a cron tick
1611
+ */
1612
+ async handleCronTick(config) {
1613
+ const event = {
1614
+ id: `evt-${Date.now()}`,
1615
+ type: "cron",
1616
+ source: config.id,
1617
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1618
+ data: { cron: config.cron }
1619
+ };
1620
+ this.recordEvent(event);
1621
+ console.log(`\u23F0 Cron tick: ${config.name}`);
1622
+ for (const step of config.workflow) {
1623
+ try {
1624
+ await this.executor.queueToolExecution(step.toolId, {
1625
+ ...step.params,
1626
+ _event: event
1627
+ });
1628
+ } catch (error) {
1629
+ console.error(`\u274C Workflow step failed: ${step.toolId}`, error);
1630
+ }
1631
+ }
1632
+ }
1633
+ /**
1634
+ * Trigger a manual event (for testing or programmatic triggers)
1635
+ */
1636
+ async triggerManual(name, data = {}) {
1637
+ const event = {
1638
+ id: `evt-${Date.now()}`,
1639
+ type: "manual",
1640
+ source: name,
1641
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1642
+ data
1643
+ };
1644
+ this.recordEvent(event);
1645
+ console.log(`\u{1F446} Manual trigger: ${name}`);
1646
+ await this.context.storeMemory(`Manual trigger fired: ${name}`, {
1647
+ keywords: ["trigger", "manual", name],
1648
+ metadata: { event }
1649
+ });
1650
+ }
1651
+ /**
1652
+ * Get recent events
1653
+ */
1654
+ getRecentEvents(limit = 10) {
1655
+ return this.eventHistory.slice(-limit).reverse();
1656
+ }
1657
+ /**
1658
+ * Get active listeners status
1659
+ */
1660
+ getStatus() {
1661
+ return {
1662
+ fileWatchers: this.watchers.size,
1663
+ cronTriggers: this.cronIntervals.size,
1664
+ recentEvents: this.eventHistory.length
1665
+ };
1666
+ }
1667
+ /**
1668
+ * Stop all listeners
1669
+ */
1670
+ async stopAll() {
1671
+ for (const [id] of this.watchers) {
1672
+ await this.unregisterFileWatcher(id);
1673
+ }
1674
+ for (const [id] of this.cronIntervals) {
1675
+ this.unregisterCronTrigger(id);
1676
+ }
1677
+ console.log("\u{1F6D1} All listeners stopped");
1678
+ }
1679
+ recordEvent(event) {
1680
+ this.eventHistory.push(event);
1681
+ if (this.eventHistory.length > this.maxEventHistory) {
1682
+ this.eventHistory = this.eventHistory.slice(-this.maxEventHistory);
1683
+ }
1684
+ }
1685
+ /**
1686
+ * Simple cron parser - converts basic cron expressions to milliseconds
1687
+ * Supports: @hourly, @daily, @weekly, and simple intervals like every N minutes
1688
+ */
1689
+ parseCronToMs(cron) {
1690
+ switch (cron) {
1691
+ case "@hourly":
1692
+ return 60 * 60 * 1e3;
1693
+ case "@daily":
1694
+ return 24 * 60 * 60 * 1e3;
1695
+ case "@weekly":
1696
+ return 7 * 24 * 60 * 60 * 1e3;
1697
+ case "@minutely":
1698
+ return 60 * 1e3;
1699
+ }
1700
+ const match = cron.match(/^\*\/(\d+)\s/);
1701
+ if (match) {
1702
+ const minutes = parseInt(match[1], 10);
1703
+ if (minutes > 0 && minutes <= 60) {
1704
+ return minutes * 60 * 1e3;
1705
+ }
1706
+ }
1707
+ console.warn(`\u26A0\uFE0F Unrecognized cron pattern "${cron}", using 1 minute interval`);
1708
+ return 60 * 1e3;
1709
+ }
1710
+ };
1711
+ }
1712
+ });
1713
+
1714
+ // src/daemon/index.ts
1715
+ var daemon_exports = {};
1716
+ __export(daemon_exports, {
1717
+ RainfallDaemon: () => RainfallDaemon,
1718
+ getDaemonInstance: () => getDaemonInstance,
1719
+ getDaemonStatus: () => getDaemonStatus,
1720
+ startDaemon: () => startDaemon,
1721
+ stopDaemon: () => stopDaemon
1722
+ });
1723
+ async function startDaemon(config = {}) {
1724
+ if (daemonInstance) {
1725
+ console.log("Daemon already running");
1726
+ return daemonInstance;
1727
+ }
1728
+ daemonInstance = new RainfallDaemon(config);
1729
+ await daemonInstance.start();
1730
+ return daemonInstance;
342
1731
  }
343
- var IntegrationsNamespace = class {
344
- constructor(client) {
345
- this.client = client;
1732
+ async function stopDaemon() {
1733
+ if (!daemonInstance) {
1734
+ console.log("Daemon not running");
1735
+ return;
346
1736
  }
347
- get github() {
348
- return {
349
- issues: {
350
- create: (params) => this.client.executeTool("github-create-issue", params),
351
- list: (params) => this.client.executeTool("github-list-issues", params),
352
- get: (params) => this.client.executeTool("github-get-issue", params),
353
- update: (params) => this.client.executeTool("github-update-issue", params),
354
- addComment: (params) => this.client.executeTool("github-add-issue-comment", params)
355
- },
356
- repos: {
357
- get: (params) => this.client.executeTool("github-get-repository", params),
358
- listBranches: (params) => this.client.executeTool("github-list-branches", params)
359
- },
360
- pullRequests: {
361
- list: (params) => this.client.executeTool("github-list-pull-requests", params),
362
- get: (params) => this.client.executeTool("github-get-pull-request", params)
1737
+ await daemonInstance.stop();
1738
+ daemonInstance = null;
1739
+ }
1740
+ function getDaemonStatus() {
1741
+ if (!daemonInstance) {
1742
+ return null;
1743
+ }
1744
+ return daemonInstance.getStatus();
1745
+ }
1746
+ function getDaemonInstance() {
1747
+ return daemonInstance;
1748
+ }
1749
+ var import_ws, import_express, RainfallDaemon, daemonInstance;
1750
+ var init_daemon = __esm({
1751
+ "src/daemon/index.ts"() {
1752
+ "use strict";
1753
+ init_cjs_shims();
1754
+ import_ws = require("ws");
1755
+ import_express = __toESM(require("express"));
1756
+ init_sdk();
1757
+ init_networked();
1758
+ init_context();
1759
+ init_listeners();
1760
+ RainfallDaemon = class {
1761
+ wss;
1762
+ openaiApp;
1763
+ rainfall;
1764
+ port;
1765
+ openaiPort;
1766
+ rainfallConfig;
1767
+ tools = [];
1768
+ toolSchemas = /* @__PURE__ */ new Map();
1769
+ clients = /* @__PURE__ */ new Set();
1770
+ debug;
1771
+ // New services
1772
+ networkedExecutor;
1773
+ context;
1774
+ listeners;
1775
+ constructor(config = {}) {
1776
+ this.port = config.port || 8765;
1777
+ this.openaiPort = config.openaiPort || 8787;
1778
+ this.rainfallConfig = config.rainfallConfig;
1779
+ this.debug = config.debug || false;
1780
+ this.openaiApp = (0, import_express.default)();
1781
+ this.openaiApp.use(import_express.default.json());
1782
+ }
1783
+ async start() {
1784
+ this.log("\u{1F327}\uFE0F Rainfall Daemon starting...");
1785
+ await this.initializeRainfall();
1786
+ if (!this.rainfall) {
1787
+ throw new Error("Failed to initialize Rainfall SDK");
1788
+ }
1789
+ this.context = new RainfallDaemonContext(this.rainfall, {
1790
+ maxLocalMemories: 1e3,
1791
+ maxMessageHistory: 100,
1792
+ ...this.rainfallConfig
1793
+ });
1794
+ await this.context.initialize();
1795
+ this.networkedExecutor = new RainfallNetworkedExecutor(this.rainfall, {
1796
+ wsPort: this.port,
1797
+ httpPort: this.openaiPort,
1798
+ hostname: process.env.HOSTNAME || "local-daemon",
1799
+ capabilities: {
1800
+ localExec: true,
1801
+ fileWatch: true,
1802
+ passiveListen: true
1803
+ }
1804
+ });
1805
+ await this.networkedExecutor.registerEdgeNode();
1806
+ await this.networkedExecutor.subscribeToResults((jobId, result, error) => {
1807
+ this.log(`\u{1F4EC} Job ${jobId} ${error ? "failed" : "completed"}`, error || result);
1808
+ });
1809
+ this.listeners = new RainfallListenerRegistry(
1810
+ this.rainfall,
1811
+ this.context,
1812
+ this.networkedExecutor
1813
+ );
1814
+ await this.loadTools();
1815
+ await this.startWebSocketServer();
1816
+ await this.startOpenAIProxy();
1817
+ console.log(`\u{1F680} Rainfall daemon running`);
1818
+ console.log(` WebSocket (MCP): ws://localhost:${this.port}`);
1819
+ console.log(` OpenAI API: http://localhost:${this.openaiPort}/v1/chat/completions`);
1820
+ console.log(` Health Check: http://localhost:${this.openaiPort}/health`);
1821
+ console.log(` Edge Node ID: ${this.networkedExecutor.getEdgeNodeId() || "local"}`);
1822
+ console.log(` Tools loaded: ${this.tools.length}`);
1823
+ console.log(` Press Ctrl+C to stop`);
1824
+ process.on("SIGINT", () => this.stop());
1825
+ process.on("SIGTERM", () => this.stop());
1826
+ }
1827
+ async stop() {
1828
+ this.log("\u{1F6D1} Shutting down Rainfall daemon...");
1829
+ if (this.listeners) {
1830
+ await this.listeners.stopAll();
1831
+ }
1832
+ if (this.networkedExecutor) {
1833
+ await this.networkedExecutor.unregisterEdgeNode();
1834
+ }
1835
+ for (const client of this.clients) {
1836
+ client.close();
1837
+ }
1838
+ this.clients.clear();
1839
+ if (this.wss) {
1840
+ this.wss.close();
1841
+ this.wss = void 0;
1842
+ }
1843
+ console.log("\u{1F44B} Rainfall daemon stopped");
1844
+ }
1845
+ /**
1846
+ * Get the networked executor for distributed job management
1847
+ */
1848
+ getNetworkedExecutor() {
1849
+ return this.networkedExecutor;
1850
+ }
1851
+ /**
1852
+ * Get the context for memory/session management
1853
+ */
1854
+ getContext() {
1855
+ return this.context;
1856
+ }
1857
+ /**
1858
+ * Get the listener registry for passive triggers
1859
+ */
1860
+ getListenerRegistry() {
1861
+ return this.listeners;
1862
+ }
1863
+ async initializeRainfall() {
1864
+ if (this.rainfallConfig?.apiKey) {
1865
+ this.rainfall = new Rainfall(this.rainfallConfig);
1866
+ } else {
1867
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1868
+ const config = loadConfig2();
1869
+ if (config.apiKey) {
1870
+ this.rainfall = new Rainfall({
1871
+ apiKey: config.apiKey,
1872
+ baseUrl: config.baseUrl
1873
+ });
1874
+ } else {
1875
+ throw new Error("No API key configured. Run: rainfall auth login <api-key>");
1876
+ }
1877
+ }
1878
+ }
1879
+ async loadTools() {
1880
+ if (!this.rainfall) return;
1881
+ try {
1882
+ this.tools = await this.rainfall.listTools();
1883
+ this.log(`\u{1F4E6} Loaded ${this.tools.length} tools`);
1884
+ } catch (error) {
1885
+ console.warn("\u26A0\uFE0F Failed to load tools:", error instanceof Error ? error.message : error);
1886
+ this.tools = [];
1887
+ }
1888
+ }
1889
+ async getToolSchema(toolId) {
1890
+ if (this.toolSchemas.has(toolId)) {
1891
+ return this.toolSchemas.get(toolId);
1892
+ }
1893
+ if (!this.rainfall) return null;
1894
+ try {
1895
+ const schema = await this.rainfall.getToolSchema(toolId);
1896
+ this.toolSchemas.set(toolId, schema);
1897
+ return schema;
1898
+ } catch {
1899
+ return null;
1900
+ }
1901
+ }
1902
+ async startWebSocketServer() {
1903
+ this.wss = new import_ws.WebSocketServer({ port: this.port });
1904
+ this.wss.on("connection", (ws) => {
1905
+ this.log("\u{1F7E2} MCP client connected");
1906
+ this.clients.add(ws);
1907
+ ws.on("message", async (data) => {
1908
+ try {
1909
+ const message = JSON.parse(data.toString());
1910
+ const response = await this.handleMCPMessage(message);
1911
+ ws.send(JSON.stringify(response));
1912
+ } catch (error) {
1913
+ const errorResponse = {
1914
+ jsonrpc: "2.0",
1915
+ id: void 0,
1916
+ error: {
1917
+ code: -32700,
1918
+ message: error instanceof Error ? error.message : "Parse error"
1919
+ }
1920
+ };
1921
+ ws.send(JSON.stringify(errorResponse));
1922
+ }
1923
+ });
1924
+ ws.on("close", () => {
1925
+ this.log("\u{1F534} MCP client disconnected");
1926
+ this.clients.delete(ws);
1927
+ });
1928
+ ws.on("error", (error) => {
1929
+ console.error("WebSocket error:", error);
1930
+ this.clients.delete(ws);
1931
+ });
1932
+ });
1933
+ }
1934
+ async handleMCPMessage(message) {
1935
+ const { id, method, params } = message;
1936
+ switch (method) {
1937
+ case "initialize":
1938
+ return {
1939
+ jsonrpc: "2.0",
1940
+ id,
1941
+ result: {
1942
+ protocolVersion: "2024-11-05",
1943
+ capabilities: {
1944
+ tools: { listChanged: true }
1945
+ },
1946
+ serverInfo: {
1947
+ name: "rainfall-daemon",
1948
+ version: "0.1.0"
1949
+ }
1950
+ }
1951
+ };
1952
+ case "tools/list":
1953
+ return {
1954
+ jsonrpc: "2.0",
1955
+ id,
1956
+ result: {
1957
+ tools: await this.getMCPTools()
1958
+ }
1959
+ };
1960
+ case "tools/call": {
1961
+ const toolName = params?.name;
1962
+ const toolParams = params?.arguments;
1963
+ try {
1964
+ const startTime = Date.now();
1965
+ const result = await this.executeTool(toolName, toolParams);
1966
+ const duration = Date.now() - startTime;
1967
+ if (this.context) {
1968
+ this.context.recordExecution(toolName, toolParams || {}, result, { duration });
1969
+ }
1970
+ return {
1971
+ jsonrpc: "2.0",
1972
+ id,
1973
+ result: {
1974
+ content: [
1975
+ {
1976
+ type: "text",
1977
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1978
+ }
1979
+ ]
1980
+ }
1981
+ };
1982
+ } catch (error) {
1983
+ const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
1984
+ if (this.context) {
1985
+ this.context.recordExecution(toolName, toolParams || {}, null, {
1986
+ error: errorMessage,
1987
+ duration: 0
1988
+ });
1989
+ }
1990
+ return {
1991
+ jsonrpc: "2.0",
1992
+ id,
1993
+ error: {
1994
+ code: -32603,
1995
+ message: errorMessage
1996
+ }
1997
+ };
1998
+ }
1999
+ }
2000
+ case "ping":
2001
+ return {
2002
+ jsonrpc: "2.0",
2003
+ id,
2004
+ result: {}
2005
+ };
2006
+ default:
2007
+ return {
2008
+ jsonrpc: "2.0",
2009
+ id,
2010
+ error: {
2011
+ code: -32601,
2012
+ message: `Method not found: ${method}`
2013
+ }
2014
+ };
2015
+ }
2016
+ }
2017
+ async getMCPTools() {
2018
+ const mcpTools = [];
2019
+ for (const tool of this.tools) {
2020
+ const schema = await this.getToolSchema(tool.id);
2021
+ if (schema) {
2022
+ const toolSchema = schema;
2023
+ mcpTools.push({
2024
+ name: tool.id,
2025
+ description: toolSchema.description || tool.description,
2026
+ inputSchema: toolSchema.parameters || { type: "object", properties: {} }
2027
+ });
2028
+ }
2029
+ }
2030
+ return mcpTools;
2031
+ }
2032
+ async executeTool(toolId, params) {
2033
+ if (!this.rainfall) {
2034
+ throw new Error("Rainfall SDK not initialized");
2035
+ }
2036
+ return this.rainfall.executeTool(toolId, params);
2037
+ }
2038
+ async startOpenAIProxy() {
2039
+ this.openaiApp.get("/v1/models", async (_req, res) => {
2040
+ try {
2041
+ if (this.rainfall) {
2042
+ const models = await this.rainfall.listModels();
2043
+ res.json({
2044
+ object: "list",
2045
+ data: models.map((m) => ({
2046
+ id: m.id,
2047
+ object: "model",
2048
+ created: Math.floor(Date.now() / 1e3),
2049
+ owned_by: "rainfall"
2050
+ }))
2051
+ });
2052
+ } else {
2053
+ res.json({
2054
+ object: "list",
2055
+ data: [
2056
+ { id: "llama-3.3-70b-versatile", object: "model", created: Date.now(), owned_by: "groq" },
2057
+ { id: "gpt-4o", object: "model", created: Date.now(), owned_by: "openai" },
2058
+ { id: "claude-3-5-sonnet", object: "model", created: Date.now(), owned_by: "anthropic" },
2059
+ { id: "gemini-2.0-flash-exp", object: "model", created: Date.now(), owned_by: "gemini" }
2060
+ ]
2061
+ });
2062
+ }
2063
+ } catch (error) {
2064
+ res.status(500).json({ error: "Failed to fetch models" });
2065
+ }
2066
+ });
2067
+ this.openaiApp.post("/v1/chat/completions", async (req, res) => {
2068
+ const body = req.body;
2069
+ if (!body.messages || !Array.isArray(body.messages)) {
2070
+ res.status(400).json({
2071
+ error: {
2072
+ message: "Missing required field: messages",
2073
+ type: "invalid_request_error"
2074
+ }
2075
+ });
2076
+ return;
2077
+ }
2078
+ if (!this.rainfall) {
2079
+ res.status(503).json({
2080
+ error: {
2081
+ message: "Rainfall SDK not initialized",
2082
+ type: "service_unavailable"
2083
+ }
2084
+ });
2085
+ return;
2086
+ }
2087
+ try {
2088
+ const me = await this.rainfall.getMe();
2089
+ const subscriberId = me.id;
2090
+ const localToolMap = await this.buildLocalToolMap();
2091
+ let allTools = [];
2092
+ if (body.tools && body.tools.length > 0) {
2093
+ allTools = body.tools;
2094
+ } else if (body.tool_choice) {
2095
+ const openaiTools = await this.getOpenAITools();
2096
+ allTools = openaiTools;
2097
+ }
2098
+ let messages = [...body.messages];
2099
+ const maxToolIterations = 10;
2100
+ let toolIterations = 0;
2101
+ while (toolIterations < maxToolIterations) {
2102
+ toolIterations++;
2103
+ const llmResponse = await this.callLLM({
2104
+ subscriberId,
2105
+ model: body.model,
2106
+ messages,
2107
+ tools: allTools.length > 0 ? allTools : void 0,
2108
+ tool_choice: body.tool_choice,
2109
+ temperature: body.temperature,
2110
+ max_tokens: body.max_tokens,
2111
+ stream: false,
2112
+ // Always non-streaming for tool loop
2113
+ tool_priority: body.tool_priority,
2114
+ enable_stacked: body.enable_stacked
2115
+ });
2116
+ const choice = llmResponse.choices?.[0];
2117
+ let toolCalls = choice?.message?.tool_calls || [];
2118
+ const content = choice?.message?.content || "";
2119
+ const reasoningContent = choice?.message?.reasoning_content || "";
2120
+ const fullContent = content + " " + reasoningContent;
2121
+ const xmlToolCalls = this.parseXMLToolCalls(fullContent);
2122
+ if (xmlToolCalls.length > 0) {
2123
+ this.log(`\u{1F4CB} Parsed ${xmlToolCalls.length} XML tool calls from content`);
2124
+ toolCalls = xmlToolCalls;
2125
+ }
2126
+ if (!toolCalls || toolCalls.length === 0) {
2127
+ if (body.stream) {
2128
+ await this.streamResponse(res, llmResponse);
2129
+ } else {
2130
+ res.json(llmResponse);
2131
+ }
2132
+ this.updateContext(body.messages, llmResponse);
2133
+ return;
2134
+ }
2135
+ messages.push({
2136
+ role: "assistant",
2137
+ content: choice?.message?.content || "",
2138
+ tool_calls: toolCalls
2139
+ });
2140
+ for (const toolCall of toolCalls) {
2141
+ const toolName = toolCall.function?.name;
2142
+ const toolArgsStr = toolCall.function?.arguments || "{}";
2143
+ if (!toolName) continue;
2144
+ this.log(`\u{1F527} Tool call: ${toolName}`);
2145
+ let toolResult;
2146
+ let toolError;
2147
+ try {
2148
+ const localTool = this.findLocalTool(toolName, localToolMap);
2149
+ if (localTool) {
2150
+ this.log(` \u2192 Executing locally`);
2151
+ const args = JSON.parse(toolArgsStr);
2152
+ toolResult = await this.executeLocalTool(localTool.id, args);
2153
+ } else {
2154
+ const shouldExecuteLocal = body.tool_priority === "local" || body.tool_priority === "stacked";
2155
+ if (shouldExecuteLocal) {
2156
+ try {
2157
+ const args = JSON.parse(toolArgsStr);
2158
+ toolResult = await this.rainfall.executeTool(toolName.replace(/_/g, "-"), args);
2159
+ } catch {
2160
+ toolResult = { _pending: true, tool: toolName, args: toolArgsStr };
2161
+ }
2162
+ } else {
2163
+ toolResult = { _pending: true, tool: toolName, args: toolArgsStr };
2164
+ }
2165
+ }
2166
+ } catch (error) {
2167
+ toolError = error instanceof Error ? error.message : String(error);
2168
+ this.log(` \u2192 Error: ${toolError}`);
2169
+ }
2170
+ messages.push({
2171
+ role: "tool",
2172
+ content: toolError ? JSON.stringify({ error: toolError }) : typeof toolResult === "string" ? toolResult : JSON.stringify(toolResult),
2173
+ tool_call_id: toolCall.id
2174
+ });
2175
+ if (this.context) {
2176
+ this.context.recordExecution(
2177
+ toolName,
2178
+ JSON.parse(toolArgsStr || "{}"),
2179
+ toolResult,
2180
+ { error: toolError, duration: 0 }
2181
+ );
2182
+ }
2183
+ }
2184
+ }
2185
+ res.status(500).json({
2186
+ error: {
2187
+ message: "Maximum tool execution iterations reached",
2188
+ type: "tool_execution_error"
2189
+ }
2190
+ });
2191
+ } catch (error) {
2192
+ this.log("Chat completions error:", error);
2193
+ res.status(500).json({
2194
+ error: {
2195
+ message: error instanceof Error ? error.message : "Internal server error",
2196
+ type: "internal_error"
2197
+ }
2198
+ });
2199
+ }
2200
+ });
2201
+ this.openaiApp.get("/health", (_req, res) => {
2202
+ res.json({
2203
+ status: "ok",
2204
+ daemon: "rainfall",
2205
+ version: "0.1.0",
2206
+ tools_loaded: this.tools.length,
2207
+ edge_node_id: this.networkedExecutor?.getEdgeNodeId(),
2208
+ clients_connected: this.clients.size
2209
+ });
2210
+ });
2211
+ this.openaiApp.get("/status", (_req, res) => {
2212
+ res.json(this.getStatus());
2213
+ });
2214
+ this.openaiApp.post("/v1/queue", async (req, res) => {
2215
+ const { tool_id, params, execution_mode = "any" } = req.body;
2216
+ if (!tool_id) {
2217
+ res.status(400).json({ error: "Missing required field: tool_id" });
2218
+ return;
2219
+ }
2220
+ if (!this.networkedExecutor) {
2221
+ res.status(503).json({ error: "Networked executor not available" });
2222
+ return;
2223
+ }
2224
+ try {
2225
+ const jobId = await this.networkedExecutor.queueToolExecution(
2226
+ tool_id,
2227
+ params || {},
2228
+ { executionMode: execution_mode }
2229
+ );
2230
+ res.json({ job_id: jobId, status: "queued" });
2231
+ } catch (error) {
2232
+ res.status(500).json({
2233
+ error: error instanceof Error ? error.message : "Failed to queue job"
2234
+ });
2235
+ }
2236
+ });
2237
+ return new Promise((resolve) => {
2238
+ this.openaiApp.listen(this.openaiPort, () => {
2239
+ resolve();
2240
+ });
2241
+ });
2242
+ }
2243
+ /**
2244
+ * Build a map of local Rainfall tools for quick lookup
2245
+ * Maps OpenAI-style underscore names to Rainfall tool IDs
2246
+ */
2247
+ async buildLocalToolMap() {
2248
+ const map = /* @__PURE__ */ new Map();
2249
+ for (const tool of this.tools) {
2250
+ const openAiName = tool.id.replace(/-/g, "_");
2251
+ map.set(openAiName, {
2252
+ id: tool.id,
2253
+ name: openAiName,
2254
+ description: tool.description
2255
+ });
2256
+ map.set(tool.id, {
2257
+ id: tool.id,
2258
+ name: openAiName,
2259
+ description: tool.description
2260
+ });
2261
+ }
2262
+ return map;
2263
+ }
2264
+ /**
2265
+ * Find a local Rainfall tool by name (OpenAI underscore format or original)
2266
+ */
2267
+ findLocalTool(toolName, localToolMap) {
2268
+ if (localToolMap.has(toolName)) {
2269
+ return localToolMap.get(toolName);
2270
+ }
2271
+ const dashedName = toolName.replace(/_/g, "-");
2272
+ if (localToolMap.has(dashedName)) {
2273
+ return localToolMap.get(dashedName);
2274
+ }
2275
+ return void 0;
2276
+ }
2277
+ /**
2278
+ * Execute a local Rainfall tool
2279
+ */
2280
+ async executeLocalTool(toolId, args) {
2281
+ if (!this.rainfall) {
2282
+ throw new Error("Rainfall SDK not initialized");
2283
+ }
2284
+ const startTime = Date.now();
2285
+ try {
2286
+ const result = await this.rainfall.executeTool(toolId, args);
2287
+ const duration = Date.now() - startTime;
2288
+ this.log(` \u2713 Completed in ${duration}ms`);
2289
+ return result;
2290
+ } catch (error) {
2291
+ const duration = Date.now() - startTime;
2292
+ this.log(` \u2717 Failed after ${duration}ms`);
2293
+ throw error;
2294
+ }
2295
+ }
2296
+ /**
2297
+ * Parse XML-style tool calls from model output
2298
+ * Handles formats like: <function=name><parameter=key>value</parameter></function>
2299
+ */
2300
+ parseXMLToolCalls(content) {
2301
+ const toolCalls = [];
2302
+ const functionRegex = /<function=([^>]+)>([\s\S]*?)<\/function>/gi;
2303
+ let match;
2304
+ while ((match = functionRegex.exec(content)) !== null) {
2305
+ const functionName = match[1].trim();
2306
+ const paramsBlock = match[2];
2307
+ const params = {};
2308
+ const paramRegex = /<parameter=([^>]+)>([\s\S]*?)<\/parameter>/gi;
2309
+ let paramMatch;
2310
+ while ((paramMatch = paramRegex.exec(paramsBlock)) !== null) {
2311
+ const paramName = paramMatch[1].trim();
2312
+ const paramValue = paramMatch[2].trim();
2313
+ params[paramName] = paramValue;
2314
+ }
2315
+ toolCalls.push({
2316
+ id: `xml-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
2317
+ type: "function",
2318
+ function: {
2319
+ name: functionName,
2320
+ arguments: JSON.stringify(params)
2321
+ }
2322
+ });
2323
+ this.log(`\u{1F4CB} Parsed XML tool call: ${functionName}(${JSON.stringify(params)})`);
2324
+ }
2325
+ return toolCalls;
2326
+ }
2327
+ /**
2328
+ * Call the LLM via Rainfall backend, LM Studio, RunPod, or other providers
2329
+ *
2330
+ * Provider priority:
2331
+ * 1. Config file (llm.provider, llm.baseUrl)
2332
+ * 2. Environment variables (OPENAI_API_KEY, OLLAMA_HOST, etc.)
2333
+ * 3. Default to Rainfall (credits-based)
2334
+ */
2335
+ async callLLM(params) {
2336
+ if (!this.rainfall) {
2337
+ throw new Error("Rainfall SDK not initialized");
2338
+ }
2339
+ const { loadConfig: loadConfig2, getProviderBaseUrl: getProviderBaseUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2340
+ const config = loadConfig2();
2341
+ const provider = config.llm?.provider || "rainfall";
2342
+ switch (provider) {
2343
+ case "local":
2344
+ case "ollama":
2345
+ return this.callLocalLLM(params, config);
2346
+ case "openai":
2347
+ case "anthropic":
2348
+ return this.callExternalLLM(params, config, provider);
2349
+ case "rainfall":
2350
+ default:
2351
+ return this.rainfall.chatCompletions({
2352
+ subscriber_id: params.subscriberId,
2353
+ model: params.model,
2354
+ messages: params.messages,
2355
+ stream: params.stream || false,
2356
+ temperature: params.temperature,
2357
+ max_tokens: params.max_tokens,
2358
+ tools: params.tools,
2359
+ tool_choice: params.tool_choice,
2360
+ tool_priority: params.tool_priority,
2361
+ enable_stacked: params.enable_stacked
2362
+ });
2363
+ }
2364
+ }
2365
+ /**
2366
+ * Call external LLM provider (OpenAI, Anthropic) via their OpenAI-compatible APIs
2367
+ */
2368
+ async callExternalLLM(params, config, provider) {
2369
+ const { getProviderBaseUrl: getProviderBaseUrl2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2370
+ const baseUrl = config.llm?.baseUrl || getProviderBaseUrl2({ llm: { provider } });
2371
+ const apiKey = config.llm?.apiKey;
2372
+ if (!apiKey) {
2373
+ throw new Error(`${provider} API key not configured. Set via: rainfall config set llm.apiKey <key>`);
2374
+ }
2375
+ const model = params.model || config.llm?.model || (provider === "anthropic" ? "claude-3-5-sonnet-20241022" : "gpt-4o");
2376
+ const url = `${baseUrl}/chat/completions`;
2377
+ const response = await fetch(url, {
2378
+ method: "POST",
2379
+ headers: {
2380
+ "Content-Type": "application/json",
2381
+ "Authorization": `Bearer ${apiKey}`
2382
+ },
2383
+ body: JSON.stringify({
2384
+ model,
2385
+ messages: params.messages,
2386
+ tools: params.tools,
2387
+ tool_choice: params.tool_choice,
2388
+ temperature: params.temperature,
2389
+ max_tokens: params.max_tokens,
2390
+ stream: false
2391
+ // Tool loop requires non-streaming
2392
+ })
2393
+ });
2394
+ if (!response.ok) {
2395
+ const error = await response.text();
2396
+ throw new Error(`${provider} API error: ${error}`);
2397
+ }
2398
+ return response.json();
2399
+ }
2400
+ /**
2401
+ * Call a local LLM (LM Studio, Ollama, etc.)
2402
+ */
2403
+ async callLocalLLM(params, config) {
2404
+ const baseUrl = config.llm?.baseUrl || "http://localhost:1234/v1";
2405
+ const apiKey = config.llm?.apiKey || "not-needed";
2406
+ const model = params.model || config.llm?.model || "local-model";
2407
+ const url = `${baseUrl}/chat/completions`;
2408
+ const response = await fetch(url, {
2409
+ method: "POST",
2410
+ headers: {
2411
+ "Content-Type": "application/json",
2412
+ "Authorization": `Bearer ${apiKey}`
2413
+ },
2414
+ body: JSON.stringify({
2415
+ model,
2416
+ messages: params.messages,
2417
+ tools: params.tools,
2418
+ tool_choice: params.tool_choice,
2419
+ temperature: params.temperature,
2420
+ max_tokens: params.max_tokens,
2421
+ stream: false
2422
+ // Tool loop requires non-streaming
2423
+ })
2424
+ });
2425
+ if (!response.ok) {
2426
+ const error = await response.text();
2427
+ throw new Error(`Local LLM error: ${error}`);
2428
+ }
2429
+ return response.json();
2430
+ }
2431
+ /**
2432
+ * Stream a response to the client (converts non-streaming to SSE format)
2433
+ */
2434
+ async streamResponse(res, response) {
2435
+ res.setHeader("Content-Type", "text/event-stream");
2436
+ res.setHeader("Cache-Control", "no-cache");
2437
+ res.setHeader("Connection", "keep-alive");
2438
+ const message = response.choices?.[0]?.message;
2439
+ const id = response.id || `chatcmpl-${Date.now()}`;
2440
+ const model = response.model || "unknown";
2441
+ const created = Math.floor(Date.now() / 1e3);
2442
+ res.write(`data: ${JSON.stringify({
2443
+ id,
2444
+ object: "chat.completion.chunk",
2445
+ created,
2446
+ model,
2447
+ choices: [{ index: 0, delta: { role: "assistant" }, finish_reason: null }]
2448
+ })}
2449
+
2450
+ `);
2451
+ const content = message?.content || "";
2452
+ const chunkSize = 10;
2453
+ for (let i = 0; i < content.length; i += chunkSize) {
2454
+ const chunk = content.slice(i, i + chunkSize);
2455
+ res.write(`data: ${JSON.stringify({
2456
+ id,
2457
+ object: "chat.completion.chunk",
2458
+ created,
2459
+ model,
2460
+ choices: [{ index: 0, delta: { content: chunk }, finish_reason: null }]
2461
+ })}
2462
+
2463
+ `);
2464
+ }
2465
+ res.write(`data: ${JSON.stringify({
2466
+ id,
2467
+ object: "chat.completion.chunk",
2468
+ created,
2469
+ model,
2470
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }]
2471
+ })}
2472
+
2473
+ `);
2474
+ res.write("data: [DONE]\n\n");
2475
+ res.end();
363
2476
  }
364
- };
365
- }
366
- get notion() {
367
- return {
368
- pages: {
369
- create: (params) => this.client.executeTool("notion-pages-create", params),
370
- retrieve: (params) => this.client.executeTool("notion-pages-retrieve", params),
371
- update: (params) => this.client.executeTool("notion-pages-update", params)
372
- },
373
- databases: {
374
- query: (params) => this.client.executeTool("notion-databases-query", params),
375
- retrieve: (params) => this.client.executeTool("notion-databases-retrieve", params)
376
- },
377
- blocks: {
378
- appendChildren: (params) => this.client.executeTool("notion-blocks-append-children", params),
379
- retrieveChildren: (params) => this.client.executeTool("notion-blocks-retrieve-children", params)
2477
+ /**
2478
+ * Update context with conversation history
2479
+ */
2480
+ updateContext(originalMessages, response) {
2481
+ if (!this.context) return;
2482
+ const lastUserMessage = originalMessages.filter((m) => m.role === "user").pop();
2483
+ if (lastUserMessage) {
2484
+ this.context.addMessage("user", lastUserMessage.content);
2485
+ }
2486
+ const assistantContent = response.choices?.[0]?.message?.content;
2487
+ if (assistantContent) {
2488
+ this.context.addMessage("assistant", assistantContent);
2489
+ }
380
2490
  }
381
- };
382
- }
383
- get linear() {
384
- return {
385
- issues: {
386
- create: (params) => this.client.executeTool("linear-core-issueCreate", params),
387
- list: (params) => this.client.executeTool("linear-core-issues", params),
388
- get: (params) => this.client.executeTool("linear-core-issue", params),
389
- update: (params) => this.client.executeTool("linear-core-issueUpdate", params),
390
- archive: (params) => this.client.executeTool("linear-core-issueArchive", params)
391
- },
392
- teams: {
393
- list: () => this.client.executeTool("linear-core-teams", {})
2491
+ async getOpenAITools() {
2492
+ const tools = [];
2493
+ for (const tool of this.tools.slice(0, 128)) {
2494
+ const schema = await this.getToolSchema(tool.id);
2495
+ if (schema) {
2496
+ const toolSchema = schema;
2497
+ let parameters = { type: "object", properties: {}, required: [] };
2498
+ if (toolSchema.parameters && typeof toolSchema.parameters === "object") {
2499
+ const rawParams = toolSchema.parameters;
2500
+ parameters = {
2501
+ type: rawParams.type || "object",
2502
+ properties: rawParams.properties || {},
2503
+ required: rawParams.required || []
2504
+ };
2505
+ }
2506
+ tools.push({
2507
+ type: "function",
2508
+ function: {
2509
+ name: tool.id.replace(/-/g, "_"),
2510
+ // OpenAI requires underscore names
2511
+ description: toolSchema.description || tool.description,
2512
+ parameters
2513
+ }
2514
+ });
2515
+ }
2516
+ }
2517
+ return tools;
394
2518
  }
395
- };
396
- }
397
- get slack() {
398
- return {
399
- messages: {
400
- send: (params) => this.client.executeTool("slack-core-postMessage", params),
401
- list: (params) => this.client.executeTool("slack-core-listMessages", params)
402
- },
403
- channels: {
404
- list: () => this.client.executeTool("slack-core-listChannels", {})
405
- },
406
- users: {
407
- list: () => this.client.executeTool("slack-core-listUsers", {})
408
- },
409
- reactions: {
410
- add: (params) => this.client.executeTool("slack-core-addReaction", params)
2519
+ buildResponseContent() {
2520
+ const edgeNodeId = this.networkedExecutor?.getEdgeNodeId();
2521
+ const toolCount = this.tools.length;
2522
+ return `Rainfall daemon online. Edge node: ${edgeNodeId || "local"}. ${toolCount} tools available. What would you like to execute locally or in the cloud?`;
411
2523
  }
412
- };
413
- }
414
- get figma() {
415
- return {
416
- files: {
417
- get: (params) => this.client.executeTool("figma-files-getFile", { fileKey: params.fileKey }),
418
- getNodes: (params) => this.client.executeTool("figma-files-getFileNodes", { fileKey: params.fileKey, nodeIds: params.nodeIds }),
419
- getImages: (params) => this.client.executeTool("figma-files-getFileImage", { fileKey: params.fileKey, nodeIds: params.nodeIds, format: params.format }),
420
- getComments: (params) => this.client.executeTool("figma-comments-getFileComments", { fileKey: params.fileKey }),
421
- postComment: (params) => this.client.executeTool("figma-comments-postComment", { fileKey: params.fileKey, message: params.message, nodeId: params.nodeId })
422
- },
423
- projects: {
424
- list: (params) => this.client.executeTool("figma-projects-getTeamProjects", { teamId: params.teamId }),
425
- getFiles: (params) => this.client.executeTool("figma-projects-getProjectFiles", { projectId: params.projectId })
2524
+ getStatus() {
2525
+ return {
2526
+ running: !!this.wss,
2527
+ port: this.port,
2528
+ openaiPort: this.openaiPort,
2529
+ toolsLoaded: this.tools.length,
2530
+ clientsConnected: this.clients.size,
2531
+ edgeNodeId: this.networkedExecutor?.getEdgeNodeId(),
2532
+ context: this.context?.getStatus() || {
2533
+ memoriesCached: 0,
2534
+ activeSessions: 0,
2535
+ executionHistorySize: 0
2536
+ },
2537
+ listeners: this.listeners?.getStatus() || {
2538
+ fileWatchers: 0,
2539
+ cronTriggers: 0,
2540
+ recentEvents: 0
2541
+ }
2542
+ };
426
2543
  }
427
- };
428
- }
429
- get stripe() {
430
- return {
431
- customers: {
432
- create: (params) => this.client.executeTool("stripe-customers-create", params),
433
- retrieve: (params) => this.client.executeTool("stripe-customers-retrieve", { customerId: params.customerId }),
434
- update: (params) => this.client.executeTool("stripe-customers-update", params),
435
- listPaymentMethods: (params) => this.client.executeTool("stripe-customers-list-payment-methods", { customerId: params.customerId })
436
- },
437
- paymentIntents: {
438
- create: (params) => this.client.executeTool("stripe-payment-intents-create", params),
439
- retrieve: (params) => this.client.executeTool("stripe-payment-intents-retrieve", { paymentIntentId: params.paymentIntentId }),
440
- confirm: (params) => this.client.executeTool("stripe-payment-intents-confirm", { paymentIntentId: params.paymentIntentId })
441
- },
442
- subscriptions: {
443
- create: (params) => this.client.executeTool("stripe-subscriptions-create", params),
444
- retrieve: (params) => this.client.executeTool("stripe-subscriptions-retrieve", { subscriptionId: params.subscriptionId }),
445
- cancel: (params) => this.client.executeTool("stripe-subscriptions-cancel", { subscriptionId: params.subscriptionId })
2544
+ log(...args) {
2545
+ if (this.debug) {
2546
+ console.log(...args);
2547
+ }
446
2548
  }
447
2549
  };
2550
+ daemonInstance = null;
448
2551
  }
449
- };
450
-
451
- // src/namespaces/memory.ts
452
- function createMemory(client) {
453
- return {
454
- create: (params) => client.executeTool("memory-create", params),
455
- get: (params) => client.executeTool("memory-get", { memoryId: params.memoryId }),
456
- recall: (params) => client.executeTool("memory-recall", params),
457
- list: (params) => client.executeTool("memory-list", params ?? {}),
458
- update: (params) => client.executeTool("memory-update", params),
459
- delete: (params) => client.executeTool("memory-delete", { memoryId: params.memoryId })
460
- };
461
- }
462
-
463
- // src/namespaces/articles.ts
464
- function createArticles(client) {
465
- return {
466
- search: (params) => client.executeTool("article-search", params),
467
- create: (params) => client.executeTool("article-create", params),
468
- createFromUrl: (params) => client.executeTool("article-create-from-url", params),
469
- fetch: (params) => client.executeTool("article-fetch", params),
470
- recent: (params) => client.executeTool("article-recent", params ?? {}),
471
- relevant: (params) => client.executeTool("article-relevant-news", params),
472
- summarize: (params) => client.executeTool("article-summarize", params),
473
- extractTopics: (params) => client.executeTool("article-topic-extractor", params)
474
- };
475
- }
476
-
477
- // src/namespaces/web.ts
478
- function createWeb(client) {
479
- return {
480
- search: {
481
- exa: (params) => client.executeTool("exa-web-search", params),
482
- perplexity: (params) => client.executeTool("perplexity-search", params)
483
- },
484
- fetch: (params) => client.executeTool("web-fetch", params),
485
- htmlToMarkdown: (params) => client.executeTool("html-to-markdown-converter", params),
486
- extractHtml: (params) => client.executeTool("extract-html-selector", params)
487
- };
488
- }
489
-
490
- // src/namespaces/ai.ts
491
- function createAI(client) {
492
- return {
493
- embeddings: {
494
- document: (params) => client.executeTool("jina-document-embedding", params),
495
- query: (params) => client.executeTool("jina-query-embedding", params),
496
- image: (params) => client.executeTool("jina-image-embedding", { image: params.imageBase64 })
497
- },
498
- image: {
499
- generate: (params) => client.executeTool("image-generation", params)
500
- },
501
- ocr: (params) => client.executeTool("ocr-text-extraction", { image: params.imageBase64 }),
502
- vision: (params) => client.executeTool("llama-scout-vision", { image: params.imageBase64, prompt: params.prompt }),
503
- chat: (params) => client.executeTool("xai-chat-completions", params),
504
- complete: (params) => client.executeTool("fim", params),
505
- classify: (params) => client.executeTool("jina-document-classifier", params),
506
- segment: (params) => client.executeTool("jina-text-segmenter", params)
507
- };
508
- }
509
-
510
- // src/namespaces/data.ts
511
- function createData(client) {
512
- return {
513
- csv: {
514
- query: (params) => client.executeTool("query-csv", params),
515
- convert: (params) => client.executeTool("csv-convert", params)
516
- },
517
- scripts: {
518
- create: (params) => client.executeTool("create-saved-script", params),
519
- execute: (params) => client.executeTool("execute-saved-script", params),
520
- list: () => client.executeTool("list-saved-scripts", {}),
521
- update: (params) => client.executeTool("update-saved-script", params),
522
- delete: (params) => client.executeTool("delete-saved-script", params)
523
- },
524
- similarity: {
525
- search: (params) => client.executeTool("duck-db-similarity-search", params),
526
- duckDbSearch: (params) => client.executeTool("duck-db-similarity-search", params)
527
- }
528
- };
529
- }
530
-
531
- // src/namespaces/utils.ts
532
- function createUtils(client) {
533
- return {
534
- mermaid: (params) => client.executeTool("mermaid-diagram-generator", { mermaid: params.diagram }),
535
- documentConvert: (params) => client.executeTool("document-format-converter", {
536
- base64: `data:${params.mimeType};base64,${Buffer.from(params.document).toString("base64")}`,
537
- format: params.format
538
- }),
539
- regex: {
540
- match: (params) => client.executeTool("regex-match", params),
541
- replace: (params) => client.executeTool("regex-replace", params)
542
- },
543
- jsonExtract: (params) => client.executeTool("json-extract", params),
544
- digest: (params) => client.executeTool("digest-generator", { text: params.data }),
545
- monteCarlo: (params) => client.executeTool("monte-carlo-simulation", params)
546
- };
547
- }
548
-
549
- // src/sdk.ts
550
- var Rainfall = class {
551
- client;
552
- _integrations;
553
- _memory;
554
- _articles;
555
- _web;
556
- _ai;
557
- _data;
558
- _utils;
559
- constructor(config) {
560
- this.client = new RainfallClient(config);
561
- }
562
- /**
563
- * Integrations namespace - GitHub, Notion, Linear, Slack, Figma, Stripe
564
- *
565
- * @example
566
- * ```typescript
567
- * // GitHub
568
- * await rainfall.integrations.github.issues.create({
569
- * owner: 'facebook',
570
- * repo: 'react',
571
- * title: 'Bug report'
572
- * });
573
- *
574
- * // Slack
575
- * await rainfall.integrations.slack.messages.send({
576
- * channelId: 'C123456',
577
- * text: 'Hello team!'
578
- * });
579
- *
580
- * // Linear
581
- * const issues = await rainfall.integrations.linear.issues.list();
582
- * ```
583
- */
584
- get integrations() {
585
- if (!this._integrations) {
586
- this._integrations = createIntegrations(this.client);
587
- }
588
- return this._integrations;
589
- }
590
- /**
591
- * Memory namespace - Semantic memory storage and retrieval
592
- *
593
- * @example
594
- * ```typescript
595
- * // Store a memory
596
- * await rainfall.memory.create({
597
- * content: 'User prefers dark mode',
598
- * keywords: ['preference', 'ui']
599
- * });
600
- *
601
- * // Recall similar memories
602
- * const memories = await rainfall.memory.recall({
603
- * query: 'user preferences',
604
- * topK: 5
605
- * });
606
- * ```
607
- */
608
- get memory() {
609
- if (!this._memory) {
610
- this._memory = createMemory(this.client);
611
- }
612
- return this._memory;
613
- }
614
- /**
615
- * Articles namespace - News aggregation and article management
616
- *
617
- * @example
618
- * ```typescript
619
- * // Search news
620
- * const articles = await rainfall.articles.search({
621
- * query: 'artificial intelligence'
622
- * });
623
- *
624
- * // Create from URL
625
- * const article = await rainfall.articles.createFromUrl({
626
- * url: 'https://example.com/article'
627
- * });
628
- *
629
- * // Summarize
630
- * const summary = await rainfall.articles.summarize({
631
- * text: article.content
632
- * });
633
- * ```
634
- */
635
- get articles() {
636
- if (!this._articles) {
637
- this._articles = createArticles(this.client);
638
- }
639
- return this._articles;
640
- }
641
- /**
642
- * Web namespace - Web search, scraping, and content extraction
643
- *
644
- * @example
645
- * ```typescript
646
- * // Search with Exa
647
- * const results = await rainfall.web.search.exa({
648
- * query: 'latest AI research'
649
- * });
650
- *
651
- * // Fetch and convert
652
- * const html = await rainfall.web.fetch({ url: 'https://example.com' });
653
- * const markdown = await rainfall.web.htmlToMarkdown({ html });
654
- *
655
- * // Extract specific elements
656
- * const links = await rainfall.web.extractHtml({
657
- * html,
658
- * selector: 'a[href]'
659
- * });
660
- * ```
661
- */
662
- get web() {
663
- if (!this._web) {
664
- this._web = createWeb(this.client);
665
- }
666
- return this._web;
667
- }
668
- /**
669
- * AI namespace - Embeddings, image generation, OCR, vision, chat
670
- *
671
- * @example
672
- * ```typescript
673
- * // Generate embeddings
674
- * const embedding = await rainfall.ai.embeddings.document({
675
- * text: 'Hello world'
676
- * });
677
- *
678
- * // Generate image
679
- * const image = await rainfall.ai.image.generate({
680
- * prompt: 'A serene mountain landscape'
681
- * });
682
- *
683
- * // OCR
684
- * const text = await rainfall.ai.ocr({ imageBase64: '...' });
685
- *
686
- * // Chat
687
- * const response = await rainfall.ai.chat({
688
- * messages: [{ role: 'user', content: 'Hello!' }]
689
- * });
690
- * ```
691
- */
692
- get ai() {
693
- if (!this._ai) {
694
- this._ai = createAI(this.client);
695
- }
696
- return this._ai;
697
- }
698
- /**
699
- * Data namespace - CSV processing, scripts, similarity search
700
- *
701
- * @example
702
- * ```typescript
703
- * // Query CSV with SQL
704
- * const results = await rainfall.data.csv.query({
705
- * sql: 'SELECT * FROM data WHERE value > 100'
706
- * });
707
- *
708
- * // Execute saved script
709
- * const result = await rainfall.data.scripts.execute({
710
- * name: 'my-script',
711
- * params: { input: 'data' }
712
- * });
713
- * ```
714
- */
715
- get data() {
716
- if (!this._data) {
717
- this._data = createData(this.client);
718
- }
719
- return this._data;
720
- }
721
- /**
722
- * Utils namespace - Mermaid diagrams, document conversion, regex, JSON extraction
723
- *
724
- * @example
725
- * ```typescript
726
- * // Generate diagram
727
- * const diagram = await rainfall.utils.mermaid({
728
- * diagram: 'graph TD; A-->B;'
729
- * });
730
- *
731
- * // Convert document
732
- * const pdf = await rainfall.utils.documentConvert({
733
- * document: markdownContent,
734
- * mimeType: 'text/markdown',
735
- * format: 'pdf'
736
- * });
737
- *
738
- * // Extract JSON from text
739
- * const json = await rainfall.utils.jsonExtract({
740
- * text: 'Here is some data: {"key": "value"}'
741
- * });
742
- * ```
743
- */
744
- get utils() {
745
- if (!this._utils) {
746
- this._utils = createUtils(this.client);
747
- }
748
- return this._utils;
749
- }
750
- /**
751
- * Get the underlying HTTP client for advanced usage
752
- */
753
- getClient() {
754
- return this.client;
755
- }
756
- /**
757
- * List all available tools
758
- */
759
- async listTools() {
760
- return this.client.listTools();
761
- }
762
- /**
763
- * Get schema for a specific tool
764
- */
765
- async getToolSchema(toolId) {
766
- return this.client.getToolSchema(toolId);
767
- }
768
- /**
769
- * Execute any tool by ID (low-level access)
770
- */
771
- async executeTool(toolId, params) {
772
- return this.client.executeTool(toolId, params);
773
- }
774
- /**
775
- * Get current subscriber info and usage
776
- */
777
- async getMe() {
778
- return this.client.getMe();
779
- }
780
- /**
781
- * Get current rate limit info
782
- */
783
- getRateLimitInfo() {
784
- return this.client.getRateLimitInfo();
785
- }
786
- };
2552
+ });
787
2553
 
788
2554
  // src/cli/index.ts
789
- var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".rainfall");
790
- var CONFIG_FILE = (0, import_path.join)(CONFIG_DIR, "config.json");
791
- function loadConfig() {
792
- if (!(0, import_fs.existsSync)(CONFIG_FILE)) {
793
- return {};
794
- }
795
- try {
796
- return JSON.parse((0, import_fs.readFileSync)(CONFIG_FILE, "utf8"));
797
- } catch {
798
- return {};
799
- }
800
- }
801
- function saveConfig(config) {
802
- if (!(0, import_fs.existsSync)(CONFIG_DIR)) {
803
- (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
804
- }
805
- (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2));
806
- }
807
- function getRainfall() {
808
- const config = loadConfig();
809
- if (!config.apiKey) {
810
- console.error("Error: No API key configured. Run: rainfall auth login");
811
- process.exit(1);
812
- }
813
- return new Rainfall({
814
- apiKey: config.apiKey,
815
- baseUrl: config.baseUrl
816
- });
817
- }
2555
+ init_cjs_shims();
2556
+ var import_fs2 = require("fs");
2557
+ var import_path2 = require("path");
2558
+ var import_url = require("url");
2559
+ init_sdk();
2560
+ init_config();
2561
+ var import_child_process = require("child_process");
818
2562
  function printHelp() {
819
2563
  console.log(`
820
2564
  Rainfall CLI - 200+ tools, one key
@@ -832,28 +2576,66 @@ Commands:
832
2576
  tools search <query> Search for tools
833
2577
 
834
2578
  run <tool> [options] Execute a tool
2579
+
2580
+ daemon start Start the Rainfall daemon
2581
+ daemon stop Stop the Rainfall daemon
2582
+ daemon restart Restart the Rainfall daemon
2583
+ daemon status Check daemon status
2584
+
2585
+ workflow new Create a new workflow (interactive)
2586
+ workflow run <workflow> Run a saved workflow
835
2587
 
836
2588
  me Show account info and usage
837
2589
 
838
2590
  config get [key] Get configuration value
839
2591
  config set <key> <value> Set configuration value
2592
+ config llm Show LLM configuration
2593
+
2594
+ version Show version information
2595
+ upgrade Upgrade to the latest version
840
2596
 
841
2597
  help Show this help message
842
2598
 
2599
+ Configuration keys:
2600
+ llm.provider LLM provider (rainfall|openai|anthropic|ollama|local)
2601
+ llm.baseUrl Base URL for the LLM API
2602
+ llm.apiKey API key for the LLM provider
2603
+ llm.model Default model to use
2604
+
843
2605
  Options for 'run':
844
2606
  --params, -p <json> Tool parameters as JSON
845
2607
  --file, -f <path> Read parameters from file
846
2608
  --raw Output raw JSON
2609
+ --<key> <value> Pass individual parameters (e.g., --query "AI news")
2610
+
2611
+ Options for 'daemon start':
2612
+ --port <port> WebSocket port (default: 8765)
2613
+ --openai-port <port> OpenAI API port (default: 8787)
2614
+ --debug Enable verbose debug logging
847
2615
 
848
2616
  Examples:
849
2617
  rainfall auth login
850
2618
  rainfall tools list
851
2619
  rainfall tools describe github-create-issue
852
2620
  rainfall run exa-web-search -p '{"query": "AI news"}'
2621
+ rainfall run exa-web-search --query "AI news"
2622
+ rainfall run github-create-issue --owner facebook --repo react --title "Bug"
853
2623
  rainfall run article-summarize -f ./article.json
2624
+ rainfall daemon start
854
2625
  echo '{"query": "hello"}' | rainfall run exa-web-search
855
2626
  `);
856
2627
  }
2628
+ function getRainfall() {
2629
+ const config = loadConfig();
2630
+ if (!config.apiKey) {
2631
+ console.error("Error: No API key configured. Run: rainfall auth login");
2632
+ process.exit(1);
2633
+ }
2634
+ return new Rainfall({
2635
+ apiKey: config.apiKey,
2636
+ baseUrl: config.baseUrl
2637
+ });
2638
+ }
857
2639
  async function authLogin(args) {
858
2640
  const apiKey = args[0] || process.env.RAINFALL_API_KEY;
859
2641
  if (!apiKey) {
@@ -1018,16 +2800,20 @@ Options:
1018
2800
  -p, --params <json> Tool parameters as JSON string
1019
2801
  -f, --file <path> Read parameters from JSON file
1020
2802
  --raw Output raw JSON (no formatting)
2803
+ --<key> <value> Pass individual parameters (e.g., --query "AI news")
1021
2804
 
1022
2805
  Examples:
1023
2806
  rainfall run figma-users-getMe
1024
2807
  rainfall run exa-web-search -p '{"query": "AI news"}'
2808
+ rainfall run exa-web-search --query "AI news"
2809
+ rainfall run github-create-issue --owner facebook --repo react --title "Bug"
1025
2810
  rainfall run github-create-issue -f ./issue.json
1026
2811
  echo '{"query": "hello"}' | rainfall run exa-web-search
1027
2812
  `);
1028
2813
  return;
1029
2814
  }
1030
2815
  let params = {};
2816
+ const rawArgs = [];
1031
2817
  for (let i = 1; i < args.length; i++) {
1032
2818
  const arg = args[i];
1033
2819
  if (arg === "--params" || arg === "-p") {
@@ -1049,12 +2835,26 @@ Examples:
1049
2835
  process.exit(1);
1050
2836
  }
1051
2837
  try {
1052
- params = JSON.parse((0, import_fs.readFileSync)(filePath, "utf8"));
2838
+ params = JSON.parse((0, import_fs2.readFileSync)(filePath, "utf8"));
1053
2839
  } catch {
1054
2840
  console.error(`Error: Could not read or parse file: ${filePath}`);
1055
2841
  process.exit(1);
1056
2842
  }
1057
2843
  } else if (arg === "--raw") {
2844
+ } else if (arg.startsWith("--")) {
2845
+ const key = arg.slice(2);
2846
+ const value = args[++i];
2847
+ if (value === void 0) {
2848
+ console.error(`Error: ${arg} requires a value`);
2849
+ process.exit(1);
2850
+ }
2851
+ try {
2852
+ params[key] = JSON.parse(value);
2853
+ } catch {
2854
+ params[key] = value;
2855
+ }
2856
+ } else {
2857
+ rawArgs.push(arg);
1058
2858
  }
1059
2859
  }
1060
2860
  if (!process.stdin.isTTY) {
@@ -1092,6 +2892,20 @@ Examples:
1092
2892
  }
1093
2893
  }
1094
2894
  const rainfall = getRainfall();
2895
+ if (rawArgs.length === 1 && Object.keys(params).length === 0) {
2896
+ try {
2897
+ const schema = await rainfall.getToolSchema(toolId);
2898
+ if (schema.parameters && typeof schema.parameters === "object") {
2899
+ const paramEntries = Object.entries(schema.parameters);
2900
+ const requiredParams = paramEntries.filter(([, p]) => !p.optional);
2901
+ if (requiredParams.length === 1) {
2902
+ const [paramName] = requiredParams[0];
2903
+ params = { [paramName]: rawArgs[0] };
2904
+ }
2905
+ }
2906
+ } catch {
2907
+ }
2908
+ }
1095
2909
  try {
1096
2910
  const result = await rainfall.executeTool(toolId, params);
1097
2911
  if (args.includes("--raw")) {
@@ -1118,7 +2932,16 @@ function configGet(args) {
1118
2932
  const key = args[0];
1119
2933
  const config = loadConfig();
1120
2934
  if (key) {
1121
- console.log(config[key] || "");
2935
+ const parts = key.split(".");
2936
+ let value = config;
2937
+ for (const part of parts) {
2938
+ value = value?.[part];
2939
+ }
2940
+ if (typeof value === "object" && value !== null) {
2941
+ console.log(JSON.stringify(value, null, 2));
2942
+ } else {
2943
+ console.log(value ?? "");
2944
+ }
1122
2945
  } else {
1123
2946
  console.log(JSON.stringify(config, null, 2));
1124
2947
  }
@@ -1129,13 +2952,231 @@ function configSet(args) {
1129
2952
  if (!key || !value) {
1130
2953
  console.error("Error: Both key and value required");
1131
2954
  console.error("\nUsage: rainfall config set <key> <value>");
2955
+ console.error("\nExamples:");
2956
+ console.error(" rainfall config set llm.provider local");
2957
+ console.error(" rainfall config set llm.baseUrl http://localhost:1234/v1");
2958
+ console.error(" rainfall config set llm.model llama-3.3-70b-versatile");
1132
2959
  process.exit(1);
1133
2960
  }
1134
2961
  const config = loadConfig();
1135
- config[key] = value;
2962
+ const parts = key.split(".");
2963
+ if (parts.length === 1) {
2964
+ config[key] = value;
2965
+ } else {
2966
+ let target = config;
2967
+ for (let i = 0; i < parts.length - 1; i++) {
2968
+ if (!target[parts[i]] || typeof target[parts[i]] !== "object") {
2969
+ target[parts[i]] = {};
2970
+ }
2971
+ target = target[parts[i]];
2972
+ }
2973
+ target[parts[parts.length - 1]] = value;
2974
+ }
1136
2975
  saveConfig(config);
1137
2976
  console.log(`\u2713 Set ${key} = ${value}`);
1138
2977
  }
2978
+ function configLLM() {
2979
+ const config = loadConfig();
2980
+ const llm = config.llm || { provider: "rainfall" };
2981
+ console.log("LLM Configuration:");
2982
+ console.log(` Provider: ${llm.provider}`);
2983
+ console.log(` Base URL: ${llm.baseUrl || "(default)"}`);
2984
+ console.log(` Model: ${llm.model || "(default)"}`);
2985
+ console.log(` API Key: ${llm.apiKey ? "****" + llm.apiKey.slice(-4) : "(none)"}`);
2986
+ console.log();
2987
+ console.log("Providers:");
2988
+ console.log(" rainfall - Use Rainfall backend (default, uses your credits)");
2989
+ console.log(" openai - Use OpenAI API directly");
2990
+ console.log(" anthropic - Use Anthropic API directly");
2991
+ console.log(" ollama - Use local Ollama instance");
2992
+ console.log(" local - Use any OpenAI-compatible endpoint (LM Studio, etc.)");
2993
+ console.log();
2994
+ console.log("Examples:");
2995
+ console.log(" rainfall config set llm.provider local");
2996
+ console.log(" rainfall config set llm.baseUrl http://localhost:1234/v1");
2997
+ console.log(" rainfall config set llm.provider openai");
2998
+ console.log(" rainfall config set llm.apiKey sk-...");
2999
+ }
3000
+ function getPackageJson() {
3001
+ try {
3002
+ const __filename2 = (0, import_url.fileURLToPath)(importMetaUrl);
3003
+ const __dirname = (0, import_path2.dirname)(__filename2);
3004
+ const packagePath = (0, import_path2.join)(__dirname, "..", "..", "package.json");
3005
+ const content = (0, import_fs2.readFileSync)(packagePath, "utf8");
3006
+ return JSON.parse(content);
3007
+ } catch {
3008
+ return { version: "unknown", name: "@rainfall-devkit/sdk" };
3009
+ }
3010
+ }
3011
+ function showVersion() {
3012
+ const pkg = getPackageJson();
3013
+ console.log(`${pkg.name} v${pkg.version}`);
3014
+ }
3015
+ async function upgrade() {
3016
+ const pkg = getPackageJson();
3017
+ console.log(`Upgrading ${pkg.name}...`);
3018
+ const execPath = process.argv[0];
3019
+ const isBun = execPath.includes("bun");
3020
+ let command;
3021
+ let args;
3022
+ if (isBun) {
3023
+ command = "bun";
3024
+ args = ["add", "-g", `${pkg.name}@latest`];
3025
+ } else {
3026
+ command = "npm";
3027
+ args = ["i", "-g", `${pkg.name}@latest`];
3028
+ }
3029
+ console.log(`Running: ${command} ${args.join(" ")}`);
3030
+ console.log();
3031
+ return new Promise((resolve, reject) => {
3032
+ const child = (0, import_child_process.spawn)(command, args, {
3033
+ stdio: "inherit",
3034
+ shell: true
3035
+ });
3036
+ child.on("close", (code) => {
3037
+ if (code === 0) {
3038
+ console.log();
3039
+ console.log("\u2713 Upgrade complete");
3040
+ resolve();
3041
+ } else {
3042
+ reject(new Error(`Upgrade failed with exit code ${code}`));
3043
+ }
3044
+ });
3045
+ child.on("error", (err) => {
3046
+ reject(err);
3047
+ });
3048
+ });
3049
+ }
3050
+ async function daemonStart(args) {
3051
+ let port;
3052
+ let openaiPort;
3053
+ let debug = false;
3054
+ for (let i = 0; i < args.length; i++) {
3055
+ const arg = args[i];
3056
+ if (arg === "--port") {
3057
+ const val = parseInt(args[++i], 10);
3058
+ if (!isNaN(val)) port = val;
3059
+ } else if (arg === "--openai-port") {
3060
+ const val = parseInt(args[++i], 10);
3061
+ if (!isNaN(val)) openaiPort = val;
3062
+ } else if (arg === "--debug") {
3063
+ debug = true;
3064
+ }
3065
+ }
3066
+ const { startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3067
+ try {
3068
+ await startDaemon2({ port, openaiPort, debug });
3069
+ process.on("SIGINT", async () => {
3070
+ console.log("\n");
3071
+ const { stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3072
+ await stopDaemon2();
3073
+ process.exit(0);
3074
+ });
3075
+ process.on("SIGTERM", async () => {
3076
+ const { stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3077
+ await stopDaemon2();
3078
+ process.exit(0);
3079
+ });
3080
+ } catch (error) {
3081
+ console.error("Failed to start daemon:", error instanceof Error ? error.message : error);
3082
+ process.exit(1);
3083
+ }
3084
+ }
3085
+ async function daemonStop() {
3086
+ const { stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3087
+ await stopDaemon2();
3088
+ }
3089
+ async function daemonRestart(args) {
3090
+ const { stopDaemon: stopDaemon2, startDaemon: startDaemon2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3091
+ let port;
3092
+ let openaiPort;
3093
+ let debug = false;
3094
+ for (let i = 0; i < args.length; i++) {
3095
+ const arg = args[i];
3096
+ if (arg === "--port") {
3097
+ const val = parseInt(args[++i], 10);
3098
+ if (!isNaN(val)) port = val;
3099
+ } else if (arg === "--openai-port") {
3100
+ const val = parseInt(args[++i], 10);
3101
+ if (!isNaN(val)) openaiPort = val;
3102
+ } else if (arg === "--debug") {
3103
+ debug = true;
3104
+ }
3105
+ }
3106
+ console.log("\u{1F504} Restarting daemon...");
3107
+ try {
3108
+ await stopDaemon2();
3109
+ await new Promise((resolve) => setTimeout(resolve, 500));
3110
+ await startDaemon2({ port, openaiPort, debug });
3111
+ process.on("SIGINT", async () => {
3112
+ console.log("\n");
3113
+ const { stopDaemon: stop } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3114
+ await stop();
3115
+ process.exit(0);
3116
+ });
3117
+ process.on("SIGTERM", async () => {
3118
+ const { stopDaemon: stop } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3119
+ await stop();
3120
+ process.exit(0);
3121
+ });
3122
+ } catch (error) {
3123
+ console.error("Failed to restart daemon:", error instanceof Error ? error.message : error);
3124
+ process.exit(1);
3125
+ }
3126
+ }
3127
+ async function daemonStatus() {
3128
+ const { getDaemonStatus: getDaemonStatus2 } = await Promise.resolve().then(() => (init_daemon(), daemon_exports));
3129
+ const status = getDaemonStatus2();
3130
+ if (!status) {
3131
+ console.log("Daemon not running");
3132
+ console.log("Run: rainfall daemon start");
3133
+ return;
3134
+ }
3135
+ console.log("Daemon status:");
3136
+ console.log(` Running: ${status.running ? "yes" : "no"}`);
3137
+ console.log(` WebSocket port: ${status.port}`);
3138
+ console.log(` OpenAI API port: ${status.openaiPort}`);
3139
+ console.log(` Tools loaded: ${status.toolsLoaded}`);
3140
+ console.log(` Clients connected: ${status.clientsConnected}`);
3141
+ console.log(` Edge Node ID: ${status.edgeNodeId || "local"}`);
3142
+ console.log();
3143
+ console.log("Context:");
3144
+ console.log(` Memories cached: ${status.context.memoriesCached}`);
3145
+ console.log(` Active sessions: ${status.context.activeSessions}`);
3146
+ console.log(` Current session: ${status.context.currentSession || "none"}`);
3147
+ console.log(` Execution history: ${status.context.executionHistorySize}`);
3148
+ console.log();
3149
+ console.log("Listeners:");
3150
+ console.log(` File watchers: ${status.listeners.fileWatchers}`);
3151
+ console.log(` Cron triggers: ${status.listeners.cronTriggers}`);
3152
+ console.log(` Recent events: ${status.listeners.recentEvents}`);
3153
+ }
3154
+ async function workflowNew() {
3155
+ console.log("\u{1F6A7} Interactive workflow creation coming soon!");
3156
+ console.log();
3157
+ console.log("For now, create workflows using the SDK:");
3158
+ console.log(' import { createFileWatcherWorkflow } from "@rainfall-devkit/sdk/daemon";');
3159
+ console.log();
3160
+ console.log("Example:");
3161
+ console.log(` const workflow = createFileWatcherWorkflow('pdf-processor', '~/Downloads', {`);
3162
+ console.log(` pattern: '*.pdf',`);
3163
+ console.log(` events: ['create'],`);
3164
+ console.log(` workflow: [`);
3165
+ console.log(` { toolId: 'ocr-pdf', params: {} },`);
3166
+ console.log(` { toolId: 'notion-create-page', params: { parent: '...' } },`);
3167
+ console.log(` ],`);
3168
+ console.log(` });`);
3169
+ }
3170
+ async function workflowRun(args) {
3171
+ const workflowId = args[0];
3172
+ if (!workflowId) {
3173
+ console.error("Error: Workflow ID required");
3174
+ console.error("\nUsage: rainfall workflow run <workflow-id>");
3175
+ process.exit(1);
3176
+ }
3177
+ console.log(`\u{1F6A7} Running workflow: ${workflowId}`);
3178
+ console.log("Workflow execution coming soon!");
3179
+ }
1139
3180
  async function main() {
1140
3181
  const args = process.argv.slice(2);
1141
3182
  const command = args[0];
@@ -1179,6 +3220,40 @@ async function main() {
1179
3220
  case "run":
1180
3221
  await runTool(args.slice(1));
1181
3222
  break;
3223
+ case "daemon":
3224
+ switch (subcommand) {
3225
+ case "start":
3226
+ await daemonStart(rest);
3227
+ break;
3228
+ case "stop":
3229
+ await daemonStop();
3230
+ break;
3231
+ case "restart":
3232
+ await daemonRestart(rest);
3233
+ break;
3234
+ case "status":
3235
+ await daemonStatus();
3236
+ break;
3237
+ default:
3238
+ console.error("Error: Unknown daemon subcommand");
3239
+ console.error("\nUsage: rainfall daemon <start|stop|restart|status>");
3240
+ process.exit(1);
3241
+ }
3242
+ break;
3243
+ case "workflow":
3244
+ switch (subcommand) {
3245
+ case "new":
3246
+ await workflowNew();
3247
+ break;
3248
+ case "run":
3249
+ await workflowRun(rest);
3250
+ break;
3251
+ default:
3252
+ console.error("Error: Unknown workflow subcommand");
3253
+ console.error("\nUsage: rainfall workflow <new|run>");
3254
+ process.exit(1);
3255
+ }
3256
+ break;
1182
3257
  case "me":
1183
3258
  await showMe();
1184
3259
  break;
@@ -1190,12 +3265,23 @@ async function main() {
1190
3265
  case "set":
1191
3266
  configSet(rest);
1192
3267
  break;
3268
+ case "llm":
3269
+ configLLM();
3270
+ break;
1193
3271
  default:
1194
3272
  console.error("Error: Unknown config subcommand");
1195
- console.error("\nUsage: rainfall config <get|set>");
3273
+ console.error("\nUsage: rainfall config <get|set|llm>");
1196
3274
  process.exit(1);
1197
3275
  }
1198
3276
  break;
3277
+ case "version":
3278
+ case "--version":
3279
+ case "-v":
3280
+ showVersion();
3281
+ break;
3282
+ case "upgrade":
3283
+ await upgrade();
3284
+ break;
1199
3285
  case "help":
1200
3286
  case "--help":
1201
3287
  case "-h":