@dominusnode/gemini-functions 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 CHANGED
@@ -6,40 +6,40 @@ Gemini-format function declarations and handler implementations for the Dominus
6
6
 
7
7
  This directory contains:
8
8
 
9
- | File | Description |
10
- |------|-------------|
11
- | `functions.json` | Array of 22 Gemini `FunctionDeclaration` objects |
12
- | `handler.ts` | TypeScript handler that dispatches function calls to the Dominus Node API |
13
- | `handler.py` | Python handler that dispatches function calls to the Dominus Node API |
9
+ | File | Description |
10
+ | ---------------- | ------------------------------------------------------------------------- |
11
+ | `functions.json` | Array of 22 Gemini `FunctionDeclaration` objects |
12
+ | `handler.ts` | TypeScript handler that dispatches function calls to the Dominus Node API |
13
+ | `handler.py` | Python handler that dispatches function calls to the Dominus Node API |
14
14
 
15
15
  The function declarations follow the [Gemini function calling specification](https://ai.google.dev/docs/function_calling), using uppercase types (`STRING`, `INTEGER`, `OBJECT`, `BOOLEAN`, `ARRAY`) and embedding constraints in description text rather than using JSON Schema keywords like `minimum`, `maximum`, `enum`, or `default`.
16
16
 
17
17
  ## Available Functions (22)
18
18
 
19
- | Function | Description | Auth Required |
20
- |----------|-------------|---------------|
21
- | `dominusnode_proxied_fetch` | Make an HTTP request through Dominus Node's rotating proxy network | Yes |
22
- | `dominusnode_check_balance` | Check wallet balance (cents, USD, currency) | Yes |
23
- | `dominusnode_check_usage` | Check usage stats for a time period (day/week/month) | Yes |
24
- | `dominusnode_get_proxy_config` | Get proxy endpoints, supported countries, geo-targeting info | Yes |
25
- | `dominusnode_list_sessions` | List currently active proxy sessions | Yes |
26
- | `dominusnode_create_agentic_wallet` | Create a sub-wallet for an AI agent with spending limits | Yes |
27
- | `dominusnode_fund_agentic_wallet` | Transfer funds from main wallet to an agentic wallet | Yes |
28
- | `dominusnode_agentic_wallet_balance` | Check an agentic wallet's balance and status | Yes |
29
- | `dominusnode_list_agentic_wallets` | List all agentic wallets | Yes |
30
- | `dominusnode_agentic_transactions` | Get agentic wallet transaction history | Yes |
31
- | `dominusnode_freeze_agentic_wallet` | Freeze an agentic wallet | Yes |
32
- | `dominusnode_unfreeze_agentic_wallet` | Unfreeze an agentic wallet | Yes |
33
- | `dominusnode_delete_agentic_wallet` | Delete an agentic wallet (refunds balance) | Yes |
34
- | `dominusnode_create_team` | Create a team for shared proxy billing | Yes |
35
- | `dominusnode_list_teams` | List teams you belong to | Yes |
36
- | `dominusnode_team_details` | Get team details | Yes |
37
- | `dominusnode_team_fund` | Fund a team wallet | Yes |
38
- | `dominusnode_team_create_key` | Create a shared team API key | Yes |
39
- | `dominusnode_team_usage` | Get team wallet transaction history | Yes |
40
- | `dominusnode_update_team` | Update team settings | Yes |
41
- | `dominusnode_update_team_member_role` | Update a team member's role | Yes |
42
- | `dominusnode_topup_paypal` | Create a PayPal wallet top-up session | Yes |
19
+ | Function | Description | Auth Required |
20
+ | ------------------------------------- | ------------------------------------------------------------------ | ------------- |
21
+ | `dominusnode_proxied_fetch` | Make an HTTP request through Dominus Node's rotating proxy network | Yes |
22
+ | `dominusnode_check_balance` | Check wallet balance (cents, USD, currency) | Yes |
23
+ | `dominusnode_check_usage` | Check usage stats for a time period (day/week/month) | Yes |
24
+ | `dominusnode_get_proxy_config` | Get proxy endpoints, supported countries, geo-targeting info | Yes |
25
+ | `dominusnode_list_sessions` | List currently active proxy sessions | Yes |
26
+ | `dominusnode_create_agentic_wallet` | Create a sub-wallet for an AI agent with spending limits | Yes |
27
+ | `dominusnode_fund_agentic_wallet` | Transfer funds from main wallet to an agentic wallet | Yes |
28
+ | `dominusnode_agentic_wallet_balance` | Check an agentic wallet's balance and status | Yes |
29
+ | `dominusnode_list_agentic_wallets` | List all agentic wallets | Yes |
30
+ | `dominusnode_agentic_transactions` | Get agentic wallet transaction history | Yes |
31
+ | `dominusnode_freeze_agentic_wallet` | Freeze an agentic wallet | Yes |
32
+ | `dominusnode_unfreeze_agentic_wallet` | Unfreeze an agentic wallet | Yes |
33
+ | `dominusnode_delete_agentic_wallet` | Delete an agentic wallet (refunds balance) | Yes |
34
+ | `dominusnode_create_team` | Create a team for shared proxy billing | Yes |
35
+ | `dominusnode_list_teams` | List teams you belong to | Yes |
36
+ | `dominusnode_team_details` | Get team details | Yes |
37
+ | `dominusnode_team_fund` | Fund a team wallet | Yes |
38
+ | `dominusnode_team_create_key` | Create a shared team API key | Yes |
39
+ | `dominusnode_team_usage` | Get team wallet transaction history | Yes |
40
+ | `dominusnode_update_team` | Update team settings | Yes |
41
+ | `dominusnode_update_team_member_role` | Update a team member's role | Yes |
42
+ | `dominusnode_topup_paypal` | Create a PayPal wallet top-up session | Yes |
43
43
 
44
44
  ## Usage with Google Gemini (Python)
45
45
 
@@ -154,7 +154,7 @@ console.log(JSON.parse(fetchResult));
154
154
 
155
155
  // Example: PayPal top-up
156
156
  const topup = await handler("dominusnode_topup_paypal", {
157
- amount_cents: 5000, // $50.00
157
+ amount_cents: 5000, // $50.00
158
158
  });
159
159
  console.log(JSON.parse(topup));
160
160
  ```
@@ -163,14 +163,14 @@ console.log(JSON.parse(topup));
163
163
 
164
164
  Gemini function declarations differ from OpenAI's JSON Schema format:
165
165
 
166
- | Feature | OpenAI/JSON Schema | Gemini |
167
- |---------|-------------------|--------|
168
- | Type names | `"string"`, `"integer"` | `"STRING"`, `"INTEGER"` |
169
- | `default` | Supported | Not supported -- moved to description |
170
- | `minimum`/`maximum` | Supported | Not supported -- moved to description |
171
- | `enum` | Supported | Not supported -- listed in description |
172
- | `minLength`/`maxLength` | Supported | Not supported -- moved to description |
173
- | `additionalProperties` | Supported | Not supported -- omitted |
166
+ | Feature | OpenAI/JSON Schema | Gemini |
167
+ | ----------------------- | ----------------------- | -------------------------------------- |
168
+ | Type names | `"string"`, `"integer"` | `"STRING"`, `"INTEGER"` |
169
+ | `default` | Supported | Not supported -- moved to description |
170
+ | `minimum`/`maximum` | Supported | Not supported -- moved to description |
171
+ | `enum` | Supported | Not supported -- listed in description |
172
+ | `minLength`/`maxLength` | Supported | Not supported -- moved to description |
173
+ | `additionalProperties` | Supported | Not supported -- omitted |
174
174
 
175
175
  All constraints are expressed in the `description` field text for Gemini compatibility.
176
176
 
@@ -207,18 +207,20 @@ API keys (`dn_live_*`, `dn_test_*`) are scrubbed from all error messages returne
207
207
 
208
208
  ## Proxy Pricing
209
209
 
210
- | Proxy Type | Price | Best For |
211
- |------------|-------|----------|
210
+ | Proxy Type | Price | Best For |
211
+ | ----------------- | -------- | -------------------------------------- |
212
212
  | Datacenter (`dc`) | $3.00/GB | General scraping, speed-critical tasks |
213
- | Residential | $5.00/GB | Anti-detection, geo-restricted content |
213
+ | Residential | $5.00/GB | Anti-detection, geo-restricted content |
214
214
 
215
215
  ## Dependencies
216
216
 
217
217
  ### TypeScript Handler
218
+
218
219
  - Node.js 18+ (uses native `fetch` and `AbortSignal.timeout`)
219
220
  - No external dependencies
220
221
 
221
222
  ### Python Handler
223
+
222
224
  - Python 3.8+
223
225
  - `httpx` (`pip install httpx`)
224
226
 
package/dist/handler.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Dominus Node Gemini / Vertex AI function calling handler (TypeScript).
3
3
  *
4
- * 53 tools for AI agents to interact with the Dominus Node REST API.
4
+ * 57 tools for AI agents to interact with the Dominus Node REST API.
5
5
  *
6
6
  * Provides a factory function that creates a handler for dispatching
7
7
  * Google Gemini function calls to the Dominus Node REST API. Works with
package/dist/handler.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Dominus Node Gemini / Vertex AI function calling handler (TypeScript).
3
3
  *
4
- * 53 tools for AI agents to interact with the Dominus Node REST API.
4
+ * 57 tools for AI agents to interact with the Dominus Node REST API.
5
5
  *
6
6
  * Provides a factory function that creates a handler for dispatching
7
7
  * Google Gemini function calls to the Dominus Node REST API. Works with
@@ -24,10 +24,63 @@
24
24
  *
25
25
  * @module
26
26
  */
27
+ import * as crypto from "node:crypto";
27
28
  import dns from "dns/promises";
28
29
  import * as http from "node:http";
29
30
  import * as tls from "node:tls";
30
31
  // ---------------------------------------------------------------------------
32
+ // SHA-256 Proof-of-Work solver
33
+ // ---------------------------------------------------------------------------
34
+ function countLeadingZeroBits(buf) {
35
+ let count = 0;
36
+ for (const byte of buf) {
37
+ if (byte === 0) {
38
+ count += 8;
39
+ continue;
40
+ }
41
+ let mask = 0x80;
42
+ while (mask && !(byte & mask)) {
43
+ count++;
44
+ mask >>= 1;
45
+ }
46
+ break;
47
+ }
48
+ return count;
49
+ }
50
+ async function solvePoW(baseUrl) {
51
+ try {
52
+ const resp = await fetch(`${baseUrl}/api/auth/pow/challenge`, {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/json" },
55
+ redirect: "error",
56
+ });
57
+ if (!resp.ok)
58
+ return null;
59
+ const text = await resp.text();
60
+ if (text.length > 10_485_760)
61
+ return null;
62
+ const challenge = JSON.parse(text);
63
+ const prefix = challenge.prefix ?? "";
64
+ const difficulty = challenge.difficulty ?? 20;
65
+ const challengeId = challenge.challengeId ?? "";
66
+ if (!prefix || !challengeId)
67
+ return null;
68
+ for (let nonce = 0; nonce < 100_000_000; nonce++) {
69
+ const hash = crypto
70
+ .createHash("sha256")
71
+ .update(prefix + nonce.toString())
72
+ .digest();
73
+ if (countLeadingZeroBits(hash) >= difficulty) {
74
+ return { challengeId, nonce: nonce.toString() };
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ // ---------------------------------------------------------------------------
31
84
  // SSRF Prevention -- URL validation
32
85
  // ---------------------------------------------------------------------------
33
86
  const BLOCKED_HOSTNAMES = new Set([
@@ -296,7 +349,7 @@ async function checkDnsRebinding(hostname) {
296
349
  // ---------------------------------------------------------------------------
297
350
  // Credential sanitization
298
351
  // ---------------------------------------------------------------------------
299
- const CREDENTIAL_RE = /dn_(live|test)_[a-zA-Z0-9]+/g;
352
+ const CREDENTIAL_RE = /dn_(live|test|proxy)_[a-zA-Z0-9]+/g;
300
353
  function sanitizeError(message) {
301
354
  return message.replace(CREDENTIAL_RE, "***");
302
355
  }
@@ -509,8 +562,13 @@ export function createDominusNodeFunctionHandler(config) {
509
562
  try {
510
563
  // Strip security-sensitive headers from user-provided headers
511
564
  const BLOCKED_HEADERS = new Set([
512
- "host", "connection", "content-length", "transfer-encoding",
513
- "proxy-authorization", "authorization", "user-agent",
565
+ "host",
566
+ "connection",
567
+ "content-length",
568
+ "transfer-encoding",
569
+ "proxy-authorization",
570
+ "authorization",
571
+ "user-agent",
514
572
  ]);
515
573
  const safeHeaders = {};
516
574
  if (headers) {
@@ -546,7 +604,10 @@ export function createDominusNodeFunctionHandler(config) {
546
604
  port: proxyPort,
547
605
  method: "CONNECT",
548
606
  path: `${parsed.hostname.includes(":") ? `[${parsed.hostname}]` : parsed.hostname}:${parsed.port || 443}`,
549
- headers: { "Proxy-Authorization": proxyAuth, Host: `${parsed.hostname.includes(":") ? `[${parsed.hostname}]` : parsed.hostname}:${parsed.port || 443}` },
607
+ headers: {
608
+ "Proxy-Authorization": proxyAuth,
609
+ Host: `${parsed.hostname.includes(":") ? `[${parsed.hostname}]` : parsed.hostname}:${parsed.port || 443}`,
610
+ },
550
611
  });
551
612
  connectReq.on("connect", (_res, tunnelSocket) => {
552
613
  if (_res.statusCode !== 200) {
@@ -590,7 +651,9 @@ export function createDominusNodeFunctionHandler(config) {
590
651
  return;
591
652
  }
592
653
  const headerSection = raw.substring(0, headerEnd);
593
- const body = raw.substring(headerEnd + 4).substring(0, MAX_BODY_BYTES);
654
+ const body = raw
655
+ .substring(headerEnd + 4)
656
+ .substring(0, MAX_BODY_BYTES);
594
657
  const statusLine = headerSection.split("\r\n")[0];
595
658
  const statusMatch = statusLine.match(/^HTTP\/\d\.\d\s+(\d+)/);
596
659
  const status = statusMatch ? parseInt(statusMatch[1], 10) : 0;
@@ -598,18 +661,29 @@ export function createDominusNodeFunctionHandler(config) {
598
661
  for (const line of headerSection.split("\r\n").slice(1)) {
599
662
  const ci = line.indexOf(":");
600
663
  if (ci > 0)
601
- headers[line.substring(0, ci).trim().toLowerCase()] = line.substring(ci + 1).trim();
664
+ headers[line.substring(0, ci).trim().toLowerCase()] = line
665
+ .substring(ci + 1)
666
+ .trim();
602
667
  }
603
668
  stripDangerousKeys(headers);
604
669
  resolve({ status, headers, body });
605
670
  };
606
671
  tlsSocket.on("end", finalize);
607
672
  tlsSocket.on("close", finalize);
608
- tlsSocket.on("error", (err) => { clearTimeout(timeout); reject(err); });
673
+ tlsSocket.on("error", (err) => {
674
+ clearTimeout(timeout);
675
+ reject(err);
676
+ });
677
+ });
678
+ tlsSocket.on("error", (err) => {
679
+ clearTimeout(timeout);
680
+ reject(err);
609
681
  });
610
- tlsSocket.on("error", (err) => { clearTimeout(timeout); reject(err); });
611
682
  });
612
- connectReq.on("error", (err) => { clearTimeout(timeout); reject(err); });
683
+ connectReq.on("error", (err) => {
684
+ clearTimeout(timeout);
685
+ reject(err);
686
+ });
613
687
  connectReq.end();
614
688
  }
615
689
  else {
@@ -619,19 +693,28 @@ export function createDominusNodeFunctionHandler(config) {
619
693
  port: proxyPort,
620
694
  method,
621
695
  path: url,
622
- headers: { ...safeHeaders, "Proxy-Authorization": proxyAuth, Host: parsed.host },
696
+ headers: {
697
+ ...safeHeaders,
698
+ "Proxy-Authorization": proxyAuth,
699
+ Host: parsed.host,
700
+ },
623
701
  }, (res) => {
624
702
  const chunks = [];
625
703
  let byteCount = 0;
626
704
  let httpFinalized = false;
627
- res.on("data", (chunk) => { byteCount += chunk.length; if (byteCount <= MAX_BODY_BYTES)
628
- chunks.push(chunk); });
705
+ res.on("data", (chunk) => {
706
+ byteCount += chunk.length;
707
+ if (byteCount <= MAX_BODY_BYTES)
708
+ chunks.push(chunk);
709
+ });
629
710
  const finalize = () => {
630
711
  if (httpFinalized)
631
712
  return;
632
713
  httpFinalized = true;
633
714
  clearTimeout(timeout);
634
- const body = Buffer.concat(chunks).toString("utf-8").substring(0, MAX_BODY_BYTES);
715
+ const body = Buffer.concat(chunks)
716
+ .toString("utf-8")
717
+ .substring(0, MAX_BODY_BYTES);
635
718
  const headers = {};
636
719
  for (const [k, v] of Object.entries(res.headers)) {
637
720
  if (v)
@@ -642,9 +725,15 @@ export function createDominusNodeFunctionHandler(config) {
642
725
  };
643
726
  res.on("end", finalize);
644
727
  res.on("close", finalize);
645
- res.on("error", (err) => { clearTimeout(timeout); reject(err); });
728
+ res.on("error", (err) => {
729
+ clearTimeout(timeout);
730
+ reject(err);
731
+ });
732
+ });
733
+ req.on("error", (err) => {
734
+ clearTimeout(timeout);
735
+ reject(err);
646
736
  });
647
- req.on("error", (err) => { clearTimeout(timeout); reject(err); });
648
737
  req.end();
649
738
  }
650
739
  });
@@ -684,13 +773,17 @@ export function createDominusNodeFunctionHandler(config) {
684
773
  const label = args.label;
685
774
  const spendingLimitCents = args.spending_limit_cents;
686
775
  if (!label || typeof label !== "string") {
687
- return JSON.stringify({ error: "label is required and must be a string" });
776
+ return JSON.stringify({
777
+ error: "label is required and must be a string",
778
+ });
688
779
  }
689
780
  if (label.length > 100) {
690
781
  return JSON.stringify({ error: "label must be 100 characters or fewer" });
691
782
  }
692
783
  if (/[\x00-\x1f\x7f]/.test(label)) {
693
- return JSON.stringify({ error: "label contains invalid control characters" });
784
+ return JSON.stringify({
785
+ error: "label contains invalid control characters",
786
+ });
694
787
  }
695
788
  if (!Number.isInteger(spendingLimitCents) ||
696
789
  spendingLimitCents <= 0 ||
@@ -704,9 +797,12 @@ export function createDominusNodeFunctionHandler(config) {
704
797
  spendingLimitCents,
705
798
  };
706
799
  // Validate optional daily_limit_cents
707
- if (args.daily_limit_cents !== undefined && args.daily_limit_cents !== null) {
800
+ if (args.daily_limit_cents !== undefined &&
801
+ args.daily_limit_cents !== null) {
708
802
  const dailyLimit = Number(args.daily_limit_cents);
709
- if (!Number.isInteger(dailyLimit) || dailyLimit < 1 || dailyLimit > 1_000_000) {
803
+ if (!Number.isInteger(dailyLimit) ||
804
+ dailyLimit < 1 ||
805
+ dailyLimit > 1_000_000) {
710
806
  return JSON.stringify({
711
807
  error: "daily_limit_cents must be a positive integer between 1 and 1,000,000",
712
808
  });
@@ -716,22 +812,32 @@ export function createDominusNodeFunctionHandler(config) {
716
812
  // Validate optional allowed_domains
717
813
  if (args.allowed_domains !== undefined && args.allowed_domains !== null) {
718
814
  if (!Array.isArray(args.allowed_domains)) {
719
- return JSON.stringify({ error: "allowed_domains must be an array of domain strings" });
815
+ return JSON.stringify({
816
+ error: "allowed_domains must be an array of domain strings",
817
+ });
720
818
  }
721
819
  if (args.allowed_domains.length > 100) {
722
- return JSON.stringify({ error: "allowed_domains must have at most 100 entries" });
820
+ return JSON.stringify({
821
+ error: "allowed_domains must have at most 100 entries",
822
+ });
723
823
  }
724
824
  const domainRe = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
725
825
  for (let i = 0; i < args.allowed_domains.length; i++) {
726
826
  const d = args.allowed_domains[i];
727
827
  if (typeof d !== "string") {
728
- return JSON.stringify({ error: `allowed_domains[${i}] must be a string` });
828
+ return JSON.stringify({
829
+ error: `allowed_domains[${i}] must be a string`,
830
+ });
729
831
  }
730
832
  if (d.length > 253) {
731
- return JSON.stringify({ error: `allowed_domains[${i}] exceeds 253 characters` });
833
+ return JSON.stringify({
834
+ error: `allowed_domains[${i}] exceeds 253 characters`,
835
+ });
732
836
  }
733
837
  if (!domainRe.test(d)) {
734
- return JSON.stringify({ error: `allowed_domains[${i}] is not a valid domain: ${d}` });
838
+ return JSON.stringify({
839
+ error: `allowed_domains[${i}] is not a valid domain: ${d}`,
840
+ });
735
841
  }
736
842
  }
737
843
  body.allowedDomains = args.allowed_domains;
@@ -743,7 +849,9 @@ export function createDominusNodeFunctionHandler(config) {
743
849
  const walletId = args.wallet_id;
744
850
  const amountCents = args.amount_cents;
745
851
  if (!walletId || typeof walletId !== "string") {
746
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
852
+ return JSON.stringify({
853
+ error: "wallet_id is required and must be a string",
854
+ });
747
855
  }
748
856
  if (!Number.isInteger(amountCents) ||
749
857
  amountCents <= 0 ||
@@ -758,7 +866,9 @@ export function createDominusNodeFunctionHandler(config) {
758
866
  async function handleAgenticWalletBalance(args) {
759
867
  const walletId = args.wallet_id;
760
868
  if (!walletId || typeof walletId !== "string") {
761
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
869
+ return JSON.stringify({
870
+ error: "wallet_id is required and must be a string",
871
+ });
762
872
  }
763
873
  const result = await api("GET", `/api/agent-wallet/${encodeURIComponent(walletId)}`);
764
874
  return JSON.stringify(result);
@@ -770,13 +880,17 @@ export function createDominusNodeFunctionHandler(config) {
770
880
  async function handleAgenticTransactions(args) {
771
881
  const walletId = args.wallet_id;
772
882
  if (!walletId || typeof walletId !== "string") {
773
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
883
+ return JSON.stringify({
884
+ error: "wallet_id is required and must be a string",
885
+ });
774
886
  }
775
887
  const limit = args.limit;
776
888
  const params = new URLSearchParams();
777
889
  if (limit !== undefined) {
778
890
  if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
779
- return JSON.stringify({ error: "limit must be an integer between 1 and 100" });
891
+ return JSON.stringify({
892
+ error: "limit must be an integer between 1 and 100",
893
+ });
780
894
  }
781
895
  params.set("limit", String(limit));
782
896
  }
@@ -787,7 +901,9 @@ export function createDominusNodeFunctionHandler(config) {
787
901
  async function handleFreezeAgenticWallet(args) {
788
902
  const walletId = args.wallet_id;
789
903
  if (!walletId || typeof walletId !== "string") {
790
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
904
+ return JSON.stringify({
905
+ error: "wallet_id is required and must be a string",
906
+ });
791
907
  }
792
908
  const result = await api("POST", `/api/agent-wallet/${encodeURIComponent(walletId)}/freeze`);
793
909
  return JSON.stringify(result);
@@ -795,7 +911,9 @@ export function createDominusNodeFunctionHandler(config) {
795
911
  async function handleUnfreezeAgenticWallet(args) {
796
912
  const walletId = args.wallet_id;
797
913
  if (!walletId || typeof walletId !== "string") {
798
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
914
+ return JSON.stringify({
915
+ error: "wallet_id is required and must be a string",
916
+ });
799
917
  }
800
918
  const result = await api("POST", `/api/agent-wallet/${encodeURIComponent(walletId)}/unfreeze`);
801
919
  return JSON.stringify(result);
@@ -803,7 +921,9 @@ export function createDominusNodeFunctionHandler(config) {
803
921
  async function handleDeleteAgenticWallet(args) {
804
922
  const walletId = args.wallet_id;
805
923
  if (!walletId || typeof walletId !== "string") {
806
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
924
+ return JSON.stringify({
925
+ error: "wallet_id is required and must be a string",
926
+ });
807
927
  }
808
928
  const result = await api("DELETE", `/api/agent-wallet/${encodeURIComponent(walletId)}`);
809
929
  return JSON.stringify(result);
@@ -817,13 +937,17 @@ export function createDominusNodeFunctionHandler(config) {
817
937
  return JSON.stringify({ error: "name must be 100 characters or fewer" });
818
938
  }
819
939
  if (/[\x00-\x1f\x7f]/.test(name)) {
820
- return JSON.stringify({ error: "name contains invalid control characters" });
940
+ return JSON.stringify({
941
+ error: "name contains invalid control characters",
942
+ });
821
943
  }
822
944
  const body = { name };
823
945
  if (args.max_members !== undefined) {
824
946
  const maxMembers = Number(args.max_members);
825
947
  if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
826
- return JSON.stringify({ error: "max_members must be an integer between 1 and 100" });
948
+ return JSON.stringify({
949
+ error: "max_members must be an integer between 1 and 100",
950
+ });
827
951
  }
828
952
  body.maxMembers = maxMembers;
829
953
  }
@@ -837,7 +961,9 @@ export function createDominusNodeFunctionHandler(config) {
837
961
  async function handleTeamDetails(args) {
838
962
  const teamId = args.team_id;
839
963
  if (!teamId || typeof teamId !== "string") {
840
- return JSON.stringify({ error: "team_id is required and must be a string" });
964
+ return JSON.stringify({
965
+ error: "team_id is required and must be a string",
966
+ });
841
967
  }
842
968
  const result = await api("GET", `/api/teams/${encodeURIComponent(teamId)}`);
843
969
  return JSON.stringify(result);
@@ -846,7 +972,9 @@ export function createDominusNodeFunctionHandler(config) {
846
972
  const teamId = args.team_id;
847
973
  const amountCents = args.amount_cents;
848
974
  if (!teamId || typeof teamId !== "string") {
849
- return JSON.stringify({ error: "team_id is required and must be a string" });
975
+ return JSON.stringify({
976
+ error: "team_id is required and must be a string",
977
+ });
850
978
  }
851
979
  if (!Number.isInteger(amountCents) ||
852
980
  amountCents < 100 ||
@@ -862,16 +990,22 @@ export function createDominusNodeFunctionHandler(config) {
862
990
  const teamId = args.team_id;
863
991
  const label = args.label;
864
992
  if (!teamId || typeof teamId !== "string") {
865
- return JSON.stringify({ error: "team_id is required and must be a string" });
993
+ return JSON.stringify({
994
+ error: "team_id is required and must be a string",
995
+ });
866
996
  }
867
997
  if (!label || typeof label !== "string") {
868
- return JSON.stringify({ error: "label is required and must be a string" });
998
+ return JSON.stringify({
999
+ error: "label is required and must be a string",
1000
+ });
869
1001
  }
870
1002
  if (label.length > 100) {
871
1003
  return JSON.stringify({ error: "label must be 100 characters or fewer" });
872
1004
  }
873
1005
  if (/[\x00-\x1f\x7f]/.test(label)) {
874
- return JSON.stringify({ error: "label contains invalid control characters" });
1006
+ return JSON.stringify({
1007
+ error: "label contains invalid control characters",
1008
+ });
875
1009
  }
876
1010
  const result = await api("POST", `/api/teams/${encodeURIComponent(teamId)}/keys`, { label });
877
1011
  return JSON.stringify(result);
@@ -879,13 +1013,17 @@ export function createDominusNodeFunctionHandler(config) {
879
1013
  async function handleTeamUsage(args) {
880
1014
  const teamId = args.team_id;
881
1015
  if (!teamId || typeof teamId !== "string") {
882
- return JSON.stringify({ error: "team_id is required and must be a string" });
1016
+ return JSON.stringify({
1017
+ error: "team_id is required and must be a string",
1018
+ });
883
1019
  }
884
1020
  const limit = args.limit;
885
1021
  const params = new URLSearchParams();
886
1022
  if (limit !== undefined) {
887
1023
  if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
888
- return JSON.stringify({ error: "limit must be an integer between 1 and 100" });
1024
+ return JSON.stringify({
1025
+ error: "limit must be an integer between 1 and 100",
1026
+ });
889
1027
  }
890
1028
  params.set("limit", String(limit));
891
1029
  }
@@ -896,7 +1034,9 @@ export function createDominusNodeFunctionHandler(config) {
896
1034
  async function handleUpdateTeam(args) {
897
1035
  const teamId = args.team_id;
898
1036
  if (!teamId || typeof teamId !== "string") {
899
- return JSON.stringify({ error: "team_id is required and must be a string" });
1037
+ return JSON.stringify({
1038
+ error: "team_id is required and must be a string",
1039
+ });
900
1040
  }
901
1041
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
902
1042
  return JSON.stringify({ error: "team_id must be a valid UUID" });
@@ -908,22 +1048,30 @@ export function createDominusNodeFunctionHandler(config) {
908
1048
  return JSON.stringify({ error: "name must be a non-empty string" });
909
1049
  }
910
1050
  if (name.length > 100) {
911
- return JSON.stringify({ error: "name must be 100 characters or fewer" });
1051
+ return JSON.stringify({
1052
+ error: "name must be 100 characters or fewer",
1053
+ });
912
1054
  }
913
1055
  if (/[\x00-\x1f\x7f]/.test(name)) {
914
- return JSON.stringify({ error: "name contains invalid control characters" });
1056
+ return JSON.stringify({
1057
+ error: "name contains invalid control characters",
1058
+ });
915
1059
  }
916
1060
  body.name = name;
917
1061
  }
918
1062
  if (args.max_members !== undefined) {
919
1063
  const maxMembers = Number(args.max_members);
920
1064
  if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
921
- return JSON.stringify({ error: "max_members must be an integer between 1 and 100" });
1065
+ return JSON.stringify({
1066
+ error: "max_members must be an integer between 1 and 100",
1067
+ });
922
1068
  }
923
1069
  body.maxMembers = maxMembers;
924
1070
  }
925
1071
  if (Object.keys(body).length === 0) {
926
- return JSON.stringify({ error: "At least one of name or max_members must be provided" });
1072
+ return JSON.stringify({
1073
+ error: "At least one of name or max_members must be provided",
1074
+ });
927
1075
  }
928
1076
  const result = await api("PATCH", `/api/teams/${encodeURIComponent(teamId)}`, body);
929
1077
  return JSON.stringify(result);
@@ -933,13 +1081,17 @@ export function createDominusNodeFunctionHandler(config) {
933
1081
  const userId = args.user_id;
934
1082
  const role = args.role;
935
1083
  if (!teamId || typeof teamId !== "string") {
936
- return JSON.stringify({ error: "team_id is required and must be a string" });
1084
+ return JSON.stringify({
1085
+ error: "team_id is required and must be a string",
1086
+ });
937
1087
  }
938
1088
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
939
1089
  return JSON.stringify({ error: "team_id must be a valid UUID" });
940
1090
  }
941
1091
  if (!userId || typeof userId !== "string") {
942
- return JSON.stringify({ error: "user_id is required and must be a string" });
1092
+ return JSON.stringify({
1093
+ error: "user_id is required and must be a string",
1094
+ });
943
1095
  }
944
1096
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
945
1097
  return JSON.stringify({ error: "user_id must be a valid UUID" });
@@ -962,7 +1114,9 @@ export function createDominusNodeFunctionHandler(config) {
962
1114
  error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
963
1115
  });
964
1116
  }
965
- const result = await api("POST", "/api/wallet/topup/paypal", { amountCents });
1117
+ const result = await api("POST", "/api/wallet/topup/paypal", {
1118
+ amountCents,
1119
+ });
966
1120
  return JSON.stringify(result);
967
1121
  }
968
1122
  async function handleTopupStripe(args) {
@@ -974,11 +1128,23 @@ export function createDominusNodeFunctionHandler(config) {
974
1128
  error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
975
1129
  });
976
1130
  }
977
- const result = await api("POST", "/api/wallet/topup/stripe", { amountCents });
1131
+ const result = await api("POST", "/api/wallet/topup/stripe", {
1132
+ amountCents,
1133
+ });
978
1134
  return JSON.stringify(result);
979
1135
  }
980
1136
  const VALID_CRYPTO_CURRENCIES = new Set([
981
- "btc", "eth", "ltc", "xmr", "zec", "usdc", "sol", "usdt", "dai", "bnb", "link",
1137
+ "btc",
1138
+ "eth",
1139
+ "ltc",
1140
+ "xmr",
1141
+ "zec",
1142
+ "usdc",
1143
+ "sol",
1144
+ "usdt",
1145
+ "dai",
1146
+ "bnb",
1147
+ "link",
982
1148
  ]);
983
1149
  async function handleTopupCrypto(args) {
984
1150
  const amountUsd = args.amount_usd;
@@ -1017,7 +1183,9 @@ export function createDominusNodeFunctionHandler(config) {
1017
1183
  if (args.limit !== undefined) {
1018
1184
  const limit = Number(args.limit);
1019
1185
  if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
1020
- return JSON.stringify({ error: "limit must be an integer between 1 and 100" });
1186
+ return JSON.stringify({
1187
+ error: "limit must be an integer between 1 and 100",
1188
+ });
1021
1189
  }
1022
1190
  params.set("limit", String(limit));
1023
1191
  }
@@ -1032,10 +1200,14 @@ export function createDominusNodeFunctionHandler(config) {
1032
1200
  async function handleCheckPayment(args) {
1033
1201
  const invoiceId = args.invoice_id;
1034
1202
  if (!invoiceId || typeof invoiceId !== "string") {
1035
- return JSON.stringify({ error: "invoice_id is required and must be a string" });
1203
+ return JSON.stringify({
1204
+ error: "invoice_id is required and must be a string",
1205
+ });
1036
1206
  }
1037
1207
  if (/[\x00-\x1f\x7f]/.test(invoiceId)) {
1038
- return JSON.stringify({ error: "invoice_id contains invalid control characters" });
1208
+ return JSON.stringify({
1209
+ error: "invoice_id contains invalid control characters",
1210
+ });
1039
1211
  }
1040
1212
  const result = await api("GET", `/api/wallet/topup/crypto/${encodeURIComponent(invoiceId)}/status`);
1041
1213
  return JSON.stringify(result);
@@ -1048,7 +1220,9 @@ export function createDominusNodeFunctionHandler(config) {
1048
1220
  if (args.days !== undefined) {
1049
1221
  const days = Number(args.days);
1050
1222
  if (!Number.isInteger(days) || days < 1 || days > 90) {
1051
- return JSON.stringify({ error: "days must be an integer between 1 and 90" });
1223
+ return JSON.stringify({
1224
+ error: "days must be an integer between 1 and 90",
1225
+ });
1052
1226
  }
1053
1227
  params.set("days", String(days));
1054
1228
  }
@@ -1061,7 +1235,9 @@ export function createDominusNodeFunctionHandler(config) {
1061
1235
  if (args.limit !== undefined) {
1062
1236
  const limit = Number(args.limit);
1063
1237
  if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
1064
- return JSON.stringify({ error: "limit must be an integer between 1 and 50" });
1238
+ return JSON.stringify({
1239
+ error: "limit must be an integer between 1 and 50",
1240
+ });
1065
1241
  }
1066
1242
  params.set("limit", String(limit));
1067
1243
  }
@@ -1076,19 +1252,27 @@ export function createDominusNodeFunctionHandler(config) {
1076
1252
  const email = args.email;
1077
1253
  const password = args.password;
1078
1254
  if (!email || typeof email !== "string") {
1079
- return JSON.stringify({ error: "email is required and must be a string" });
1255
+ return JSON.stringify({
1256
+ error: "email is required and must be a string",
1257
+ });
1080
1258
  }
1081
1259
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
1082
1260
  return JSON.stringify({ error: "email must be a valid email address" });
1083
1261
  }
1084
1262
  if (/[\x00-\x1f\x7f]/.test(email)) {
1085
- return JSON.stringify({ error: "email contains invalid control characters" });
1263
+ return JSON.stringify({
1264
+ error: "email contains invalid control characters",
1265
+ });
1086
1266
  }
1087
1267
  if (!password || typeof password !== "string") {
1088
- return JSON.stringify({ error: "password is required and must be a string" });
1268
+ return JSON.stringify({
1269
+ error: "password is required and must be a string",
1270
+ });
1089
1271
  }
1090
1272
  if (password.length < 8 || password.length > 128) {
1091
- return JSON.stringify({ error: "password must be between 8 and 128 characters" });
1273
+ return JSON.stringify({
1274
+ error: "password must be between 8 and 128 characters",
1275
+ });
1092
1276
  }
1093
1277
  try {
1094
1278
  const reqHeaders = {
@@ -1099,10 +1283,15 @@ export function createDominusNodeFunctionHandler(config) {
1099
1283
  reqHeaders["X-DominusNode-Agent"] = "mcp";
1100
1284
  reqHeaders["X-DominusNode-Agent-Secret"] = agentSecret;
1101
1285
  }
1286
+ // Solve PoW for CAPTCHA-free registration
1287
+ const pow = await solvePoW(baseUrl);
1288
+ const regBody = { email, password };
1289
+ if (pow)
1290
+ regBody.pow = pow;
1102
1291
  const response = await fetch(`${baseUrl}/api/auth/register`, {
1103
1292
  method: "POST",
1104
1293
  headers: reqHeaders,
1105
- body: JSON.stringify({ email, password }),
1294
+ body: JSON.stringify(regBody),
1106
1295
  signal: AbortSignal.timeout(timeoutMs),
1107
1296
  redirect: "error",
1108
1297
  });
@@ -1124,29 +1313,43 @@ export function createDominusNodeFunctionHandler(config) {
1124
1313
  return JSON.stringify({ error: sanitizeError(message) });
1125
1314
  }
1126
1315
  const data = safeJsonParse(responseText);
1127
- return JSON.stringify({ userId: data.userId, email: data.email, message: data.message ?? "Registration successful" });
1316
+ return JSON.stringify({
1317
+ userId: data.userId,
1318
+ email: data.email,
1319
+ message: data.message ?? "Registration successful",
1320
+ });
1128
1321
  }
1129
1322
  catch (err) {
1130
- return JSON.stringify({ error: sanitizeError(err instanceof Error ? err.message : String(err)) });
1323
+ return JSON.stringify({
1324
+ error: sanitizeError(err instanceof Error ? err.message : String(err)),
1325
+ });
1131
1326
  }
1132
1327
  }
1133
1328
  async function handleLogin(args) {
1134
1329
  const email = args.email;
1135
1330
  const password = args.password;
1136
1331
  if (!email || typeof email !== "string") {
1137
- return JSON.stringify({ error: "email is required and must be a string" });
1332
+ return JSON.stringify({
1333
+ error: "email is required and must be a string",
1334
+ });
1138
1335
  }
1139
1336
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
1140
1337
  return JSON.stringify({ error: "email must be a valid email address" });
1141
1338
  }
1142
1339
  if (/[\x00-\x1f\x7f]/.test(email)) {
1143
- return JSON.stringify({ error: "email contains invalid control characters" });
1340
+ return JSON.stringify({
1341
+ error: "email contains invalid control characters",
1342
+ });
1144
1343
  }
1145
1344
  if (!password || typeof password !== "string") {
1146
- return JSON.stringify({ error: "password is required and must be a string" });
1345
+ return JSON.stringify({
1346
+ error: "password is required and must be a string",
1347
+ });
1147
1348
  }
1148
1349
  if (password.length < 8 || password.length > 128) {
1149
- return JSON.stringify({ error: "password must be between 8 and 128 characters" });
1350
+ return JSON.stringify({
1351
+ error: "password must be between 8 and 128 characters",
1352
+ });
1150
1353
  }
1151
1354
  try {
1152
1355
  const reqHeaders = {
@@ -1182,10 +1385,15 @@ export function createDominusNodeFunctionHandler(config) {
1182
1385
  return JSON.stringify({ error: sanitizeError(message) });
1183
1386
  }
1184
1387
  const data = safeJsonParse(responseText);
1185
- return JSON.stringify({ token: data.token, message: data.message ?? "Login successful" });
1388
+ return JSON.stringify({
1389
+ token: data.token,
1390
+ message: data.message ?? "Login successful",
1391
+ });
1186
1392
  }
1187
1393
  catch (err) {
1188
- return JSON.stringify({ error: sanitizeError(err instanceof Error ? err.message : String(err)) });
1394
+ return JSON.stringify({
1395
+ error: sanitizeError(err instanceof Error ? err.message : String(err)),
1396
+ });
1189
1397
  }
1190
1398
  }
1191
1399
  async function handleGetAccountInfo() {
@@ -1195,10 +1403,14 @@ export function createDominusNodeFunctionHandler(config) {
1195
1403
  async function handleVerifyEmail(args) {
1196
1404
  const token = args.token;
1197
1405
  if (!token || typeof token !== "string") {
1198
- return JSON.stringify({ error: "token is required and must be a string" });
1406
+ return JSON.stringify({
1407
+ error: "token is required and must be a string",
1408
+ });
1199
1409
  }
1200
1410
  if (/[\x00-\x1f\x7f]/.test(token)) {
1201
- return JSON.stringify({ error: "token contains invalid control characters" });
1411
+ return JSON.stringify({
1412
+ error: "token contains invalid control characters",
1413
+ });
1202
1414
  }
1203
1415
  try {
1204
1416
  const reqHeaders = {
@@ -1237,7 +1449,9 @@ export function createDominusNodeFunctionHandler(config) {
1237
1449
  return JSON.stringify(data);
1238
1450
  }
1239
1451
  catch (err) {
1240
- return JSON.stringify({ error: sanitizeError(err instanceof Error ? err.message : String(err)) });
1452
+ return JSON.stringify({
1453
+ error: sanitizeError(err instanceof Error ? err.message : String(err)),
1454
+ });
1241
1455
  }
1242
1456
  }
1243
1457
  async function handleResendVerification() {
@@ -1248,16 +1462,24 @@ export function createDominusNodeFunctionHandler(config) {
1248
1462
  const currentPassword = args.current_password;
1249
1463
  const newPassword = args.new_password;
1250
1464
  if (!currentPassword || typeof currentPassword !== "string") {
1251
- return JSON.stringify({ error: "current_password is required and must be a string" });
1465
+ return JSON.stringify({
1466
+ error: "current_password is required and must be a string",
1467
+ });
1252
1468
  }
1253
1469
  if (currentPassword.length < 8 || currentPassword.length > 128) {
1254
- return JSON.stringify({ error: "current_password must be between 8 and 128 characters" });
1470
+ return JSON.stringify({
1471
+ error: "current_password must be between 8 and 128 characters",
1472
+ });
1255
1473
  }
1256
1474
  if (!newPassword || typeof newPassword !== "string") {
1257
- return JSON.stringify({ error: "new_password is required and must be a string" });
1475
+ return JSON.stringify({
1476
+ error: "new_password is required and must be a string",
1477
+ });
1258
1478
  }
1259
1479
  if (newPassword.length < 8 || newPassword.length > 128) {
1260
- return JSON.stringify({ error: "new_password must be between 8 and 128 characters" });
1480
+ return JSON.stringify({
1481
+ error: "new_password must be between 8 and 128 characters",
1482
+ });
1261
1483
  }
1262
1484
  const result = await api("POST", "/api/auth/change-password", {
1263
1485
  currentPassword,
@@ -1275,13 +1497,17 @@ export function createDominusNodeFunctionHandler(config) {
1275
1497
  async function handleCreateKey(args) {
1276
1498
  const label = args.label;
1277
1499
  if (!label || typeof label !== "string") {
1278
- return JSON.stringify({ error: "label is required and must be a string" });
1500
+ return JSON.stringify({
1501
+ error: "label is required and must be a string",
1502
+ });
1279
1503
  }
1280
1504
  if (label.length > 100) {
1281
1505
  return JSON.stringify({ error: "label must be 100 characters or fewer" });
1282
1506
  }
1283
1507
  if (/[\x00-\x1f\x7f]/.test(label)) {
1284
- return JSON.stringify({ error: "label contains invalid control characters" });
1508
+ return JSON.stringify({
1509
+ error: "label contains invalid control characters",
1510
+ });
1285
1511
  }
1286
1512
  const result = await api("POST", "/api/keys", { label });
1287
1513
  return JSON.stringify(result);
@@ -1289,10 +1515,14 @@ export function createDominusNodeFunctionHandler(config) {
1289
1515
  async function handleRevokeKey(args) {
1290
1516
  const keyId = args.key_id;
1291
1517
  if (!keyId || typeof keyId !== "string") {
1292
- return JSON.stringify({ error: "key_id is required and must be a string" });
1518
+ return JSON.stringify({
1519
+ error: "key_id is required and must be a string",
1520
+ });
1293
1521
  }
1294
1522
  if (/[\x00-\x1f\x7f]/.test(keyId)) {
1295
- return JSON.stringify({ error: "key_id contains invalid control characters" });
1523
+ return JSON.stringify({
1524
+ error: "key_id contains invalid control characters",
1525
+ });
1296
1526
  }
1297
1527
  const result = await api("DELETE", `/api/keys/${encodeURIComponent(keyId)}`);
1298
1528
  return JSON.stringify(result);
@@ -1311,10 +1541,14 @@ export function createDominusNodeFunctionHandler(config) {
1311
1541
  async function handleChangePlan(args) {
1312
1542
  const planId = args.plan_id;
1313
1543
  if (!planId || typeof planId !== "string") {
1314
- return JSON.stringify({ error: "plan_id is required and must be a string" });
1544
+ return JSON.stringify({
1545
+ error: "plan_id is required and must be a string",
1546
+ });
1315
1547
  }
1316
1548
  if (/[\x00-\x1f\x7f]/.test(planId)) {
1317
- return JSON.stringify({ error: "plan_id contains invalid control characters" });
1549
+ return JSON.stringify({
1550
+ error: "plan_id contains invalid control characters",
1551
+ });
1318
1552
  }
1319
1553
  const result = await api("PUT", "/api/plans/user/plan", { planId });
1320
1554
  return JSON.stringify(result);
@@ -1325,7 +1559,9 @@ export function createDominusNodeFunctionHandler(config) {
1325
1559
  async function handleTeamDelete(args) {
1326
1560
  const teamId = args.team_id;
1327
1561
  if (!teamId || typeof teamId !== "string") {
1328
- return JSON.stringify({ error: "team_id is required and must be a string" });
1562
+ return JSON.stringify({
1563
+ error: "team_id is required and must be a string",
1564
+ });
1329
1565
  }
1330
1566
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1331
1567
  return JSON.stringify({ error: "team_id must be a valid UUID" });
@@ -1337,16 +1573,22 @@ export function createDominusNodeFunctionHandler(config) {
1337
1573
  const teamId = args.team_id;
1338
1574
  const keyId = args.key_id;
1339
1575
  if (!teamId || typeof teamId !== "string") {
1340
- return JSON.stringify({ error: "team_id is required and must be a string" });
1576
+ return JSON.stringify({
1577
+ error: "team_id is required and must be a string",
1578
+ });
1341
1579
  }
1342
1580
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1343
1581
  return JSON.stringify({ error: "team_id must be a valid UUID" });
1344
1582
  }
1345
1583
  if (!keyId || typeof keyId !== "string") {
1346
- return JSON.stringify({ error: "key_id is required and must be a string" });
1584
+ return JSON.stringify({
1585
+ error: "key_id is required and must be a string",
1586
+ });
1347
1587
  }
1348
1588
  if (/[\x00-\x1f\x7f]/.test(keyId)) {
1349
- return JSON.stringify({ error: "key_id contains invalid control characters" });
1589
+ return JSON.stringify({
1590
+ error: "key_id contains invalid control characters",
1591
+ });
1350
1592
  }
1351
1593
  const result = await api("DELETE", `/api/teams/${encodeURIComponent(teamId)}/keys/${encodeURIComponent(keyId)}`);
1352
1594
  return JSON.stringify(result);
@@ -1354,7 +1596,9 @@ export function createDominusNodeFunctionHandler(config) {
1354
1596
  async function handleTeamListKeys(args) {
1355
1597
  const teamId = args.team_id;
1356
1598
  if (!teamId || typeof teamId !== "string") {
1357
- return JSON.stringify({ error: "team_id is required and must be a string" });
1599
+ return JSON.stringify({
1600
+ error: "team_id is required and must be a string",
1601
+ });
1358
1602
  }
1359
1603
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1360
1604
  return JSON.stringify({ error: "team_id must be a valid UUID" });
@@ -1365,7 +1609,9 @@ export function createDominusNodeFunctionHandler(config) {
1365
1609
  async function handleTeamListMembers(args) {
1366
1610
  const teamId = args.team_id;
1367
1611
  if (!teamId || typeof teamId !== "string") {
1368
- return JSON.stringify({ error: "team_id is required and must be a string" });
1612
+ return JSON.stringify({
1613
+ error: "team_id is required and must be a string",
1614
+ });
1369
1615
  }
1370
1616
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1371
1617
  return JSON.stringify({ error: "team_id must be a valid UUID" });
@@ -1377,13 +1623,17 @@ export function createDominusNodeFunctionHandler(config) {
1377
1623
  const teamId = args.team_id;
1378
1624
  const userId = args.user_id;
1379
1625
  if (!teamId || typeof teamId !== "string") {
1380
- return JSON.stringify({ error: "team_id is required and must be a string" });
1626
+ return JSON.stringify({
1627
+ error: "team_id is required and must be a string",
1628
+ });
1381
1629
  }
1382
1630
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1383
1631
  return JSON.stringify({ error: "team_id must be a valid UUID" });
1384
1632
  }
1385
1633
  if (!userId || typeof userId !== "string") {
1386
- return JSON.stringify({ error: "user_id is required and must be a string" });
1634
+ return JSON.stringify({
1635
+ error: "user_id is required and must be a string",
1636
+ });
1387
1637
  }
1388
1638
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
1389
1639
  return JSON.stringify({ error: "user_id must be a valid UUID" });
@@ -1395,13 +1645,17 @@ export function createDominusNodeFunctionHandler(config) {
1395
1645
  const teamId = args.team_id;
1396
1646
  const userId = args.user_id;
1397
1647
  if (!teamId || typeof teamId !== "string") {
1398
- return JSON.stringify({ error: "team_id is required and must be a string" });
1648
+ return JSON.stringify({
1649
+ error: "team_id is required and must be a string",
1650
+ });
1399
1651
  }
1400
1652
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1401
1653
  return JSON.stringify({ error: "team_id must be a valid UUID" });
1402
1654
  }
1403
1655
  if (!userId || typeof userId !== "string") {
1404
- return JSON.stringify({ error: "user_id is required and must be a string" });
1656
+ return JSON.stringify({
1657
+ error: "user_id is required and must be a string",
1658
+ });
1405
1659
  }
1406
1660
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
1407
1661
  return JSON.stringify({ error: "user_id must be a valid UUID" });
@@ -1414,19 +1668,25 @@ export function createDominusNodeFunctionHandler(config) {
1414
1668
  const email = args.email;
1415
1669
  const role = args.role ?? "member";
1416
1670
  if (!teamId || typeof teamId !== "string") {
1417
- return JSON.stringify({ error: "team_id is required and must be a string" });
1671
+ return JSON.stringify({
1672
+ error: "team_id is required and must be a string",
1673
+ });
1418
1674
  }
1419
1675
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1420
1676
  return JSON.stringify({ error: "team_id must be a valid UUID" });
1421
1677
  }
1422
1678
  if (!email || typeof email !== "string") {
1423
- return JSON.stringify({ error: "email is required and must be a string" });
1679
+ return JSON.stringify({
1680
+ error: "email is required and must be a string",
1681
+ });
1424
1682
  }
1425
1683
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
1426
1684
  return JSON.stringify({ error: "email must be a valid email address" });
1427
1685
  }
1428
1686
  if (/[\x00-\x1f\x7f]/.test(email)) {
1429
- return JSON.stringify({ error: "email contains invalid control characters" });
1687
+ return JSON.stringify({
1688
+ error: "email contains invalid control characters",
1689
+ });
1430
1690
  }
1431
1691
  if (role !== "member" && role !== "admin") {
1432
1692
  return JSON.stringify({ error: "role must be 'member' or 'admin'" });
@@ -1437,7 +1697,9 @@ export function createDominusNodeFunctionHandler(config) {
1437
1697
  async function handleTeamListInvites(args) {
1438
1698
  const teamId = args.team_id;
1439
1699
  if (!teamId || typeof teamId !== "string") {
1440
- return JSON.stringify({ error: "team_id is required and must be a string" });
1700
+ return JSON.stringify({
1701
+ error: "team_id is required and must be a string",
1702
+ });
1441
1703
  }
1442
1704
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1443
1705
  return JSON.stringify({ error: "team_id must be a valid UUID" });
@@ -1449,13 +1711,17 @@ export function createDominusNodeFunctionHandler(config) {
1449
1711
  const teamId = args.team_id;
1450
1712
  const inviteId = args.invite_id;
1451
1713
  if (!teamId || typeof teamId !== "string") {
1452
- return JSON.stringify({ error: "team_id is required and must be a string" });
1714
+ return JSON.stringify({
1715
+ error: "team_id is required and must be a string",
1716
+ });
1453
1717
  }
1454
1718
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
1455
1719
  return JSON.stringify({ error: "team_id must be a valid UUID" });
1456
1720
  }
1457
1721
  if (!inviteId || typeof inviteId !== "string") {
1458
- return JSON.stringify({ error: "invite_id is required and must be a string" });
1722
+ return JSON.stringify({
1723
+ error: "invite_id is required and must be a string",
1724
+ });
1459
1725
  }
1460
1726
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(inviteId)) {
1461
1727
  return JSON.stringify({ error: "invite_id must be a valid UUID" });
@@ -1520,11 +1786,19 @@ export function createDominusNodeFunctionHandler(config) {
1520
1786
  dominusnode_topup_crypto: handleTopupCrypto,
1521
1787
  dominusnode_x402_info: handleX402Info,
1522
1788
  dominusnode_update_wallet_policy: handleUpdateWalletPolicy,
1789
+ // MPP (5)
1790
+ dominusnode_mpp_info: handleMppInfo,
1791
+ dominusnode_mpp_challenge: handleMppChallenge,
1792
+ dominusnode_pay_mpp: handlePayMpp,
1793
+ dominusnode_mpp_session_open: handleMppSessionOpen,
1794
+ dominusnode_mpp_session_close: handleMppSessionClose,
1523
1795
  };
1524
1796
  async function handleUpdateWalletPolicy(args) {
1525
1797
  const walletId = args.wallet_id;
1526
1798
  if (!walletId || typeof walletId !== "string") {
1527
- return JSON.stringify({ error: "wallet_id is required and must be a string" });
1799
+ return JSON.stringify({
1800
+ error: "wallet_id is required and must be a string",
1801
+ });
1528
1802
  }
1529
1803
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(walletId)) {
1530
1804
  return JSON.stringify({ error: "wallet_id must be a valid UUID" });
@@ -1536,7 +1810,9 @@ export function createDominusNodeFunctionHandler(config) {
1536
1810
  }
1537
1811
  else {
1538
1812
  const dailyLimit = Number(args.daily_limit_cents);
1539
- if (!Number.isInteger(dailyLimit) || dailyLimit < 1 || dailyLimit > 1_000_000) {
1813
+ if (!Number.isInteger(dailyLimit) ||
1814
+ dailyLimit < 1 ||
1815
+ dailyLimit > 1_000_000) {
1540
1816
  return JSON.stringify({
1541
1817
  error: "daily_limit_cents must be a positive integer between 1 and 1,000,000, or null to clear",
1542
1818
  });
@@ -1550,22 +1826,32 @@ export function createDominusNodeFunctionHandler(config) {
1550
1826
  }
1551
1827
  else {
1552
1828
  if (!Array.isArray(args.allowed_domains)) {
1553
- return JSON.stringify({ error: "allowed_domains must be an array of domain strings, or null to clear" });
1829
+ return JSON.stringify({
1830
+ error: "allowed_domains must be an array of domain strings, or null to clear",
1831
+ });
1554
1832
  }
1555
1833
  if (args.allowed_domains.length > 100) {
1556
- return JSON.stringify({ error: "allowed_domains must have at most 100 entries" });
1834
+ return JSON.stringify({
1835
+ error: "allowed_domains must have at most 100 entries",
1836
+ });
1557
1837
  }
1558
1838
  const domainRe = /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
1559
1839
  for (let i = 0; i < args.allowed_domains.length; i++) {
1560
1840
  const d = args.allowed_domains[i];
1561
1841
  if (typeof d !== "string") {
1562
- return JSON.stringify({ error: `allowed_domains[${i}] must be a string` });
1842
+ return JSON.stringify({
1843
+ error: `allowed_domains[${i}] must be a string`,
1844
+ });
1563
1845
  }
1564
1846
  if (d.length > 253) {
1565
- return JSON.stringify({ error: `allowed_domains[${i}] exceeds 253 characters` });
1847
+ return JSON.stringify({
1848
+ error: `allowed_domains[${i}] exceeds 253 characters`,
1849
+ });
1566
1850
  }
1567
1851
  if (!domainRe.test(d)) {
1568
- return JSON.stringify({ error: `allowed_domains[${i}] is not a valid domain: ${d}` });
1852
+ return JSON.stringify({
1853
+ error: `allowed_domains[${i}] is not a valid domain: ${d}`,
1854
+ });
1569
1855
  }
1570
1856
  }
1571
1857
  body.allowedDomains = args.allowed_domains;
@@ -1584,6 +1870,121 @@ export function createDominusNodeFunctionHandler(config) {
1584
1870
  return JSON.stringify(result);
1585
1871
  }
1586
1872
  // -----------------------------------------------------------------------
1873
+ // MPP (Machine Payment Protocol) handlers
1874
+ // -----------------------------------------------------------------------
1875
+ async function handleMppInfo() {
1876
+ // MPP info is public (no auth required), but we use the authenticated path
1877
+ // for simplicity since the handler is already authenticated.
1878
+ const result = await api("GET", "/api/mpp/info");
1879
+ return JSON.stringify(result);
1880
+ }
1881
+ async function handleMppChallenge(args) {
1882
+ const poolType = String(args.pool_type ?? "dc");
1883
+ if (poolType !== "dc" && poolType !== "residential") {
1884
+ return JSON.stringify({
1885
+ error: "pool_type must be 'dc' or 'residential'",
1886
+ });
1887
+ }
1888
+ // This is a public endpoint -- no auth required
1889
+ try {
1890
+ const reqHeaders = {
1891
+ "User-Agent": "dominusnode-gemini/1.0.0",
1892
+ "Content-Type": "application/json",
1893
+ };
1894
+ if (agentSecret) {
1895
+ reqHeaders["X-DominusNode-Agent"] = "mcp";
1896
+ reqHeaders["X-DominusNode-Agent-Secret"] = agentSecret;
1897
+ }
1898
+ const response = await fetch(`${baseUrl}/api/mpp/challenge`, {
1899
+ method: "POST",
1900
+ headers: reqHeaders,
1901
+ body: JSON.stringify({ poolType }),
1902
+ signal: AbortSignal.timeout(timeoutMs),
1903
+ redirect: "error",
1904
+ });
1905
+ const responseText = await response.text();
1906
+ if (responseText.length > MAX_RESPONSE_BYTES) {
1907
+ return JSON.stringify({ error: "Response body exceeds size limit" });
1908
+ }
1909
+ if (!response.ok) {
1910
+ let message;
1911
+ try {
1912
+ const parsed = JSON.parse(responseText);
1913
+ message = parsed.error ?? parsed.message ?? responseText;
1914
+ }
1915
+ catch {
1916
+ message = responseText;
1917
+ }
1918
+ if (message.length > 500)
1919
+ message = message.slice(0, 500) + "... [truncated]";
1920
+ return JSON.stringify({
1921
+ error: `MPP challenge failed: ${sanitizeError(message)}`,
1922
+ });
1923
+ }
1924
+ const data = safeJsonParse(responseText);
1925
+ return JSON.stringify(data);
1926
+ }
1927
+ catch (err) {
1928
+ return JSON.stringify({
1929
+ error: `MPP challenge failed: ${sanitizeError(err instanceof Error ? err.message : String(err))}`,
1930
+ });
1931
+ }
1932
+ }
1933
+ async function handlePayMpp(args) {
1934
+ const amountCents = args.amount_cents;
1935
+ const method = String(args.method ?? "");
1936
+ if (!Number.isInteger(amountCents) ||
1937
+ amountCents < 500 ||
1938
+ amountCents > 100_000) {
1939
+ return JSON.stringify({
1940
+ error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
1941
+ });
1942
+ }
1943
+ if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
1944
+ return JSON.stringify({
1945
+ error: "method must be one of: tempo, stripe_spt, lightning",
1946
+ });
1947
+ }
1948
+ const result = await api("POST", "/api/mpp/topup", { amountCents, method });
1949
+ return JSON.stringify(result);
1950
+ }
1951
+ async function handleMppSessionOpen(args) {
1952
+ const maxDepositCents = args.max_deposit_cents;
1953
+ const method = String(args.method ?? "");
1954
+ const poolType = String(args.pool_type ?? "dc");
1955
+ if (!Number.isInteger(maxDepositCents) ||
1956
+ maxDepositCents < 500 ||
1957
+ maxDepositCents > 100_000) {
1958
+ return JSON.stringify({
1959
+ error: "max_deposit_cents must be an integer between 500 and 100,000",
1960
+ });
1961
+ }
1962
+ if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
1963
+ return JSON.stringify({
1964
+ error: "method must be one of: tempo, stripe_spt, lightning",
1965
+ });
1966
+ }
1967
+ if (!["dc", "residential"].includes(poolType)) {
1968
+ return JSON.stringify({
1969
+ error: "pool_type must be one of: dc, residential",
1970
+ });
1971
+ }
1972
+ const result = await api("POST", "/api/mpp/session/open", {
1973
+ maxDepositCents,
1974
+ method,
1975
+ poolType,
1976
+ });
1977
+ return JSON.stringify(result);
1978
+ }
1979
+ async function handleMppSessionClose(args) {
1980
+ const channelId = String(args.channel_id ?? "");
1981
+ if (!channelId) {
1982
+ return JSON.stringify({ error: "channel_id is required" });
1983
+ }
1984
+ const result = await api("POST", "/api/mpp/session/close", { channelId });
1985
+ return JSON.stringify(result);
1986
+ }
1987
+ // -----------------------------------------------------------------------
1587
1988
  // Main handler
1588
1989
  // -----------------------------------------------------------------------
1589
1990
  return async function handler(name, args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dominusnode/gemini-functions",
3
- "version": "1.2.0",
3
+ "version": "1.5.2",
4
4
  "description": "Dominus Node function declarations and handlers for Google Gemini / Vertex AI",
5
5
  "main": "dist/handler.js",
6
6
  "types": "dist/handler.d.ts",
@@ -13,6 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc",
16
+ "prepare": "npm run build",
16
17
  "test": "vitest run",
17
18
  "test:watch": "vitest",
18
19
  "test:py": "python -m pytest test_handler.py -v"