n8n-nodes-dominusnode 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/credentials/DominusNodeApi.credentials.js +8 -0
  2. package/dist/credentials/DominusNodeApi.credentials.js.map +1 -1
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.js +9 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.d.ts +23 -0
  7. package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js +223 -0
  8. package/dist/nodes/DominusNodeAccount/DominusNodeAccount.node.js.map +1 -0
  9. package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.d.ts +20 -0
  10. package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js +145 -0
  11. package/dist/nodes/DominusNodeKeys/DominusNodeKeys.node.js.map +1 -0
  12. package/dist/nodes/DominusNodePlans/DominusNodePlans.node.d.ts +19 -0
  13. package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js +123 -0
  14. package/dist/nodes/DominusNodePlans/DominusNodePlans.node.js.map +1 -0
  15. package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.d.ts +2 -1
  16. package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js +14 -2
  17. package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js.map +1 -1
  18. package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.d.ts +27 -0
  19. package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js +258 -0
  20. package/dist/nodes/DominusNodeTeams/DominusNodeTeams.node.js.map +1 -0
  21. package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.d.ts +3 -1
  22. package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js +49 -2
  23. package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js.map +1 -1
  24. package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.d.ts +5 -1
  25. package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js +46 -2
  26. package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js.map +1 -1
  27. package/dist/shared/auth.d.ts +2 -1
  28. package/dist/shared/auth.js +16 -5
  29. package/dist/shared/auth.js.map +1 -1
  30. package/package.json +7 -3
  31. package/src/credentials/DominusNodeApi.credentials.ts +9 -0
  32. package/src/index.ts +4 -0
  33. package/src/nodes/DominusNodeAccount/DominusNodeAccount.node.ts +283 -0
  34. package/src/nodes/DominusNodeKeys/DominusNodeKeys.node.ts +192 -0
  35. package/src/nodes/DominusNodePlans/DominusNodePlans.node.ts +154 -0
  36. package/src/nodes/DominusNodeProxy/DominusNodeProxy.node.ts +13 -2
  37. package/src/nodes/DominusNodeTeams/DominusNodeTeams.node.ts +351 -0
  38. package/src/nodes/DominusNodeUsage/DominusNodeUsage.node.ts +55 -2
  39. package/src/nodes/DominusNodeWallet/DominusNodeWallet.node.ts +54 -2
  40. package/src/shared/auth.ts +19 -4
  41. package/tests/DominusNodeProxy.test.ts +2 -2
  42. package/tests/DominusNodeUsage.test.ts +2 -2
  43. package/tests/DominusNodeWallet.test.ts +2 -2
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Dominus Node Teams n8n community node.
3
+ *
4
+ * Operations (9 tools):
5
+ * - Delete Team: Delete a team
6
+ * - Revoke Team Key: Revoke a team API key
7
+ * - List Team Keys: List all API keys for a team
8
+ * - List Team Members: List all members of a team
9
+ * - Add Team Member: Add a user to a team
10
+ * - Remove Team Member: Remove a user from a team
11
+ * - Invite Team Member: Send an email invitation to join a team
12
+ * - List Team Invites: List pending invitations for a team
13
+ * - Cancel Team Invite: Cancel a pending team invitation
14
+ *
15
+ * Security:
16
+ * - UUID validation for team/user/key/invite IDs
17
+ * - Email validation for invitations
18
+ * - Control character rejection
19
+ * - Credential sanitization in error messages
20
+ *
21
+ * @module
22
+ */
23
+
24
+ import {
25
+ IDataObject,
26
+ IExecuteFunctions,
27
+ INodeExecutionData,
28
+ INodeType,
29
+ INodeTypeDescription,
30
+ NodeOperationError,
31
+ } from "n8n-workflow";
32
+
33
+ import { DominusNodeAuth, sanitizeError } from "../../shared/auth";
34
+
35
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
36
+ const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
37
+
38
+ export class DominusNodeTeams implements INodeType {
39
+ description: INodeTypeDescription = {
40
+ displayName: "Dominus Node Teams",
41
+ name: "dominusNodeTeams",
42
+ icon: "file:dominusnode.svg",
43
+ group: ["transform"],
44
+ version: 1,
45
+ subtitle: '={{$parameter["operation"]}}',
46
+ description: "Manage Dominus Node team members, keys, and invitations",
47
+ defaults: { name: "Dominus Node Teams" },
48
+ inputs: ["main"],
49
+ outputs: ["main"],
50
+ credentials: [{ name: "dominusNodeApi", required: true }],
51
+ properties: [
52
+ {
53
+ displayName: "Operation",
54
+ name: "operation",
55
+ type: "options",
56
+ noDataExpression: true,
57
+ options: [
58
+ { name: "Delete Team", value: "teamDelete", description: "Delete a team", action: "Delete team" },
59
+ { name: "Revoke Team Key", value: "teamRevokeKey", description: "Revoke a team API key", action: "Revoke team key" },
60
+ { name: "List Team Keys", value: "teamListKeys", description: "List all API keys for a team", action: "List team keys" },
61
+ { name: "List Team Members", value: "teamListMembers", description: "List all members of a team", action: "List team members" },
62
+ { name: "Add Team Member", value: "teamAddMember", description: "Add a user to a team", action: "Add team member" },
63
+ { name: "Remove Team Member", value: "teamRemoveMember", description: "Remove a user from a team", action: "Remove team member" },
64
+ { name: "Invite Team Member", value: "teamInviteMember", description: "Send an email invitation to join a team", action: "Invite team member" },
65
+ { name: "List Team Invites", value: "teamListInvites", description: "List pending invitations for a team", action: "List team invites" },
66
+ { name: "Cancel Team Invite", value: "teamCancelInvite", description: "Cancel a pending team invitation", action: "Cancel team invite" },
67
+ ],
68
+ default: "teamListMembers",
69
+ },
70
+
71
+ // --- Team ID (shared) ---
72
+ {
73
+ displayName: "Team ID",
74
+ name: "teamId",
75
+ type: "string",
76
+ default: "",
77
+ required: true,
78
+ description: "Team UUID",
79
+ displayOptions: {
80
+ show: {
81
+ operation: [
82
+ "teamDelete",
83
+ "teamRevokeKey",
84
+ "teamListKeys",
85
+ "teamListMembers",
86
+ "teamAddMember",
87
+ "teamRemoveMember",
88
+ "teamInviteMember",
89
+ "teamListInvites",
90
+ "teamCancelInvite",
91
+ ],
92
+ },
93
+ },
94
+ },
95
+
96
+ // --- Key ID for revoke ---
97
+ {
98
+ displayName: "Key ID",
99
+ name: "keyId",
100
+ type: "string",
101
+ default: "",
102
+ required: true,
103
+ description: "UUID of the team API key to revoke",
104
+ displayOptions: { show: { operation: ["teamRevokeKey"] } },
105
+ },
106
+
107
+ // --- User ID for add/remove member ---
108
+ {
109
+ displayName: "User ID",
110
+ name: "userId",
111
+ type: "string",
112
+ default: "",
113
+ required: true,
114
+ description: "UUID of the user to add or remove",
115
+ displayOptions: { show: { operation: ["teamAddMember", "teamRemoveMember"] } },
116
+ },
117
+
118
+ // --- Role for add member / invite ---
119
+ {
120
+ displayName: "Role",
121
+ name: "role",
122
+ type: "options",
123
+ options: [
124
+ { name: "Admin", value: "admin" },
125
+ { name: "Member", value: "member" },
126
+ ],
127
+ default: "member",
128
+ description: "Role for the team member",
129
+ displayOptions: { show: { operation: ["teamAddMember", "teamInviteMember"] } },
130
+ },
131
+
132
+ // --- Invite email ---
133
+ {
134
+ displayName: "Email",
135
+ name: "inviteEmail",
136
+ type: "string",
137
+ default: "",
138
+ required: true,
139
+ description: "Email address to send the invitation to",
140
+ displayOptions: { show: { operation: ["teamInviteMember"] } },
141
+ },
142
+
143
+ // --- Invite ID for cancel ---
144
+ {
145
+ displayName: "Invite ID",
146
+ name: "inviteId",
147
+ type: "string",
148
+ default: "",
149
+ required: true,
150
+ description: "UUID of the invitation to cancel",
151
+ displayOptions: { show: { operation: ["teamCancelInvite"] } },
152
+ },
153
+ ],
154
+ };
155
+
156
+ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
157
+ const items = this.getInputData();
158
+ const returnData: INodeExecutionData[] = [];
159
+ const credentials = await this.getCredentials("dominusNodeApi");
160
+
161
+ const apiKey = credentials.apiKey as string;
162
+ const baseUrl = (credentials.baseUrl as string) || "https://api.dominusnode.com";
163
+
164
+ if (!apiKey) {
165
+ throw new NodeOperationError(this.getNode(), "API Key is required");
166
+ }
167
+
168
+ const agentSecret = (credentials.agentSecret as string) || undefined;
169
+ const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
170
+ const operation = this.getNodeParameter("operation", 0) as string;
171
+
172
+ for (let i = 0; i < items.length; i++) {
173
+ try {
174
+ let result: unknown;
175
+
176
+ // All operations require teamId
177
+ const teamId = this.getNodeParameter("teamId", i) as string;
178
+ validateUuid(this, teamId, "teamId", i);
179
+
180
+ switch (operation) {
181
+ case "teamDelete": {
182
+ result = await auth.apiRequest(
183
+ "DELETE",
184
+ `/api/teams/${encodeURIComponent(teamId)}`,
185
+ );
186
+ break;
187
+ }
188
+
189
+ case "teamRevokeKey": {
190
+ const keyId = this.getNodeParameter("keyId", i) as string;
191
+ validateUuid(this, keyId, "keyId", i);
192
+ result = await auth.apiRequest(
193
+ "DELETE",
194
+ `/api/teams/${encodeURIComponent(teamId)}/keys/${encodeURIComponent(keyId)}`,
195
+ );
196
+ break;
197
+ }
198
+
199
+ case "teamListKeys": {
200
+ result = await auth.apiRequest(
201
+ "GET",
202
+ `/api/teams/${encodeURIComponent(teamId)}/keys`,
203
+ );
204
+ break;
205
+ }
206
+
207
+ case "teamListMembers": {
208
+ result = await auth.apiRequest(
209
+ "GET",
210
+ `/api/teams/${encodeURIComponent(teamId)}/members`,
211
+ );
212
+ break;
213
+ }
214
+
215
+ case "teamAddMember": {
216
+ const userId = this.getNodeParameter("userId", i) as string;
217
+ const role = this.getNodeParameter("role", i, "member") as string;
218
+ validateUuid(this, userId, "userId", i);
219
+ if (role !== "member" && role !== "admin") {
220
+ throw new NodeOperationError(
221
+ this.getNode(),
222
+ "Role must be 'member' or 'admin'",
223
+ { itemIndex: i },
224
+ );
225
+ }
226
+ result = await auth.apiRequest(
227
+ "POST",
228
+ `/api/teams/${encodeURIComponent(teamId)}/members`,
229
+ { userId, role },
230
+ );
231
+ break;
232
+ }
233
+
234
+ case "teamRemoveMember": {
235
+ const userId = this.getNodeParameter("userId", i) as string;
236
+ validateUuid(this, userId, "userId", i);
237
+ result = await auth.apiRequest(
238
+ "DELETE",
239
+ `/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`,
240
+ );
241
+ break;
242
+ }
243
+
244
+ case "teamInviteMember": {
245
+ const email = this.getNodeParameter("inviteEmail", i) as string;
246
+ const role = this.getNodeParameter("role", i, "member") as string;
247
+ const safeEmail = validateEmail(this, email, i);
248
+ if (role !== "member" && role !== "admin") {
249
+ throw new NodeOperationError(
250
+ this.getNode(),
251
+ "Role must be 'member' or 'admin'",
252
+ { itemIndex: i },
253
+ );
254
+ }
255
+ result = await auth.apiRequest(
256
+ "POST",
257
+ `/api/teams/${encodeURIComponent(teamId)}/invites`,
258
+ { email: safeEmail, role },
259
+ );
260
+ break;
261
+ }
262
+
263
+ case "teamListInvites": {
264
+ result = await auth.apiRequest(
265
+ "GET",
266
+ `/api/teams/${encodeURIComponent(teamId)}/invites`,
267
+ );
268
+ break;
269
+ }
270
+
271
+ case "teamCancelInvite": {
272
+ const inviteId = this.getNodeParameter("inviteId", i) as string;
273
+ validateUuid(this, inviteId, "inviteId", i);
274
+ result = await auth.apiRequest(
275
+ "DELETE",
276
+ `/api/teams/${encodeURIComponent(teamId)}/invites/${encodeURIComponent(inviteId)}`,
277
+ );
278
+ break;
279
+ }
280
+
281
+ default:
282
+ throw new NodeOperationError(
283
+ this.getNode(),
284
+ `Unknown operation: ${operation}`,
285
+ { itemIndex: i },
286
+ );
287
+ }
288
+
289
+ returnData.push({ json: (result ?? {}) as IDataObject });
290
+ } catch (err) {
291
+ if (this.continueOnFail()) {
292
+ returnData.push({
293
+ json: {
294
+ error: sanitizeError(err instanceof Error ? err.message : String(err)),
295
+ },
296
+ });
297
+ continue;
298
+ }
299
+ if (err instanceof NodeOperationError) throw err;
300
+ throw new NodeOperationError(
301
+ this.getNode(),
302
+ sanitizeError(err instanceof Error ? err.message : String(err)),
303
+ { itemIndex: i },
304
+ );
305
+ }
306
+ }
307
+
308
+ return [returnData];
309
+ }
310
+ }
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Validation helpers
314
+ // ---------------------------------------------------------------------------
315
+
316
+ function validateUuid(
317
+ ctx: IExecuteFunctions,
318
+ value: string,
319
+ fieldName: string,
320
+ itemIndex: number,
321
+ ): void {
322
+ if (!value || typeof value !== "string") {
323
+ throw new NodeOperationError(ctx.getNode(), `${fieldName} is required`, { itemIndex });
324
+ }
325
+ if (!UUID_RE.test(value)) {
326
+ throw new NodeOperationError(ctx.getNode(), `${fieldName} must be a valid UUID`, { itemIndex });
327
+ }
328
+ }
329
+
330
+ function validateEmail(
331
+ ctx: IExecuteFunctions,
332
+ email: string,
333
+ itemIndex: number,
334
+ ): string {
335
+ const trimmed = (email ?? "").trim();
336
+ if (!trimmed || !EMAIL_RE.test(trimmed)) {
337
+ throw new NodeOperationError(
338
+ ctx.getNode(),
339
+ "A valid email address is required",
340
+ { itemIndex },
341
+ );
342
+ }
343
+ if (trimmed.length > 254) {
344
+ throw new NodeOperationError(
345
+ ctx.getNode(),
346
+ "Email address too long (max 254 characters)",
347
+ { itemIndex },
348
+ );
349
+ }
350
+ return trimmed;
351
+ }
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Dominus Node Usage n8n community node.
3
3
  *
4
- * Operations:
4
+ * Operations (3 tools):
5
5
  * - Check Usage: Get proxy usage statistics for a given period
6
+ * - Get Daily Usage: Get daily usage breakdown for the last N days
7
+ * - Get Top Hosts: Get top accessed hosts by bandwidth
6
8
  *
7
9
  * @module
8
10
  */
@@ -46,6 +48,18 @@ export class DominusNodeUsage implements INodeType {
46
48
  description: "Get proxy usage statistics for a given period",
47
49
  action: "Check usage",
48
50
  },
51
+ {
52
+ name: "Get Daily Usage",
53
+ value: "getDailyUsage",
54
+ description: "Get daily usage breakdown for the last N days",
55
+ action: "Get daily usage",
56
+ },
57
+ {
58
+ name: "Get Top Hosts",
59
+ value: "getTopHosts",
60
+ description: "Get top accessed hosts by bandwidth",
61
+ action: "Get top hosts",
62
+ },
49
63
  ],
50
64
  default: "checkUsage",
51
65
  },
@@ -62,6 +76,22 @@ export class DominusNodeUsage implements INodeType {
62
76
  description: "Time period for usage statistics",
63
77
  displayOptions: { show: { operation: ["checkUsage"] } },
64
78
  },
79
+ {
80
+ displayName: "Days",
81
+ name: "days",
82
+ type: "number",
83
+ default: 7,
84
+ description: "Number of days of daily usage to return (1-365)",
85
+ displayOptions: { show: { operation: ["getDailyUsage"] } },
86
+ },
87
+ {
88
+ displayName: "Limit",
89
+ name: "topHostsLimit",
90
+ type: "number",
91
+ default: 10,
92
+ description: "Number of top hosts to return (1-100)",
93
+ displayOptions: { show: { operation: ["getTopHosts"] } },
94
+ },
65
95
  ],
66
96
  };
67
97
 
@@ -77,7 +107,8 @@ export class DominusNodeUsage implements INodeType {
77
107
  throw new NodeOperationError(this.getNode(), "API Key is required");
78
108
  }
79
109
 
80
- const auth = new DominusNodeAuth(apiKey, baseUrl);
110
+ const agentSecret = (credentials.agentSecret as string) || undefined;
111
+ const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
81
112
 
82
113
  for (let i = 0; i < items.length; i++) {
83
114
  try {
@@ -99,6 +130,28 @@ export class DominusNodeUsage implements INodeType {
99
130
  const params = new URLSearchParams({ since, until });
100
131
  const result = await auth.apiRequest("GET", `/api/usage?${params.toString()}`);
101
132
 
133
+ returnData.push({ json: (result ?? {}) as IDataObject });
134
+ } else if (operation === "getDailyUsage") {
135
+ const days = this.getNodeParameter("days", i, 7) as number;
136
+ if (!Number.isInteger(days) || days < 1 || days > 365) {
137
+ throw new NodeOperationError(
138
+ this.getNode(),
139
+ "Days must be an integer between 1 and 365",
140
+ { itemIndex: i },
141
+ );
142
+ }
143
+ const result = await auth.apiRequest("GET", `/api/usage/daily?days=${days}`);
144
+ returnData.push({ json: (result ?? {}) as IDataObject });
145
+ } else if (operation === "getTopHosts") {
146
+ const limit = this.getNodeParameter("topHostsLimit", i, 10) as number;
147
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
148
+ throw new NodeOperationError(
149
+ this.getNode(),
150
+ "Limit must be an integer between 1 and 100",
151
+ { itemIndex: i },
152
+ );
153
+ }
154
+ const result = await auth.apiRequest("GET", `/api/usage/top-hosts?limit=${limit}`);
102
155
  returnData.push({ json: (result ?? {}) as IDataObject });
103
156
  } else {
104
157
  throw new NodeOperationError(
@@ -1,11 +1,14 @@
1
1
  /**
2
2
  * Dominus Node Wallet n8n community node.
3
3
  *
4
- * Operations:
4
+ * Operations (25 tools):
5
5
  * - Check Balance: Get current wallet balance
6
6
  * - Top Up (Stripe): Create a Stripe checkout session
7
7
  * - Top Up (Crypto): Create a crypto payment invoice
8
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
9
12
  * - Create Agentic Wallet: Create a sub-wallet with spending limits
10
13
  * - Fund Agentic Wallet: Transfer funds to an agentic wallet
11
14
  * - Get Agentic Wallet Balance: Check an agentic wallet's balance
@@ -23,6 +26,7 @@
23
26
  * - Team Usage: Get team wallet transaction history
24
27
  * - Update Team: Update team name/max members
25
28
  * - Update Team Member Role: Update a team member's role
29
+ * - x402 Info: Get x402 micropayment protocol information
26
30
  *
27
31
  * @module
28
32
  */
@@ -70,6 +74,9 @@ export class DominusNodeWallet implements INodeType {
70
74
  { name: "Top Up (Stripe)", value: "topUpStripe", description: "Create a Stripe checkout session", action: "Top up stripe" },
71
75
  { name: "Top Up (Crypto)", value: "topUpCrypto", description: "Create a crypto payment invoice", action: "Top up crypto" },
72
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" },
73
80
  // Agentic Wallets
74
81
  { name: "Create Agentic Wallet", value: "createAgenticWallet", description: "Create a sub-wallet with spending limits", action: "Create agentic wallet" },
75
82
  { name: "Fund Agentic Wallet", value: "fundAgenticWallet", description: "Transfer funds to an agentic wallet", action: "Fund agentic wallet" },
@@ -137,6 +144,25 @@ export class DominusNodeWallet implements INodeType {
137
144
  displayOptions: { show: { operation: ["topUpCrypto"] } },
138
145
  },
139
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
+
140
166
  // --- Agentic wallet params ---
141
167
  {
142
168
  displayName: "Label",
@@ -343,7 +369,8 @@ export class DominusNodeWallet implements INodeType {
343
369
  throw new NodeOperationError(this.getNode(), "API Key is required");
344
370
  }
345
371
 
346
- const auth = new DominusNodeAuth(apiKey, baseUrl);
372
+ const agentSecret = (credentials.agentSecret as string) || undefined;
373
+ const auth = new DominusNodeAuth(apiKey, baseUrl, 30000, agentSecret);
347
374
  const operation = this.getNodeParameter("operation", 0) as string;
348
375
 
349
376
  for (let i = 0; i < items.length; i++) {
@@ -410,6 +437,31 @@ export class DominusNodeWallet implements INodeType {
410
437
  break;
411
438
  }
412
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
+
413
465
  // ----- Agentic Wallets -----
414
466
  case "createAgenticWallet": {
415
467
  const label = this.getNodeParameter("agenticLabel", i) as string;
@@ -156,14 +156,18 @@ export class DominusNodeAuth {
156
156
  private token: string | null = null;
157
157
  private authPromise: Promise<void> | null = null;
158
158
 
159
+ private agentSecret?: string;
160
+
159
161
  constructor(
160
162
  private apiKey: string,
161
163
  private baseUrl: string,
162
164
  private timeoutMs: number = 30000,
165
+ agentSecret?: string,
163
166
  ) {
164
167
  if (!apiKey || typeof apiKey !== "string") {
165
168
  throw new Error("apiKey is required and must be a non-empty string");
166
169
  }
170
+ this.agentSecret = agentSecret || process.env.DOMINUSNODE_AGENT_SECRET;
167
171
  }
168
172
 
169
173
  /**
@@ -202,6 +206,11 @@ export class DominusNodeAuth {
202
206
  Authorization: `Bearer ${this.token}`,
203
207
  };
204
208
 
209
+ if (this.agentSecret) {
210
+ headers["X-DominusNode-Agent"] = "mcp";
211
+ headers["X-DominusNode-Agent-Secret"] = this.agentSecret;
212
+ }
213
+
205
214
  const response = await fetch(url, {
206
215
  method,
207
216
  headers,
@@ -247,12 +256,18 @@ export class DominusNodeAuth {
247
256
  }
248
257
 
249
258
  private async authenticate(): Promise<void> {
259
+ const authHeaders: Record<string, string> = {
260
+ "User-Agent": "n8n-nodes-dominusnode/1.0.0",
261
+ "Content-Type": "application/json",
262
+ };
263
+ if (this.agentSecret) {
264
+ authHeaders["X-DominusNode-Agent"] = "mcp";
265
+ authHeaders["X-DominusNode-Agent-Secret"] = this.agentSecret;
266
+ }
267
+
250
268
  const response = await fetch(`${this.baseUrl}/api/auth/verify-key`, {
251
269
  method: "POST",
252
- headers: {
253
- "User-Agent": "n8n-nodes-dominusnode/1.0.0",
254
- "Content-Type": "application/json",
255
- },
270
+ headers: authHeaders,
256
271
  body: JSON.stringify({ apiKey: this.apiKey }),
257
272
  signal: AbortSignal.timeout(this.timeoutMs),
258
273
  redirect: "error",
@@ -76,10 +76,10 @@ describe("DominusNodeProxy", () => {
76
76
  ]);
77
77
  });
78
78
 
79
- it("has three operations", () => {
79
+ it("has four operations", () => {
80
80
  const opProp = node.description.properties.find((p) => p.name === "operation");
81
81
  expect(opProp).toBeDefined();
82
- expect((opProp as any).options).toHaveLength(3);
82
+ expect((opProp as any).options).toHaveLength(4);
83
83
  });
84
84
  });
85
85
 
@@ -95,11 +95,11 @@ describe("DominusNodeUsage - metadata", () => {
95
95
  ]);
96
96
  });
97
97
 
98
- it("has one operation", () => {
98
+ it("has three operations", () => {
99
99
  const node = new DominusNodeUsage();
100
100
  const opProp = node.description.properties.find((p) => p.name === "operation");
101
101
  expect(opProp).toBeDefined();
102
- expect((opProp as any).options).toHaveLength(1);
102
+ expect((opProp as any).options).toHaveLength(3);
103
103
  });
104
104
 
105
105
  it("has period options: day, week, month", () => {
@@ -93,11 +93,11 @@ describe("DominusNodeWallet - metadata", () => {
93
93
  ]);
94
94
  });
95
95
 
96
- it("has 22 operations", () => {
96
+ it("has 25 operations", () => {
97
97
  const node = new DominusNodeWallet();
98
98
  const opProp = node.description.properties.find((p) => p.name === "operation");
99
99
  expect(opProp).toBeDefined();
100
- expect((opProp as any).options).toHaveLength(22);
100
+ expect((opProp as any).options).toHaveLength(25);
101
101
  });
102
102
  });
103
103