@dominusnode/openai-functions 1.1.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.
- package/README.md +32 -29
- package/dist/handler.d.ts +3 -0
- package/dist/handler.js +916 -62
- package/functions.json +13 -1
- package/package.json +3 -1
package/dist/handler.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dominus Node OpenAI-compatible function calling handler (TypeScript).
|
|
3
3
|
*
|
|
4
|
+
* 57 tools covering proxy, wallet, usage, account lifecycle, API keys,
|
|
5
|
+
* plans, and teams.
|
|
6
|
+
*
|
|
4
7
|
* Provides a factory function that creates a handler for dispatching
|
|
5
8
|
* OpenAI function calls to the Dominus Node REST API. Works with any
|
|
6
9
|
* function-calling LLM system: OpenAI GPT, Anthropic Claude tool_use,
|
|
@@ -22,10 +25,63 @@
|
|
|
22
25
|
*
|
|
23
26
|
* @module
|
|
24
27
|
*/
|
|
28
|
+
import * as crypto from "node:crypto";
|
|
25
29
|
import dns from "dns/promises";
|
|
26
30
|
import * as http from "node:http";
|
|
27
31
|
import * as tls from "node:tls";
|
|
28
32
|
// ---------------------------------------------------------------------------
|
|
33
|
+
// SHA-256 Proof-of-Work solver
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
function countLeadingZeroBits(buf) {
|
|
36
|
+
let count = 0;
|
|
37
|
+
for (const byte of buf) {
|
|
38
|
+
if (byte === 0) {
|
|
39
|
+
count += 8;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
let mask = 0x80;
|
|
43
|
+
while (mask && !(byte & mask)) {
|
|
44
|
+
count++;
|
|
45
|
+
mask >>= 1;
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return count;
|
|
50
|
+
}
|
|
51
|
+
async function solvePoW(baseUrl) {
|
|
52
|
+
try {
|
|
53
|
+
const resp = await fetch(`${baseUrl}/api/auth/pow/challenge`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
redirect: "error",
|
|
57
|
+
});
|
|
58
|
+
if (!resp.ok)
|
|
59
|
+
return null;
|
|
60
|
+
const text = await resp.text();
|
|
61
|
+
if (text.length > 10_485_760)
|
|
62
|
+
return null;
|
|
63
|
+
const challenge = JSON.parse(text);
|
|
64
|
+
const prefix = challenge.prefix ?? "";
|
|
65
|
+
const difficulty = challenge.difficulty ?? 20;
|
|
66
|
+
const challengeId = challenge.challengeId ?? "";
|
|
67
|
+
if (!prefix || !challengeId)
|
|
68
|
+
return null;
|
|
69
|
+
for (let nonce = 0; nonce < 100_000_000; nonce++) {
|
|
70
|
+
const hash = crypto
|
|
71
|
+
.createHash("sha256")
|
|
72
|
+
.update(prefix + nonce.toString())
|
|
73
|
+
.digest();
|
|
74
|
+
if (countLeadingZeroBits(hash) >= difficulty) {
|
|
75
|
+
return { challengeId, nonce: nonce.toString() };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
29
85
|
// SSRF Prevention -- URL validation
|
|
30
86
|
// ---------------------------------------------------------------------------
|
|
31
87
|
const BLOCKED_HOSTNAMES = new Set([
|
|
@@ -281,7 +337,7 @@ async function checkDnsRebinding(hostname) {
|
|
|
281
337
|
// ---------------------------------------------------------------------------
|
|
282
338
|
// Credential sanitization
|
|
283
339
|
// ---------------------------------------------------------------------------
|
|
284
|
-
const CREDENTIAL_RE = /dn_(live|test)_[a-zA-Z0-9]+/g;
|
|
340
|
+
const CREDENTIAL_RE = /dn_(live|test|proxy)_[a-zA-Z0-9]+/g;
|
|
285
341
|
function sanitizeError(message) {
|
|
286
342
|
return message.replace(CREDENTIAL_RE, "***");
|
|
287
343
|
}
|
|
@@ -501,8 +557,13 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
501
557
|
try {
|
|
502
558
|
// Strip security-sensitive headers from user-provided headers
|
|
503
559
|
const BLOCKED_HEADERS = new Set([
|
|
504
|
-
"host",
|
|
505
|
-
"
|
|
560
|
+
"host",
|
|
561
|
+
"connection",
|
|
562
|
+
"content-length",
|
|
563
|
+
"transfer-encoding",
|
|
564
|
+
"proxy-authorization",
|
|
565
|
+
"authorization",
|
|
566
|
+
"user-agent",
|
|
506
567
|
]);
|
|
507
568
|
const safeHeaders = {};
|
|
508
569
|
if (headers) {
|
|
@@ -533,13 +594,18 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
533
594
|
const timeout = setTimeout(() => reject(new Error("Proxy request timed out after 30000ms")), 30_000);
|
|
534
595
|
if (parsed.protocol === "https:") {
|
|
535
596
|
// HTTPS: CONNECT tunnel + TLS
|
|
536
|
-
const connectHost = parsed.hostname.includes(":")
|
|
597
|
+
const connectHost = parsed.hostname.includes(":")
|
|
598
|
+
? `[${parsed.hostname}]`
|
|
599
|
+
: parsed.hostname;
|
|
537
600
|
const connectReq = http.request({
|
|
538
601
|
hostname: proxyHost,
|
|
539
602
|
port: proxyPort,
|
|
540
603
|
method: "CONNECT",
|
|
541
604
|
path: `${connectHost}:${parsed.port || 443}`,
|
|
542
|
-
headers: {
|
|
605
|
+
headers: {
|
|
606
|
+
"Proxy-Authorization": proxyAuth,
|
|
607
|
+
Host: `${connectHost}:${parsed.port || 443}`,
|
|
608
|
+
},
|
|
543
609
|
});
|
|
544
610
|
connectReq.on("connect", (_res, tunnelSocket) => {
|
|
545
611
|
if (_res.statusCode !== 200) {
|
|
@@ -583,7 +649,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
583
649
|
return;
|
|
584
650
|
}
|
|
585
651
|
const headerSection = raw.substring(0, headerEnd);
|
|
586
|
-
const body = raw
|
|
652
|
+
const body = raw
|
|
653
|
+
.substring(headerEnd + 4)
|
|
654
|
+
.substring(0, MAX_BODY_BYTES);
|
|
587
655
|
const statusLine = headerSection.split("\r\n")[0];
|
|
588
656
|
const statusMatch = statusLine.match(/^HTTP\/\d\.\d\s+(\d+)/);
|
|
589
657
|
const status = statusMatch ? parseInt(statusMatch[1], 10) : 0;
|
|
@@ -591,18 +659,29 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
591
659
|
for (const line of headerSection.split("\r\n").slice(1)) {
|
|
592
660
|
const ci = line.indexOf(":");
|
|
593
661
|
if (ci > 0)
|
|
594
|
-
headers[line.substring(0, ci).trim().toLowerCase()] = line
|
|
662
|
+
headers[line.substring(0, ci).trim().toLowerCase()] = line
|
|
663
|
+
.substring(ci + 1)
|
|
664
|
+
.trim();
|
|
595
665
|
}
|
|
596
666
|
stripDangerousKeys(headers);
|
|
597
667
|
resolve({ status, headers, body });
|
|
598
668
|
};
|
|
599
669
|
tlsSocket.on("end", finalize);
|
|
600
670
|
tlsSocket.on("close", finalize);
|
|
601
|
-
tlsSocket.on("error", (err) => {
|
|
671
|
+
tlsSocket.on("error", (err) => {
|
|
672
|
+
clearTimeout(timeout);
|
|
673
|
+
reject(err);
|
|
674
|
+
});
|
|
602
675
|
});
|
|
603
|
-
tlsSocket.on("error", (err) => {
|
|
676
|
+
tlsSocket.on("error", (err) => {
|
|
677
|
+
clearTimeout(timeout);
|
|
678
|
+
reject(err);
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
connectReq.on("error", (err) => {
|
|
682
|
+
clearTimeout(timeout);
|
|
683
|
+
reject(err);
|
|
604
684
|
});
|
|
605
|
-
connectReq.on("error", (err) => { clearTimeout(timeout); reject(err); });
|
|
606
685
|
connectReq.end();
|
|
607
686
|
}
|
|
608
687
|
else {
|
|
@@ -612,19 +691,28 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
612
691
|
port: proxyPort,
|
|
613
692
|
method,
|
|
614
693
|
path: url,
|
|
615
|
-
headers: {
|
|
694
|
+
headers: {
|
|
695
|
+
...safeHeaders,
|
|
696
|
+
"Proxy-Authorization": proxyAuth,
|
|
697
|
+
Host: parsed.host,
|
|
698
|
+
},
|
|
616
699
|
}, (res) => {
|
|
617
700
|
const chunks = [];
|
|
618
701
|
let byteCount = 0;
|
|
619
|
-
res.on("data", (chunk) => {
|
|
620
|
-
|
|
702
|
+
res.on("data", (chunk) => {
|
|
703
|
+
byteCount += chunk.length;
|
|
704
|
+
if (byteCount <= MAX_BODY_BYTES)
|
|
705
|
+
chunks.push(chunk);
|
|
706
|
+
});
|
|
621
707
|
let httpFinalized = false;
|
|
622
708
|
const finalize = () => {
|
|
623
709
|
if (httpFinalized)
|
|
624
710
|
return;
|
|
625
711
|
httpFinalized = true;
|
|
626
712
|
clearTimeout(timeout);
|
|
627
|
-
const body = Buffer.concat(chunks)
|
|
713
|
+
const body = Buffer.concat(chunks)
|
|
714
|
+
.toString("utf-8")
|
|
715
|
+
.substring(0, MAX_BODY_BYTES);
|
|
628
716
|
const headers = {};
|
|
629
717
|
for (const [k, v] of Object.entries(res.headers)) {
|
|
630
718
|
if (v)
|
|
@@ -635,9 +723,15 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
635
723
|
};
|
|
636
724
|
res.on("end", finalize);
|
|
637
725
|
res.on("close", finalize);
|
|
638
|
-
res.on("error", (err) => {
|
|
726
|
+
res.on("error", (err) => {
|
|
727
|
+
clearTimeout(timeout);
|
|
728
|
+
reject(err);
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
req.on("error", (err) => {
|
|
732
|
+
clearTimeout(timeout);
|
|
733
|
+
reject(err);
|
|
639
734
|
});
|
|
640
|
-
req.on("error", (err) => { clearTimeout(timeout); reject(err); });
|
|
641
735
|
req.end();
|
|
642
736
|
}
|
|
643
737
|
});
|
|
@@ -677,13 +771,17 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
677
771
|
const label = args.label;
|
|
678
772
|
const spendingLimitCents = args.spending_limit_cents;
|
|
679
773
|
if (!label || typeof label !== "string") {
|
|
680
|
-
return JSON.stringify({
|
|
774
|
+
return JSON.stringify({
|
|
775
|
+
error: "label is required and must be a string",
|
|
776
|
+
});
|
|
681
777
|
}
|
|
682
778
|
if (label.length > 100) {
|
|
683
779
|
return JSON.stringify({ error: "label must be 100 characters or fewer" });
|
|
684
780
|
}
|
|
685
781
|
if (/[\x00-\x1f\x7f]/.test(label)) {
|
|
686
|
-
return JSON.stringify({
|
|
782
|
+
return JSON.stringify({
|
|
783
|
+
error: "label contains invalid control characters",
|
|
784
|
+
});
|
|
687
785
|
}
|
|
688
786
|
if (!Number.isInteger(spendingLimitCents) ||
|
|
689
787
|
spendingLimitCents <= 0 ||
|
|
@@ -699,7 +797,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
699
797
|
// Optional daily_limit_cents
|
|
700
798
|
if (args.daily_limit_cents !== undefined) {
|
|
701
799
|
const dailyLimit = args.daily_limit_cents;
|
|
702
|
-
if (!Number.isInteger(dailyLimit) ||
|
|
800
|
+
if (!Number.isInteger(dailyLimit) ||
|
|
801
|
+
dailyLimit < 1 ||
|
|
802
|
+
dailyLimit > 1_000_000) {
|
|
703
803
|
return JSON.stringify({
|
|
704
804
|
error: "daily_limit_cents must be an integer between 1 and 1,000,000",
|
|
705
805
|
});
|
|
@@ -710,18 +810,26 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
710
810
|
if (args.allowed_domains !== undefined) {
|
|
711
811
|
const domains = args.allowed_domains;
|
|
712
812
|
if (!Array.isArray(domains)) {
|
|
713
|
-
return JSON.stringify({
|
|
813
|
+
return JSON.stringify({
|
|
814
|
+
error: "allowed_domains must be an array of strings",
|
|
815
|
+
});
|
|
714
816
|
}
|
|
715
817
|
if (domains.length > 100) {
|
|
716
|
-
return JSON.stringify({
|
|
818
|
+
return JSON.stringify({
|
|
819
|
+
error: "allowed_domains must have 100 or fewer entries",
|
|
820
|
+
});
|
|
717
821
|
}
|
|
718
822
|
const domainRe = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
|
719
823
|
for (const d of domains) {
|
|
720
824
|
if (typeof d !== "string") {
|
|
721
|
-
return JSON.stringify({
|
|
825
|
+
return JSON.stringify({
|
|
826
|
+
error: "Each allowed_domains entry must be a string",
|
|
827
|
+
});
|
|
722
828
|
}
|
|
723
829
|
if (d.length > 253) {
|
|
724
|
-
return JSON.stringify({
|
|
830
|
+
return JSON.stringify({
|
|
831
|
+
error: "Each allowed_domains entry must be 253 characters or fewer",
|
|
832
|
+
});
|
|
725
833
|
}
|
|
726
834
|
if (!domainRe.test(d)) {
|
|
727
835
|
return JSON.stringify({ error: `Invalid domain format: ${d}` });
|
|
@@ -736,7 +844,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
736
844
|
const walletId = args.wallet_id;
|
|
737
845
|
const amountCents = args.amount_cents;
|
|
738
846
|
if (!walletId || typeof walletId !== "string") {
|
|
739
|
-
return JSON.stringify({
|
|
847
|
+
return JSON.stringify({
|
|
848
|
+
error: "wallet_id is required and must be a string",
|
|
849
|
+
});
|
|
740
850
|
}
|
|
741
851
|
if (!Number.isInteger(amountCents) ||
|
|
742
852
|
amountCents <= 0 ||
|
|
@@ -751,7 +861,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
751
861
|
async function handleAgenticWalletBalance(args) {
|
|
752
862
|
const walletId = args.wallet_id;
|
|
753
863
|
if (!walletId || typeof walletId !== "string") {
|
|
754
|
-
return JSON.stringify({
|
|
864
|
+
return JSON.stringify({
|
|
865
|
+
error: "wallet_id is required and must be a string",
|
|
866
|
+
});
|
|
755
867
|
}
|
|
756
868
|
const result = await api("GET", `/api/agent-wallet/${encodeURIComponent(walletId)}`);
|
|
757
869
|
return JSON.stringify(result);
|
|
@@ -763,13 +875,17 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
763
875
|
async function handleAgenticTransactions(args) {
|
|
764
876
|
const walletId = args.wallet_id;
|
|
765
877
|
if (!walletId || typeof walletId !== "string") {
|
|
766
|
-
return JSON.stringify({
|
|
878
|
+
return JSON.stringify({
|
|
879
|
+
error: "wallet_id is required and must be a string",
|
|
880
|
+
});
|
|
767
881
|
}
|
|
768
882
|
const limit = args.limit;
|
|
769
883
|
const params = new URLSearchParams();
|
|
770
884
|
if (limit !== undefined) {
|
|
771
885
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
772
|
-
return JSON.stringify({
|
|
886
|
+
return JSON.stringify({
|
|
887
|
+
error: "limit must be an integer between 1 and 100",
|
|
888
|
+
});
|
|
773
889
|
}
|
|
774
890
|
params.set("limit", String(limit));
|
|
775
891
|
}
|
|
@@ -780,7 +896,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
780
896
|
async function handleFreezeAgenticWallet(args) {
|
|
781
897
|
const walletId = args.wallet_id;
|
|
782
898
|
if (!walletId || typeof walletId !== "string") {
|
|
783
|
-
return JSON.stringify({
|
|
899
|
+
return JSON.stringify({
|
|
900
|
+
error: "wallet_id is required and must be a string",
|
|
901
|
+
});
|
|
784
902
|
}
|
|
785
903
|
const result = await api("POST", `/api/agent-wallet/${encodeURIComponent(walletId)}/freeze`);
|
|
786
904
|
return JSON.stringify(result);
|
|
@@ -788,7 +906,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
788
906
|
async function handleUnfreezeAgenticWallet(args) {
|
|
789
907
|
const walletId = args.wallet_id;
|
|
790
908
|
if (!walletId || typeof walletId !== "string") {
|
|
791
|
-
return JSON.stringify({
|
|
909
|
+
return JSON.stringify({
|
|
910
|
+
error: "wallet_id is required and must be a string",
|
|
911
|
+
});
|
|
792
912
|
}
|
|
793
913
|
const result = await api("POST", `/api/agent-wallet/${encodeURIComponent(walletId)}/unfreeze`);
|
|
794
914
|
return JSON.stringify(result);
|
|
@@ -796,7 +916,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
796
916
|
async function handleDeleteAgenticWallet(args) {
|
|
797
917
|
const walletId = args.wallet_id;
|
|
798
918
|
if (!walletId || typeof walletId !== "string") {
|
|
799
|
-
return JSON.stringify({
|
|
919
|
+
return JSON.stringify({
|
|
920
|
+
error: "wallet_id is required and must be a string",
|
|
921
|
+
});
|
|
800
922
|
}
|
|
801
923
|
const result = await api("DELETE", `/api/agent-wallet/${encodeURIComponent(walletId)}`);
|
|
802
924
|
return JSON.stringify(result);
|
|
@@ -810,13 +932,17 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
810
932
|
return JSON.stringify({ error: "name must be 100 characters or fewer" });
|
|
811
933
|
}
|
|
812
934
|
if (/[\x00-\x1f\x7f]/.test(name)) {
|
|
813
|
-
return JSON.stringify({
|
|
935
|
+
return JSON.stringify({
|
|
936
|
+
error: "name contains invalid control characters",
|
|
937
|
+
});
|
|
814
938
|
}
|
|
815
939
|
const body = { name };
|
|
816
940
|
if (args.max_members !== undefined) {
|
|
817
941
|
const maxMembers = Number(args.max_members);
|
|
818
942
|
if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
|
|
819
|
-
return JSON.stringify({
|
|
943
|
+
return JSON.stringify({
|
|
944
|
+
error: "max_members must be an integer between 1 and 100",
|
|
945
|
+
});
|
|
820
946
|
}
|
|
821
947
|
body.maxMembers = maxMembers;
|
|
822
948
|
}
|
|
@@ -830,7 +956,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
830
956
|
async function handleTeamDetails(args) {
|
|
831
957
|
const teamId = args.team_id;
|
|
832
958
|
if (!teamId || typeof teamId !== "string") {
|
|
833
|
-
return JSON.stringify({
|
|
959
|
+
return JSON.stringify({
|
|
960
|
+
error: "team_id is required and must be a string",
|
|
961
|
+
});
|
|
834
962
|
}
|
|
835
963
|
const result = await api("GET", `/api/teams/${encodeURIComponent(teamId)}`);
|
|
836
964
|
return JSON.stringify(result);
|
|
@@ -839,7 +967,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
839
967
|
const teamId = args.team_id;
|
|
840
968
|
const amountCents = args.amount_cents;
|
|
841
969
|
if (!teamId || typeof teamId !== "string") {
|
|
842
|
-
return JSON.stringify({
|
|
970
|
+
return JSON.stringify({
|
|
971
|
+
error: "team_id is required and must be a string",
|
|
972
|
+
});
|
|
843
973
|
}
|
|
844
974
|
if (!Number.isInteger(amountCents) ||
|
|
845
975
|
amountCents < 100 ||
|
|
@@ -855,16 +985,22 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
855
985
|
const teamId = args.team_id;
|
|
856
986
|
const label = args.label;
|
|
857
987
|
if (!teamId || typeof teamId !== "string") {
|
|
858
|
-
return JSON.stringify({
|
|
988
|
+
return JSON.stringify({
|
|
989
|
+
error: "team_id is required and must be a string",
|
|
990
|
+
});
|
|
859
991
|
}
|
|
860
992
|
if (!label || typeof label !== "string") {
|
|
861
|
-
return JSON.stringify({
|
|
993
|
+
return JSON.stringify({
|
|
994
|
+
error: "label is required and must be a string",
|
|
995
|
+
});
|
|
862
996
|
}
|
|
863
997
|
if (label.length > 100) {
|
|
864
998
|
return JSON.stringify({ error: "label must be 100 characters or fewer" });
|
|
865
999
|
}
|
|
866
1000
|
if (/[\x00-\x1f\x7f]/.test(label)) {
|
|
867
|
-
return JSON.stringify({
|
|
1001
|
+
return JSON.stringify({
|
|
1002
|
+
error: "label contains invalid control characters",
|
|
1003
|
+
});
|
|
868
1004
|
}
|
|
869
1005
|
const result = await api("POST", `/api/teams/${encodeURIComponent(teamId)}/keys`, { label });
|
|
870
1006
|
return JSON.stringify(result);
|
|
@@ -872,13 +1008,17 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
872
1008
|
async function handleTeamUsage(args) {
|
|
873
1009
|
const teamId = args.team_id;
|
|
874
1010
|
if (!teamId || typeof teamId !== "string") {
|
|
875
|
-
return JSON.stringify({
|
|
1011
|
+
return JSON.stringify({
|
|
1012
|
+
error: "team_id is required and must be a string",
|
|
1013
|
+
});
|
|
876
1014
|
}
|
|
877
1015
|
const limit = args.limit;
|
|
878
1016
|
const params = new URLSearchParams();
|
|
879
1017
|
if (limit !== undefined) {
|
|
880
1018
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
881
|
-
return JSON.stringify({
|
|
1019
|
+
return JSON.stringify({
|
|
1020
|
+
error: "limit must be an integer between 1 and 100",
|
|
1021
|
+
});
|
|
882
1022
|
}
|
|
883
1023
|
params.set("limit", String(limit));
|
|
884
1024
|
}
|
|
@@ -889,7 +1029,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
889
1029
|
async function handleUpdateTeam(args) {
|
|
890
1030
|
const teamId = args.team_id;
|
|
891
1031
|
if (!teamId || typeof teamId !== "string") {
|
|
892
|
-
return JSON.stringify({
|
|
1032
|
+
return JSON.stringify({
|
|
1033
|
+
error: "team_id is required and must be a string",
|
|
1034
|
+
});
|
|
893
1035
|
}
|
|
894
1036
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
895
1037
|
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
@@ -901,22 +1043,30 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
901
1043
|
return JSON.stringify({ error: "name must be a non-empty string" });
|
|
902
1044
|
}
|
|
903
1045
|
if (name.length > 100) {
|
|
904
|
-
return JSON.stringify({
|
|
1046
|
+
return JSON.stringify({
|
|
1047
|
+
error: "name must be 100 characters or fewer",
|
|
1048
|
+
});
|
|
905
1049
|
}
|
|
906
1050
|
if (/[\x00-\x1f\x7f]/.test(name)) {
|
|
907
|
-
return JSON.stringify({
|
|
1051
|
+
return JSON.stringify({
|
|
1052
|
+
error: "name contains invalid control characters",
|
|
1053
|
+
});
|
|
908
1054
|
}
|
|
909
1055
|
body.name = name;
|
|
910
1056
|
}
|
|
911
1057
|
if (args.max_members !== undefined) {
|
|
912
1058
|
const maxMembers = Number(args.max_members);
|
|
913
1059
|
if (!Number.isInteger(maxMembers) || maxMembers < 1 || maxMembers > 100) {
|
|
914
|
-
return JSON.stringify({
|
|
1060
|
+
return JSON.stringify({
|
|
1061
|
+
error: "max_members must be an integer between 1 and 100",
|
|
1062
|
+
});
|
|
915
1063
|
}
|
|
916
1064
|
body.maxMembers = maxMembers;
|
|
917
1065
|
}
|
|
918
1066
|
if (Object.keys(body).length === 0) {
|
|
919
|
-
return JSON.stringify({
|
|
1067
|
+
return JSON.stringify({
|
|
1068
|
+
error: "At least one of name or max_members must be provided",
|
|
1069
|
+
});
|
|
920
1070
|
}
|
|
921
1071
|
const result = await api("PATCH", `/api/teams/${encodeURIComponent(teamId)}`, body);
|
|
922
1072
|
return JSON.stringify(result);
|
|
@@ -930,7 +1080,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
930
1080
|
error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
|
|
931
1081
|
});
|
|
932
1082
|
}
|
|
933
|
-
const result = await api("POST", "/api/wallet/topup/paypal", {
|
|
1083
|
+
const result = await api("POST", "/api/wallet/topup/paypal", {
|
|
1084
|
+
amountCents,
|
|
1085
|
+
});
|
|
934
1086
|
return JSON.stringify(result);
|
|
935
1087
|
}
|
|
936
1088
|
async function handleTopupStripe(args) {
|
|
@@ -942,21 +1094,38 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
942
1094
|
error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
|
|
943
1095
|
});
|
|
944
1096
|
}
|
|
945
|
-
const result = await api("POST", "/api/wallet/topup/stripe", {
|
|
1097
|
+
const result = await api("POST", "/api/wallet/topup/stripe", {
|
|
1098
|
+
amountCents,
|
|
1099
|
+
});
|
|
946
1100
|
return JSON.stringify(result);
|
|
947
1101
|
}
|
|
948
1102
|
async function handleTopupCrypto(args) {
|
|
949
1103
|
const amountUsd = args.amount_usd;
|
|
950
1104
|
const currency = args.currency;
|
|
951
|
-
if (typeof amountUsd !== "number" ||
|
|
1105
|
+
if (typeof amountUsd !== "number" ||
|
|
1106
|
+
!Number.isFinite(amountUsd) ||
|
|
1107
|
+
amountUsd < 5 ||
|
|
1108
|
+
amountUsd > 1000) {
|
|
952
1109
|
return JSON.stringify({
|
|
953
1110
|
error: "amount_usd must be a number between 5 and 1,000",
|
|
954
1111
|
});
|
|
955
1112
|
}
|
|
956
1113
|
const validCurrencies = new Set([
|
|
957
|
-
"BTC",
|
|
1114
|
+
"BTC",
|
|
1115
|
+
"ETH",
|
|
1116
|
+
"LTC",
|
|
1117
|
+
"XMR",
|
|
1118
|
+
"ZEC",
|
|
1119
|
+
"USDC",
|
|
1120
|
+
"SOL",
|
|
1121
|
+
"USDT",
|
|
1122
|
+
"DAI",
|
|
1123
|
+
"BNB",
|
|
1124
|
+
"LINK",
|
|
958
1125
|
]);
|
|
959
|
-
if (!currency ||
|
|
1126
|
+
if (!currency ||
|
|
1127
|
+
typeof currency !== "string" ||
|
|
1128
|
+
!validCurrencies.has(currency.toUpperCase())) {
|
|
960
1129
|
return JSON.stringify({
|
|
961
1130
|
error: "currency must be one of: BTC, ETH, LTC, XMR, ZEC, USDC, SOL, USDT, DAI, BNB, LINK",
|
|
962
1131
|
});
|
|
@@ -970,7 +1139,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
970
1139
|
async function handleUpdateWalletPolicy(args) {
|
|
971
1140
|
const walletId = args.wallet_id;
|
|
972
1141
|
if (!walletId || typeof walletId !== "string") {
|
|
973
|
-
return JSON.stringify({
|
|
1142
|
+
return JSON.stringify({
|
|
1143
|
+
error: "wallet_id is required and must be a string",
|
|
1144
|
+
});
|
|
974
1145
|
}
|
|
975
1146
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(walletId)) {
|
|
976
1147
|
return JSON.stringify({ error: "wallet_id must be a valid UUID" });
|
|
@@ -983,7 +1154,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
983
1154
|
}
|
|
984
1155
|
else {
|
|
985
1156
|
const dailyLimit = args.daily_limit_cents;
|
|
986
|
-
if (!Number.isInteger(dailyLimit) ||
|
|
1157
|
+
if (!Number.isInteger(dailyLimit) ||
|
|
1158
|
+
dailyLimit < 1 ||
|
|
1159
|
+
dailyLimit > 1_000_000) {
|
|
987
1160
|
return JSON.stringify({
|
|
988
1161
|
error: "daily_limit_cents must be an integer between 1 and 1,000,000 (or null to remove)",
|
|
989
1162
|
});
|
|
@@ -999,18 +1172,26 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
999
1172
|
else {
|
|
1000
1173
|
const domains = args.allowed_domains;
|
|
1001
1174
|
if (!Array.isArray(domains)) {
|
|
1002
|
-
return JSON.stringify({
|
|
1175
|
+
return JSON.stringify({
|
|
1176
|
+
error: "allowed_domains must be an array of strings or null",
|
|
1177
|
+
});
|
|
1003
1178
|
}
|
|
1004
1179
|
if (domains.length > 100) {
|
|
1005
|
-
return JSON.stringify({
|
|
1180
|
+
return JSON.stringify({
|
|
1181
|
+
error: "allowed_domains must have 100 or fewer entries",
|
|
1182
|
+
});
|
|
1006
1183
|
}
|
|
1007
1184
|
const domainRe = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
|
|
1008
1185
|
for (const d of domains) {
|
|
1009
1186
|
if (typeof d !== "string") {
|
|
1010
|
-
return JSON.stringify({
|
|
1187
|
+
return JSON.stringify({
|
|
1188
|
+
error: "Each allowed_domains entry must be a string",
|
|
1189
|
+
});
|
|
1011
1190
|
}
|
|
1012
1191
|
if (d.length > 253) {
|
|
1013
|
-
return JSON.stringify({
|
|
1192
|
+
return JSON.stringify({
|
|
1193
|
+
error: "Each allowed_domains entry must be 253 characters or fewer",
|
|
1194
|
+
});
|
|
1014
1195
|
}
|
|
1015
1196
|
if (!domainRe.test(d)) {
|
|
1016
1197
|
return JSON.stringify({ error: `Invalid domain format: ${d}` });
|
|
@@ -1020,7 +1201,9 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
1020
1201
|
}
|
|
1021
1202
|
}
|
|
1022
1203
|
if (Object.keys(body).length === 0) {
|
|
1023
|
-
return JSON.stringify({
|
|
1204
|
+
return JSON.stringify({
|
|
1205
|
+
error: "At least one of daily_limit_cents or allowed_domains must be provided",
|
|
1206
|
+
});
|
|
1024
1207
|
}
|
|
1025
1208
|
const result = await api("PATCH", `/api/agent-wallet/${encodeURIComponent(walletId)}/policy`, body);
|
|
1026
1209
|
return JSON.stringify(result);
|
|
@@ -1030,13 +1213,17 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
1030
1213
|
const userId = args.user_id;
|
|
1031
1214
|
const role = args.role;
|
|
1032
1215
|
if (!teamId || typeof teamId !== "string") {
|
|
1033
|
-
return JSON.stringify({
|
|
1216
|
+
return JSON.stringify({
|
|
1217
|
+
error: "team_id is required and must be a string",
|
|
1218
|
+
});
|
|
1034
1219
|
}
|
|
1035
1220
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1036
1221
|
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1037
1222
|
}
|
|
1038
1223
|
if (!userId || typeof userId !== "string") {
|
|
1039
|
-
return JSON.stringify({
|
|
1224
|
+
return JSON.stringify({
|
|
1225
|
+
error: "user_id is required and must be a string",
|
|
1226
|
+
});
|
|
1040
1227
|
}
|
|
1041
1228
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
|
|
1042
1229
|
return JSON.stringify({ error: "user_id must be a valid UUID" });
|
|
@@ -1051,14 +1238,552 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
1051
1238
|
return JSON.stringify(result);
|
|
1052
1239
|
}
|
|
1053
1240
|
// -----------------------------------------------------------------------
|
|
1241
|
+
// Proxy extended
|
|
1242
|
+
// -----------------------------------------------------------------------
|
|
1243
|
+
async function handleGetProxyStatus() {
|
|
1244
|
+
const result = await api("GET", "/api/proxy/status");
|
|
1245
|
+
return JSON.stringify(result);
|
|
1246
|
+
}
|
|
1247
|
+
// -----------------------------------------------------------------------
|
|
1248
|
+
// Wallet extended
|
|
1249
|
+
// -----------------------------------------------------------------------
|
|
1250
|
+
async function handleGetTransactions(args) {
|
|
1251
|
+
const params = new URLSearchParams();
|
|
1252
|
+
if (args.limit !== undefined) {
|
|
1253
|
+
const limit = args.limit;
|
|
1254
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
1255
|
+
return JSON.stringify({
|
|
1256
|
+
error: "limit must be an integer between 1 and 100",
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
params.set("limit", String(limit));
|
|
1260
|
+
}
|
|
1261
|
+
const qs = params.toString();
|
|
1262
|
+
const result = await api("GET", `/api/wallet/transactions${qs ? `?${qs}` : ""}`);
|
|
1263
|
+
return JSON.stringify(result);
|
|
1264
|
+
}
|
|
1265
|
+
async function handleGetForecast() {
|
|
1266
|
+
const result = await api("GET", "/api/wallet/forecast");
|
|
1267
|
+
return JSON.stringify(result);
|
|
1268
|
+
}
|
|
1269
|
+
async function handleCheckPayment(args) {
|
|
1270
|
+
const invoiceId = args.invoice_id;
|
|
1271
|
+
if (!invoiceId || typeof invoiceId !== "string") {
|
|
1272
|
+
return JSON.stringify({
|
|
1273
|
+
error: "invoice_id is required and must be a string",
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
const result = await api("GET", `/api/wallet/topup/crypto/${encodeURIComponent(invoiceId)}/status`);
|
|
1277
|
+
return JSON.stringify(result);
|
|
1278
|
+
}
|
|
1279
|
+
// -----------------------------------------------------------------------
|
|
1280
|
+
// Usage extended
|
|
1281
|
+
// -----------------------------------------------------------------------
|
|
1282
|
+
async function handleGetDailyUsage(args) {
|
|
1283
|
+
const days = args.days ?? 30;
|
|
1284
|
+
if (!Number.isInteger(days) || days < 1 || days > 90) {
|
|
1285
|
+
return JSON.stringify({
|
|
1286
|
+
error: "days must be an integer between 1 and 90",
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
const params = new URLSearchParams({ days: String(days) });
|
|
1290
|
+
const result = await api("GET", `/api/usage/daily?${params.toString()}`);
|
|
1291
|
+
return JSON.stringify(result);
|
|
1292
|
+
}
|
|
1293
|
+
async function handleGetTopHosts(args) {
|
|
1294
|
+
const params = new URLSearchParams();
|
|
1295
|
+
if (args.limit !== undefined) {
|
|
1296
|
+
const limit = args.limit;
|
|
1297
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 50) {
|
|
1298
|
+
return JSON.stringify({
|
|
1299
|
+
error: "limit must be an integer between 1 and 50",
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
params.set("limit", String(limit));
|
|
1303
|
+
}
|
|
1304
|
+
const qs = params.toString();
|
|
1305
|
+
const result = await api("GET", `/api/usage/top-hosts${qs ? `?${qs}` : ""}`);
|
|
1306
|
+
return JSON.stringify(result);
|
|
1307
|
+
}
|
|
1308
|
+
// -----------------------------------------------------------------------
|
|
1309
|
+
// Account lifecycle
|
|
1310
|
+
// -----------------------------------------------------------------------
|
|
1311
|
+
async function handleRegister(args) {
|
|
1312
|
+
const email = args.email;
|
|
1313
|
+
const password = args.password;
|
|
1314
|
+
if (!email || typeof email !== "string") {
|
|
1315
|
+
return JSON.stringify({
|
|
1316
|
+
error: "email is required and must be a string",
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
|
|
1320
|
+
return JSON.stringify({ error: "email must be a valid email address" });
|
|
1321
|
+
}
|
|
1322
|
+
if (!password || typeof password !== "string") {
|
|
1323
|
+
return JSON.stringify({
|
|
1324
|
+
error: "password is required and must be a string",
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
if (password.length < 8 || password.length > 128) {
|
|
1328
|
+
return JSON.stringify({
|
|
1329
|
+
error: "password must be between 8 and 128 characters",
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
const headers = {
|
|
1333
|
+
"User-Agent": "dominusnode-openai-functions/1.0.0",
|
|
1334
|
+
"Content-Type": "application/json",
|
|
1335
|
+
};
|
|
1336
|
+
if (agentSecret) {
|
|
1337
|
+
headers["X-DominusNode-Agent"] = "mcp";
|
|
1338
|
+
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
1339
|
+
}
|
|
1340
|
+
// Solve PoW for CAPTCHA-free registration
|
|
1341
|
+
const pow = await solvePoW(baseUrl);
|
|
1342
|
+
const regBody = { email, password };
|
|
1343
|
+
if (pow)
|
|
1344
|
+
regBody.pow = pow;
|
|
1345
|
+
const response = await fetch(`${baseUrl}/api/auth/register`, {
|
|
1346
|
+
method: "POST",
|
|
1347
|
+
headers,
|
|
1348
|
+
body: JSON.stringify(regBody),
|
|
1349
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1350
|
+
redirect: "error",
|
|
1351
|
+
});
|
|
1352
|
+
const text = await response.text();
|
|
1353
|
+
if (text.length > MAX_RESPONSE_BYTES) {
|
|
1354
|
+
return JSON.stringify({ error: "Response body exceeds size limit" });
|
|
1355
|
+
}
|
|
1356
|
+
if (!response.ok) {
|
|
1357
|
+
let message;
|
|
1358
|
+
try {
|
|
1359
|
+
const parsed = JSON.parse(text);
|
|
1360
|
+
message = parsed.error ?? parsed.message ?? text;
|
|
1361
|
+
}
|
|
1362
|
+
catch {
|
|
1363
|
+
message = text;
|
|
1364
|
+
}
|
|
1365
|
+
if (message.length > 500)
|
|
1366
|
+
message = message.slice(0, 500) + "... [truncated]";
|
|
1367
|
+
return JSON.stringify({
|
|
1368
|
+
error: `Registration failed: ${sanitizeError(message)}`,
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
const data = safeJsonParse(text);
|
|
1372
|
+
stripDangerousKeys(data);
|
|
1373
|
+
return JSON.stringify({
|
|
1374
|
+
userId: data.userId,
|
|
1375
|
+
email: data.email,
|
|
1376
|
+
message: data.message,
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
async function handleLogin(args) {
|
|
1380
|
+
const email = args.email;
|
|
1381
|
+
const password = args.password;
|
|
1382
|
+
if (!email || typeof email !== "string") {
|
|
1383
|
+
return JSON.stringify({
|
|
1384
|
+
error: "email is required and must be a string",
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
|
|
1388
|
+
return JSON.stringify({ error: "email must be a valid email address" });
|
|
1389
|
+
}
|
|
1390
|
+
if (!password || typeof password !== "string") {
|
|
1391
|
+
return JSON.stringify({
|
|
1392
|
+
error: "password is required and must be a string",
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
if (password.length < 8 || password.length > 128) {
|
|
1396
|
+
return JSON.stringify({
|
|
1397
|
+
error: "password must be between 8 and 128 characters",
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
const headers = {
|
|
1401
|
+
"User-Agent": "dominusnode-openai-functions/1.0.0",
|
|
1402
|
+
"Content-Type": "application/json",
|
|
1403
|
+
};
|
|
1404
|
+
if (agentSecret) {
|
|
1405
|
+
headers["X-DominusNode-Agent"] = "mcp";
|
|
1406
|
+
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
1407
|
+
}
|
|
1408
|
+
const response = await fetch(`${baseUrl}/api/auth/login`, {
|
|
1409
|
+
method: "POST",
|
|
1410
|
+
headers,
|
|
1411
|
+
body: JSON.stringify({ email, password }),
|
|
1412
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1413
|
+
redirect: "error",
|
|
1414
|
+
});
|
|
1415
|
+
const text = await response.text();
|
|
1416
|
+
if (text.length > MAX_RESPONSE_BYTES) {
|
|
1417
|
+
return JSON.stringify({ error: "Response body exceeds size limit" });
|
|
1418
|
+
}
|
|
1419
|
+
if (!response.ok) {
|
|
1420
|
+
let message;
|
|
1421
|
+
try {
|
|
1422
|
+
const parsed = JSON.parse(text);
|
|
1423
|
+
message = parsed.error ?? parsed.message ?? text;
|
|
1424
|
+
}
|
|
1425
|
+
catch {
|
|
1426
|
+
message = text;
|
|
1427
|
+
}
|
|
1428
|
+
if (message.length > 500)
|
|
1429
|
+
message = message.slice(0, 500) + "... [truncated]";
|
|
1430
|
+
return JSON.stringify({
|
|
1431
|
+
error: `Login failed: ${sanitizeError(message)}`,
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
const data = safeJsonParse(text);
|
|
1435
|
+
stripDangerousKeys(data);
|
|
1436
|
+
return JSON.stringify({ token: data.token, message: data.message });
|
|
1437
|
+
}
|
|
1438
|
+
async function handleGetAccountInfo() {
|
|
1439
|
+
const result = await api("GET", "/api/auth/me");
|
|
1440
|
+
return JSON.stringify(result);
|
|
1441
|
+
}
|
|
1442
|
+
async function handleVerifyEmail(args) {
|
|
1443
|
+
const token = args.token;
|
|
1444
|
+
if (!token || typeof token !== "string") {
|
|
1445
|
+
return JSON.stringify({
|
|
1446
|
+
error: "token is required and must be a string",
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
const headers = {
|
|
1450
|
+
"User-Agent": "dominusnode-openai-functions/1.0.0",
|
|
1451
|
+
"Content-Type": "application/json",
|
|
1452
|
+
};
|
|
1453
|
+
if (agentSecret) {
|
|
1454
|
+
headers["X-DominusNode-Agent"] = "mcp";
|
|
1455
|
+
headers["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
1456
|
+
}
|
|
1457
|
+
const response = await fetch(`${baseUrl}/api/auth/verify-email`, {
|
|
1458
|
+
method: "POST",
|
|
1459
|
+
headers,
|
|
1460
|
+
body: JSON.stringify({ token }),
|
|
1461
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1462
|
+
redirect: "error",
|
|
1463
|
+
});
|
|
1464
|
+
const text = await response.text();
|
|
1465
|
+
if (text.length > MAX_RESPONSE_BYTES) {
|
|
1466
|
+
return JSON.stringify({ error: "Response body exceeds size limit" });
|
|
1467
|
+
}
|
|
1468
|
+
if (!response.ok) {
|
|
1469
|
+
let message;
|
|
1470
|
+
try {
|
|
1471
|
+
const parsed = JSON.parse(text);
|
|
1472
|
+
message = parsed.error ?? parsed.message ?? text;
|
|
1473
|
+
}
|
|
1474
|
+
catch {
|
|
1475
|
+
message = text;
|
|
1476
|
+
}
|
|
1477
|
+
if (message.length > 500)
|
|
1478
|
+
message = message.slice(0, 500) + "... [truncated]";
|
|
1479
|
+
return JSON.stringify({
|
|
1480
|
+
error: `Email verification failed: ${sanitizeError(message)}`,
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
const data = safeJsonParse(text);
|
|
1484
|
+
stripDangerousKeys(data);
|
|
1485
|
+
return JSON.stringify(data);
|
|
1486
|
+
}
|
|
1487
|
+
async function handleResendVerification() {
|
|
1488
|
+
const result = await api("POST", "/api/auth/resend-verification");
|
|
1489
|
+
return JSON.stringify(result);
|
|
1490
|
+
}
|
|
1491
|
+
async function handleUpdatePassword(args) {
|
|
1492
|
+
const currentPassword = args.current_password;
|
|
1493
|
+
const newPassword = args.new_password;
|
|
1494
|
+
if (!currentPassword || typeof currentPassword !== "string") {
|
|
1495
|
+
return JSON.stringify({
|
|
1496
|
+
error: "current_password is required and must be a string",
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
if (!newPassword || typeof newPassword !== "string") {
|
|
1500
|
+
return JSON.stringify({
|
|
1501
|
+
error: "new_password is required and must be a string",
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
if (newPassword.length < 8 || newPassword.length > 128) {
|
|
1505
|
+
return JSON.stringify({
|
|
1506
|
+
error: "new_password must be between 8 and 128 characters",
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
const result = await api("POST", "/api/auth/change-password", {
|
|
1510
|
+
currentPassword,
|
|
1511
|
+
newPassword,
|
|
1512
|
+
});
|
|
1513
|
+
return JSON.stringify(result);
|
|
1514
|
+
}
|
|
1515
|
+
// -----------------------------------------------------------------------
|
|
1516
|
+
// API Keys
|
|
1517
|
+
// -----------------------------------------------------------------------
|
|
1518
|
+
async function handleListKeys() {
|
|
1519
|
+
const result = await api("GET", "/api/keys");
|
|
1520
|
+
return JSON.stringify(result);
|
|
1521
|
+
}
|
|
1522
|
+
async function handleCreateKey(args) {
|
|
1523
|
+
const label = args.label;
|
|
1524
|
+
if (!label || typeof label !== "string") {
|
|
1525
|
+
return JSON.stringify({
|
|
1526
|
+
error: "label is required and must be a string",
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
if (label.length > 100) {
|
|
1530
|
+
return JSON.stringify({ error: "label must be 100 characters or fewer" });
|
|
1531
|
+
}
|
|
1532
|
+
if (/[\x00-\x1f\x7f]/.test(label)) {
|
|
1533
|
+
return JSON.stringify({
|
|
1534
|
+
error: "label contains invalid control characters",
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
const result = await api("POST", "/api/keys", { label });
|
|
1538
|
+
return JSON.stringify(result);
|
|
1539
|
+
}
|
|
1540
|
+
async function handleRevokeKey(args) {
|
|
1541
|
+
const keyId = args.key_id;
|
|
1542
|
+
if (!keyId || typeof keyId !== "string") {
|
|
1543
|
+
return JSON.stringify({
|
|
1544
|
+
error: "key_id is required and must be a string",
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(keyId)) {
|
|
1548
|
+
return JSON.stringify({ error: "key_id must be a valid UUID" });
|
|
1549
|
+
}
|
|
1550
|
+
const result = await api("DELETE", `/api/keys/${encodeURIComponent(keyId)}`);
|
|
1551
|
+
return JSON.stringify(result);
|
|
1552
|
+
}
|
|
1553
|
+
// -----------------------------------------------------------------------
|
|
1554
|
+
// Plans
|
|
1555
|
+
// -----------------------------------------------------------------------
|
|
1556
|
+
async function handleGetPlan() {
|
|
1557
|
+
const result = await api("GET", "/api/plans/user/plan");
|
|
1558
|
+
return JSON.stringify(result);
|
|
1559
|
+
}
|
|
1560
|
+
async function handleListPlans() {
|
|
1561
|
+
const result = await api("GET", "/api/plans");
|
|
1562
|
+
return JSON.stringify(result);
|
|
1563
|
+
}
|
|
1564
|
+
async function handleChangePlan(args) {
|
|
1565
|
+
const planId = args.plan_id;
|
|
1566
|
+
if (!planId || typeof planId !== "string") {
|
|
1567
|
+
return JSON.stringify({
|
|
1568
|
+
error: "plan_id is required and must be a string",
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(planId)) {
|
|
1572
|
+
return JSON.stringify({ error: "plan_id must be a valid UUID" });
|
|
1573
|
+
}
|
|
1574
|
+
const result = await api("PUT", "/api/plans/user/plan", { planId });
|
|
1575
|
+
return JSON.stringify(result);
|
|
1576
|
+
}
|
|
1577
|
+
// -----------------------------------------------------------------------
|
|
1578
|
+
// Teams extended
|
|
1579
|
+
// -----------------------------------------------------------------------
|
|
1580
|
+
async function handleTeamDelete(args) {
|
|
1581
|
+
const teamId = args.team_id;
|
|
1582
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1583
|
+
return JSON.stringify({
|
|
1584
|
+
error: "team_id is required and must be a string",
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1588
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1589
|
+
}
|
|
1590
|
+
const result = await api("DELETE", `/api/teams/${encodeURIComponent(teamId)}`);
|
|
1591
|
+
return JSON.stringify(result);
|
|
1592
|
+
}
|
|
1593
|
+
async function handleTeamRevokeKey(args) {
|
|
1594
|
+
const teamId = args.team_id;
|
|
1595
|
+
const keyId = args.key_id;
|
|
1596
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1597
|
+
return JSON.stringify({
|
|
1598
|
+
error: "team_id is required and must be a string",
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1602
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1603
|
+
}
|
|
1604
|
+
if (!keyId || typeof keyId !== "string") {
|
|
1605
|
+
return JSON.stringify({
|
|
1606
|
+
error: "key_id is required and must be a string",
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(keyId)) {
|
|
1610
|
+
return JSON.stringify({ error: "key_id must be a valid UUID" });
|
|
1611
|
+
}
|
|
1612
|
+
const result = await api("DELETE", `/api/teams/${encodeURIComponent(teamId)}/keys/${encodeURIComponent(keyId)}`);
|
|
1613
|
+
return JSON.stringify(result);
|
|
1614
|
+
}
|
|
1615
|
+
async function handleTeamListKeys(args) {
|
|
1616
|
+
const teamId = args.team_id;
|
|
1617
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1618
|
+
return JSON.stringify({
|
|
1619
|
+
error: "team_id is required and must be a string",
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1623
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1624
|
+
}
|
|
1625
|
+
const result = await api("GET", `/api/teams/${encodeURIComponent(teamId)}/keys`);
|
|
1626
|
+
return JSON.stringify(result);
|
|
1627
|
+
}
|
|
1628
|
+
async function handleTeamListMembers(args) {
|
|
1629
|
+
const teamId = args.team_id;
|
|
1630
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1631
|
+
return JSON.stringify({
|
|
1632
|
+
error: "team_id is required and must be a string",
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1636
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1637
|
+
}
|
|
1638
|
+
const result = await api("GET", `/api/teams/${encodeURIComponent(teamId)}/members`);
|
|
1639
|
+
return JSON.stringify(result);
|
|
1640
|
+
}
|
|
1641
|
+
async function handleTeamAddMember(args) {
|
|
1642
|
+
const teamId = args.team_id;
|
|
1643
|
+
const userId = args.user_id;
|
|
1644
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1645
|
+
return JSON.stringify({
|
|
1646
|
+
error: "team_id is required and must be a string",
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1650
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1651
|
+
}
|
|
1652
|
+
if (!userId || typeof userId !== "string") {
|
|
1653
|
+
return JSON.stringify({
|
|
1654
|
+
error: "user_id is required and must be a string",
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
|
|
1658
|
+
return JSON.stringify({ error: "user_id must be a valid UUID" });
|
|
1659
|
+
}
|
|
1660
|
+
const result = await api("POST", `/api/teams/${encodeURIComponent(teamId)}/members`, { userId });
|
|
1661
|
+
return JSON.stringify(result);
|
|
1662
|
+
}
|
|
1663
|
+
async function handleTeamRemoveMember(args) {
|
|
1664
|
+
const teamId = args.team_id;
|
|
1665
|
+
const userId = args.user_id;
|
|
1666
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1667
|
+
return JSON.stringify({
|
|
1668
|
+
error: "team_id is required and must be a string",
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1672
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1673
|
+
}
|
|
1674
|
+
if (!userId || typeof userId !== "string") {
|
|
1675
|
+
return JSON.stringify({
|
|
1676
|
+
error: "user_id is required and must be a string",
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(userId)) {
|
|
1680
|
+
return JSON.stringify({ error: "user_id must be a valid UUID" });
|
|
1681
|
+
}
|
|
1682
|
+
const result = await api("DELETE", `/api/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`);
|
|
1683
|
+
return JSON.stringify(result);
|
|
1684
|
+
}
|
|
1685
|
+
async function handleTeamInviteMember(args) {
|
|
1686
|
+
const teamId = args.team_id;
|
|
1687
|
+
const email = args.email;
|
|
1688
|
+
const role = args.role;
|
|
1689
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1690
|
+
return JSON.stringify({
|
|
1691
|
+
error: "team_id is required and must be a string",
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1695
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1696
|
+
}
|
|
1697
|
+
if (!email || typeof email !== "string") {
|
|
1698
|
+
return JSON.stringify({
|
|
1699
|
+
error: "email is required and must be a string",
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) {
|
|
1703
|
+
return JSON.stringify({ error: "email must be a valid email address" });
|
|
1704
|
+
}
|
|
1705
|
+
if (!role || typeof role !== "string") {
|
|
1706
|
+
return JSON.stringify({ error: "role is required and must be a string" });
|
|
1707
|
+
}
|
|
1708
|
+
if (role !== "member" && role !== "admin") {
|
|
1709
|
+
return JSON.stringify({ error: "role must be 'member' or 'admin'" });
|
|
1710
|
+
}
|
|
1711
|
+
const result = await api("POST", `/api/teams/${encodeURIComponent(teamId)}/invites`, { email, role });
|
|
1712
|
+
return JSON.stringify(result);
|
|
1713
|
+
}
|
|
1714
|
+
async function handleTeamListInvites(args) {
|
|
1715
|
+
const teamId = args.team_id;
|
|
1716
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1717
|
+
return JSON.stringify({
|
|
1718
|
+
error: "team_id is required and must be a string",
|
|
1719
|
+
});
|
|
1720
|
+
}
|
|
1721
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1722
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1723
|
+
}
|
|
1724
|
+
const result = await api("GET", `/api/teams/${encodeURIComponent(teamId)}/invites`);
|
|
1725
|
+
return JSON.stringify(result);
|
|
1726
|
+
}
|
|
1727
|
+
async function handleTeamCancelInvite(args) {
|
|
1728
|
+
const teamId = args.team_id;
|
|
1729
|
+
const inviteId = args.invite_id;
|
|
1730
|
+
if (!teamId || typeof teamId !== "string") {
|
|
1731
|
+
return JSON.stringify({
|
|
1732
|
+
error: "team_id is required and must be a string",
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(teamId)) {
|
|
1736
|
+
return JSON.stringify({ error: "team_id must be a valid UUID" });
|
|
1737
|
+
}
|
|
1738
|
+
if (!inviteId || typeof inviteId !== "string") {
|
|
1739
|
+
return JSON.stringify({
|
|
1740
|
+
error: "invite_id is required and must be a string",
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(inviteId)) {
|
|
1744
|
+
return JSON.stringify({ error: "invite_id must be a valid UUID" });
|
|
1745
|
+
}
|
|
1746
|
+
const result = await api("DELETE", `/api/teams/${encodeURIComponent(teamId)}/invites/${encodeURIComponent(inviteId)}`);
|
|
1747
|
+
return JSON.stringify(result);
|
|
1748
|
+
}
|
|
1749
|
+
// -----------------------------------------------------------------------
|
|
1054
1750
|
// Dispatch table
|
|
1055
1751
|
// -----------------------------------------------------------------------
|
|
1056
1752
|
const handlers = {
|
|
1753
|
+
// Proxy (3)
|
|
1057
1754
|
dominusnode_proxied_fetch: handleProxiedFetch,
|
|
1755
|
+
dominusnode_get_proxy_config: handleGetProxyConfig,
|
|
1756
|
+
dominusnode_get_proxy_status: handleGetProxyStatus,
|
|
1757
|
+
// Wallet (7)
|
|
1058
1758
|
dominusnode_check_balance: handleCheckBalance,
|
|
1759
|
+
dominusnode_get_transactions: handleGetTransactions,
|
|
1760
|
+
dominusnode_get_forecast: handleGetForecast,
|
|
1761
|
+
dominusnode_check_payment: handleCheckPayment,
|
|
1762
|
+
dominusnode_topup_paypal: handleTopupPaypal,
|
|
1763
|
+
dominusnode_topup_stripe: handleTopupStripe,
|
|
1764
|
+
dominusnode_topup_crypto: handleTopupCrypto,
|
|
1765
|
+
// Usage (3)
|
|
1059
1766
|
dominusnode_check_usage: handleCheckUsage,
|
|
1060
|
-
|
|
1767
|
+
dominusnode_get_daily_usage: handleGetDailyUsage,
|
|
1768
|
+
dominusnode_get_top_hosts: handleGetTopHosts,
|
|
1769
|
+
// Sessions (1)
|
|
1061
1770
|
dominusnode_list_sessions: handleListSessions,
|
|
1771
|
+
// Account lifecycle (6)
|
|
1772
|
+
dominusnode_register: handleRegister,
|
|
1773
|
+
dominusnode_login: handleLogin,
|
|
1774
|
+
dominusnode_get_account_info: handleGetAccountInfo,
|
|
1775
|
+
dominusnode_verify_email: handleVerifyEmail,
|
|
1776
|
+
dominusnode_resend_verification: handleResendVerification,
|
|
1777
|
+
dominusnode_update_password: handleUpdatePassword,
|
|
1778
|
+
// API Keys (3)
|
|
1779
|
+
dominusnode_list_keys: handleListKeys,
|
|
1780
|
+
dominusnode_create_key: handleCreateKey,
|
|
1781
|
+
dominusnode_revoke_key: handleRevokeKey,
|
|
1782
|
+
// Plans (3)
|
|
1783
|
+
dominusnode_get_plan: handleGetPlan,
|
|
1784
|
+
dominusnode_list_plans: handleListPlans,
|
|
1785
|
+
dominusnode_change_plan: handleChangePlan,
|
|
1786
|
+
// Agentic wallets (7)
|
|
1062
1787
|
dominusnode_create_agentic_wallet: handleCreateAgenticWallet,
|
|
1063
1788
|
dominusnode_fund_agentic_wallet: handleFundAgenticWallet,
|
|
1064
1789
|
dominusnode_agentic_wallet_balance: handleAgenticWalletBalance,
|
|
@@ -1068,6 +1793,7 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
1068
1793
|
dominusnode_unfreeze_agentic_wallet: handleUnfreezeAgenticWallet,
|
|
1069
1794
|
dominusnode_delete_agentic_wallet: handleDeleteAgenticWallet,
|
|
1070
1795
|
dominusnode_update_wallet_policy: handleUpdateWalletPolicy,
|
|
1796
|
+
// Teams (17)
|
|
1071
1797
|
dominusnode_create_team: handleCreateTeam,
|
|
1072
1798
|
dominusnode_list_teams: handleListTeams,
|
|
1073
1799
|
dominusnode_team_details: handleTeamDetails,
|
|
@@ -1076,16 +1802,144 @@ export function createDominusNodeFunctionHandler(config) {
|
|
|
1076
1802
|
dominusnode_team_usage: handleTeamUsage,
|
|
1077
1803
|
dominusnode_update_team: handleUpdateTeam,
|
|
1078
1804
|
dominusnode_update_team_member_role: handleUpdateTeamMemberRole,
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1805
|
+
dominusnode_team_delete: handleTeamDelete,
|
|
1806
|
+
dominusnode_team_revoke_key: handleTeamRevokeKey,
|
|
1807
|
+
dominusnode_team_list_keys: handleTeamListKeys,
|
|
1808
|
+
dominusnode_team_list_members: handleTeamListMembers,
|
|
1809
|
+
dominusnode_team_add_member: handleTeamAddMember,
|
|
1810
|
+
dominusnode_team_remove_member: handleTeamRemoveMember,
|
|
1811
|
+
dominusnode_team_invite_member: handleTeamInviteMember,
|
|
1812
|
+
dominusnode_team_list_invites: handleTeamListInvites,
|
|
1813
|
+
dominusnode_team_cancel_invite: handleTeamCancelInvite,
|
|
1814
|
+
// x402 (1)
|
|
1082
1815
|
dominusnode_x402_info: handleX402Info,
|
|
1816
|
+
// MPP (5)
|
|
1817
|
+
dominusnode_mpp_info: handleMppInfo,
|
|
1818
|
+
dominusnode_mpp_challenge: handleMppChallenge,
|
|
1819
|
+
dominusnode_pay_mpp: handlePayMpp,
|
|
1820
|
+
dominusnode_mpp_session_open: handleMppSessionOpen,
|
|
1821
|
+
dominusnode_mpp_session_close: handleMppSessionClose,
|
|
1083
1822
|
};
|
|
1084
1823
|
async function handleX402Info() {
|
|
1085
1824
|
const result = await api("GET", "/api/x402/info");
|
|
1086
1825
|
return JSON.stringify(result);
|
|
1087
1826
|
}
|
|
1088
1827
|
// -----------------------------------------------------------------------
|
|
1828
|
+
// MPP (Machine Payment Protocol) handlers
|
|
1829
|
+
// -----------------------------------------------------------------------
|
|
1830
|
+
async function handleMppInfo() {
|
|
1831
|
+
// MPP info is public (no auth required), but we use the authenticated path
|
|
1832
|
+
// for simplicity since the handler is already authenticated.
|
|
1833
|
+
const result = await api("GET", "/api/mpp/info");
|
|
1834
|
+
return JSON.stringify(result);
|
|
1835
|
+
}
|
|
1836
|
+
async function handleMppChallenge(args) {
|
|
1837
|
+
const poolType = String(args.pool_type ?? "dc");
|
|
1838
|
+
if (poolType !== "dc" && poolType !== "residential") {
|
|
1839
|
+
return JSON.stringify({
|
|
1840
|
+
error: "pool_type must be 'dc' or 'residential'",
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
// This is a public endpoint -- no auth required
|
|
1844
|
+
try {
|
|
1845
|
+
const reqHeaders = {
|
|
1846
|
+
"User-Agent": "dominusnode-openai-functions/1.0.0",
|
|
1847
|
+
"Content-Type": "application/json",
|
|
1848
|
+
};
|
|
1849
|
+
if (agentSecret) {
|
|
1850
|
+
reqHeaders["X-DominusNode-Agent"] = "mcp";
|
|
1851
|
+
reqHeaders["X-DominusNode-Agent-Secret"] = agentSecret;
|
|
1852
|
+
}
|
|
1853
|
+
const response = await fetch(`${baseUrl}/api/mpp/challenge`, {
|
|
1854
|
+
method: "POST",
|
|
1855
|
+
headers: reqHeaders,
|
|
1856
|
+
body: JSON.stringify({ poolType }),
|
|
1857
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
1858
|
+
redirect: "error",
|
|
1859
|
+
});
|
|
1860
|
+
const responseText = await response.text();
|
|
1861
|
+
if (responseText.length > MAX_RESPONSE_BYTES) {
|
|
1862
|
+
return JSON.stringify({ error: "Response body exceeds size limit" });
|
|
1863
|
+
}
|
|
1864
|
+
if (!response.ok) {
|
|
1865
|
+
let message;
|
|
1866
|
+
try {
|
|
1867
|
+
const parsed = JSON.parse(responseText);
|
|
1868
|
+
message = parsed.error ?? parsed.message ?? responseText;
|
|
1869
|
+
}
|
|
1870
|
+
catch {
|
|
1871
|
+
message = responseText;
|
|
1872
|
+
}
|
|
1873
|
+
if (message.length > 500)
|
|
1874
|
+
message = message.slice(0, 500) + "... [truncated]";
|
|
1875
|
+
return JSON.stringify({
|
|
1876
|
+
error: `MPP challenge failed: ${sanitizeError(message)}`,
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
const data = safeJsonParse(responseText);
|
|
1880
|
+
return JSON.stringify(data);
|
|
1881
|
+
}
|
|
1882
|
+
catch (err) {
|
|
1883
|
+
return JSON.stringify({
|
|
1884
|
+
error: `MPP challenge failed: ${sanitizeError(err instanceof Error ? err.message : String(err))}`,
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
async function handlePayMpp(args) {
|
|
1889
|
+
const amountCents = args.amount_cents;
|
|
1890
|
+
const method = String(args.method ?? "");
|
|
1891
|
+
if (!Number.isInteger(amountCents) ||
|
|
1892
|
+
amountCents < 500 ||
|
|
1893
|
+
amountCents > 100_000) {
|
|
1894
|
+
return JSON.stringify({
|
|
1895
|
+
error: "amount_cents must be an integer between 500 ($5) and 100,000 ($1,000)",
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
|
|
1899
|
+
return JSON.stringify({
|
|
1900
|
+
error: "method must be one of: tempo, stripe_spt, lightning",
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
const result = await api("POST", "/api/mpp/topup", { amountCents, method });
|
|
1904
|
+
return JSON.stringify(result);
|
|
1905
|
+
}
|
|
1906
|
+
async function handleMppSessionOpen(args) {
|
|
1907
|
+
const maxDepositCents = args.max_deposit_cents;
|
|
1908
|
+
const method = String(args.method ?? "");
|
|
1909
|
+
const poolType = String(args.pool_type ?? "dc");
|
|
1910
|
+
if (!Number.isInteger(maxDepositCents) ||
|
|
1911
|
+
maxDepositCents < 500 ||
|
|
1912
|
+
maxDepositCents > 100_000) {
|
|
1913
|
+
return JSON.stringify({
|
|
1914
|
+
error: "max_deposit_cents must be an integer between 500 and 100,000",
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
if (!["tempo", "stripe_spt", "lightning"].includes(method)) {
|
|
1918
|
+
return JSON.stringify({
|
|
1919
|
+
error: "method must be one of: tempo, stripe_spt, lightning",
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
if (!["dc", "residential"].includes(poolType)) {
|
|
1923
|
+
return JSON.stringify({
|
|
1924
|
+
error: "pool_type must be one of: dc, residential",
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
const result = await api("POST", "/api/mpp/session/open", {
|
|
1928
|
+
maxDepositCents,
|
|
1929
|
+
method,
|
|
1930
|
+
poolType,
|
|
1931
|
+
});
|
|
1932
|
+
return JSON.stringify(result);
|
|
1933
|
+
}
|
|
1934
|
+
async function handleMppSessionClose(args) {
|
|
1935
|
+
const channelId = String(args.channel_id ?? "");
|
|
1936
|
+
if (!channelId) {
|
|
1937
|
+
return JSON.stringify({ error: "channel_id is required" });
|
|
1938
|
+
}
|
|
1939
|
+
const result = await api("POST", "/api/mpp/session/close", { channelId });
|
|
1940
|
+
return JSON.stringify(result);
|
|
1941
|
+
}
|
|
1942
|
+
// -----------------------------------------------------------------------
|
|
1089
1943
|
// Main handler
|
|
1090
1944
|
// -----------------------------------------------------------------------
|
|
1091
1945
|
return async function handler(name, args) {
|