@langwatch/mcp-server 0.10.1 → 2.0.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/.python-version +1 -0
- package/.release-please-shim +1 -1
- package/CHANGELOG.md +75 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +64 -18
- package/dist/{archive-project-KAZFHR4A.js → archive-project-4EQGLBJJ.js} +5 -4
- package/dist/{archive-scenario-6M2IB7Y2.js → archive-scenario-W6BXYDZ3.js} +5 -4
- package/dist/{archive-suite-NXZ3YDS3.js → archive-suite-4T6BSRS7.js} +5 -4
- package/dist/{assign-prompt-tag-WPUJO55U.js → assign-prompt-tag-VLXUWEKU.js} +4 -3
- package/dist/chunk-37SMPMQS.js +12 -0
- package/dist/{chunk-HRFQAFPR.js → chunk-4QC7SA2L.js} +221 -134
- package/dist/{chunk-XRQNXN2Z.js → chunk-5CCZFUXS.js} +2 -2
- package/dist/{chunk-ITN2QGHF.js → chunk-77BNWQ3U.js} +2 -2
- package/dist/{chunk-B6UV3JVI.js → chunk-7RHG56OH.js} +2 -2
- package/dist/{chunk-CAY57CKF.js → chunk-A2LEVYQ5.js} +78 -5
- package/dist/{chunk-U2QIW2SW.js → chunk-AQJO2EI5.js} +2 -2
- package/dist/{chunk-GWEW5XON.js → chunk-CDYQOY5U.js} +2 -2
- package/dist/{chunk-LTEJDL4V.js → chunk-J42FWRJ2.js} +2 -2
- package/dist/chunk-SAS5OB7F.js +58 -0
- package/dist/{chunk-AGGE6NPR.js → chunk-SFB4MU6H.js} +2 -2
- package/dist/{chunk-COCNCXVJ.js → chunk-TJCXFNDT.js} +2 -2
- package/dist/chunk-TOXJQDIZ.js +432 -0
- package/dist/chunk-UJPXFA26.js +267 -0
- package/dist/{chunk-KOO5733S.js → chunk-WT4PO4A3.js} +8 -2
- package/dist/{chunk-RDUXCCFR.js → chunk-YX3A3CFM.js} +2 -2
- package/dist/{chunk-UKRCZJJC.js → chunk-YZLYHLLI.js} +2 -2
- package/dist/{chunk-ESGZVPPC.js → chunk-ZWIL4EFL.js} +2 -2
- package/dist/config.js +2 -1
- package/dist/{create-agent-BI7UTJ6A.js → create-agent-7XHO6UIS.js} +5 -4
- package/dist/{create-annotation-SNM2J6S4.js → create-annotation-YQXRQC6A.js} +5 -4
- package/dist/{create-api-key-23MXYC2L.js → create-api-key-YUS36KBU.js} +5 -4
- package/dist/{create-dashboard-CPOOOIKK.js → create-dashboard-4Z5JO5LB.js} +5 -4
- package/dist/{create-dataset-BVXPVVY2.js → create-dataset-E6NMSVX7.js} +5 -4
- package/dist/{create-dataset-records-S7VFIIMR.js → create-dataset-records-SKHPRD2K.js} +5 -4
- package/dist/{create-evaluator-WJAAQOUE.js → create-evaluator-JXF5PF33.js} +5 -4
- package/dist/create-mcp-server.js +4 -3
- package/dist/{create-project-BF7LMZH5.js → create-project-CS2CB57Y.js} +5 -4
- package/dist/{create-prompt-LZ3QHSU4.js → create-prompt-F5RMX3L3.js} +4 -3
- package/dist/{create-prompt-tag-IF4J24QM.js → create-prompt-tag-GMRWTEFR.js} +4 -3
- package/dist/{create-scenario-BK4E5DEU.js → create-scenario-JDR2VB2H.js} +5 -4
- package/dist/{create-suite-HEQPNLND.js → create-suite-LZS6QIAF.js} +5 -4
- package/dist/{delete-agent-Y4T2G5PT.js → delete-agent-O3EEH4S5.js} +5 -4
- package/dist/{delete-annotation-KYDMQTZG.js → delete-annotation-XP6FQM7A.js} +5 -4
- package/dist/{delete-dashboard-3RW647WJ.js → delete-dashboard-RFUNYCNP.js} +5 -4
- package/dist/{delete-dataset-SZAHFHHS.js → delete-dataset-3MJFRDLP.js} +5 -4
- package/dist/{delete-dataset-records-ISHKGEBH.js → delete-dataset-records-WPMU5GL5.js} +5 -4
- package/dist/{delete-evaluator-VKIG5I3S.js → delete-evaluator-33SP2JFX.js} +5 -4
- package/dist/{delete-prompt-tag-VYYL4GTF.js → delete-prompt-tag-KNMZVSPB.js} +4 -3
- package/dist/{delete-workflow-SHNPYSWQ.js → delete-workflow-3NUXO46U.js} +5 -4
- package/dist/discover-evaluator-schema-XTAD4X7D.js +2191 -0
- package/dist/{discover-scenario-schema-MEEEVND7.js → discover-scenario-schema-FE64QK2D.js} +3 -1
- package/dist/{discover-schema-3T52ORPB.js → discover-schema-L6QDPON3.js} +3 -1
- package/dist/{get-agent-IZSP7AR5.js → get-agent-Y7MWZ65F.js} +5 -4
- package/dist/{get-analytics-EDFAYYIQ.js → get-analytics-TETCSOK6.js} +4 -3
- package/dist/{get-dashboard-WSAO6IQ4.js → get-dashboard-KHBEGZGL.js} +5 -4
- package/dist/{get-dataset-BEVNXQNF.js → get-dataset-RCMU5VAH.js} +5 -4
- package/dist/{get-evaluator-HCVXNPZR.js → get-evaluator-ADMKVWYZ.js} +5 -4
- package/dist/{get-project-H4V3GYRQ.js → get-project-RAK7URQJ.js} +5 -4
- package/dist/{get-prompt-EWRJVVGB.js → get-prompt-OY5B7TRI.js} +4 -3
- package/dist/{get-scenario-24YUJS5W.js → get-scenario-ZTAJJL5T.js} +5 -4
- package/dist/{get-simulation-run-UX2W6F6I.js → get-simulation-run-T2ZQWYH2.js} +5 -4
- package/dist/{get-suite-B65ZKZJA.js → get-suite-J7JVWO6O.js} +5 -4
- package/dist/{get-trace-ZR7MIITH.js → get-trace-K7U234Q4.js} +8 -10
- package/dist/{get-workflow-YROHUQJW.js → get-workflow-L5L2PSK3.js} +5 -4
- package/dist/http-security-EU3CHTY7.js +26 -0
- package/dist/http-server-YXHFPPGA.js +24706 -0
- package/dist/index.js +23280 -3135
- package/dist/{langwatch-api-annotations-OPEXNTPF.js → langwatch-api-annotations-FDAS6NQ7.js} +5 -4
- package/dist/{langwatch-api-dashboards-NPQM6QJI.js → langwatch-api-dashboards-JTVCS7ZJ.js} +5 -4
- package/dist/{langwatch-api-monitors-3L25OOOS.js → langwatch-api-monitors-3J7UIQME.js} +4 -3
- package/dist/{langwatch-api-secrets-ZFURUA62.js → langwatch-api-secrets-BUBSNCNO.js} +4 -3
- package/dist/{langwatch-api-triggers-XGXXT7DD.js → langwatch-api-triggers-EDFUCJN6.js} +4 -3
- package/dist/langwatch-api-workflows-NBJX5KOG.js +16 -0
- package/dist/{list-agents-6ZNOV7FK.js → list-agents-KFHKGPXV.js} +5 -4
- package/dist/{list-annotations-6P3IWILH.js → list-annotations-ZTVP4EEL.js} +5 -4
- package/dist/{list-api-keys-JJI62KD2.js → list-api-keys-CWD4UQT5.js} +5 -4
- package/dist/{list-dashboards-G2WJOXWB.js → list-dashboards-PWG6P6KR.js} +5 -4
- package/dist/{list-dataset-records-GWWVOCII.js → list-dataset-records-IOE7LQXS.js} +5 -4
- package/dist/{list-datasets-HBTH33X4.js → list-datasets-ZT4JYUDT.js} +5 -4
- package/dist/{list-evaluators-K5OGB6TX.js → list-evaluators-SJHC7VBH.js} +5 -4
- package/dist/{list-model-providers-JMOGZLSO.js → list-model-providers-PPD2WPD2.js} +5 -4
- package/dist/{list-projects-OXJC2NW5.js → list-projects-K7QAIX2I.js} +5 -4
- package/dist/{list-prompt-tags-J2HPHVAZ.js → list-prompt-tags-FOO6TNHB.js} +4 -3
- package/dist/{list-prompts-C7GR72XL.js → list-prompts-SC2HAUFG.js} +4 -3
- package/dist/{list-scenarios-JEPGIIDO.js → list-scenarios-F2WYNRIE.js} +5 -4
- package/dist/{list-simulation-runs-DRSAGPEC.js → list-simulation-runs-KF5THG5H.js} +5 -4
- package/dist/{list-suites-POTZ3ZT6.js → list-suites-CRY4VWW6.js} +5 -4
- package/dist/{list-workflows-QQZQIOHB.js → list-workflows-QSJWURCK.js} +5 -4
- package/dist/{rename-prompt-tag-4AAV2BS4.js → rename-prompt-tag-EKWU5QF3.js} +4 -3
- package/dist/report-issue-XYVAPAYJ.js +412 -0
- package/dist/{revoke-api-key-TONB4ADQ.js → revoke-api-key-FTRTZMOF.js} +5 -4
- package/dist/{run-agent-7QE3YEH7.js → run-agent-6SM4XNA7.js} +5 -4
- package/dist/{run-suite-Q66OSPO4.js → run-suite-NKZCJ2IN.js} +5 -4
- package/dist/{run-workflow-RWX2D752.js → run-workflow-QPXC627O.js} +5 -4
- package/dist/{search-traces-M5E5VB4X.js → search-traces-A6HFKPLJ.js} +10 -3
- package/dist/{set-model-provider-XENT5D2P.js → set-model-provider-3WKT63ZN.js} +5 -4
- package/dist/{update-agent-GML25EE3.js → update-agent-SMAEHLXH.js} +5 -4
- package/dist/{update-dataset-R4MM6NNY.js → update-dataset-BDUK7TUO.js} +5 -4
- package/dist/{update-dataset-record-FN4BL5P4.js → update-dataset-record-4ZSDRBSW.js} +5 -4
- package/dist/{update-evaluator-LH2RDDB5.js → update-evaluator-LS6TIBY7.js} +5 -4
- package/dist/{update-project-H53IHRBB.js → update-project-LKVYXENH.js} +5 -4
- package/dist/{update-prompt-M33OTTAJ.js → update-prompt-ODCRQRN6.js} +4 -3
- package/dist/{update-scenario-FKTQRAFQ.js → update-scenario-NEXCAQI7.js} +5 -4
- package/dist/{update-suite-JJTNOTXN.js → update-suite-SNO3GS7T.js} +5 -4
- package/package.json +20 -18
- package/pyproject.toml +10 -2
- package/src/__tests__/all-tools.integration.test.ts +56 -120
- package/src/__tests__/config.unit.test.ts +33 -0
- package/src/__tests__/docker.integration.test.ts +91 -7
- package/src/__tests__/documentation-fetch-security.integration.test.ts +182 -0
- package/src/__tests__/feature-map-tool-drift.unit.test.ts +93 -0
- package/src/__tests__/http-security-origin.unit.test.ts +97 -0
- package/src/__tests__/http-security-sessions.unit.test.ts +209 -0
- package/src/__tests__/http-security-verifier.unit.test.ts +248 -0
- package/src/__tests__/http-server-allocation.integration.test.ts +279 -0
- package/src/__tests__/http-server-origin.integration.test.ts +169 -0
- package/src/__tests__/http-server-session-auth.integration.test.ts +341 -0
- package/src/__tests__/http-transport.integration.test.ts +85 -87
- package/src/__tests__/langwatch-api.unit.test.ts +206 -0
- package/src/__tests__/public-http-request-security.integration.test.ts +118 -0
- package/src/__tests__/report-issue.integration.test.ts +147 -0
- package/src/__tests__/support/http-server-harness.ts +138 -0
- package/src/__tests__/tools.unit.test.ts +24 -0
- package/src/config.ts +17 -2
- package/src/create-mcp-server.ts +43 -29
- package/src/documentation-fetch.ts +111 -0
- package/src/http-security.ts +492 -0
- package/src/http-server.ts +536 -152
- package/src/index.ts +49 -2
- package/src/langwatch-api-agents.ts +2 -2
- package/src/langwatch-api.ts +138 -8
- package/src/public-http-request.ts +242 -0
- package/src/tools/discover-evaluator-schema.ts +2 -2
- package/src/tools/get-trace.ts +2 -12
- package/src/tools/report-issue.ts +150 -0
- package/src/tools/search-traces.ts +4 -0
- package/src/utils/format-evaluations.ts +20 -0
- package/tsconfig.json +8 -1
- package/uv.lock +261 -207
- package/dist/chunk-IUP5IHBD.js +0 -71
- package/dist/discover-evaluator-schema-H23XCLNE.js +0 -1402
- package/dist/http-server-GXVEDA7G.js +0 -226
- package/dist/langwatch-api-workflows-CXCTTCXT.js +0 -15
- package/pnpm-workspace.yaml +0 -17
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import {
|
|
2
|
+
makeRequest
|
|
3
|
+
} from "./chunk-A2LEVYQ5.js";
|
|
4
|
+
|
|
5
|
+
// src/public-http-request.ts
|
|
6
|
+
import { lookup as dnsLookup } from "dns/promises";
|
|
7
|
+
import { request as httpRequest } from "http";
|
|
8
|
+
import { request as httpsRequest } from "https";
|
|
9
|
+
import { isIP } from "net";
|
|
10
|
+
|
|
11
|
+
// ../../packages/ssrf/src/index.ts
|
|
12
|
+
function ipv4ToBytes(input) {
|
|
13
|
+
const parts = input.split(".");
|
|
14
|
+
if (parts.length !== 4) return null;
|
|
15
|
+
const bytes = new Uint8Array(4);
|
|
16
|
+
for (let i = 0; i < 4; i++) {
|
|
17
|
+
if (!/^\d{1,3}$/.test(parts[i])) return null;
|
|
18
|
+
const n = Number(parts[i]);
|
|
19
|
+
if (n > 255) return null;
|
|
20
|
+
bytes[i] = n;
|
|
21
|
+
}
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
function ipv6ToBytes(input) {
|
|
25
|
+
let s = input;
|
|
26
|
+
const zone = s.indexOf("%");
|
|
27
|
+
if (zone !== -1) s = s.slice(0, zone);
|
|
28
|
+
if (s.includes(".")) {
|
|
29
|
+
const lastColon = s.lastIndexOf(":");
|
|
30
|
+
if (lastColon === -1) return null;
|
|
31
|
+
const v4 = ipv4ToBytes(s.slice(lastColon + 1));
|
|
32
|
+
if (!v4) return null;
|
|
33
|
+
const hi = (v4[0] << 8 | v4[1]).toString(16);
|
|
34
|
+
const lo = (v4[2] << 8 | v4[3]).toString(16);
|
|
35
|
+
s = `${s.slice(0, lastColon + 1)}${hi}:${lo}`;
|
|
36
|
+
}
|
|
37
|
+
const halves = s.split("::");
|
|
38
|
+
if (halves.length > 2) return null;
|
|
39
|
+
const parseGroups = (part) => {
|
|
40
|
+
if (part === "") return [];
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const g of part.split(":")) {
|
|
43
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(g)) return null;
|
|
44
|
+
out.push(parseInt(g, 16));
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
};
|
|
48
|
+
const head = parseGroups(halves[0]);
|
|
49
|
+
if (head === null) return null;
|
|
50
|
+
let groups;
|
|
51
|
+
if (halves.length === 2) {
|
|
52
|
+
const tail = parseGroups(halves[1]);
|
|
53
|
+
if (tail === null) return null;
|
|
54
|
+
const missing = 8 - head.length - tail.length;
|
|
55
|
+
if (missing < 0) return null;
|
|
56
|
+
groups = [...head, ...new Array(missing).fill(0), ...tail];
|
|
57
|
+
} else {
|
|
58
|
+
groups = head;
|
|
59
|
+
}
|
|
60
|
+
if (groups.length !== 8) return null;
|
|
61
|
+
const bytes = new Uint8Array(16);
|
|
62
|
+
for (let i = 0; i < 8; i++) {
|
|
63
|
+
bytes[i * 2] = groups[i] >> 8 & 255;
|
|
64
|
+
bytes[i * 2 + 1] = groups[i] & 255;
|
|
65
|
+
}
|
|
66
|
+
return bytes;
|
|
67
|
+
}
|
|
68
|
+
function ipToBytes(ip) {
|
|
69
|
+
return ip.includes(":") ? ipv6ToBytes(ip) : ipv4ToBytes(ip);
|
|
70
|
+
}
|
|
71
|
+
function unmap(bytes) {
|
|
72
|
+
if (bytes.length !== 16) return bytes;
|
|
73
|
+
for (let i = 0; i < 10; i++) {
|
|
74
|
+
if (bytes[i] !== 0) return bytes;
|
|
75
|
+
}
|
|
76
|
+
if (bytes[10] === 255 && bytes[11] === 255) return bytes.slice(12, 16);
|
|
77
|
+
return bytes;
|
|
78
|
+
}
|
|
79
|
+
function parsePrefix({ cidr, rfc }) {
|
|
80
|
+
const slash = cidr.lastIndexOf("/");
|
|
81
|
+
const bytes = ipToBytes(cidr.slice(0, slash));
|
|
82
|
+
if (!bytes) throw new Error(`@langwatch/ssrf: bad prefix ${cidr}`);
|
|
83
|
+
return { bytes, bits: Number(cidr.slice(slash + 1)), rfc };
|
|
84
|
+
}
|
|
85
|
+
function prefixContains({
|
|
86
|
+
prefix,
|
|
87
|
+
addr
|
|
88
|
+
}) {
|
|
89
|
+
if (prefix.bytes.length !== addr.length) return false;
|
|
90
|
+
let bits = prefix.bits;
|
|
91
|
+
let i = 0;
|
|
92
|
+
while (bits >= 8) {
|
|
93
|
+
if (prefix.bytes[i] !== addr[i]) return false;
|
|
94
|
+
i++;
|
|
95
|
+
bits -= 8;
|
|
96
|
+
}
|
|
97
|
+
if (bits > 0) {
|
|
98
|
+
const mask = 255 << 8 - bits & 255;
|
|
99
|
+
if ((prefix.bytes[i] & mask) !== (addr[i] & mask)) return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
var METADATA_ADDRESSES = [
|
|
104
|
+
"169.254.169.254",
|
|
105
|
+
// AWS/GCP/Azure/Oracle IMDS
|
|
106
|
+
"169.254.170.2",
|
|
107
|
+
// AWS ECS/Fargate task metadata
|
|
108
|
+
"168.63.129.16",
|
|
109
|
+
// Azure WireServer / host DNS
|
|
110
|
+
"fd00:ec2::254"
|
|
111
|
+
// AWS EC2 IMDS over IPv6
|
|
112
|
+
].map((ip) => {
|
|
113
|
+
const bytes = unmap(ipToBytes(ip));
|
|
114
|
+
return bytes;
|
|
115
|
+
});
|
|
116
|
+
var SPECIAL_PREFIXES = [
|
|
117
|
+
// IPv4
|
|
118
|
+
parsePrefix({ cidr: "0.0.0.0/8", rfc: "RFC1122 this network / unspecified" }),
|
|
119
|
+
parsePrefix({ cidr: "10.0.0.0/8", rfc: "RFC1918 private" }),
|
|
120
|
+
parsePrefix({
|
|
121
|
+
cidr: "100.64.0.0/10",
|
|
122
|
+
rfc: "RFC6598 CGNAT / shared address space"
|
|
123
|
+
}),
|
|
124
|
+
parsePrefix({ cidr: "127.0.0.0/8", rfc: "RFC1122 loopback" }),
|
|
125
|
+
parsePrefix({ cidr: "169.254.0.0/16", rfc: "RFC3927 link-local" }),
|
|
126
|
+
parsePrefix({ cidr: "172.16.0.0/12", rfc: "RFC1918 private" }),
|
|
127
|
+
parsePrefix({
|
|
128
|
+
cidr: "192.0.0.0/24",
|
|
129
|
+
rfc: "RFC6890 IETF protocol assignments"
|
|
130
|
+
}),
|
|
131
|
+
parsePrefix({
|
|
132
|
+
cidr: "192.0.2.0/24",
|
|
133
|
+
rfc: "RFC5737 TEST-NET-1 documentation"
|
|
134
|
+
}),
|
|
135
|
+
parsePrefix({
|
|
136
|
+
cidr: "192.88.99.0/24",
|
|
137
|
+
rfc: "RFC7526 6to4 relay anycast, deprecated"
|
|
138
|
+
}),
|
|
139
|
+
parsePrefix({ cidr: "192.168.0.0/16", rfc: "RFC1918 private" }),
|
|
140
|
+
parsePrefix({ cidr: "198.18.0.0/15", rfc: "RFC2544 benchmarking" }),
|
|
141
|
+
parsePrefix({
|
|
142
|
+
cidr: "198.51.100.0/24",
|
|
143
|
+
rfc: "RFC5737 TEST-NET-2 documentation"
|
|
144
|
+
}),
|
|
145
|
+
parsePrefix({
|
|
146
|
+
cidr: "203.0.113.0/24",
|
|
147
|
+
rfc: "RFC5737 TEST-NET-3 documentation"
|
|
148
|
+
}),
|
|
149
|
+
parsePrefix({ cidr: "224.0.0.0/4", rfc: "RFC5771 multicast" }),
|
|
150
|
+
parsePrefix({
|
|
151
|
+
cidr: "240.0.0.0/4",
|
|
152
|
+
rfc: "RFC1112 reserved incl. 255.255.255.255 broadcast"
|
|
153
|
+
}),
|
|
154
|
+
// IPv6
|
|
155
|
+
parsePrefix({ cidr: "::/128", rfc: "RFC4291 unspecified" }),
|
|
156
|
+
parsePrefix({ cidr: "::1/128", rfc: "RFC4291 loopback" }),
|
|
157
|
+
parsePrefix({ cidr: "64:ff9b::/96", rfc: "RFC6052 well-known NAT64" }),
|
|
158
|
+
parsePrefix({ cidr: "64:ff9b:1::/48", rfc: "RFC8215 local-use NAT64" }),
|
|
159
|
+
parsePrefix({ cidr: "100::/64", rfc: "RFC6666 discard-only" }),
|
|
160
|
+
parsePrefix({ cidr: "2001::/32", rfc: "RFC4380 Teredo" }),
|
|
161
|
+
parsePrefix({ cidr: "2001:db8::/32", rfc: "RFC3849 documentation" }),
|
|
162
|
+
parsePrefix({ cidr: "2002::/16", rfc: "RFC3056 6to4" }),
|
|
163
|
+
parsePrefix({ cidr: "3fff::/20", rfc: "RFC9637 documentation" }),
|
|
164
|
+
parsePrefix({ cidr: "fc00::/7", rfc: "RFC4193 unique-local" }),
|
|
165
|
+
parsePrefix({ cidr: "fe80::/10", rfc: "RFC4291 link-local unicast" }),
|
|
166
|
+
parsePrefix({ cidr: "ff00::/8", rfc: "RFC4291 multicast" })
|
|
167
|
+
];
|
|
168
|
+
function bytesEqual({ a, b }) {
|
|
169
|
+
if (a.length !== b.length) return false;
|
|
170
|
+
for (let i = 0; i < a.length; i++) {
|
|
171
|
+
if (a[i] !== b[i]) return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
function classify(ip) {
|
|
176
|
+
const raw = ipToBytes(ip);
|
|
177
|
+
if (!raw) return "special";
|
|
178
|
+
const addr = unmap(raw);
|
|
179
|
+
if (METADATA_ADDRESSES.some((m) => bytesEqual({ a: m, b: addr })))
|
|
180
|
+
return "metadata";
|
|
181
|
+
for (const prefix of SPECIAL_PREFIXES) {
|
|
182
|
+
if (prefixContains({ prefix, addr })) return "special";
|
|
183
|
+
}
|
|
184
|
+
return "global";
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/public-http-request.ts
|
|
188
|
+
var MAX_REDIRECTS = 5;
|
|
189
|
+
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
190
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
191
|
+
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
192
|
+
var SENSITIVE_REDIRECT_HEADERS = /* @__PURE__ */ new Set([
|
|
193
|
+
"authorization",
|
|
194
|
+
"cookie",
|
|
195
|
+
"cookie2",
|
|
196
|
+
"proxy-authorization",
|
|
197
|
+
"x-api-key",
|
|
198
|
+
"api-key",
|
|
199
|
+
"x-auth-token",
|
|
200
|
+
"x-access-token"
|
|
201
|
+
]);
|
|
202
|
+
function unsafeDestinationError() {
|
|
203
|
+
return new Error("HTTP agent destinations must resolve only to globally routable public addresses");
|
|
204
|
+
}
|
|
205
|
+
function hostnameWithoutBrackets(hostname) {
|
|
206
|
+
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
207
|
+
}
|
|
208
|
+
function assertPublicAddress(address) {
|
|
209
|
+
if (classify(address) !== "global") {
|
|
210
|
+
throw unsafeDestinationError();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function withTimeout(promise, timeoutMs) {
|
|
214
|
+
let timeout;
|
|
215
|
+
try {
|
|
216
|
+
return await Promise.race([
|
|
217
|
+
promise,
|
|
218
|
+
new Promise((_resolve, reject) => {
|
|
219
|
+
timeout = setTimeout(() => reject(new Error("request timed out")), timeoutMs);
|
|
220
|
+
timeout.unref();
|
|
221
|
+
})
|
|
222
|
+
]);
|
|
223
|
+
} finally {
|
|
224
|
+
if (timeout) {
|
|
225
|
+
clearTimeout(timeout);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function headersForRedirect(headers, crossOrigin) {
|
|
230
|
+
if (!headers || !crossOrigin) {
|
|
231
|
+
return headers;
|
|
232
|
+
}
|
|
233
|
+
const safeHeaders = Object.fromEntries(
|
|
234
|
+
Object.entries(headers).filter(([name]) => !SENSITIVE_REDIRECT_HEADERS.has(name.toLowerCase()))
|
|
235
|
+
);
|
|
236
|
+
return Object.keys(safeHeaders).length > 0 ? safeHeaders : void 0;
|
|
237
|
+
}
|
|
238
|
+
async function resolvePublicDestination(input, resolver = dnsLookup) {
|
|
239
|
+
let url;
|
|
240
|
+
try {
|
|
241
|
+
url = new URL(input);
|
|
242
|
+
} catch {
|
|
243
|
+
throw new Error("HTTP agent URL is invalid");
|
|
244
|
+
}
|
|
245
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "") {
|
|
246
|
+
throw unsafeDestinationError();
|
|
247
|
+
}
|
|
248
|
+
const hostname = hostnameWithoutBrackets(url.hostname);
|
|
249
|
+
const literalFamily = isIP(hostname);
|
|
250
|
+
if (literalFamily !== 0) {
|
|
251
|
+
assertPublicAddress(hostname);
|
|
252
|
+
return { address: hostname, family: literalFamily, url };
|
|
253
|
+
}
|
|
254
|
+
let addresses;
|
|
255
|
+
try {
|
|
256
|
+
addresses = await withTimeout(
|
|
257
|
+
resolver(hostname, { all: true, verbatim: true }),
|
|
258
|
+
REQUEST_TIMEOUT_MS
|
|
259
|
+
);
|
|
260
|
+
} catch {
|
|
261
|
+
throw new Error("HTTP agent destination could not be resolved");
|
|
262
|
+
}
|
|
263
|
+
if (addresses.length === 0) {
|
|
264
|
+
throw new Error("HTTP agent destination could not be resolved");
|
|
265
|
+
}
|
|
266
|
+
for (const { address } of addresses) {
|
|
267
|
+
assertPublicAddress(address);
|
|
268
|
+
}
|
|
269
|
+
const selected = addresses.find(({ family }) => family === 4) ?? addresses[0];
|
|
270
|
+
return {
|
|
271
|
+
address: selected.address,
|
|
272
|
+
family: selected.family,
|
|
273
|
+
url
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function requestBody(destination, options) {
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
const hostname = hostnameWithoutBrackets(destination.url.hostname);
|
|
279
|
+
const requestOptions = {
|
|
280
|
+
protocol: destination.url.protocol,
|
|
281
|
+
hostname,
|
|
282
|
+
port: destination.url.port || void 0,
|
|
283
|
+
path: `${destination.url.pathname}${destination.url.search}`,
|
|
284
|
+
method: options.method ?? "GET",
|
|
285
|
+
headers: options.headers,
|
|
286
|
+
family: destination.family,
|
|
287
|
+
signal: options.signal,
|
|
288
|
+
lookup: (_hostname, _options, callback) => {
|
|
289
|
+
callback(null, destination.address, destination.family);
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const transport = destination.url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
293
|
+
const request = transport(requestOptions, (response) => {
|
|
294
|
+
const chunks = [];
|
|
295
|
+
let receivedBytes = 0;
|
|
296
|
+
response.once("aborted", () => {
|
|
297
|
+
reject(new Error("HTTP agent response was interrupted"));
|
|
298
|
+
});
|
|
299
|
+
response.once("error", reject);
|
|
300
|
+
const contentLength = Number(response.headers["content-length"]);
|
|
301
|
+
if (Number.isFinite(contentLength) && contentLength > MAX_RESPONSE_BYTES) {
|
|
302
|
+
response.destroy(new Error("HTTP agent response is too large"));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
response.on("data", (chunk) => {
|
|
306
|
+
receivedBytes += chunk.length;
|
|
307
|
+
if (receivedBytes > MAX_RESPONSE_BYTES) {
|
|
308
|
+
response.destroy(new Error("HTTP agent response is too large"));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
chunks.push(chunk);
|
|
312
|
+
});
|
|
313
|
+
response.once("end", () => {
|
|
314
|
+
resolve({
|
|
315
|
+
body: Buffer.concat(chunks).toString("utf8"),
|
|
316
|
+
location: response.headers.location,
|
|
317
|
+
statusCode: response.statusCode ?? 0
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
request.once("error", reject);
|
|
322
|
+
request.setTimeout(REQUEST_TIMEOUT_MS, () => {
|
|
323
|
+
request.destroy(new Error("HTTP agent request timed out"));
|
|
324
|
+
});
|
|
325
|
+
if (options.body !== void 0) {
|
|
326
|
+
request.write(options.body);
|
|
327
|
+
}
|
|
328
|
+
request.end();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
async function requestPublicJson(input, options = {}, redirectCount = 0) {
|
|
332
|
+
const destination = await resolvePublicDestination(input);
|
|
333
|
+
const response = await requestBody(destination, options);
|
|
334
|
+
if (REDIRECT_STATUSES.has(response.statusCode) && response.location) {
|
|
335
|
+
if (redirectCount >= MAX_REDIRECTS) {
|
|
336
|
+
throw new Error(`HTTP agent returned too many redirects`);
|
|
337
|
+
}
|
|
338
|
+
const redirectUrl = new URL(response.location, destination.url);
|
|
339
|
+
const redirectHeaders = headersForRedirect(
|
|
340
|
+
options.headers,
|
|
341
|
+
redirectUrl.origin !== destination.url.origin
|
|
342
|
+
);
|
|
343
|
+
const preserveBody = response.statusCode === 307 || response.statusCode === 308;
|
|
344
|
+
return requestPublicJson(
|
|
345
|
+
redirectUrl.toString(),
|
|
346
|
+
preserveBody ? { ...options, headers: redirectHeaders } : {
|
|
347
|
+
...options,
|
|
348
|
+
body: void 0,
|
|
349
|
+
headers: redirectHeaders,
|
|
350
|
+
method: "GET"
|
|
351
|
+
},
|
|
352
|
+
redirectCount + 1
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
356
|
+
throw new Error(`HTTP agent returned an unsuccessful status: ${response.statusCode}`);
|
|
357
|
+
}
|
|
358
|
+
let parsed;
|
|
359
|
+
try {
|
|
360
|
+
parsed = JSON.parse(response.body);
|
|
361
|
+
} catch {
|
|
362
|
+
throw new Error("HTTP agent returned invalid JSON");
|
|
363
|
+
}
|
|
364
|
+
return parsed;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/langwatch-api-agents.ts
|
|
368
|
+
async function listAgents(params) {
|
|
369
|
+
const query = new URLSearchParams();
|
|
370
|
+
if (params?.page) query.set("page", String(params.page));
|
|
371
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
372
|
+
const qs = query.toString() ? `?${query}` : "";
|
|
373
|
+
return makeRequest("GET", `/api/agents${qs}`);
|
|
374
|
+
}
|
|
375
|
+
async function getAgent(id) {
|
|
376
|
+
return makeRequest(
|
|
377
|
+
"GET",
|
|
378
|
+
`/api/agents/${encodeURIComponent(id)}`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
async function createAgent(data) {
|
|
382
|
+
return makeRequest("POST", "/api/agents", data);
|
|
383
|
+
}
|
|
384
|
+
async function updateAgent(params) {
|
|
385
|
+
const { id, ...data } = params;
|
|
386
|
+
return makeRequest(
|
|
387
|
+
"PATCH",
|
|
388
|
+
`/api/agents/${encodeURIComponent(id)}`,
|
|
389
|
+
data
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
async function runAgent(id, input) {
|
|
393
|
+
const agent = await getAgent(id);
|
|
394
|
+
const config = agent.config ?? {};
|
|
395
|
+
if (agent.type === "http") {
|
|
396
|
+
const url = config.url;
|
|
397
|
+
if (!url) {
|
|
398
|
+
throw new Error("HTTP agent has no URL configured");
|
|
399
|
+
}
|
|
400
|
+
const result2 = await requestPublicJson(url, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: { "Content-Type": "application/json" },
|
|
403
|
+
body: JSON.stringify(input ?? {})
|
|
404
|
+
});
|
|
405
|
+
return { agentType: "http", result: result2 };
|
|
406
|
+
}
|
|
407
|
+
const workflowId = config.workflowId;
|
|
408
|
+
if (!workflowId) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`Agent "${agent.name}" (type: ${agent.type}) cannot be executed directly. Only HTTP agents and workflow-linked agents can be run.`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
const { runWorkflow } = await import("./langwatch-api-workflows-NBJX5KOG.js");
|
|
414
|
+
const result = await runWorkflow(workflowId, input);
|
|
415
|
+
return { agentType: agent.type, result };
|
|
416
|
+
}
|
|
417
|
+
async function deleteAgent(id) {
|
|
418
|
+
return makeRequest(
|
|
419
|
+
"DELETE",
|
|
420
|
+
`/api/agents/${encodeURIComponent(id)}`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export {
|
|
425
|
+
listAgents,
|
|
426
|
+
getAgent,
|
|
427
|
+
createAgent,
|
|
428
|
+
updateAgent,
|
|
429
|
+
runAgent,
|
|
430
|
+
deleteAgent
|
|
431
|
+
};
|
|
432
|
+
//# sourceMappingURL=chunk-TOXJQDIZ.js.map
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// src/http-security.ts
|
|
2
|
+
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3
|
+
var DEFAULT_BIND_HOST = "127.0.0.1";
|
|
4
|
+
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "::1", "localhost"]);
|
|
5
|
+
function stripBrackets(host) {
|
|
6
|
+
return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
7
|
+
}
|
|
8
|
+
function isLoopbackHost(host) {
|
|
9
|
+
return LOOPBACK_HOSTS.has(stripBrackets(host.trim().toLowerCase()));
|
|
10
|
+
}
|
|
11
|
+
var ALWAYS_ALLOWED_ORIGIN_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
12
|
+
function normalizeOrigin(origin) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = new URL(origin.trim());
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (parsed.origin === "null") return null;
|
|
20
|
+
return {
|
|
21
|
+
origin: parsed.origin,
|
|
22
|
+
hostname: stripBrackets(parsed.hostname.toLowerCase())
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function parseAllowedOrigins(raw) {
|
|
26
|
+
if (!raw) return [];
|
|
27
|
+
const parsed = raw.split(",").map((entry) => normalizeOrigin(entry)?.origin).filter((entry) => entry !== void 0);
|
|
28
|
+
return [...new Set(parsed)];
|
|
29
|
+
}
|
|
30
|
+
function isOriginAllowed({
|
|
31
|
+
origin,
|
|
32
|
+
allowedOrigins
|
|
33
|
+
}) {
|
|
34
|
+
const normalized = normalizeOrigin(origin);
|
|
35
|
+
if (!normalized) return false;
|
|
36
|
+
if (ALWAYS_ALLOWED_ORIGIN_HOSTS.has(normalized.hostname)) return true;
|
|
37
|
+
return allowedOrigins.includes(normalized.origin);
|
|
38
|
+
}
|
|
39
|
+
var KEY_DERIVATION_SECRET = randomBytes(32);
|
|
40
|
+
function hashApiKey(apiKey) {
|
|
41
|
+
return createHmac("sha256", KEY_DERIVATION_SECRET).update(apiKey).digest("hex");
|
|
42
|
+
}
|
|
43
|
+
function apiKeysMatch({
|
|
44
|
+
presentedKey,
|
|
45
|
+
expectedKey
|
|
46
|
+
}) {
|
|
47
|
+
const presented = createHmac("sha256", KEY_DERIVATION_SECRET).update(presentedKey).digest();
|
|
48
|
+
const expected = createHmac("sha256", KEY_DERIVATION_SECRET).update(expectedKey).digest();
|
|
49
|
+
return timingSafeEqual(presented, expected);
|
|
50
|
+
}
|
|
51
|
+
function createRateLimiter({
|
|
52
|
+
windowMs,
|
|
53
|
+
maxRequests
|
|
54
|
+
}) {
|
|
55
|
+
const entries = /* @__PURE__ */ new Map();
|
|
56
|
+
return {
|
|
57
|
+
isBlocked(ip) {
|
|
58
|
+
const entry = entries.get(ip);
|
|
59
|
+
if (!entry || Date.now() - entry.windowStart > windowMs) return false;
|
|
60
|
+
return entry.count >= maxRequests;
|
|
61
|
+
},
|
|
62
|
+
track(ip) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
const entry = entries.get(ip);
|
|
65
|
+
if (!entry || now - entry.windowStart > windowMs) {
|
|
66
|
+
entries.set(ip, { count: 1, windowStart: now });
|
|
67
|
+
} else {
|
|
68
|
+
entry.count++;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
sweep() {
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
for (const [ip, entry] of entries) {
|
|
74
|
+
if (now - entry.windowStart > windowMs) entries.delete(ip);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
clear() {
|
|
78
|
+
entries.clear();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var VERIFY_PATH = "/api/me/project";
|
|
83
|
+
function createApiKeyVerifier({
|
|
84
|
+
endpoint,
|
|
85
|
+
positiveTtlMs = 6e4,
|
|
86
|
+
negativeTtlMs = 3e4,
|
|
87
|
+
maxEntries = 1e4,
|
|
88
|
+
requestTimeoutMs = 5e3,
|
|
89
|
+
fetchImpl = fetch
|
|
90
|
+
}) {
|
|
91
|
+
const cache = /* @__PURE__ */ new Map();
|
|
92
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
93
|
+
function sweep() {
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
for (const [key, entry] of cache) {
|
|
96
|
+
if (now >= entry.expiresAt) cache.delete(key);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function remember(hashed, valid) {
|
|
100
|
+
if (cache.size >= maxEntries) {
|
|
101
|
+
sweep();
|
|
102
|
+
while (cache.size >= maxEntries) {
|
|
103
|
+
const oldest = cache.keys().next();
|
|
104
|
+
if (oldest.done) break;
|
|
105
|
+
cache.delete(oldest.value);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
cache.set(hashed, {
|
|
109
|
+
valid,
|
|
110
|
+
expiresAt: Date.now() + (valid ? positiveTtlMs : negativeTtlMs)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async function askApi(apiKey) {
|
|
114
|
+
let response;
|
|
115
|
+
try {
|
|
116
|
+
response = await fetchImpl(`${endpoint}${VERIFY_PATH}`, {
|
|
117
|
+
method: "GET",
|
|
118
|
+
headers: { "X-Auth-Token": apiKey },
|
|
119
|
+
// An upstream that accepts the connection and never answers would
|
|
120
|
+
// otherwise leave the in-flight promise unsettled, parking every
|
|
121
|
+
// request for this key behind it until the socket dies.
|
|
122
|
+
signal: AbortSignal.timeout(requestTimeoutMs)
|
|
123
|
+
});
|
|
124
|
+
} catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
await response.body?.cancel().catch(() => void 0);
|
|
128
|
+
if (response.ok) return true;
|
|
129
|
+
if (response.status === 401 || response.status === 403) return false;
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
async verify(apiKey) {
|
|
134
|
+
const hashed = hashApiKey(apiKey);
|
|
135
|
+
const cached = cache.get(hashed);
|
|
136
|
+
if (cached && Date.now() < cached.expiresAt) return cached.valid;
|
|
137
|
+
const pending = inFlight.get(hashed);
|
|
138
|
+
if (pending) return pending;
|
|
139
|
+
const request = askApi(apiKey).then((result) => {
|
|
140
|
+
if (result !== null) remember(hashed, result);
|
|
141
|
+
return result === true;
|
|
142
|
+
}).finally(() => {
|
|
143
|
+
inFlight.delete(hashed);
|
|
144
|
+
});
|
|
145
|
+
inFlight.set(hashed, request);
|
|
146
|
+
return request;
|
|
147
|
+
},
|
|
148
|
+
sweep,
|
|
149
|
+
clear() {
|
|
150
|
+
cache.clear();
|
|
151
|
+
inFlight.clear();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function createSessionStore({
|
|
156
|
+
maxAgeMs,
|
|
157
|
+
closeTransport
|
|
158
|
+
}) {
|
|
159
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
160
|
+
const countByKey = /* @__PURE__ */ new Map();
|
|
161
|
+
function decrement(apiKey) {
|
|
162
|
+
const hashed = hashApiKey(apiKey);
|
|
163
|
+
const next = (countByKey.get(hashed) ?? 1) - 1;
|
|
164
|
+
if (next <= 0) countByKey.delete(hashed);
|
|
165
|
+
else countByKey.set(hashed, next);
|
|
166
|
+
}
|
|
167
|
+
function drop(sessionId, record) {
|
|
168
|
+
sessions.delete(sessionId);
|
|
169
|
+
decrement(record.apiKey);
|
|
170
|
+
}
|
|
171
|
+
function reserve(apiKey) {
|
|
172
|
+
const hashed = hashApiKey(apiKey);
|
|
173
|
+
countByKey.set(hashed, (countByKey.get(hashed) ?? 0) + 1);
|
|
174
|
+
let settled = false;
|
|
175
|
+
return {
|
|
176
|
+
commit({ sessionId, transport }) {
|
|
177
|
+
if (settled) return;
|
|
178
|
+
settled = true;
|
|
179
|
+
const existing = sessions.get(sessionId);
|
|
180
|
+
if (existing) drop(sessionId, existing);
|
|
181
|
+
sessions.set(sessionId, {
|
|
182
|
+
transport,
|
|
183
|
+
apiKey,
|
|
184
|
+
lastActivityAt: Date.now()
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
release() {
|
|
188
|
+
if (settled) return;
|
|
189
|
+
settled = true;
|
|
190
|
+
decrement(apiKey);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
get(sessionId) {
|
|
196
|
+
return sessions.get(sessionId);
|
|
197
|
+
},
|
|
198
|
+
reserve,
|
|
199
|
+
add({ sessionId, transport, apiKey }) {
|
|
200
|
+
reserve(apiKey).commit({ sessionId, transport });
|
|
201
|
+
},
|
|
202
|
+
touch(sessionId) {
|
|
203
|
+
const record = sessions.get(sessionId);
|
|
204
|
+
if (record) record.lastActivityAt = Date.now();
|
|
205
|
+
},
|
|
206
|
+
remove(sessionId) {
|
|
207
|
+
const record = sessions.get(sessionId);
|
|
208
|
+
if (record) drop(sessionId, record);
|
|
209
|
+
},
|
|
210
|
+
countForKey(apiKey) {
|
|
211
|
+
return countByKey.get(hashApiKey(apiKey)) ?? 0;
|
|
212
|
+
},
|
|
213
|
+
sweep() {
|
|
214
|
+
const now = Date.now();
|
|
215
|
+
for (const [sessionId, record] of sessions) {
|
|
216
|
+
if (now - record.lastActivityAt > maxAgeMs) {
|
|
217
|
+
drop(sessionId, record);
|
|
218
|
+
closeTransport(record.transport);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
closeAll() {
|
|
223
|
+
for (const [sessionId, record] of sessions) {
|
|
224
|
+
drop(sessionId, record);
|
|
225
|
+
closeTransport(record.transport);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
get size() {
|
|
229
|
+
return sessions.size;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function admitOAuthToken({
|
|
234
|
+
apiKey,
|
|
235
|
+
tokens,
|
|
236
|
+
maxPerKey
|
|
237
|
+
}) {
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
const held = [];
|
|
240
|
+
for (const [token, entry] of tokens) {
|
|
241
|
+
if (now >= entry.expiresAt) {
|
|
242
|
+
tokens.delete(token);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (entry.apiKey === apiKey) {
|
|
246
|
+
held.push({ token, expiresAt: entry.expiresAt });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
held.sort((a, b) => a.expiresAt - b.expiresAt);
|
|
250
|
+
for (let i = 0; i <= held.length - maxPerKey; i += 1) {
|
|
251
|
+
tokens.delete(held[i].token);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export {
|
|
256
|
+
DEFAULT_BIND_HOST,
|
|
257
|
+
isLoopbackHost,
|
|
258
|
+
parseAllowedOrigins,
|
|
259
|
+
isOriginAllowed,
|
|
260
|
+
hashApiKey,
|
|
261
|
+
apiKeysMatch,
|
|
262
|
+
createRateLimiter,
|
|
263
|
+
createApiKeyVerifier,
|
|
264
|
+
createSessionStore,
|
|
265
|
+
admitOAuthToken
|
|
266
|
+
};
|
|
267
|
+
//# sourceMappingURL=chunk-UJPXFA26.js.map
|