Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/quality/file-size-baseline.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"_comment": "Catraca de tamanho (check-file-size.mjs). frozen so pode encolher; arquivos novos <= cap. --update ratcheta.",
"_rebaseline_2026_06_20_4401_webfetch_validators": "PR #4401 own growth: src/lib/providers/validation.ts 4428->4450 (+22 = two new API-key validators — Firecrawl + Jina Reader — each a cohesive provider validator branch mirroring the existing ones; 401/403->invalid else->valid). Not extractable (it IS the per-provider validator list). Covered by tests/unit/provider-validation-webfetch-4401.test.ts.",
"_rebaseline_2026_06_20_4380_parse_once": "PR #4380 own growth: src/sse/handlers/chat.ts 1486->1491 (+5 = thread the once-parsed request body from the route guard into handleChat, replacing the duplicate re-parse). The reusable body accessor lives in the new src/sse/handlers/requestBody.ts (<cap). Thin wiring at the single entry chokepoint; not extractable. Covered by tests/unit/chat-request-body-parse-once-4380.test.ts.",
"_rebaseline_2026_06_19_4313_harvest_x_sweep_reconcile": "RECONCILIACAO ao mergear #4313 (5 harvested features) APOS o provider-sweep #4324: valores MEDIDOS na arvore combinada release(com #4324)+#4313. route.ts 2580->2586 (sweep +19 NAMED_OPENAI_STYLE + #4313 +3 openadapter/dit/tokenrouter = uniao no Set, regioes disjuntas). providers.ts 3198->3242 (sweep 6 dead-provider marks + #4313 3 novas entries APIKEY_PROVIDERS). combos/page.tsx 4350->4385 (#4313 #3266 allowlist UI) e providers/page.tsx 1925->1927 (#4313 #4240 serviceKind state) — intocados pelo sweep, crescimento puro do #4313. Mesmo padrao release-volatil de medir-no-merge dos baselines de complexity/zizmor deste lote.",
"_rebaseline_2026_06_19_provider_sweep_merge_reconcile": "provider-model-sweep PR merge into release/v3.8.30: the sweep's route.ts growth (NAMED_OPENAI_STYLE_PROVIDERS +19 entries, base 2538->2564) and #4259's cloudflare parseResponse (2538->2554) are disjoint regions that both land, so the merged route.ts frozen value is reconciled to its measured size here. constants/providers.ts carries the sweep's 6 dead-provider deprecation marks on top of release. chatCore.ts stays at #4266's 5128 (sweep does not touch it).",
Expand Down Expand Up @@ -168,7 +169,7 @@
"src/lib/evals/evalRunner.ts": 961,
"src/lib/memory/retrieval.ts": 1171,
"src/lib/modelsDevSync.ts": 934,
"src/lib/providers/validation.ts": 4428,
"src/lib/providers/validation.ts": 4450,
"src/lib/tailscaleTunnel.ts": 1202,
"src/lib/usage/callLogs.ts": 975,
"src/lib/usage/providerLimits.ts": 949,
Expand Down
22 changes: 22 additions & 0 deletions src/lib/providers/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2678,6 +2678,28 @@ const SEARCH_VALIDATOR_CONFIGS: Record<
},
};
},
// ── Web-fetch providers (#4401) ──
// firecrawl / jina-reader were added as webFetch-kind providers in #2645 with their
// own executors but no validator, so the dashboard "Validate" step returned
// "Provider validation not supported" and accounts could not be added through the UI.
// Probe each provider's real fetch endpoint with the same Bearer auth the executor
// uses; validateSearchProvider maps 200/<500 → valid, 401/403 → invalid key,
// >=500 → failure (a credit-exhausted / rate-limited key still validates).
firecrawl: (apiKey) => ({
url: "https://api.firecrawl.dev/v1/scrape",
init: {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
body: JSON.stringify({ url: "https://example.com", formats: ["markdown"] }),
},
}),
"jina-reader": (apiKey) => ({
url: "https://r.jina.ai/https://example.com",
init: {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
},
}),
};

// See open-sse/executors/muse-spark-web.ts for the rationale: Meta migrated
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/provider-validation-webfetch-4401.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import test from "node:test";
import assert from "node:assert/strict";

// #4401: Firecrawl and Jina Reader were added as webFetch providers in #2645 with
// their own executors, but no API-key validator was registered — so adding an account
// through the dashboard failed with "Provider validation not supported". These tests
// pin the validator dispatch (firecrawl → POST api.firecrawl.dev/v1/scrape with Bearer;
// jina-reader → GET r.jina.ai/<url> with Bearer) and the auth-failure mapping.

const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");

const originalFetch = globalThis.fetch;

test.afterEach(() => {
globalThis.fetch = originalFetch;
});

function headerValue(init: RequestInit | undefined, name: string): string | undefined {
const headers = (init?.headers || {}) as Record<string, string>;
return headers[name];
}

test("#4401 firecrawl validator probes the scrape endpoint with Bearer auth and accepts a 200", async () => {
const calls: { url: string; init: RequestInit }[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return new Response(JSON.stringify({ success: true, data: {} }), { status: 200 });
};

const result = await validateProviderApiKey({ provider: "firecrawl", apiKey: "fc-test-key" });

assert.equal(result.valid, true);
assert.equal(result.unsupported ?? false, false);
assert.equal(calls.length, 1);
assert.equal(calls[0].url, "https://api.firecrawl.dev/v1/scrape");
assert.equal(calls[0].init.method, "POST");
assert.equal(headerValue(calls[0].init, "Authorization"), "Bearer fc-test-key");
});

test("#4401 jina-reader validator probes r.jina.ai with Bearer auth and accepts a 200", async () => {
const calls: { url: string; init: RequestInit }[] = [];
globalThis.fetch = async (url, init = {}) => {
calls.push({ url: String(url), init });
return new Response("# Example\n", { status: 200 });
};

const result = await validateProviderApiKey({ provider: "jina-reader", apiKey: "jina-test-key" });

assert.equal(result.valid, true);
assert.equal(result.unsupported ?? false, false);
assert.equal(calls.length, 1);
assert.match(calls[0].url, /^https:\/\/r\.jina\.ai\//);
assert.equal(headerValue(calls[0].init, "Authorization"), "Bearer jina-test-key");
});

test("#4401 webFetch validators map 401/403 to an invalid-key error, not 'not supported'", async () => {
globalThis.fetch = async () =>
new Response(JSON.stringify({ error: "unauthorized" }), { status: 401 });

const firecrawl = await validateProviderApiKey({ provider: "firecrawl", apiKey: "bad" });
const jina = await validateProviderApiKey({ provider: "jina-reader", apiKey: "bad" });

assert.equal(firecrawl.valid, false);
assert.equal(firecrawl.error, "Invalid API key");
assert.equal(jina.valid, false);
assert.equal(jina.error, "Invalid API key");
});
Loading