@apifuse/provider-sdk 2.2.0-beta.30 → 2.2.0-beta.32
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/CHANGELOG.md +8 -0
- package/dist/error-resolution.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/dist/runtime/choice.d.ts +1 -0
- package/dist/runtime/choice.js +96 -21
- package/dist/runtime/resolver-vendors/capsolver.d.ts +24 -0
- package/dist/runtime/resolver-vendors/capsolver.js +402 -0
- package/dist/runtime/resolver.js +8 -1
- package/dist/server/serve-implementation.js +2 -1
- package/package.json +1 -1
- package/src/error-resolution.ts +1 -0
- package/src/index.ts +1 -0
- package/src/provider.ts +1 -0
- package/src/runtime/choice.ts +133 -41
- package/src/runtime/resolver-vendors/capsolver.ts +563 -0
- package/src/runtime/resolver.ts +8 -1
- package/src/server/serve-implementation.ts +2 -0
package/dist/runtime/resolver.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ProviderError } from "../errors.js";
|
|
|
4
4
|
import { getStealthProfile } from "../stealth/profiles.js";
|
|
5
5
|
import { resolverChallengeAllowsDirectCache, resolverChallengeIsCacheable, resolverChallengeIsIdentityScoped, resolverChallengeIssuingIdentity, } from "./resolver-vendors/bindings.js";
|
|
6
6
|
import { createBrowserResolverVendorAdapter } from "./resolver-vendors/browser.js";
|
|
7
|
+
import { createCapsolverResolverVendorAdapter } from "./resolver-vendors/capsolver.js";
|
|
7
8
|
import { assertResolverHostAllowed } from "./resolver-vendors/hosts.js";
|
|
8
9
|
import { createTwoCaptchaResolverVendorAdapter } from "./resolver-vendors/twocaptcha.js";
|
|
9
10
|
import { RESOLVER_VENDOR_CAPABILITIES, ResolverVendorUnavailableError, resolverVendorSupports, } from "./resolver-vendors/types.js";
|
|
@@ -61,6 +62,13 @@ const resolverAdapterRegistry = {
|
|
|
61
62
|
timeoutMs,
|
|
62
63
|
});
|
|
63
64
|
},
|
|
65
|
+
capsolver(configuration, timeoutMs, allowedHosts) {
|
|
66
|
+
return createCapsolverResolverVendorAdapter({
|
|
67
|
+
allowedHosts,
|
|
68
|
+
apiKey: configuration,
|
|
69
|
+
timeoutMs,
|
|
70
|
+
});
|
|
71
|
+
},
|
|
64
72
|
browser(configuration, timeoutMs, allowedHosts) {
|
|
65
73
|
return createBrowserResolverVendorAdapter({
|
|
66
74
|
allowedHosts,
|
|
@@ -104,7 +112,6 @@ export function swapResolverDefaultUserAgentForTests(resolver) {
|
|
|
104
112
|
// This is the sole allowlist for declared vendors whose registry entry may be absent.
|
|
105
113
|
// Remove a vendor here when its adapter is registered.
|
|
106
114
|
const KNOWN_UNIMPLEMENTED_RESOLVER_VENDORS = new Set([
|
|
107
|
-
"capsolver",
|
|
108
115
|
"capmonster",
|
|
109
116
|
"custom",
|
|
110
117
|
]);
|
|
@@ -11,7 +11,7 @@ import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.j
|
|
|
11
11
|
import { categoryForStatus, sourceForCategory, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability.js";
|
|
12
12
|
import { createScratchpad } from "../runtime/auth-flow.js";
|
|
13
13
|
import { createProviderCache } from "../runtime/cache.js";
|
|
14
|
-
import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice.js";
|
|
14
|
+
import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, PROVIDER_RUNTIME_CHOICE_WORD_ISSUANCE_ENV, } from "../runtime/choice.js";
|
|
15
15
|
import { createCredentialContext } from "../runtime/credential.js";
|
|
16
16
|
import { createEnvContext } from "../runtime/env.js";
|
|
17
17
|
import { executeOperation } from "../runtime/executor.js";
|
|
@@ -376,6 +376,7 @@ function createProviderContext(provider, request, operationId, options, state =
|
|
|
376
376
|
const env = createEnvContext([
|
|
377
377
|
...(provider.secrets?.map((secret) => secret.name) ?? []),
|
|
378
378
|
PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
|
|
379
|
+
PROVIDER_RUNTIME_CHOICE_WORD_ISSUANCE_ENV,
|
|
379
380
|
]);
|
|
380
381
|
const credential = createCredentialContext({
|
|
381
382
|
allowedKeys: provider.credential?.keys,
|
package/package.json
CHANGED
package/src/error-resolution.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const SDK_OWNED_PROVIDER_ERROR_CODES = new Set([
|
|
|
20
20
|
"RUNTIME_UNSUPPORTED",
|
|
21
21
|
"PROVIDER_STATE_UNSUPPORTED",
|
|
22
22
|
"CHOICE_TOKEN_MASTER_SECRET_NOT_CONFIGURED",
|
|
23
|
+
"CHOICE_WORD_ISSUANCE_INVALID",
|
|
23
24
|
"CHOICE_STATE_PAYLOAD_TOO_LARGE",
|
|
24
25
|
"CHOICE_STATE_UNAVAILABLE",
|
|
25
26
|
"CHOICE_CONTEXT_REQUIRED",
|
package/src/index.ts
CHANGED
package/src/provider.ts
CHANGED
package/src/runtime/choice.ts
CHANGED
|
@@ -39,6 +39,8 @@ import type {
|
|
|
39
39
|
|
|
40
40
|
export const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV =
|
|
41
41
|
"APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
42
|
+
export const PROVIDER_RUNTIME_CHOICE_WORD_ISSUANCE_ENV =
|
|
43
|
+
"APIFUSE__PROVIDER_RUNTIME__CHOICE_WORD_ISSUANCE";
|
|
42
44
|
|
|
43
45
|
const PRIMARY_CHOICE_TOKEN_KID = "v1";
|
|
44
46
|
const MANAGED_CHOICE_TOKEN_VERSION = 1;
|
|
@@ -131,28 +133,54 @@ export function createProviderChoiceContext(
|
|
|
131
133
|
const issuedAtMs = issueOptions.nowMs ?? Date.now();
|
|
132
134
|
const resolvedStorage = resolveIssueStorage(issueOptions.storage, issueOptions.payload);
|
|
133
135
|
if (resolvedStorage.mode === "server") {
|
|
136
|
+
const issuance = resolveChoiceWordIssuance(options.env);
|
|
137
|
+
const keys = hasRequestedChoiceBinding(issueOptions.bind)
|
|
138
|
+
? deriveManagedChoiceKeys({
|
|
139
|
+
masterSecret: resolveMasterSecret(),
|
|
140
|
+
providerId: options.providerId,
|
|
141
|
+
purpose: issueOptions.purpose,
|
|
142
|
+
kid,
|
|
143
|
+
})
|
|
144
|
+
: undefined;
|
|
134
145
|
const binding = hasRequestedChoiceBinding(issueOptions.bind)
|
|
135
146
|
? createChoiceBinding({
|
|
136
|
-
keys:
|
|
137
|
-
masterSecret: resolveMasterSecret(),
|
|
138
|
-
providerId: options.providerId,
|
|
139
|
-
purpose: issueOptions.purpose,
|
|
140
|
-
kid,
|
|
141
|
-
}),
|
|
147
|
+
keys: keys!,
|
|
142
148
|
options: issueOptions.bind,
|
|
143
149
|
request: options.request,
|
|
144
150
|
credential: options.credential,
|
|
145
151
|
required: true,
|
|
146
152
|
})
|
|
147
153
|
: undefined;
|
|
148
|
-
|
|
154
|
+
const baseEnvelope = {
|
|
155
|
+
v: MANAGED_CHOICE_TOKEN_VERSION,
|
|
156
|
+
provider_id: options.providerId,
|
|
157
|
+
purpose: issueOptions.purpose,
|
|
158
|
+
issued_at_ms: issuedAtMs,
|
|
159
|
+
ttl_ms: issueOptions.ttlMs,
|
|
160
|
+
binding,
|
|
161
|
+
} satisfies Omit<ManagedChoiceEnvelope, "payload">;
|
|
162
|
+
if (issuance === "legacy") {
|
|
163
|
+
const legacyKeys =
|
|
164
|
+
keys ??
|
|
165
|
+
deriveManagedChoiceKeys({
|
|
166
|
+
masterSecret: resolveMasterSecret(),
|
|
167
|
+
providerId: options.providerId,
|
|
168
|
+
purpose: issueOptions.purpose,
|
|
169
|
+
kid,
|
|
170
|
+
});
|
|
171
|
+
return issueLegacyServerStoredChoice({
|
|
172
|
+
baseEnvelope,
|
|
173
|
+
issueOptions,
|
|
174
|
+
storage: resolvedStorage.storage,
|
|
175
|
+
contextState: options.state,
|
|
176
|
+
kid,
|
|
177
|
+
keys: legacyKeys,
|
|
178
|
+
issuedAtMs,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return issueWordServerStoredChoice({
|
|
149
182
|
baseEnvelope: {
|
|
150
|
-
|
|
151
|
-
provider_id: options.providerId,
|
|
152
|
-
purpose: issueOptions.purpose,
|
|
153
|
-
issued_at_ms: issuedAtMs,
|
|
154
|
-
ttl_ms: issueOptions.ttlMs,
|
|
155
|
-
binding,
|
|
183
|
+
...baseEnvelope,
|
|
156
184
|
},
|
|
157
185
|
issueOptions,
|
|
158
186
|
storage: resolvedStorage.storage,
|
|
@@ -233,8 +261,8 @@ export function createProviderChoiceContext(
|
|
|
233
261
|
masterSecret: resolveMasterSecret(),
|
|
234
262
|
providerId: options.providerId,
|
|
235
263
|
purpose: parseOptions.purpose,
|
|
236
|
-
|
|
237
|
-
|
|
264
|
+
kid,
|
|
265
|
+
}),
|
|
238
266
|
onConsume: (result) =>
|
|
239
267
|
emitChoiceTelemetry(options.onTelemetry, {
|
|
240
268
|
providerId: options.providerId,
|
|
@@ -247,15 +275,16 @@ export function createProviderChoiceContext(
|
|
|
247
275
|
replay: result.status === "already-consumed",
|
|
248
276
|
}),
|
|
249
277
|
});
|
|
250
|
-
return observeChoiceParse<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
278
|
+
return observeChoiceParse<ProviderChoiceTokenPayload | ProviderChoiceExplicitParseResult>(
|
|
279
|
+
parsed,
|
|
280
|
+
{
|
|
281
|
+
onTelemetry: options.onTelemetry,
|
|
282
|
+
providerId: options.providerId,
|
|
283
|
+
purpose: parseOptions.purpose,
|
|
284
|
+
format: "word",
|
|
285
|
+
consumeMode,
|
|
286
|
+
},
|
|
287
|
+
);
|
|
259
288
|
}
|
|
260
289
|
|
|
261
290
|
// Legacy encrypted-envelope compatibility fallback. Removal is gated on
|
|
@@ -285,13 +314,9 @@ export function createProviderChoiceContext(
|
|
|
285
314
|
purpose: parseOptions.purpose,
|
|
286
315
|
kid: tokenKid,
|
|
287
316
|
});
|
|
288
|
-
const signedBody = [
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
encodedIv,
|
|
292
|
-
encryptedPayload,
|
|
293
|
-
authTag,
|
|
294
|
-
].join(".");
|
|
317
|
+
const signedBody = [parseOptions.prefix, tokenKid, encodedIv, encryptedPayload, authTag].join(
|
|
318
|
+
".",
|
|
319
|
+
);
|
|
295
320
|
assertManagedChoiceSignature({
|
|
296
321
|
signedBody,
|
|
297
322
|
signature,
|
|
@@ -347,15 +372,16 @@ export function createProviderChoiceContext(
|
|
|
347
372
|
}),
|
|
348
373
|
)
|
|
349
374
|
: payload;
|
|
350
|
-
return observeChoiceParse<
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
375
|
+
return observeChoiceParse<ProviderChoiceTokenPayload | ProviderChoiceExplicitParseResult>(
|
|
376
|
+
parsed,
|
|
377
|
+
{
|
|
378
|
+
onTelemetry: options.onTelemetry,
|
|
379
|
+
providerId: options.providerId,
|
|
380
|
+
purpose: parseOptions.purpose,
|
|
381
|
+
format: "legacy",
|
|
382
|
+
consumeMode,
|
|
383
|
+
},
|
|
384
|
+
);
|
|
359
385
|
} catch (error) {
|
|
360
386
|
emitChoiceParseFailure(options.onTelemetry, error, {
|
|
361
387
|
providerId: options.providerId,
|
|
@@ -485,6 +511,22 @@ function createLegacyExplicitParseResult(options: {
|
|
|
485
511
|
};
|
|
486
512
|
}
|
|
487
513
|
|
|
514
|
+
function resolveChoiceWordIssuance(env?: EnvContext): "legacy" | "word" {
|
|
515
|
+
const configured = env?.get(PROVIDER_RUNTIME_CHOICE_WORD_ISSUANCE_ENV);
|
|
516
|
+
const value = configured?.trim() ?? "";
|
|
517
|
+
if (value === "" || value === "legacy") return "legacy";
|
|
518
|
+
if (value === "word") return "word";
|
|
519
|
+
throw new ProviderError(
|
|
520
|
+
`Unsupported provider choice word issuance mode "${value}". Expected "legacy" or "word".`,
|
|
521
|
+
{
|
|
522
|
+
code: "CHOICE_WORD_ISSUANCE_INVALID",
|
|
523
|
+
category: "input_validation",
|
|
524
|
+
retryable: false,
|
|
525
|
+
details: { env: PROVIDER_RUNTIME_CHOICE_WORD_ISSUANCE_ENV },
|
|
526
|
+
},
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
488
530
|
function resolveChoiceMasterSecret(options: CreateProviderChoiceContextOptions): string {
|
|
489
531
|
const configured =
|
|
490
532
|
options.masterSecret ?? options.env?.get(PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV);
|
|
@@ -559,7 +601,7 @@ function encryptManagedChoiceToken(options: {
|
|
|
559
601
|
return `${signedBody}.${signature}`;
|
|
560
602
|
}
|
|
561
603
|
|
|
562
|
-
async function
|
|
604
|
+
async function issueWordServerStoredChoice<TPayload extends ProviderChoiceTokenPayload>(options: {
|
|
563
605
|
readonly baseEnvelope: Omit<ManagedChoiceEnvelope, "payload">;
|
|
564
606
|
readonly issueOptions: ProviderChoiceIssueOptions<TPayload>;
|
|
565
607
|
readonly storage: ServerProviderChoiceStorageOptions;
|
|
@@ -615,6 +657,56 @@ async function issueServerStoredChoice<TPayload extends ProviderChoiceTokenPaylo
|
|
|
615
657
|
});
|
|
616
658
|
}
|
|
617
659
|
|
|
660
|
+
/**
|
|
661
|
+
* Beta.28-compatible server handle issuance. The state stores the payload and
|
|
662
|
+
* the client receives the existing six-part encrypted envelope whose payload
|
|
663
|
+
* is a server-state handle.
|
|
664
|
+
*/
|
|
665
|
+
async function issueLegacyServerStoredChoice<TPayload extends ProviderChoiceTokenPayload>(options: {
|
|
666
|
+
readonly baseEnvelope: Omit<ManagedChoiceEnvelope, "payload">;
|
|
667
|
+
readonly issueOptions: ProviderChoiceIssueOptions<TPayload>;
|
|
668
|
+
readonly storage: ServerProviderChoiceStorageOptions;
|
|
669
|
+
readonly contextState?: ProviderRuntimeState;
|
|
670
|
+
readonly kid: string;
|
|
671
|
+
readonly keys: ManagedChoiceKeys;
|
|
672
|
+
readonly issuedAtMs: number;
|
|
673
|
+
}): Promise<string> {
|
|
674
|
+
const serializedPayload = serializeChoicePayload(options.issueOptions.payload);
|
|
675
|
+
const payloadBytes = Buffer.byteLength(serializedPayload, "utf8");
|
|
676
|
+
if (payloadBytes > options.storage.maxValueBytes) {
|
|
677
|
+
throw new ProviderError("Provider choice payload exceeds state storage policy.", {
|
|
678
|
+
code: "CHOICE_STATE_PAYLOAD_TOO_LARGE",
|
|
679
|
+
category: "input_validation",
|
|
680
|
+
retryable: false,
|
|
681
|
+
details: { maxValueBytes: options.storage.maxValueBytes, payloadBytes },
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
const stateId = `choice_${randomBytes(16).toString("base64url")}`;
|
|
685
|
+
const namespace = resolveChoiceStateNamespace({
|
|
686
|
+
storage: options.storage,
|
|
687
|
+
contextState: options.contextState,
|
|
688
|
+
ttlMs: options.issueOptions.ttlMs,
|
|
689
|
+
});
|
|
690
|
+
await namespace.set(optionsStateKey(stateId), options.issueOptions.payload, {
|
|
691
|
+
ttl: stateTtl(options.storage, options.issueOptions.ttlMs),
|
|
692
|
+
});
|
|
693
|
+
const envelope: ManagedChoiceEnvelope = {
|
|
694
|
+
...options.baseEnvelope,
|
|
695
|
+
payload: {
|
|
696
|
+
storage: "server",
|
|
697
|
+
state_id: stateId,
|
|
698
|
+
payload_digest: digestChoicePayload(serializedPayload),
|
|
699
|
+
created_at_ms: options.issuedAtMs,
|
|
700
|
+
},
|
|
701
|
+
};
|
|
702
|
+
return encryptManagedChoiceToken({
|
|
703
|
+
prefix: options.issueOptions.prefix,
|
|
704
|
+
kid: options.kid,
|
|
705
|
+
envelope,
|
|
706
|
+
keys: options.keys,
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
|
|
618
710
|
async function parseWordServerStoredChoice(options: {
|
|
619
711
|
readonly stateKey: string;
|
|
620
712
|
readonly parseOptions: ProviderChoiceParseOptions;
|