@dominusnode/flowise-tools 1.0.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.
@@ -0,0 +1,1668 @@
1
+ "use strict";
2
+ /**
3
+ * DomiNode Flowise Toolkit
4
+ *
5
+ * Provides 24 LangChain-compatible DynamicStructuredTool instances for use
6
+ * inside Flowise AI workflows. Covers proxied fetching, wallet management,
7
+ * agentic wallets, teams, PayPal top-up, and x402 micropayment info.
8
+ *
9
+ * Security:
10
+ * - Full SSRF protection (private IP blocking, DNS rebinding, Teredo/6to4,
11
+ * IPv4-mapped/compatible IPv6, hex/octal/decimal normalization, zone ID
12
+ * stripping, .localhost/.local/.internal/.arpa TLD blocking, embedded
13
+ * credential blocking)
14
+ * - OFAC sanctioned country validation (CU, IR, KP, RU, SY)
15
+ * - Credential scrubbing in all error outputs
16
+ * - Prototype pollution prevention on all parsed JSON (recursive)
17
+ * - HTTP method restriction (GET/HEAD/OPTIONS only for proxied fetch)
18
+ * - 10 MB response cap, 4000 char truncation, 30 s timeout
19
+ * - Redirect following disabled to prevent open redirect abuse
20
+ */
21
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
22
+ if (k2 === undefined) k2 = k;
23
+ var desc = Object.getOwnPropertyDescriptor(m, k);
24
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
25
+ desc = { enumerable: true, get: function() { return m[k]; } };
26
+ }
27
+ Object.defineProperty(o, k2, desc);
28
+ }) : (function(o, m, k, k2) {
29
+ if (k2 === undefined) k2 = k;
30
+ o[k2] = m[k];
31
+ }));
32
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
33
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
34
+ }) : function(o, v) {
35
+ o["default"] = v;
36
+ });
37
+ var __importStar = (this && this.__importStar) || (function () {
38
+ var ownKeys = function(o) {
39
+ ownKeys = Object.getOwnPropertyNames || function (o) {
40
+ var ar = [];
41
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
42
+ return ar;
43
+ };
44
+ return ownKeys(o);
45
+ };
46
+ return function (mod) {
47
+ if (mod && mod.__esModule) return mod;
48
+ var result = {};
49
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
50
+ __setModuleDefault(result, mod);
51
+ return result;
52
+ };
53
+ })();
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.DominusNodeToolkit = exports.BLOCKED_HOSTNAMES = exports.SANCTIONED_COUNTRIES = void 0;
56
+ exports.scrubCredentials = scrubCredentials;
57
+ exports.truncate = truncate;
58
+ exports.normalizeIpv4 = normalizeIpv4;
59
+ exports.isPrivateIp = isPrivateIp;
60
+ exports.validateTargetUrl = validateTargetUrl;
61
+ exports.validateCountry = validateCountry;
62
+ exports.validateUuid = validateUuid;
63
+ exports.checkDnsRebinding = checkDnsRebinding;
64
+ exports.stripDangerousKeys = stripDangerousKeys;
65
+ exports.formatBytes = formatBytes;
66
+ exports.formatCents = formatCents;
67
+ // @ts-ignore
68
+ const tools_1 = require("@langchain/core/tools");
69
+ const zod_1 = require("zod");
70
+ const http = __importStar(require("node:http"));
71
+ const tls = __importStar(require("node:tls"));
72
+ const dns = __importStar(require("dns/promises"));
73
+ // ---------------------------------------------------------------------------
74
+ // Constants
75
+ // ---------------------------------------------------------------------------
76
+ const MAX_RESPONSE_CHARS = 4000;
77
+ const REQUEST_TIMEOUT_MS = 30000;
78
+ const MAX_RESPONSE_BYTES = 10 * 1024 * 1024; // 10 MB hard cap
79
+ const MAX_PROXY_RESPONSE_BYTES = 1048576; // 1 MB for proxied fetch
80
+ /** OFAC sanctioned countries -- must never be used as geo-targeting destinations. */
81
+ exports.SANCTIONED_COUNTRIES = new Set(["CU", "IR", "KP", "RU", "SY"]);
82
+ /** ISO 3166-1 alpha-2 country code pattern. */
83
+ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
84
+ /** UUID v4 pattern for wallet/team IDs. */
85
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
86
+ // ---------------------------------------------------------------------------
87
+ // Credential scrubbing
88
+ // ---------------------------------------------------------------------------
89
+ /** Remove any dn_live_* or dn_test_* tokens from error messages. */
90
+ function scrubCredentials(msg) {
91
+ return msg.replace(/dn_(live|test)_[A-Za-z0-9_-]+/g, "dn_$1_***REDACTED***");
92
+ }
93
+ function safeError(err) {
94
+ const raw = err instanceof Error ? err.message : String(err);
95
+ return scrubCredentials(raw);
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Response truncation
99
+ // ---------------------------------------------------------------------------
100
+ function truncate(text, max = MAX_RESPONSE_CHARS) {
101
+ if (text.length <= max)
102
+ return text;
103
+ return text.slice(0, max) + `\n\n... [truncated, ${text.length - max} chars omitted]`;
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // SSRF Protection
107
+ // ---------------------------------------------------------------------------
108
+ exports.BLOCKED_HOSTNAMES = new Set([
109
+ "localhost",
110
+ "localhost.localdomain",
111
+ "ip6-localhost",
112
+ "ip6-loopback",
113
+ "[::1]",
114
+ "[::ffff:127.0.0.1]",
115
+ "0.0.0.0",
116
+ "[::]",
117
+ ]);
118
+ /**
119
+ * Normalize non-standard IPv4 representations to dotted-decimal.
120
+ * Handles: decimal integers (2130706433), hex (0x7f000001), octal octets (0177.0.0.1).
121
+ */
122
+ function normalizeIpv4(hostname) {
123
+ // Single decimal integer
124
+ if (/^\d+$/.test(hostname)) {
125
+ const n = parseInt(hostname, 10);
126
+ if (n >= 0 && n <= 0xffffffff) {
127
+ return `${(n >>> 24) & 0xff}.${(n >>> 16) & 0xff}.${(n >>> 8) & 0xff}.${n & 0xff}`;
128
+ }
129
+ }
130
+ // Hex notation
131
+ if (/^0x[0-9a-fA-F]+$/i.test(hostname)) {
132
+ const n = parseInt(hostname, 16);
133
+ if (n >= 0 && n <= 0xffffffff) {
134
+ return `${(n >>> 24) & 0xff}.${(n >>> 16) & 0xff}.${(n >>> 8) & 0xff}.${n & 0xff}`;
135
+ }
136
+ }
137
+ // Octal / mixed-radix octets
138
+ const parts = hostname.split(".");
139
+ if (parts.length === 4) {
140
+ const octets = [];
141
+ for (const part of parts) {
142
+ let val;
143
+ if (/^0x[0-9a-fA-F]+$/i.test(part)) {
144
+ val = parseInt(part, 16);
145
+ }
146
+ else if (/^0\d+$/.test(part)) {
147
+ val = parseInt(part, 8);
148
+ }
149
+ else if (/^\d+$/.test(part)) {
150
+ val = parseInt(part, 10);
151
+ }
152
+ else {
153
+ return null;
154
+ }
155
+ if (isNaN(val) || val < 0 || val > 255)
156
+ return null;
157
+ octets.push(val);
158
+ }
159
+ return octets.join(".");
160
+ }
161
+ return null;
162
+ }
163
+ function isPrivateIp(hostname) {
164
+ let ip = hostname.replace(/^\[|\]$/g, "");
165
+ // Strip IPv6 zone ID
166
+ const zoneIdx = ip.indexOf("%");
167
+ if (zoneIdx !== -1) {
168
+ ip = ip.substring(0, zoneIdx);
169
+ }
170
+ const normalized = normalizeIpv4(ip);
171
+ const checkIp = normalized ?? ip;
172
+ // IPv4 private ranges
173
+ const ipv4Match = checkIp.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
174
+ if (ipv4Match) {
175
+ const a = Number(ipv4Match[1]);
176
+ const b = Number(ipv4Match[2]);
177
+ if (a === 0)
178
+ return true;
179
+ if (a === 10)
180
+ return true;
181
+ if (a === 127)
182
+ return true;
183
+ if (a === 169 && b === 254)
184
+ return true;
185
+ if (a === 172 && b >= 16 && b <= 31)
186
+ return true;
187
+ if (a === 192 && b === 168)
188
+ return true;
189
+ if (a === 100 && b >= 64 && b <= 127)
190
+ return true;
191
+ if (a >= 224)
192
+ return true;
193
+ return false;
194
+ }
195
+ // IPv6 private ranges
196
+ const ipLower = ip.toLowerCase();
197
+ if (ipLower === "::1")
198
+ return true;
199
+ if (ipLower === "::")
200
+ return true;
201
+ if (ipLower.startsWith("fc") || ipLower.startsWith("fd"))
202
+ return true;
203
+ if (ipLower.startsWith("fe80"))
204
+ return true;
205
+ if (ipLower.startsWith("::ffff:")) {
206
+ const embedded = ipLower.slice(7);
207
+ if (embedded.includes(".")) {
208
+ return isPrivateIp(embedded);
209
+ }
210
+ const hexParts = embedded.split(":");
211
+ if (hexParts.length === 2) {
212
+ const hi = parseInt(hexParts[0], 16);
213
+ const lo = parseInt(hexParts[1], 16);
214
+ if (!isNaN(hi) && !isNaN(lo)) {
215
+ const reconstructed = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
216
+ return isPrivateIp(reconstructed);
217
+ }
218
+ }
219
+ return isPrivateIp(embedded);
220
+ }
221
+ // IPv4-compatible IPv6 (::x.x.x.x) -- deprecated but still parsed
222
+ if (ipLower.startsWith("::") && !ipLower.startsWith("::ffff:")) {
223
+ const rest = ipLower.slice(2);
224
+ if (rest && rest.includes("."))
225
+ return isPrivateIp(rest);
226
+ const hexParts = rest.split(":");
227
+ if (hexParts.length === 2 && hexParts[0] && hexParts[1]) {
228
+ const hi = parseInt(hexParts[0], 16);
229
+ const lo = parseInt(hexParts[1], 16);
230
+ if (!isNaN(hi) && !isNaN(lo)) {
231
+ const reconstructed = `${(hi >> 8) & 0xff}.${hi & 0xff}.${(lo >> 8) & 0xff}.${lo & 0xff}`;
232
+ return isPrivateIp(reconstructed);
233
+ }
234
+ }
235
+ }
236
+ // Teredo (2001:0000::/32) -- block unconditionally
237
+ if (ipLower.startsWith("2001:0000:") || ipLower.startsWith("2001:0:"))
238
+ return true;
239
+ // 6to4 (2002::/16) -- block unconditionally
240
+ if (ipLower.startsWith("2002:"))
241
+ return true;
242
+ // IPv6 multicast (ff00::/8)
243
+ if (ipLower.startsWith("ff"))
244
+ return true;
245
+ return false;
246
+ }
247
+ /**
248
+ * Validate a URL for safety before proxying.
249
+ * Blocks: private IPs, localhost, .local/.internal/.arpa, embedded credentials, non-http(s).
250
+ */
251
+ function validateTargetUrl(url) {
252
+ let parsed;
253
+ try {
254
+ parsed = new URL(url);
255
+ }
256
+ catch {
257
+ throw new Error(`Invalid URL: ${scrubCredentials(url)}`);
258
+ }
259
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
260
+ throw new Error(`Only http: and https: protocols are supported, got ${parsed.protocol}`);
261
+ }
262
+ // Block embedded credentials (user:pass@host)
263
+ if (parsed.username || parsed.password) {
264
+ throw new Error("URLs with embedded credentials are not allowed");
265
+ }
266
+ const hostname = parsed.hostname.toLowerCase();
267
+ if (exports.BLOCKED_HOSTNAMES.has(hostname)) {
268
+ throw new Error("Requests to localhost/loopback addresses are blocked");
269
+ }
270
+ if (isPrivateIp(hostname)) {
271
+ throw new Error("Requests to private/internal IP addresses are blocked");
272
+ }
273
+ // Block .localhost TLD (RFC 6761)
274
+ if (hostname.endsWith(".localhost")) {
275
+ throw new Error("Requests to localhost/loopback addresses are blocked");
276
+ }
277
+ // Block internal network TLDs
278
+ if (hostname.endsWith(".local") ||
279
+ hostname.endsWith(".internal") ||
280
+ hostname.endsWith(".arpa")) {
281
+ throw new Error("Requests to internal network hostnames are blocked");
282
+ }
283
+ return parsed;
284
+ }
285
+ /**
286
+ * Validate a country code: must be 2 uppercase letters and not OFAC sanctioned.
287
+ */
288
+ function validateCountry(country) {
289
+ if (!country)
290
+ return undefined;
291
+ const upper = country.toUpperCase().trim();
292
+ if (!COUNTRY_CODE_RE.test(upper)) {
293
+ throw new Error(`Invalid country code: "${country}". Must be a 2-letter ISO 3166-1 code.`);
294
+ }
295
+ if (exports.SANCTIONED_COUNTRIES.has(upper)) {
296
+ throw new Error(`Country "${upper}" is OFAC sanctioned and cannot be used as a proxy target.`);
297
+ }
298
+ return upper;
299
+ }
300
+ /**
301
+ * Validate a UUID string.
302
+ */
303
+ function validateUuid(id, label) {
304
+ const trimmed = id.trim();
305
+ if (!UUID_RE.test(trimmed)) {
306
+ throw new Error(`Invalid ${label}: must be a valid UUID.`);
307
+ }
308
+ return trimmed;
309
+ }
310
+ // ---------------------------------------------------------------------------
311
+ // DNS rebinding protection
312
+ // ---------------------------------------------------------------------------
313
+ /**
314
+ * Resolve a hostname via DNS and verify none of the resolved IPs are private.
315
+ * Skips check if the hostname is already an IP literal.
316
+ */
317
+ async function checkDnsRebinding(hostname) {
318
+ // If hostname is already an IP literal (v4 or bracketed v6), skip DNS resolution
319
+ const stripped = hostname.replace(/^\[|\]$/g, "");
320
+ if (/^(\d{1,3}\.){3}\d{1,3}$/.test(stripped) || stripped.includes(":")) {
321
+ return;
322
+ }
323
+ const resolvedIps = [];
324
+ try {
325
+ const ipv4s = await dns.resolve4(hostname);
326
+ resolvedIps.push(...ipv4s);
327
+ }
328
+ catch {
329
+ // NODATA or NXDOMAIN for A records is fine, continue to AAAA
330
+ }
331
+ try {
332
+ const ipv6s = await dns.resolve6(hostname);
333
+ resolvedIps.push(...ipv6s);
334
+ }
335
+ catch {
336
+ // NODATA or NXDOMAIN for AAAA records is fine
337
+ }
338
+ for (const ip of resolvedIps) {
339
+ if (isPrivateIp(ip)) {
340
+ throw new Error(`DNS rebinding detected: hostname resolves to private IP ${ip}`);
341
+ }
342
+ }
343
+ }
344
+ // ---------------------------------------------------------------------------
345
+ // Prototype pollution prevention (recursive)
346
+ // ---------------------------------------------------------------------------
347
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
348
+ function stripDangerousKeys(obj, depth = 0) {
349
+ if (depth > 50 || !obj || typeof obj !== "object")
350
+ return;
351
+ if (Array.isArray(obj)) {
352
+ for (const item of obj)
353
+ stripDangerousKeys(item, depth + 1);
354
+ return;
355
+ }
356
+ const record = obj;
357
+ for (const key of Object.keys(record)) {
358
+ if (DANGEROUS_KEYS.has(key)) {
359
+ delete record[key];
360
+ }
361
+ else if (record[key] && typeof record[key] === "object") {
362
+ stripDangerousKeys(record[key], depth + 1);
363
+ }
364
+ }
365
+ }
366
+ // ---------------------------------------------------------------------------
367
+ // Formatting helpers
368
+ // ---------------------------------------------------------------------------
369
+ function formatBytes(bytes) {
370
+ if (bytes < 1024)
371
+ return `${bytes} B`;
372
+ if (bytes < 1024 * 1024)
373
+ return `${(bytes / 1024).toFixed(1)} KB`;
374
+ if (bytes < 1024 * 1024 * 1024)
375
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
376
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(3)} GB`;
377
+ }
378
+ function formatCents(cents) {
379
+ return `$${(cents / 100).toFixed(2)}`;
380
+ }
381
+ // ---------------------------------------------------------------------------
382
+ // DominusNodeToolkit
383
+ // ---------------------------------------------------------------------------
384
+ class DominusNodeToolkit {
385
+ constructor(options = {}) {
386
+ this.token = null;
387
+ this.tokenExpiresAt = 0;
388
+ this.apiKey = options.apiKey || process.env.DOMINUSNODE_API_KEY || "";
389
+ this.baseUrl = (options.baseUrl || process.env.DOMINUSNODE_BASE_URL || "https://api.dominusnode.com").replace(/\/+$/, "");
390
+ this.proxyHost = options.proxyHost || process.env.DOMINUSNODE_PROXY_HOST || "proxy.dominusnode.com";
391
+ const portVal = Number(options.proxyPort || process.env.DOMINUSNODE_PROXY_PORT || 8080);
392
+ this.proxyPort = isNaN(portVal) || portVal < 1 || portVal > 65535 ? 8080 : portVal;
393
+ this.timeout = options.timeout || 30000;
394
+ }
395
+ // -----------------------------------------------------------------------
396
+ // Authentication
397
+ // -----------------------------------------------------------------------
398
+ async _authenticate() {
399
+ if (!this.apiKey) {
400
+ throw new Error("DomiNode API key is required. Pass apiKey in options or set DOMINUSNODE_API_KEY env var.");
401
+ }
402
+ if (!this.apiKey.startsWith("dn_live_") && !this.apiKey.startsWith("dn_test_")) {
403
+ throw new Error('DOMINUSNODE_API_KEY must start with "dn_live_" or "dn_test_".');
404
+ }
405
+ const res = await fetch(`${this.baseUrl}/api/auth/verify-key`, {
406
+ method: "POST",
407
+ headers: {
408
+ "Content-Type": "application/json",
409
+ "User-Agent": "dominusnode-flowise/1.0.0",
410
+ },
411
+ body: JSON.stringify({ apiKey: this.apiKey }),
412
+ redirect: "error",
413
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
414
+ });
415
+ if (!res.ok) {
416
+ const text = await res.text().catch(() => "");
417
+ throw new Error(`Auth failed (${res.status}): ${scrubCredentials(text.slice(0, 500))}`);
418
+ }
419
+ const data = (await res.json());
420
+ stripDangerousKeys(data);
421
+ this.token = data.token;
422
+ // JWT expires in 15 min, refresh at 14 min for safety
423
+ this.tokenExpiresAt = Date.now() + 14 * 60 * 1000;
424
+ return this.token;
425
+ }
426
+ async _ensureAuth() {
427
+ if (!this.token || Date.now() >= this.tokenExpiresAt) {
428
+ await this._authenticate();
429
+ }
430
+ }
431
+ // -----------------------------------------------------------------------
432
+ // API request helpers
433
+ // -----------------------------------------------------------------------
434
+ async _apiRequest(method, path, body) {
435
+ await this._ensureAuth();
436
+ const url = `${this.baseUrl}${path}`;
437
+ const headers = {
438
+ Authorization: `Bearer ${this.token}`,
439
+ Accept: "application/json",
440
+ "User-Agent": "dominusnode-flowise/1.0.0",
441
+ };
442
+ const init = {
443
+ method,
444
+ headers,
445
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
446
+ redirect: "error",
447
+ };
448
+ if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
449
+ headers["Content-Type"] = "application/json";
450
+ init.body = JSON.stringify(body);
451
+ }
452
+ let res;
453
+ try {
454
+ res = await fetch(url, init);
455
+ }
456
+ catch (err) {
457
+ throw new Error(`API request failed: ${safeError(err)}`);
458
+ }
459
+ let rawText;
460
+ try {
461
+ rawText = await res.text();
462
+ }
463
+ catch {
464
+ rawText = "";
465
+ }
466
+ if (new TextEncoder().encode(rawText).length > MAX_RESPONSE_BYTES) {
467
+ throw new Error("API response too large");
468
+ }
469
+ if (!res.ok) {
470
+ let errorMsg = `API error ${res.status}`;
471
+ if (rawText) {
472
+ try {
473
+ const parsed = JSON.parse(rawText);
474
+ stripDangerousKeys(parsed);
475
+ if (parsed.error) {
476
+ errorMsg = `API error ${res.status}: ${scrubCredentials(String(parsed.error))}`;
477
+ }
478
+ else if (parsed.message) {
479
+ errorMsg = `API error ${res.status}: ${scrubCredentials(String(parsed.message))}`;
480
+ }
481
+ }
482
+ catch {
483
+ errorMsg = `API error ${res.status}: ${scrubCredentials(rawText.slice(0, 200))}`;
484
+ }
485
+ }
486
+ throw new Error(errorMsg);
487
+ }
488
+ if (!rawText || rawText.trim().length === 0) {
489
+ return {};
490
+ }
491
+ try {
492
+ const parsed = JSON.parse(rawText);
493
+ stripDangerousKeys(parsed);
494
+ return parsed;
495
+ }
496
+ catch {
497
+ throw new Error("Failed to parse API response as JSON");
498
+ }
499
+ }
500
+ async _requestWithRetry(method, path, body) {
501
+ try {
502
+ return await this._apiRequest(method, path, body);
503
+ }
504
+ catch (err) {
505
+ if (err instanceof Error && err.message.includes("401")) {
506
+ this.token = null;
507
+ this.tokenExpiresAt = 0;
508
+ return await this._apiRequest(method, path, body);
509
+ }
510
+ throw err;
511
+ }
512
+ }
513
+ // -----------------------------------------------------------------------
514
+ // Tool 1: proxiedFetch
515
+ // -----------------------------------------------------------------------
516
+ async proxiedFetch(url, method = "GET", country, proxyType = "dc") {
517
+ try {
518
+ if (!url)
519
+ return "Error: url parameter is required.";
520
+ const parsedUrl = validateTargetUrl(url);
521
+ await checkDnsRebinding(parsedUrl.hostname);
522
+ const validCountry = validateCountry(country);
523
+ const methodUpper = (method || "GET").toUpperCase();
524
+ const ALLOWED_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
525
+ if (!ALLOWED_METHODS.has(methodUpper)) {
526
+ return `Error: HTTP method "${methodUpper}" is not allowed. Only GET, HEAD, and OPTIONS are permitted.`;
527
+ }
528
+ if (proxyType !== "dc" && proxyType !== "residential" && proxyType !== "auto") {
529
+ return "Error: proxyType must be 'dc', 'residential', or 'auto'.";
530
+ }
531
+ const apiKey = this.apiKey;
532
+ if (!apiKey)
533
+ return "Error: API key is required for proxied fetch.";
534
+ const parts = [];
535
+ if (proxyType && proxyType !== "auto")
536
+ parts.push(proxyType);
537
+ if (validCountry)
538
+ parts.push(`country-${validCountry.toUpperCase()}`);
539
+ const username = parts.length > 0 ? parts.join("-") : "auto";
540
+ const proxyAuth = "Basic " + Buffer.from(`${username}:${apiKey}`).toString("base64");
541
+ const parsed = new URL(url);
542
+ const result = await new Promise((resolve, reject) => {
543
+ const timer = setTimeout(() => reject(new Error("Proxy request timed out")), REQUEST_TIMEOUT_MS);
544
+ if (parsed.protocol === "https:") {
545
+ const connectHost = parsed.hostname.includes(":")
546
+ ? `[${parsed.hostname}]`
547
+ : parsed.hostname;
548
+ const connectReq = http.request({
549
+ hostname: this.proxyHost,
550
+ port: this.proxyPort,
551
+ method: "CONNECT",
552
+ path: `${connectHost}:${parsed.port || 443}`,
553
+ headers: {
554
+ "Proxy-Authorization": proxyAuth,
555
+ Host: `${connectHost}:${parsed.port || 443}`,
556
+ },
557
+ });
558
+ connectReq.on("connect", (_res, sock) => {
559
+ if (_res.statusCode !== 200) {
560
+ clearTimeout(timer);
561
+ sock.destroy();
562
+ reject(new Error(`CONNECT failed: ${_res.statusCode}`));
563
+ return;
564
+ }
565
+ const tlsSock = tls.connect({
566
+ host: parsed.hostname,
567
+ socket: sock,
568
+ servername: parsed.hostname,
569
+ minVersion: "TLSv1.2",
570
+ }, () => {
571
+ const reqLine = `${methodUpper} ${parsed.pathname + parsed.search} HTTP/1.1\r\n` +
572
+ `Host: ${parsed.host}\r\n` +
573
+ `User-Agent: dominusnode-flowise/1.0.0\r\n` +
574
+ `Connection: close\r\n\r\n`;
575
+ tlsSock.write(reqLine);
576
+ const chunks = [];
577
+ let bytes = 0;
578
+ tlsSock.on("data", (c) => {
579
+ bytes += c.length;
580
+ if (bytes <= MAX_PROXY_RESPONSE_BYTES + 16384)
581
+ chunks.push(c);
582
+ });
583
+ let done = false;
584
+ const fin = () => {
585
+ if (done)
586
+ return;
587
+ done = true;
588
+ clearTimeout(timer);
589
+ const raw = Buffer.concat(chunks).toString("utf-8");
590
+ const hEnd = raw.indexOf("\r\n\r\n");
591
+ if (hEnd === -1) {
592
+ reject(new Error("Malformed response"));
593
+ return;
594
+ }
595
+ const hdr = raw.substring(0, hEnd);
596
+ const respBody = raw.substring(hEnd + 4).substring(0, MAX_PROXY_RESPONSE_BYTES);
597
+ const sm = hdr.split("\r\n")[0].match(/^HTTP\/\d\.\d\s+(\d+)/);
598
+ const hdrs = {};
599
+ for (const l of hdr.split("\r\n").slice(1)) {
600
+ const ci = l.indexOf(":");
601
+ if (ci > 0)
602
+ hdrs[l.substring(0, ci).trim().toLowerCase()] = l.substring(ci + 1).trim();
603
+ }
604
+ resolve({
605
+ status: sm ? parseInt(sm[1], 10) : 0,
606
+ headers: hdrs,
607
+ body: respBody,
608
+ });
609
+ };
610
+ tlsSock.on("end", fin);
611
+ tlsSock.on("close", fin);
612
+ tlsSock.on("error", (e) => {
613
+ clearTimeout(timer);
614
+ reject(e);
615
+ });
616
+ });
617
+ tlsSock.on("error", (e) => {
618
+ clearTimeout(timer);
619
+ reject(e);
620
+ });
621
+ });
622
+ connectReq.on("error", (e) => {
623
+ clearTimeout(timer);
624
+ reject(e);
625
+ });
626
+ connectReq.end();
627
+ }
628
+ else {
629
+ // HTTP (non-TLS)
630
+ const req = http.request({
631
+ hostname: this.proxyHost,
632
+ port: this.proxyPort,
633
+ method: methodUpper,
634
+ path: url,
635
+ headers: {
636
+ "Proxy-Authorization": proxyAuth,
637
+ Host: parsed.host ?? "",
638
+ },
639
+ }, (res) => {
640
+ const chunks = [];
641
+ let bytes = 0;
642
+ res.on("data", (c) => {
643
+ bytes += c.length;
644
+ if (bytes <= MAX_PROXY_RESPONSE_BYTES)
645
+ chunks.push(c);
646
+ });
647
+ let done = false;
648
+ const fin = () => {
649
+ if (done)
650
+ return;
651
+ done = true;
652
+ clearTimeout(timer);
653
+ const respBody = Buffer.concat(chunks).toString("utf-8").substring(0, MAX_PROXY_RESPONSE_BYTES);
654
+ const hdrs = {};
655
+ for (const [k, v] of Object.entries(res.headers)) {
656
+ if (v)
657
+ hdrs[k] = Array.isArray(v) ? v.join(", ") : v;
658
+ }
659
+ resolve({ status: res.statusCode ?? 0, headers: hdrs, body: respBody });
660
+ };
661
+ res.on("end", fin);
662
+ res.on("close", fin);
663
+ res.on("error", (e) => {
664
+ clearTimeout(timer);
665
+ reject(e);
666
+ });
667
+ });
668
+ req.on("error", (e) => {
669
+ clearTimeout(timer);
670
+ reject(e);
671
+ });
672
+ req.end();
673
+ }
674
+ });
675
+ const lines = [
676
+ `HTTP ${result.status}`,
677
+ `Pool: ${proxyType} | Country: ${validCountry ?? "auto"}`,
678
+ "",
679
+ ];
680
+ if (result.headers) {
681
+ const showHeaders = ["content-type", "content-length", "server", "x-cache", "cache-control"];
682
+ for (const h of showHeaders) {
683
+ if (result.headers[h]) {
684
+ lines.push(`${h}: ${result.headers[h]}`);
685
+ }
686
+ }
687
+ lines.push("");
688
+ }
689
+ if (result.body) {
690
+ lines.push(truncate(result.body));
691
+ }
692
+ return scrubCredentials(truncate(lines.join("\n")));
693
+ }
694
+ catch (err) {
695
+ return `Error: ${safeError(err)}`;
696
+ }
697
+ }
698
+ // -----------------------------------------------------------------------
699
+ // Tool 2: checkBalance
700
+ // -----------------------------------------------------------------------
701
+ async checkBalance() {
702
+ try {
703
+ const data = await this._requestWithRetry("GET", "/api/wallet");
704
+ const balanceCents = data.balanceCents ?? 0;
705
+ const balanceUsd = data.balanceUsd ?? balanceCents / 100;
706
+ const dcGbRemaining = (balanceCents / 300).toFixed(2);
707
+ const resGbRemaining = (balanceCents / 500).toFixed(2);
708
+ return [
709
+ `Wallet Balance: $${balanceUsd.toFixed(2)}`,
710
+ "",
711
+ "Estimated remaining bandwidth:",
712
+ ` Datacenter ($3/GB): ~${dcGbRemaining} GB`,
713
+ ` Residential ($5/GB): ~${resGbRemaining} GB`,
714
+ "",
715
+ "Use check_usage to see recent consumption.",
716
+ ].join("\n");
717
+ }
718
+ catch (err) {
719
+ return `Error: ${safeError(err)}`;
720
+ }
721
+ }
722
+ // -----------------------------------------------------------------------
723
+ // Tool 3: checkUsage
724
+ // -----------------------------------------------------------------------
725
+ async checkUsage(days = 30) {
726
+ try {
727
+ const d = Math.min(Math.max(Math.floor(days), 1), 365);
728
+ const since = new Date(Date.now() - d * 24 * 60 * 60 * 1000).toISOString();
729
+ const until = new Date().toISOString();
730
+ const data = await this._requestWithRetry("GET", `/api/usage?since=${encodeURIComponent(since)}&until=${encodeURIComponent(until)}`);
731
+ const s = data.summary;
732
+ return [
733
+ `Usage Summary (last ${d} days):`,
734
+ ` Total Bandwidth: ${formatBytes(s.totalBytes)}`,
735
+ ` Total Cost: $${s.totalCostUsd.toFixed(2)}`,
736
+ ` Total Requests: ${s.requestCount}`,
737
+ "",
738
+ "Use check_balance to see remaining funds.",
739
+ ].join("\n");
740
+ }
741
+ catch (err) {
742
+ return `Error: ${safeError(err)}`;
743
+ }
744
+ }
745
+ // -----------------------------------------------------------------------
746
+ // Tool 4: getProxyConfig
747
+ // -----------------------------------------------------------------------
748
+ async getProxyConfig() {
749
+ try {
750
+ const data = await this._requestWithRetry("GET", "/api/proxy/config");
751
+ const lines = [
752
+ "DomiNode Proxy Configuration",
753
+ "",
754
+ `Proxy Host: ${data.proxyHost ?? "proxy.dominusnode.com"}`,
755
+ `HTTP Proxy Port: ${data.httpProxyPort ?? 8080}`,
756
+ `SOCKS5 Proxy Port: ${data.socks5ProxyPort ?? 1080}`,
757
+ "",
758
+ "Available Pools:",
759
+ ];
760
+ if (data.pools) {
761
+ for (const pool of data.pools) {
762
+ lines.push(` ${pool.name} (${pool.type}): ${formatCents(pool.pricePerGbCents)}/GB`);
763
+ if (pool.countries && pool.countries.length > 0) {
764
+ lines.push(` Countries: ${pool.countries.slice(0, 20).join(", ")}${pool.countries.length > 20 ? "..." : ""}`);
765
+ }
766
+ }
767
+ }
768
+ else {
769
+ lines.push(" Datacenter (dc): $3.00/GB");
770
+ lines.push(" Residential (residential): $5.00/GB");
771
+ }
772
+ lines.push("");
773
+ lines.push("Blocked countries (OFAC): CU, IR, KP, RU, SY");
774
+ return lines.join("\n");
775
+ }
776
+ catch (err) {
777
+ return `Error: ${safeError(err)}`;
778
+ }
779
+ }
780
+ // -----------------------------------------------------------------------
781
+ // Tool 5: listSessions
782
+ // -----------------------------------------------------------------------
783
+ async listSessions() {
784
+ try {
785
+ const data = await this._requestWithRetry("GET", "/api/sessions/active");
786
+ const sessions = data.sessions ?? [];
787
+ if (sessions.length === 0) {
788
+ return "No active proxy sessions.";
789
+ }
790
+ const lines = [`Active Sessions (${sessions.length}):`, ""];
791
+ for (const s of sessions) {
792
+ const target = s.targetHost ? ` | Target: ${s.targetHost}` : "";
793
+ const bw = s.bytesIn !== undefined || s.bytesOut !== undefined
794
+ ? ` | In: ${formatBytes(s.bytesIn ?? 0)} / Out: ${formatBytes(s.bytesOut ?? 0)}`
795
+ : "";
796
+ lines.push(` ${s.id.substring(0, 8)}... | ${s.status} | Since: ${s.startedAt}${target}${bw}`);
797
+ }
798
+ return truncate(lines.join("\n"));
799
+ }
800
+ catch (err) {
801
+ return `Error: ${safeError(err)}`;
802
+ }
803
+ }
804
+ // -----------------------------------------------------------------------
805
+ // Tool 6: createAgenticWallet
806
+ // -----------------------------------------------------------------------
807
+ async createAgenticWallet(label, spendingLimitCents = 10000, dailyLimitCents, allowedDomains) {
808
+ try {
809
+ if (!label || label.trim().length === 0 || label.length > 100) {
810
+ return "Error: label is required and must be 1-100 characters.";
811
+ }
812
+ if (/[\x00-\x1f\x7f]/.test(label)) {
813
+ return "Error: label contains invalid control characters.";
814
+ }
815
+ const limitCents = Math.min(Math.max(Math.floor(spendingLimitCents), 0), 1000000);
816
+ const body = {
817
+ label: label.trim(),
818
+ spendingLimitCents: limitCents,
819
+ };
820
+ if (dailyLimitCents !== undefined && dailyLimitCents !== null) {
821
+ if (!Number.isInteger(dailyLimitCents) || dailyLimitCents < 1 || dailyLimitCents > 1000000) {
822
+ return "Error: daily_limit_cents must be an integer between 1 and 1,000,000.";
823
+ }
824
+ body.dailyLimitCents = dailyLimitCents;
825
+ }
826
+ if (allowedDomains !== undefined && allowedDomains !== null) {
827
+ if (!Array.isArray(allowedDomains) || allowedDomains.length > 100) {
828
+ return "Error: allowed_domains must be an array of at most 100 entries.";
829
+ }
830
+ for (const d of allowedDomains) {
831
+ if (typeof d !== "string" || d.length > 253) {
832
+ return `Error: invalid domain "${d}". Must be a string of at most 253 characters.`;
833
+ }
834
+ }
835
+ body.allowedDomains = allowedDomains;
836
+ }
837
+ const data = await this._requestWithRetry("POST", "/api/agent-wallet", body);
838
+ const lines = [
839
+ "Agentic Wallet Created",
840
+ "",
841
+ `Wallet ID: ${data.id}`,
842
+ `Label: ${data.label}`,
843
+ `Balance: ${formatCents(data.balanceCents)}`,
844
+ `Spending Limit: ${data.spendingLimitCents > 0 ? `${formatCents(data.spendingLimitCents)} per transaction` : "No limit"}`,
845
+ ];
846
+ if (data.dailyLimitCents != null) {
847
+ lines.push(`Daily Limit: ${formatCents(data.dailyLimitCents)}`);
848
+ }
849
+ if (data.allowedDomains && data.allowedDomains.length > 0) {
850
+ lines.push(`Allowed Domains: ${data.allowedDomains.join(", ")}`);
851
+ }
852
+ lines.push(`Status: ${data.status}`, "", "Next: Use fund_agentic_wallet to add funds from your main wallet.");
853
+ return lines.join("\n");
854
+ }
855
+ catch (err) {
856
+ return `Error: ${safeError(err)}`;
857
+ }
858
+ }
859
+ // -----------------------------------------------------------------------
860
+ // Tool 6b: updateWalletPolicy
861
+ // -----------------------------------------------------------------------
862
+ async updateWalletPolicy(walletId, dailyLimitCents, allowedDomains) {
863
+ try {
864
+ const wid = validateUuid(walletId, "wallet_id");
865
+ const body = {};
866
+ if (dailyLimitCents !== undefined) {
867
+ if (dailyLimitCents === null) {
868
+ body.dailyLimitCents = null;
869
+ }
870
+ else {
871
+ if (!Number.isInteger(dailyLimitCents) || dailyLimitCents < 1 || dailyLimitCents > 1000000) {
872
+ return "Error: daily_limit_cents must be an integer between 1 and 1,000,000, or null to remove.";
873
+ }
874
+ body.dailyLimitCents = dailyLimitCents;
875
+ }
876
+ }
877
+ if (allowedDomains !== undefined) {
878
+ if (allowedDomains === null) {
879
+ body.allowedDomains = null;
880
+ }
881
+ else {
882
+ if (!Array.isArray(allowedDomains) || allowedDomains.length > 100) {
883
+ return "Error: allowed_domains must be an array of at most 100 entries, or null to remove.";
884
+ }
885
+ for (const d of allowedDomains) {
886
+ if (typeof d !== "string" || d.length > 253) {
887
+ return `Error: invalid domain "${d}". Must be a string of at most 253 characters.`;
888
+ }
889
+ }
890
+ body.allowedDomains = allowedDomains;
891
+ }
892
+ }
893
+ if (Object.keys(body).length === 0) {
894
+ return "Error: at least one of daily_limit_cents or allowed_domains must be provided.";
895
+ }
896
+ const data = await this._requestWithRetry("PATCH", `/api/agent-wallet/${encodeURIComponent(wid)}/policy`, body);
897
+ const lines = [
898
+ "Wallet Policy Updated",
899
+ "",
900
+ `Wallet ID: ${data.id}`,
901
+ `Label: ${data.label}`,
902
+ ];
903
+ if (data.dailyLimitCents != null) {
904
+ lines.push(`Daily Limit: ${formatCents(data.dailyLimitCents)}`);
905
+ }
906
+ else {
907
+ lines.push("Daily Limit: None");
908
+ }
909
+ if (data.allowedDomains && data.allowedDomains.length > 0) {
910
+ lines.push(`Allowed Domains: ${data.allowedDomains.join(", ")}`);
911
+ }
912
+ else {
913
+ lines.push("Allowed Domains: None (unrestricted)");
914
+ }
915
+ lines.push(`Status: ${data.status}`);
916
+ return lines.join("\n");
917
+ }
918
+ catch (err) {
919
+ return `Error: ${safeError(err)}`;
920
+ }
921
+ }
922
+ // -----------------------------------------------------------------------
923
+ // Tool 7: fundAgenticWallet
924
+ // -----------------------------------------------------------------------
925
+ async fundAgenticWallet(walletId, amountCents) {
926
+ try {
927
+ const wid = validateUuid(walletId, "wallet_id");
928
+ if (!Number.isInteger(amountCents) || amountCents < 100 || amountCents > 1000000) {
929
+ return "Error: amount_cents must be an integer between 100 ($1) and 1000000 ($10,000).";
930
+ }
931
+ const data = await this._requestWithRetry("POST", `/api/agent-wallet/${encodeURIComponent(wid)}/fund`, {
932
+ amountCents,
933
+ });
934
+ const tx = data.transaction;
935
+ return [
936
+ "Wallet Funded Successfully",
937
+ "",
938
+ `Transaction ID: ${tx.id}`,
939
+ `Amount: ${formatCents(tx.amountCents)}`,
940
+ `Type: ${tx.type}`,
941
+ "",
942
+ "Funds transferred from main wallet. Use check_agentic_balance to verify.",
943
+ ].join("\n");
944
+ }
945
+ catch (err) {
946
+ return `Error: ${safeError(err)}`;
947
+ }
948
+ }
949
+ // -----------------------------------------------------------------------
950
+ // Tool 8: agenticWalletBalance
951
+ // -----------------------------------------------------------------------
952
+ async agenticWalletBalance(walletId) {
953
+ try {
954
+ const wid = validateUuid(walletId, "wallet_id");
955
+ const data = await this._requestWithRetry("GET", `/api/agent-wallet/${encodeURIComponent(wid)}`);
956
+ return [
957
+ `Agentic Wallet: ${data.label}`,
958
+ "",
959
+ `Wallet ID: ${data.id}`,
960
+ `Balance: ${formatCents(data.balanceCents)}`,
961
+ `Spending Limit: ${data.spendingLimitCents > 0 ? `${formatCents(data.spendingLimitCents)} per tx` : "No limit"}`,
962
+ `Status: ${data.status}`,
963
+ `Created: ${data.createdAt}`,
964
+ ].join("\n");
965
+ }
966
+ catch (err) {
967
+ return `Error: ${safeError(err)}`;
968
+ }
969
+ }
970
+ // -----------------------------------------------------------------------
971
+ // Tool 9: listAgenticWallets
972
+ // -----------------------------------------------------------------------
973
+ async listAgenticWallets() {
974
+ try {
975
+ const data = await this._requestWithRetry("GET", "/api/agent-wallet");
976
+ const wallets = data.wallets ?? [];
977
+ if (wallets.length === 0) {
978
+ return "No agentic wallets found. Use create_agentic_wallet to create one.";
979
+ }
980
+ const lines = [`Agentic Wallets (${wallets.length})`, ""];
981
+ for (const w of wallets) {
982
+ const limit = w.spendingLimitCents > 0 ? `${formatCents(w.spendingLimitCents)}/tx` : "none";
983
+ lines.push(` ${w.label} (${w.id.slice(0, 8)}...)`);
984
+ lines.push(` Balance: ${formatCents(w.balanceCents)} | Limit: ${limit} | Status: ${w.status}`);
985
+ lines.push("");
986
+ }
987
+ return truncate(lines.join("\n"));
988
+ }
989
+ catch (err) {
990
+ return `Error: ${safeError(err)}`;
991
+ }
992
+ }
993
+ // -----------------------------------------------------------------------
994
+ // Tool 10: agenticTransactions
995
+ // -----------------------------------------------------------------------
996
+ async agenticTransactions(walletId, limit = 20) {
997
+ try {
998
+ const wid = validateUuid(walletId, "wallet_id");
999
+ const lim = Math.min(Math.max(Math.floor(limit), 1), 100);
1000
+ const data = await this._requestWithRetry("GET", `/api/agent-wallet/${encodeURIComponent(wid)}/transactions?limit=${lim}`);
1001
+ const txs = data.transactions ?? [];
1002
+ if (txs.length === 0) {
1003
+ return "No transactions found for this wallet.";
1004
+ }
1005
+ const lines = [`Wallet Transactions (${txs.length})`, ""];
1006
+ for (const tx of txs) {
1007
+ const sign = tx.type === "fund" || tx.type === "refund" ? "+" : "-";
1008
+ const session = tx.sessionId ? ` | Session: ${tx.sessionId.slice(0, 8)}` : "";
1009
+ lines.push(` ${sign}${formatCents(tx.amountCents)} [${tx.type}] ${tx.description}`);
1010
+ lines.push(` ${tx.createdAt}${session}`);
1011
+ }
1012
+ return truncate(lines.join("\n"));
1013
+ }
1014
+ catch (err) {
1015
+ return `Error: ${safeError(err)}`;
1016
+ }
1017
+ }
1018
+ // -----------------------------------------------------------------------
1019
+ // Tool 11: freezeAgenticWallet
1020
+ // -----------------------------------------------------------------------
1021
+ async freezeAgenticWallet(walletId) {
1022
+ try {
1023
+ const wid = validateUuid(walletId, "wallet_id");
1024
+ const data = await this._requestWithRetry("POST", `/api/agent-wallet/${encodeURIComponent(wid)}/freeze`);
1025
+ return [
1026
+ "Agentic Wallet Frozen",
1027
+ "",
1028
+ `Wallet ID: ${data.id}`,
1029
+ `Label: ${data.label}`,
1030
+ `Balance: ${formatCents(data.balanceCents)}`,
1031
+ `Status: ${data.status}`,
1032
+ "",
1033
+ "Spending is paused. Use unfreeze_agentic_wallet to re-enable.",
1034
+ ].join("\n");
1035
+ }
1036
+ catch (err) {
1037
+ return `Error: ${safeError(err)}`;
1038
+ }
1039
+ }
1040
+ // -----------------------------------------------------------------------
1041
+ // Tool 12: unfreezeAgenticWallet
1042
+ // -----------------------------------------------------------------------
1043
+ async unfreezeAgenticWallet(walletId) {
1044
+ try {
1045
+ const wid = validateUuid(walletId, "wallet_id");
1046
+ const data = await this._requestWithRetry("POST", `/api/agent-wallet/${encodeURIComponent(wid)}/unfreeze`);
1047
+ return [
1048
+ "Agentic Wallet Unfrozen",
1049
+ "",
1050
+ `Wallet ID: ${data.id}`,
1051
+ `Label: ${data.label}`,
1052
+ `Balance: ${formatCents(data.balanceCents)}`,
1053
+ `Status: ${data.status}`,
1054
+ "",
1055
+ "The wallet is now active and spending is re-enabled.",
1056
+ ].join("\n");
1057
+ }
1058
+ catch (err) {
1059
+ return `Error: ${safeError(err)}`;
1060
+ }
1061
+ }
1062
+ // -----------------------------------------------------------------------
1063
+ // Tool 13: deleteAgenticWallet
1064
+ // -----------------------------------------------------------------------
1065
+ async deleteAgenticWallet(walletId) {
1066
+ try {
1067
+ const wid = validateUuid(walletId, "wallet_id");
1068
+ await this._requestWithRetry("DELETE", `/api/agent-wallet/${encodeURIComponent(wid)}`);
1069
+ return [
1070
+ "Agentic Wallet Deleted",
1071
+ "",
1072
+ `Wallet ID: ${wid}`,
1073
+ "",
1074
+ "Any remaining balance has been refunded to your main wallet.",
1075
+ "Use check_balance to verify the refund.",
1076
+ ].join("\n");
1077
+ }
1078
+ catch (err) {
1079
+ return `Error: ${safeError(err)}`;
1080
+ }
1081
+ }
1082
+ // -----------------------------------------------------------------------
1083
+ // Tool 14: createTeam
1084
+ // -----------------------------------------------------------------------
1085
+ async createTeam(name, maxMembers) {
1086
+ try {
1087
+ if (!name || name.trim().length === 0 || name.length > 100) {
1088
+ return "Error: name is required and must be 1-100 characters.";
1089
+ }
1090
+ if (/[\x00-\x1f\x7f]/.test(name)) {
1091
+ return "Error: team name contains invalid control characters.";
1092
+ }
1093
+ const body = { name: name.trim() };
1094
+ if (maxMembers !== undefined) {
1095
+ if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
1096
+ return "Error: max_members must be an integer between 1 and 100.";
1097
+ }
1098
+ body.maxMembers = maxMembers;
1099
+ }
1100
+ const data = await this._requestWithRetry("POST", "/api/teams", body);
1101
+ return [
1102
+ "Team Created",
1103
+ "",
1104
+ `Team ID: ${data.id}`,
1105
+ `Name: ${data.name}`,
1106
+ `Max Members: ${data.maxMembers ?? "Unlimited"}`,
1107
+ `Balance: ${formatCents(data.balanceCents ?? 0)}`,
1108
+ `Created: ${data.createdAt}`,
1109
+ "",
1110
+ "Next: Use team_fund to add funds, then team_create_key for a shared API key.",
1111
+ ].join("\n");
1112
+ }
1113
+ catch (err) {
1114
+ return `Error: ${safeError(err)}`;
1115
+ }
1116
+ }
1117
+ // -----------------------------------------------------------------------
1118
+ // Tool 15: listTeams
1119
+ // -----------------------------------------------------------------------
1120
+ async listTeams() {
1121
+ try {
1122
+ const data = await this._requestWithRetry("GET", "/api/teams");
1123
+ const teams = data.teams ?? [];
1124
+ if (teams.length === 0) {
1125
+ return "No teams found. Use create_team to create one.";
1126
+ }
1127
+ const lines = [`Teams (${teams.length})`, ""];
1128
+ for (const t of teams) {
1129
+ const memberInfo = t.memberCount !== undefined ? ` | Members: ${t.memberCount}` : "";
1130
+ lines.push(` ${t.name} (${t.id.slice(0, 8)}...)`);
1131
+ lines.push(` Role: ${t.role} | Balance: ${formatCents(t.balanceCents)}${memberInfo}`);
1132
+ lines.push("");
1133
+ }
1134
+ return truncate(lines.join("\n"));
1135
+ }
1136
+ catch (err) {
1137
+ return `Error: ${safeError(err)}`;
1138
+ }
1139
+ }
1140
+ // -----------------------------------------------------------------------
1141
+ // Tool 16: teamDetails
1142
+ // -----------------------------------------------------------------------
1143
+ async teamDetails(teamId) {
1144
+ try {
1145
+ const tid = validateUuid(teamId, "team_id");
1146
+ const data = await this._requestWithRetry("GET", `/api/teams/${encodeURIComponent(tid)}`);
1147
+ const lines = [
1148
+ `Team: ${data.name}`,
1149
+ "",
1150
+ `Team ID: ${data.id}`,
1151
+ `Owner: ${data.ownerId}`,
1152
+ `Status: ${data.status}`,
1153
+ `Your Role: ${data.role}`,
1154
+ `Balance: ${formatCents(data.balanceCents)}`,
1155
+ `Max Members: ${data.maxMembers ?? "Unlimited"}`,
1156
+ `Created: ${data.createdAt}`,
1157
+ ];
1158
+ const members = data.members ?? [];
1159
+ if (members.length > 0) {
1160
+ lines.push("", `Members (${members.length}):`);
1161
+ for (const m of members) {
1162
+ lines.push(` ${m.email} -- ${m.role} (joined ${m.joinedAt})`);
1163
+ }
1164
+ }
1165
+ return truncate(lines.join("\n"));
1166
+ }
1167
+ catch (err) {
1168
+ return `Error: ${safeError(err)}`;
1169
+ }
1170
+ }
1171
+ // -----------------------------------------------------------------------
1172
+ // Tool 17: teamFund
1173
+ // -----------------------------------------------------------------------
1174
+ async teamFund(teamId, amountCents) {
1175
+ try {
1176
+ const tid = validateUuid(teamId, "team_id");
1177
+ if (!Number.isInteger(amountCents) || amountCents < 100 || amountCents > 1000000) {
1178
+ return "Error: amount_cents must be an integer between 100 ($1) and 1000000 ($10,000).";
1179
+ }
1180
+ const data = await this._requestWithRetry("POST", `/api/teams/${encodeURIComponent(tid)}/wallet/fund`, {
1181
+ amountCents,
1182
+ });
1183
+ const tx = data.transaction;
1184
+ return [
1185
+ "Team Funded Successfully",
1186
+ "",
1187
+ `Transaction ID: ${tx.id}`,
1188
+ `Amount: ${formatCents(tx.amountCents)}`,
1189
+ `Type: ${tx.type}`,
1190
+ "",
1191
+ "Funds transferred from your personal wallet.",
1192
+ "Use team_details to check the new team balance.",
1193
+ ].join("\n");
1194
+ }
1195
+ catch (err) {
1196
+ return `Error: ${safeError(err)}`;
1197
+ }
1198
+ }
1199
+ // -----------------------------------------------------------------------
1200
+ // Tool 18: teamCreateKey
1201
+ // -----------------------------------------------------------------------
1202
+ async teamCreateKey(teamId, label) {
1203
+ try {
1204
+ const tid = validateUuid(teamId, "team_id");
1205
+ if (!label || label.trim().length === 0 || label.length > 100) {
1206
+ return "Error: label is required and must be 1-100 characters.";
1207
+ }
1208
+ if (/[\x00-\x1f\x7f]/.test(label)) {
1209
+ return "Error: label contains invalid control characters.";
1210
+ }
1211
+ const data = await this._requestWithRetry("POST", `/api/teams/${encodeURIComponent(tid)}/keys`, {
1212
+ label: label.trim(),
1213
+ });
1214
+ return [
1215
+ "Team API Key Created",
1216
+ "",
1217
+ `Key ID: ${data.id}`,
1218
+ `API Key: ${data.key}`,
1219
+ `Prefix: ${data.prefix}`,
1220
+ `Label: ${data.label}`,
1221
+ `Team ID: ${tid}`,
1222
+ `Created: ${data.createdAt}`,
1223
+ "",
1224
+ "IMPORTANT: Save this API key now -- it will not be shown again.",
1225
+ "Usage is billed against the team wallet.",
1226
+ ].join("\n");
1227
+ }
1228
+ catch (err) {
1229
+ return `Error: ${safeError(err)}`;
1230
+ }
1231
+ }
1232
+ // -----------------------------------------------------------------------
1233
+ // Tool 19: teamUsage
1234
+ // -----------------------------------------------------------------------
1235
+ async teamUsage(teamId, limit = 20) {
1236
+ try {
1237
+ const tid = validateUuid(teamId, "team_id");
1238
+ const lim = Math.min(Math.max(Math.floor(limit), 1), 100);
1239
+ const data = await this._requestWithRetry("GET", `/api/teams/${encodeURIComponent(tid)}/wallet/transactions?limit=${lim}`);
1240
+ const txs = data.transactions ?? [];
1241
+ if (txs.length === 0) {
1242
+ return "No transactions found for this team. Use team_fund to add funds.";
1243
+ }
1244
+ const lines = [`Team Transactions (${txs.length})`, ""];
1245
+ for (const tx of txs) {
1246
+ const sign = tx.type === "fund" || tx.type === "refund" ? "+" : "-";
1247
+ lines.push(` ${sign}${formatCents(tx.amountCents)} [${tx.type}] ${tx.description}`);
1248
+ lines.push(` ${tx.createdAt} | Wallet: ${tx.walletId.slice(0, 8)}...`);
1249
+ }
1250
+ return truncate(lines.join("\n"));
1251
+ }
1252
+ catch (err) {
1253
+ return `Error: ${safeError(err)}`;
1254
+ }
1255
+ }
1256
+ // -----------------------------------------------------------------------
1257
+ // Tool 20: updateTeam
1258
+ // -----------------------------------------------------------------------
1259
+ async updateTeam(teamId, name, maxMembers) {
1260
+ try {
1261
+ const tid = validateUuid(teamId, "team_id");
1262
+ const body = {};
1263
+ if (name !== undefined) {
1264
+ if (!name || name.trim().length === 0 || name.length > 100) {
1265
+ return "Error: name must be 1-100 characters.";
1266
+ }
1267
+ if (/[\x00-\x1f\x7f]/.test(name)) {
1268
+ return "Error: team name contains invalid control characters.";
1269
+ }
1270
+ body.name = name.trim();
1271
+ }
1272
+ if (maxMembers !== undefined) {
1273
+ if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
1274
+ return "Error: max_members must be an integer between 1 and 100.";
1275
+ }
1276
+ body.maxMembers = maxMembers;
1277
+ }
1278
+ if (Object.keys(body).length === 0) {
1279
+ return "Error: At least one of name or max_members must be provided.";
1280
+ }
1281
+ const data = await this._requestWithRetry("PATCH", `/api/teams/${encodeURIComponent(tid)}`, body);
1282
+ return [
1283
+ "Team Updated",
1284
+ "",
1285
+ `Team ID: ${data.id}`,
1286
+ `Name: ${data.name}`,
1287
+ `Max Members: ${data.maxMembers ?? "Unlimited"}`,
1288
+ `Status: ${data.status}`,
1289
+ ].join("\n");
1290
+ }
1291
+ catch (err) {
1292
+ return `Error: ${safeError(err)}`;
1293
+ }
1294
+ }
1295
+ // -----------------------------------------------------------------------
1296
+ // Tool 21: updateTeamMemberRole
1297
+ // -----------------------------------------------------------------------
1298
+ async updateTeamMemberRole(teamId, userId, role) {
1299
+ try {
1300
+ const tid = validateUuid(teamId, "team_id");
1301
+ const uid = validateUuid(userId, "user_id");
1302
+ if (role !== "member" && role !== "admin") {
1303
+ return "Error: role must be 'member' or 'admin'.";
1304
+ }
1305
+ const data = await this._requestWithRetry("PATCH", `/api/teams/${encodeURIComponent(tid)}/members/${encodeURIComponent(uid)}`, {
1306
+ role,
1307
+ });
1308
+ return [
1309
+ "Team Member Role Updated",
1310
+ "",
1311
+ `Team ID: ${data.teamId ?? tid}`,
1312
+ `User ID: ${data.userId ?? uid}`,
1313
+ `New Role: ${data.role ?? role}`,
1314
+ ].join("\n");
1315
+ }
1316
+ catch (err) {
1317
+ return `Error: ${safeError(err)}`;
1318
+ }
1319
+ }
1320
+ // -----------------------------------------------------------------------
1321
+ // Tool 22: topupPaypal
1322
+ // -----------------------------------------------------------------------
1323
+ async topupPaypal(amountCents) {
1324
+ try {
1325
+ if (!Number.isInteger(amountCents) || amountCents < 500 || amountCents > 100000) {
1326
+ return "Error: amount_cents must be an integer between 500 ($5) and 100000 ($1,000).";
1327
+ }
1328
+ const data = await this._requestWithRetry("POST", "/api/wallet/topup/paypal", { amountCents });
1329
+ return [
1330
+ "PayPal Top-Up Order Created",
1331
+ "",
1332
+ `Order ID: ${data.orderId}`,
1333
+ `Amount: ${formatCents(data.amountCents)}`,
1334
+ `Approval URL: ${data.approvalUrl}`,
1335
+ "",
1336
+ "Open the approval URL in a browser to complete payment.",
1337
+ "Once approved, the funds will be credited to your wallet automatically.",
1338
+ ].join("\n");
1339
+ }
1340
+ catch (err) {
1341
+ return `Error: ${safeError(err)}`;
1342
+ }
1343
+ }
1344
+ // -----------------------------------------------------------------------
1345
+ // Tool 23: x402Info
1346
+ // -----------------------------------------------------------------------
1347
+ async x402Info() {
1348
+ try {
1349
+ const data = await this._requestWithRetry("GET", "/api/x402/info");
1350
+ return JSON.stringify(data, null, 2);
1351
+ }
1352
+ catch (err) {
1353
+ return `Error: ${safeError(err)}`;
1354
+ }
1355
+ }
1356
+ // -----------------------------------------------------------------------
1357
+ // getTools() -- returns LangChain DynamicStructuredTool[] for Flowise
1358
+ // -----------------------------------------------------------------------
1359
+ getTools() {
1360
+ return [
1361
+ // 1. proxied_fetch
1362
+ new tools_1.DynamicStructuredTool({
1363
+ name: "dominusnode_proxied_fetch",
1364
+ description: "Fetch a URL through DomiNode's rotating proxy network. Supports geo-targeting " +
1365
+ "by country. Returns status code, headers, and response body (truncated).",
1366
+ schema: zod_1.z.object({
1367
+ url: zod_1.z.string().describe("The target URL to fetch (http or https)"),
1368
+ method: zod_1.z.enum(["GET", "HEAD", "OPTIONS"]).default("GET").describe("HTTP method"),
1369
+ country: zod_1.z.string().optional().describe("ISO 3166-1 alpha-2 country code for geo-targeting (e.g., US, GB, DE)"),
1370
+ proxyType: zod_1.z.enum(["dc", "residential"]).default("dc").describe("Proxy pool: dc ($3/GB) or residential ($5/GB)"),
1371
+ }),
1372
+ func: async ({ url, method, country, proxyType }) => {
1373
+ return this.proxiedFetch(url, method, country, proxyType);
1374
+ },
1375
+ }),
1376
+ // 2. check_balance
1377
+ new tools_1.DynamicStructuredTool({
1378
+ name: "dominusnode_check_balance",
1379
+ description: "Check your DomiNode wallet balance and estimated remaining bandwidth at current pricing.",
1380
+ schema: zod_1.z.object({}),
1381
+ func: async () => {
1382
+ return this.checkBalance();
1383
+ },
1384
+ }),
1385
+ // 3. check_usage
1386
+ new tools_1.DynamicStructuredTool({
1387
+ name: "dominusnode_check_usage",
1388
+ description: "View bandwidth usage statistics for a specified time period. Shows total bytes, cost, and request count.",
1389
+ schema: zod_1.z.object({
1390
+ days: zod_1.z.number().min(1).max(365).default(30).describe("Number of days to look back (1-365)"),
1391
+ }),
1392
+ func: async ({ days }) => {
1393
+ return this.checkUsage(days);
1394
+ },
1395
+ }),
1396
+ // 4. get_proxy_config
1397
+ new tools_1.DynamicStructuredTool({
1398
+ name: "dominusnode_get_proxy_config",
1399
+ description: "Get available proxy pools, pricing, supported countries, and endpoint configuration.",
1400
+ schema: zod_1.z.object({}),
1401
+ func: async () => {
1402
+ return this.getProxyConfig();
1403
+ },
1404
+ }),
1405
+ // 5. list_sessions
1406
+ new tools_1.DynamicStructuredTool({
1407
+ name: "dominusnode_list_sessions",
1408
+ description: "List all active proxy sessions showing target hosts, bandwidth used, and status.",
1409
+ schema: zod_1.z.object({}),
1410
+ func: async () => {
1411
+ return this.listSessions();
1412
+ },
1413
+ }),
1414
+ // 6. create_agentic_wallet
1415
+ new tools_1.DynamicStructuredTool({
1416
+ name: "dominusnode_create_agentic_wallet",
1417
+ description: "Create a server-side custodial agentic wallet for autonomous proxy billing. " +
1418
+ "Set a spending limit per transaction for safety. Optionally set a daily budget cap and domain allowlist.",
1419
+ schema: zod_1.z.object({
1420
+ label: zod_1.z.string().min(1).max(100).describe('Label for this wallet (e.g., "scraper-bot")'),
1421
+ spendingLimitCents: zod_1.z
1422
+ .number()
1423
+ .int()
1424
+ .min(0)
1425
+ .max(1000000)
1426
+ .default(10000)
1427
+ .describe("Max spend per transaction in cents (0 = no limit, default $100)"),
1428
+ dailyLimitCents: zod_1.z
1429
+ .number()
1430
+ .int()
1431
+ .positive()
1432
+ .max(1000000)
1433
+ .optional()
1434
+ .describe("Optional daily budget cap in cents (max 1,000,000 = $10,000)"),
1435
+ allowedDomains: zod_1.z
1436
+ .array(zod_1.z.string().max(253))
1437
+ .max(100)
1438
+ .optional()
1439
+ .describe("Optional list of allowed domains for proxy access (max 100 entries)"),
1440
+ }),
1441
+ func: async ({ label, spendingLimitCents, dailyLimitCents, allowedDomains }) => {
1442
+ return this.createAgenticWallet(label, spendingLimitCents, dailyLimitCents, allowedDomains);
1443
+ },
1444
+ }),
1445
+ // 7. fund_agentic_wallet
1446
+ new tools_1.DynamicStructuredTool({
1447
+ name: "dominusnode_fund_agentic_wallet",
1448
+ description: "Transfer funds from your main wallet to an agentic wallet. Min $1, max $10,000.",
1449
+ schema: zod_1.z.object({
1450
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1451
+ amountCents: zod_1.z.number().int().min(100).max(1000000).describe("Amount in cents to transfer"),
1452
+ }),
1453
+ func: async ({ walletId, amountCents }) => {
1454
+ return this.fundAgenticWallet(walletId, amountCents);
1455
+ },
1456
+ }),
1457
+ // 8. agentic_wallet_balance
1458
+ new tools_1.DynamicStructuredTool({
1459
+ name: "dominusnode_agentic_wallet_balance",
1460
+ description: "Check the balance and details of an agentic wallet.",
1461
+ schema: zod_1.z.object({
1462
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1463
+ }),
1464
+ func: async ({ walletId }) => {
1465
+ return this.agenticWalletBalance(walletId);
1466
+ },
1467
+ }),
1468
+ // 9. list_agentic_wallets
1469
+ new tools_1.DynamicStructuredTool({
1470
+ name: "dominusnode_list_agentic_wallets",
1471
+ description: "List all your agentic wallets with balances and status.",
1472
+ schema: zod_1.z.object({}),
1473
+ func: async () => {
1474
+ return this.listAgenticWallets();
1475
+ },
1476
+ }),
1477
+ // 10. agentic_transactions
1478
+ new tools_1.DynamicStructuredTool({
1479
+ name: "dominusnode_agentic_transactions",
1480
+ description: "Get transaction history for an agentic wallet.",
1481
+ schema: zod_1.z.object({
1482
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1483
+ limit: zod_1.z.number().int().min(1).max(100).default(20).describe("Number of transactions to return"),
1484
+ }),
1485
+ func: async ({ walletId, limit }) => {
1486
+ return this.agenticTransactions(walletId, limit);
1487
+ },
1488
+ }),
1489
+ // 11. freeze_agentic_wallet
1490
+ new tools_1.DynamicStructuredTool({
1491
+ name: "dominusnode_freeze_agentic_wallet",
1492
+ description: "Freeze an agentic wallet to prevent spending. Balance is preserved until unfrozen.",
1493
+ schema: zod_1.z.object({
1494
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1495
+ }),
1496
+ func: async ({ walletId }) => {
1497
+ return this.freezeAgenticWallet(walletId);
1498
+ },
1499
+ }),
1500
+ // 12. unfreeze_agentic_wallet
1501
+ new tools_1.DynamicStructuredTool({
1502
+ name: "dominusnode_unfreeze_agentic_wallet",
1503
+ description: "Unfreeze a previously frozen agentic wallet to re-enable spending.",
1504
+ schema: zod_1.z.object({
1505
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1506
+ }),
1507
+ func: async ({ walletId }) => {
1508
+ return this.unfreezeAgenticWallet(walletId);
1509
+ },
1510
+ }),
1511
+ // 13. delete_agentic_wallet
1512
+ new tools_1.DynamicStructuredTool({
1513
+ name: "dominusnode_delete_agentic_wallet",
1514
+ description: "Delete an agentic wallet. Must be unfrozen first. Remaining balance refunded to main wallet.",
1515
+ schema: zod_1.z.object({
1516
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1517
+ }),
1518
+ func: async ({ walletId }) => {
1519
+ return this.deleteAgenticWallet(walletId);
1520
+ },
1521
+ }),
1522
+ // 13b. update_wallet_policy
1523
+ new tools_1.DynamicStructuredTool({
1524
+ name: "dominusnode_update_wallet_policy",
1525
+ description: "Update spending policy for an agentic wallet. Set or remove daily budget caps and domain restrictions.",
1526
+ schema: zod_1.z.object({
1527
+ walletId: zod_1.z.string().uuid().describe("Agentic wallet ID (UUID)"),
1528
+ dailyLimitCents: zod_1.z
1529
+ .number()
1530
+ .int()
1531
+ .positive()
1532
+ .max(1000000)
1533
+ .nullable()
1534
+ .optional()
1535
+ .describe("Daily budget cap in cents (max 1,000,000). Set null to remove."),
1536
+ allowedDomains: zod_1.z
1537
+ .array(zod_1.z.string().max(253))
1538
+ .max(100)
1539
+ .nullable()
1540
+ .optional()
1541
+ .describe("List of allowed domains (max 100). Set null to remove restriction."),
1542
+ }),
1543
+ func: async ({ walletId, dailyLimitCents, allowedDomains }) => {
1544
+ return this.updateWalletPolicy(walletId, dailyLimitCents, allowedDomains);
1545
+ },
1546
+ }),
1547
+ // 14. create_team
1548
+ new tools_1.DynamicStructuredTool({
1549
+ name: "dominusnode_create_team",
1550
+ description: "Create a new team with shared wallet billing. Teams let multiple users share proxy access.",
1551
+ schema: zod_1.z.object({
1552
+ name: zod_1.z.string().min(1).max(100).describe("Team name (1-100 characters)"),
1553
+ maxMembers: zod_1.z.number().int().min(1).max(100).optional().describe("Maximum team members (1-100, optional)"),
1554
+ }),
1555
+ func: async ({ name, maxMembers }) => {
1556
+ return this.createTeam(name, maxMembers);
1557
+ },
1558
+ }),
1559
+ // 15. list_teams
1560
+ new tools_1.DynamicStructuredTool({
1561
+ name: "dominusnode_list_teams",
1562
+ description: "List all teams you belong to, with your role and team balance.",
1563
+ schema: zod_1.z.object({}),
1564
+ func: async () => {
1565
+ return this.listTeams();
1566
+ },
1567
+ }),
1568
+ // 16. team_details
1569
+ new tools_1.DynamicStructuredTool({
1570
+ name: "dominusnode_team_details",
1571
+ description: "Get detailed info about a team including balance, members, settings, and your role.",
1572
+ schema: zod_1.z.object({
1573
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1574
+ }),
1575
+ func: async ({ teamId }) => {
1576
+ return this.teamDetails(teamId);
1577
+ },
1578
+ }),
1579
+ // 17. team_fund
1580
+ new tools_1.DynamicStructuredTool({
1581
+ name: "dominusnode_team_fund",
1582
+ description: "Transfer funds from your personal wallet to a team wallet. Min $1, max $10,000.",
1583
+ schema: zod_1.z.object({
1584
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1585
+ amountCents: zod_1.z.number().int().min(100).max(1000000).describe("Amount in cents to transfer"),
1586
+ }),
1587
+ func: async ({ teamId, amountCents }) => {
1588
+ return this.teamFund(teamId, amountCents);
1589
+ },
1590
+ }),
1591
+ // 18. team_create_key
1592
+ new tools_1.DynamicStructuredTool({
1593
+ name: "dominusnode_team_create_key",
1594
+ description: "Create a shared API key for a team. Usage is billed against the team wallet. " +
1595
+ "The key is shown only once.",
1596
+ schema: zod_1.z.object({
1597
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1598
+ label: zod_1.z.string().min(1).max(100).describe('Label for the API key (e.g., "production")'),
1599
+ }),
1600
+ func: async ({ teamId, label }) => {
1601
+ return this.teamCreateKey(teamId, label);
1602
+ },
1603
+ }),
1604
+ // 19. team_usage
1605
+ new tools_1.DynamicStructuredTool({
1606
+ name: "dominusnode_team_usage",
1607
+ description: "Get the team wallet transaction history (funding, usage charges, refunds).",
1608
+ schema: zod_1.z.object({
1609
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1610
+ limit: zod_1.z.number().int().min(1).max(100).default(20).describe("Number of transactions to return"),
1611
+ }),
1612
+ func: async ({ teamId, limit }) => {
1613
+ return this.teamUsage(teamId, limit);
1614
+ },
1615
+ }),
1616
+ // 20. update_team
1617
+ new tools_1.DynamicStructuredTool({
1618
+ name: "dominusnode_update_team",
1619
+ description: "Update a team's settings (name and/or max member count). Owners and admins only.",
1620
+ schema: zod_1.z.object({
1621
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1622
+ name: zod_1.z.string().min(1).max(100).optional().describe("New team name"),
1623
+ maxMembers: zod_1.z.number().int().min(1).max(100).optional().describe("New max member count"),
1624
+ }),
1625
+ func: async ({ teamId, name, maxMembers }) => {
1626
+ return this.updateTeam(teamId, name, maxMembers);
1627
+ },
1628
+ }),
1629
+ // 21. update_team_member_role
1630
+ new tools_1.DynamicStructuredTool({
1631
+ name: "dominusnode_update_team_member_role",
1632
+ description: "Update a team member's role (member or admin). Owners and admins only.",
1633
+ schema: zod_1.z.object({
1634
+ teamId: zod_1.z.string().uuid().describe("Team ID (UUID)"),
1635
+ userId: zod_1.z.string().uuid().describe("User ID (UUID) of the member"),
1636
+ role: zod_1.z.enum(["member", "admin"]).describe("New role for the member"),
1637
+ }),
1638
+ func: async ({ teamId, userId, role }) => {
1639
+ return this.updateTeamMemberRole(teamId, userId, role);
1640
+ },
1641
+ }),
1642
+ // 22. topup_paypal
1643
+ new tools_1.DynamicStructuredTool({
1644
+ name: "dominusnode_topup_paypal",
1645
+ description: "Top up your DomiNode wallet via PayPal. Creates a PayPal order and returns an " +
1646
+ "approval URL. Minimum $5, maximum $1,000.",
1647
+ schema: zod_1.z.object({
1648
+ amountCents: zod_1.z.number().int().min(500).max(100000).describe("Amount in cents to top up (min 500 = $5)"),
1649
+ }),
1650
+ func: async ({ amountCents }) => {
1651
+ return this.topupPaypal(amountCents);
1652
+ },
1653
+ }),
1654
+ // 24. x402_info
1655
+ new tools_1.DynamicStructuredTool({
1656
+ name: "dominusnode_x402_info",
1657
+ description: "Get x402 micropayment protocol information including supported facilitators, " +
1658
+ "pricing, and payment options for AI agent micropayments.",
1659
+ schema: zod_1.z.object({}),
1660
+ func: async () => {
1661
+ return this.x402Info();
1662
+ },
1663
+ }),
1664
+ ];
1665
+ }
1666
+ }
1667
+ exports.DominusNodeToolkit = DominusNodeToolkit;
1668
+ //# sourceMappingURL=toolkit.js.map