n8n-nodes-dominusnode 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/README.md +13 -10
  3. package/dist/credentials/DominusNodeApi.credentials.js +1 -1
  4. package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js +132 -9
  5. package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js.map +1 -1
  6. package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js +21 -4
  7. package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js.map +1 -1
  8. package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js +24 -5
  9. package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js.map +1 -1
  10. package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js +21 -8
  11. package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js.map +1 -1
  12. package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js +63 -12
  13. package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js.map +1 -1
  14. package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js.map +1 -1
  15. package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js +320 -40
  16. package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js.map +1 -1
  17. package/dist/shared/auth.js +1 -1
  18. package/dist/shared/auth.js.map +1 -1
  19. package/dist/shared/ssrf.js.map +1 -1
  20. package/package.json +2 -1
  21. package/src/credentials/DominusNodeApi.credentials.ts +0 -48
  22. package/src/index.ts +0 -8
  23. package/src/nodes/DominusNodeAccount/DominusNodeAccount.node.ts +0 -283
  24. package/src/nodes/DominusNodeKeys/DominusNodeKeys.node.ts +0 -192
  25. package/src/nodes/DominusNodePlans/DominusNodePlans.node.ts +0 -154
  26. package/src/nodes/DominusNodeProxy/DominusNodeProxy.node.ts +0 -470
  27. package/src/nodes/DominusNodeTeams/DominusNodeTeams.node.ts +0 -351
  28. package/src/nodes/DominusNodeUsage/DominusNodeUsage.node.ts +0 -183
  29. package/src/nodes/DominusNodeWallet/DominusNodeWallet.node.ts +0 -950
  30. package/src/shared/auth.ts +0 -287
  31. package/src/shared/constants.ts +0 -11
  32. package/src/shared/ssrf.ts +0 -257
  33. package/tests/DominusNodeProxy.test.ts +0 -281
  34. package/tests/DominusNodeUsage.test.ts +0 -250
  35. package/tests/DominusNodeWallet.test.ts +0 -591
  36. package/tests/ssrf.test.ts +0 -238
  37. package/tsconfig.json +0 -18
  38. package/vitest.config.ts +0 -8
@@ -1,950 +0,0 @@
1
- /**
2
- * Dominus Node Wallet n8n community node.
3
- *
4
- * Operations (25 tools):
5
- * - Check Balance: Get current wallet balance
6
- * - Top Up (Stripe): Create a Stripe checkout session
7
- * - Top Up (Crypto): Create a crypto payment invoice
8
- * - Top Up (PayPal): Create a PayPal checkout session via Stripe
9
- * - Get Transactions: Get wallet transaction history
10
- * - Get Forecast: Get wallet spend forecast
11
- * - Check Payment: Check crypto payment invoice status
12
- * - Create Agentic Wallet: Create a sub-wallet with spending limits
13
- * - Fund Agentic Wallet: Transfer funds to an agentic wallet
14
- * - Get Agentic Wallet Balance: Check an agentic wallet's balance
15
- * - List Agentic Wallets: List all agentic wallets
16
- * - Get Agentic Transactions: Get transaction history for an agentic wallet
17
- * - Freeze Agentic Wallet: Freeze an agentic wallet
18
- * - Unfreeze Agentic Wallet: Unfreeze an agentic wallet
19
- * - Delete Agentic Wallet: Delete an agentic wallet
20
- * - Update Wallet Policy: Update agentic wallet spending policy
21
- * - Create Team: Create a new team
22
- * - List Teams: List all teams
23
- * - Team Details: Get team details
24
- * - Fund Team: Fund a team wallet
25
- * - Create Team Key: Create an API key for a team
26
- * - Team Usage: Get team wallet transaction history
27
- * - Update Team: Update team name/max members
28
- * - Update Team Member Role: Update a team member's role
29
- * - x402 Info: Get x402 micropayment protocol information
30
- *
31
- * @module
32
- */
33
-
34
- import {
35
- IDataObject,
36
- IExecuteFunctions,
37
- INodeExecutionData,
38
- INodeType,
39
- INodeTypeDescription,
40
- NodeOperationError,
41
- } from "n8n-workflow";
42
-
43
- import { DominusNodeAuth, sanitizeError } from "../../shared/auth";
44
-
45
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46
- const CONTROL_CHAR_RE = /[\x00-\x1f\x7f]/;
47
- const DOMAIN_RE = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
48
- const VALID_CRYPTO_CURRENCIES = new Set([
49
- "btc", "eth", "ltc", "xmr", "zec", "usdc", "sol", "usdt", "dai", "bnb",
50
- ]);
51
-
52
- export class DominusNodeWallet implements INodeType {
53
- description: INodeTypeDescription = {
54
- displayName: "Dominus Node Wallet",
55
- name: "dominusNodeWallet",
56
- icon: "file:dominusnode.svg",
57
- group: ["transform"],
58
- version: 1,
59
- subtitle: '={{$parameter["operation"]}}',
60
- description: "Manage Dominus Node wallet, agentic wallets, and teams",
61
- defaults: { name: "Dominus Node Wallet" },
62
- inputs: ["main"],
63
- outputs: ["main"],
64
- credentials: [{ name: "dominusNodeApi", required: true }],
65
- properties: [
66
- {
67
- displayName: "Operation",
68
- name: "operation",
69
- type: "options",
70
- noDataExpression: true,
71
- options: [
72
- // Wallet
73
- { name: "Check Balance", value: "checkBalance", description: "Get current wallet balance", action: "Check balance" },
74
- { name: "Top Up (Stripe)", value: "topUpStripe", description: "Create a Stripe checkout session", action: "Top up stripe" },
75
- { name: "Top Up (Crypto)", value: "topUpCrypto", description: "Create a crypto payment invoice", action: "Top up crypto" },
76
- { name: "Top Up (PayPal)", value: "topUpPaypal", description: "Create a PayPal checkout session", action: "Top up paypal" },
77
- { name: "Get Transactions", value: "getTransactions", description: "Get wallet transaction history", action: "Get transactions" },
78
- { name: "Get Forecast", value: "getForecast", description: "Get wallet spend forecast", action: "Get forecast" },
79
- { name: "Check Payment", value: "checkPayment", description: "Check crypto payment invoice status", action: "Check payment" },
80
- // Agentic Wallets
81
- { name: "Create Agentic Wallet", value: "createAgenticWallet", description: "Create a sub-wallet with spending limits", action: "Create agentic wallet" },
82
- { name: "Fund Agentic Wallet", value: "fundAgenticWallet", description: "Transfer funds to an agentic wallet", action: "Fund agentic wallet" },
83
- { name: "Get Agentic Wallet Balance", value: "getAgenticBalance", description: "Get agentic wallet balance", action: "Get agentic wallet balance" },
84
- { name: "List Agentic Wallets", value: "listAgenticWallets", description: "List all agentic wallets", action: "List agentic wallets" },
85
- { name: "Get Agentic Transactions", value: "getAgenticTransactions", description: "Get agentic wallet transactions", action: "Get agentic transactions" },
86
- { name: "Freeze Agentic Wallet", value: "freezeAgenticWallet", description: "Freeze an agentic wallet", action: "Freeze agentic wallet" },
87
- { name: "Unfreeze Agentic Wallet", value: "unfreezeAgenticWallet", description: "Unfreeze an agentic wallet", action: "Unfreeze agentic wallet" },
88
- { name: "Delete Agentic Wallet", value: "deleteAgenticWallet", description: "Delete an agentic wallet", action: "Delete agentic wallet" },
89
- { name: "Update Wallet Policy", value: "updateWalletPolicy", description: "Update agentic wallet spending policy", action: "Update wallet policy" },
90
- // Teams
91
- { name: "Create Team", value: "createTeam", description: "Create a new team", action: "Create team" },
92
- { name: "List Teams", value: "listTeams", description: "List all teams", action: "List teams" },
93
- { name: "Team Details", value: "teamDetails", description: "Get team details", action: "Team details" },
94
- { name: "Fund Team", value: "fundTeam", description: "Fund a team wallet", action: "Fund team" },
95
- { name: "Create Team Key", value: "createTeamKey", description: "Create an API key for a team", action: "Create team key" },
96
- { name: "Team Usage", value: "teamUsage", description: "Get team wallet transactions", action: "Team usage" },
97
- { name: "Update Team", value: "updateTeam", description: "Update team settings", action: "Update team" },
98
- { name: "Update Team Member Role", value: "updateTeamMemberRole", description: "Update a team member role", action: "Update team member role" },
99
- // x402
100
- { name: "x402 Info", value: "x402Info", description: "Get x402 micropayment protocol information", action: "Get x402 info" },
101
- ],
102
- default: "checkBalance",
103
- },
104
-
105
- // --- Stripe top-up ---
106
- {
107
- displayName: "Amount (Cents)",
108
- name: "amountCents",
109
- type: "number",
110
- default: 500,
111
- required: true,
112
- description: "Amount in cents (e.g., 500 = $5.00). Minimum 500 ($5).",
113
- displayOptions: { show: { operation: ["topUpStripe", "topUpPaypal"] } },
114
- },
115
-
116
- // --- Crypto top-up ---
117
- {
118
- displayName: "Amount (USD)",
119
- name: "amountUsd",
120
- type: "number",
121
- default: 10,
122
- required: true,
123
- description: "Amount in USD. Minimum $5.",
124
- displayOptions: { show: { operation: ["topUpCrypto"] } },
125
- },
126
- {
127
- displayName: "Currency",
128
- name: "currency",
129
- type: "options",
130
- options: [
131
- { name: "Bitcoin (BTC)", value: "btc" },
132
- { name: "Ethereum (ETH)", value: "eth" },
133
- { name: "Litecoin (LTC)", value: "ltc" },
134
- { name: "Monero (XMR)", value: "xmr" },
135
- { name: "Zcash (ZEC)", value: "zec" },
136
- { name: "USDC", value: "usdc" },
137
- { name: "Solana (SOL)", value: "sol" },
138
- { name: "Tether (USDT)", value: "usdt" },
139
- { name: "DAI", value: "dai" },
140
- { name: "BNB", value: "bnb" },
141
- ],
142
- default: "btc",
143
- description: "Cryptocurrency to pay with",
144
- displayOptions: { show: { operation: ["topUpCrypto"] } },
145
- },
146
-
147
- // --- Transaction / payment params ---
148
- {
149
- displayName: "Limit",
150
- name: "walletTransactionLimit",
151
- type: "number",
152
- default: 20,
153
- description: "Number of transactions to return (1-100)",
154
- displayOptions: { show: { operation: ["getTransactions"] } },
155
- },
156
- {
157
- displayName: "Invoice ID",
158
- name: "invoiceId",
159
- type: "string",
160
- default: "",
161
- required: true,
162
- description: "Crypto payment invoice UUID to check status",
163
- displayOptions: { show: { operation: ["checkPayment"] } },
164
- },
165
-
166
- // --- Agentic wallet params ---
167
- {
168
- displayName: "Label",
169
- name: "agenticLabel",
170
- type: "string",
171
- default: "",
172
- required: true,
173
- description: "Label for the agentic wallet (max 100 chars)",
174
- displayOptions: { show: { operation: ["createAgenticWallet"] } },
175
- },
176
- {
177
- displayName: "Spending Limit (Cents)",
178
- name: "spendingLimitCents",
179
- type: "number",
180
- default: 1000,
181
- required: true,
182
- description: "Per-transaction spending limit in cents",
183
- displayOptions: { show: { operation: ["createAgenticWallet"] } },
184
- },
185
- {
186
- displayName: "Daily Limit (Cents)",
187
- name: "dailyLimitCents",
188
- type: "number",
189
- default: 0,
190
- description: "Optional daily budget cap in cents (0 = no limit, max 1,000,000)",
191
- displayOptions: { show: { operation: ["createAgenticWallet"] } },
192
- },
193
- {
194
- displayName: "Allowed Domains",
195
- name: "allowedDomains",
196
- type: "string",
197
- default: "",
198
- description: "Comma-separated domain allowlist (e.g., \"example.com,api.example.org\"). Leave empty for no restriction.",
199
- displayOptions: { show: { operation: ["createAgenticWallet"] } },
200
- },
201
- {
202
- displayName: "Wallet ID",
203
- name: "policyWalletId",
204
- type: "string",
205
- default: "",
206
- required: true,
207
- description: "Agentic wallet UUID to update policy for",
208
- displayOptions: { show: { operation: ["updateWalletPolicy"] } },
209
- },
210
- {
211
- displayName: "Daily Limit (Cents)",
212
- name: "policyDailyLimitCents",
213
- type: "number",
214
- default: 0,
215
- description: "Daily budget cap in cents (0 = no limit, max 1,000,000). Set to -1 to remove.",
216
- displayOptions: { show: { operation: ["updateWalletPolicy"] } },
217
- },
218
- {
219
- displayName: "Allowed Domains",
220
- name: "policyAllowedDomains",
221
- type: "string",
222
- default: "",
223
- description: "Comma-separated domain allowlist. Leave empty to skip, set to \"*\" to remove restriction.",
224
- displayOptions: { show: { operation: ["updateWalletPolicy"] } },
225
- },
226
- {
227
- displayName: "Wallet ID",
228
- name: "walletId",
229
- type: "string",
230
- default: "",
231
- required: true,
232
- description: "Agentic wallet UUID",
233
- displayOptions: {
234
- show: {
235
- operation: [
236
- "fundAgenticWallet",
237
- "getAgenticBalance",
238
- "getAgenticTransactions",
239
- "freezeAgenticWallet",
240
- "unfreezeAgenticWallet",
241
- "deleteAgenticWallet",
242
- ],
243
- },
244
- },
245
- },
246
- {
247
- displayName: "Amount (Cents)",
248
- name: "fundAmountCents",
249
- type: "number",
250
- default: 100,
251
- required: true,
252
- description: "Amount in cents to transfer to the agentic wallet",
253
- displayOptions: { show: { operation: ["fundAgenticWallet"] } },
254
- },
255
- {
256
- displayName: "Limit",
257
- name: "transactionLimit",
258
- type: "number",
259
- default: 20,
260
- description: "Number of transactions to return (1-100)",
261
- displayOptions: { show: { operation: ["getAgenticTransactions", "teamUsage"] } },
262
- },
263
-
264
- // --- Team params ---
265
- {
266
- displayName: "Team Name",
267
- name: "teamName",
268
- type: "string",
269
- default: "",
270
- required: true,
271
- description: "Name for the team (max 100 chars)",
272
- displayOptions: { show: { operation: ["createTeam"] } },
273
- },
274
- {
275
- displayName: "Max Members",
276
- name: "maxMembers",
277
- type: "number",
278
- default: 10,
279
- description: "Maximum team members (1-100)",
280
- displayOptions: { show: { operation: ["createTeam"] } },
281
- },
282
- {
283
- displayName: "Team ID",
284
- name: "teamId",
285
- type: "string",
286
- default: "",
287
- required: true,
288
- description: "Team UUID",
289
- displayOptions: {
290
- show: {
291
- operation: [
292
- "teamDetails",
293
- "fundTeam",
294
- "createTeamKey",
295
- "teamUsage",
296
- "updateTeam",
297
- "updateTeamMemberRole",
298
- ],
299
- },
300
- },
301
- },
302
- {
303
- displayName: "Amount (Cents)",
304
- name: "teamFundAmountCents",
305
- type: "number",
306
- default: 500,
307
- required: true,
308
- description: "Amount in cents to fund the team wallet. Min 100 ($1), max 1,000,000 ($10,000).",
309
- displayOptions: { show: { operation: ["fundTeam"] } },
310
- },
311
- {
312
- displayName: "Key Label",
313
- name: "keyLabel",
314
- type: "string",
315
- default: "",
316
- required: true,
317
- description: "Label for the team API key (max 100 chars)",
318
- displayOptions: { show: { operation: ["createTeamKey"] } },
319
- },
320
- {
321
- displayName: "New Team Name",
322
- name: "updateTeamName",
323
- type: "string",
324
- default: "",
325
- description: "New name for the team",
326
- displayOptions: { show: { operation: ["updateTeam"] } },
327
- },
328
- {
329
- displayName: "New Max Members",
330
- name: "updateMaxMembers",
331
- type: "number",
332
- default: 0,
333
- description: "New max members (1-100). Set to 0 to skip.",
334
- displayOptions: { show: { operation: ["updateTeam"] } },
335
- },
336
- {
337
- displayName: "User ID",
338
- name: "userId",
339
- type: "string",
340
- default: "",
341
- required: true,
342
- description: "User UUID whose role to update",
343
- displayOptions: { show: { operation: ["updateTeamMemberRole"] } },
344
- },
345
- {
346
- displayName: "Role",
347
- name: "role",
348
- type: "options",
349
- options: [
350
- { name: "Admin", value: "admin" },
351
- { name: "Member", value: "member" },
352
- ],
353
- default: "member",
354
- description: "New role for the team member",
355
- displayOptions: { show: { operation: ["updateTeamMemberRole"] } },
356
- },
357
- ],
358
- };
359
-
360
- async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
361
- const items = this.getInputData();
362
- const returnData: INodeExecutionData[] = [];
363
- const credentials = await this.getCredentials("dominusNodeApi");
364
-
365
- const apiKey = credentials.apiKey as string;
366
- const baseUrl = (credentials.baseUrl as string) || "https://api.dominusnode.com";
367
-
368
- if (!apiKey) {
369
- throw new NodeOperationError(this.getNode(), "API Key is required");
370
- }
371
-
372
- const agentSecret = (credentials.agentSecret as string) || undefined;
373
- const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
374
- const operation = this.getNodeParameter("operation", 0) as string;
375
-
376
- for (let i = 0; i < items.length; i++) {
377
- try {
378
- let result: unknown;
379
-
380
- switch (operation) {
381
- // ----- Wallet -----
382
- case "checkBalance": {
383
- result = await auth.apiRequest("GET", "/api/wallet");
384
- break;
385
- }
386
-
387
- case "topUpStripe": {
388
- const amountCents = this.getNodeParameter("amountCents", i) as number;
389
- if (!Number.isInteger(amountCents) || amountCents < 500 || amountCents > 1_000_000) {
390
- throw new NodeOperationError(
391
- this.getNode(),
392
- "amountCents must be an integer between 500 ($5) and 1,000,000 ($10,000)",
393
- { itemIndex: i },
394
- );
395
- }
396
- result = await auth.apiRequest("POST", "/api/wallet/topup/stripe", { amountCents });
397
- break;
398
- }
399
-
400
- case "topUpCrypto": {
401
- const amountUsd = this.getNodeParameter("amountUsd", i) as number;
402
- const currency = this.getNodeParameter("currency", i) as string;
403
- if (typeof amountUsd !== "number" || amountUsd < 5 || amountUsd > 10_000) {
404
- throw new NodeOperationError(
405
- this.getNode(),
406
- "amountUsd must be between $5 and $10,000",
407
- { itemIndex: i },
408
- );
409
- }
410
- if (!VALID_CRYPTO_CURRENCIES.has(currency)) {
411
- throw new NodeOperationError(
412
- this.getNode(),
413
- `Invalid currency. Valid options: ${[...VALID_CRYPTO_CURRENCIES].join(", ")}`,
414
- { itemIndex: i },
415
- );
416
- }
417
- result = await auth.apiRequest("POST", "/api/wallet/topup/crypto", {
418
- amountUsd,
419
- currency,
420
- });
421
- break;
422
- }
423
-
424
- case "topUpPaypal": {
425
- const amountCents = this.getNodeParameter("amountCents", i) as number;
426
- if (!Number.isInteger(amountCents) || amountCents < 500 || amountCents > 1_000_000) {
427
- throw new NodeOperationError(
428
- this.getNode(),
429
- "amountCents must be an integer between 500 ($5) and 1,000,000 ($10,000)",
430
- { itemIndex: i },
431
- );
432
- }
433
- result = await auth.apiRequest("POST", "/api/wallet/topup/stripe", {
434
- amountCents,
435
- paymentMethod: "paypal",
436
- });
437
- break;
438
- }
439
-
440
- case "getTransactions": {
441
- const limit = this.getNodeParameter("walletTransactionLimit", i, 20) as number;
442
- validateLimit(this, limit, i);
443
-
444
- const params = new URLSearchParams();
445
- params.set("limit", String(limit));
446
- result = await auth.apiRequest("GET", `/api/wallet/transactions?${params.toString()}`);
447
- break;
448
- }
449
-
450
- case "getForecast": {
451
- result = await auth.apiRequest("GET", "/api/wallet/forecast");
452
- break;
453
- }
454
-
455
- case "checkPayment": {
456
- const invoiceId = this.getNodeParameter("invoiceId", i) as string;
457
- validateUuid(this, invoiceId, "invoiceId", i);
458
- result = await auth.apiRequest(
459
- "GET",
460
- `/api/wallet/topup/crypto/${encodeURIComponent(invoiceId)}/status`,
461
- );
462
- break;
463
- }
464
-
465
- // ----- Agentic Wallets -----
466
- case "createAgenticWallet": {
467
- const label = this.getNodeParameter("agenticLabel", i) as string;
468
- const spendingLimitCents = this.getNodeParameter("spendingLimitCents", i) as number;
469
- const dailyLimitCents = this.getNodeParameter("dailyLimitCents", i, 0) as number;
470
- const allowedDomainsRaw = this.getNodeParameter("allowedDomains", i, "") as string;
471
-
472
- if (!label || typeof label !== "string") {
473
- throw new NodeOperationError(this.getNode(), "Label is required", { itemIndex: i });
474
- }
475
- if (label.length > 100) {
476
- throw new NodeOperationError(
477
- this.getNode(),
478
- "Label must be 100 characters or fewer",
479
- { itemIndex: i },
480
- );
481
- }
482
- if (CONTROL_CHAR_RE.test(label)) {
483
- throw new NodeOperationError(
484
- this.getNode(),
485
- "Label contains invalid control characters",
486
- { itemIndex: i },
487
- );
488
- }
489
- if (
490
- !Number.isInteger(spendingLimitCents) ||
491
- spendingLimitCents <= 0 ||
492
- spendingLimitCents > 2_147_483_647
493
- ) {
494
- throw new NodeOperationError(
495
- this.getNode(),
496
- "Spending limit must be a positive integer",
497
- { itemIndex: i },
498
- );
499
- }
500
-
501
- const body: Record<string, unknown> = { label, spendingLimitCents };
502
-
503
- if (dailyLimitCents > 0) {
504
- if (!Number.isInteger(dailyLimitCents) || dailyLimitCents > 1_000_000) {
505
- throw new NodeOperationError(
506
- this.getNode(),
507
- "dailyLimitCents must be an integer between 1 and 1,000,000",
508
- { itemIndex: i },
509
- );
510
- }
511
- body.dailyLimitCents = dailyLimitCents;
512
- }
513
-
514
- if (allowedDomainsRaw && allowedDomainsRaw.trim().length > 0) {
515
- const domains = allowedDomainsRaw.split(",").map((d) => d.trim()).filter(Boolean);
516
- if (domains.length > 100) {
517
- throw new NodeOperationError(
518
- this.getNode(),
519
- "allowedDomains may contain at most 100 entries",
520
- { itemIndex: i },
521
- );
522
- }
523
- for (const d of domains) {
524
- if (d.length > 253 || !DOMAIN_RE.test(d)) {
525
- throw new NodeOperationError(
526
- this.getNode(),
527
- `Invalid domain: "${d}". Must be a valid domain name (max 253 chars).`,
528
- { itemIndex: i },
529
- );
530
- }
531
- }
532
- body.allowedDomains = domains;
533
- }
534
-
535
- result = await auth.apiRequest("POST", "/api/agent-wallet", body);
536
- break;
537
- }
538
-
539
- case "fundAgenticWallet": {
540
- const walletId = this.getNodeParameter("walletId", i) as string;
541
- const amountCents = this.getNodeParameter("fundAmountCents", i) as number;
542
- validateUuid(this, walletId, "walletId", i);
543
-
544
- if (
545
- !Number.isInteger(amountCents) ||
546
- amountCents <= 0 ||
547
- amountCents > 2_147_483_647
548
- ) {
549
- throw new NodeOperationError(
550
- this.getNode(),
551
- "Amount must be a positive integer",
552
- { itemIndex: i },
553
- );
554
- }
555
-
556
- result = await auth.apiRequest(
557
- "POST",
558
- `/api/agent-wallet/${encodeURIComponent(walletId)}/fund`,
559
- { amountCents },
560
- );
561
- break;
562
- }
563
-
564
- case "getAgenticBalance": {
565
- const walletId = this.getNodeParameter("walletId", i) as string;
566
- validateUuid(this, walletId, "walletId", i);
567
- result = await auth.apiRequest(
568
- "GET",
569
- `/api/agent-wallet/${encodeURIComponent(walletId)}`,
570
- );
571
- break;
572
- }
573
-
574
- case "listAgenticWallets": {
575
- result = await auth.apiRequest("GET", "/api/agent-wallet");
576
- break;
577
- }
578
-
579
- case "getAgenticTransactions": {
580
- const walletId = this.getNodeParameter("walletId", i) as string;
581
- const limit = this.getNodeParameter("transactionLimit", i, 20) as number;
582
- validateUuid(this, walletId, "walletId", i);
583
- validateLimit(this, limit, i);
584
-
585
- const params = new URLSearchParams();
586
- params.set("limit", String(limit));
587
- result = await auth.apiRequest(
588
- "GET",
589
- `/api/agent-wallet/${encodeURIComponent(walletId)}/transactions?${params.toString()}`,
590
- );
591
- break;
592
- }
593
-
594
- case "freezeAgenticWallet": {
595
- const walletId = this.getNodeParameter("walletId", i) as string;
596
- validateUuid(this, walletId, "walletId", i);
597
- result = await auth.apiRequest(
598
- "POST",
599
- `/api/agent-wallet/${encodeURIComponent(walletId)}/freeze`,
600
- );
601
- break;
602
- }
603
-
604
- case "unfreezeAgenticWallet": {
605
- const walletId = this.getNodeParameter("walletId", i) as string;
606
- validateUuid(this, walletId, "walletId", i);
607
- result = await auth.apiRequest(
608
- "POST",
609
- `/api/agent-wallet/${encodeURIComponent(walletId)}/unfreeze`,
610
- );
611
- break;
612
- }
613
-
614
- case "deleteAgenticWallet": {
615
- const walletId = this.getNodeParameter("walletId", i) as string;
616
- validateUuid(this, walletId, "walletId", i);
617
- result = await auth.apiRequest(
618
- "DELETE",
619
- `/api/agent-wallet/${encodeURIComponent(walletId)}`,
620
- );
621
- break;
622
- }
623
-
624
- case "updateWalletPolicy": {
625
- const walletId = this.getNodeParameter("policyWalletId", i) as string;
626
- const dailyLimitCents = this.getNodeParameter("policyDailyLimitCents", i, 0) as number;
627
- const allowedDomainsRaw = this.getNodeParameter("policyAllowedDomains", i, "") as string;
628
-
629
- validateUuid(this, walletId, "policyWalletId", i);
630
-
631
- const body: Record<string, unknown> = {};
632
-
633
- if (dailyLimitCents === -1) {
634
- body.dailyLimitCents = null;
635
- } else if (dailyLimitCents > 0) {
636
- if (!Number.isInteger(dailyLimitCents) || dailyLimitCents > 1_000_000) {
637
- throw new NodeOperationError(
638
- this.getNode(),
639
- "dailyLimitCents must be an integer between 1 and 1,000,000 (or -1 to remove)",
640
- { itemIndex: i },
641
- );
642
- }
643
- body.dailyLimitCents = dailyLimitCents;
644
- }
645
-
646
- if (allowedDomainsRaw === "*") {
647
- body.allowedDomains = null;
648
- } else if (allowedDomainsRaw && allowedDomainsRaw.trim().length > 0) {
649
- const domains = allowedDomainsRaw.split(",").map((d) => d.trim()).filter(Boolean);
650
- if (domains.length > 100) {
651
- throw new NodeOperationError(
652
- this.getNode(),
653
- "allowedDomains may contain at most 100 entries",
654
- { itemIndex: i },
655
- );
656
- }
657
- for (const d of domains) {
658
- if (d.length > 253 || !DOMAIN_RE.test(d)) {
659
- throw new NodeOperationError(
660
- this.getNode(),
661
- `Invalid domain: "${d}". Must be a valid domain name (max 253 chars).`,
662
- { itemIndex: i },
663
- );
664
- }
665
- }
666
- body.allowedDomains = domains;
667
- }
668
-
669
- if (Object.keys(body).length === 0) {
670
- throw new NodeOperationError(
671
- this.getNode(),
672
- "At least one of dailyLimitCents or allowedDomains must be provided",
673
- { itemIndex: i },
674
- );
675
- }
676
-
677
- result = await auth.apiRequest(
678
- "PATCH",
679
- `/api/agent-wallet/${encodeURIComponent(walletId)}/policy`,
680
- body,
681
- );
682
- break;
683
- }
684
-
685
- // ----- Teams -----
686
- case "createTeam": {
687
- const name = this.getNodeParameter("teamName", i) as string;
688
- const maxMembers = this.getNodeParameter("maxMembers", i, 10) as number;
689
-
690
- if (!name || typeof name !== "string") {
691
- throw new NodeOperationError(this.getNode(), "Team name is required", { itemIndex: i });
692
- }
693
- if (name.length > 100) {
694
- throw new NodeOperationError(
695
- this.getNode(),
696
- "Team name must be 100 characters or fewer",
697
- { itemIndex: i },
698
- );
699
- }
700
- if (CONTROL_CHAR_RE.test(name)) {
701
- throw new NodeOperationError(
702
- this.getNode(),
703
- "Team name contains invalid control characters",
704
- { itemIndex: i },
705
- );
706
- }
707
-
708
- const body: Record<string, unknown> = { name };
709
- if (maxMembers) {
710
- if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
711
- throw new NodeOperationError(
712
- this.getNode(),
713
- "maxMembers must be an integer between 1 and 100",
714
- { itemIndex: i },
715
- );
716
- }
717
- body.maxMembers = maxMembers;
718
- }
719
-
720
- result = await auth.apiRequest("POST", "/api/teams", body);
721
- break;
722
- }
723
-
724
- case "listTeams": {
725
- result = await auth.apiRequest("GET", "/api/teams");
726
- break;
727
- }
728
-
729
- case "teamDetails": {
730
- const teamId = this.getNodeParameter("teamId", i) as string;
731
- validateUuid(this, teamId, "teamId", i);
732
- result = await auth.apiRequest("GET", `/api/teams/${encodeURIComponent(teamId)}`);
733
- break;
734
- }
735
-
736
- case "fundTeam": {
737
- const teamId = this.getNodeParameter("teamId", i) as string;
738
- const amountCents = this.getNodeParameter("teamFundAmountCents", i) as number;
739
- validateUuid(this, teamId, "teamId", i);
740
-
741
- if (
742
- !Number.isInteger(amountCents) ||
743
- amountCents < 100 ||
744
- amountCents > 1_000_000
745
- ) {
746
- throw new NodeOperationError(
747
- this.getNode(),
748
- "amountCents must be between 100 ($1) and 1,000,000 ($10,000)",
749
- { itemIndex: i },
750
- );
751
- }
752
-
753
- result = await auth.apiRequest(
754
- "POST",
755
- `/api/teams/${encodeURIComponent(teamId)}/wallet/fund`,
756
- { amountCents },
757
- );
758
- break;
759
- }
760
-
761
- case "createTeamKey": {
762
- const teamId = this.getNodeParameter("teamId", i) as string;
763
- const label = this.getNodeParameter("keyLabel", i) as string;
764
- validateUuid(this, teamId, "teamId", i);
765
-
766
- if (!label || typeof label !== "string") {
767
- throw new NodeOperationError(this.getNode(), "Key label is required", { itemIndex: i });
768
- }
769
- if (label.length > 100) {
770
- throw new NodeOperationError(
771
- this.getNode(),
772
- "Key label must be 100 characters or fewer",
773
- { itemIndex: i },
774
- );
775
- }
776
- if (CONTROL_CHAR_RE.test(label)) {
777
- throw new NodeOperationError(
778
- this.getNode(),
779
- "Key label contains invalid control characters",
780
- { itemIndex: i },
781
- );
782
- }
783
-
784
- result = await auth.apiRequest(
785
- "POST",
786
- `/api/teams/${encodeURIComponent(teamId)}/keys`,
787
- { label },
788
- );
789
- break;
790
- }
791
-
792
- case "teamUsage": {
793
- const teamId = this.getNodeParameter("teamId", i) as string;
794
- const limit = this.getNodeParameter("transactionLimit", i, 20) as number;
795
- validateUuid(this, teamId, "teamId", i);
796
- validateLimit(this, limit, i);
797
-
798
- const params = new URLSearchParams();
799
- params.set("limit", String(limit));
800
- result = await auth.apiRequest(
801
- "GET",
802
- `/api/teams/${encodeURIComponent(teamId)}/wallet/transactions?${params.toString()}`,
803
- );
804
- break;
805
- }
806
-
807
- case "updateTeam": {
808
- const teamId = this.getNodeParameter("teamId", i) as string;
809
- validateUuid(this, teamId, "teamId", i);
810
-
811
- const body: Record<string, unknown> = {};
812
- const newName = this.getNodeParameter("updateTeamName", i, "") as string;
813
- const newMax = this.getNodeParameter("updateMaxMembers", i, 0) as number;
814
-
815
- if (newName) {
816
- if (newName.length > 100) {
817
- throw new NodeOperationError(
818
- this.getNode(),
819
- "Team name must be 100 characters or fewer",
820
- { itemIndex: i },
821
- );
822
- }
823
- if (CONTROL_CHAR_RE.test(newName)) {
824
- throw new NodeOperationError(
825
- this.getNode(),
826
- "Team name contains invalid control characters",
827
- { itemIndex: i },
828
- );
829
- }
830
- body.name = newName;
831
- }
832
-
833
- if (newMax > 0) {
834
- if (!Number.isInteger(newMax) || newMax < 1 || newMax > 100) {
835
- throw new NodeOperationError(
836
- this.getNode(),
837
- "maxMembers must be an integer between 1 and 100",
838
- { itemIndex: i },
839
- );
840
- }
841
- body.maxMembers = newMax;
842
- }
843
-
844
- if (Object.keys(body).length === 0) {
845
- throw new NodeOperationError(
846
- this.getNode(),
847
- "At least one of name or maxMembers must be provided",
848
- { itemIndex: i },
849
- );
850
- }
851
-
852
- result = await auth.apiRequest(
853
- "PATCH",
854
- `/api/teams/${encodeURIComponent(teamId)}`,
855
- body,
856
- );
857
- break;
858
- }
859
-
860
- case "updateTeamMemberRole": {
861
- const teamId = this.getNodeParameter("teamId", i) as string;
862
- const userId = this.getNodeParameter("userId", i) as string;
863
- const role = this.getNodeParameter("role", i) as string;
864
-
865
- validateUuid(this, teamId, "teamId", i);
866
- validateUuid(this, userId, "userId", i);
867
-
868
- if (role !== "member" && role !== "admin") {
869
- throw new NodeOperationError(
870
- this.getNode(),
871
- "Role must be 'member' or 'admin'",
872
- { itemIndex: i },
873
- );
874
- }
875
-
876
- result = await auth.apiRequest(
877
- "PATCH",
878
- `/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`,
879
- { role },
880
- );
881
- break;
882
- }
883
-
884
- case "x402Info": {
885
- result = await auth.apiRequest("GET", "/api/x402/info");
886
- break;
887
- }
888
-
889
- default:
890
- throw new NodeOperationError(
891
- this.getNode(),
892
- `Unknown operation: ${operation}`,
893
- { itemIndex: i },
894
- );
895
- }
896
-
897
- returnData.push({ json: (result ?? {}) as IDataObject });
898
- } catch (err) {
899
- if (this.continueOnFail()) {
900
- returnData.push({
901
- json: {
902
- error: sanitizeError(err instanceof Error ? err.message : String(err)),
903
- },
904
- });
905
- continue;
906
- }
907
- if (err instanceof NodeOperationError) throw err;
908
- throw new NodeOperationError(
909
- this.getNode(),
910
- sanitizeError(err instanceof Error ? err.message : String(err)),
911
- { itemIndex: i },
912
- );
913
- }
914
- }
915
-
916
- return [returnData];
917
- }
918
- }
919
-
920
- // ---------------------------------------------------------------------------
921
- // Validation helpers
922
- // ---------------------------------------------------------------------------
923
-
924
- function validateUuid(
925
- ctx: IExecuteFunctions,
926
- value: string,
927
- fieldName: string,
928
- itemIndex: number,
929
- ): void {
930
- if (!value || typeof value !== "string") {
931
- throw new NodeOperationError(ctx.getNode(), `${fieldName} is required`, { itemIndex });
932
- }
933
- if (!UUID_RE.test(value)) {
934
- throw new NodeOperationError(ctx.getNode(), `${fieldName} must be a valid UUID`, { itemIndex });
935
- }
936
- }
937
-
938
- function validateLimit(
939
- ctx: IExecuteFunctions,
940
- limit: number,
941
- itemIndex: number,
942
- ): void {
943
- if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
944
- throw new NodeOperationError(
945
- ctx.getNode(),
946
- "Limit must be an integer between 1 and 100",
947
- { itemIndex },
948
- );
949
- }
950
- }