@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.
Files changed (119) hide show
  1. package/dist/{BuiltinToolExecutor-CS2WpXhM.d.cts → BuiltinToolExecutor-BHgH-EaZ.d.cts} +25 -2
  2. package/dist/{BuiltinToolExecutor-BluWyeob.d.ts → BuiltinToolExecutor-Bi1LmiTP.d.ts} +25 -2
  3. package/dist/{CompletionService-BiJqftc-.d.cts → CompletionService-BniP7j88.d.cts} +2 -2
  4. package/dist/{CompletionService-NXN_Av5W.d.ts → CompletionService-BqrKPwfA.d.ts} +2 -2
  5. package/dist/{ProviderProxy-CgIcCVwk.d.cts → ProviderProxy-Bz_nkOwq.d.cts} +1 -1
  6. package/dist/{ProviderProxy-DYoY5XoT.d.ts → ProviderProxy-Ckr6oTrE.d.ts} +1 -1
  7. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +2 -2
  8. package/dist/auth/GeminiCodeAssistProjectResolver.js +1 -1
  9. package/dist/{chunk-FJNKXOWE.js → chunk-2C3TTBS7.js} +4 -4
  10. package/dist/{chunk-NZYN7C3P.cjs → chunk-2RCCLQYV.cjs} +53 -53
  11. package/dist/{chunk-CDHS2QDC.js → chunk-4PFWFODB.js} +53 -53
  12. package/dist/{chunk-OST5RJOJ.cjs → chunk-4Z3ZLLC2.cjs} +13 -7
  13. package/dist/chunk-7RJI3NDI.cjs +236 -0
  14. package/dist/chunk-BTBEJRGL.cjs +781 -0
  15. package/dist/chunk-DBBR6RHF.js +236 -0
  16. package/dist/chunk-DXWFBBSK.js +781 -0
  17. package/dist/chunk-E4QHVXV5.cjs +118 -0
  18. package/dist/chunk-L7O2YQ5O.js +386 -0
  19. package/dist/{chunk-WSJE42SG.js → chunk-LKDAN4D7.js} +3684 -2462
  20. package/dist/{chunk-DEIUR2TH.js → chunk-LQYS2N47.js} +12 -8
  21. package/dist/chunk-MZEGQSKQ.js +118 -0
  22. package/dist/chunk-PTU7SXM3.cjs +386 -0
  23. package/dist/{chunk-CTKM773J.cjs → chunk-UPQIWJIE.cjs} +12 -8
  24. package/dist/{chunk-CFSGYFOZ.js → chunk-VL5TUDTM.js} +7 -1
  25. package/dist/{chunk-7B56AJBB.cjs → chunk-VSYQ6XYN.cjs} +2233 -1011
  26. package/dist/{chunk-TVYA5JSQ.cjs → chunk-ZAPN7XA6.cjs} +5 -5
  27. package/dist/completion/BuiltinToolExecutor.cjs +2 -2
  28. package/dist/completion/BuiltinToolExecutor.d.cts +3 -1
  29. package/dist/completion/BuiltinToolExecutor.d.ts +3 -1
  30. package/dist/completion/BuiltinToolExecutor.js +1 -1
  31. package/dist/completion/CompletionService.cjs +11 -10
  32. package/dist/completion/CompletionService.d.cts +6 -3
  33. package/dist/completion/CompletionService.d.ts +6 -3
  34. package/dist/completion/CompletionService.js +10 -9
  35. package/dist/completion.cjs +11 -10
  36. package/dist/completion.d.cts +6 -3
  37. package/dist/completion.d.ts +6 -3
  38. package/dist/completion.js +10 -9
  39. package/dist/egress-I46O_p5L.d.cts +126 -0
  40. package/dist/egress-I46O_p5L.d.ts +126 -0
  41. package/dist/frontends-Doz0RaWu.d.cts +97 -0
  42. package/dist/frontends-Doz0RaWu.d.ts +97 -0
  43. package/dist/image-generation.cjs +2 -2
  44. package/dist/image-generation.d.cts +7 -4
  45. package/dist/image-generation.d.ts +7 -4
  46. package/dist/image-generation.js +1 -1
  47. package/dist/index.cjs +25 -10
  48. package/dist/index.d.cts +12 -8
  49. package/dist/index.d.ts +12 -8
  50. package/dist/index.js +24 -9
  51. package/dist/openai-operation.d.cts +4 -1
  52. package/dist/openai-operation.d.ts +4 -1
  53. package/dist/outbound-api/routeResolver.d.cts +9 -5
  54. package/dist/outbound-api/routeResolver.d.ts +9 -5
  55. package/dist/outbound-api/subscriptionRegistryPort.d.cts +4 -1
  56. package/dist/outbound-api/subscriptionRegistryPort.d.ts +4 -1
  57. package/dist/outbound-api/types.d.cts +8 -4
  58. package/dist/outbound-api/types.d.ts +8 -4
  59. package/dist/outbound-api.cjs +25 -10
  60. package/dist/outbound-api.d.cts +117 -8
  61. package/dist/outbound-api.d.ts +117 -8
  62. package/dist/outbound-api.js +24 -9
  63. package/dist/pipeline/AccountAllowanceScheduling.d.cts +8 -4
  64. package/dist/pipeline/AccountAllowanceScheduling.d.ts +8 -4
  65. package/dist/pipeline/upstreamFetch.cjs +2 -2
  66. package/dist/pipeline/upstreamFetch.js +1 -1
  67. package/dist/ports.d.cts +8 -4
  68. package/dist/ports.d.ts +8 -4
  69. package/dist/provider-proxy/ProviderProxy.cjs +11 -10
  70. package/dist/provider-proxy/ProviderProxy.d.cts +5 -2
  71. package/dist/provider-proxy/ProviderProxy.d.ts +5 -2
  72. package/dist/provider-proxy/ProviderProxy.js +10 -9
  73. package/dist/provider-proxy/ingress/providerProxyShared.cjs +13 -10
  74. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +22 -3
  75. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +22 -3
  76. package/dist/provider-proxy/ingress/providerProxyShared.js +12 -9
  77. package/dist/provider-proxy/types.d.cts +4 -1
  78. package/dist/provider-proxy/types.d.ts +4 -1
  79. package/dist/provider-proxy.cjs +11 -10
  80. package/dist/provider-proxy.d.cts +10 -6
  81. package/dist/provider-proxy.d.ts +10 -6
  82. package/dist/provider-proxy.js +10 -9
  83. package/dist/proxy-UZTtjPZx.d.cts +18 -0
  84. package/dist/proxy-UZTtjPZx.d.ts +18 -0
  85. package/dist/{routeResolver-rpKQLyDO.d.cts → routeResolver-Da0yvkL1.d.cts} +2 -2
  86. package/dist/{routeResolver-CTCcSHWu.d.ts → routeResolver-STwMz_G4.d.ts} +2 -2
  87. package/dist/runtime-5NCT3J3T.cjs +8 -0
  88. package/dist/runtime-JTHUWCBM.js +8 -0
  89. package/dist/runtime-t8AZacFF.d.cts +233 -0
  90. package/dist/runtime-t8AZacFF.d.ts +233 -0
  91. package/dist/search/api.cjs +766 -0
  92. package/dist/search/api.d.cts +385 -0
  93. package/dist/search/api.d.ts +385 -0
  94. package/dist/search/api.js +766 -0
  95. package/dist/search/http.cjs +37 -0
  96. package/dist/search/http.d.cts +323 -0
  97. package/dist/search/http.d.ts +323 -0
  98. package/dist/search/http.js +37 -0
  99. package/dist/search.cjs +57 -0
  100. package/dist/search.d.cts +113 -0
  101. package/dist/search.d.ts +113 -0
  102. package/dist/search.js +57 -0
  103. package/dist/transformer/transformers/AnthropicTransformer.cjs +3 -3
  104. package/dist/transformer/transformers/AnthropicTransformer.js +2 -2
  105. package/dist/transformer/transformers.cjs +7 -7
  106. package/dist/transformer/transformers.js +6 -6
  107. package/dist/transformer.cjs +6 -6
  108. package/dist/transformer.js +5 -5
  109. package/dist/{types-DsHBBk3h.d.cts → types-BSIJdsyl.d.cts} +15 -0
  110. package/dist/{types-CbglB73l.d.cts → types-Be-yt_6T.d.cts} +1 -1
  111. package/dist/{types-CVRge76p.d.ts → types-By9Tg6-I.d.ts} +15 -0
  112. package/dist/types-CbCZqLGg.d.cts +127 -0
  113. package/dist/types-CbCZqLGg.d.ts +127 -0
  114. package/dist/{types-jjU24eYG.d.cts → types-D-oHYusb.d.cts} +73 -4
  115. package/dist/{types-Bd72H4SB.d.ts → types-Dm4_36V4.d.ts} +1 -1
  116. package/dist/{types-5T223WYQ.d.ts → types-Dvlpjn0d.d.ts} +73 -4
  117. package/package.json +6 -3
  118. package/dist/{chunk-6OC3ORW6.js → chunk-EUZRH544.js} +3 -3
  119. 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 };