@agentronics/sdk 0.6.0 → 0.7.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/dist/chunk-3WPDIMJP.js +18 -0
- package/dist/chunk-3WPDIMJP.js.map +1 -0
- package/dist/{chunk-X7NFMFN6.js → chunk-6QQS26AP.js} +264 -69
- package/dist/chunk-6QQS26AP.js.map +1 -0
- package/dist/chunk-BPDWJCPY.js +2 -0
- package/dist/chunk-BPDWJCPY.js.map +1 -0
- package/dist/index.cjs +8 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -7
- package/dist/index.d.ts +39 -7
- package/dist/index.global.js +8 -7
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{init-only-7f8_LQAI.d.ts → init-only-C80qhujz.d.ts} +227 -30
- package/dist/{init-only-Cwll8MGz.d.cts → init-only-s9E9bVC0.d.cts} +227 -30
- package/dist/init-only.cjs +8 -7
- package/dist/init-only.cjs.map +1 -1
- package/dist/init-only.d.cts +2 -2
- package/dist/init-only.d.ts +2 -2
- package/dist/init-only.global.js +8 -7
- package/dist/init-only.global.js.map +1 -1
- package/dist/init-only.js +1 -1
- package/dist/lite.cjs +1 -1
- package/dist/lite.cjs.map +1 -1
- package/dist/lite.d.cts +2 -2
- package/dist/lite.d.ts +2 -2
- package/dist/lite.global.js +1 -1
- package/dist/lite.global.js.map +1 -1
- package/dist/lite.js +1 -1
- package/dist/middleware-Dd2T772u.d.cts +429 -0
- package/dist/middleware-Dd2T772u.d.ts +429 -0
- package/dist/next.cjs +239 -64
- package/dist/next.cjs.map +1 -1
- package/dist/next.d.cts +2 -2
- package/dist/next.d.ts +2 -2
- package/dist/next.js +2 -2
- package/dist/server.cjs +361 -66
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +103 -308
- package/dist/server.d.ts +103 -308
- package/dist/server.js +93 -1
- package/dist/server.js.map +1 -1
- package/dist/webmcp-BgKzHeQ3.d.cts +97 -0
- package/dist/webmcp-BgKzHeQ3.d.ts +97 -0
- package/package.json +4 -4
- package/dist/chunk-LLD3Y2LZ.js +0 -2
- package/dist/chunk-LLD3Y2LZ.js.map +0 -1
- package/dist/chunk-X7NFMFN6.js.map +0 -1
- package/dist/chunk-Y7JYD5RA.js +0 -17
- package/dist/chunk-Y7JYD5RA.js.map +0 -1
- package/dist/webmcp-C4ikhM4g.d.cts +0 -12
- package/dist/webmcp-C4ikhM4g.d.ts +0 -12
package/dist/next.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createAgentAuthHandler } from './chunk-
|
|
2
|
-
export { readAgentHeaders, withAgentHeaders } from './chunk-
|
|
1
|
+
import { createAgentAuthHandler } from './chunk-6QQS26AP.js';
|
|
2
|
+
export { readAgentHeaders, withAgentHeaders } from './chunk-6QQS26AP.js';
|
|
3
3
|
|
|
4
4
|
// src/server/next.ts
|
|
5
5
|
function agentronicsMiddleware(options = {}) {
|
package/dist/server.cjs
CHANGED
|
@@ -6,9 +6,12 @@ var CRAWLER_SIGNATURES = [
|
|
|
6
6
|
{ name: "GPTBot", category: "ai", pattern: /GPTBot/i },
|
|
7
7
|
{ name: "OAI-SearchBot", category: "ai", pattern: /OAI-SearchBot/i },
|
|
8
8
|
{ name: "ChatGPT-User", category: "ai", pattern: /ChatGPT-User/i },
|
|
9
|
+
{ name: "Claude-User", category: "ai", pattern: /Claude-User/i },
|
|
10
|
+
{ name: "Claude-SearchBot", category: "ai", pattern: /Claude-SearchBot/i },
|
|
9
11
|
{ name: "ClaudeBot", category: "ai", pattern: /ClaudeBot/i },
|
|
10
12
|
{ name: "Claude-Web", category: "ai", pattern: /Claude-Web/i },
|
|
11
13
|
{ name: "anthropic-ai", category: "ai", pattern: /anthropic-ai/i },
|
|
14
|
+
{ name: "Perplexity-User", category: "ai", pattern: /Perplexity-User/i },
|
|
12
15
|
{ name: "PerplexityBot", category: "ai", pattern: /PerplexityBot/i },
|
|
13
16
|
{ name: "Google-Extended", category: "ai", pattern: /Google-Extended/i },
|
|
14
17
|
{ name: "Applebot-Extended", category: "ai", pattern: /Applebot-Extended/i },
|
|
@@ -26,6 +29,96 @@ var CRAWLER_SIGNATURES = [
|
|
|
26
29
|
{ name: "Applebot", category: "search", pattern: /Applebot/i }
|
|
27
30
|
];
|
|
28
31
|
|
|
32
|
+
// src/server/ipRanges.ts
|
|
33
|
+
var PUBLISHED_IP_RANGES = {
|
|
34
|
+
GPTBot: ["https://openai.com/gptbot.json"],
|
|
35
|
+
"OAI-SearchBot": ["https://openai.com/searchbot.json"],
|
|
36
|
+
"ChatGPT-User": ["https://openai.com/chatgpt-user.json"],
|
|
37
|
+
PerplexityBot: ["https://www.perplexity.ai/perplexitybot.json"],
|
|
38
|
+
"Perplexity-User": ["https://www.perplexity.ai/perplexity-user.json"],
|
|
39
|
+
Googlebot: ["https://developers.google.com/static/crawling/ipranges/common-crawlers.json"],
|
|
40
|
+
Bingbot: ["https://www.bing.com/toolbox/bingbot.json"],
|
|
41
|
+
DuckDuckBot: ["https://duckduckgo.com/duckduckbot.json"],
|
|
42
|
+
CCBot: ["https://index.commoncrawl.org/ccbot.json"]
|
|
43
|
+
};
|
|
44
|
+
var TTL_MS = 24 * 36e5;
|
|
45
|
+
var FAILED_TTL_MS = 10 * 6e4;
|
|
46
|
+
var cache = /* @__PURE__ */ new Map();
|
|
47
|
+
async function loadUrl(url, fetcher) {
|
|
48
|
+
const hit = cache.get(url);
|
|
49
|
+
if (hit && hit.until > Date.now()) return hit.cidrs;
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetcher(url, { signal: AbortSignal.timeout(5e3), redirect: "follow" });
|
|
52
|
+
if (!res.ok) throw new Error(String(res.status));
|
|
53
|
+
const body = await res.json();
|
|
54
|
+
const cidrs = (body.prefixes ?? []).flatMap((p) => [p.ipv4Prefix, p.ipv6Prefix]).filter((c) => typeof c === "string");
|
|
55
|
+
cache.set(url, { cidrs, until: Date.now() + TTL_MS });
|
|
56
|
+
return cidrs;
|
|
57
|
+
} catch {
|
|
58
|
+
const cidrs = hit?.cidrs ?? [];
|
|
59
|
+
cache.set(url, { cidrs, until: Date.now() + FAILED_TTL_MS });
|
|
60
|
+
return cidrs;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function rangesFor(name, options = {}) {
|
|
64
|
+
const fixed = options.ipRanges?.[name];
|
|
65
|
+
if (fixed) return fixed;
|
|
66
|
+
const urls = PUBLISHED_IP_RANGES[name];
|
|
67
|
+
if (!urls) return null;
|
|
68
|
+
const fetcher = options.fetcher ?? fetch;
|
|
69
|
+
return (await Promise.all(urls.map((u) => loadUrl(u, fetcher)))).flat();
|
|
70
|
+
}
|
|
71
|
+
function v4ToBigInt(ip) {
|
|
72
|
+
const parts = ip.split(".");
|
|
73
|
+
if (parts.length !== 4) return null;
|
|
74
|
+
let n = 0n;
|
|
75
|
+
for (const p of parts) {
|
|
76
|
+
if (!/^\d{1,3}$/.test(p) || Number(p) > 255) return null;
|
|
77
|
+
n = n << 8n | BigInt(p);
|
|
78
|
+
}
|
|
79
|
+
return n;
|
|
80
|
+
}
|
|
81
|
+
function v6ToBigInt(ip) {
|
|
82
|
+
let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
83
|
+
const zone = s.indexOf("%");
|
|
84
|
+
if (zone >= 0) s = s.slice(0, zone);
|
|
85
|
+
const v4 = s.match(/(\d+\.\d+\.\d+\.\d+)$/);
|
|
86
|
+
if (v4) {
|
|
87
|
+
const n2 = v4ToBigInt(v4[1]);
|
|
88
|
+
if (n2 === null) return null;
|
|
89
|
+
s = s.slice(0, -v4[1].length) + (n2 >> 16n & 0xffffn).toString(16) + ":" + (n2 & 0xffffn).toString(16);
|
|
90
|
+
}
|
|
91
|
+
const halves = s.split("::");
|
|
92
|
+
if (halves.length > 2) return null;
|
|
93
|
+
const head = halves[0] ? halves[0].split(":") : [];
|
|
94
|
+
const tail = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
|
|
95
|
+
const missing = 8 - head.length - tail.length;
|
|
96
|
+
if (halves.length === 1 ? missing !== 0 : missing < 1) return null;
|
|
97
|
+
const groups = [...head, ...Array(halves.length === 2 ? missing : 0).fill("0"), ...tail];
|
|
98
|
+
let n = 0n;
|
|
99
|
+
for (const g of groups) {
|
|
100
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
101
|
+
n = n << 16n | BigInt(parseInt(g, 16));
|
|
102
|
+
}
|
|
103
|
+
return n;
|
|
104
|
+
}
|
|
105
|
+
function ipInCidr(ip, cidr) {
|
|
106
|
+
const [range, bitsRaw] = cidr.split("/");
|
|
107
|
+
if (!range) return false;
|
|
108
|
+
const isV6 = range.includes(":");
|
|
109
|
+
const mapped = ip.toLowerCase().match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
110
|
+
const candidate = !isV6 && mapped ? mapped[1] : ip;
|
|
111
|
+
const width = isV6 ? 128 : 32;
|
|
112
|
+
const bits = bitsRaw === void 0 ? width : Number(bitsRaw);
|
|
113
|
+
if (!Number.isInteger(bits) || bits < 0 || bits > width) return false;
|
|
114
|
+
const a = isV6 ? v6ToBigInt(candidate) : v4ToBigInt(candidate);
|
|
115
|
+
const b = isV6 ? v6ToBigInt(range) : v4ToBigInt(range);
|
|
116
|
+
if (a === null || b === null) return false;
|
|
117
|
+
const shift = BigInt(width - bits);
|
|
118
|
+
return a >> shift === b >> shift;
|
|
119
|
+
}
|
|
120
|
+
var ipInRanges = (ip, cidrs) => cidrs.some((c) => ipInCidr(ip, c));
|
|
121
|
+
|
|
29
122
|
// src/server/crawlers.ts
|
|
30
123
|
var RDNS_DOMAINS = {
|
|
31
124
|
Googlebot: ["googlebot.com", "google.com", "googleusercontent.com"],
|
|
@@ -64,14 +157,19 @@ async function nodeResolver() {
|
|
|
64
157
|
}
|
|
65
158
|
}
|
|
66
159
|
var rdnsCache = /* @__PURE__ */ new Map();
|
|
67
|
-
async function verifyCrawler(claim, ip, resolver) {
|
|
160
|
+
async function verifyCrawler(claim, ip, resolver, ranges = {}) {
|
|
68
161
|
const domains = RDNS_DOMAINS[claim.name];
|
|
69
|
-
|
|
162
|
+
const cidrs = await rangesFor(claim.name, ranges);
|
|
163
|
+
if (!domains && !cidrs) return { status: "unverified", name: claim.name, reason: "no_verification_method_for_vendor" };
|
|
70
164
|
if (!ip) return { status: "unverified", name: claim.name, reason: "client_ip_unknown" };
|
|
165
|
+
if (cidrs?.length && ipInRanges(ip, cidrs)) {
|
|
166
|
+
return { status: "verified", name: claim.name, host: ip, ip, via: "ip-range" };
|
|
167
|
+
}
|
|
168
|
+
if (!domains) return { status: "unverified", name: claim.name, reason: "ip_outside_published_ranges" };
|
|
71
169
|
const key = `${claim.name}|${ip}`;
|
|
72
170
|
const hit = rdnsCache.get(key);
|
|
73
171
|
if (hit && hit.until > Date.now()) {
|
|
74
|
-
return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
|
|
172
|
+
return hit.ok ? { status: "verified", name: claim.name, host: hit.host, ip, via: "rdns" } : { status: "unverified", name: claim.name, reason: "rdns_mismatch" };
|
|
75
173
|
}
|
|
76
174
|
const dns = resolver ?? await nodeResolver();
|
|
77
175
|
if (!dns) return { status: "unverified", name: claim.name, reason: "dns_unavailable_in_runtime" };
|
|
@@ -87,7 +185,7 @@ async function verifyCrawler(claim, ip, resolver) {
|
|
|
87
185
|
const forward = await dns.lookup(host);
|
|
88
186
|
if (forward.includes(ip)) {
|
|
89
187
|
remember(true, host);
|
|
90
|
-
return { status: "verified", name: claim.name, host, ip };
|
|
188
|
+
return { status: "verified", name: claim.name, host, ip, via: "rdns" };
|
|
91
189
|
}
|
|
92
190
|
}
|
|
93
191
|
remember(false);
|
|
@@ -320,6 +418,135 @@ function bytesToBase64(bytes) {
|
|
|
320
418
|
}
|
|
321
419
|
var bytesToBase64Url = (bytes) => bytesToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
322
420
|
|
|
421
|
+
// src/server/oauth.ts
|
|
422
|
+
var josePromise;
|
|
423
|
+
var loadJose = () => josePromise ??= import('jose');
|
|
424
|
+
var remoteSets = /* @__PURE__ */ new Map();
|
|
425
|
+
var ASYMMETRIC = ["RS256", "PS256", "ES256", "EdDSA"];
|
|
426
|
+
function unverifiedIssuer(token) {
|
|
427
|
+
try {
|
|
428
|
+
const payload = token.split(".")[1];
|
|
429
|
+
const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
430
|
+
const iss = JSON.parse(json).iss;
|
|
431
|
+
return typeof iss === "string" ? iss : null;
|
|
432
|
+
} catch {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
var str = (v) => typeof v === "string" && v ? v : void 0;
|
|
437
|
+
async function sha256B64u(text) {
|
|
438
|
+
const d = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
439
|
+
return bytesToBase64Url(new Uint8Array(d));
|
|
440
|
+
}
|
|
441
|
+
var htuOf = (url) => {
|
|
442
|
+
const u = new URL(url);
|
|
443
|
+
return `${u.origin}${u.pathname}`;
|
|
444
|
+
};
|
|
445
|
+
async function verifyDpopProof(jose, proof, token, jkt, request, replay) {
|
|
446
|
+
try {
|
|
447
|
+
const { payload, protectedHeader } = await jose.jwtVerify(proof, jose.EmbeddedJWK, {
|
|
448
|
+
typ: "dpop+jwt",
|
|
449
|
+
algorithms: ASYMMETRIC
|
|
450
|
+
});
|
|
451
|
+
const jwk = protectedHeader.jwk;
|
|
452
|
+
if (!jwk) return "dpop_missing_jwk";
|
|
453
|
+
if (await jose.calculateJwkThumbprint(jwk, "sha256") !== jkt) return "dpop_key_mismatch";
|
|
454
|
+
if (str(payload.htm)?.toUpperCase() !== request.method.toUpperCase()) return "dpop_htm_mismatch";
|
|
455
|
+
if (str(payload.htu) === void 0 || htuOf(str(payload.htu)) !== htuOf(request.url)) return "dpop_htu_mismatch";
|
|
456
|
+
const iat = typeof payload.iat === "number" ? payload.iat : NaN;
|
|
457
|
+
if (!Number.isFinite(iat) || Math.abs(Date.now() / 1e3 - iat) > 300) return "dpop_stale";
|
|
458
|
+
if (str(payload.ath) !== await sha256B64u(token)) return "dpop_ath_mismatch";
|
|
459
|
+
const jti = str(payload.jti);
|
|
460
|
+
if (!jti) return "dpop_missing_jti";
|
|
461
|
+
if (replay) {
|
|
462
|
+
const key = `dpop:${jkt}:${jti}`;
|
|
463
|
+
if (await replay.has(key)) return "dpop_replayed";
|
|
464
|
+
await replay.add(key, 600);
|
|
465
|
+
}
|
|
466
|
+
return null;
|
|
467
|
+
} catch (e) {
|
|
468
|
+
return `dpop_invalid:${e.code ?? e.message}`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function verifyOAuthToken(token, o, ctx) {
|
|
472
|
+
const jose = await loadJose();
|
|
473
|
+
const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
|
|
474
|
+
const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
|
|
475
|
+
let s = remoteSets.get(uri);
|
|
476
|
+
if (!s) {
|
|
477
|
+
s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
|
|
478
|
+
remoteSets.set(uri, s);
|
|
479
|
+
}
|
|
480
|
+
return s;
|
|
481
|
+
})();
|
|
482
|
+
let payload;
|
|
483
|
+
try {
|
|
484
|
+
;
|
|
485
|
+
({ payload } = await jose.jwtVerify(token, keySet, {
|
|
486
|
+
issuer: o.issuer,
|
|
487
|
+
audience: o.audience,
|
|
488
|
+
algorithms: o.algorithms ?? ASYMMETRIC,
|
|
489
|
+
clockTolerance: o.clockToleranceSec ?? 30
|
|
490
|
+
}));
|
|
491
|
+
} catch (e) {
|
|
492
|
+
return { error: `invalid_token:${e.code ?? e.message}` };
|
|
493
|
+
}
|
|
494
|
+
const jkt = str(payload.cnf?.jkt);
|
|
495
|
+
let dpop = false;
|
|
496
|
+
if (jkt) {
|
|
497
|
+
const proof = ctx.request.headers.get("dpop");
|
|
498
|
+
if (ctx.scheme !== "dpop" || !proof) return { error: "dpop_proof_required" };
|
|
499
|
+
const bad = await verifyDpopProof(jose, proof, token, jkt, ctx.request, ctx.replayCache);
|
|
500
|
+
if (bad) return { error: bad };
|
|
501
|
+
dpop = true;
|
|
502
|
+
} else if (o.requireDpop) {
|
|
503
|
+
return { error: "token_not_dpop_bound" };
|
|
504
|
+
}
|
|
505
|
+
if (o.requiredScopes?.length) {
|
|
506
|
+
const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
|
|
507
|
+
const missing = o.requiredScopes.filter((s) => !granted.has(s));
|
|
508
|
+
if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
|
|
509
|
+
}
|
|
510
|
+
const act = payload.act;
|
|
511
|
+
const clientId = str(payload.client_id) ?? str(payload.azp);
|
|
512
|
+
const sub = str(payload.sub);
|
|
513
|
+
const agentId = str(act?.client_id) ?? str(act?.sub) ?? clientId ?? sub;
|
|
514
|
+
if (!agentId) return { error: "token_has_no_client_id" };
|
|
515
|
+
const user = act ? sub : sub && clientId && sub !== clientId ? sub : void 0;
|
|
516
|
+
const agent = {
|
|
517
|
+
id: `oauth2:${agentId}`,
|
|
518
|
+
name: str(payload.agent_name) ?? str(payload.client_name) ?? agentId,
|
|
519
|
+
vendor: str(payload.agent_vendor) ?? null,
|
|
520
|
+
claims: { iss: payload.iss, sub, scope: payload.scope ?? payload.scp, ...act ? { act } : {} },
|
|
521
|
+
dpop
|
|
522
|
+
};
|
|
523
|
+
if (user) {
|
|
524
|
+
const onBehalfOf = { sub: user };
|
|
525
|
+
if (str(payload.email)) onBehalfOf.email = str(payload.email);
|
|
526
|
+
if (str(payload.name)) onBehalfOf.name = str(payload.name);
|
|
527
|
+
agent.onBehalfOf = onBehalfOf;
|
|
528
|
+
}
|
|
529
|
+
return agent;
|
|
530
|
+
}
|
|
531
|
+
var PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
|
|
532
|
+
function protectedResourceMetadata(o) {
|
|
533
|
+
return {
|
|
534
|
+
resource: o.resource,
|
|
535
|
+
authorization_servers: o.authorizationServers,
|
|
536
|
+
bearer_methods_supported: ["header"],
|
|
537
|
+
...o.scopesSupported ? { scopes_supported: o.scopesSupported } : {},
|
|
538
|
+
...o.resourceName ? { resource_name: o.resourceName } : {},
|
|
539
|
+
...o.dpop === false ? {} : { dpop_signing_alg_values_supported: ["ES256", "EdDSA", "RS256", "PS256"] }
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function oauthChallenge(resource, opts = {}) {
|
|
543
|
+
const meta = `${resource.replace(/\/$/, "")}${PROTECTED_RESOURCE_PATH}`;
|
|
544
|
+
const parts = [`resource_metadata="${meta}"`];
|
|
545
|
+
if (opts.error) parts.push(`error="${opts.error}"`);
|
|
546
|
+
if (opts.scope) parts.push(`scope="${opts.scope}"`);
|
|
547
|
+
return `Bearer ${parts.join(", ")}`;
|
|
548
|
+
}
|
|
549
|
+
|
|
323
550
|
// src/server/httpSignatures.ts
|
|
324
551
|
var SignatureError = class extends Error {
|
|
325
552
|
constructor(code, message) {
|
|
@@ -758,61 +985,15 @@ var KNOWN_SIGNERS = {
|
|
|
758
985
|
};
|
|
759
986
|
var GENERIC_AGENT_UA = /\b(bot|crawler|spider|agent|headless|scraper)\b/i;
|
|
760
987
|
var JWT_SHAPE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/;
|
|
761
|
-
function
|
|
988
|
+
function credential(request) {
|
|
762
989
|
const h = request.headers.get("authorization");
|
|
763
|
-
|
|
764
|
-
}
|
|
765
|
-
function unverifiedJwtIssuer(token) {
|
|
766
|
-
try {
|
|
767
|
-
const payload = token.split(".")[1];
|
|
768
|
-
const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
769
|
-
const iss = JSON.parse(json).iss;
|
|
770
|
-
return typeof iss === "string" ? iss : null;
|
|
771
|
-
} catch {
|
|
772
|
-
return null;
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
var josePromise;
|
|
776
|
-
var remoteSets = /* @__PURE__ */ new Map();
|
|
777
|
-
async function verifyOAuth2(token, o) {
|
|
778
|
-
josePromise ??= import('jose');
|
|
779
|
-
const jose = await josePromise;
|
|
780
|
-
const keySet = o.jwks ? jose.createLocalJWKSet(o.jwks) : (() => {
|
|
781
|
-
const uri = o.jwksUri ?? `${o.issuer.replace(/\/$/, "")}/.well-known/jwks.json`;
|
|
782
|
-
let s = remoteSets.get(uri);
|
|
783
|
-
if (!s) {
|
|
784
|
-
s = jose.createRemoteJWKSet(new URL(uri), { cacheMaxAge: 36e5, cooldownDuration: 3e4 });
|
|
785
|
-
remoteSets.set(uri, s);
|
|
786
|
-
}
|
|
787
|
-
return s;
|
|
788
|
-
})();
|
|
789
|
-
try {
|
|
790
|
-
const { payload } = await jose.jwtVerify(token, keySet, {
|
|
791
|
-
issuer: o.issuer,
|
|
792
|
-
audience: o.audience,
|
|
793
|
-
algorithms: o.algorithms ?? ["RS256", "PS256", "ES256", "EdDSA"],
|
|
794
|
-
clockTolerance: o.clockToleranceSec ?? 30
|
|
795
|
-
});
|
|
796
|
-
if (o.requiredScopes?.length) {
|
|
797
|
-
const granted = new Set(String(payload.scope ?? payload.scp ?? "").split(/\s+/));
|
|
798
|
-
const missing = o.requiredScopes.filter((s) => !granted.has(s));
|
|
799
|
-
if (missing.length) return { error: `missing_scopes:${missing.join(",")}` };
|
|
800
|
-
}
|
|
801
|
-
const clientId = String(payload.client_id ?? payload.azp ?? payload.sub ?? "");
|
|
802
|
-
if (!clientId) return { error: "token_has_no_client_id" };
|
|
803
|
-
return {
|
|
804
|
-
id: `oauth2:${clientId}`,
|
|
805
|
-
name: typeof payload.agent_name === "string" ? payload.agent_name : clientId,
|
|
806
|
-
vendor: typeof payload.agent_vendor === "string" ? payload.agent_vendor : null,
|
|
807
|
-
method: "oauth2",
|
|
808
|
-
claims: { sub: payload.sub, scope: payload.scope ?? payload.scp }
|
|
809
|
-
};
|
|
810
|
-
} catch (e) {
|
|
811
|
-
return { error: `invalid_token:${e.code ?? e.message}` };
|
|
812
|
-
}
|
|
990
|
+
const m = h?.match(/^(bearer|dpop)\s+(.+)$/i);
|
|
991
|
+
return m ? { token: m[2].trim(), scheme: m[1].toLowerCase() } : null;
|
|
813
992
|
}
|
|
814
993
|
function createAgentAuth(options = {}) {
|
|
815
|
-
const
|
|
994
|
+
const replayCache = options.replayCache ?? memoryReplayCache();
|
|
995
|
+
const wba = options.webBotAuth === false ? null : { replayCache, ...typeof options.webBotAuth === "object" ? options.webBotAuth : {} };
|
|
996
|
+
const issuers = options.oauth2 ? Array.isArray(options.oauth2) ? options.oauth2 : [options.oauth2] : [];
|
|
816
997
|
const crawlers = options.crawlers === false ? null : typeof options.crawlers === "object" ? options.crawlers : {};
|
|
817
998
|
const keyPrefix = options.apiKey?.prefix ?? "agk_";
|
|
818
999
|
async function authenticate(request) {
|
|
@@ -841,7 +1022,8 @@ function createAgentAuth(options = {}) {
|
|
|
841
1022
|
claimedName ??= r.agentUrl ? new URL(r.agentUrl).hostname : void 0;
|
|
842
1023
|
}
|
|
843
1024
|
}
|
|
844
|
-
const
|
|
1025
|
+
const cred = credential(request);
|
|
1026
|
+
const token = cred?.token ?? request.headers.get("x-agent-key");
|
|
845
1027
|
if (token && options.apiKey && token.startsWith(keyPrefix)) {
|
|
846
1028
|
attempted.push("api-key");
|
|
847
1029
|
const id = await options.apiKey.verify(token);
|
|
@@ -858,12 +1040,23 @@ function createAgentAuth(options = {}) {
|
|
|
858
1040
|
};
|
|
859
1041
|
}
|
|
860
1042
|
reasons.push("api-key:unknown_or_revoked");
|
|
861
|
-
} else if (
|
|
862
|
-
const iss =
|
|
863
|
-
|
|
1043
|
+
} else if (cred && issuers.length && JWT_SHAPE.test(cred.token)) {
|
|
1044
|
+
const iss = unverifiedIssuer(cred.token);
|
|
1045
|
+
const config = issuers.find((i) => i.issuer === iss);
|
|
1046
|
+
if (config) {
|
|
864
1047
|
attempted.push("oauth2");
|
|
865
|
-
const r = await
|
|
866
|
-
if (!("error" in r))
|
|
1048
|
+
const r = await verifyOAuthToken(cred.token, config, { request, scheme: cred.scheme, replayCache });
|
|
1049
|
+
if (!("error" in r)) {
|
|
1050
|
+
const agent = {
|
|
1051
|
+
id: r.id,
|
|
1052
|
+
name: r.name,
|
|
1053
|
+
vendor: r.vendor,
|
|
1054
|
+
method: "oauth2",
|
|
1055
|
+
claims: { ...r.claims, dpop: r.dpop }
|
|
1056
|
+
};
|
|
1057
|
+
if (r.onBehalfOf) agent.onBehalfOf = r.onBehalfOf;
|
|
1058
|
+
return { status: "verified", agent };
|
|
1059
|
+
}
|
|
867
1060
|
reasons.push(`oauth2:${r.error}`);
|
|
868
1061
|
}
|
|
869
1062
|
}
|
|
@@ -873,11 +1066,11 @@ function createAgentAuth(options = {}) {
|
|
|
873
1066
|
claimedName ??= claim.name;
|
|
874
1067
|
if (crawlers) {
|
|
875
1068
|
attempted.push("verified-crawler");
|
|
876
|
-
const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver);
|
|
1069
|
+
const v = await verifyCrawler(claim, clientIp(request, crawlers.clientIp), crawlers.resolver, crawlers);
|
|
877
1070
|
if (v.status === "verified") {
|
|
878
1071
|
return {
|
|
879
1072
|
status: "verified",
|
|
880
|
-
agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host } }
|
|
1073
|
+
agent: { id: `crawler:${claim.name}`, name: claim.name, vendor: null, method: "verified-crawler", claims: { host: v.host, via: v.via } }
|
|
881
1074
|
};
|
|
882
1075
|
}
|
|
883
1076
|
reasons.push(`crawler:${v.reason}`);
|
|
@@ -938,6 +1131,7 @@ function toTraceEvent(result, ctx) {
|
|
|
938
1131
|
if (result.status === "none") return null;
|
|
939
1132
|
const url = new URL(ctx.request.url);
|
|
940
1133
|
const verified = result.status === "verified";
|
|
1134
|
+
const delegated = verified && Boolean(result.agent.onBehalfOf);
|
|
941
1135
|
return {
|
|
942
1136
|
id: `evt_${crypto.randomUUID()}`,
|
|
943
1137
|
siteId: ctx.siteId,
|
|
@@ -946,12 +1140,13 @@ function toTraceEvent(result, ctx) {
|
|
|
946
1140
|
type: "auth.identity_presented",
|
|
947
1141
|
agent: {
|
|
948
1142
|
class: "crawler",
|
|
949
|
-
|
|
1143
|
+
// Unverified claims stay "declared"; a token acting for a signed-in user is "linked".
|
|
1144
|
+
trust: delegated ? "linked" : verified ? "verified" : "declared",
|
|
950
1145
|
confidence: verified ? 1 : 0.5,
|
|
951
1146
|
vendor: verified ? result.agent.vendor ?? result.agent.name : result.claimedName ?? null,
|
|
952
1147
|
userAgent: ctx.request.headers.get("user-agent"),
|
|
953
1148
|
detectionVersion: "2026.09",
|
|
954
|
-
signals: { surface: "server" }
|
|
1149
|
+
signals: { surface: "server", ...verified ? { method: result.agent.method } : {} }
|
|
955
1150
|
},
|
|
956
1151
|
outcome: verified ? "success" : "error",
|
|
957
1152
|
...verified ? {} : { error: result.reason },
|
|
@@ -1002,24 +1197,124 @@ function expressAgentAuth(options = {}) {
|
|
|
1002
1197
|
};
|
|
1003
1198
|
}
|
|
1004
1199
|
|
|
1200
|
+
// src/observability/exporters/intel.ts
|
|
1201
|
+
var INGEST_PATH = "/v1/sdk/events";
|
|
1202
|
+
var createIntelExporter = ({
|
|
1203
|
+
url,
|
|
1204
|
+
ingestKey,
|
|
1205
|
+
fetcher = fetch
|
|
1206
|
+
}) => {
|
|
1207
|
+
const base = url.replace(/\/$/, "");
|
|
1208
|
+
const endpoint = base.endsWith(INGEST_PATH) ? base : `${base}${INGEST_PATH}`;
|
|
1209
|
+
return {
|
|
1210
|
+
name: "intel",
|
|
1211
|
+
async export(events) {
|
|
1212
|
+
const batch = { events };
|
|
1213
|
+
const response = await fetcher(endpoint, {
|
|
1214
|
+
method: "POST",
|
|
1215
|
+
headers: {
|
|
1216
|
+
"content-type": "application/json",
|
|
1217
|
+
authorization: `Bearer ${ingestKey}`
|
|
1218
|
+
},
|
|
1219
|
+
body: JSON.stringify(batch)
|
|
1220
|
+
});
|
|
1221
|
+
if (!response.ok) {
|
|
1222
|
+
throw new Error(`Intel exporter failed with status ${response.status}.`);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
// src/server/relay.ts
|
|
1229
|
+
var KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
1230
|
+
"agent.detected",
|
|
1231
|
+
"agent.missed",
|
|
1232
|
+
"auth.identity_presented",
|
|
1233
|
+
"auth.identity_cleared",
|
|
1234
|
+
"authz.policies_set",
|
|
1235
|
+
"authz.evaluated",
|
|
1236
|
+
"memory.accessed",
|
|
1237
|
+
"memory.updated",
|
|
1238
|
+
"tool.registered",
|
|
1239
|
+
"tool.executed",
|
|
1240
|
+
"tool.surfaced",
|
|
1241
|
+
"tool.progressed",
|
|
1242
|
+
"sdk.error"
|
|
1243
|
+
]);
|
|
1244
|
+
function createTraceRelay(options) {
|
|
1245
|
+
const exporter = createIntelExporter({ url: options.url, ingestKey: options.ingestKey, ...options.fetcher ? { fetcher: options.fetcher } : {} });
|
|
1246
|
+
const max = options.maxEvents ?? 100;
|
|
1247
|
+
return async function relay(request) {
|
|
1248
|
+
if (request.method !== "POST") return new Response(null, { status: 405 });
|
|
1249
|
+
let body;
|
|
1250
|
+
try {
|
|
1251
|
+
const text = await request.text();
|
|
1252
|
+
if (text.length > 512e3) return new Response(null, { status: 413 });
|
|
1253
|
+
body = JSON.parse(text);
|
|
1254
|
+
} catch {
|
|
1255
|
+
return new Response(null, { status: 400 });
|
|
1256
|
+
}
|
|
1257
|
+
const events = (Array.isArray(body.events) ? body.events : []).filter((e) => typeof e === "object" && e !== null && KNOWN_TYPES.has(String(e.type))).slice(0, max).map((e) => ({ ...e, siteId: options.siteId }));
|
|
1258
|
+
if (events.length) {
|
|
1259
|
+
await Promise.all([
|
|
1260
|
+
Promise.resolve(exporter.export(events)).catch(() => void 0),
|
|
1261
|
+
options.forward ? Promise.resolve().then(() => options.forward(events)).catch(() => void 0) : void 0
|
|
1262
|
+
]);
|
|
1263
|
+
}
|
|
1264
|
+
return new Response(null, { status: 202 });
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// src/server/agentContext.ts
|
|
1269
|
+
function createAgentContextHandler({ sync, cacheSeconds = 300 }) {
|
|
1270
|
+
let cached = null;
|
|
1271
|
+
return async function agentContext() {
|
|
1272
|
+
if (!cached || cached.until <= Date.now()) {
|
|
1273
|
+
try {
|
|
1274
|
+
const { siteMemory } = await sync.pullConfig();
|
|
1275
|
+
if (siteMemory && Object.keys(siteMemory).length) cached = { body: JSON.stringify(siteMemory), until: Date.now() + cacheSeconds * 1e3 };
|
|
1276
|
+
} catch {
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (!cached) return new Response(JSON.stringify({ error: "no_site_memory" }), { status: 404, headers: { "content-type": "application/json" } });
|
|
1280
|
+
return new Response(cached.body, {
|
|
1281
|
+
headers: {
|
|
1282
|
+
"content-type": "application/json",
|
|
1283
|
+
"cache-control": `public, max-age=${cacheSeconds}`,
|
|
1284
|
+
"access-control-allow-origin": "*"
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1005
1290
|
exports.AGENT_HEADERS = AGENT_HEADERS;
|
|
1006
1291
|
exports.DIRECTORY_PATH = DIRECTORY_PATH;
|
|
1292
|
+
exports.PROTECTED_RESOURCE_PATH = PROTECTED_RESOURCE_PATH;
|
|
1293
|
+
exports.PUBLISHED_IP_RANGES = PUBLISHED_IP_RANGES;
|
|
1007
1294
|
exports.RDNS_DOMAINS = RDNS_DOMAINS;
|
|
1008
1295
|
exports.WEB_BOT_AUTH_TAG = WEB_BOT_AUTH_TAG;
|
|
1009
1296
|
exports.classifyUserAgent = classifyUserAgent;
|
|
1010
1297
|
exports.clientIp = clientIp;
|
|
1011
1298
|
exports.createAgentAuth = createAgentAuth;
|
|
1012
1299
|
exports.createAgentAuthHandler = createAgentAuthHandler;
|
|
1300
|
+
exports.createAgentContextHandler = createAgentContextHandler;
|
|
1301
|
+
exports.createTraceRelay = createTraceRelay;
|
|
1013
1302
|
exports.expressAgentAuth = expressAgentAuth;
|
|
1014
1303
|
exports.generateAgentKey = generateAgentKey;
|
|
1015
1304
|
exports.hashAgentKey = hashAgentKey;
|
|
1305
|
+
exports.ipInCidr = ipInCidr;
|
|
1306
|
+
exports.ipInRanges = ipInRanges;
|
|
1016
1307
|
exports.jwkThumbprint = jwkThumbprint;
|
|
1017
1308
|
exports.memoryReplayCache = memoryReplayCache;
|
|
1309
|
+
exports.oauthChallenge = oauthChallenge;
|
|
1310
|
+
exports.protectedResourceMetadata = protectedResourceMetadata;
|
|
1311
|
+
exports.rangesFor = rangesFor;
|
|
1018
1312
|
exports.readAgentHeaders = readAgentHeaders;
|
|
1019
1313
|
exports.signatureBase = signatureBase;
|
|
1020
1314
|
exports.staticKeyVerifier = staticKeyVerifier;
|
|
1021
1315
|
exports.toTraceEvent = toTraceEvent;
|
|
1022
1316
|
exports.verifyCrawler = verifyCrawler;
|
|
1317
|
+
exports.verifyOAuthToken = verifyOAuthToken;
|
|
1023
1318
|
exports.verifyWebBotAuth = verifyWebBotAuth;
|
|
1024
1319
|
exports.withAgentHeaders = withAgentHeaders;
|
|
1025
1320
|
//# sourceMappingURL=server.cjs.map
|