@dominusnode/ai-tools 1.2.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -17
- package/dist/create-tools.d.ts +13 -3
- package/dist/create-tools.d.ts.map +1 -1
- package/dist/create-tools.js +12 -3
- package/dist/create-tools.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tools.d.ts +21 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +849 -173
- package/dist/tools.js.map +1 -1
- package/package.json +3 -1
package/dist/tools.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tool } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import * as crypto from "node:crypto";
|
|
3
4
|
import dns from "dns/promises";
|
|
4
5
|
import * as http from "node:http";
|
|
5
6
|
import * as tls from "node:tls";
|
|
@@ -73,11 +74,66 @@ function isPrivateIp(hostname) {
|
|
|
73
74
|
return true;
|
|
74
75
|
return false;
|
|
75
76
|
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// SHA-256 Proof-of-Work solver
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
function countLeadingZeroBits(buf) {
|
|
81
|
+
let count = 0;
|
|
82
|
+
for (const byte of buf) {
|
|
83
|
+
if (byte === 0) {
|
|
84
|
+
count += 8;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
let mask = 0x80;
|
|
88
|
+
while (mask && !(byte & mask)) {
|
|
89
|
+
count++;
|
|
90
|
+
mask >>= 1;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
return count;
|
|
95
|
+
}
|
|
96
|
+
async function solvePoW(powBaseUrl) {
|
|
97
|
+
try {
|
|
98
|
+
const resp = await fetch(`${powBaseUrl}/api/auth/pow/challenge`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json" },
|
|
101
|
+
redirect: "error",
|
|
102
|
+
});
|
|
103
|
+
if (!resp.ok)
|
|
104
|
+
return null;
|
|
105
|
+
const text = await resp.text();
|
|
106
|
+
if (text.length > 10_485_760)
|
|
107
|
+
return null;
|
|
108
|
+
const challenge = JSON.parse(text);
|
|
109
|
+
const prefix = challenge.prefix ?? "";
|
|
110
|
+
const difficulty = challenge.difficulty ?? 20;
|
|
111
|
+
const challengeId = challenge.challengeId ?? "";
|
|
112
|
+
if (!prefix || !challengeId)
|
|
113
|
+
return null;
|
|
114
|
+
for (let nonce = 0; nonce < 100_000_000; nonce++) {
|
|
115
|
+
const hash = crypto
|
|
116
|
+
.createHash("sha256")
|
|
117
|
+
.update(prefix + nonce.toString())
|
|
118
|
+
.digest();
|
|
119
|
+
if (countLeadingZeroBits(hash) >= difficulty) {
|
|
120
|
+
return { challengeId, nonce: nonce.toString() };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
76
129
|
/** Validate a URL is safe to fetch through the proxy. */
|
|
77
130
|
function validateUrl(urlStr) {
|
|
78
131
|
// Length limit
|
|
79
132
|
if (urlStr.length > 2048) {
|
|
80
|
-
return {
|
|
133
|
+
return {
|
|
134
|
+
valid: false,
|
|
135
|
+
error: "URL exceeds maximum length of 2048 characters",
|
|
136
|
+
};
|
|
81
137
|
}
|
|
82
138
|
let url;
|
|
83
139
|
try {
|
|
@@ -88,7 +144,10 @@ function validateUrl(urlStr) {
|
|
|
88
144
|
}
|
|
89
145
|
// Protocol check — only http(s)
|
|
90
146
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
91
|
-
return {
|
|
147
|
+
return {
|
|
148
|
+
valid: false,
|
|
149
|
+
error: `Unsupported protocol: ${url.protocol} — only http and https are allowed`,
|
|
150
|
+
};
|
|
92
151
|
}
|
|
93
152
|
// Hostname must be present
|
|
94
153
|
if (!url.hostname) {
|
|
@@ -103,16 +162,27 @@ function validateUrl(urlStr) {
|
|
|
103
162
|
return { valid: false, error: "Requests to localhost are not allowed" };
|
|
104
163
|
}
|
|
105
164
|
// Block internal network TLDs
|
|
106
|
-
if (lowerHost.endsWith(".local") ||
|
|
107
|
-
|
|
165
|
+
if (lowerHost.endsWith(".local") ||
|
|
166
|
+
lowerHost.endsWith(".internal") ||
|
|
167
|
+
lowerHost.endsWith(".arpa")) {
|
|
168
|
+
return {
|
|
169
|
+
valid: false,
|
|
170
|
+
error: "Requests to internal network hostnames are not allowed",
|
|
171
|
+
};
|
|
108
172
|
}
|
|
109
173
|
// Block private/reserved IPs
|
|
110
174
|
if (isPrivateIp(url.hostname)) {
|
|
111
|
-
return {
|
|
175
|
+
return {
|
|
176
|
+
valid: false,
|
|
177
|
+
error: "Requests to private/reserved IP addresses are not allowed",
|
|
178
|
+
};
|
|
112
179
|
}
|
|
113
180
|
// Block credentials in URL (user:pass@host)
|
|
114
181
|
if (url.username || url.password) {
|
|
115
|
-
return {
|
|
182
|
+
return {
|
|
183
|
+
valid: false,
|
|
184
|
+
error: "URLs with embedded credentials are not allowed",
|
|
185
|
+
};
|
|
116
186
|
}
|
|
117
187
|
return { valid: true, url };
|
|
118
188
|
}
|
|
@@ -122,7 +192,8 @@ const MAX_BODY_LENGTH = 4000;
|
|
|
122
192
|
function truncateBody(body) {
|
|
123
193
|
if (body.length <= MAX_BODY_LENGTH)
|
|
124
194
|
return body;
|
|
125
|
-
return body.slice(0, MAX_BODY_LENGTH) +
|
|
195
|
+
return (body.slice(0, MAX_BODY_LENGTH) +
|
|
196
|
+
`\n...[truncated, ${body.length - MAX_BODY_LENGTH} chars omitted]`);
|
|
126
197
|
}
|
|
127
198
|
// ---------------------------------------------------------------------------
|
|
128
199
|
// Safe header subset — never forward sensitive headers to the AI
|
|
@@ -197,7 +268,7 @@ async function checkDnsRebinding(hostname) {
|
|
|
197
268
|
// ---------------------------------------------------------------------------
|
|
198
269
|
// Credential sanitization for error messages
|
|
199
270
|
// ---------------------------------------------------------------------------
|
|
200
|
-
const CREDENTIAL_RE = /dn_(live|test)_[a-zA-Z0-9]+|eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g;
|
|
271
|
+
const CREDENTIAL_RE = /dn_(live|test|proxy)_[a-zA-Z0-9]+|eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g;
|
|
201
272
|
function sanitizeError(message) {
|
|
202
273
|
return message.replace(CREDENTIAL_RE, "***");
|
|
203
274
|
}
|
|
@@ -245,7 +316,11 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
245
316
|
"Supports geo-targeting by country and choice of datacenter (dc) or residential proxy. " +
|
|
246
317
|
"Use this to fetch web pages, APIs, or any HTTP resource through a proxy IP.",
|
|
247
318
|
parameters: z.object({
|
|
248
|
-
url: z
|
|
319
|
+
url: z
|
|
320
|
+
.string()
|
|
321
|
+
.max(2048)
|
|
322
|
+
.url()
|
|
323
|
+
.describe("The URL to fetch through the proxy"),
|
|
249
324
|
method: z
|
|
250
325
|
.enum(["GET", "HEAD", "OPTIONS"])
|
|
251
326
|
.default("GET")
|
|
@@ -264,7 +339,7 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
264
339
|
.optional()
|
|
265
340
|
.describe("Optional HTTP headers to include in the request"),
|
|
266
341
|
}),
|
|
267
|
-
execute: async ({ url, method, country, proxyType, headers }) => {
|
|
342
|
+
execute: async ({ url, method, country, proxyType, headers, }) => {
|
|
268
343
|
// SSRF validation
|
|
269
344
|
const validation = validateUrl(url);
|
|
270
345
|
if (!validation.valid) {
|
|
@@ -272,14 +347,18 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
272
347
|
}
|
|
273
348
|
// OFAC sanctioned country check
|
|
274
349
|
if (country && SANCTIONED_COUNTRIES.has(country.toUpperCase())) {
|
|
275
|
-
return {
|
|
350
|
+
return {
|
|
351
|
+
error: `Country '${country.toUpperCase()}' is blocked (OFAC sanctioned)`,
|
|
352
|
+
};
|
|
276
353
|
}
|
|
277
354
|
// DNS rebinding protection
|
|
278
355
|
try {
|
|
279
356
|
await checkDnsRebinding(validation.url.hostname);
|
|
280
357
|
}
|
|
281
358
|
catch (err) {
|
|
282
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
error: err instanceof Error ? err.message : "DNS validation failed",
|
|
361
|
+
};
|
|
283
362
|
}
|
|
284
363
|
// Build the proxy URL using the SDK
|
|
285
364
|
const proxyUrl = client.proxy.buildUrl(apiKey, {
|
|
@@ -293,12 +372,22 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
293
372
|
const proxyAuth = "Basic " +
|
|
294
373
|
Buffer.from(`${proxyUrlObj.username}:${proxyUrlObj.password}`).toString("base64");
|
|
295
374
|
// Validate custom headers for CRLF injection
|
|
296
|
-
const STRIPPED_HEADERS = new Set([
|
|
375
|
+
const STRIPPED_HEADERS = new Set([
|
|
376
|
+
"host",
|
|
377
|
+
"connection",
|
|
378
|
+
"content-length",
|
|
379
|
+
"transfer-encoding",
|
|
380
|
+
"proxy-authorization",
|
|
381
|
+
"authorization",
|
|
382
|
+
"user-agent",
|
|
383
|
+
]);
|
|
297
384
|
const safeHeaders = {};
|
|
298
385
|
if (headers) {
|
|
299
386
|
for (const [k, v] of Object.entries(headers)) {
|
|
300
387
|
if (/[\r\n\0]/.test(k) || /[\r\n\0]/.test(v)) {
|
|
301
|
-
return {
|
|
388
|
+
return {
|
|
389
|
+
error: `Header "${k.replace(/[\r\n\0]/g, "")}" contains invalid characters`,
|
|
390
|
+
};
|
|
302
391
|
}
|
|
303
392
|
if (!STRIPPED_HEADERS.has(k.toLowerCase())) {
|
|
304
393
|
safeHeaders[k] = v;
|
|
@@ -309,14 +398,23 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
309
398
|
const MAX_RESP = 1_048_576; // 1MB
|
|
310
399
|
const result = await new Promise((resolve, reject) => {
|
|
311
400
|
const timer = setTimeout(() => reject(new Error("Proxy request timed out")), 30_000);
|
|
312
|
-
const customHeaderLines = Object.entries(safeHeaders)
|
|
401
|
+
const customHeaderLines = Object.entries(safeHeaders)
|
|
402
|
+
.map(([k, v]) => `${k}: ${v}\r\n`)
|
|
403
|
+
.join("");
|
|
313
404
|
if (targetUrl.protocol === "https:") {
|
|
314
405
|
// HTTPS: use CONNECT tunnel through proxy
|
|
315
|
-
const connectHost = targetUrl.hostname.includes(":")
|
|
406
|
+
const connectHost = targetUrl.hostname.includes(":")
|
|
407
|
+
? `[${targetUrl.hostname}]`
|
|
408
|
+
: targetUrl.hostname;
|
|
316
409
|
const connectReq = http.request({
|
|
317
|
-
hostname: proxyHost,
|
|
410
|
+
hostname: proxyHost,
|
|
411
|
+
port: proxyPort,
|
|
412
|
+
method: "CONNECT",
|
|
318
413
|
path: `${connectHost}:${targetUrl.port || 443}`,
|
|
319
|
-
headers: {
|
|
414
|
+
headers: {
|
|
415
|
+
"Proxy-Authorization": proxyAuth,
|
|
416
|
+
Host: `${connectHost}:${targetUrl.port || 443}`,
|
|
417
|
+
},
|
|
320
418
|
});
|
|
321
419
|
connectReq.on("connect", (_res, sock) => {
|
|
322
420
|
if (_res.statusCode !== 200) {
|
|
@@ -325,13 +423,21 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
325
423
|
reject(new Error(`CONNECT failed: ${_res.statusCode}`));
|
|
326
424
|
return;
|
|
327
425
|
}
|
|
328
|
-
const tlsSock = tls.connect({
|
|
426
|
+
const tlsSock = tls.connect({
|
|
427
|
+
host: targetUrl.hostname,
|
|
428
|
+
socket: sock,
|
|
429
|
+
servername: targetUrl.hostname,
|
|
430
|
+
minVersion: "TLSv1.2",
|
|
431
|
+
}, () => {
|
|
329
432
|
const reqLine = `${method} ${targetUrl.pathname + targetUrl.search} HTTP/1.1\r\nHost: ${targetUrl.host}\r\nUser-Agent: dominusnode-vercel-ai/1.0.0\r\n${customHeaderLines}Connection: close\r\n\r\n`;
|
|
330
433
|
tlsSock.write(reqLine);
|
|
331
434
|
const chunks = [];
|
|
332
435
|
let bytes = 0;
|
|
333
|
-
tlsSock.on("data", (c) => {
|
|
334
|
-
|
|
436
|
+
tlsSock.on("data", (c) => {
|
|
437
|
+
bytes += c.length;
|
|
438
|
+
if (bytes <= MAX_RESP + 16384)
|
|
439
|
+
chunks.push(c);
|
|
440
|
+
});
|
|
335
441
|
let done = false;
|
|
336
442
|
const fin = () => {
|
|
337
443
|
if (done)
|
|
@@ -346,55 +452,96 @@ export function createProxiedFetchTool(client, apiKey) {
|
|
|
346
452
|
}
|
|
347
453
|
const hdr = raw.substring(0, hEnd);
|
|
348
454
|
const body = raw.substring(hEnd + 4).substring(0, MAX_RESP);
|
|
349
|
-
const sm = hdr
|
|
455
|
+
const sm = hdr
|
|
456
|
+
.split("\r\n")[0]
|
|
457
|
+
.match(/^HTTP\/\d\.\d\s+(\d+)\s*(.*)/);
|
|
350
458
|
const hdrs = {};
|
|
351
459
|
for (const l of hdr.split("\r\n").slice(1)) {
|
|
352
460
|
const ci = l.indexOf(":");
|
|
353
461
|
if (ci > 0)
|
|
354
|
-
hdrs[l.substring(0, ci).trim().toLowerCase()] = l
|
|
462
|
+
hdrs[l.substring(0, ci).trim().toLowerCase()] = l
|
|
463
|
+
.substring(ci + 1)
|
|
464
|
+
.trim();
|
|
355
465
|
}
|
|
356
466
|
stripDangerousKeys(hdrs);
|
|
357
|
-
resolve({
|
|
467
|
+
resolve({
|
|
468
|
+
status: sm ? parseInt(sm[1], 10) : 0,
|
|
469
|
+
statusText: sm ? (sm[2] ?? "") : "",
|
|
470
|
+
headers: hdrs,
|
|
471
|
+
body,
|
|
472
|
+
});
|
|
358
473
|
};
|
|
359
474
|
tlsSock.on("end", fin);
|
|
360
475
|
tlsSock.on("close", fin);
|
|
361
|
-
tlsSock.on("error", (e) => {
|
|
476
|
+
tlsSock.on("error", (e) => {
|
|
477
|
+
clearTimeout(timer);
|
|
478
|
+
reject(e);
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
tlsSock.on("error", (e) => {
|
|
482
|
+
clearTimeout(timer);
|
|
483
|
+
reject(e);
|
|
362
484
|
});
|
|
363
|
-
tlsSock.on("error", (e) => { clearTimeout(timer); reject(e); });
|
|
364
485
|
});
|
|
365
|
-
connectReq.on("error", (e) => {
|
|
486
|
+
connectReq.on("error", (e) => {
|
|
487
|
+
clearTimeout(timer);
|
|
488
|
+
reject(e);
|
|
489
|
+
});
|
|
366
490
|
connectReq.end();
|
|
367
491
|
}
|
|
368
492
|
else {
|
|
369
493
|
// HTTP: route through proxy with full URL as request path
|
|
370
494
|
const req = http.request({
|
|
371
|
-
hostname: proxyHost,
|
|
372
|
-
|
|
495
|
+
hostname: proxyHost,
|
|
496
|
+
port: proxyPort,
|
|
497
|
+
method,
|
|
498
|
+
path: targetUrl.toString(),
|
|
499
|
+
headers: {
|
|
500
|
+
"Proxy-Authorization": proxyAuth,
|
|
501
|
+
Host: targetUrl.host ?? "",
|
|
502
|
+
...safeHeaders,
|
|
503
|
+
},
|
|
373
504
|
}, (res) => {
|
|
374
505
|
const chunks = [];
|
|
375
506
|
let bytes = 0;
|
|
376
|
-
res.on("data", (c) => {
|
|
377
|
-
|
|
507
|
+
res.on("data", (c) => {
|
|
508
|
+
bytes += c.length;
|
|
509
|
+
if (bytes <= MAX_RESP)
|
|
510
|
+
chunks.push(c);
|
|
511
|
+
});
|
|
378
512
|
let done = false;
|
|
379
513
|
const fin = () => {
|
|
380
514
|
if (done)
|
|
381
515
|
return;
|
|
382
516
|
done = true;
|
|
383
517
|
clearTimeout(timer);
|
|
384
|
-
const body = Buffer.concat(chunks)
|
|
518
|
+
const body = Buffer.concat(chunks)
|
|
519
|
+
.toString("utf-8")
|
|
520
|
+
.substring(0, MAX_RESP);
|
|
385
521
|
const hdrs = {};
|
|
386
522
|
for (const [k, v] of Object.entries(res.headers)) {
|
|
387
523
|
if (v)
|
|
388
524
|
hdrs[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
389
525
|
}
|
|
390
526
|
stripDangerousKeys(hdrs);
|
|
391
|
-
resolve({
|
|
527
|
+
resolve({
|
|
528
|
+
status: res.statusCode ?? 0,
|
|
529
|
+
statusText: res.statusMessage ?? "",
|
|
530
|
+
headers: hdrs,
|
|
531
|
+
body,
|
|
532
|
+
});
|
|
392
533
|
};
|
|
393
534
|
res.on("end", fin);
|
|
394
535
|
res.on("close", fin);
|
|
395
|
-
res.on("error", (e) => {
|
|
536
|
+
res.on("error", (e) => {
|
|
537
|
+
clearTimeout(timer);
|
|
538
|
+
reject(e);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
req.on("error", (e) => {
|
|
542
|
+
clearTimeout(timer);
|
|
543
|
+
reject(e);
|
|
396
544
|
});
|
|
397
|
-
req.on("error", (e) => { clearTimeout(timer); reject(e); });
|
|
398
545
|
req.end();
|
|
399
546
|
}
|
|
400
547
|
});
|
|
@@ -535,7 +682,9 @@ export function createGetProxyConfigTool(client) {
|
|
|
535
682
|
}
|
|
536
683
|
catch (err) {
|
|
537
684
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
538
|
-
return {
|
|
685
|
+
return {
|
|
686
|
+
error: `Failed to get proxy config: ${sanitizeError(message)}`,
|
|
687
|
+
};
|
|
539
688
|
}
|
|
540
689
|
},
|
|
541
690
|
});
|
|
@@ -585,7 +734,9 @@ export function createTopupPaypalTool(client) {
|
|
|
585
734
|
}),
|
|
586
735
|
execute: async ({ amount_cents }) => {
|
|
587
736
|
try {
|
|
588
|
-
const result = await client.wallet.topupPaypal({
|
|
737
|
+
const result = await client.wallet.topupPaypal({
|
|
738
|
+
amountCents: amount_cents,
|
|
739
|
+
});
|
|
589
740
|
return {
|
|
590
741
|
orderId: result.orderId,
|
|
591
742
|
approvalUrl: result.approvalUrl,
|
|
@@ -594,7 +745,9 @@ export function createTopupPaypalTool(client) {
|
|
|
594
745
|
}
|
|
595
746
|
catch (err) {
|
|
596
747
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
597
|
-
return {
|
|
748
|
+
return {
|
|
749
|
+
error: `Failed to create PayPal top-up: ${sanitizeError(message)}`,
|
|
750
|
+
};
|
|
598
751
|
}
|
|
599
752
|
},
|
|
600
753
|
});
|
|
@@ -617,7 +770,9 @@ export function createTopupStripeTool(client) {
|
|
|
617
770
|
}),
|
|
618
771
|
execute: async ({ amount_cents }) => {
|
|
619
772
|
try {
|
|
620
|
-
const result = await client.wallet.topupStripe({
|
|
773
|
+
const result = await client.wallet.topupStripe({
|
|
774
|
+
amountCents: amount_cents,
|
|
775
|
+
});
|
|
621
776
|
return {
|
|
622
777
|
sessionId: result.sessionId,
|
|
623
778
|
url: result.url,
|
|
@@ -626,7 +781,9 @@ export function createTopupStripeTool(client) {
|
|
|
626
781
|
}
|
|
627
782
|
catch (err) {
|
|
628
783
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
629
|
-
return {
|
|
784
|
+
return {
|
|
785
|
+
error: `Failed to create Stripe checkout: ${sanitizeError(message)}`,
|
|
786
|
+
};
|
|
630
787
|
}
|
|
631
788
|
},
|
|
632
789
|
});
|
|
@@ -645,12 +802,27 @@ export function createTopupCryptoTool(client) {
|
|
|
645
802
|
.max(1000)
|
|
646
803
|
.describe("Amount in USD to top up (min $5, max $1,000)"),
|
|
647
804
|
currency: z
|
|
648
|
-
.enum([
|
|
805
|
+
.enum([
|
|
806
|
+
"BTC",
|
|
807
|
+
"ETH",
|
|
808
|
+
"LTC",
|
|
809
|
+
"XMR",
|
|
810
|
+
"ZEC",
|
|
811
|
+
"USDC",
|
|
812
|
+
"SOL",
|
|
813
|
+
"USDT",
|
|
814
|
+
"DAI",
|
|
815
|
+
"BNB",
|
|
816
|
+
"LINK",
|
|
817
|
+
])
|
|
649
818
|
.describe("Cryptocurrency to pay with"),
|
|
650
819
|
}),
|
|
651
|
-
execute: async ({ amount_usd, currency }) => {
|
|
820
|
+
execute: async ({ amount_usd, currency, }) => {
|
|
652
821
|
try {
|
|
653
|
-
const result = await client.wallet.topupCrypto({
|
|
822
|
+
const result = await client.wallet.topupCrypto({
|
|
823
|
+
amountUsd: amount_usd,
|
|
824
|
+
currency: currency.toLowerCase(),
|
|
825
|
+
});
|
|
654
826
|
return {
|
|
655
827
|
invoiceId: result.invoiceId,
|
|
656
828
|
invoiceUrl: result.invoiceUrl,
|
|
@@ -660,7 +832,9 @@ export function createTopupCryptoTool(client) {
|
|
|
660
832
|
}
|
|
661
833
|
catch (err) {
|
|
662
834
|
const message = err instanceof Error ? err.message : "Unknown error";
|
|
663
|
-
return {
|
|
835
|
+
return {
|
|
836
|
+
error: `Failed to create crypto invoice: ${sanitizeError(message)}`,
|
|
837
|
+
};
|
|
664
838
|
}
|
|
665
839
|
},
|
|
666
840
|
});
|
|
@@ -698,7 +872,7 @@ async function apiRequest(client, method, path, body, agentSecret) {
|
|
|
698
872
|
const apiKey = client.apiKey || "";
|
|
699
873
|
const url = `${baseUrl}${path}`;
|
|
700
874
|
const headers = {
|
|
701
|
-
|
|
875
|
+
Authorization: `Bearer ${apiKey}`,
|
|
702
876
|
"Content-Type": "application/json",
|
|
703
877
|
};
|
|
704
878
|
if (agentSecret) {
|
|
@@ -733,18 +907,40 @@ export function createCreateAgenticWalletTool(client, agentSecret) {
|
|
|
733
907
|
description: "Create a new agentic sub-wallet with a spending limit. " +
|
|
734
908
|
"Agentic wallets are custodial sub-wallets for AI agents with per-transaction spending caps.",
|
|
735
909
|
parameters: z.object({
|
|
736
|
-
label: z
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
910
|
+
label: z
|
|
911
|
+
.string()
|
|
912
|
+
.min(1)
|
|
913
|
+
.max(100)
|
|
914
|
+
.describe("Human-readable label for the wallet"),
|
|
915
|
+
spending_limit_cents: z
|
|
916
|
+
.number()
|
|
917
|
+
.int()
|
|
918
|
+
.min(1)
|
|
919
|
+
.max(2147483647)
|
|
920
|
+
.describe("Per-transaction spending limit in cents"),
|
|
921
|
+
daily_limit_cents: z
|
|
922
|
+
.number()
|
|
923
|
+
.int()
|
|
924
|
+
.min(1)
|
|
925
|
+
.max(1000000)
|
|
926
|
+
.optional()
|
|
927
|
+
.describe("Optional daily spending limit in cents"),
|
|
928
|
+
allowed_domains: z
|
|
929
|
+
.array(z.string().max(253).regex(DOMAIN_RE))
|
|
930
|
+
.max(100)
|
|
931
|
+
.optional()
|
|
932
|
+
.describe("Optional list of allowed domains"),
|
|
740
933
|
}),
|
|
741
|
-
execute: async ({ label, spending_limit_cents, daily_limit_cents, allowed_domains }) => {
|
|
934
|
+
execute: async ({ label, spending_limit_cents, daily_limit_cents, allowed_domains, }) => {
|
|
742
935
|
// Validate no control chars in label
|
|
743
936
|
if (/[\x00-\x1F\x7F]/.test(label)) {
|
|
744
937
|
return { error: "label contains invalid control characters" };
|
|
745
938
|
}
|
|
746
939
|
try {
|
|
747
|
-
const body = {
|
|
940
|
+
const body = {
|
|
941
|
+
label,
|
|
942
|
+
spendingLimitCents: spending_limit_cents,
|
|
943
|
+
};
|
|
748
944
|
if (daily_limit_cents !== undefined)
|
|
749
945
|
body.dailyLimitCents = daily_limit_cents;
|
|
750
946
|
if (allowed_domains !== undefined)
|
|
@@ -753,7 +949,9 @@ export function createCreateAgenticWalletTool(client, agentSecret) {
|
|
|
753
949
|
return result;
|
|
754
950
|
}
|
|
755
951
|
catch (err) {
|
|
756
|
-
return {
|
|
952
|
+
return {
|
|
953
|
+
error: `Failed to create agentic wallet: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
954
|
+
};
|
|
757
955
|
}
|
|
758
956
|
},
|
|
759
957
|
});
|
|
@@ -765,15 +963,25 @@ export function createFundAgenticWalletTool(client, agentSecret) {
|
|
|
765
963
|
return tool({
|
|
766
964
|
description: "Transfer funds from the main wallet to an agentic sub-wallet.",
|
|
767
965
|
parameters: z.object({
|
|
768
|
-
wallet_id: z
|
|
769
|
-
|
|
966
|
+
wallet_id: z
|
|
967
|
+
.string()
|
|
968
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
969
|
+
.describe("UUID of the agentic wallet"),
|
|
970
|
+
amount_cents: z
|
|
971
|
+
.number()
|
|
972
|
+
.int()
|
|
973
|
+
.min(1)
|
|
974
|
+
.max(2147483647)
|
|
975
|
+
.describe("Amount in cents to transfer"),
|
|
770
976
|
}),
|
|
771
|
-
execute: async ({ wallet_id, amount_cents }) => {
|
|
977
|
+
execute: async ({ wallet_id, amount_cents, }) => {
|
|
772
978
|
try {
|
|
773
979
|
return await apiRequest(client, "POST", `/api/agent-wallet/${encodeURIComponent(wallet_id)}/fund`, { amountCents: amount_cents }, agentSecret);
|
|
774
980
|
}
|
|
775
981
|
catch (err) {
|
|
776
|
-
return {
|
|
982
|
+
return {
|
|
983
|
+
error: `Failed to fund wallet: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
984
|
+
};
|
|
777
985
|
}
|
|
778
986
|
},
|
|
779
987
|
});
|
|
@@ -785,14 +993,19 @@ export function createAgenticWalletBalanceTool(client, agentSecret) {
|
|
|
785
993
|
return tool({
|
|
786
994
|
description: "Check the balance and details of an agentic sub-wallet.",
|
|
787
995
|
parameters: z.object({
|
|
788
|
-
wallet_id: z
|
|
996
|
+
wallet_id: z
|
|
997
|
+
.string()
|
|
998
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
999
|
+
.describe("UUID of the agentic wallet"),
|
|
789
1000
|
}),
|
|
790
1001
|
execute: async ({ wallet_id }) => {
|
|
791
1002
|
try {
|
|
792
1003
|
return await apiRequest(client, "GET", `/api/agent-wallet/${encodeURIComponent(wallet_id)}`, undefined, agentSecret);
|
|
793
1004
|
}
|
|
794
1005
|
catch (err) {
|
|
795
|
-
return {
|
|
1006
|
+
return {
|
|
1007
|
+
error: `Failed to get wallet balance: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1008
|
+
};
|
|
796
1009
|
}
|
|
797
1010
|
},
|
|
798
1011
|
});
|
|
@@ -809,7 +1022,9 @@ export function createListAgenticWalletsTool(client, agentSecret) {
|
|
|
809
1022
|
return await apiRequest(client, "GET", "/api/agent-wallet", undefined, agentSecret);
|
|
810
1023
|
}
|
|
811
1024
|
catch (err) {
|
|
812
|
-
return {
|
|
1025
|
+
return {
|
|
1026
|
+
error: `Failed to list wallets: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1027
|
+
};
|
|
813
1028
|
}
|
|
814
1029
|
},
|
|
815
1030
|
});
|
|
@@ -821,16 +1036,27 @@ export function createAgenticTransactionsTool(client, agentSecret) {
|
|
|
821
1036
|
return tool({
|
|
822
1037
|
description: "List recent transactions for an agentic sub-wallet.",
|
|
823
1038
|
parameters: z.object({
|
|
824
|
-
wallet_id: z
|
|
825
|
-
|
|
1039
|
+
wallet_id: z
|
|
1040
|
+
.string()
|
|
1041
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1042
|
+
.describe("UUID of the agentic wallet"),
|
|
1043
|
+
limit: z
|
|
1044
|
+
.number()
|
|
1045
|
+
.int()
|
|
1046
|
+
.min(1)
|
|
1047
|
+
.max(100)
|
|
1048
|
+
.optional()
|
|
1049
|
+
.describe("Maximum transactions to return (1-100)"),
|
|
826
1050
|
}),
|
|
827
|
-
execute: async ({ wallet_id, limit }) => {
|
|
1051
|
+
execute: async ({ wallet_id, limit, }) => {
|
|
828
1052
|
try {
|
|
829
1053
|
const qs = limit ? `?limit=${limit}` : "";
|
|
830
1054
|
return await apiRequest(client, "GET", `/api/agent-wallet/${encodeURIComponent(wallet_id)}/transactions${qs}`, undefined, agentSecret);
|
|
831
1055
|
}
|
|
832
1056
|
catch (err) {
|
|
833
|
-
return {
|
|
1057
|
+
return {
|
|
1058
|
+
error: `Failed to get transactions: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1059
|
+
};
|
|
834
1060
|
}
|
|
835
1061
|
},
|
|
836
1062
|
});
|
|
@@ -842,14 +1068,19 @@ export function createFreezeAgenticWalletTool(client, agentSecret) {
|
|
|
842
1068
|
return tool({
|
|
843
1069
|
description: "Freeze an agentic sub-wallet to prevent further spending.",
|
|
844
1070
|
parameters: z.object({
|
|
845
|
-
wallet_id: z
|
|
1071
|
+
wallet_id: z
|
|
1072
|
+
.string()
|
|
1073
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1074
|
+
.describe("UUID of the agentic wallet"),
|
|
846
1075
|
}),
|
|
847
1076
|
execute: async ({ wallet_id }) => {
|
|
848
1077
|
try {
|
|
849
1078
|
return await apiRequest(client, "POST", `/api/agent-wallet/${encodeURIComponent(wallet_id)}/freeze`, undefined, agentSecret);
|
|
850
1079
|
}
|
|
851
1080
|
catch (err) {
|
|
852
|
-
return {
|
|
1081
|
+
return {
|
|
1082
|
+
error: `Failed to freeze wallet: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1083
|
+
};
|
|
853
1084
|
}
|
|
854
1085
|
},
|
|
855
1086
|
});
|
|
@@ -861,14 +1092,19 @@ export function createUnfreezeAgenticWalletTool(client, agentSecret) {
|
|
|
861
1092
|
return tool({
|
|
862
1093
|
description: "Unfreeze a previously frozen agentic sub-wallet to re-enable spending.",
|
|
863
1094
|
parameters: z.object({
|
|
864
|
-
wallet_id: z
|
|
1095
|
+
wallet_id: z
|
|
1096
|
+
.string()
|
|
1097
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1098
|
+
.describe("UUID of the agentic wallet"),
|
|
865
1099
|
}),
|
|
866
1100
|
execute: async ({ wallet_id }) => {
|
|
867
1101
|
try {
|
|
868
1102
|
return await apiRequest(client, "POST", `/api/agent-wallet/${encodeURIComponent(wallet_id)}/unfreeze`, undefined, agentSecret);
|
|
869
1103
|
}
|
|
870
1104
|
catch (err) {
|
|
871
|
-
return {
|
|
1105
|
+
return {
|
|
1106
|
+
error: `Failed to unfreeze wallet: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1107
|
+
};
|
|
872
1108
|
}
|
|
873
1109
|
},
|
|
874
1110
|
});
|
|
@@ -881,14 +1117,19 @@ export function createDeleteAgenticWalletTool(client, agentSecret) {
|
|
|
881
1117
|
return tool({
|
|
882
1118
|
description: "Delete an agentic sub-wallet. Must be active (not frozen). Remaining balance returns to main wallet.",
|
|
883
1119
|
parameters: z.object({
|
|
884
|
-
wallet_id: z
|
|
1120
|
+
wallet_id: z
|
|
1121
|
+
.string()
|
|
1122
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1123
|
+
.describe("UUID of the agentic wallet"),
|
|
885
1124
|
}),
|
|
886
1125
|
execute: async ({ wallet_id }) => {
|
|
887
1126
|
try {
|
|
888
1127
|
return await apiRequest(client, "DELETE", `/api/agent-wallet/${encodeURIComponent(wallet_id)}`, undefined, agentSecret);
|
|
889
1128
|
}
|
|
890
1129
|
catch (err) {
|
|
891
|
-
return {
|
|
1130
|
+
return {
|
|
1131
|
+
error: `Failed to delete wallet: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1132
|
+
};
|
|
892
1133
|
}
|
|
893
1134
|
},
|
|
894
1135
|
});
|
|
@@ -900,24 +1141,41 @@ export function createUpdateWalletPolicyTool(client, agentSecret) {
|
|
|
900
1141
|
return tool({
|
|
901
1142
|
description: "Update the policy (daily limit, allowed domains) of an agentic sub-wallet.",
|
|
902
1143
|
parameters: z.object({
|
|
903
|
-
wallet_id: z
|
|
904
|
-
|
|
905
|
-
|
|
1144
|
+
wallet_id: z
|
|
1145
|
+
.string()
|
|
1146
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1147
|
+
.describe("UUID of the agentic wallet"),
|
|
1148
|
+
daily_limit_cents: z
|
|
1149
|
+
.number()
|
|
1150
|
+
.int()
|
|
1151
|
+
.min(1)
|
|
1152
|
+
.max(1000000)
|
|
1153
|
+
.optional()
|
|
1154
|
+
.describe("New daily spending limit in cents"),
|
|
1155
|
+
allowed_domains: z
|
|
1156
|
+
.array(z.string().max(253).regex(DOMAIN_RE))
|
|
1157
|
+
.max(100)
|
|
1158
|
+
.optional()
|
|
1159
|
+
.describe("New allowed domains list"),
|
|
906
1160
|
}),
|
|
907
|
-
execute: async ({ wallet_id, daily_limit_cents, allowed_domains }) => {
|
|
1161
|
+
execute: async ({ wallet_id, daily_limit_cents, allowed_domains, }) => {
|
|
908
1162
|
const body = {};
|
|
909
1163
|
if (daily_limit_cents !== undefined)
|
|
910
1164
|
body.dailyLimitCents = daily_limit_cents;
|
|
911
1165
|
if (allowed_domains !== undefined)
|
|
912
1166
|
body.allowedDomains = allowed_domains;
|
|
913
1167
|
if (Object.keys(body).length === 0) {
|
|
914
|
-
return {
|
|
1168
|
+
return {
|
|
1169
|
+
error: "At least one of daily_limit_cents or allowed_domains must be provided",
|
|
1170
|
+
};
|
|
915
1171
|
}
|
|
916
1172
|
try {
|
|
917
1173
|
return await apiRequest(client, "PATCH", `/api/agent-wallet/${encodeURIComponent(wallet_id)}/policy`, body, agentSecret);
|
|
918
1174
|
}
|
|
919
1175
|
catch (err) {
|
|
920
|
-
return {
|
|
1176
|
+
return {
|
|
1177
|
+
error: `Failed to update policy: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1178
|
+
};
|
|
921
1179
|
}
|
|
922
1180
|
},
|
|
923
1181
|
});
|
|
@@ -934,18 +1192,32 @@ export function createRegisterTool(agentSecret) {
|
|
|
934
1192
|
"After registering, use verifyEmail or rely on MCP agent auto-verification.",
|
|
935
1193
|
parameters: z.object({
|
|
936
1194
|
email: z.string().email().describe("Email address for the new account"),
|
|
937
|
-
password: z
|
|
1195
|
+
password: z
|
|
1196
|
+
.string()
|
|
1197
|
+
.min(8)
|
|
1198
|
+
.max(128)
|
|
1199
|
+
.describe("Password (min 8 characters)"),
|
|
938
1200
|
}),
|
|
939
|
-
execute: async ({ email, password }) => {
|
|
1201
|
+
execute: async ({ email, password, }) => {
|
|
940
1202
|
try {
|
|
941
1203
|
const baseUrl = process.env.DOMINUSNODE_BASE_URL || "https://api.dominusnode.com";
|
|
942
|
-
const headers = {
|
|
1204
|
+
const headers = {
|
|
1205
|
+
"Content-Type": "application/json",
|
|
1206
|
+
};
|
|
943
1207
|
if (agentSecret) {
|
|
944
1208
|
headers["X-DominusNode-Agent"] = "mcp";
|
|
945
1209
|
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
946
1210
|
}
|
|
1211
|
+
// Solve PoW for CAPTCHA-free registration
|
|
1212
|
+
const pow = await solvePoW(baseUrl);
|
|
1213
|
+
const regBody = { email, password };
|
|
1214
|
+
if (pow)
|
|
1215
|
+
regBody.pow = pow;
|
|
947
1216
|
const resp = await fetch(`${baseUrl}/api/auth/register`, {
|
|
948
|
-
method: "POST",
|
|
1217
|
+
method: "POST",
|
|
1218
|
+
headers,
|
|
1219
|
+
body: JSON.stringify(regBody),
|
|
1220
|
+
redirect: "error",
|
|
949
1221
|
});
|
|
950
1222
|
if (!resp.ok) {
|
|
951
1223
|
const text = await resp.text().catch(() => "");
|
|
@@ -953,10 +1225,16 @@ export function createRegisterTool(agentSecret) {
|
|
|
953
1225
|
}
|
|
954
1226
|
const data = JSON.parse(await resp.text());
|
|
955
1227
|
stripDangerousKeys(data);
|
|
956
|
-
return {
|
|
1228
|
+
return {
|
|
1229
|
+
userId: data.user?.id,
|
|
1230
|
+
email: data.user?.email,
|
|
1231
|
+
message: "Account created. Verify email to unlock financial features.",
|
|
1232
|
+
};
|
|
957
1233
|
}
|
|
958
1234
|
catch (err) {
|
|
959
|
-
return {
|
|
1235
|
+
return {
|
|
1236
|
+
error: `Failed to register: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1237
|
+
};
|
|
960
1238
|
}
|
|
961
1239
|
},
|
|
962
1240
|
});
|
|
@@ -971,16 +1249,21 @@ export function createLoginTool(agentSecret) {
|
|
|
971
1249
|
email: z.string().email().describe("Account email address"),
|
|
972
1250
|
password: z.string().min(1).describe("Account password"),
|
|
973
1251
|
}),
|
|
974
|
-
execute: async ({ email, password }) => {
|
|
1252
|
+
execute: async ({ email, password, }) => {
|
|
975
1253
|
try {
|
|
976
1254
|
const baseUrl = process.env.DOMINUSNODE_BASE_URL || "https://api.dominusnode.com";
|
|
977
|
-
const headers = {
|
|
1255
|
+
const headers = {
|
|
1256
|
+
"Content-Type": "application/json",
|
|
1257
|
+
};
|
|
978
1258
|
if (agentSecret) {
|
|
979
1259
|
headers["X-DominusNode-Agent"] = "mcp";
|
|
980
1260
|
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
981
1261
|
}
|
|
982
1262
|
const resp = await fetch(`${baseUrl}/api/auth/login`, {
|
|
983
|
-
method: "POST",
|
|
1263
|
+
method: "POST",
|
|
1264
|
+
headers,
|
|
1265
|
+
body: JSON.stringify({ email, password }),
|
|
1266
|
+
redirect: "error",
|
|
984
1267
|
});
|
|
985
1268
|
if (!resp.ok) {
|
|
986
1269
|
const text = await resp.text().catch(() => "");
|
|
@@ -989,12 +1272,21 @@ export function createLoginTool(agentSecret) {
|
|
|
989
1272
|
const data = JSON.parse(await resp.text());
|
|
990
1273
|
stripDangerousKeys(data);
|
|
991
1274
|
if (data.mfaRequired) {
|
|
992
|
-
return {
|
|
1275
|
+
return {
|
|
1276
|
+
mfaRequired: true,
|
|
1277
|
+
challengeToken: data.challengeToken,
|
|
1278
|
+
message: "MFA verification required",
|
|
1279
|
+
};
|
|
993
1280
|
}
|
|
994
|
-
return {
|
|
1281
|
+
return {
|
|
1282
|
+
accessToken: data.accessToken ? "[REDACTED]" : undefined,
|
|
1283
|
+
message: "Login successful",
|
|
1284
|
+
};
|
|
995
1285
|
}
|
|
996
1286
|
catch (err) {
|
|
997
|
-
return {
|
|
1287
|
+
return {
|
|
1288
|
+
error: `Failed to login: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1289
|
+
};
|
|
998
1290
|
}
|
|
999
1291
|
},
|
|
1000
1292
|
});
|
|
@@ -1020,7 +1312,9 @@ export function createGetAccountInfoTool(client, agentSecret) {
|
|
|
1020
1312
|
};
|
|
1021
1313
|
}
|
|
1022
1314
|
catch (err) {
|
|
1023
|
-
return {
|
|
1315
|
+
return {
|
|
1316
|
+
error: `Failed to get account info: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1317
|
+
};
|
|
1024
1318
|
}
|
|
1025
1319
|
},
|
|
1026
1320
|
});
|
|
@@ -1032,18 +1326,26 @@ export function createVerifyEmailTool(agentSecret) {
|
|
|
1032
1326
|
return tool({
|
|
1033
1327
|
description: "Verify email address using the verification token sent to email. MCP agents are auto-verified.",
|
|
1034
1328
|
parameters: z.object({
|
|
1035
|
-
token: z
|
|
1329
|
+
token: z
|
|
1330
|
+
.string()
|
|
1331
|
+
.min(1)
|
|
1332
|
+
.describe("Email verification token from the verification email"),
|
|
1036
1333
|
}),
|
|
1037
1334
|
execute: async ({ token }) => {
|
|
1038
1335
|
try {
|
|
1039
1336
|
const baseUrl = process.env.DOMINUSNODE_BASE_URL || "https://api.dominusnode.com";
|
|
1040
|
-
const headers = {
|
|
1337
|
+
const headers = {
|
|
1338
|
+
"Content-Type": "application/json",
|
|
1339
|
+
};
|
|
1041
1340
|
if (agentSecret) {
|
|
1042
1341
|
headers["X-DominusNode-Agent"] = "mcp";
|
|
1043
1342
|
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
1044
1343
|
}
|
|
1045
1344
|
const resp = await fetch(`${baseUrl}/api/auth/verify-email`, {
|
|
1046
|
-
method: "POST",
|
|
1345
|
+
method: "POST",
|
|
1346
|
+
headers,
|
|
1347
|
+
body: JSON.stringify({ token }),
|
|
1348
|
+
redirect: "error",
|
|
1047
1349
|
});
|
|
1048
1350
|
if (!resp.ok) {
|
|
1049
1351
|
const text = await resp.text().catch(() => "");
|
|
@@ -1052,7 +1354,9 @@ export function createVerifyEmailTool(agentSecret) {
|
|
|
1052
1354
|
return { success: true, message: "Email verified successfully" };
|
|
1053
1355
|
}
|
|
1054
1356
|
catch (err) {
|
|
1055
|
-
return {
|
|
1357
|
+
return {
|
|
1358
|
+
error: `Failed to verify email: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1359
|
+
};
|
|
1056
1360
|
}
|
|
1057
1361
|
},
|
|
1058
1362
|
});
|
|
@@ -1070,7 +1374,9 @@ export function createResendVerificationTool(client, agentSecret) {
|
|
|
1070
1374
|
return { success: true, message: "Verification email sent" };
|
|
1071
1375
|
}
|
|
1072
1376
|
catch (err) {
|
|
1073
|
-
return {
|
|
1377
|
+
return {
|
|
1378
|
+
error: `Failed to resend verification: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1379
|
+
};
|
|
1074
1380
|
}
|
|
1075
1381
|
},
|
|
1076
1382
|
});
|
|
@@ -1083,9 +1389,13 @@ export function createUpdatePasswordTool(client, agentSecret) {
|
|
|
1083
1389
|
description: "Change the password for the current Dominus Node account.",
|
|
1084
1390
|
parameters: z.object({
|
|
1085
1391
|
current_password: z.string().min(1).describe("Current password"),
|
|
1086
|
-
new_password: z
|
|
1392
|
+
new_password: z
|
|
1393
|
+
.string()
|
|
1394
|
+
.min(8)
|
|
1395
|
+
.max(128)
|
|
1396
|
+
.describe("New password (min 8 characters)"),
|
|
1087
1397
|
}),
|
|
1088
|
-
execute: async ({ current_password, new_password }) => {
|
|
1398
|
+
execute: async ({ current_password, new_password, }) => {
|
|
1089
1399
|
try {
|
|
1090
1400
|
await apiRequest(client, "POST", "/api/auth/change-password", {
|
|
1091
1401
|
currentPassword: current_password,
|
|
@@ -1094,7 +1404,9 @@ export function createUpdatePasswordTool(client, agentSecret) {
|
|
|
1094
1404
|
return { success: true, message: "Password updated" };
|
|
1095
1405
|
}
|
|
1096
1406
|
catch (err) {
|
|
1097
|
-
return {
|
|
1407
|
+
return {
|
|
1408
|
+
error: `Failed to update password: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1409
|
+
};
|
|
1098
1410
|
}
|
|
1099
1411
|
},
|
|
1100
1412
|
});
|
|
@@ -1114,7 +1426,9 @@ export function createListKeysTool(client, agentSecret) {
|
|
|
1114
1426
|
return await apiRequest(client, "GET", "/api/keys", undefined, agentSecret);
|
|
1115
1427
|
}
|
|
1116
1428
|
catch (err) {
|
|
1117
|
-
return {
|
|
1429
|
+
return {
|
|
1430
|
+
error: `Failed to list keys: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1431
|
+
};
|
|
1118
1432
|
}
|
|
1119
1433
|
},
|
|
1120
1434
|
});
|
|
@@ -1127,7 +1441,12 @@ export function createCreateKeyTool(client, agentSecret) {
|
|
|
1127
1441
|
description: "Create a new Dominus Node API key. The full key is shown only once — store it securely. " +
|
|
1128
1442
|
"WARNING: API keys are secret credentials. Never log or share them.",
|
|
1129
1443
|
parameters: z.object({
|
|
1130
|
-
label: z
|
|
1444
|
+
label: z
|
|
1445
|
+
.string()
|
|
1446
|
+
.min(1)
|
|
1447
|
+
.max(100)
|
|
1448
|
+
.optional()
|
|
1449
|
+
.describe("Human-readable label for the key"),
|
|
1131
1450
|
}),
|
|
1132
1451
|
execute: async ({ label }) => {
|
|
1133
1452
|
if (label && /[\x00-\x1F\x7F]/.test(label)) {
|
|
@@ -1140,7 +1459,9 @@ export function createCreateKeyTool(client, agentSecret) {
|
|
|
1140
1459
|
return await apiRequest(client, "POST", "/api/keys", body, agentSecret);
|
|
1141
1460
|
}
|
|
1142
1461
|
catch (err) {
|
|
1143
|
-
return {
|
|
1462
|
+
return {
|
|
1463
|
+
error: `Failed to create key: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1464
|
+
};
|
|
1144
1465
|
}
|
|
1145
1466
|
},
|
|
1146
1467
|
});
|
|
@@ -1152,7 +1473,10 @@ export function createRevokeKeyTool(client, agentSecret) {
|
|
|
1152
1473
|
return tool({
|
|
1153
1474
|
description: "Revoke (permanently delete) a Dominus Node API key. This cannot be undone.",
|
|
1154
1475
|
parameters: z.object({
|
|
1155
|
-
key_id: z
|
|
1476
|
+
key_id: z
|
|
1477
|
+
.string()
|
|
1478
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1479
|
+
.describe("UUID of the API key to revoke"),
|
|
1156
1480
|
}),
|
|
1157
1481
|
execute: async ({ key_id }) => {
|
|
1158
1482
|
try {
|
|
@@ -1160,7 +1484,9 @@ export function createRevokeKeyTool(client, agentSecret) {
|
|
|
1160
1484
|
return { success: true, message: "API key revoked" };
|
|
1161
1485
|
}
|
|
1162
1486
|
catch (err) {
|
|
1163
|
-
return {
|
|
1487
|
+
return {
|
|
1488
|
+
error: `Failed to revoke key: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1489
|
+
};
|
|
1164
1490
|
}
|
|
1165
1491
|
},
|
|
1166
1492
|
});
|
|
@@ -1175,8 +1501,19 @@ export function createGetTransactionsTool(client, agentSecret) {
|
|
|
1175
1501
|
return tool({
|
|
1176
1502
|
description: "Get wallet transaction history showing top-ups, usage charges, and transfers.",
|
|
1177
1503
|
parameters: z.object({
|
|
1178
|
-
limit: z
|
|
1179
|
-
|
|
1504
|
+
limit: z
|
|
1505
|
+
.number()
|
|
1506
|
+
.int()
|
|
1507
|
+
.min(1)
|
|
1508
|
+
.max(100)
|
|
1509
|
+
.optional()
|
|
1510
|
+
.describe("Maximum transactions to return (default 50)"),
|
|
1511
|
+
offset: z
|
|
1512
|
+
.number()
|
|
1513
|
+
.int()
|
|
1514
|
+
.min(0)
|
|
1515
|
+
.optional()
|
|
1516
|
+
.describe("Offset for pagination"),
|
|
1180
1517
|
}),
|
|
1181
1518
|
execute: async ({ limit, offset }) => {
|
|
1182
1519
|
try {
|
|
@@ -1189,7 +1526,9 @@ export function createGetTransactionsTool(client, agentSecret) {
|
|
|
1189
1526
|
return await apiRequest(client, "GET", `/api/wallet/transactions${qs}`, undefined, agentSecret);
|
|
1190
1527
|
}
|
|
1191
1528
|
catch (err) {
|
|
1192
|
-
return {
|
|
1529
|
+
return {
|
|
1530
|
+
error: `Failed to get transactions: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1531
|
+
};
|
|
1193
1532
|
}
|
|
1194
1533
|
},
|
|
1195
1534
|
});
|
|
@@ -1206,7 +1545,9 @@ export function createGetForecastTool(client, agentSecret) {
|
|
|
1206
1545
|
return await apiRequest(client, "GET", "/api/wallet/forecast", undefined, agentSecret);
|
|
1207
1546
|
}
|
|
1208
1547
|
catch (err) {
|
|
1209
|
-
return {
|
|
1548
|
+
return {
|
|
1549
|
+
error: `Failed to get forecast: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1550
|
+
};
|
|
1210
1551
|
}
|
|
1211
1552
|
},
|
|
1212
1553
|
});
|
|
@@ -1218,14 +1559,19 @@ export function createCheckPaymentTool(client, agentSecret) {
|
|
|
1218
1559
|
return tool({
|
|
1219
1560
|
description: "Check the status of a cryptocurrency payment invoice. Use after creating a crypto top-up.",
|
|
1220
1561
|
parameters: z.object({
|
|
1221
|
-
invoice_id: z
|
|
1562
|
+
invoice_id: z
|
|
1563
|
+
.string()
|
|
1564
|
+
.min(1)
|
|
1565
|
+
.describe("The invoice ID from the crypto top-up creation"),
|
|
1222
1566
|
}),
|
|
1223
1567
|
execute: async ({ invoice_id }) => {
|
|
1224
1568
|
try {
|
|
1225
1569
|
return await apiRequest(client, "GET", `/api/wallet/crypto/status/${encodeURIComponent(invoice_id)}`, undefined, agentSecret);
|
|
1226
1570
|
}
|
|
1227
1571
|
catch (err) {
|
|
1228
|
-
return {
|
|
1572
|
+
return {
|
|
1573
|
+
error: `Failed to check payment: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1574
|
+
};
|
|
1229
1575
|
}
|
|
1230
1576
|
},
|
|
1231
1577
|
});
|
|
@@ -1240,8 +1586,14 @@ export function createGetDailyUsageTool(client, agentSecret) {
|
|
|
1240
1586
|
return tool({
|
|
1241
1587
|
description: "Get daily proxy usage breakdown showing bytes, cost, and request count per day.",
|
|
1242
1588
|
parameters: z.object({
|
|
1243
|
-
since: z
|
|
1244
|
-
|
|
1589
|
+
since: z
|
|
1590
|
+
.string()
|
|
1591
|
+
.optional()
|
|
1592
|
+
.describe("Start date (ISO 8601). Defaults to 30 days ago."),
|
|
1593
|
+
until: z
|
|
1594
|
+
.string()
|
|
1595
|
+
.optional()
|
|
1596
|
+
.describe("End date (ISO 8601). Defaults to now."),
|
|
1245
1597
|
}),
|
|
1246
1598
|
execute: async ({ since, until }) => {
|
|
1247
1599
|
try {
|
|
@@ -1254,7 +1606,9 @@ export function createGetDailyUsageTool(client, agentSecret) {
|
|
|
1254
1606
|
return await apiRequest(client, "GET", `/api/usage/daily${qs}`, undefined, agentSecret);
|
|
1255
1607
|
}
|
|
1256
1608
|
catch (err) {
|
|
1257
|
-
return {
|
|
1609
|
+
return {
|
|
1610
|
+
error: `Failed to get daily usage: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1611
|
+
};
|
|
1258
1612
|
}
|
|
1259
1613
|
},
|
|
1260
1614
|
});
|
|
@@ -1266,11 +1620,23 @@ export function createGetTopHostsTool(client, agentSecret) {
|
|
|
1266
1620
|
return tool({
|
|
1267
1621
|
description: "Get the top accessed hosts (domains) through the proxy, ranked by total bytes transferred.",
|
|
1268
1622
|
parameters: z.object({
|
|
1269
|
-
since: z
|
|
1270
|
-
|
|
1271
|
-
|
|
1623
|
+
since: z
|
|
1624
|
+
.string()
|
|
1625
|
+
.optional()
|
|
1626
|
+
.describe("Start date (ISO 8601). Defaults to 30 days ago."),
|
|
1627
|
+
until: z
|
|
1628
|
+
.string()
|
|
1629
|
+
.optional()
|
|
1630
|
+
.describe("End date (ISO 8601). Defaults to now."),
|
|
1631
|
+
limit: z
|
|
1632
|
+
.number()
|
|
1633
|
+
.int()
|
|
1634
|
+
.min(1)
|
|
1635
|
+
.max(100)
|
|
1636
|
+
.optional()
|
|
1637
|
+
.describe("Max hosts to return (default 10)"),
|
|
1272
1638
|
}),
|
|
1273
|
-
execute: async ({ since, until, limit }) => {
|
|
1639
|
+
execute: async ({ since, until, limit, }) => {
|
|
1274
1640
|
try {
|
|
1275
1641
|
const params = new URLSearchParams();
|
|
1276
1642
|
if (since)
|
|
@@ -1283,7 +1649,9 @@ export function createGetTopHostsTool(client, agentSecret) {
|
|
|
1283
1649
|
return await apiRequest(client, "GET", `/api/usage/top-hosts${qs}`, undefined, agentSecret);
|
|
1284
1650
|
}
|
|
1285
1651
|
catch (err) {
|
|
1286
|
-
return {
|
|
1652
|
+
return {
|
|
1653
|
+
error: `Failed to get top hosts: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1654
|
+
};
|
|
1287
1655
|
}
|
|
1288
1656
|
},
|
|
1289
1657
|
});
|
|
@@ -1303,7 +1671,9 @@ export function createGetPlanTool(client, agentSecret) {
|
|
|
1303
1671
|
return await apiRequest(client, "GET", "/api/plans/user/plan", undefined, agentSecret);
|
|
1304
1672
|
}
|
|
1305
1673
|
catch (err) {
|
|
1306
|
-
return {
|
|
1674
|
+
return {
|
|
1675
|
+
error: `Failed to get plan: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1676
|
+
};
|
|
1307
1677
|
}
|
|
1308
1678
|
},
|
|
1309
1679
|
});
|
|
@@ -1320,7 +1690,9 @@ export function createListPlansTool(client, agentSecret) {
|
|
|
1320
1690
|
return await apiRequest(client, "GET", "/api/plans", undefined, agentSecret);
|
|
1321
1691
|
}
|
|
1322
1692
|
catch (err) {
|
|
1323
|
-
return {
|
|
1693
|
+
return {
|
|
1694
|
+
error: `Failed to list plans: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1695
|
+
};
|
|
1324
1696
|
}
|
|
1325
1697
|
},
|
|
1326
1698
|
});
|
|
@@ -1339,7 +1711,9 @@ export function createChangePlanTool(client, agentSecret) {
|
|
|
1339
1711
|
return await apiRequest(client, "PUT", "/api/plans/user/plan", { planId: plan_id }, agentSecret);
|
|
1340
1712
|
}
|
|
1341
1713
|
catch (err) {
|
|
1342
|
-
return {
|
|
1714
|
+
return {
|
|
1715
|
+
error: `Failed to change plan: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1716
|
+
};
|
|
1343
1717
|
}
|
|
1344
1718
|
},
|
|
1345
1719
|
});
|
|
@@ -1359,7 +1733,9 @@ export function createGetProxyStatusTool(client, agentSecret) {
|
|
|
1359
1733
|
return await apiRequest(client, "GET", "/api/proxy/status", undefined, agentSecret);
|
|
1360
1734
|
}
|
|
1361
1735
|
catch (err) {
|
|
1362
|
-
return {
|
|
1736
|
+
return {
|
|
1737
|
+
error: `Failed to get proxy status: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1738
|
+
};
|
|
1363
1739
|
}
|
|
1364
1740
|
},
|
|
1365
1741
|
});
|
|
@@ -1375,9 +1751,15 @@ export function createCreateTeamTool(client, agentSecret) {
|
|
|
1375
1751
|
description: "Create a new Dominus Node team with a shared wallet for collaborative proxy usage.",
|
|
1376
1752
|
parameters: z.object({
|
|
1377
1753
|
name: z.string().min(1).max(100).describe("Team name"),
|
|
1378
|
-
max_members: z
|
|
1754
|
+
max_members: z
|
|
1755
|
+
.number()
|
|
1756
|
+
.int()
|
|
1757
|
+
.min(2)
|
|
1758
|
+
.max(100)
|
|
1759
|
+
.optional()
|
|
1760
|
+
.describe("Maximum team members (default 10)"),
|
|
1379
1761
|
}),
|
|
1380
|
-
execute: async ({ name, max_members }) => {
|
|
1762
|
+
execute: async ({ name, max_members, }) => {
|
|
1381
1763
|
if (/[\x00-\x1F\x7F]/.test(name)) {
|
|
1382
1764
|
return { error: "name contains invalid control characters" };
|
|
1383
1765
|
}
|
|
@@ -1388,7 +1770,9 @@ export function createCreateTeamTool(client, agentSecret) {
|
|
|
1388
1770
|
return await apiRequest(client, "POST", "/api/teams", body, agentSecret);
|
|
1389
1771
|
}
|
|
1390
1772
|
catch (err) {
|
|
1391
|
-
return {
|
|
1773
|
+
return {
|
|
1774
|
+
error: `Failed to create team: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1775
|
+
};
|
|
1392
1776
|
}
|
|
1393
1777
|
},
|
|
1394
1778
|
});
|
|
@@ -1405,7 +1789,9 @@ export function createListTeamsTool(client, agentSecret) {
|
|
|
1405
1789
|
return await apiRequest(client, "GET", "/api/teams", undefined, agentSecret);
|
|
1406
1790
|
}
|
|
1407
1791
|
catch (err) {
|
|
1408
|
-
return {
|
|
1792
|
+
return {
|
|
1793
|
+
error: `Failed to list teams: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1794
|
+
};
|
|
1409
1795
|
}
|
|
1410
1796
|
},
|
|
1411
1797
|
});
|
|
@@ -1417,14 +1803,19 @@ export function createTeamDetailsTool(client, agentSecret) {
|
|
|
1417
1803
|
return tool({
|
|
1418
1804
|
description: "Get detailed information about a team including wallet balance, members count, and settings.",
|
|
1419
1805
|
parameters: z.object({
|
|
1420
|
-
team_id: z
|
|
1806
|
+
team_id: z
|
|
1807
|
+
.string()
|
|
1808
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1809
|
+
.describe("UUID of the team"),
|
|
1421
1810
|
}),
|
|
1422
1811
|
execute: async ({ team_id }) => {
|
|
1423
1812
|
try {
|
|
1424
1813
|
return await apiRequest(client, "GET", `/api/teams/${encodeURIComponent(team_id)}`, undefined, agentSecret);
|
|
1425
1814
|
}
|
|
1426
1815
|
catch (err) {
|
|
1427
|
-
return {
|
|
1816
|
+
return {
|
|
1817
|
+
error: `Failed to get team details: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1818
|
+
};
|
|
1428
1819
|
}
|
|
1429
1820
|
},
|
|
1430
1821
|
});
|
|
@@ -1436,11 +1827,20 @@ export function createUpdateTeamTool(client, agentSecret) {
|
|
|
1436
1827
|
return tool({
|
|
1437
1828
|
description: "Update team settings such as name or max members. Requires owner or admin role.",
|
|
1438
1829
|
parameters: z.object({
|
|
1439
|
-
team_id: z
|
|
1830
|
+
team_id: z
|
|
1831
|
+
.string()
|
|
1832
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1833
|
+
.describe("UUID of the team"),
|
|
1440
1834
|
name: z.string().min(1).max(100).optional().describe("New team name"),
|
|
1441
|
-
max_members: z
|
|
1835
|
+
max_members: z
|
|
1836
|
+
.number()
|
|
1837
|
+
.int()
|
|
1838
|
+
.min(2)
|
|
1839
|
+
.max(100)
|
|
1840
|
+
.optional()
|
|
1841
|
+
.describe("New max members limit"),
|
|
1442
1842
|
}),
|
|
1443
|
-
execute: async ({ team_id, name, max_members }) => {
|
|
1843
|
+
execute: async ({ team_id, name, max_members, }) => {
|
|
1444
1844
|
if (name && /[\x00-\x1F\x7F]/.test(name)) {
|
|
1445
1845
|
return { error: "name contains invalid control characters" };
|
|
1446
1846
|
}
|
|
@@ -1450,13 +1850,17 @@ export function createUpdateTeamTool(client, agentSecret) {
|
|
|
1450
1850
|
if (max_members !== undefined)
|
|
1451
1851
|
body.maxMembers = max_members;
|
|
1452
1852
|
if (Object.keys(body).length === 0) {
|
|
1453
|
-
return {
|
|
1853
|
+
return {
|
|
1854
|
+
error: "At least one field (name or max_members) must be provided",
|
|
1855
|
+
};
|
|
1454
1856
|
}
|
|
1455
1857
|
try {
|
|
1456
1858
|
return await apiRequest(client, "PATCH", `/api/teams/${encodeURIComponent(team_id)}`, body, agentSecret);
|
|
1457
1859
|
}
|
|
1458
1860
|
catch (err) {
|
|
1459
|
-
return {
|
|
1861
|
+
return {
|
|
1862
|
+
error: `Failed to update team: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1863
|
+
};
|
|
1460
1864
|
}
|
|
1461
1865
|
},
|
|
1462
1866
|
});
|
|
@@ -1468,7 +1872,10 @@ export function createTeamDeleteTool(client, agentSecret) {
|
|
|
1468
1872
|
return tool({
|
|
1469
1873
|
description: "Delete a team permanently. Requires owner role. Remaining team wallet balance is NOT refunded.",
|
|
1470
1874
|
parameters: z.object({
|
|
1471
|
-
team_id: z
|
|
1875
|
+
team_id: z
|
|
1876
|
+
.string()
|
|
1877
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1878
|
+
.describe("UUID of the team to delete"),
|
|
1472
1879
|
}),
|
|
1473
1880
|
execute: async ({ team_id }) => {
|
|
1474
1881
|
try {
|
|
@@ -1476,7 +1883,9 @@ export function createTeamDeleteTool(client, agentSecret) {
|
|
|
1476
1883
|
return { success: true, message: "Team deleted" };
|
|
1477
1884
|
}
|
|
1478
1885
|
catch (err) {
|
|
1479
|
-
return {
|
|
1886
|
+
return {
|
|
1887
|
+
error: `Failed to delete team: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1888
|
+
};
|
|
1480
1889
|
}
|
|
1481
1890
|
},
|
|
1482
1891
|
});
|
|
@@ -1488,15 +1897,25 @@ export function createTeamFundTool(client, agentSecret) {
|
|
|
1488
1897
|
return tool({
|
|
1489
1898
|
description: "Fund a team wallet by transferring from your personal wallet. Requires owner or admin role.",
|
|
1490
1899
|
parameters: z.object({
|
|
1491
|
-
team_id: z
|
|
1492
|
-
|
|
1900
|
+
team_id: z
|
|
1901
|
+
.string()
|
|
1902
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1903
|
+
.describe("UUID of the team"),
|
|
1904
|
+
amount_cents: z
|
|
1905
|
+
.number()
|
|
1906
|
+
.int()
|
|
1907
|
+
.min(1)
|
|
1908
|
+
.max(2147483647)
|
|
1909
|
+
.describe("Amount in cents to transfer"),
|
|
1493
1910
|
}),
|
|
1494
|
-
execute: async ({ team_id, amount_cents }) => {
|
|
1911
|
+
execute: async ({ team_id, amount_cents, }) => {
|
|
1495
1912
|
try {
|
|
1496
1913
|
return await apiRequest(client, "POST", `/api/teams/${encodeURIComponent(team_id)}/wallet/fund`, { amountCents: amount_cents }, agentSecret);
|
|
1497
1914
|
}
|
|
1498
1915
|
catch (err) {
|
|
1499
|
-
return {
|
|
1916
|
+
return {
|
|
1917
|
+
error: `Failed to fund team: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1918
|
+
};
|
|
1500
1919
|
}
|
|
1501
1920
|
},
|
|
1502
1921
|
});
|
|
@@ -1508,10 +1927,18 @@ export function createTeamCreateKeyTool(client, agentSecret) {
|
|
|
1508
1927
|
return tool({
|
|
1509
1928
|
description: "Create an API key for a team. Team keys bill to the team wallet. Requires owner or admin role.",
|
|
1510
1929
|
parameters: z.object({
|
|
1511
|
-
team_id: z
|
|
1512
|
-
|
|
1930
|
+
team_id: z
|
|
1931
|
+
.string()
|
|
1932
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1933
|
+
.describe("UUID of the team"),
|
|
1934
|
+
label: z
|
|
1935
|
+
.string()
|
|
1936
|
+
.min(1)
|
|
1937
|
+
.max(100)
|
|
1938
|
+
.optional()
|
|
1939
|
+
.describe("Label for the team API key"),
|
|
1513
1940
|
}),
|
|
1514
|
-
execute: async ({ team_id, label }) => {
|
|
1941
|
+
execute: async ({ team_id, label, }) => {
|
|
1515
1942
|
if (label && /[\x00-\x1F\x7F]/.test(label)) {
|
|
1516
1943
|
return { error: "label contains invalid control characters" };
|
|
1517
1944
|
}
|
|
@@ -1522,7 +1949,9 @@ export function createTeamCreateKeyTool(client, agentSecret) {
|
|
|
1522
1949
|
return await apiRequest(client, "POST", `/api/teams/${encodeURIComponent(team_id)}/keys`, body, agentSecret);
|
|
1523
1950
|
}
|
|
1524
1951
|
catch (err) {
|
|
1525
|
-
return {
|
|
1952
|
+
return {
|
|
1953
|
+
error: `Failed to create team key: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1954
|
+
};
|
|
1526
1955
|
}
|
|
1527
1956
|
},
|
|
1528
1957
|
});
|
|
@@ -1534,16 +1963,24 @@ export function createTeamRevokeKeyTool(client, agentSecret) {
|
|
|
1534
1963
|
return tool({
|
|
1535
1964
|
description: "Revoke (delete) a team API key. Requires owner or admin role.",
|
|
1536
1965
|
parameters: z.object({
|
|
1537
|
-
team_id: z
|
|
1538
|
-
|
|
1966
|
+
team_id: z
|
|
1967
|
+
.string()
|
|
1968
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1969
|
+
.describe("UUID of the team"),
|
|
1970
|
+
key_id: z
|
|
1971
|
+
.string()
|
|
1972
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1973
|
+
.describe("UUID of the team key to revoke"),
|
|
1539
1974
|
}),
|
|
1540
|
-
execute: async ({ team_id, key_id }) => {
|
|
1975
|
+
execute: async ({ team_id, key_id, }) => {
|
|
1541
1976
|
try {
|
|
1542
1977
|
await apiRequest(client, "DELETE", `/api/teams/${encodeURIComponent(team_id)}/keys/${encodeURIComponent(key_id)}`, undefined, agentSecret);
|
|
1543
1978
|
return { success: true, message: "Team key revoked" };
|
|
1544
1979
|
}
|
|
1545
1980
|
catch (err) {
|
|
1546
|
-
return {
|
|
1981
|
+
return {
|
|
1982
|
+
error: `Failed to revoke team key: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
1983
|
+
};
|
|
1547
1984
|
}
|
|
1548
1985
|
},
|
|
1549
1986
|
});
|
|
@@ -1555,14 +1992,19 @@ export function createTeamListKeysTool(client, agentSecret) {
|
|
|
1555
1992
|
return tool({
|
|
1556
1993
|
description: "List all API keys for a team. Shows key ID, label, prefix, and creation date.",
|
|
1557
1994
|
parameters: z.object({
|
|
1558
|
-
team_id: z
|
|
1995
|
+
team_id: z
|
|
1996
|
+
.string()
|
|
1997
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
1998
|
+
.describe("UUID of the team"),
|
|
1559
1999
|
}),
|
|
1560
2000
|
execute: async ({ team_id }) => {
|
|
1561
2001
|
try {
|
|
1562
2002
|
return await apiRequest(client, "GET", `/api/teams/${encodeURIComponent(team_id)}/keys`, undefined, agentSecret);
|
|
1563
2003
|
}
|
|
1564
2004
|
catch (err) {
|
|
1565
|
-
return {
|
|
2005
|
+
return {
|
|
2006
|
+
error: `Failed to list team keys: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2007
|
+
};
|
|
1566
2008
|
}
|
|
1567
2009
|
},
|
|
1568
2010
|
});
|
|
@@ -1574,14 +2016,19 @@ export function createTeamUsageTool(client, agentSecret) {
|
|
|
1574
2016
|
return tool({
|
|
1575
2017
|
description: "Get proxy usage statistics for a team including total bytes, cost, and per-member breakdown.",
|
|
1576
2018
|
parameters: z.object({
|
|
1577
|
-
team_id: z
|
|
2019
|
+
team_id: z
|
|
2020
|
+
.string()
|
|
2021
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2022
|
+
.describe("UUID of the team"),
|
|
1578
2023
|
}),
|
|
1579
2024
|
execute: async ({ team_id }) => {
|
|
1580
2025
|
try {
|
|
1581
2026
|
return await apiRequest(client, "GET", `/api/teams/${encodeURIComponent(team_id)}/usage`, undefined, agentSecret);
|
|
1582
2027
|
}
|
|
1583
2028
|
catch (err) {
|
|
1584
|
-
return {
|
|
2029
|
+
return {
|
|
2030
|
+
error: `Failed to get team usage: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2031
|
+
};
|
|
1585
2032
|
}
|
|
1586
2033
|
},
|
|
1587
2034
|
});
|
|
@@ -1593,14 +2040,19 @@ export function createTeamListMembersTool(client, agentSecret) {
|
|
|
1593
2040
|
return tool({
|
|
1594
2041
|
description: "List all members of a team with their roles (owner/admin/member) and join dates.",
|
|
1595
2042
|
parameters: z.object({
|
|
1596
|
-
team_id: z
|
|
2043
|
+
team_id: z
|
|
2044
|
+
.string()
|
|
2045
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2046
|
+
.describe("UUID of the team"),
|
|
1597
2047
|
}),
|
|
1598
2048
|
execute: async ({ team_id }) => {
|
|
1599
2049
|
try {
|
|
1600
2050
|
return await apiRequest(client, "GET", `/api/teams/${encodeURIComponent(team_id)}/members`, undefined, agentSecret);
|
|
1601
2051
|
}
|
|
1602
2052
|
catch (err) {
|
|
1603
|
-
return {
|
|
2053
|
+
return {
|
|
2054
|
+
error: `Failed to list team members: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2055
|
+
};
|
|
1604
2056
|
}
|
|
1605
2057
|
},
|
|
1606
2058
|
});
|
|
@@ -1612,11 +2064,17 @@ export function createTeamAddMemberTool(client, agentSecret) {
|
|
|
1612
2064
|
return tool({
|
|
1613
2065
|
description: "Add a member directly to a team by email. Requires owner or admin role.",
|
|
1614
2066
|
parameters: z.object({
|
|
1615
|
-
team_id: z
|
|
2067
|
+
team_id: z
|
|
2068
|
+
.string()
|
|
2069
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2070
|
+
.describe("UUID of the team"),
|
|
1616
2071
|
email: z.string().email().describe("Email of the user to add"),
|
|
1617
|
-
role: z
|
|
2072
|
+
role: z
|
|
2073
|
+
.enum(["admin", "member"])
|
|
2074
|
+
.optional()
|
|
2075
|
+
.describe("Role to assign (default: member)"),
|
|
1618
2076
|
}),
|
|
1619
|
-
execute: async ({ team_id, email, role }) => {
|
|
2077
|
+
execute: async ({ team_id, email, role, }) => {
|
|
1620
2078
|
try {
|
|
1621
2079
|
const body = { email };
|
|
1622
2080
|
if (role)
|
|
@@ -1624,7 +2082,9 @@ export function createTeamAddMemberTool(client, agentSecret) {
|
|
|
1624
2082
|
return await apiRequest(client, "POST", `/api/teams/${encodeURIComponent(team_id)}/members`, body, agentSecret);
|
|
1625
2083
|
}
|
|
1626
2084
|
catch (err) {
|
|
1627
|
-
return {
|
|
2085
|
+
return {
|
|
2086
|
+
error: `Failed to add member: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2087
|
+
};
|
|
1628
2088
|
}
|
|
1629
2089
|
},
|
|
1630
2090
|
});
|
|
@@ -1636,16 +2096,24 @@ export function createTeamRemoveMemberTool(client, agentSecret) {
|
|
|
1636
2096
|
return tool({
|
|
1637
2097
|
description: "Remove a member from a team. Requires owner or admin role. Cannot remove the owner.",
|
|
1638
2098
|
parameters: z.object({
|
|
1639
|
-
team_id: z
|
|
1640
|
-
|
|
2099
|
+
team_id: z
|
|
2100
|
+
.string()
|
|
2101
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2102
|
+
.describe("UUID of the team"),
|
|
2103
|
+
user_id: z
|
|
2104
|
+
.string()
|
|
2105
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2106
|
+
.describe("UUID of the member to remove"),
|
|
1641
2107
|
}),
|
|
1642
|
-
execute: async ({ team_id, user_id }) => {
|
|
2108
|
+
execute: async ({ team_id, user_id, }) => {
|
|
1643
2109
|
try {
|
|
1644
2110
|
await apiRequest(client, "DELETE", `/api/teams/${encodeURIComponent(team_id)}/members/${encodeURIComponent(user_id)}`, undefined, agentSecret);
|
|
1645
2111
|
return { success: true, message: "Member removed" };
|
|
1646
2112
|
}
|
|
1647
2113
|
catch (err) {
|
|
1648
|
-
return {
|
|
2114
|
+
return {
|
|
2115
|
+
error: `Failed to remove member: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2116
|
+
};
|
|
1649
2117
|
}
|
|
1650
2118
|
},
|
|
1651
2119
|
});
|
|
@@ -1657,16 +2125,24 @@ export function createUpdateTeamMemberRoleTool(client, agentSecret) {
|
|
|
1657
2125
|
return tool({
|
|
1658
2126
|
description: "Update a team member's role (admin or member). Requires owner role.",
|
|
1659
2127
|
parameters: z.object({
|
|
1660
|
-
team_id: z
|
|
1661
|
-
|
|
2128
|
+
team_id: z
|
|
2129
|
+
.string()
|
|
2130
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2131
|
+
.describe("UUID of the team"),
|
|
2132
|
+
user_id: z
|
|
2133
|
+
.string()
|
|
2134
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2135
|
+
.describe("UUID of the member"),
|
|
1662
2136
|
role: z.enum(["admin", "member"]).describe("New role for the member"),
|
|
1663
2137
|
}),
|
|
1664
|
-
execute: async ({ team_id, user_id, role }) => {
|
|
2138
|
+
execute: async ({ team_id, user_id, role, }) => {
|
|
1665
2139
|
try {
|
|
1666
2140
|
return await apiRequest(client, "PATCH", `/api/teams/${encodeURIComponent(team_id)}/members/${encodeURIComponent(user_id)}`, { role }, agentSecret);
|
|
1667
2141
|
}
|
|
1668
2142
|
catch (err) {
|
|
1669
|
-
return {
|
|
2143
|
+
return {
|
|
2144
|
+
error: `Failed to update member role: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2145
|
+
};
|
|
1670
2146
|
}
|
|
1671
2147
|
},
|
|
1672
2148
|
});
|
|
@@ -1678,11 +2154,17 @@ export function createTeamInviteMemberTool(client, agentSecret) {
|
|
|
1678
2154
|
return tool({
|
|
1679
2155
|
description: "Invite a user to join a team via email. They receive an invitation link. Requires owner or admin role.",
|
|
1680
2156
|
parameters: z.object({
|
|
1681
|
-
team_id: z
|
|
2157
|
+
team_id: z
|
|
2158
|
+
.string()
|
|
2159
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2160
|
+
.describe("UUID of the team"),
|
|
1682
2161
|
email: z.string().email().describe("Email address to invite"),
|
|
1683
|
-
role: z
|
|
2162
|
+
role: z
|
|
2163
|
+
.enum(["admin", "member"])
|
|
2164
|
+
.optional()
|
|
2165
|
+
.describe("Role to assign when they accept (default: member)"),
|
|
1684
2166
|
}),
|
|
1685
|
-
execute: async ({ team_id, email, role }) => {
|
|
2167
|
+
execute: async ({ team_id, email, role, }) => {
|
|
1686
2168
|
try {
|
|
1687
2169
|
const body = { email };
|
|
1688
2170
|
if (role)
|
|
@@ -1690,7 +2172,9 @@ export function createTeamInviteMemberTool(client, agentSecret) {
|
|
|
1690
2172
|
return await apiRequest(client, "POST", `/api/teams/${encodeURIComponent(team_id)}/invites`, body, agentSecret);
|
|
1691
2173
|
}
|
|
1692
2174
|
catch (err) {
|
|
1693
|
-
return {
|
|
2175
|
+
return {
|
|
2176
|
+
error: `Failed to invite member: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2177
|
+
};
|
|
1694
2178
|
}
|
|
1695
2179
|
},
|
|
1696
2180
|
});
|
|
@@ -1702,14 +2186,19 @@ export function createTeamListInvitesTool(client, agentSecret) {
|
|
|
1702
2186
|
return tool({
|
|
1703
2187
|
description: "List all pending invitations for a team.",
|
|
1704
2188
|
parameters: z.object({
|
|
1705
|
-
team_id: z
|
|
2189
|
+
team_id: z
|
|
2190
|
+
.string()
|
|
2191
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2192
|
+
.describe("UUID of the team"),
|
|
1706
2193
|
}),
|
|
1707
2194
|
execute: async ({ team_id }) => {
|
|
1708
2195
|
try {
|
|
1709
2196
|
return await apiRequest(client, "GET", `/api/teams/${encodeURIComponent(team_id)}/invites`, undefined, agentSecret);
|
|
1710
2197
|
}
|
|
1711
2198
|
catch (err) {
|
|
1712
|
-
return {
|
|
2199
|
+
return {
|
|
2200
|
+
error: `Failed to list invites: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2201
|
+
};
|
|
1713
2202
|
}
|
|
1714
2203
|
},
|
|
1715
2204
|
});
|
|
@@ -1721,20 +2210,207 @@ export function createTeamCancelInviteTool(client, agentSecret) {
|
|
|
1721
2210
|
return tool({
|
|
1722
2211
|
description: "Cancel a pending team invitation. Requires owner or admin role.",
|
|
1723
2212
|
parameters: z.object({
|
|
1724
|
-
team_id: z
|
|
1725
|
-
|
|
2213
|
+
team_id: z
|
|
2214
|
+
.string()
|
|
2215
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2216
|
+
.describe("UUID of the team"),
|
|
2217
|
+
invite_id: z
|
|
2218
|
+
.string()
|
|
2219
|
+
.regex(UUID_RE, "Must be a valid UUID")
|
|
2220
|
+
.describe("UUID of the invitation to cancel"),
|
|
1726
2221
|
}),
|
|
1727
|
-
execute: async ({ team_id, invite_id }) => {
|
|
2222
|
+
execute: async ({ team_id, invite_id, }) => {
|
|
1728
2223
|
try {
|
|
1729
2224
|
await apiRequest(client, "DELETE", `/api/teams/${encodeURIComponent(team_id)}/invites/${encodeURIComponent(invite_id)}`, undefined, agentSecret);
|
|
1730
2225
|
return { success: true, message: "Invitation cancelled" };
|
|
1731
2226
|
}
|
|
1732
2227
|
catch (err) {
|
|
1733
|
-
return {
|
|
2228
|
+
return {
|
|
2229
|
+
error: `Failed to cancel invite: ${sanitizeError(err instanceof Error ? err.message : "Unknown error")}`,
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
// ---------------------------------------------------------------------------
|
|
2236
|
+
// MPP (Machine Payment Protocol) tools
|
|
2237
|
+
// ---------------------------------------------------------------------------
|
|
2238
|
+
/**
|
|
2239
|
+
* Creates a tool to get MPP protocol info (no auth required).
|
|
2240
|
+
*/
|
|
2241
|
+
export function createMppInfoTool(client, agentSecret) {
|
|
2242
|
+
return tool({
|
|
2243
|
+
description: "Get Machine Payment Protocol (MPP) information including enabled status, " +
|
|
2244
|
+
"supported payment methods, pricing, and session limits. No authentication required. " +
|
|
2245
|
+
"MPP enables keyless proxy access -- AI agents can use the proxy WITHOUT any API key " +
|
|
2246
|
+
"by calling mpp_challenge first, then using the returned credential as proxy auth.",
|
|
2247
|
+
parameters: z.object({}),
|
|
2248
|
+
execute: async () => {
|
|
2249
|
+
try {
|
|
2250
|
+
const result = await apiRequest(client, "GET", "/api/mpp/info", undefined, agentSecret);
|
|
2251
|
+
return result;
|
|
2252
|
+
}
|
|
2253
|
+
catch (err) {
|
|
2254
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2255
|
+
return { error: `Failed to get MPP info: ${sanitizeError(message)}` };
|
|
2256
|
+
}
|
|
2257
|
+
},
|
|
2258
|
+
});
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* Creates a tool to get an MPP challenge for keyless proxy access (no auth required).
|
|
2262
|
+
*/
|
|
2263
|
+
export function createMppChallengeTool(client, agentSecret) {
|
|
2264
|
+
return tool({
|
|
2265
|
+
description: "Get an MPP challenge for keyless proxy access. No authentication required. " +
|
|
2266
|
+
"Returns a credential that can be used to access the proxy WITHOUT any API key -- " +
|
|
2267
|
+
"just pay per request via MPP. Specify 'dc' for datacenter ($3/GB) or " +
|
|
2268
|
+
"'residential' ($5/GB) proxy pool.",
|
|
2269
|
+
parameters: z.object({
|
|
2270
|
+
pool_type: z
|
|
2271
|
+
.enum(["dc", "residential"])
|
|
2272
|
+
.describe("Proxy pool type: 'dc' for datacenter ($3/GB) or 'residential' ($5/GB)"),
|
|
2273
|
+
}),
|
|
2274
|
+
execute: async ({ pool_type }) => {
|
|
2275
|
+
try {
|
|
2276
|
+
const baseUrl = client.baseUrl ||
|
|
2277
|
+
process.env.DOMINUSNODE_BASE_URL ||
|
|
2278
|
+
"https://api.dominusnode.com";
|
|
2279
|
+
const headers = {
|
|
2280
|
+
"Content-Type": "application/json",
|
|
2281
|
+
};
|
|
2282
|
+
if (agentSecret) {
|
|
2283
|
+
headers["X-DominusNode-Agent"] = "mcp";
|
|
2284
|
+
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
2285
|
+
}
|
|
2286
|
+
const resp = await fetch(`${baseUrl}/api/mpp/challenge`, {
|
|
2287
|
+
method: "POST",
|
|
2288
|
+
headers,
|
|
2289
|
+
body: JSON.stringify({ poolType: pool_type }),
|
|
2290
|
+
redirect: "error",
|
|
2291
|
+
});
|
|
2292
|
+
if (!resp.ok) {
|
|
2293
|
+
const text = await resp.text().catch(() => "");
|
|
2294
|
+
throw new Error(`MPP challenge failed (${resp.status}): ${text.slice(0, 200)}`);
|
|
2295
|
+
}
|
|
2296
|
+
const text = await resp.text();
|
|
2297
|
+
if (text.length > MAX_RESPONSE_BODY_BYTES) {
|
|
2298
|
+
throw new Error("Response body exceeds size limit");
|
|
2299
|
+
}
|
|
2300
|
+
const data = JSON.parse(text);
|
|
2301
|
+
stripDangerousKeys(data);
|
|
2302
|
+
return data;
|
|
2303
|
+
}
|
|
2304
|
+
catch (err) {
|
|
2305
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2306
|
+
return {
|
|
2307
|
+
error: `Failed to get MPP challenge: ${sanitizeError(message)}`,
|
|
2308
|
+
};
|
|
2309
|
+
}
|
|
2310
|
+
},
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
/**
|
|
2314
|
+
* Creates a tool to top up wallet via MPP.
|
|
2315
|
+
*/
|
|
2316
|
+
export function createPayMppTool(client, agentSecret) {
|
|
2317
|
+
return tool({
|
|
2318
|
+
description: "Top up your Dominus Node wallet via Machine Payment Protocol (MPP). " +
|
|
2319
|
+
"Supports tempo, stripe_spt, and lightning payment methods.",
|
|
2320
|
+
parameters: z.object({
|
|
2321
|
+
amount_cents: z
|
|
2322
|
+
.number()
|
|
2323
|
+
.int()
|
|
2324
|
+
.min(500)
|
|
2325
|
+
.max(100000)
|
|
2326
|
+
.describe("Amount in cents to top up (min 500 = $5, max 100000 = $1,000)"),
|
|
2327
|
+
method: z
|
|
2328
|
+
.enum(["tempo", "stripe_spt", "lightning"])
|
|
2329
|
+
.describe("MPP payment method: tempo, stripe_spt, or lightning"),
|
|
2330
|
+
}),
|
|
2331
|
+
execute: async ({ amount_cents, method, }) => {
|
|
2332
|
+
try {
|
|
2333
|
+
const result = await apiRequest(client, "POST", "/api/mpp/topup", {
|
|
2334
|
+
amountCents: amount_cents,
|
|
2335
|
+
method,
|
|
2336
|
+
}, agentSecret);
|
|
2337
|
+
return result;
|
|
2338
|
+
}
|
|
2339
|
+
catch (err) {
|
|
2340
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2341
|
+
return { error: `Failed to top up via MPP: ${sanitizeError(message)}` };
|
|
2342
|
+
}
|
|
2343
|
+
},
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
/**
|
|
2347
|
+
* Creates a tool to open an MPP pay-as-you-go session.
|
|
2348
|
+
*/
|
|
2349
|
+
export function createMppSessionOpenTool(client, agentSecret) {
|
|
2350
|
+
return tool({
|
|
2351
|
+
description: "Open a pay-as-you-go MPP session. Returns a channelId for metered proxy usage. " +
|
|
2352
|
+
"Deposit is held and refunded on close minus usage.",
|
|
2353
|
+
parameters: z.object({
|
|
2354
|
+
max_deposit_cents: z
|
|
2355
|
+
.number()
|
|
2356
|
+
.int()
|
|
2357
|
+
.min(500)
|
|
2358
|
+
.max(100000)
|
|
2359
|
+
.describe("Maximum deposit in cents for the session (min 500, max 100000)"),
|
|
2360
|
+
method: z
|
|
2361
|
+
.enum(["tempo", "stripe_spt", "lightning"])
|
|
2362
|
+
.describe("MPP payment method: tempo, stripe_spt, or lightning"),
|
|
2363
|
+
pool_type: z
|
|
2364
|
+
.enum(["dc", "residential"])
|
|
2365
|
+
.default("dc")
|
|
2366
|
+
.describe("Proxy pool type: dc ($3/GB) or residential ($5/GB)"),
|
|
2367
|
+
}),
|
|
2368
|
+
execute: async ({ max_deposit_cents, method, pool_type, }) => {
|
|
2369
|
+
try {
|
|
2370
|
+
const result = await apiRequest(client, "POST", "/api/mpp/session/open", {
|
|
2371
|
+
maxDepositCents: max_deposit_cents,
|
|
2372
|
+
method,
|
|
2373
|
+
poolType: pool_type,
|
|
2374
|
+
}, agentSecret);
|
|
2375
|
+
return result;
|
|
2376
|
+
}
|
|
2377
|
+
catch (err) {
|
|
2378
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2379
|
+
return {
|
|
2380
|
+
error: `Failed to open MPP session: ${sanitizeError(message)}`,
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
},
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Creates a tool to close an MPP pay-as-you-go session.
|
|
2388
|
+
*/
|
|
2389
|
+
export function createMppSessionCloseTool(client, agentSecret) {
|
|
2390
|
+
return tool({
|
|
2391
|
+
description: "Close an MPP pay-as-you-go session. Returns the amount spent and refunded.",
|
|
2392
|
+
parameters: z.object({
|
|
2393
|
+
channel_id: z
|
|
2394
|
+
.string()
|
|
2395
|
+
.min(1)
|
|
2396
|
+
.describe("The channelId returned from mpp_session_open"),
|
|
2397
|
+
}),
|
|
2398
|
+
execute: async ({ channel_id }) => {
|
|
2399
|
+
try {
|
|
2400
|
+
const result = await apiRequest(client, "POST", "/api/mpp/session/close", {
|
|
2401
|
+
channelId: channel_id,
|
|
2402
|
+
}, agentSecret);
|
|
2403
|
+
return result;
|
|
2404
|
+
}
|
|
2405
|
+
catch (err) {
|
|
2406
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
2407
|
+
return {
|
|
2408
|
+
error: `Failed to close MPP session: ${sanitizeError(message)}`,
|
|
2409
|
+
};
|
|
1734
2410
|
}
|
|
1735
2411
|
},
|
|
1736
2412
|
});
|
|
1737
2413
|
}
|
|
1738
2414
|
// Re-export validation helpers for testing
|
|
1739
|
-
export { validateUrl, isPrivateIp, truncateBody, filterHeaders, apiRequest, UUID_RE, DOMAIN_RE };
|
|
2415
|
+
export { validateUrl, isPrivateIp, truncateBody, filterHeaders, apiRequest, UUID_RE, DOMAIN_RE, };
|
|
1740
2416
|
//# sourceMappingURL=tools.js.map
|