@apifuse/provider-sdk 2.2.0-beta.25 → 2.2.0-beta.26
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/AUTHORING.md +7 -6
- package/CHANGELOG.md +5 -1
- package/README.md +3 -3
- package/bin/apifuse-check.ts +62 -3
- package/bin/apifuse-pack-check.ts +8 -2
- package/bin/apifuse-pack-smoke.ts +43 -2
- package/bin/apifuse-pack-types.ts +58 -0
- package/dist/auth.js +29 -0
- package/dist/cli/templates/provider/README.md.tpl +4 -4
- package/dist/contract-serialization.d.ts +20 -1
- package/dist/contract-serialization.js +583 -8
- package/dist/contract.d.ts +2 -0
- package/dist/contract.js +9 -5
- package/dist/declaration-validation.d.ts +23 -0
- package/dist/declaration-validation.js +159 -0
- package/dist/define.d.ts +1 -1
- package/dist/define.js +13 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/lint.js +85 -3
- package/dist/provider.d.ts +1 -1
- package/dist/provider.js +1 -1
- package/dist/runtime/resolver-vendors/bindings.d.ts +42 -2
- package/dist/runtime/resolver-vendors/bindings.js +31 -6
- package/dist/runtime/resolver-vendors/browser.d.ts +3 -7
- package/dist/runtime/resolver-vendors/browser.js +7 -22
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +23 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +264 -0
- package/dist/runtime/resolver-vendors/types.d.ts +44 -3
- package/dist/runtime/resolver-vendors/types.js +10 -0
- package/dist/runtime/resolver.d.ts +17 -2
- package/dist/runtime/resolver.js +237 -15
- package/dist/runtime/stealth.d.ts +26 -4
- package/dist/runtime/stealth.js +224 -114
- package/dist/schema.d.ts +63 -0
- package/dist/schema.js +808 -8
- package/dist/server/serve.js +8 -0
- package/dist/stealth/profiles.js +16 -7
- package/dist/types.d.ts +34 -1
- package/package.json +2 -2
- package/src/auth.ts +40 -0
- package/src/cli/templates/provider/README.md.tpl +4 -4
- package/src/contract-serialization.ts +857 -8
- package/src/contract.ts +16 -5
- package/src/declaration-validation.ts +202 -0
- package/src/define.ts +23 -2
- package/src/index.ts +12 -0
- package/src/lint.ts +98 -3
- package/src/provider.ts +10 -0
- package/src/runtime/resolver-vendors/bindings.ts +40 -15
- package/src/runtime/resolver-vendors/browser.ts +9 -31
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +366 -0
- package/src/runtime/resolver-vendors/types.ts +54 -0
- package/src/runtime/resolver.ts +304 -24
- package/src/runtime/stealth.ts +317 -136
- package/src/schema.ts +1060 -9
- package/src/server/serve.ts +8 -0
- package/src/stealth/profiles.ts +17 -7
- package/src/types.ts +36 -3
package/AUTHORING.md
CHANGED
|
@@ -365,7 +365,7 @@ export default defineProvider({
|
|
|
365
365
|
```
|
|
366
366
|
<!-- @magic-end:sample -->
|
|
367
367
|
|
|
368
|
-
The journey runner supplies `ctx.gateway`, `ctx.sms.waitForOtp()`, `ctx.journal.sideEffect()`, `ctx.state`, and `ctx.event.operation()` to the
|
|
368
|
+
The journey runner supplies `ctx.gateway`, `ctx.sms.waitForOtp()`, `ctx.journal.sideEffect()`, `ctx.state`, and `ctx.event.operation()` to the required journey `run` function. Provider authors should keep `run` small: call the covered operations in step order, stop at the declared safe boundary, and let the generated health metadata carry schedule, timeout, required secret, and SMS matcher information to the health monitor.
|
|
369
369
|
|
|
370
370
|
For authenticated journeys, open a fresh connection inside `run` with `ctx.gateway.connect({ input: { ... } })`, execute covered operations with the returned `connectionId`, and disconnect in a `finally` block. Do not require or store long-lived `HEALTH_MONITOR_*_CONNECTION_ID` secrets; those stale connection IDs can hide broken login ceremonies.
|
|
371
371
|
|
|
@@ -734,11 +734,12 @@ const credentialsAuth = defineCredentialsAuth({
|
|
|
734
734
|
request's `context`.
|
|
735
735
|
- Stealth/browser providers may require local runtime setup outside Provider code:
|
|
736
736
|
keep access-sensitive operations on `ctx.stealth.fetch()` with an SDK stealth
|
|
737
|
-
`profile`; the TypeScript runtime uses `
|
|
737
|
+
`profile`; the TypeScript runtime uses `wreq-js` behind that interface, so do
|
|
738
738
|
not add per-operation JA3, HTTP/2 SETTINGS, or pseudo-header tuning. `ctx.stealth`
|
|
739
|
-
supports Chrome
|
|
740
|
-
|
|
741
|
-
(`nodriver` is Python-runtime only);
|
|
739
|
+
supports Chrome, Firefox, and Safari profiles; use `ctx.browser` when a
|
|
740
|
+
Provider needs real browser execution. TypeScript browser Providers use
|
|
741
|
+
`browser.engine: "playwright-stealth"` (`nodriver` is Python-runtime only);
|
|
742
|
+
install local browser assets with
|
|
742
743
|
`bunx playwright install chromium`, or set
|
|
743
744
|
`APIFUSE__CDP_POOL__URL` for remote browser debugging.
|
|
744
745
|
|
|
@@ -786,7 +787,7 @@ its own diagnostics.
|
|
|
786
787
|
|
|
787
788
|
Set `maxBodyBytes` on `ctx.stealth.fetch()` or `session.redirects.run()` when an
|
|
788
789
|
upstream response has a known safe maximum. The limit is opt-in and counts
|
|
789
|
-
decoded bytes as
|
|
790
|
+
decoded bytes as `wreq-js` streams them. It applies to every redirect hop, uses a
|
|
790
791
|
parseable `Content-Length` for an early rejection, and still enforces the limit
|
|
791
792
|
incrementally when the header is absent or inaccurate. Exceeding the limit
|
|
792
793
|
aborts the response and throws a non-retryable `TransportError` with code
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @apifuse/provider-sdk Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.0-beta.26
|
|
4
|
+
|
|
5
|
+
- Release candidate for main commit 924fe13d1101e7840d799a562a7a70174355185d.
|
|
6
|
+
|
|
3
7
|
## 2.2.0-beta.25
|
|
4
8
|
|
|
5
9
|
- Release candidate for main commit e6df658b95c0728b671fd221919ec2f85f82b0b7.
|
|
@@ -205,7 +209,7 @@
|
|
|
205
209
|
|
|
206
210
|
## 2.1.0-beta.3
|
|
207
211
|
|
|
208
|
-
- Replace the legacy TypeScript request transport with `ctx.stealth`, backed by
|
|
212
|
+
- Replace the legacy TypeScript request transport with `ctx.stealth`, backed by browser-grade TLS/HTTP2 impersonation without Python runtime dependencies.
|
|
209
213
|
- Add the public `apifuse submit-check` / `apifuse bounty-check` CLI for score-based pre-submission provider quality checks.
|
|
210
214
|
- Ship `SUBMISSION.md` in the npm package so bounty contributors can follow the checklist without access to the private monorepo.
|
|
211
215
|
- Include submit-check in generated provider validation scripts and packed-artifact smoke coverage.
|
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ the bad request path; provider/runtime failures include `code`, `message`, and
|
|
|
137
137
|
`context` object.
|
|
138
138
|
- **Stealth-sensitive providers**: use `ctx.http` for normal JSON/REST calls and
|
|
139
139
|
`ctx.stealth.fetch()` when you need browser-like session or cookie control.
|
|
140
|
-
`ctx.stealth.fetch()` uses the
|
|
140
|
+
`ctx.stealth.fetch()` uses the `wreq-js`-backed browser stealth transport and
|
|
141
141
|
accepts request controls for `params`, `sensitiveParams`, `proxy`, `timeout`, `profile`,
|
|
142
142
|
`maxBodyBytes`, `redirect`, `throwOnHttpError`, and
|
|
143
143
|
`stealth.insecureSkipVerify`. For login
|
|
@@ -145,8 +145,8 @@ the bad request path; provider/runtime failures include `code`, `message`, and
|
|
|
145
145
|
a session with `ctx.stealth.createSession()` and use `session.redirects.run()`;
|
|
146
146
|
inspect accumulated cookies through `session.cookies`. Select an SDK stealth
|
|
147
147
|
`profile` such as `chrome-146`; do not tune JA3, HTTP/2 SETTINGS, or
|
|
148
|
-
pseudo-header order in provider code. Chrome
|
|
149
|
-
supported; use `ctx.browser` when
|
|
148
|
+
pseudo-header order in provider code. Chrome, Firefox, and Safari profiles
|
|
149
|
+
are supported; use `ctx.browser` when the provider needs browser execution.
|
|
150
150
|
- **Query-parameter credentials**: when an upstream requires a credential in
|
|
151
151
|
its URL query, pass it through `sensitiveParams`, not `params` and never a
|
|
152
152
|
hand-built URL. It is sent as a normal query parameter while the SDK redacts
|
package/bin/apifuse-check.ts
CHANGED
|
@@ -11,6 +11,11 @@ import {
|
|
|
11
11
|
PROMPT_ASSET_SYNC_REMEDIATION,
|
|
12
12
|
verifyPromptAssets,
|
|
13
13
|
} from "../src/cli/prompt-assets.js";
|
|
14
|
+
import {
|
|
15
|
+
DECLARATION_INVALID_CODE,
|
|
16
|
+
validateFailClosedDeclaration,
|
|
17
|
+
} from "../src/declaration-validation.js";
|
|
18
|
+
import { isProviderError } from "../src/errors.js";
|
|
14
19
|
import type { ProviderDefinition } from "../src/index.js";
|
|
15
20
|
import { lintProvider, type ProviderLintMode } from "../src/lint.js";
|
|
16
21
|
import { safeParseSchemaSync } from "../src/schema.js";
|
|
@@ -119,14 +124,25 @@ export async function runChecks(
|
|
|
119
124
|
const dockerfilePath = resolve(providerRoot, "Dockerfile");
|
|
120
125
|
const packageJsonPath = resolve(providerRoot, "package.json");
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
let providerModule: Record<string, unknown> | undefined;
|
|
128
|
+
let providerImportError: unknown;
|
|
129
|
+
if (existsSync(indexPath)) {
|
|
130
|
+
try {
|
|
131
|
+
providerModule = (await import(pathToFileURL(indexPath).href)) as Record<string, unknown>;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (isProviderError(error) && error.code === DECLARATION_INVALID_CODE) {
|
|
134
|
+
providerImportError = error;
|
|
135
|
+
} else {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
125
140
|
const provider = assertProviderDefinition(providerModule?.default);
|
|
126
141
|
const providerSourceFiles = collectProviderSourceFiles(providerRoot);
|
|
127
142
|
|
|
128
143
|
return [
|
|
129
144
|
checkIndex(indexPath, provider),
|
|
145
|
+
checkDeclaration(provider, providerImportError),
|
|
130
146
|
checkOperations(provider),
|
|
131
147
|
checkFixtures(provider),
|
|
132
148
|
checkSchemas(provider),
|
|
@@ -138,6 +154,49 @@ export async function runChecks(
|
|
|
138
154
|
];
|
|
139
155
|
}
|
|
140
156
|
|
|
157
|
+
const DECLARATION_CHECK_MESSAGE = "Provider declaration passes fail-closed validation";
|
|
158
|
+
|
|
159
|
+
function checkDeclaration(
|
|
160
|
+
provider: ProviderDefinition | undefined,
|
|
161
|
+
importError: unknown,
|
|
162
|
+
): CheckResult {
|
|
163
|
+
if (importError !== undefined) {
|
|
164
|
+
return {
|
|
165
|
+
message: DECLARATION_CHECK_MESSAGE,
|
|
166
|
+
passed: false,
|
|
167
|
+
details: formatDeclarationError(importError),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (!provider) return { message: DECLARATION_CHECK_MESSAGE, passed: false };
|
|
171
|
+
try {
|
|
172
|
+
validateFailClosedDeclaration(provider);
|
|
173
|
+
return { message: DECLARATION_CHECK_MESSAGE, passed: true };
|
|
174
|
+
} catch (error) {
|
|
175
|
+
return {
|
|
176
|
+
message: DECLARATION_CHECK_MESSAGE,
|
|
177
|
+
passed: false,
|
|
178
|
+
details: formatDeclarationError(error),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function formatDeclarationError(error: unknown): string[] {
|
|
184
|
+
if (isProviderError(error) && error.code === DECLARATION_INVALID_CODE) {
|
|
185
|
+
const details = error.details;
|
|
186
|
+
if (isRecord(details) && Array.isArray(details.violations)) {
|
|
187
|
+
return details.violations.map((violation) => {
|
|
188
|
+
if (!isRecord(violation)) return String(violation);
|
|
189
|
+
const ruleId = typeof violation.ruleId === "string" ? violation.ruleId : "unknown-rule";
|
|
190
|
+
const path = typeof violation.path === "string" ? violation.path : "unknown-path";
|
|
191
|
+
const message = typeof violation.message === "string" ? `: ${violation.message}` : "";
|
|
192
|
+
const fix = typeof violation.fix === "string" ? ` Fix: ${violation.fix}` : "";
|
|
193
|
+
return `${path} [${ruleId}]${message}${fix}`;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return [error instanceof Error ? error.message : String(error)];
|
|
198
|
+
}
|
|
199
|
+
|
|
141
200
|
export const PROMPT_ASSETS_CHECK_MESSAGE =
|
|
142
201
|
"Agent prompt assets match the installed SDK version";
|
|
143
202
|
|
|
@@ -156,9 +156,9 @@ function assertPublicSmokeDocs(label: string, content: string): void {
|
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
if (!content.includes("
|
|
159
|
+
if (!content.includes("wreq-js")) {
|
|
160
160
|
throw new Error(
|
|
161
|
-
`${label} must include
|
|
161
|
+
`${label} must include wreq-js stealth runtime guidance for TLS/browser bounties.`,
|
|
162
162
|
);
|
|
163
163
|
}
|
|
164
164
|
|
|
@@ -168,6 +168,12 @@ function assertPublicSmokeDocs(label: string, content: string): void {
|
|
|
168
168
|
);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
if (!content.includes("Chrome, Firefox, and Safari")) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`${label} must document the browser families supported by the TypeScript stealth runtime.`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
171
177
|
if (
|
|
172
178
|
!content.includes('browser.engine: "playwright-stealth"') ||
|
|
173
179
|
!content.includes("nodriver")
|
|
@@ -73,6 +73,7 @@ try {
|
|
|
73
73
|
],
|
|
74
74
|
consumerDir,
|
|
75
75
|
);
|
|
76
|
+
smokePackedStealthNative(consumerDir);
|
|
76
77
|
|
|
77
78
|
const cliBin = join(consumerDir, "node_modules", ".bin", "apifuse");
|
|
78
79
|
if (!existsSync(cliBin)) {
|
|
@@ -216,6 +217,43 @@ function run(command: string, args: string[], cwd: string): void {
|
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
function smokePackedStealthNative(consumerDir: string): void {
|
|
221
|
+
run(
|
|
222
|
+
"bun",
|
|
223
|
+
[
|
|
224
|
+
"--eval",
|
|
225
|
+
[
|
|
226
|
+
'import { createServer } from "node:http";',
|
|
227
|
+
'import { createStealthClient } from "@apifuse/provider-sdk";',
|
|
228
|
+
"const server = createServer((_request, response) => {",
|
|
229
|
+
' response.setHeader("set-cookie", "pack_native_cookie=landed; Path=/");',
|
|
230
|
+
' response.end("packed native stealth ok");',
|
|
231
|
+
"});",
|
|
232
|
+
"await new Promise((resolve, reject) => {",
|
|
233
|
+
' server.once("error", reject);',
|
|
234
|
+
' server.listen(0, "127.0.0.1", resolve);',
|
|
235
|
+
"});",
|
|
236
|
+
"const address = server.address();",
|
|
237
|
+
'if (!address || typeof address === "string") throw new Error("Local server has no TCP address");',
|
|
238
|
+
'const baseUrl = "http://127.0.0.1:" + address.port;',
|
|
239
|
+
'const session = createStealthClient(baseUrl).createSession({ profile: "safari-17" });',
|
|
240
|
+
"try {",
|
|
241
|
+
' const response = await session.fetch("/native");',
|
|
242
|
+
' if (response.body !== "packed native stealth ok") throw new Error("Unexpected stealth body: " + response.body);',
|
|
243
|
+
' if (session.cookies.get("pack_native_cookie", baseUrl + "/native") !== "landed") {',
|
|
244
|
+
' throw new Error("Packed stealth Set-Cookie did not land in the SDK jar");',
|
|
245
|
+
" }",
|
|
246
|
+
' console.log("packed native Safari stealth request OK");',
|
|
247
|
+
"} finally {",
|
|
248
|
+
" session.close();",
|
|
249
|
+
" await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));",
|
|
250
|
+
"}",
|
|
251
|
+
].join("\n"),
|
|
252
|
+
],
|
|
253
|
+
consumerDir,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
219
257
|
function assertGeneratedReadme(providerDir: string): void {
|
|
220
258
|
const readme = readFileSync(join(providerDir, "README.md"), "utf8");
|
|
221
259
|
if (!readme.includes('"requestId":"req_local_ping"')) {
|
|
@@ -227,8 +265,11 @@ function assertGeneratedReadme(providerDir: string): void {
|
|
|
227
265
|
if (!readme.includes("bunx playwright install chromium")) {
|
|
228
266
|
throw new Error("Generated README is missing browser runtime troubleshooting guidance.");
|
|
229
267
|
}
|
|
230
|
-
if (!readme.includes("
|
|
231
|
-
throw new Error("Generated README is missing
|
|
268
|
+
if (!readme.includes("wreq-js")) {
|
|
269
|
+
throw new Error("Generated README is missing wreq-js stealth runtime guidance.");
|
|
270
|
+
}
|
|
271
|
+
if (!readme.includes("Chrome, Firefox, and Safari")) {
|
|
272
|
+
throw new Error("Generated README is missing supported stealth browser families.");
|
|
232
273
|
}
|
|
233
274
|
if (!readme.includes("bun run submit-check")) {
|
|
234
275
|
throw new Error("Generated README must document the submit-check pre-submission workflow.");
|
|
@@ -143,6 +143,51 @@ const NEGATIVE_CONTROLS = [
|
|
|
143
143
|
"",
|
|
144
144
|
].join("\n"),
|
|
145
145
|
},
|
|
146
|
+
{
|
|
147
|
+
filename: "negative-control-aws-waf-site-key-type.ts",
|
|
148
|
+
expectedCode: "TS2322",
|
|
149
|
+
description: "aws_waf siteKey must be a string",
|
|
150
|
+
source: [
|
|
151
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
152
|
+
"",
|
|
153
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
154
|
+
'\tkind: "aws_waf",',
|
|
155
|
+
'\tpageUrl: "https://example.com",',
|
|
156
|
+
"\tsiteKey: 123,",
|
|
157
|
+
"};",
|
|
158
|
+
"",
|
|
159
|
+
].join("\n"),
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
filename: "negative-control-aws-waf-context-type.ts",
|
|
163
|
+
expectedCode: "TS2322",
|
|
164
|
+
description: "aws_waf context must be a string",
|
|
165
|
+
source: [
|
|
166
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
167
|
+
"",
|
|
168
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
169
|
+
'\tkind: "aws_waf",',
|
|
170
|
+
'\tpageUrl: "https://example.com",',
|
|
171
|
+
"\tcontext: 123,",
|
|
172
|
+
"};",
|
|
173
|
+
"",
|
|
174
|
+
].join("\n"),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
filename: "negative-control-aws-waf-unknown-field.ts",
|
|
178
|
+
expectedCode: "TS2353",
|
|
179
|
+
description: "aws_waf rejects fields it does not declare",
|
|
180
|
+
source: [
|
|
181
|
+
'import type { ProviderChallenge } from "@apifuse/provider-sdk";',
|
|
182
|
+
"",
|
|
183
|
+
"export const mustNotCompile: ProviderChallenge = {",
|
|
184
|
+
'\tkind: "aws_waf",',
|
|
185
|
+
'\tpageUrl: "https://example.com",',
|
|
186
|
+
"\tunknownField: 1,",
|
|
187
|
+
"};",
|
|
188
|
+
"",
|
|
189
|
+
].join("\n"),
|
|
190
|
+
},
|
|
146
191
|
{
|
|
147
192
|
filename: "negative-control-recaptcha-v3-action.ts",
|
|
148
193
|
expectedCode: "TS2322",
|
|
@@ -184,6 +229,19 @@ const NEGATIVE_CONTROLS = [
|
|
|
184
229
|
"",
|
|
185
230
|
].join("\n"),
|
|
186
231
|
},
|
|
232
|
+
{
|
|
233
|
+
filename: "negative-control-resolver-runtime-adapter-factories.ts",
|
|
234
|
+
expectedCode: "TS2353",
|
|
235
|
+
description: "ResolverRuntimeOptions does not accept caller-supplied adapter factories",
|
|
236
|
+
source: [
|
|
237
|
+
'import type { ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
|
|
238
|
+
"",
|
|
239
|
+
"export const mustNotCompile: ResolverRuntimeOptions = {",
|
|
240
|
+
"\tadapterFactories: {},",
|
|
241
|
+
"};",
|
|
242
|
+
"",
|
|
243
|
+
].join("\n"),
|
|
244
|
+
},
|
|
187
245
|
{
|
|
188
246
|
filename: "negative-control-resolver-runtime-allowed-hosts.ts",
|
|
189
247
|
expectedCode: "TS2322",
|
package/dist/auth.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthError, ProviderError } from "./errors.js";
|
|
2
|
+
import { declarationInvalidError, DECLARATION_RULE_IDS, } from "./declaration-validation.js";
|
|
2
3
|
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
3
4
|
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
4
5
|
const DEFAULT_ABORT_TURN_ID = "auth.abort";
|
|
@@ -379,6 +380,7 @@ export function defineCredentialsAuth(options) {
|
|
|
379
380
|
const retryTurnId = options.retryTurnId ?? "credentials.retry";
|
|
380
381
|
const completeTurnId = options.completeTurnId ?? "credentials.complete";
|
|
381
382
|
const challenges = (options.challenges ?? {});
|
|
383
|
+
validateCredentialsAuthChallenges(challenges);
|
|
382
384
|
return {
|
|
383
385
|
auth: {
|
|
384
386
|
mode: "credentials",
|
|
@@ -425,3 +427,30 @@ export function defineCredentialsAuth(options) {
|
|
|
425
427
|
},
|
|
426
428
|
};
|
|
427
429
|
}
|
|
430
|
+
function validateCredentialsAuthChallenges(challenges) {
|
|
431
|
+
const violations = [];
|
|
432
|
+
for (const [challengeId, challenge] of Object.entries(challenges)) {
|
|
433
|
+
const fieldCount = challenge.fields && typeof challenge.fields === "object"
|
|
434
|
+
? Object.keys(challenge.fields).length
|
|
435
|
+
: 0;
|
|
436
|
+
const fieldsDeclared = challenge.fields !== undefined && challenge.fields !== null;
|
|
437
|
+
const hasFields = fieldCount > 0;
|
|
438
|
+
const hasVerify = typeof challenge.verify === "function";
|
|
439
|
+
const hasPoll = typeof challenge.poll === "function";
|
|
440
|
+
const isInteractive = hasFields && hasVerify && !hasPoll;
|
|
441
|
+
const isPolling = !fieldsDeclared && !hasVerify && hasPoll;
|
|
442
|
+
const isHybrid = hasFields && hasVerify && hasPoll;
|
|
443
|
+
const emptyFieldsDeclared = fieldsDeclared && !hasFields;
|
|
444
|
+
if (!emptyFieldsDeclared && (isInteractive || isPolling || isHybrid))
|
|
445
|
+
continue;
|
|
446
|
+
const path = `challenges.${challengeId}`;
|
|
447
|
+
violations.push({
|
|
448
|
+
ruleId: DECLARATION_RULE_IDS.challengeShape,
|
|
449
|
+
path,
|
|
450
|
+
message: "challenge must be interactive, polling, or an explicit hybrid.",
|
|
451
|
+
fix: `Give ${path} non-empty fields plus verify, poll alone, or all three for a hybrid.`,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
if (violations.length > 0)
|
|
455
|
+
throw declarationInvalidError(violations);
|
|
456
|
+
}
|
|
@@ -114,10 +114,10 @@ Structured errors return an `error` object with `code`, `message`,
|
|
|
114
114
|
- Auth flow: call `/auth/start`, then `/auth/continue` with the same `flowId`;
|
|
115
115
|
carry returned `contextPatch` values into the next request's `context`.
|
|
116
116
|
- Stealth/browser runtime: keep access-sensitive operations on `ctx.stealth.fetch()` with an
|
|
117
|
-
SDK stealth `profile`; the TypeScript stealth runtime uses `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(`nodriver` is Python-runtime only)
|
|
117
|
+
SDK stealth `profile`; the TypeScript stealth runtime uses `wreq-js` internally
|
|
118
|
+
and supports Chrome, Firefox, and Safari profiles. Use `ctx.browser` only when
|
|
119
|
+
the provider needs browser execution; TypeScript browser Providers use
|
|
120
|
+
`browser.engine: "playwright-stealth"` (`nodriver` is Python-runtime only). Install local Chromium with
|
|
121
121
|
`bunx playwright install chromium` or set `APIFUSE__CDP_POOL__URL`.
|
|
122
122
|
|
|
123
123
|
## Next steps
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { type JsonValue } from "./contract-json.js";
|
|
2
2
|
import type { SchemaLike } from "./types.js";
|
|
3
|
-
|
|
3
|
+
interface DescribeSchemaOptions {
|
|
4
|
+
readonly eventName?: string;
|
|
5
|
+
readonly operationId?: string;
|
|
6
|
+
readonly outputTextTrust?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class OutputTextTrustProjectionError extends Error {
|
|
9
|
+
readonly schemaPath: string;
|
|
10
|
+
readonly operationId?: string | undefined;
|
|
11
|
+
readonly eventName?: string | undefined;
|
|
12
|
+
readonly classification: "untrusted";
|
|
13
|
+
readonly code = "output_text_trust_projection_failed";
|
|
14
|
+
constructor(schemaPath: string, cause: unknown, operationId?: string | undefined, eventName?: string | undefined);
|
|
15
|
+
}
|
|
16
|
+
export declare class OutputTextTrustProjectionMarkerError extends TypeError {
|
|
17
|
+
readonly schemaPath: string;
|
|
18
|
+
readonly code = "invalid_output_text_trust_projection_marker";
|
|
19
|
+
constructor(schemaPath: string);
|
|
20
|
+
}
|
|
21
|
+
export declare function describeSchema(schema: SchemaLike, options?: DescribeSchemaOptions): JsonValue;
|
|
4
22
|
export declare function serializeSmsMatcher(value: Record<string, unknown>): Record<string, unknown>;
|
|
23
|
+
export {};
|