@omnicross/core 0.2.0 → 0.3.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/{BuiltinToolExecutor-CS2WpXhM.d.cts → BuiltinToolExecutor-BHgH-EaZ.d.cts} +25 -2
- package/dist/{BuiltinToolExecutor-BluWyeob.d.ts → BuiltinToolExecutor-Bi1LmiTP.d.ts} +25 -2
- package/dist/{CompletionService-BiJqftc-.d.cts → CompletionService-BniP7j88.d.cts} +2 -2
- package/dist/{CompletionService-NXN_Av5W.d.ts → CompletionService-BqrKPwfA.d.ts} +2 -2
- package/dist/{ProviderProxy-CgIcCVwk.d.cts → ProviderProxy-Bz_nkOwq.d.cts} +1 -1
- package/dist/{ProviderProxy-DYoY5XoT.d.ts → ProviderProxy-Ckr6oTrE.d.ts} +1 -1
- package/dist/auth/GeminiCodeAssistProjectResolver.cjs +2 -2
- package/dist/auth/GeminiCodeAssistProjectResolver.js +1 -1
- package/dist/{chunk-FJNKXOWE.js → chunk-2C3TTBS7.js} +4 -4
- package/dist/{chunk-NZYN7C3P.cjs → chunk-2RCCLQYV.cjs} +53 -53
- package/dist/{chunk-CDHS2QDC.js → chunk-4PFWFODB.js} +53 -53
- package/dist/{chunk-OST5RJOJ.cjs → chunk-4Z3ZLLC2.cjs} +13 -7
- package/dist/chunk-7RJI3NDI.cjs +236 -0
- package/dist/chunk-BTBEJRGL.cjs +781 -0
- package/dist/chunk-DBBR6RHF.js +236 -0
- package/dist/chunk-DXWFBBSK.js +781 -0
- package/dist/chunk-E4QHVXV5.cjs +118 -0
- package/dist/chunk-L7O2YQ5O.js +386 -0
- package/dist/{chunk-WSJE42SG.js → chunk-LKDAN4D7.js} +3684 -2462
- package/dist/{chunk-DEIUR2TH.js → chunk-LQYS2N47.js} +12 -8
- package/dist/chunk-MZEGQSKQ.js +118 -0
- package/dist/chunk-PTU7SXM3.cjs +386 -0
- package/dist/{chunk-CTKM773J.cjs → chunk-UPQIWJIE.cjs} +12 -8
- package/dist/{chunk-CFSGYFOZ.js → chunk-VL5TUDTM.js} +7 -1
- package/dist/{chunk-7B56AJBB.cjs → chunk-VSYQ6XYN.cjs} +2233 -1011
- package/dist/{chunk-TVYA5JSQ.cjs → chunk-ZAPN7XA6.cjs} +5 -5
- package/dist/completion/BuiltinToolExecutor.cjs +2 -2
- package/dist/completion/BuiltinToolExecutor.d.cts +3 -1
- package/dist/completion/BuiltinToolExecutor.d.ts +3 -1
- package/dist/completion/BuiltinToolExecutor.js +1 -1
- package/dist/completion/CompletionService.cjs +11 -10
- package/dist/completion/CompletionService.d.cts +6 -3
- package/dist/completion/CompletionService.d.ts +6 -3
- package/dist/completion/CompletionService.js +10 -9
- package/dist/completion.cjs +11 -10
- package/dist/completion.d.cts +6 -3
- package/dist/completion.d.ts +6 -3
- package/dist/completion.js +10 -9
- package/dist/egress-I46O_p5L.d.cts +126 -0
- package/dist/egress-I46O_p5L.d.ts +126 -0
- package/dist/frontends-Doz0RaWu.d.cts +97 -0
- package/dist/frontends-Doz0RaWu.d.ts +97 -0
- package/dist/image-generation.cjs +2 -2
- package/dist/image-generation.d.cts +7 -4
- package/dist/image-generation.d.ts +7 -4
- package/dist/image-generation.js +1 -1
- package/dist/index.cjs +25 -10
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +24 -9
- package/dist/openai-operation.d.cts +4 -1
- package/dist/openai-operation.d.ts +4 -1
- package/dist/outbound-api/routeResolver.d.cts +9 -5
- package/dist/outbound-api/routeResolver.d.ts +9 -5
- package/dist/outbound-api/subscriptionRegistryPort.d.cts +4 -1
- package/dist/outbound-api/subscriptionRegistryPort.d.ts +4 -1
- package/dist/outbound-api/types.d.cts +8 -4
- package/dist/outbound-api/types.d.ts +8 -4
- package/dist/outbound-api.cjs +25 -10
- package/dist/outbound-api.d.cts +117 -8
- package/dist/outbound-api.d.ts +117 -8
- package/dist/outbound-api.js +24 -9
- package/dist/pipeline/AccountAllowanceScheduling.d.cts +8 -4
- package/dist/pipeline/AccountAllowanceScheduling.d.ts +8 -4
- package/dist/pipeline/upstreamFetch.cjs +2 -2
- package/dist/pipeline/upstreamFetch.js +1 -1
- package/dist/ports.d.cts +8 -4
- package/dist/ports.d.ts +8 -4
- package/dist/provider-proxy/ProviderProxy.cjs +11 -10
- package/dist/provider-proxy/ProviderProxy.d.cts +5 -2
- package/dist/provider-proxy/ProviderProxy.d.ts +5 -2
- package/dist/provider-proxy/ProviderProxy.js +10 -9
- package/dist/provider-proxy/ingress/providerProxyShared.cjs +13 -10
- package/dist/provider-proxy/ingress/providerProxyShared.d.cts +22 -3
- package/dist/provider-proxy/ingress/providerProxyShared.d.ts +22 -3
- package/dist/provider-proxy/ingress/providerProxyShared.js +12 -9
- package/dist/provider-proxy/types.d.cts +4 -1
- package/dist/provider-proxy/types.d.ts +4 -1
- package/dist/provider-proxy.cjs +11 -10
- package/dist/provider-proxy.d.cts +10 -6
- package/dist/provider-proxy.d.ts +10 -6
- package/dist/provider-proxy.js +10 -9
- package/dist/proxy-UZTtjPZx.d.cts +18 -0
- package/dist/proxy-UZTtjPZx.d.ts +18 -0
- package/dist/{routeResolver-rpKQLyDO.d.cts → routeResolver-Da0yvkL1.d.cts} +2 -2
- package/dist/{routeResolver-CTCcSHWu.d.ts → routeResolver-STwMz_G4.d.ts} +2 -2
- package/dist/runtime-5NCT3J3T.cjs +8 -0
- package/dist/runtime-JTHUWCBM.js +8 -0
- package/dist/runtime-t8AZacFF.d.cts +233 -0
- package/dist/runtime-t8AZacFF.d.ts +233 -0
- package/dist/search/api.cjs +766 -0
- package/dist/search/api.d.cts +385 -0
- package/dist/search/api.d.ts +385 -0
- package/dist/search/api.js +766 -0
- package/dist/search/http.cjs +37 -0
- package/dist/search/http.d.cts +323 -0
- package/dist/search/http.d.ts +323 -0
- package/dist/search/http.js +37 -0
- package/dist/search.cjs +57 -0
- package/dist/search.d.cts +113 -0
- package/dist/search.d.ts +113 -0
- package/dist/search.js +57 -0
- package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -3
- package/dist/transformer/transformers/AnthropicTransformer.js +2 -2
- package/dist/transformer/transformers.cjs +7 -7
- package/dist/transformer/transformers.js +6 -6
- package/dist/transformer.cjs +6 -6
- package/dist/transformer.js +5 -5
- package/dist/{types-DsHBBk3h.d.cts → types-BSIJdsyl.d.cts} +15 -0
- package/dist/{types-CbglB73l.d.cts → types-Be-yt_6T.d.cts} +1 -1
- package/dist/{types-CVRge76p.d.ts → types-By9Tg6-I.d.ts} +15 -0
- package/dist/types-CbCZqLGg.d.cts +127 -0
- package/dist/types-CbCZqLGg.d.ts +127 -0
- package/dist/{types-jjU24eYG.d.cts → types-D-oHYusb.d.cts} +73 -4
- package/dist/{types-Bd72H4SB.d.ts → types-Dm4_36V4.d.ts} +1 -1
- package/dist/{types-5T223WYQ.d.ts → types-Dvlpjn0d.d.ts} +73 -4
- package/package.json +6 -3
- package/dist/{chunk-6OC3ORW6.js → chunk-EUZRH544.js} +3 -3
- package/dist/{chunk-SQMAEDNE.cjs → chunk-POUTD5AM.cjs} +2 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
var _chunkBTBEJRGLcjs = require('../chunk-BTBEJRGL.cjs');
|
|
19
|
+
require('../chunk-7RJI3NDI.cjs');
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
exports.DEFAULT_MAX_RESULTS = _chunkBTBEJRGLcjs.DEFAULT_MAX_RESULTS; exports.DEFAULT_SEARCH_TIMEOUT_MS = _chunkBTBEJRGLcjs.DEFAULT_SEARCH_TIMEOUT_MS; exports.HTTP_BING_PROVIDER_ID = _chunkBTBEJRGLcjs.HTTP_BING_PROVIDER_ID; exports.HTTP_DUCKDUCKGO_PROVIDER_ID = _chunkBTBEJRGLcjs.HTTP_DUCKDUCKGO_PROVIDER_ID; exports.HTTP_SEARCH_CAPABILITIES = _chunkBTBEJRGLcjs.HTTP_SEARCH_CAPABILITIES; exports.HttpBingProvider = _chunkBTBEJRGLcjs.HttpBingProvider; exports.HttpDuckDuckGoProvider = _chunkBTBEJRGLcjs.HttpDuckDuckGoProvider; exports.MAX_REDIRECTS = _chunkBTBEJRGLcjs.MAX_REDIRECTS; exports.MAX_RESULTS = _chunkBTBEJRGLcjs.MAX_RESULTS; exports.SEARCH_BROWSER_HEADERS = _chunkBTBEJRGLcjs.SEARCH_BROWSER_HEADERS; exports.SEARCH_HTTP_TRANSPORT_ID = _chunkBTBEJRGLcjs.SEARCH_HTTP_TRANSPORT_ID; exports.SEARCH_RESPONSE_BYTES = _chunkBTBEJRGLcjs.SEARCH_RESPONSE_BYTES; exports.builtinHttpSearchContributions = _chunkBTBEJRGLcjs.builtinHttpSearchContributions; exports.clampMaxResults = _chunkBTBEJRGLcjs.clampMaxResults; exports.createSearchHttpTransport = _chunkBTBEJRGLcjs.createSearchHttpTransport; exports.defaultSearchHttpTransport = _chunkBTBEJRGLcjs.defaultSearchHttpTransport;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { SearchProviderId, SearchResult, SearchProviderCapabilities, SearchProviderContribution, SearchProvider, SearchOptions } from '@omnicross/contracts/search-types';
|
|
2
|
+
import { ProxyConfig } from '@omnicross/contracts/account-tokens-types';
|
|
3
|
+
import { Dispatcher } from 'undici';
|
|
4
|
+
import { S as SearchEgressPolicy } from '../egress-I46O_p5L.cjs';
|
|
5
|
+
import { ImpitOptions, Impit } from 'impit';
|
|
6
|
+
import { P as ProxyEnvironment } from '../proxy-UZTtjPZx.cjs';
|
|
7
|
+
import 'node:dns';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shared vocabulary for the keyless HTTP search slice (plan 阶段2).
|
|
11
|
+
*
|
|
12
|
+
* Everything here is transport- and parser-neutral so the providers, the
|
|
13
|
+
* shared transport, and the doctor can agree on one set of names without
|
|
14
|
+
* importing each other.
|
|
15
|
+
*
|
|
16
|
+
* @module search/http/types
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The transport identity stamped on every failure this slice throws.
|
|
21
|
+
*
|
|
22
|
+
* Phase 1 ships exactly one transport. `impit` (Elftia's browser-fingerprint
|
|
23
|
+
* client) is deliberately NOT introduced here — the injection seam below is the
|
|
24
|
+
* whole extent of future-proofing, so an alternative transport can never become
|
|
25
|
+
* an implicit behavior change.
|
|
26
|
+
*/
|
|
27
|
+
declare const SEARCH_HTTP_TRANSPORT_ID = "undici";
|
|
28
|
+
/**
|
|
29
|
+
* Which phase of a search failed (plan §11.4 — transport identity and failure
|
|
30
|
+
* stage must be observable).
|
|
31
|
+
*
|
|
32
|
+
* - `connect` — the request never produced a response (DNS, TCP, TLS).
|
|
33
|
+
* - `fetch` — a response arrived but is unusable (refused status, deadline).
|
|
34
|
+
* - `redirect` — the redirect chain exceeded its cap.
|
|
35
|
+
* - `body-cap` — the response body outgrew its byte cap mid-stream.
|
|
36
|
+
* - `challenge` — the engine served a bot challenge instead of a SERP.
|
|
37
|
+
* - `trust` — the page parsed, but the anti-decoy check refused it.
|
|
38
|
+
* - `parse` — the page is not recognizable as this engine's SERP.
|
|
39
|
+
*/
|
|
40
|
+
type SearchHttpStage = 'connect' | 'fetch' | 'redirect' | 'body-cap' | 'challenge' | 'trust' | 'parse';
|
|
41
|
+
/** One fetched search page. */
|
|
42
|
+
interface SearchHttpResource {
|
|
43
|
+
/** The URL the response was finally served from (after redirects). */
|
|
44
|
+
finalUrl: string;
|
|
45
|
+
/** HTTP status of the final response. */
|
|
46
|
+
status: number;
|
|
47
|
+
/** `content-type` header value, or `''`. */
|
|
48
|
+
contentType: string;
|
|
49
|
+
/** Decoded response body. */
|
|
50
|
+
rawText: string;
|
|
51
|
+
}
|
|
52
|
+
/** What the transport needs to execute one attempt. */
|
|
53
|
+
interface SearchHttpRequest {
|
|
54
|
+
/** Hard budget for this single attempt, in milliseconds. */
|
|
55
|
+
timeoutMs: number;
|
|
56
|
+
/** Byte cap enforced while the body streams in. */
|
|
57
|
+
maxResponseBytes: number;
|
|
58
|
+
/** Caller cancellation. Aborting surfaces as code `cancelled`, never `timeout`. */
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
/** Stamped onto thrown errors so a failure names its provider. */
|
|
61
|
+
providerId?: SearchProviderId;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The seam both providers accept in their constructor.
|
|
65
|
+
*
|
|
66
|
+
* This is the test seam (fixtures are served through it) and the seam a future
|
|
67
|
+
* alternative transport would occupy.
|
|
68
|
+
*/
|
|
69
|
+
type SearchHttpTransport = (url: string, request: SearchHttpRequest) => Promise<SearchHttpResource>;
|
|
70
|
+
/**
|
|
71
|
+
* The low-level fetch primitive the shared transport drives — undici's `fetch`
|
|
72
|
+
* by default. Separate from {@link SearchHttpTransport} so redirect, body-cap
|
|
73
|
+
* and header behavior can be unit-tested without a network.
|
|
74
|
+
*/
|
|
75
|
+
type SearchHttpFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
76
|
+
/** The outcome of running one engine's parser over one page. */
|
|
77
|
+
interface ParsedSerp {
|
|
78
|
+
/**
|
|
79
|
+
* Whether the page is structurally recognizable as this engine's SERP.
|
|
80
|
+
*
|
|
81
|
+
* This is what separates "the engine found nothing" (recognized, zero
|
|
82
|
+
* results → `[]`) from "the engine's markup changed under us" (unrecognized
|
|
83
|
+
* → `parse_failed`).
|
|
84
|
+
*/
|
|
85
|
+
recognized: boolean;
|
|
86
|
+
/** Usable organic results, already clamped to the caller's `maxResults`. */
|
|
87
|
+
results: SearchResult[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The builtin HTTP search contributions.
|
|
92
|
+
*
|
|
93
|
+
* Everything the registry must not infer is declared here: `source` and `kind`
|
|
94
|
+
* are stated, never derived from the id's spelling, and the capability set is
|
|
95
|
+
* explicit down to the options these engines do NOT honor.
|
|
96
|
+
*
|
|
97
|
+
* `priorityHint` is deliberately absent. Provider ordering is the orchestrator's
|
|
98
|
+
* decision (阶段3); encoding a preference here would recreate exactly the
|
|
99
|
+
* scattered-fallback-order problem the extraction exists to remove.
|
|
100
|
+
*
|
|
101
|
+
* @module search/http/contributions
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* What both keyless HTTP engines can do.
|
|
106
|
+
*
|
|
107
|
+
* `supportsRegion` / `supportsLanguage` / `supportsTimeRange` are false because
|
|
108
|
+
* the scraped endpoints never honored those options; per the contract, a
|
|
109
|
+
* provider ignores an option it does not declare rather than failing.
|
|
110
|
+
*/
|
|
111
|
+
declare const HTTP_SEARCH_CAPABILITIES: Readonly<SearchProviderCapabilities>;
|
|
112
|
+
/**
|
|
113
|
+
* Both builtin HTTP providers, ready to hand to a registry.
|
|
114
|
+
*
|
|
115
|
+
* @param transport - optional shared transport for both providers (tests, or a
|
|
116
|
+
* future alternative client). Omit for the production undici transport.
|
|
117
|
+
*/
|
|
118
|
+
declare function builtinHttpSearchContributions(transport?: SearchHttpTransport): SearchProviderContribution[];
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The candidate-URL walk both HTTP providers share.
|
|
122
|
+
*
|
|
123
|
+
* Mirrors the baselined `searchWebViaFetch` loop (one shared deadline, split
|
|
124
|
+
* evenly across the remaining candidates, last error retained) with one
|
|
125
|
+
* plan-mandated behavior change: the three-way outcome distinction.
|
|
126
|
+
*
|
|
127
|
+
* Elftia collapses "the engine found nothing" and "the engine's markup changed"
|
|
128
|
+
* into a single `response contained no result entries` error. Here they are
|
|
129
|
+
* different answers:
|
|
130
|
+
*
|
|
131
|
+
* - a recognized SERP with zero organic results → `[]` (a legitimate answer);
|
|
132
|
+
* - an unrecognizable page → `parse_failed` (the parser-drift alarm);
|
|
133
|
+
* - a challenge or decoy page → a `challenge`/`trust` staged failure, checked
|
|
134
|
+
* before either of the above.
|
|
135
|
+
*
|
|
136
|
+
* The divergence is deliberate (plan §11.4) and is recorded for 阶段5's
|
|
137
|
+
* behavior-comparison report as an allowed difference.
|
|
138
|
+
*
|
|
139
|
+
* @module search/http/engine
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/** Response byte cap at the search call sites (the fetch layer allows 5 MiB). */
|
|
143
|
+
declare const SEARCH_RESPONSE_BYTES: number;
|
|
144
|
+
/** Default budget for one whole search, across all its candidate URLs. */
|
|
145
|
+
declare const DEFAULT_SEARCH_TIMEOUT_MS = 15000;
|
|
146
|
+
/** `maxResults` clamp: [1, 10], defaulting to 5. */
|
|
147
|
+
declare const DEFAULT_MAX_RESULTS = 5;
|
|
148
|
+
declare const MAX_RESULTS = 10;
|
|
149
|
+
/**
|
|
150
|
+
* Clamp a caller's `maxResults` into the engine-supported range.
|
|
151
|
+
*
|
|
152
|
+
* `NaN` passes through to `slice(0, NaN)` and yields `[]`, exactly as the
|
|
153
|
+
* baseline does. Left at parity deliberately: normalizing pathological option
|
|
154
|
+
* values belongs to the runtime that owns option handling (阶段3), not to two
|
|
155
|
+
* providers that would then disagree with every other one.
|
|
156
|
+
*/
|
|
157
|
+
declare function clampMaxResults(maxResults: number | undefined): number;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The browser navigation header profile the search transport sends.
|
|
161
|
+
*
|
|
162
|
+
* This is the profile `packages/core/test-fixtures/http-search/` was captured
|
|
163
|
+
* with, so changing it invalidates the fixtures as live-behavior evidence.
|
|
164
|
+
*
|
|
165
|
+
* Ported verbatim from Elftia's `webFetchHttp.browserHeaders()` — a verified
|
|
166
|
+
* config; do not deviate from it. The Chromium version is read from the
|
|
167
|
+
* RUNTIME, exactly as Elftia does: `process.versions.chrome` inside an
|
|
168
|
+
* Electron host (a real, complete build number), and Elftia's own fixed
|
|
169
|
+
* fallback when no Chromium runtime reports one (plain Node). Never pin or
|
|
170
|
+
* invent a version string here — a fabricated build number is a bot
|
|
171
|
+
* fingerprint no real browser ever sends.
|
|
172
|
+
*
|
|
173
|
+
* @module search/http/headers
|
|
174
|
+
*/
|
|
175
|
+
/** The exact header set sent with every search request. */
|
|
176
|
+
declare const SEARCH_BROWSER_HEADERS: Readonly<Record<string, string>>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* `http-bing` — keyless Bing search over the shared HTTP transport.
|
|
180
|
+
*
|
|
181
|
+
* Standalone by construction: `new HttpBingProvider()` works with no config, no
|
|
182
|
+
* registry and no host. Nothing in Phase 1 阶段2 registers or calls it — that is
|
|
183
|
+
* 阶段3's job.
|
|
184
|
+
*
|
|
185
|
+
* @module search/http/HttpBingProvider
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
/** The id this provider registers under. */
|
|
189
|
+
declare const HTTP_BING_PROVIDER_ID = "http-bing";
|
|
190
|
+
declare class HttpBingProvider implements SearchProvider {
|
|
191
|
+
readonly id: SearchProviderId;
|
|
192
|
+
private readonly transport;
|
|
193
|
+
constructor(transport?: SearchHttpTransport);
|
|
194
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* `http-duckduckgo` — keyless DuckDuckGo search over the shared HTTP transport.
|
|
199
|
+
*
|
|
200
|
+
* Two candidate endpoints, tried in order under one shared deadline: the html
|
|
201
|
+
* endpoint first, then the lite endpoint. The lite endpoint is reached only when
|
|
202
|
+
* the html one produced no usable results (or failed outright) — the baselined
|
|
203
|
+
* behavior.
|
|
204
|
+
*
|
|
205
|
+
* @module search/http/HttpDuckDuckGoProvider
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
/** The id this provider registers under. */
|
|
209
|
+
declare const HTTP_DUCKDUCKGO_PROVIDER_ID = "http-duckduckgo";
|
|
210
|
+
declare class HttpDuckDuckGoProvider implements SearchProvider {
|
|
211
|
+
readonly id: SearchProviderId;
|
|
212
|
+
private readonly transport;
|
|
213
|
+
constructor(transport?: SearchHttpTransport);
|
|
214
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The browser-impersonating fetch primitive for the keyless HTTP slice.
|
|
219
|
+
*
|
|
220
|
+
* Ported from Elftia's `nodeWebFetchTransport` (the impit half): impit supplies
|
|
221
|
+
* a current-Chrome TLS/HTTP2 fingerprint, which is the difference between a
|
|
222
|
+
* real SERP and a bot-decoy page on engines that fingerprint the client
|
|
223
|
+
* handshake. This is a verified config — `browser`, `followRedirects` and
|
|
224
|
+
* `vanillaFallback` mirror Elftia exactly; do not deviate.
|
|
225
|
+
*
|
|
226
|
+
* Fixed semantics:
|
|
227
|
+
* - `followRedirects: false`, so the transport's manual, egress-validated
|
|
228
|
+
* redirect walk keeps sole ownership of hop decisions.
|
|
229
|
+
* - `vanillaFallback: false`: if the platform binary is unavailable we degrade
|
|
230
|
+
* to the bounded undici path IN THIS MODULE'S CALLER — never silently to a
|
|
231
|
+
* vanilla (non-impersonating) impit mode that would look like a browser and
|
|
232
|
+
* handshake like Node.
|
|
233
|
+
* - Request-specific `user-agent`/`sec-ch-ua*` headers are REMOVED, so the
|
|
234
|
+
* headers stay consistent with the impersonated fingerprint (impit sends its
|
|
235
|
+
* own matching set).
|
|
236
|
+
* - One client per proxy URL, cached; if `impit` cannot be imported at all the
|
|
237
|
+
* answer is `undefined` and the caller uses the undici fallback.
|
|
238
|
+
*
|
|
239
|
+
* @module search/http/impit
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
/** Structural constructor type — resilient to impit being absent at runtime. */
|
|
243
|
+
type ImpitConstructor = new (options?: ImpitOptions) => Impit;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The shared HTTP transport both keyless search providers run on.
|
|
247
|
+
*
|
|
248
|
+
* Ported from Elftia's `webFetchHttp.fetchWebResource` + `nodeWebFetchTransport`
|
|
249
|
+
* (baselined in `docs/design/search-baseline/elftia-search-baseline.md` §3.3),
|
|
250
|
+
* with the failure channel replaced: instead of bare `Error` strings, every
|
|
251
|
+
* failure is a `SearchProviderError` carrying `details.transport` and
|
|
252
|
+
* `details.stage`.
|
|
253
|
+
*
|
|
254
|
+
* Fixed semantics:
|
|
255
|
+
* - impit first (Elftia's verified browser-impersonating client — a real
|
|
256
|
+
* Chrome TLS/HTTP2 fingerprint; `followRedirects: false` keeps this
|
|
257
|
+
* transport's manual, egress-validated walk in charge), with the layered
|
|
258
|
+
* `resolveProxyConfig` as its proxy source. When the impit platform binary
|
|
259
|
+
* is unavailable, the bounded undici path serves instead, with a per-request
|
|
260
|
+
* proxy dispatcher: the layered `resolveProxyDispatcher` override first (the
|
|
261
|
+
* daemon's `fetchUpstream` resolver), else the `EnvHttpProxyAgent` the
|
|
262
|
+
* environment configures (cached per proxy signature).
|
|
263
|
+
* - The pinned browser navigation header profile (see `./headers`).
|
|
264
|
+
* - `redirect: 'manual'`, at most {@link MAX_REDIRECTS} hops, with the initial
|
|
265
|
+
* URL and every hop target checked against the search egress policy
|
|
266
|
+
* (`../egress`). These two engines are fixed-host and keyless, so nothing an
|
|
267
|
+
* attacker controls picks the URL — but a redirect target is upstream input,
|
|
268
|
+
* and this is the seam 阶段2 identified and 阶段4 fills. The default policy
|
|
269
|
+
* permits every public host, so the fixed engines and the live `cn.bing.com`
|
|
270
|
+
* geo-redirect are unaffected.
|
|
271
|
+
* - A byte cap enforced WHILE the body streams — an oversized page fails, it is
|
|
272
|
+
* never silently truncated (this diverges from Elftia, which truncates and
|
|
273
|
+
* parses the fragment; the spec requires a `body-cap` failure).
|
|
274
|
+
* - One deadline per attempt; caller aborts surface as `cancelled`, the deadline
|
|
275
|
+
* as `timeout`, never conflated.
|
|
276
|
+
* - Compression is undici's business: it negotiates `Accept-Encoding` and
|
|
277
|
+
* decodes the response.
|
|
278
|
+
*
|
|
279
|
+
* `impit` is NOT used. undici is the sole Phase-1 transport; the injection seam
|
|
280
|
+
* below is the only future-proofing.
|
|
281
|
+
*
|
|
282
|
+
* @module search/http/transport
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
/** Redirect hop cap. */
|
|
286
|
+
declare const MAX_REDIRECTS = 5;
|
|
287
|
+
/** Knobs for {@link createSearchHttpTransport}. */
|
|
288
|
+
interface SearchHttpTransportOptions {
|
|
289
|
+
/** The fetch primitive to drive. Defaults to undici's `fetch`. */
|
|
290
|
+
fetch?: SearchHttpFetch;
|
|
291
|
+
/** Environment to read proxy variables from. Defaults to `process.env`. */
|
|
292
|
+
env?: ProxyEnvironment;
|
|
293
|
+
/**
|
|
294
|
+
* Layered proxy-dispatcher override for one request URL, AHEAD of the env
|
|
295
|
+
* layer. The daemon passes `fetchUpstream`'s resolver here so search egress
|
|
296
|
+
* follows the same `server.proxy` stack (global config, socks5 included) as
|
|
297
|
+
* LLM upstream traffic; a `NO_PROXY`/loopback target yields `undefined`
|
|
298
|
+
* inside that resolver and falls through to the env layer unchanged.
|
|
299
|
+
*/
|
|
300
|
+
resolveProxyDispatcher?: (url: string) => Dispatcher | undefined;
|
|
301
|
+
/**
|
|
302
|
+
* Layered proxy CONFIG for one request URL — the impit path's proxy source,
|
|
303
|
+
* because impit takes a proxy URL, not an undici dispatcher. The daemon
|
|
304
|
+
* passes its `fetchUpstream`-layered resolver, so `server.proxy` (socks5
|
|
305
|
+
* included) governs impersonated requests exactly like LLM egress.
|
|
306
|
+
*/
|
|
307
|
+
resolveProxyConfig?: (url: string) => ProxyConfig | undefined;
|
|
308
|
+
/** Test seam for the impit constructor loader. Omit for the real import. */
|
|
309
|
+
loadImpit?: () => Promise<ImpitConstructor | undefined>;
|
|
310
|
+
/** Redirect hop cap. Defaults to {@link MAX_REDIRECTS}. */
|
|
311
|
+
maxRedirects?: number;
|
|
312
|
+
/**
|
|
313
|
+
* Egress policy for the initial URL and every redirect hop. Defaults to
|
|
314
|
+
* public-only, which is all these fixed-host engines ever need.
|
|
315
|
+
*/
|
|
316
|
+
egressPolicy?: SearchEgressPolicy;
|
|
317
|
+
}
|
|
318
|
+
/** Build a transport. Tests inject `fetch`; production takes the default. */
|
|
319
|
+
declare function createSearchHttpTransport(options?: SearchHttpTransportOptions): SearchHttpTransport;
|
|
320
|
+
/** The production transport: undici, reading proxy settings from `process.env`. */
|
|
321
|
+
declare const defaultSearchHttpTransport: SearchHttpTransport;
|
|
322
|
+
|
|
323
|
+
export { DEFAULT_MAX_RESULTS, DEFAULT_SEARCH_TIMEOUT_MS, HTTP_BING_PROVIDER_ID, HTTP_DUCKDUCKGO_PROVIDER_ID, HTTP_SEARCH_CAPABILITIES, HttpBingProvider, HttpDuckDuckGoProvider, MAX_REDIRECTS, MAX_RESULTS, type ParsedSerp, SEARCH_BROWSER_HEADERS, SEARCH_HTTP_TRANSPORT_ID, SEARCH_RESPONSE_BYTES, type SearchHttpFetch, type SearchHttpRequest, type SearchHttpResource, type SearchHttpStage, type SearchHttpTransport, type SearchHttpTransportOptions, builtinHttpSearchContributions, clampMaxResults, createSearchHttpTransport, defaultSearchHttpTransport };
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { SearchProviderId, SearchResult, SearchProviderCapabilities, SearchProviderContribution, SearchProvider, SearchOptions } from '@omnicross/contracts/search-types';
|
|
2
|
+
import { ProxyConfig } from '@omnicross/contracts/account-tokens-types';
|
|
3
|
+
import { Dispatcher } from 'undici';
|
|
4
|
+
import { S as SearchEgressPolicy } from '../egress-I46O_p5L.js';
|
|
5
|
+
import { ImpitOptions, Impit } from 'impit';
|
|
6
|
+
import { P as ProxyEnvironment } from '../proxy-UZTtjPZx.js';
|
|
7
|
+
import 'node:dns';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shared vocabulary for the keyless HTTP search slice (plan 阶段2).
|
|
11
|
+
*
|
|
12
|
+
* Everything here is transport- and parser-neutral so the providers, the
|
|
13
|
+
* shared transport, and the doctor can agree on one set of names without
|
|
14
|
+
* importing each other.
|
|
15
|
+
*
|
|
16
|
+
* @module search/http/types
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The transport identity stamped on every failure this slice throws.
|
|
21
|
+
*
|
|
22
|
+
* Phase 1 ships exactly one transport. `impit` (Elftia's browser-fingerprint
|
|
23
|
+
* client) is deliberately NOT introduced here — the injection seam below is the
|
|
24
|
+
* whole extent of future-proofing, so an alternative transport can never become
|
|
25
|
+
* an implicit behavior change.
|
|
26
|
+
*/
|
|
27
|
+
declare const SEARCH_HTTP_TRANSPORT_ID = "undici";
|
|
28
|
+
/**
|
|
29
|
+
* Which phase of a search failed (plan §11.4 — transport identity and failure
|
|
30
|
+
* stage must be observable).
|
|
31
|
+
*
|
|
32
|
+
* - `connect` — the request never produced a response (DNS, TCP, TLS).
|
|
33
|
+
* - `fetch` — a response arrived but is unusable (refused status, deadline).
|
|
34
|
+
* - `redirect` — the redirect chain exceeded its cap.
|
|
35
|
+
* - `body-cap` — the response body outgrew its byte cap mid-stream.
|
|
36
|
+
* - `challenge` — the engine served a bot challenge instead of a SERP.
|
|
37
|
+
* - `trust` — the page parsed, but the anti-decoy check refused it.
|
|
38
|
+
* - `parse` — the page is not recognizable as this engine's SERP.
|
|
39
|
+
*/
|
|
40
|
+
type SearchHttpStage = 'connect' | 'fetch' | 'redirect' | 'body-cap' | 'challenge' | 'trust' | 'parse';
|
|
41
|
+
/** One fetched search page. */
|
|
42
|
+
interface SearchHttpResource {
|
|
43
|
+
/** The URL the response was finally served from (after redirects). */
|
|
44
|
+
finalUrl: string;
|
|
45
|
+
/** HTTP status of the final response. */
|
|
46
|
+
status: number;
|
|
47
|
+
/** `content-type` header value, or `''`. */
|
|
48
|
+
contentType: string;
|
|
49
|
+
/** Decoded response body. */
|
|
50
|
+
rawText: string;
|
|
51
|
+
}
|
|
52
|
+
/** What the transport needs to execute one attempt. */
|
|
53
|
+
interface SearchHttpRequest {
|
|
54
|
+
/** Hard budget for this single attempt, in milliseconds. */
|
|
55
|
+
timeoutMs: number;
|
|
56
|
+
/** Byte cap enforced while the body streams in. */
|
|
57
|
+
maxResponseBytes: number;
|
|
58
|
+
/** Caller cancellation. Aborting surfaces as code `cancelled`, never `timeout`. */
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
/** Stamped onto thrown errors so a failure names its provider. */
|
|
61
|
+
providerId?: SearchProviderId;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The seam both providers accept in their constructor.
|
|
65
|
+
*
|
|
66
|
+
* This is the test seam (fixtures are served through it) and the seam a future
|
|
67
|
+
* alternative transport would occupy.
|
|
68
|
+
*/
|
|
69
|
+
type SearchHttpTransport = (url: string, request: SearchHttpRequest) => Promise<SearchHttpResource>;
|
|
70
|
+
/**
|
|
71
|
+
* The low-level fetch primitive the shared transport drives — undici's `fetch`
|
|
72
|
+
* by default. Separate from {@link SearchHttpTransport} so redirect, body-cap
|
|
73
|
+
* and header behavior can be unit-tested without a network.
|
|
74
|
+
*/
|
|
75
|
+
type SearchHttpFetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
76
|
+
/** The outcome of running one engine's parser over one page. */
|
|
77
|
+
interface ParsedSerp {
|
|
78
|
+
/**
|
|
79
|
+
* Whether the page is structurally recognizable as this engine's SERP.
|
|
80
|
+
*
|
|
81
|
+
* This is what separates "the engine found nothing" (recognized, zero
|
|
82
|
+
* results → `[]`) from "the engine's markup changed under us" (unrecognized
|
|
83
|
+
* → `parse_failed`).
|
|
84
|
+
*/
|
|
85
|
+
recognized: boolean;
|
|
86
|
+
/** Usable organic results, already clamped to the caller's `maxResults`. */
|
|
87
|
+
results: SearchResult[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The builtin HTTP search contributions.
|
|
92
|
+
*
|
|
93
|
+
* Everything the registry must not infer is declared here: `source` and `kind`
|
|
94
|
+
* are stated, never derived from the id's spelling, and the capability set is
|
|
95
|
+
* explicit down to the options these engines do NOT honor.
|
|
96
|
+
*
|
|
97
|
+
* `priorityHint` is deliberately absent. Provider ordering is the orchestrator's
|
|
98
|
+
* decision (阶段3); encoding a preference here would recreate exactly the
|
|
99
|
+
* scattered-fallback-order problem the extraction exists to remove.
|
|
100
|
+
*
|
|
101
|
+
* @module search/http/contributions
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* What both keyless HTTP engines can do.
|
|
106
|
+
*
|
|
107
|
+
* `supportsRegion` / `supportsLanguage` / `supportsTimeRange` are false because
|
|
108
|
+
* the scraped endpoints never honored those options; per the contract, a
|
|
109
|
+
* provider ignores an option it does not declare rather than failing.
|
|
110
|
+
*/
|
|
111
|
+
declare const HTTP_SEARCH_CAPABILITIES: Readonly<SearchProviderCapabilities>;
|
|
112
|
+
/**
|
|
113
|
+
* Both builtin HTTP providers, ready to hand to a registry.
|
|
114
|
+
*
|
|
115
|
+
* @param transport - optional shared transport for both providers (tests, or a
|
|
116
|
+
* future alternative client). Omit for the production undici transport.
|
|
117
|
+
*/
|
|
118
|
+
declare function builtinHttpSearchContributions(transport?: SearchHttpTransport): SearchProviderContribution[];
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The candidate-URL walk both HTTP providers share.
|
|
122
|
+
*
|
|
123
|
+
* Mirrors the baselined `searchWebViaFetch` loop (one shared deadline, split
|
|
124
|
+
* evenly across the remaining candidates, last error retained) with one
|
|
125
|
+
* plan-mandated behavior change: the three-way outcome distinction.
|
|
126
|
+
*
|
|
127
|
+
* Elftia collapses "the engine found nothing" and "the engine's markup changed"
|
|
128
|
+
* into a single `response contained no result entries` error. Here they are
|
|
129
|
+
* different answers:
|
|
130
|
+
*
|
|
131
|
+
* - a recognized SERP with zero organic results → `[]` (a legitimate answer);
|
|
132
|
+
* - an unrecognizable page → `parse_failed` (the parser-drift alarm);
|
|
133
|
+
* - a challenge or decoy page → a `challenge`/`trust` staged failure, checked
|
|
134
|
+
* before either of the above.
|
|
135
|
+
*
|
|
136
|
+
* The divergence is deliberate (plan §11.4) and is recorded for 阶段5's
|
|
137
|
+
* behavior-comparison report as an allowed difference.
|
|
138
|
+
*
|
|
139
|
+
* @module search/http/engine
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/** Response byte cap at the search call sites (the fetch layer allows 5 MiB). */
|
|
143
|
+
declare const SEARCH_RESPONSE_BYTES: number;
|
|
144
|
+
/** Default budget for one whole search, across all its candidate URLs. */
|
|
145
|
+
declare const DEFAULT_SEARCH_TIMEOUT_MS = 15000;
|
|
146
|
+
/** `maxResults` clamp: [1, 10], defaulting to 5. */
|
|
147
|
+
declare const DEFAULT_MAX_RESULTS = 5;
|
|
148
|
+
declare const MAX_RESULTS = 10;
|
|
149
|
+
/**
|
|
150
|
+
* Clamp a caller's `maxResults` into the engine-supported range.
|
|
151
|
+
*
|
|
152
|
+
* `NaN` passes through to `slice(0, NaN)` and yields `[]`, exactly as the
|
|
153
|
+
* baseline does. Left at parity deliberately: normalizing pathological option
|
|
154
|
+
* values belongs to the runtime that owns option handling (阶段3), not to two
|
|
155
|
+
* providers that would then disagree with every other one.
|
|
156
|
+
*/
|
|
157
|
+
declare function clampMaxResults(maxResults: number | undefined): number;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The browser navigation header profile the search transport sends.
|
|
161
|
+
*
|
|
162
|
+
* This is the profile `packages/core/test-fixtures/http-search/` was captured
|
|
163
|
+
* with, so changing it invalidates the fixtures as live-behavior evidence.
|
|
164
|
+
*
|
|
165
|
+
* Ported verbatim from Elftia's `webFetchHttp.browserHeaders()` — a verified
|
|
166
|
+
* config; do not deviate from it. The Chromium version is read from the
|
|
167
|
+
* RUNTIME, exactly as Elftia does: `process.versions.chrome` inside an
|
|
168
|
+
* Electron host (a real, complete build number), and Elftia's own fixed
|
|
169
|
+
* fallback when no Chromium runtime reports one (plain Node). Never pin or
|
|
170
|
+
* invent a version string here — a fabricated build number is a bot
|
|
171
|
+
* fingerprint no real browser ever sends.
|
|
172
|
+
*
|
|
173
|
+
* @module search/http/headers
|
|
174
|
+
*/
|
|
175
|
+
/** The exact header set sent with every search request. */
|
|
176
|
+
declare const SEARCH_BROWSER_HEADERS: Readonly<Record<string, string>>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* `http-bing` — keyless Bing search over the shared HTTP transport.
|
|
180
|
+
*
|
|
181
|
+
* Standalone by construction: `new HttpBingProvider()` works with no config, no
|
|
182
|
+
* registry and no host. Nothing in Phase 1 阶段2 registers or calls it — that is
|
|
183
|
+
* 阶段3's job.
|
|
184
|
+
*
|
|
185
|
+
* @module search/http/HttpBingProvider
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
/** The id this provider registers under. */
|
|
189
|
+
declare const HTTP_BING_PROVIDER_ID = "http-bing";
|
|
190
|
+
declare class HttpBingProvider implements SearchProvider {
|
|
191
|
+
readonly id: SearchProviderId;
|
|
192
|
+
private readonly transport;
|
|
193
|
+
constructor(transport?: SearchHttpTransport);
|
|
194
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* `http-duckduckgo` — keyless DuckDuckGo search over the shared HTTP transport.
|
|
199
|
+
*
|
|
200
|
+
* Two candidate endpoints, tried in order under one shared deadline: the html
|
|
201
|
+
* endpoint first, then the lite endpoint. The lite endpoint is reached only when
|
|
202
|
+
* the html one produced no usable results (or failed outright) — the baselined
|
|
203
|
+
* behavior.
|
|
204
|
+
*
|
|
205
|
+
* @module search/http/HttpDuckDuckGoProvider
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
/** The id this provider registers under. */
|
|
209
|
+
declare const HTTP_DUCKDUCKGO_PROVIDER_ID = "http-duckduckgo";
|
|
210
|
+
declare class HttpDuckDuckGoProvider implements SearchProvider {
|
|
211
|
+
readonly id: SearchProviderId;
|
|
212
|
+
private readonly transport;
|
|
213
|
+
constructor(transport?: SearchHttpTransport);
|
|
214
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The browser-impersonating fetch primitive for the keyless HTTP slice.
|
|
219
|
+
*
|
|
220
|
+
* Ported from Elftia's `nodeWebFetchTransport` (the impit half): impit supplies
|
|
221
|
+
* a current-Chrome TLS/HTTP2 fingerprint, which is the difference between a
|
|
222
|
+
* real SERP and a bot-decoy page on engines that fingerprint the client
|
|
223
|
+
* handshake. This is a verified config — `browser`, `followRedirects` and
|
|
224
|
+
* `vanillaFallback` mirror Elftia exactly; do not deviate.
|
|
225
|
+
*
|
|
226
|
+
* Fixed semantics:
|
|
227
|
+
* - `followRedirects: false`, so the transport's manual, egress-validated
|
|
228
|
+
* redirect walk keeps sole ownership of hop decisions.
|
|
229
|
+
* - `vanillaFallback: false`: if the platform binary is unavailable we degrade
|
|
230
|
+
* to the bounded undici path IN THIS MODULE'S CALLER — never silently to a
|
|
231
|
+
* vanilla (non-impersonating) impit mode that would look like a browser and
|
|
232
|
+
* handshake like Node.
|
|
233
|
+
* - Request-specific `user-agent`/`sec-ch-ua*` headers are REMOVED, so the
|
|
234
|
+
* headers stay consistent with the impersonated fingerprint (impit sends its
|
|
235
|
+
* own matching set).
|
|
236
|
+
* - One client per proxy URL, cached; if `impit` cannot be imported at all the
|
|
237
|
+
* answer is `undefined` and the caller uses the undici fallback.
|
|
238
|
+
*
|
|
239
|
+
* @module search/http/impit
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
/** Structural constructor type — resilient to impit being absent at runtime. */
|
|
243
|
+
type ImpitConstructor = new (options?: ImpitOptions) => Impit;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The shared HTTP transport both keyless search providers run on.
|
|
247
|
+
*
|
|
248
|
+
* Ported from Elftia's `webFetchHttp.fetchWebResource` + `nodeWebFetchTransport`
|
|
249
|
+
* (baselined in `docs/design/search-baseline/elftia-search-baseline.md` §3.3),
|
|
250
|
+
* with the failure channel replaced: instead of bare `Error` strings, every
|
|
251
|
+
* failure is a `SearchProviderError` carrying `details.transport` and
|
|
252
|
+
* `details.stage`.
|
|
253
|
+
*
|
|
254
|
+
* Fixed semantics:
|
|
255
|
+
* - impit first (Elftia's verified browser-impersonating client — a real
|
|
256
|
+
* Chrome TLS/HTTP2 fingerprint; `followRedirects: false` keeps this
|
|
257
|
+
* transport's manual, egress-validated walk in charge), with the layered
|
|
258
|
+
* `resolveProxyConfig` as its proxy source. When the impit platform binary
|
|
259
|
+
* is unavailable, the bounded undici path serves instead, with a per-request
|
|
260
|
+
* proxy dispatcher: the layered `resolveProxyDispatcher` override first (the
|
|
261
|
+
* daemon's `fetchUpstream` resolver), else the `EnvHttpProxyAgent` the
|
|
262
|
+
* environment configures (cached per proxy signature).
|
|
263
|
+
* - The pinned browser navigation header profile (see `./headers`).
|
|
264
|
+
* - `redirect: 'manual'`, at most {@link MAX_REDIRECTS} hops, with the initial
|
|
265
|
+
* URL and every hop target checked against the search egress policy
|
|
266
|
+
* (`../egress`). These two engines are fixed-host and keyless, so nothing an
|
|
267
|
+
* attacker controls picks the URL — but a redirect target is upstream input,
|
|
268
|
+
* and this is the seam 阶段2 identified and 阶段4 fills. The default policy
|
|
269
|
+
* permits every public host, so the fixed engines and the live `cn.bing.com`
|
|
270
|
+
* geo-redirect are unaffected.
|
|
271
|
+
* - A byte cap enforced WHILE the body streams — an oversized page fails, it is
|
|
272
|
+
* never silently truncated (this diverges from Elftia, which truncates and
|
|
273
|
+
* parses the fragment; the spec requires a `body-cap` failure).
|
|
274
|
+
* - One deadline per attempt; caller aborts surface as `cancelled`, the deadline
|
|
275
|
+
* as `timeout`, never conflated.
|
|
276
|
+
* - Compression is undici's business: it negotiates `Accept-Encoding` and
|
|
277
|
+
* decodes the response.
|
|
278
|
+
*
|
|
279
|
+
* `impit` is NOT used. undici is the sole Phase-1 transport; the injection seam
|
|
280
|
+
* below is the only future-proofing.
|
|
281
|
+
*
|
|
282
|
+
* @module search/http/transport
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
/** Redirect hop cap. */
|
|
286
|
+
declare const MAX_REDIRECTS = 5;
|
|
287
|
+
/** Knobs for {@link createSearchHttpTransport}. */
|
|
288
|
+
interface SearchHttpTransportOptions {
|
|
289
|
+
/** The fetch primitive to drive. Defaults to undici's `fetch`. */
|
|
290
|
+
fetch?: SearchHttpFetch;
|
|
291
|
+
/** Environment to read proxy variables from. Defaults to `process.env`. */
|
|
292
|
+
env?: ProxyEnvironment;
|
|
293
|
+
/**
|
|
294
|
+
* Layered proxy-dispatcher override for one request URL, AHEAD of the env
|
|
295
|
+
* layer. The daemon passes `fetchUpstream`'s resolver here so search egress
|
|
296
|
+
* follows the same `server.proxy` stack (global config, socks5 included) as
|
|
297
|
+
* LLM upstream traffic; a `NO_PROXY`/loopback target yields `undefined`
|
|
298
|
+
* inside that resolver and falls through to the env layer unchanged.
|
|
299
|
+
*/
|
|
300
|
+
resolveProxyDispatcher?: (url: string) => Dispatcher | undefined;
|
|
301
|
+
/**
|
|
302
|
+
* Layered proxy CONFIG for one request URL — the impit path's proxy source,
|
|
303
|
+
* because impit takes a proxy URL, not an undici dispatcher. The daemon
|
|
304
|
+
* passes its `fetchUpstream`-layered resolver, so `server.proxy` (socks5
|
|
305
|
+
* included) governs impersonated requests exactly like LLM egress.
|
|
306
|
+
*/
|
|
307
|
+
resolveProxyConfig?: (url: string) => ProxyConfig | undefined;
|
|
308
|
+
/** Test seam for the impit constructor loader. Omit for the real import. */
|
|
309
|
+
loadImpit?: () => Promise<ImpitConstructor | undefined>;
|
|
310
|
+
/** Redirect hop cap. Defaults to {@link MAX_REDIRECTS}. */
|
|
311
|
+
maxRedirects?: number;
|
|
312
|
+
/**
|
|
313
|
+
* Egress policy for the initial URL and every redirect hop. Defaults to
|
|
314
|
+
* public-only, which is all these fixed-host engines ever need.
|
|
315
|
+
*/
|
|
316
|
+
egressPolicy?: SearchEgressPolicy;
|
|
317
|
+
}
|
|
318
|
+
/** Build a transport. Tests inject `fetch`; production takes the default. */
|
|
319
|
+
declare function createSearchHttpTransport(options?: SearchHttpTransportOptions): SearchHttpTransport;
|
|
320
|
+
/** The production transport: undici, reading proxy settings from `process.env`. */
|
|
321
|
+
declare const defaultSearchHttpTransport: SearchHttpTransport;
|
|
322
|
+
|
|
323
|
+
export { DEFAULT_MAX_RESULTS, DEFAULT_SEARCH_TIMEOUT_MS, HTTP_BING_PROVIDER_ID, HTTP_DUCKDUCKGO_PROVIDER_ID, HTTP_SEARCH_CAPABILITIES, HttpBingProvider, HttpDuckDuckGoProvider, MAX_REDIRECTS, MAX_RESULTS, type ParsedSerp, SEARCH_BROWSER_HEADERS, SEARCH_HTTP_TRANSPORT_ID, SEARCH_RESPONSE_BYTES, type SearchHttpFetch, type SearchHttpRequest, type SearchHttpResource, type SearchHttpStage, type SearchHttpTransport, type SearchHttpTransportOptions, builtinHttpSearchContributions, clampMaxResults, createSearchHttpTransport, defaultSearchHttpTransport };
|