@adcp/sdk 14.0.0 → 14.1.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/lib/adapters/implicit-account-store.d.mts +12 -7
- package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
- package/dist/lib/adapters/implicit-account-store.js +69 -15
- package/dist/lib/adapters/implicit-account-store.mjs +69 -15
- package/dist/lib/core/AgentClient.d.mts +1 -0
- package/dist/lib/core/AgentClient.d.ts +1 -0
- package/dist/lib/core/AgentClient.js +3 -0
- package/dist/lib/core/AgentClient.mjs +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +31 -1
- package/dist/lib/core/SingleAgentClient.d.ts +31 -1
- package/dist/lib/core/SingleAgentClient.js +355 -35
- package/dist/lib/core/SingleAgentClient.mjs +365 -37
- package/dist/lib/core/TaskExecutor.js +2 -1
- package/dist/lib/core/TaskExecutor.mjs +2 -1
- package/dist/lib/core/account-key.d.mts +3 -0
- package/dist/lib/core/account-key.d.ts +3 -0
- package/dist/lib/core/account-key.js +41 -0
- package/dist/lib/core/account-key.mjs +17 -0
- package/dist/lib/core/account-resolution.d.mts +2 -0
- package/dist/lib/core/account-resolution.d.ts +2 -0
- package/dist/lib/core/buyer-account-registry.d.mts +65 -0
- package/dist/lib/core/buyer-account-registry.d.ts +65 -0
- package/dist/lib/core/buyer-account-registry.js +518 -0
- package/dist/lib/core/buyer-account-registry.mjs +494 -0
- package/dist/lib/core/product-cache.d.mts +18 -0
- package/dist/lib/core/product-cache.d.ts +18 -0
- package/dist/lib/core/product-cache.js +137 -0
- package/dist/lib/core/product-cache.mjs +112 -0
- package/dist/lib/errors/index.d.mts +40 -1
- package/dist/lib/errors/index.d.ts +40 -1
- package/dist/lib/errors/index.js +69 -3
- package/dist/lib/errors/index.mjs +64 -3
- package/dist/lib/governance/authorization.d.mts +17 -1
- package/dist/lib/governance/authorization.d.ts +17 -1
- package/dist/lib/governance/authorization.js +55 -7
- package/dist/lib/governance/authorization.mjs +59 -7
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +2 -0
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/index.d.mts +5 -3
- package/dist/lib/index.d.ts +5 -3
- package/dist/lib/index.js +20 -0
- package/dist/lib/index.mjs +19 -0
- package/dist/lib/protocols/a2a.js +9 -1
- package/dist/lib/protocols/a2a.mjs +9 -1
- package/dist/lib/protocols/index.js +9 -2
- package/dist/lib/protocols/index.mjs +9 -2
- package/dist/lib/protocols/mcp-modern.js +2 -1
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +5 -2
- package/dist/lib/protocols/mcp.mjs +5 -2
- package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
- package/dist/lib/protocols/rawResponseCapture.js +41 -29
- package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
- package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
- package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
- package/dist/lib/protocols/signedRequestRejection.js +209 -0
- package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
- package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
- package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
- package/dist/lib/protocols/transportDiagnostics.js +2 -0
- package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
- package/dist/lib/registry/types.generated.d.mts +112 -45
- package/dist/lib/registry/types.generated.d.ts +112 -45
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-provisioning.d.mts +2 -0
- package/dist/lib/server/account-provisioning.d.ts +2 -0
- package/dist/lib/server/account-provisioning.js +30 -0
- package/dist/lib/server/account-provisioning.mjs +6 -0
- package/dist/lib/server/account-reference-warnings.d.mts +12 -0
- package/dist/lib/server/account-reference-warnings.d.ts +12 -0
- package/dist/lib/server/account-reference-warnings.js +48 -0
- package/dist/lib/server/account-reference-warnings.mjs +23 -0
- package/dist/lib/server/auth-signature.js +1 -0
- package/dist/lib/server/auth-signature.mjs +1 -0
- package/dist/lib/server/create-adcp-server.d.mts +34 -0
- package/dist/lib/server/create-adcp-server.d.ts +34 -0
- package/dist/lib/server/create-adcp-server.js +225 -14
- package/dist/lib/server/create-adcp-server.mjs +225 -14
- package/dist/lib/server/decisioning/account.d.mts +2 -0
- package/dist/lib/server/decisioning/account.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.js +0 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +1 -1
- package/dist/lib/signing/agent-resolver/errors.d.ts +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +2 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +2 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +2 -1
- package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
- package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.js +101 -132
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +102 -133
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
- package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
- package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
- package/dist/lib/signing/brand-jwks.d.mts +27 -75
- package/dist/lib/signing/brand-jwks.d.ts +27 -75
- package/dist/lib/signing/brand-jwks.js +112 -182
- package/dist/lib/signing/brand-jwks.mjs +112 -182
- package/dist/lib/signing/errors.d.mts +3 -1
- package/dist/lib/signing/errors.d.ts +3 -1
- package/dist/lib/signing/errors.js +4 -1
- package/dist/lib/signing/errors.mjs +4 -1
- package/dist/lib/signing/jwks-https.d.mts +7 -0
- package/dist/lib/signing/jwks-https.d.ts +7 -0
- package/dist/lib/signing/jwks-https.js +31 -8
- package/dist/lib/signing/jwks-https.mjs +31 -8
- package/dist/lib/signing/jwks.d.mts +8 -0
- package/dist/lib/signing/jwks.d.ts +8 -0
- package/dist/lib/signing/middleware.js +2 -1
- package/dist/lib/signing/middleware.mjs +2 -1
- package/dist/lib/signing/publisher-pins.d.mts +11 -0
- package/dist/lib/signing/publisher-pins.d.ts +11 -0
- package/dist/lib/signing/publisher-pins.js +125 -0
- package/dist/lib/signing/publisher-pins.mjs +101 -0
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/types.d.mts +5 -0
- package/dist/lib/signing/types.d.ts +5 -0
- package/dist/lib/signing/verifier.js +49 -4
- package/dist/lib/signing/verifier.mjs +49 -4
- package/dist/lib/signing/webhook-verifier.d.mts +7 -2
- package/dist/lib/signing/webhook-verifier.d.ts +7 -2
- package/dist/lib/signing/webhook-verifier.js +42 -2
- package/dist/lib/signing/webhook-verifier.mjs +43 -3
- package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
- package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
- package/dist/lib/testing/storyboard/account-policy.js +35 -0
- package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
- package/dist/lib/testing/storyboard/context.js +6 -0
- package/dist/lib/testing/storyboard/context.mjs +6 -0
- package/dist/lib/testing/storyboard/request-builder.js +12 -2
- package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
- package/dist/lib/testing/storyboard/runner.js +3 -2
- package/dist/lib/testing/storyboard/runner.mjs +3 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/wholesale-feed-sync/sync.d.mts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.d.ts +1 -0
- package/dist/lib/wholesale-feed-sync/sync.js +92 -21
- package/dist/lib/wholesale-feed-sync/sync.mjs +92 -21
- package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
- package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +2 -2
- package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +104 -0
- package/docs/guides/SIGNING-GUIDE.md +16 -7
- package/docs/guides/account-resolution.md +86 -0
- package/docs/llms.txt +3 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-4.x-to-5.x.md +1 -0
- package/docs/migration-agent-resolution-3.3.md +123 -0
- package/docs/recipes/verifying-inbound-webhooks.md +56 -15
- package/package.json +2 -2
|
@@ -1,32 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Receiver-side ergonomic: resolve a sender's JWKS by fetching their
|
|
3
|
-
* `brand.json`, extracting the `jwks_uri` for the agent whose webhooks we're
|
|
4
|
-
* verifying, and delegating to {@link HttpsJwksResolver}.
|
|
5
|
-
*
|
|
6
|
-
* Hand the resulting instance to `verifyWebhookSignature.jwks` (or
|
|
7
|
-
* `verifyRequestSignature.jwks`) and the receiver never has to know where the
|
|
8
|
-
* sender hosts their keys — brand.json is the single source of truth.
|
|
9
|
-
*
|
|
10
|
-
* Follows the two documented redirect variants of `brand.json`
|
|
11
|
-
* (`authoritative_location` and `house`) up to a caller-configurable hop
|
|
12
|
-
* depth. When the selected agent has no `jwks_uri`, falls back to
|
|
13
|
-
* `/.well-known/jwks.json` on the origin of the agent's `url` — but only
|
|
14
|
-
* when that origin matches the final brand.json origin. The spec's
|
|
15
|
-
* well-known fallback exists so publishers hosting brand.json and their
|
|
16
|
-
* agents on the same origin can skip the explicit `jwks_uri`; accepting a
|
|
17
|
-
* cross-origin fallback would let an attacker pivot the trust anchor to a
|
|
18
|
-
* controlled host with a permissive JWKS.
|
|
19
|
-
*
|
|
20
|
-
* Caching is stacked with the inner {@link HttpsJwksResolver}: brand.json
|
|
21
|
-
* honors its own `Cache-Control`/`ETag` (bounded by `maxAgeSeconds`), and
|
|
22
|
-
* unknown-kid refreshes cascade — first to the JWKS endpoint, then (if the
|
|
23
|
-
* JWKS still doesn't have the kid and the brand.json cooldown has elapsed) to
|
|
24
|
-
* brand.json itself, in case the sender rotated `jwks_uri`.
|
|
25
|
-
*/
|
|
1
|
+
/** Capability-confirmed sender key discovery and SSRF-safe brand.json fetching. */
|
|
26
2
|
import { type SsrfDnsLookup } from '../net';
|
|
27
3
|
import type { JwksResolver } from './jwks';
|
|
28
4
|
import { type HttpsJwksResolverOptions } from './jwks-https';
|
|
29
5
|
import type { AdcpJsonWebKey } from './types';
|
|
6
|
+
import type { AgentProtocol, FetchCapabilitiesFn } from './agent-resolver/resolve-agent';
|
|
7
|
+
import type { JwksResolution } from './jwks';
|
|
30
8
|
export type BrandAgentType = 'brand' | 'rights' | 'measurement' | 'governance' | 'creative' | 'sales' | 'buying' | 'signals';
|
|
31
9
|
export type BrandJsonResolverErrorCode = 'invalid_url' | 'invalid_house' | 'redirect_loop' | 'redirect_depth_exceeded' | 'fetch_failed' | 'invalid_body' | 'schema_invalid' | 'agent_not_found' | 'agent_ambiguous' | 'jwks_origin_mismatch';
|
|
32
10
|
/**
|
|
@@ -45,21 +23,20 @@ export declare class BrandJsonResolverError extends Error {
|
|
|
45
23
|
});
|
|
46
24
|
}
|
|
47
25
|
export interface BrandJsonJwksResolverOptions {
|
|
26
|
+
/** Expected agent URL. Prefer supplying it; legacy configurations infer one onboarding URL, then confirm it through capabilities and canonical matching. */
|
|
27
|
+
agentUrl?: string;
|
|
28
|
+
/** Capabilities transport. Defaults to MCP; A2A integrations must set 'a2a'. */
|
|
29
|
+
protocol?: AgentProtocol;
|
|
30
|
+
/** Enable the 3.x webhook-only domain-derived fallback. Default true. Request verification refuses these keys. */
|
|
31
|
+
legacyWebhookFallback?: boolean;
|
|
32
|
+
fetchCapabilities?: FetchCapabilitiesFn;
|
|
48
33
|
/** Functional role of the agent whose keys we want to resolve. */
|
|
49
34
|
agentType: BrandAgentType;
|
|
50
35
|
/**
|
|
51
|
-
* Agent id from `agents[].id
|
|
52
|
-
* one agent of the requested type (otherwise the selector is ambiguous).
|
|
53
|
-
* Optional when the type is unique.
|
|
36
|
+
* Agent id from `agents[].id`, used only to narrow canonical URL matches.
|
|
54
37
|
*/
|
|
55
38
|
agentId?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Brand id within a house portfolio (`brands[].id`). When omitted on a
|
|
58
|
-
* portfolio brand.json, the resolver looks at `house.agents[]`. When set,
|
|
59
|
-
* the resolver looks at `brands[brandId].agents[]` first and falls back to
|
|
60
|
-
* `house.agents[]` if no agent of the requested type is declared on the
|
|
61
|
-
* brand itself.
|
|
62
|
-
*/
|
|
39
|
+
/** @deprecated Used only to infer an onboarding URL when agentUrl is absent. Canonical verification searches all operator collections. */
|
|
63
40
|
brandId?: string;
|
|
64
41
|
/**
|
|
65
42
|
* Minimum seconds between brand.json refetches. Mirrors the JWKS cooldown
|
|
@@ -73,11 +50,7 @@ export interface BrandJsonJwksResolverOptions {
|
|
|
73
50
|
* allow longer. Default 3600s (1 hour).
|
|
74
51
|
*/
|
|
75
52
|
maxAgeSeconds?: number;
|
|
76
|
-
/**
|
|
77
|
-
* Maximum redirect hops to follow through `authoritative_location` /
|
|
78
|
-
* `house` variants. Default 3 — enough for `authoritative_location → house
|
|
79
|
-
* → portfolio` without inviting loops.
|
|
80
|
-
*/
|
|
53
|
+
/** @deprecated Only controls legacy onboarding indirection. Explicit capability URLs allow no redirects; webhook fallback allows one document hop. */
|
|
81
54
|
maxRedirects?: number;
|
|
82
55
|
/**
|
|
83
56
|
* Allow `http://` / private-IP brand.json and JWKS URLs (dev loops only).
|
|
@@ -90,52 +63,29 @@ export interface BrandJsonJwksResolverOptions {
|
|
|
90
63
|
* address remains subject to SSRF classification and connection pinning.
|
|
91
64
|
*/
|
|
92
65
|
lookup?: SsrfDnsLookup;
|
|
93
|
-
/**
|
|
94
|
-
* Forwarded to the inner {@link HttpsJwksResolver} constructor.
|
|
95
|
-
* `allowPrivateIp`, `lookup`, and `now` are set from the outer options and
|
|
96
|
-
* should not be passed here.
|
|
97
|
-
*/
|
|
66
|
+
/** Existing settings remain accepted. maxAgeSeconds/minCooldownSeconds configure caching; protocol floors and fail-closed behavior replace other settings. */
|
|
98
67
|
jwksOptions?: Omit<HttpsJwksResolverOptions, 'allowPrivateIp' | 'lookup' | 'now'>;
|
|
99
68
|
/** Clock override for deterministic tests. Returns epoch seconds. */
|
|
100
69
|
now?: () => number;
|
|
101
70
|
}
|
|
102
71
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* the
|
|
72
|
+
* Confirmed operator mapping. The supplied brand.json URL must agree with
|
|
73
|
+
* capabilities on every cache refresh. Prefer ResolvedAgentJwksResolver
|
|
74
|
+
* when the application's onboarding record holds only the seller URL.
|
|
106
75
|
*/
|
|
107
76
|
export declare class BrandJsonJwksResolver implements JwksResolver {
|
|
108
|
-
private readonly
|
|
109
|
-
private readonly
|
|
110
|
-
private
|
|
111
|
-
private
|
|
112
|
-
private
|
|
113
|
-
private readonly allowPrivateIp;
|
|
114
|
-
private readonly lookup;
|
|
115
|
-
private readonly jwksOptions;
|
|
116
|
-
private readonly now;
|
|
117
|
-
private snapshot;
|
|
118
|
-
private inner;
|
|
119
|
-
private inFlight;
|
|
77
|
+
private readonly brandJsonUrl;
|
|
78
|
+
private readonly options;
|
|
79
|
+
private resolver?;
|
|
80
|
+
private onboarding?;
|
|
81
|
+
private lastOnboardingAttempt;
|
|
120
82
|
constructor(brandJsonUrl: string, options: BrandJsonJwksResolverOptions);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
* expired brand.json snapshot, refreshes respecting the cooldown. Unknown
|
|
124
|
-
* kids cascade: first ask the inner HttpsJwksResolver (which will refetch
|
|
125
|
-
* its own URL if cooldown has elapsed); if still unknown, refresh
|
|
126
|
-
* brand.json in case `jwks_uri` rotated.
|
|
127
|
-
*/
|
|
83
|
+
private createResolver;
|
|
84
|
+
private confirmedResolver;
|
|
128
85
|
resolve(keyid: string): Promise<AdcpJsonWebKey | null>;
|
|
129
|
-
|
|
130
|
-
* The agent URL we resolved `jwks_uri` from. Populated after the first
|
|
131
|
-
* successful refresh; useful for verifier result attribution
|
|
132
|
-
* (`VerifyWebhookOptions.agentUrlForKeyid`).
|
|
133
|
-
*/
|
|
86
|
+
resolveWithMetadata(keyid: string): Promise<JwksResolution>;
|
|
134
87
|
get agentUrl(): string | undefined;
|
|
135
|
-
/** Force a refetch of both brand.json and the inner JWKS, bypassing the cooldown. */
|
|
136
88
|
forceRefresh(): Promise<void>;
|
|
137
|
-
private refresh;
|
|
138
|
-
private doRefresh;
|
|
139
89
|
}
|
|
140
90
|
export interface FetchedBrandJson {
|
|
141
91
|
status: 'ok' | 'not_modified';
|
|
@@ -149,6 +99,8 @@ export interface FetchBrandJsonOptions {
|
|
|
149
99
|
startUrl: string;
|
|
150
100
|
/** ETag sent only to the entry URL for cache revalidation. */
|
|
151
101
|
currentEtag?: string;
|
|
102
|
+
/** Initial domain-derived fetch may redirect only between host and exact www counterpart. */
|
|
103
|
+
domainDerived?: boolean;
|
|
152
104
|
/** Maximum JSON-level `authoritative_location` / `house` hops. Default 3, hard maximum 10. */
|
|
153
105
|
maxRedirects?: number;
|
|
154
106
|
/** Permit HTTP and private addresses for controlled development environments. */
|
|
@@ -24,7 +24,9 @@ __export(brand_jwks_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(brand_jwks_exports);
|
|
26
26
|
var import_net = require('../net/index.js');
|
|
27
|
-
var
|
|
27
|
+
var import_resolved_agent_jwks = require('./agent-resolver/resolved-agent-jwks.js');
|
|
28
|
+
var import_strict_json = require('./agent-resolver/strict-json.js');
|
|
29
|
+
var import_select_agent = require('./agent-resolver/select-agent.js');
|
|
28
30
|
class BrandJsonResolverError extends Error {
|
|
29
31
|
code;
|
|
30
32
|
cause;
|
|
@@ -43,115 +45,110 @@ const DEFAULT_MAX_AGE_SECONDS = 3600;
|
|
|
43
45
|
const DEFAULT_MAX_REDIRECTS = 3;
|
|
44
46
|
const BARE_HOSTNAME = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/;
|
|
45
47
|
class BrandJsonJwksResolver {
|
|
46
|
-
url;
|
|
47
|
-
selector;
|
|
48
|
-
minCooldown;
|
|
49
|
-
maxAge;
|
|
50
|
-
maxRedirects;
|
|
51
|
-
allowPrivateIp;
|
|
52
|
-
lookup;
|
|
53
|
-
jwksOptions;
|
|
54
|
-
now;
|
|
55
|
-
snapshot;
|
|
56
|
-
inner;
|
|
57
|
-
inFlight;
|
|
58
48
|
constructor(brandJsonUrl, options) {
|
|
59
|
-
this.
|
|
60
|
-
this.
|
|
49
|
+
this.brandJsonUrl = brandJsonUrl;
|
|
50
|
+
this.options = options;
|
|
51
|
+
for (const lifetime of [options.maxAgeSeconds, options.jwksOptions?.maxAgeSeconds]) {
|
|
52
|
+
if (lifetime !== void 0 && (!Number.isFinite(lifetime) || lifetime <= 0))
|
|
53
|
+
throw new TypeError("maxAgeSeconds must be a finite positive number.");
|
|
54
|
+
}
|
|
55
|
+
for (const cooldown of [options.minCooldownSeconds, options.jwksOptions?.minCooldownSeconds]) {
|
|
56
|
+
if (cooldown !== void 0 && (!Number.isFinite(cooldown) || cooldown < 0))
|
|
57
|
+
throw new TypeError("minCooldownSeconds must be a finite non-negative number.");
|
|
58
|
+
}
|
|
59
|
+
if (options.agentUrl !== void 0) this.resolver = this.createResolver(options.agentUrl, brandJsonUrl);
|
|
60
|
+
}
|
|
61
|
+
brandJsonUrl;
|
|
62
|
+
options;
|
|
63
|
+
resolver;
|
|
64
|
+
onboarding;
|
|
65
|
+
lastOnboardingAttempt = Number.NEGATIVE_INFINITY;
|
|
66
|
+
createResolver(agentUrl, operatorUrl) {
|
|
67
|
+
const options = this.options;
|
|
68
|
+
return new import_resolved_agent_jwks.ResolvedAgentJwksResolver(agentUrl, options.protocol ?? "mcp", {
|
|
61
69
|
agentType: options.agentType,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
agentId: options.agentId,
|
|
71
|
+
expectedBrandJsonUrl: operatorUrl,
|
|
72
|
+
fetchCapabilities: options.fetchCapabilities,
|
|
73
|
+
legacyWebhookFallback: options.legacyWebhookFallback ?? true,
|
|
74
|
+
allowPrivateIp: options.allowPrivateIp,
|
|
75
|
+
lookup: options.lookup,
|
|
76
|
+
now: options.now,
|
|
77
|
+
cacheTtlSeconds: Math.min(
|
|
78
|
+
options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS,
|
|
79
|
+
options.jwksOptions?.maxAgeSeconds ?? 1800
|
|
80
|
+
),
|
|
81
|
+
unknownKidCooldownSeconds: options.minCooldownSeconds ?? options.jwksOptions?.minCooldownSeconds ?? DEFAULT_MIN_COOLDOWN_SECONDS
|
|
82
|
+
});
|
|
72
83
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
if (!this.inner) return null;
|
|
88
|
-
const hit = await this.inner.resolve(keyid);
|
|
89
|
-
if (hit) return hit;
|
|
90
|
-
if (this.snapshot && this.now() - this.snapshot.fetchedAt >= this.minCooldown) {
|
|
91
|
-
await this.refresh().catch(() => {
|
|
84
|
+
async confirmedResolver() {
|
|
85
|
+
if (this.resolver) return this.resolver;
|
|
86
|
+
if (this.onboarding) return this.onboarding;
|
|
87
|
+
const now = this.options.now?.() ?? Date.now() / 1e3;
|
|
88
|
+
if (now - this.lastOnboardingAttempt < 30)
|
|
89
|
+
throw new BrandJsonResolverError("fetch_failed", "Operator onboarding discovery is in its retry cooldown.");
|
|
90
|
+
this.lastOnboardingAttempt = now;
|
|
91
|
+
this.onboarding = (async () => {
|
|
92
|
+
const record = await fetchBrandJson({
|
|
93
|
+
startUrl: this.brandJsonUrl,
|
|
94
|
+
maxRedirects: this.options.maxRedirects,
|
|
95
|
+
allowPrivateIp: this.options.allowPrivateIp,
|
|
96
|
+
lookup: this.options.lookup
|
|
92
97
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
const agentUrl = inferOnboardingAgentUrl(record.data, this.options);
|
|
99
|
+
const resolver = this.createResolver(agentUrl, record.finalUrl);
|
|
100
|
+
await resolver.forceRefresh();
|
|
101
|
+
this.resolver = resolver;
|
|
102
|
+
return resolver;
|
|
103
|
+
})().finally(() => {
|
|
104
|
+
this.onboarding = void 0;
|
|
105
|
+
});
|
|
106
|
+
return this.onboarding;
|
|
107
|
+
}
|
|
108
|
+
async resolve(keyid) {
|
|
109
|
+
return (await this.confirmedResolver()).resolve(keyid);
|
|
110
|
+
}
|
|
111
|
+
async resolveWithMetadata(keyid) {
|
|
112
|
+
return (await this.confirmedResolver()).resolveWithMetadata(keyid);
|
|
96
113
|
}
|
|
97
|
-
/**
|
|
98
|
-
* The agent URL we resolved `jwks_uri` from. Populated after the first
|
|
99
|
-
* successful refresh; useful for verifier result attribution
|
|
100
|
-
* (`VerifyWebhookOptions.agentUrlForKeyid`).
|
|
101
|
-
*/
|
|
102
114
|
get agentUrl() {
|
|
103
|
-
return this.
|
|
115
|
+
return this.resolver?.resolvedAgentUrl;
|
|
104
116
|
}
|
|
105
|
-
/** Force a refetch of both brand.json and the inner JWKS, bypassing the cooldown. */
|
|
106
117
|
async forceRefresh() {
|
|
107
|
-
this.
|
|
108
|
-
this.
|
|
109
|
-
await this.refresh();
|
|
110
|
-
}
|
|
111
|
-
async refresh() {
|
|
112
|
-
if (this.inFlight) {
|
|
113
|
-
await this.inFlight;
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
this.inFlight = this.doRefresh().finally(() => {
|
|
117
|
-
this.inFlight = void 0;
|
|
118
|
-
});
|
|
119
|
-
await this.inFlight;
|
|
118
|
+
if (this.resolver) await this.resolver.forceRefresh();
|
|
119
|
+
else await this.confirmedResolver();
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
...this.snapshot,
|
|
132
|
-
...fetched.etag && { etag: fetched.etag },
|
|
133
|
-
fetchedAt: this.now(),
|
|
134
|
-
expiresAt: this.now() + computeLifetime(fetched.cacheControl, this.maxAge)
|
|
135
|
-
};
|
|
136
|
-
return;
|
|
121
|
+
}
|
|
122
|
+
function inferOnboardingAgentUrl(document, selector) {
|
|
123
|
+
const record = document;
|
|
124
|
+
const candidates = (agents) => !Array.isArray(agents) ? [] : agents.filter(
|
|
125
|
+
(entry) => entry?.type === selector.agentType && (selector.agentId === void 0 || entry.id === selector.agentId) && typeof entry.url === "string"
|
|
126
|
+
).flatMap((entry) => {
|
|
127
|
+
try {
|
|
128
|
+
return [(0, import_select_agent.canonicalAgentUrl)(entry.url)];
|
|
129
|
+
} catch {
|
|
130
|
+
return [];
|
|
137
131
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
132
|
+
});
|
|
133
|
+
let urls;
|
|
134
|
+
if (isPortfolioHouse(record.house)) {
|
|
135
|
+
const house = record.house;
|
|
136
|
+
urls = [];
|
|
137
|
+
if (selector.brandId !== void 0 && Array.isArray(record.brands)) {
|
|
138
|
+
const brands = record.brands.filter((brand) => brand?.id === selector.brandId);
|
|
139
|
+
if (brands.length > 1)
|
|
140
|
+
throw new BrandJsonResolverError("agent_ambiguous", "Onboarding brand selector is ambiguous.");
|
|
141
|
+
urls = candidates(brands[0]?.agents);
|
|
146
142
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
143
|
+
if (urls.length === 0) urls = candidates(house.agents);
|
|
144
|
+
} else urls = candidates(record.agents);
|
|
145
|
+
const unique = [...new Set(urls)];
|
|
146
|
+
if (unique.length !== 1)
|
|
147
|
+
throw new BrandJsonResolverError(
|
|
148
|
+
unique.length === 0 ? "agent_not_found" : "agent_ambiguous",
|
|
149
|
+
"Onboarding must identify exactly one agent URL; supply agentUrl explicitly."
|
|
150
|
+
);
|
|
151
|
+
return unique[0];
|
|
155
152
|
}
|
|
156
153
|
const MAX_BRAND_JSON_TIMEOUT_MS = 1e4;
|
|
157
154
|
const MAX_BRAND_JSON_BODY_BYTES = 262144;
|
|
@@ -172,6 +169,8 @@ async function fetchBrandJson(args) {
|
|
|
172
169
|
const allowPrivateIp = args.allowPrivateIp === true;
|
|
173
170
|
const seen = /* @__PURE__ */ new Set();
|
|
174
171
|
let url = canonicalizeUrl(args.startUrl, allowPrivateIp);
|
|
172
|
+
const initial = new URL(url);
|
|
173
|
+
let httpRedirects = 0;
|
|
175
174
|
for (let hop = 0; hop <= maxRedirects; hop++) {
|
|
176
175
|
if (seen.has(url)) {
|
|
177
176
|
throw new BrandJsonResolverError("redirect_loop", `brand.json redirect loop detected`);
|
|
@@ -194,6 +193,19 @@ async function fetchBrandJson(args) {
|
|
|
194
193
|
} catch (cause) {
|
|
195
194
|
throw new BrandJsonResolverError("fetch_failed", "Unable to fetch brand.json", { cause });
|
|
196
195
|
}
|
|
196
|
+
if (args.domainDerived && hop === 0 && [301, 302, 303, 307, 308].includes(res.status)) {
|
|
197
|
+
const location = res.headers.location;
|
|
198
|
+
if (!location || httpRedirects++ >= 3)
|
|
199
|
+
throw new BrandJsonResolverError("fetch_failed", "Invalid domain-derived redirect");
|
|
200
|
+
const target = new URL(location, url);
|
|
201
|
+
const baseHost = initial.hostname.startsWith("www.") ? initial.hostname.slice(4) : initial.hostname;
|
|
202
|
+
if (target.hostname !== baseHost && target.hostname !== `www.${baseHost}` || target.port !== initial.port || target.protocol !== initial.protocol || target.username || target.password) {
|
|
203
|
+
throw new BrandJsonResolverError("invalid_url", "brand.json redirect leaves its original host/www boundary");
|
|
204
|
+
}
|
|
205
|
+
url = canonicalizeUrl(target.href, allowPrivateIp);
|
|
206
|
+
hop--;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
197
209
|
if (hop === 0 && res.status === 304) {
|
|
198
210
|
return {
|
|
199
211
|
status: "not_modified",
|
|
@@ -211,7 +223,7 @@ async function fetchBrandJson(args) {
|
|
|
211
223
|
const text = Buffer.from(res.body).toString("utf8");
|
|
212
224
|
let parsed;
|
|
213
225
|
try {
|
|
214
|
-
parsed =
|
|
226
|
+
parsed = (0, import_strict_json.parseStrictJson)(text);
|
|
215
227
|
} catch {
|
|
216
228
|
throw new BrandJsonResolverError("invalid_body", `brand.json response is not valid JSON`);
|
|
217
229
|
}
|
|
@@ -303,88 +315,6 @@ function assertBrandJsonShape(obj) {
|
|
|
303
315
|
}
|
|
304
316
|
}
|
|
305
317
|
}
|
|
306
|
-
function selectAgent(data, finalBrandUrl, selector) {
|
|
307
|
-
if (!data || typeof data !== "object") {
|
|
308
|
-
throw new BrandJsonResolverError(
|
|
309
|
-
"agent_not_found",
|
|
310
|
-
`brand.json has no agent matching ${describeSelector(selector)}`
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
const obj = data;
|
|
314
|
-
let picked;
|
|
315
|
-
if (isPortfolioHouse(obj.house)) {
|
|
316
|
-
const house = obj.house;
|
|
317
|
-
if (selector.brandId !== void 0) {
|
|
318
|
-
const brands = Array.isArray(obj.brands) ? obj.brands : [];
|
|
319
|
-
const brand = brands.find((b) => b && b.id === selector.brandId);
|
|
320
|
-
if (brand) picked = pickAgent(brand.agents, finalBrandUrl, selector);
|
|
321
|
-
}
|
|
322
|
-
picked ??= pickAgent(house.agents, finalBrandUrl, selector);
|
|
323
|
-
} else {
|
|
324
|
-
picked = pickAgent(obj.agents, finalBrandUrl, selector);
|
|
325
|
-
}
|
|
326
|
-
if (!picked) {
|
|
327
|
-
throw new BrandJsonResolverError(
|
|
328
|
-
"agent_not_found",
|
|
329
|
-
`brand.json has no agent matching ${describeSelector(selector)}`
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
return picked;
|
|
333
|
-
}
|
|
334
|
-
function pickAgent(agents, finalBrandUrl, selector) {
|
|
335
|
-
if (!Array.isArray(agents)) return void 0;
|
|
336
|
-
const matches = agents.filter((a) => {
|
|
337
|
-
if (!a || typeof a !== "object") return false;
|
|
338
|
-
const e = a;
|
|
339
|
-
if (e.type !== selector.agentType) return false;
|
|
340
|
-
if (selector.agentId !== void 0 && e.id !== selector.agentId) return false;
|
|
341
|
-
return typeof e.url === "string";
|
|
342
|
-
});
|
|
343
|
-
if (matches.length === 0) return void 0;
|
|
344
|
-
if (matches.length > 1 && selector.agentId === void 0) {
|
|
345
|
-
throw new BrandJsonResolverError(
|
|
346
|
-
"agent_ambiguous",
|
|
347
|
-
`brand.json declares ${matches.length} agents of type "${selector.agentType}"; pass \`agentId\` to disambiguate (choices: ${matches.map((m) => m.id ?? "<no-id>").join(", ")})`
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
const agent = matches[0];
|
|
351
|
-
const url = agent.url;
|
|
352
|
-
const jwksUri = agent.jwks_uri ?? defaultJwksUri(url, finalBrandUrl);
|
|
353
|
-
return { url, jwksUri };
|
|
354
|
-
}
|
|
355
|
-
function defaultJwksUri(agentUrl, finalBrandUrl) {
|
|
356
|
-
let agent;
|
|
357
|
-
try {
|
|
358
|
-
agent = new URL(agentUrl);
|
|
359
|
-
} catch {
|
|
360
|
-
throw new BrandJsonResolverError("invalid_url", `agent.url is not a valid URL`);
|
|
361
|
-
}
|
|
362
|
-
const brand = new URL(finalBrandUrl);
|
|
363
|
-
if (agent.origin !== brand.origin) {
|
|
364
|
-
throw new BrandJsonResolverError(
|
|
365
|
-
"jwks_origin_mismatch",
|
|
366
|
-
`agent.url origin (${agent.origin}) does not match brand.json origin (${brand.origin}); publisher must declare an explicit jwks_uri for cross-origin agents`
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
return `${agent.origin}/.well-known/jwks.json`;
|
|
370
|
-
}
|
|
371
|
-
function describeSelector(selector) {
|
|
372
|
-
const parts = [`type=${selector.agentType}`];
|
|
373
|
-
if (selector.agentId !== void 0) parts.push(`id=${selector.agentId}`);
|
|
374
|
-
if (selector.brandId !== void 0) parts.push(`brand=${selector.brandId}`);
|
|
375
|
-
return parts.join(" ");
|
|
376
|
-
}
|
|
377
|
-
function computeLifetime(cacheControl, maxAge) {
|
|
378
|
-
if (!cacheControl) return maxAge;
|
|
379
|
-
const lower = cacheControl.toLowerCase();
|
|
380
|
-
if (/\bno-store\b|\bno-cache\b/.test(lower)) return 0;
|
|
381
|
-
const match = /max-age\s*=\s*(\d+)/.exec(lower);
|
|
382
|
-
if (match) {
|
|
383
|
-
const serverMax = Number(match[1]);
|
|
384
|
-
if (Number.isFinite(serverMax)) return Math.min(serverMax, maxAge);
|
|
385
|
-
}
|
|
386
|
-
return maxAge;
|
|
387
|
-
}
|
|
388
318
|
// Annotate the CommonJS export names for ESM import in node:
|
|
389
319
|
0 && (module.exports = {
|
|
390
320
|
BrandJsonJwksResolver,
|