@mini-z/dsh-search-providers 0.1.2 → 0.2.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/CHANGELOG.md +16 -0
- package/README.md +175 -104
- package/dsh/index.js +115 -38
- package/dsh/index.js.map +1 -1
- package/package.json +8 -3
- package/search-providers.example.json +28 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- Load provider settings from `~/.dsh/search-providers.json`, or a file selected by the Cordis `configFile` option. Inline provider settings and environment-variable configuration are no longer supported.
|
|
8
|
+
- Configure Codex credentials with `providers.codex.credentialFile`; relative paths resolve from the JSON configuration directory.
|
|
9
|
+
- Use built-in official service endpoints. Remove provider `baseUrl` and TinyFish `options.fetchBaseUrl` from existing configuration.
|
|
10
|
+
|
|
11
|
+
### Improvements
|
|
12
|
+
|
|
13
|
+
- Validate configuration at startup and report errors without echoing credential values.
|
|
14
|
+
- Include `search-providers.example.json` in the published package. See README for migration steps.
|
|
15
|
+
- Increase the per-fallback outer timeout from 20 seconds to 55 seconds for both search and page reading; aggregate tool deadlines are unchanged.
|
|
16
|
+
- Add configuration loading, official endpoint, and timeout regression tests.
|
package/README.md
CHANGED
|
@@ -1,188 +1,259 @@
|
|
|
1
|
-
# dsh-search-providers
|
|
1
|
+
# @mini-z/dsh-search-providers
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@mini-z/dsh-search-providers)
|
|
4
|
+
[](./package.json)
|
|
5
|
+
|
|
6
|
+
A modular [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/dsh) plugin that provides `web_search` and `read_page` through multiple search services with capability-aware fallback and cooldown handling.
|
|
7
|
+
|
|
8
|
+
**Codex is the default `web_search` provider.** If its static credential is unavailable, or a request fails, the plugin proceeds through the configured fallback chain.
|
|
4
9
|
|
|
5
10
|
## Features
|
|
6
11
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
12
|
+
- Replaces the native `web_search` provider through the DSH web seam.
|
|
13
|
+
- Registers `read_page` for clean, structured page extraction.
|
|
14
|
+
- Uses Codex-first search routing by default.
|
|
15
|
+
- Filters fallback candidates by capability, so search-only providers are never used for page fetching.
|
|
16
|
+
- Temporarily cools failing provider capabilities instead of repeatedly calling them.
|
|
17
|
+
- Disables only confirmed authentication failures until DSH restarts.
|
|
18
|
+
- Propagates caller cancellation without cooling providers or continuing fallback.
|
|
19
|
+
- Supports custom active-provider and fallback order configuration.
|
|
20
|
+
- Includes a key-free demo provider for wiring tests.
|
|
21
|
+
|
|
22
|
+
## Provider support and default routing
|
|
23
|
+
|
|
24
|
+
| Provider | `web_search` | `read_page` | Credential |
|
|
25
|
+
|----------|:------------:|:-----------:|------------|
|
|
26
|
+
| Codex | ✅ | ❌ | Static credential JSON referenced by `providers.codex.credentialFile` |
|
|
27
|
+
| TinyFish | ✅ | ✅ | `providers.tinyfish.apiKey` in JSON |
|
|
28
|
+
| Tavily | ✅ | ❌ | `providers.tavily.apiKey` in JSON |
|
|
29
|
+
| Exa | ✅ | ✅ | `providers.exa.apiKey` in JSON |
|
|
30
|
+
| Firecrawl | ✅ | ✅ | `providers.firecrawl.apiKey` in JSON |
|
|
31
|
+
| Demo | ✅ | ✅ | None |
|
|
32
|
+
|
|
33
|
+
Default `web_search` order:
|
|
34
|
+
|
|
35
|
+
1. Codex
|
|
36
|
+
2. TinyFish
|
|
37
|
+
3. Tavily
|
|
38
|
+
4. Exa
|
|
39
|
+
5. Firecrawl
|
|
40
|
+
|
|
41
|
+
Default `read_page` order:
|
|
13
42
|
|
|
14
|
-
|
|
43
|
+
1. TinyFish
|
|
44
|
+
2. Exa
|
|
45
|
+
3. Firecrawl
|
|
15
46
|
|
|
16
|
-
|
|
17
|
-
|----------|--------|-------|------------------|
|
|
18
|
-
| TinyFish | ✅ | ✅ | Yes |
|
|
19
|
-
| Tavily | ✅ | ❌ | Yes |
|
|
20
|
-
| Exa | ✅ | ✅ | Yes |
|
|
21
|
-
| Firecrawl| ✅ | ✅ | Yes |
|
|
22
|
-
| Codex | ✅ | ❌ | Static credential file in `~/.dsh` |
|
|
23
|
-
| Demo | ✅ | ✅ | No |
|
|
47
|
+
Codex and Tavily are skipped automatically for `read_page` because they do not implement page fetching. Providers without usable credentials are skipped without being marked as failed.
|
|
24
48
|
|
|
25
49
|
## Installation
|
|
26
50
|
|
|
27
|
-
###
|
|
51
|
+
### Install from npm
|
|
28
52
|
|
|
29
53
|
```bash
|
|
30
54
|
npx -y @deepseek-ai/dsh plugin --profile web add @mini-z/dsh-search-providers
|
|
31
55
|
```
|
|
32
56
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
1. Copy the project folder (or the migration zip) to the target machine.
|
|
36
|
-
2. Install dependencies:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
cd dsh-search-providers
|
|
40
|
-
bun install --frozen-lockfile
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
3. Configure API keys in `~/.dsh/.env` (see [Configuration](#configuration)).
|
|
57
|
+
Replace `web` with another DSH profile name when needed. Restart the corresponding DSH process after installation or configuration changes.
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
### Install from a local checkout or tarball
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
> The built plugin artifacts are already included in `dsh/`, so you do not need to run `bun run build` unless you modify the source.
|
|
61
|
+
```bash
|
|
62
|
+
npx -y @deepseek-ai/dsh plugin --profile web add /path/to/dsh-search-providers
|
|
63
|
+
```
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
The npm package and release tarballs include the built `dsh/` artifacts. A local source checkout only needs rebuilding after source changes:
|
|
55
66
|
|
|
56
67
|
```bash
|
|
57
|
-
|
|
58
|
-
cd dsh-search-providers
|
|
59
|
-
bun install
|
|
68
|
+
bun install --frozen-lockfile
|
|
60
69
|
bun run build
|
|
61
70
|
```
|
|
62
71
|
|
|
63
72
|
## Configuration
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
### Environment variables
|
|
74
|
+
The independent JSON configuration loader replaces inline Cordis settings and environment-variable configuration. Cordis accepts **only** the optional `configFile` setting; when omitted, it defaults to `~/.dsh/search-providers.json`.
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
The package-root [`search-providers.example.json`](./search-providers.example.json) is included in the package. Copy it to your chosen configuration path and supply credentials for at least one provider. For example, `~/.dsh/search-providers.json`:
|
|
70
77
|
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"provider": "codex",
|
|
81
|
+
"fallbackChain": ["tinyfish", "tavily", "exa", "firecrawl"],
|
|
82
|
+
"providers": {
|
|
83
|
+
"codex": {
|
|
84
|
+
"credentialFile": "./codex-credential.json",
|
|
85
|
+
"timeoutMs": 55000
|
|
86
|
+
},
|
|
87
|
+
"exa": {
|
|
88
|
+
"apiKey": "replace-with-your-api-key",
|
|
89
|
+
"timeoutMs": 55000
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
77
93
|
```
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
The JSON fields are `provider`, `fallbackChain`, and `providers`. Provider entries support `apiKey`, `timeoutMs`, provider-specific `options`, and `credentialFile` for Codex. Official endpoints for Codex, TinyFish, Tavily, Exa, and Firecrawl are built in and cannot be overridden. The default provider request timeout is 55 seconds. Restrict configuration and credential files to the owning user; do not commit secrets.
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
### File paths and loading
|
|
98
|
+
|
|
99
|
+
- Relative `configFile` paths resolve from the DSH process's current working directory, not the Cordis profile directory.
|
|
100
|
+
- Both `~/` and `~\` expand to the user's home directory.
|
|
101
|
+
- Relative `providers.codex.credentialFile` paths resolve from the JSON configuration file's directory, not the process working directory. This field also supports `~/` and `~\` home expansion.
|
|
102
|
+
- The JSON configuration is loaded once at plugin startup. Restart the corresponding DSH process after changes; there is no live reload.
|
|
103
|
+
- A missing default file logs a warning and uses empty configuration, with no credentials.
|
|
104
|
+
- An explicitly specified missing file, invalid JSON, or a schema-invalid configuration throws a clear error rather than silently falling back.
|
|
105
|
+
- The plugin no longer reads environment variables, including API keys, endpoints, timeouts, Codex credentials, and demo mode. Values in `~/.dsh/.env` are not used by this plugin.
|
|
106
|
+
|
|
107
|
+
### Override the JSON configuration path
|
|
108
|
+
|
|
109
|
+
To use a different file, add an id-targeted override to `~/.dsh/profiles/<profile>/cordis.patch.yml`:
|
|
86
110
|
|
|
87
|
-
|
|
111
|
+
```yaml
|
|
112
|
+
- id: search-providers
|
|
113
|
+
config:
|
|
114
|
+
configFile: ~/.dsh/search-providers-work.json
|
|
115
|
+
```
|
|
88
116
|
|
|
89
|
-
|
|
117
|
+
Omit `configFile` to use the default path. Do not put provider settings or secrets in this Cordis override.
|
|
90
118
|
|
|
91
|
-
|
|
119
|
+
### Change the active provider or fallback chain
|
|
120
|
+
|
|
121
|
+
Set `provider` and `fallbackChain` in the JSON file, for example:
|
|
92
122
|
|
|
93
123
|
```json
|
|
94
124
|
{
|
|
95
|
-
"
|
|
96
|
-
"
|
|
125
|
+
"provider": "exa",
|
|
126
|
+
"fallbackChain": ["codex", "tinyfish", "tavily", "firecrawl"],
|
|
127
|
+
"providers": {
|
|
128
|
+
"exa": {
|
|
129
|
+
"apiKey": "replace-with-your-api-key",
|
|
130
|
+
"timeoutMs": 55000
|
|
131
|
+
}
|
|
132
|
+
}
|
|
97
133
|
}
|
|
98
134
|
```
|
|
99
135
|
|
|
100
|
-
|
|
136
|
+
Routing rules:
|
|
101
137
|
|
|
102
|
-
|
|
138
|
+
- `provider` is always attempted first when it is available and supports the requested capability.
|
|
139
|
+
- `fallbackChain` contains the providers attempted afterward.
|
|
140
|
+
- Duplicate provider ids are removed while preserving order.
|
|
141
|
+
- `fallbackChain: []` disables fallback and uses only the active provider.
|
|
142
|
+
- Providers that do not support the requested capability are omitted from attempts and diagnostics.
|
|
103
143
|
|
|
104
|
-
|
|
144
|
+
For TinyFish, Tavily, Exa, and Firecrawl, provider entries support `apiKey`, `timeoutMs`, and provider-specific `options`. Generic `options` remains supported, but does not allow endpoint overrides. TinyFish uses built-in official endpoints for both search and page fetching.
|
|
105
145
|
|
|
106
|
-
|
|
146
|
+
Codex uses `credentialFile` and `timeoutMs` from its provider configuration. Its endpoint is fixed, and it intentionally ignores `apiKey` and `options`; credentials come exclusively from the file selected by `providers.codex.credentialFile`.
|
|
107
147
|
|
|
108
|
-
|
|
148
|
+
### Migration from inline Cordis and environment settings
|
|
109
149
|
|
|
110
|
-
|
|
150
|
+
1. Create the independent JSON configuration using the packaged example. Move inline Cordis `provider`, `providers`, and `fallbackChain` values into that JSON file. Old inline provider/fallback settings are rejected with a migration message; they are not merged or silently ignored.
|
|
151
|
+
2. Move each provider's `*_API_KEY` and `*_TIMEOUT_MS` values into its JSON `apiKey` and `timeoutMs` fields. Move `CODEX_CREDENTIAL_FILE` to `providers.codex.credentialFile` and `CODEX_TIMEOUT_MS` to `providers.codex.timeoutMs`. Check relative credential paths against the JSON file directory. Do not migrate `*_BASE_URL` values: official endpoints are built in.
|
|
152
|
+
3. Replace `SEARCH_PROVIDERS_DEMO` with `"provider": "demo"` in JSON when demo mode is wanted. Environment variables no longer configure any provider.
|
|
153
|
+
4. Remove any existing provider `baseUrl` fields and TinyFish `options.fetchBaseUrl` from the JSON configuration; these legacy endpoint overrides are rejected. Remove an empty `options` object if nothing remains. Keep API keys, timeouts, routing (`provider` and `fallbackChain`), Codex `credentialFile`, and other supported provider options.
|
|
154
|
+
5. Remove the old inline fields from the Cordis plugin configuration, leaving only an optional `configFile`. Restart DSH to load the new file.
|
|
111
155
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
156
|
+
## Fallback, cooldown, and cancellation behavior
|
|
157
|
+
|
|
158
|
+
The plugin rechecks provider state before every attempt instead of relying on a stale snapshot. A provider whose cooldown expires while other providers are being tried may participate again in the same request.
|
|
159
|
+
|
|
160
|
+
| Failure class | Effect |
|
|
161
|
+
|---------------|--------|
|
|
162
|
+
| Confirmed authentication failure | Provider disabled globally until DSH restarts |
|
|
163
|
+
| HTTP 429/432, quota, or rate limit | Current capability cooled for 5 minutes |
|
|
164
|
+
| Network error or timeout | Current capability cooled for 30 seconds |
|
|
165
|
+
| Other request failure | Current capability cooled for 10 seconds |
|
|
166
|
+
|
|
167
|
+
Transient state is capability-scoped: a search failure does not cool `read_page`, and a fetch failure does not cool `web_search`. Authentication disablement is provider-wide.
|
|
168
|
+
|
|
169
|
+
The first eligible provider uses its configured request timeout. Each subsequent fallback attempt has an additional 55-second outer limit, which also stops providers that ignore `AbortSignal`. The registered `read_page` tool has a 200-second aggregate deadline. These request deadlines are separate from the cooldown durations in the table above.
|
|
170
|
+
|
|
171
|
+
Caller cancellation is different from failure: it propagates immediately, preserves the original abort reason, does not continue fallback, and does not alter provider state.
|
|
172
|
+
|
|
173
|
+
Successful search output identifies the provider that actually answered and reports fallback, cooling, and disabled-provider state. Final errors list the providers genuinely attempted and the current live state.
|
|
174
|
+
|
|
175
|
+
## Codex credential file
|
|
176
|
+
|
|
177
|
+
Codex uses the standalone ChatGPT Codex search endpoint and is independent of model/login plugins. It only reads an existing static credential file; it never starts a login, refreshes a token, checks local expiry timestamps, or writes credential data.
|
|
178
|
+
|
|
179
|
+
Minimal supported JSON:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"access_token": "eyJ...",
|
|
184
|
+
"account_id": "account-id"
|
|
185
|
+
}
|
|
120
186
|
```
|
|
121
187
|
|
|
122
|
-
|
|
188
|
+
The parser also accepts `credential` and `tokens` wrappers, `access` as an alternative token field, and `accountId` as an alternative account field. If the account id is omitted, the plugin reads the standard `chatgpt_account_id` claim from the access token. Missing or malformed credentials make Codex unavailable. Codex HTTP 401/403 responses are treated as confirmed credential failures and trigger the normal fallback chain.
|
|
123
189
|
|
|
124
|
-
|
|
190
|
+
Restrict the credential file to the owning user. Codex does not implement `read_page`, and its standalone endpoint does not apply this plugin's recency, domain, location, language, or maximum-result filters.
|
|
125
191
|
|
|
126
|
-
|
|
192
|
+
## Usage
|
|
127
193
|
|
|
128
|
-
|
|
194
|
+
After loading the plugin and configuring at least one provider, ask the model to search:
|
|
129
195
|
|
|
130
196
|
```text
|
|
131
197
|
Search the web for "latest Rust memory safety features".
|
|
132
198
|
```
|
|
133
199
|
|
|
134
|
-
|
|
200
|
+
Successful results start with the provider that actually answered:
|
|
135
201
|
|
|
136
|
-
|
|
202
|
+
```text
|
|
203
|
+
[Search provider: TinyFish]
|
|
204
|
+
```
|
|
137
205
|
|
|
138
|
-
|
|
206
|
+
Read a specific page with:
|
|
139
207
|
|
|
140
208
|
```text
|
|
141
209
|
Read this page: https://example.com/article
|
|
142
210
|
```
|
|
143
211
|
|
|
144
|
-
|
|
212
|
+
`read_page` returns structured evidence containing a summary, extracted content, outgoing links, uncertainty, and operational warnings.
|
|
145
213
|
|
|
146
|
-
|
|
214
|
+
## Demo mode
|
|
147
215
|
|
|
148
|
-
|
|
216
|
+
Select the key-free demo provider in your JSON configuration file:
|
|
149
217
|
|
|
150
|
-
```
|
|
151
|
-
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"provider": "demo",
|
|
221
|
+
"fallbackChain": []
|
|
222
|
+
}
|
|
152
223
|
```
|
|
153
224
|
|
|
154
|
-
|
|
225
|
+
Restart DSH after changing the file. Demo mode returns canned search and page-fetch results for integration testing; no environment variable is needed or read.
|
|
155
226
|
|
|
156
227
|
## Development
|
|
157
228
|
|
|
158
229
|
```bash
|
|
159
|
-
bun install
|
|
230
|
+
bun install --frozen-lockfile
|
|
160
231
|
bun run typecheck
|
|
161
232
|
bun run test
|
|
162
233
|
bun run build
|
|
163
234
|
```
|
|
164
235
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
- `src/providers/types.ts` — `SearchProvider` interface.
|
|
168
|
-
- `src/providers/registry.ts` — provider registry and active-provider selection.
|
|
169
|
-
- `src/providers/cooldown.ts` — failure tracking and cooldown logic.
|
|
170
|
-
- `src/providers/tinyfish.ts`, `tavily.ts`, `exa.ts`, `firecrawl.ts`, `codex.ts` — provider implementations.
|
|
171
|
-
- `src/providers/demo.ts` — canned demo provider for testing.
|
|
172
|
-
- `src/tools/web-search.ts` — registers the plugin as `ctx.web.registerSearchProvider` with failover.
|
|
173
|
-
- `src/tools/read-page.ts` — registers the `read_page` tool with failover.
|
|
174
|
-
- `src/index.ts` — DSH plugin entry (`apply`).
|
|
236
|
+
Main components:
|
|
175
237
|
|
|
176
|
-
|
|
238
|
+
- `src/providers/types.ts` — provider interfaces and result types.
|
|
239
|
+
- `src/providers/registry.ts` — provider registration and active-provider selection.
|
|
240
|
+
- `src/providers/cooldown.ts` — authentication disablement and capability cooldown state.
|
|
241
|
+
- `src/providers/*.ts` — provider implementations.
|
|
242
|
+
- `src/tools/web-search.ts` — DSH web seam adapter and search fallback.
|
|
243
|
+
- `src/tools/read-page.ts` — `read_page` registration and fetch fallback.
|
|
244
|
+
- `src/index.ts` — plugin entry point and default provider order.
|
|
177
245
|
|
|
178
|
-
|
|
246
|
+
To add a provider, implement `SearchProvider`, register it in `src/index.ts`, and add its configuration resolution.
|
|
179
247
|
|
|
180
248
|
## Publishing
|
|
181
249
|
|
|
182
250
|
1. Bump the version in `package.json`.
|
|
183
|
-
2. `
|
|
184
|
-
3.
|
|
251
|
+
2. Run the development checks and inspect `bun pm pack --dry-run`.
|
|
252
|
+
3. Log in with `npm login`.
|
|
253
|
+
4. Publish the public scoped package with `npm publish --access public`.
|
|
254
|
+
|
|
255
|
+
Published npm versions are immutable; every release requires a new version number.
|
|
185
256
|
|
|
186
257
|
## License
|
|
187
258
|
|
|
188
|
-
MIT. The Codex interoperability implementation was developed with reference to [dsh-codex-connect](https://github.com/franksong2702/dsh-codex-connect) (Apache-2.0); no runtime dependency on that package is required.
|
|
259
|
+
MIT. The Codex interoperability implementation was developed with reference to [dsh-codex-connect](https://github.com/franksong2702/dsh-codex-connect) (Apache-2.0); no runtime dependency on that package is required.
|
package/dsh/index.js
CHANGED
|
@@ -1,19 +1,103 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, dirname, resolve } from "node:path";
|
|
1
4
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { statSync, readFileSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 55e3;
|
|
5
|
-
function
|
|
6
|
+
function loadPluginConfig(pluginConfig = {}) {
|
|
7
|
+
if (!isRecord$1(pluginConfig) || Object.keys(pluginConfig).some((key) => key !== "configFile")) {
|
|
8
|
+
throw new Error("search-providers: Cordis config only accepts configFile. Move provider, providers and fallbackChain to search-providers.json.");
|
|
9
|
+
}
|
|
10
|
+
if (pluginConfig.configFile !== void 0 && !isNonEmptyString(pluginConfig.configFile)) {
|
|
11
|
+
throw new Error("search-providers: configFile must be a non-empty string.");
|
|
12
|
+
}
|
|
13
|
+
const explicit = pluginConfig.configFile !== void 0;
|
|
14
|
+
const filename = resolveConfigPath(pluginConfig.configFile ?? join(homedir(), ".dsh", "search-providers.json"));
|
|
15
|
+
let text;
|
|
16
|
+
try {
|
|
17
|
+
text = readFileSync(filename, "utf8");
|
|
18
|
+
} catch (error) {
|
|
19
|
+
const code = error.code;
|
|
20
|
+
if (!explicit && code === "ENOENT") {
|
|
21
|
+
console.warn(
|
|
22
|
+
"[search-providers] Config file not found: " + filename + ". Create it to configure providers; environment variables are not used."
|
|
23
|
+
);
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
throw new Error("search-providers: Cannot read config file " + filename + " (" + (code ?? "read error") + ").");
|
|
27
|
+
}
|
|
28
|
+
let value;
|
|
29
|
+
try {
|
|
30
|
+
value = JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text);
|
|
31
|
+
} catch {
|
|
32
|
+
throw new Error("search-providers: Invalid JSON in " + filename + ".");
|
|
33
|
+
}
|
|
34
|
+
validateConfig(value, filename);
|
|
35
|
+
for (const entry of Object.values(value.providers ?? {})) {
|
|
36
|
+
if (entry.credentialFile !== void 0) {
|
|
37
|
+
entry.credentialFile = resolveConfigPath(entry.credentialFile, dirname(filename));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function resolveProviderConfig(pluginConfig, providerId) {
|
|
6
43
|
const pc = pluginConfig.providers?.[providerId] ?? {};
|
|
7
|
-
const apiKey = process.env[`${envPrefix}_API_KEY`] ?? pc.apiKey;
|
|
8
|
-
const baseUrl = process.env[`${envPrefix}_BASE_URL`] ?? pc.baseUrl;
|
|
9
|
-
const timeoutMs = Number(process.env[`${envPrefix}_TIMEOUT_MS`]) || pc.timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
10
44
|
return {
|
|
11
|
-
apiKey,
|
|
12
|
-
|
|
13
|
-
timeoutMs,
|
|
45
|
+
apiKey: pc.apiKey,
|
|
46
|
+
credentialFile: pc.credentialFile,
|
|
47
|
+
timeoutMs: pc.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
14
48
|
options: pc.options ?? {}
|
|
15
49
|
};
|
|
16
50
|
}
|
|
51
|
+
function resolveConfigPath(filename, base = process.cwd()) {
|
|
52
|
+
const path = filename.trim();
|
|
53
|
+
if (path === "~") return homedir();
|
|
54
|
+
if (path.startsWith("~/") || path.startsWith("~\\")) {
|
|
55
|
+
return resolve(homedir(), path.slice(2));
|
|
56
|
+
}
|
|
57
|
+
return resolve(base, path);
|
|
58
|
+
}
|
|
59
|
+
function isRecord$1(value) {
|
|
60
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
61
|
+
}
|
|
62
|
+
function isNonEmptyString(value) {
|
|
63
|
+
return typeof value === "string" && value.trim() !== "";
|
|
64
|
+
}
|
|
65
|
+
function validateConfig(value, filename) {
|
|
66
|
+
function invalid(field) {
|
|
67
|
+
throw new Error("search-providers: Invalid config in " + filename + ": " + field + ".");
|
|
68
|
+
}
|
|
69
|
+
if (!isRecord$1(value)) invalid("expected a JSON object");
|
|
70
|
+
if (Object.keys(value).some((key) => !["provider", "providers", "fallbackChain"].includes(key))) {
|
|
71
|
+
invalid("unknown top-level field; use provider, providers or fallbackChain");
|
|
72
|
+
}
|
|
73
|
+
if (value.provider !== void 0 && !isNonEmptyString(value.provider)) {
|
|
74
|
+
invalid("provider must be a non-empty string");
|
|
75
|
+
}
|
|
76
|
+
if (value.fallbackChain !== void 0 && (!Array.isArray(value.fallbackChain) || !value.fallbackChain.every(isNonEmptyString))) {
|
|
77
|
+
invalid("fallbackChain must be an array of non-empty strings");
|
|
78
|
+
}
|
|
79
|
+
if (value.providers === void 0) return;
|
|
80
|
+
if (!isRecord$1(value.providers)) invalid("providers must be an object");
|
|
81
|
+
for (const entry of Object.values(value.providers)) {
|
|
82
|
+
if (!isRecord$1(entry)) invalid("each provider must be an object");
|
|
83
|
+
if (Object.keys(entry).some((key) => !["apiKey", "credentialFile", "timeoutMs", "options"].includes(key))) {
|
|
84
|
+
invalid("unknown provider field; use apiKey, credentialFile, timeoutMs or options (official service URLs are built in)");
|
|
85
|
+
}
|
|
86
|
+
if (entry.apiKey !== void 0 && typeof entry.apiKey !== "string") invalid("apiKey must be a string");
|
|
87
|
+
if (entry.credentialFile !== void 0 && !isNonEmptyString(entry.credentialFile)) {
|
|
88
|
+
invalid("credentialFile must be a non-empty string");
|
|
89
|
+
}
|
|
90
|
+
if (entry.timeoutMs !== void 0 && (typeof entry.timeoutMs !== "number" || !Number.isSafeInteger(entry.timeoutMs) || entry.timeoutMs <= 0 || entry.timeoutMs > 2147483647)) {
|
|
91
|
+
invalid("timeoutMs must be a positive integer no greater than 2147483647");
|
|
92
|
+
}
|
|
93
|
+
if (entry.options !== void 0) {
|
|
94
|
+
if (!isRecord$1(entry.options)) invalid("options must be an object");
|
|
95
|
+
if (Object.hasOwn(entry.options, "fetchBaseUrl")) {
|
|
96
|
+
invalid("remove options.fetchBaseUrl; official service URLs are built in");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
17
101
|
class ProviderAuthenticationError extends Error {
|
|
18
102
|
kind = "provider-auth";
|
|
19
103
|
constructor(message, options) {
|
|
@@ -181,13 +265,11 @@ class TinyfishProvider {
|
|
|
181
265
|
searchable = true;
|
|
182
266
|
fetchable = true;
|
|
183
267
|
apiKey;
|
|
184
|
-
searchBaseUrl;
|
|
185
|
-
fetchBaseUrl;
|
|
268
|
+
searchBaseUrl = "https://api.search.tinyfish.ai";
|
|
269
|
+
fetchBaseUrl = "https://api.fetch.tinyfish.ai";
|
|
186
270
|
timeoutMs;
|
|
187
271
|
constructor(config) {
|
|
188
272
|
this.apiKey = config.apiKey;
|
|
189
|
-
this.searchBaseUrl = config.baseUrl ?? "https://api.search.tinyfish.ai";
|
|
190
|
-
this.fetchBaseUrl = config.options?.fetchBaseUrl ?? "https://api.fetch.tinyfish.ai";
|
|
191
273
|
this.timeoutMs = config.timeoutMs;
|
|
192
274
|
}
|
|
193
275
|
available() {
|
|
@@ -287,7 +369,7 @@ class TinyfishProvider {
|
|
|
287
369
|
ensureKey() {
|
|
288
370
|
if (!this.apiKey) {
|
|
289
371
|
throw new Error(
|
|
290
|
-
"TinyFish API key is missing.
|
|
372
|
+
"TinyFish API key is missing. Configure providers.tinyfish.apiKey in the search-providers JSON config file."
|
|
291
373
|
);
|
|
292
374
|
}
|
|
293
375
|
}
|
|
@@ -299,11 +381,10 @@ class TavilyProvider {
|
|
|
299
381
|
searchable = true;
|
|
300
382
|
fetchable = false;
|
|
301
383
|
apiKey;
|
|
302
|
-
baseUrl;
|
|
384
|
+
baseUrl = "https://api.tavily.com";
|
|
303
385
|
timeoutMs;
|
|
304
386
|
constructor(config) {
|
|
305
387
|
this.apiKey = config.apiKey;
|
|
306
|
-
this.baseUrl = config.baseUrl ?? "https://api.tavily.com";
|
|
307
388
|
this.timeoutMs = config.timeoutMs;
|
|
308
389
|
}
|
|
309
390
|
available() {
|
|
@@ -349,7 +430,7 @@ class TavilyProvider {
|
|
|
349
430
|
}
|
|
350
431
|
ensureKey() {
|
|
351
432
|
if (!this.apiKey) {
|
|
352
|
-
throw new Error("Tavily API key is missing.
|
|
433
|
+
throw new Error("Tavily API key is missing. Configure providers.tavily.apiKey in the search-providers JSON config file.");
|
|
353
434
|
}
|
|
354
435
|
}
|
|
355
436
|
}
|
|
@@ -360,11 +441,10 @@ class ExaProvider {
|
|
|
360
441
|
searchable = true;
|
|
361
442
|
fetchable = true;
|
|
362
443
|
apiKey;
|
|
363
|
-
baseUrl;
|
|
444
|
+
baseUrl = "https://api.exa.ai";
|
|
364
445
|
timeoutMs;
|
|
365
446
|
constructor(config) {
|
|
366
447
|
this.apiKey = config.apiKey;
|
|
367
|
-
this.baseUrl = config.baseUrl ?? "https://api.exa.ai";
|
|
368
448
|
this.timeoutMs = config.timeoutMs;
|
|
369
449
|
}
|
|
370
450
|
available() {
|
|
@@ -440,7 +520,7 @@ class ExaProvider {
|
|
|
440
520
|
}
|
|
441
521
|
ensureKey() {
|
|
442
522
|
if (!this.apiKey) {
|
|
443
|
-
throw new Error("Exa API key is missing.
|
|
523
|
+
throw new Error("Exa API key is missing. Configure providers.exa.apiKey in the search-providers JSON config file.");
|
|
444
524
|
}
|
|
445
525
|
}
|
|
446
526
|
}
|
|
@@ -451,11 +531,10 @@ class FirecrawlProvider {
|
|
|
451
531
|
searchable = true;
|
|
452
532
|
fetchable = true;
|
|
453
533
|
apiKey;
|
|
454
|
-
baseUrl;
|
|
534
|
+
baseUrl = "https://api.firecrawl.dev";
|
|
455
535
|
timeoutMs;
|
|
456
536
|
constructor(config) {
|
|
457
537
|
this.apiKey = config.apiKey;
|
|
458
|
-
this.baseUrl = config.baseUrl ?? "https://api.firecrawl.dev";
|
|
459
538
|
this.timeoutMs = config.timeoutMs;
|
|
460
539
|
}
|
|
461
540
|
available() {
|
|
@@ -533,7 +612,7 @@ class FirecrawlProvider {
|
|
|
533
612
|
}
|
|
534
613
|
ensureKey() {
|
|
535
614
|
if (!this.apiKey) {
|
|
536
|
-
throw new Error("Firecrawl API key is missing.
|
|
615
|
+
throw new Error("Firecrawl API key is missing. Configure providers.firecrawl.apiKey in the search-providers JSON config file.");
|
|
537
616
|
}
|
|
538
617
|
}
|
|
539
618
|
}
|
|
@@ -550,7 +629,8 @@ class CodexProvider {
|
|
|
550
629
|
credentialFile;
|
|
551
630
|
timeoutMs;
|
|
552
631
|
constructor(config) {
|
|
553
|
-
|
|
632
|
+
const filename = readNonEmptyString(config.credentialFile);
|
|
633
|
+
this.credentialFile = filename === void 0 ? void 0 : resolve(filename);
|
|
554
634
|
this.timeoutMs = Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? config.timeoutMs : 55e3;
|
|
555
635
|
}
|
|
556
636
|
available() {
|
|
@@ -630,10 +710,6 @@ class CodexProvider {
|
|
|
630
710
|
return mapCodexSearchResponse(payload);
|
|
631
711
|
}
|
|
632
712
|
}
|
|
633
|
-
function configuredCredentialFile() {
|
|
634
|
-
const filename = readNonEmptyString(process.env.CODEX_CREDENTIAL_FILE);
|
|
635
|
-
return filename === void 0 ? void 0 : resolve(filename);
|
|
636
|
-
}
|
|
637
713
|
function loadStaticCredential(filename) {
|
|
638
714
|
try {
|
|
639
715
|
const stats = statSync(filename);
|
|
@@ -854,7 +930,7 @@ const fetchSchema = {
|
|
|
854
930
|
},
|
|
855
931
|
required: ["summary", "content", "uncertainty"]
|
|
856
932
|
};
|
|
857
|
-
const FALLBACK_ATTEMPT_TIMEOUT_MS =
|
|
933
|
+
const FALLBACK_ATTEMPT_TIMEOUT_MS = 55e3;
|
|
858
934
|
function resolveCandidateIds(registry, fallbackChain) {
|
|
859
935
|
let activeId;
|
|
860
936
|
try {
|
|
@@ -1179,18 +1255,19 @@ const name = "search-providers";
|
|
|
1179
1255
|
const inject = ["tools", "web"];
|
|
1180
1256
|
const DEFAULT_PROVIDER_ORDER = ["codex", "tinyfish", "tavily", "exa", "firecrawl"];
|
|
1181
1257
|
function apply(ctx, rawConfig = {}) {
|
|
1258
|
+
const config = loadPluginConfig(rawConfig);
|
|
1182
1259
|
const registry = new ProviderRegistry();
|
|
1183
1260
|
const cooldown = new CooldownController();
|
|
1184
|
-
const demoMode =
|
|
1185
|
-
registry.register(new CodexProvider(resolveProviderConfig(
|
|
1186
|
-
registry.register(new TinyfishProvider(resolveProviderConfig(
|
|
1187
|
-
registry.register(new TavilyProvider(resolveProviderConfig(
|
|
1188
|
-
registry.register(new ExaProvider(resolveProviderConfig(
|
|
1189
|
-
registry.register(new FirecrawlProvider(resolveProviderConfig(
|
|
1261
|
+
const demoMode = config.provider === "demo";
|
|
1262
|
+
registry.register(new CodexProvider(resolveProviderConfig(config, "codex")));
|
|
1263
|
+
registry.register(new TinyfishProvider(resolveProviderConfig(config, "tinyfish")));
|
|
1264
|
+
registry.register(new TavilyProvider(resolveProviderConfig(config, "tavily")));
|
|
1265
|
+
registry.register(new ExaProvider(resolveProviderConfig(config, "exa")));
|
|
1266
|
+
registry.register(new FirecrawlProvider(resolveProviderConfig(config, "firecrawl")));
|
|
1190
1267
|
if (demoMode) {
|
|
1191
|
-
registry.register(new DemoProvider(resolveProviderConfig(
|
|
1268
|
+
registry.register(new DemoProvider(resolveProviderConfig(config, "tinyfish")));
|
|
1192
1269
|
}
|
|
1193
|
-
const activeProvider =
|
|
1270
|
+
const activeProvider = config.provider;
|
|
1194
1271
|
if (activeProvider) {
|
|
1195
1272
|
try {
|
|
1196
1273
|
registry.setActive(activeProvider);
|
|
@@ -1198,7 +1275,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
1198
1275
|
console.warn(`[search-providers] configured provider '${activeProvider}' is not registered; falling back to '${registry.list()[0]}'`);
|
|
1199
1276
|
}
|
|
1200
1277
|
}
|
|
1201
|
-
const fallbackChain =
|
|
1278
|
+
const fallbackChain = config.fallbackChain ?? DEFAULT_PROVIDER_ORDER;
|
|
1202
1279
|
registerWebSearchProvider(ctx, registry, cooldown, fallbackChain);
|
|
1203
1280
|
registerReadPageTool(ctx, registry, cooldown, fallbackChain);
|
|
1204
1281
|
console.log(`[search-providers] loaded with active provider: ${registry.getActive().id}, fallback after active: [${fallbackChain.join(", ")}]`);
|
package/dsh/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/config.ts","../src/providers/utils.ts","../src/providers/cooldown.ts","../src/providers/registry.ts","../src/providers/tinyfish.ts","../src/providers/tavily.ts","../src/providers/exa.ts","../src/providers/firecrawl.ts","../src/providers/codex.ts","../src/providers/demo.ts","../src/schemas/fetch-schema.ts","../src/tools/fallback-utils.ts","../src/tools/read-page.ts","../src/tools/web-search.ts","../src/index.ts"],"sourcesContent":["export interface ProviderConfigEntry {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs?: number;\n options?: Record<string, unknown>;\n}\n\nexport interface PluginConfig {\n /** Active provider id. */\n provider?: string;\n /** Provider-specific API keys and options. */\n providers?: Record<string, ProviderConfigEntry>;\n /** Provider ids to try after the active provider; an empty array disables fallback. */\n fallbackChain?: string[];\n}\n\nexport interface ResolvedProviderConfig {\n apiKey?: string;\n baseUrl?: string;\n timeoutMs: number;\n options: Record<string, unknown>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 55_000;\n\nexport function resolveProviderConfig(\n pluginConfig: PluginConfig,\n providerId: string,\n envPrefix: string,\n): ResolvedProviderConfig {\n const pc = pluginConfig.providers?.[providerId] ?? {};\n const apiKey =\n process.env[`${envPrefix}_API_KEY`] ??\n pc.apiKey;\n const baseUrl =\n process.env[`${envPrefix}_BASE_URL`] ??\n pc.baseUrl;\n const timeoutMs =\n Number(process.env[`${envPrefix}_TIMEOUT_MS`]) ||\n pc.timeoutMs ||\n DEFAULT_TIMEOUT_MS;\n\n return {\n apiKey,\n baseUrl,\n timeoutMs,\n options: pc.options ?? {},\n };\n}\n","export class ProviderAuthenticationError extends Error {\n readonly kind = 'provider-auth' as const;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProviderAuthenticationError';\n }\n}\n\nexport class ProviderHttpError extends Error {\n readonly kind = 'provider-http' as const;\n\n constructor(\n message: string,\n readonly status: number,\n options?: ErrorOptions,\n ) {\n super(message, options);\n this.name = 'ProviderHttpError';\n }\n}\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(new Error(`request timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n try {\n const signal = init.signal\n ? combineSignals(init.signal, controller.signal)\n : controller.signal;\n return await fetch(url, { ...init, signal });\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function combineSignals(a: AbortSignal, b: AbortSignal): AbortSignal {\n return AbortSignal.any([a, b]);\n}\n\nexport async function throwOnError(response: Response, label: string): Promise<void> {\n if (response.ok) return;\n const text = await response.text().catch(() => '');\n let detail = text.slice(0, 500);\n if (response.status === 401) {\n detail = 'Invalid or missing API key';\n } else if (response.status === 429) {\n detail = 'Rate limit exceeded';\n } else if (response.status === 432) {\n detail = 'Usage limit exceeded';\n }\n const message = `${label} returned ${response.status}: ${detail}`;\n if (response.status === 401) {\n throw new ProviderAuthenticationError(message);\n }\n throw new ProviderHttpError(message, response.status);\n}\n","import { ProviderAuthenticationError, ProviderHttpError } from './utils.js';\n\nexport type ProviderCapability = 'search' | 'fetch';\n\nexport interface CooldownEntry {\n until: number;\n reason: string;\n}\n\nexport class CooldownController {\n private disabled = new Map<string, CooldownEntry>();\n private cooling = new Map<string, CooldownEntry>();\n\n /** Record one capability failure. Confirmed authentication failures are provider-global. */\n record(providerId: string, capability: ProviderCapability, error: unknown): boolean {\n if (error instanceof ProviderAuthenticationError) {\n this.disabled.set(providerId, {\n until: Number.POSITIVE_INFINITY,\n reason: 'invalid credentials',\n });\n return true;\n }\n\n this.cooling.set(this.capabilityKey(providerId, capability), {\n until: Date.now() + this.cooldownDuration(error),\n reason: this.classifyError(error),\n });\n return true;\n }\n\n /** Check whether one provider capability is temporarily cooling. */\n isCooling(providerId: string, capability: ProviderCapability): boolean {\n return this.getEntry(this.cooling, this.capabilityKey(providerId, capability)) !== undefined;\n }\n\n /** Check whether confirmed authentication failure disabled the provider until restart. */\n isDisabled(providerId: string): boolean {\n return this.getEntry(this.disabled, providerId) !== undefined;\n }\n\n /** Get the current failure reason, if any. */\n getReason(providerId: string, capability: ProviderCapability): string | undefined {\n return this.getEntry(this.disabled, providerId)?.reason\n ?? this.getEntry(this.cooling, this.capabilityKey(providerId, capability))?.reason;\n }\n\n /** Build candidates while separating global disablement from capability cooldown. */\n filter(\n chain: string[],\n capability: ProviderCapability,\n ): { ready: string[]; cooling: string[]; disabled: string[] } {\n const ready: string[] = [];\n const cooling: string[] = [];\n const disabled: string[] = [];\n for (const id of chain) {\n if (this.isDisabled(id)) {\n disabled.push(id);\n } else if (this.isCooling(id, capability)) {\n cooling.push(id);\n } else {\n ready.push(id);\n }\n }\n return { ready, cooling, disabled };\n }\n\n private capabilityKey(providerId: string, capability: ProviderCapability): string {\n return `${providerId}:${capability}`;\n }\n\n private getEntry(map: Map<string, CooldownEntry>, key: string): CooldownEntry | undefined {\n const entry = map.get(key);\n if (!entry) return undefined;\n if (Date.now() >= entry.until) {\n map.delete(key);\n return undefined;\n }\n return entry;\n }\n\n private classifyError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (this.isRateLimitError(error, message)) return 'rate limit or quota exceeded';\n if (/timeout|timed out/i.test(message)) return 'timeout';\n if (/network|ENOTFOUND|ECONNREFUSED|fetch failed/i.test(message)) return 'network error';\n return 'request failed';\n }\n\n private cooldownDuration(error: unknown): number {\n const message = error instanceof Error ? error.message : String(error);\n // Rate limits / quotas: cool for 5 minutes.\n if (this.isRateLimitError(error, message)) return 5 * 60 * 1000;\n // Timeouts / network errors: cool for 30 seconds.\n if (/timeout|timed out|network|ENOTFOUND|ECONNREFUSED|fetch failed/i.test(message)) {\n return 30 * 1000;\n }\n // Generic failures: cool for 10 seconds.\n return 10 * 1000;\n }\n\n private isRateLimitError(error: unknown, message: string): boolean {\n return (\n (error instanceof ProviderHttpError && (error.status === 429 || error.status === 432))\n || /429|432|rate.?limit|quota|usage limit|out of credits|monthly quota/i.test(message)\n );\n }\n}\n","import type { SearchProvider } from './types.js';\n\nexport class ProviderRegistry {\n private providers = new Map<string, SearchProvider>();\n private activeId: string | undefined;\n\n register(provider: SearchProvider): void {\n if (this.providers.has(provider.id)) {\n console.warn(`[search-providers] provider '${provider.id}' already registered; overwriting`);\n }\n this.providers.set(provider.id, provider);\n if (this.activeId === undefined) {\n this.activeId = provider.id;\n }\n }\n\n setActive(id: string): void {\n if (!this.providers.has(id)) {\n throw new Error(`Provider '${id}' is not registered`);\n }\n this.activeId = id;\n }\n\n getActive(): SearchProvider {\n if (this.activeId === undefined) {\n throw new Error('No search provider registered');\n }\n const provider = this.providers.get(this.activeId);\n if (!provider) {\n throw new Error(`Active provider '${this.activeId}' disappeared`);\n }\n return provider;\n }\n\n get(id: string): SearchProvider | undefined {\n return this.providers.get(id);\n }\n\n list(): string[] {\n return Array.from(this.providers.keys());\n }\n}\n","import type { ResolvedProviderConfig } from '../config.js';\nimport type {\n FetchRequest,\n FetchResult,\n SearchProvider,\n SearchRequest,\n SearchResult,\n} from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface TinyfishSearchResponse {\n query: string;\n results: Array<{\n position: number;\n site_name?: string;\n title: string;\n snippet: string;\n url: string;\n }>;\n total_results: number;\n page: number;\n}\n\ninterface TinyfishFetchResponse {\n results: Array<{\n url: string;\n final_url?: string;\n title?: string;\n description?: string;\n language?: string;\n format?: string;\n text: string;\n }>;\n errors: Array<{ url: string; error: string }>;\n}\n\nexport class TinyfishProvider implements SearchProvider {\n readonly id = 'tinyfish';\n readonly name = 'TinyFish';\n readonly description = 'TinyFish Search & Fetch (free tier, live web results)';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly searchBaseUrl: string;\n private readonly fetchBaseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.searchBaseUrl = config.baseUrl ?? 'https://api.search.tinyfish.ai';\n this.fetchBaseUrl =\n (config.options?.fetchBaseUrl as string | undefined) ?? 'https://api.fetch.tinyfish.ai';\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const url = new URL(this.searchBaseUrl);\n url.searchParams.set('query', request.query);\n if (request.maxResults && request.maxResults > 0) {\n url.searchParams.set('max_results', String(Math.floor(request.maxResults)));\n }\n if (typeof request.recencyDays === 'number' && request.recencyDays > 0) {\n url.searchParams.set('recency_minutes', String(Math.floor(request.recencyDays * 24 * 60)));\n }\n if (request.purpose) {\n url.searchParams.set('purpose', request.purpose);\n }\n if (request.location) {\n url.searchParams.set('location', request.location);\n }\n if (request.language) {\n url.searchParams.set('language', request.language);\n }\n if (request.domainType) {\n url.searchParams.set('domain_type', request.domainType);\n }\n if (request.includeDomains && request.includeDomains.length > 0) {\n url.searchParams.set('include_domains', request.includeDomains.join(','));\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n url.searchParams.set('exclude_domains', request.excludeDomains.join(','));\n }\n\n const response = await fetchWithTimeout(url.toString(), {\n method: 'GET',\n headers: {\n 'X-API-Key': this.apiKey!,\n Accept: 'application/json',\n },\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'TinyFish Search');\n\n const body = (await response.json()) as TinyfishSearchResponse;\n const items = (body.results ?? []).map((r) => ({\n title: r.title,\n url: r.url,\n snippet: r.snippet,\n source: r.site_name,\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${body.total_results ?? items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n urls: [request.url],\n format: request.format ?? 'markdown',\n };\n if (request.query) {\n body.purpose = request.query;\n }\n if (typeof request.ttl === 'number') {\n body.ttl = request.ttl;\n }\n\n const response = await fetchWithTimeout(this.fetchBaseUrl, {\n method: 'POST',\n headers: {\n 'X-API-Key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'TinyFish Fetch');\n\n const data = (await response.json()) as TinyfishFetchResponse;\n const page = data.results?.[0];\n const error = data.errors?.[0];\n\n if (!page && error) {\n throw new Error(`TinyFish Fetch failed for ${error.url}: ${error.error}`);\n }\n if (!page) {\n throw new Error('TinyFish Fetch returned no content');\n }\n\n return {\n summary: page.title ?? page.description ?? 'Fetched page content',\n content: page.text ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error(\n 'TinyFish API key is missing. Set TINYFISH_API_KEY or configure providers.tinyfish.apiKey.',\n );\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface TavilySearchResponse {\n query: string;\n results: Array<{\n title: string;\n url: string;\n content?: string;\n raw_content?: string;\n score?: number;\n }>;\n}\n\nexport class TavilyProvider implements SearchProvider {\n readonly id = 'tavily';\n readonly name = 'Tavily';\n readonly description = 'Tavily web search';\n readonly searchable = true;\n readonly fetchable = false;\n\n private readonly apiKey?: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? 'https://api.tavily.com';\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n search_depth: 'basic',\n max_results: request.maxResults ?? 5,\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.include_domains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.exclude_domains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/search`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Tavily Search');\n\n const data = (await response.json()) as TavilySearchResponse;\n const items = (data.results ?? []).map((r) => ({\n title: r.title,\n url: r.url,\n snippet: r.content ?? r.raw_content ?? '',\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Tavily API key is missing. Set TAVILY_API_KEY or configure providers.tavily.apiKey.');\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface ExaSearchResponse {\n results: Array<{\n title?: string;\n url: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n score?: number;\n }>;\n}\n\ninterface ExaContentsResponse {\n results: Array<{\n url: string;\n text?: string;\n title?: string;\n }>;\n}\n\nexport class ExaProvider implements SearchProvider {\n readonly id = 'exa';\n readonly name = 'Exa';\n readonly description = 'Exa neural web search and contents fetch';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? 'https://api.exa.ai';\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n num_results: request.maxResults ?? 5,\n type: 'auto',\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.include_domains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.exclude_domains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/search`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Exa Search');\n\n const data = (await response.json()) as ExaSearchResponse;\n const items = (data.results ?? []).map((r) => ({\n title: r.title ?? r.url,\n url: r.url,\n snippet: r.text ?? '',\n publishedAt: r.publishedDate,\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body = {\n urls: [request.url],\n text: true,\n };\n\n const response = await fetchWithTimeout(`${this.baseUrl}/contents`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Exa Contents');\n\n const data = (await response.json()) as ExaContentsResponse;\n const page = data.results?.[0];\n if (!page) {\n throw new Error('Exa Contents returned no content');\n }\n\n return {\n summary: page.title ?? 'Fetched page content',\n content: page.text ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Exa API key is missing. Set EXA_API_KEY or configure providers.exa.apiKey.');\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface FirecrawlSearchResponse {\n success: boolean;\n data: {\n web: Array<{\n title?: string;\n url: string;\n description?: string;\n markdown?: string;\n }>;\n };\n}\n\ninterface FirecrawlScrapeResponse {\n success: boolean;\n data: {\n markdown?: string;\n metadata?: {\n title?: string;\n sourceURL?: string;\n };\n };\n}\n\nexport class FirecrawlProvider implements SearchProvider {\n readonly id = 'firecrawl';\n readonly name = 'Firecrawl';\n readonly description = 'Firecrawl web search and page scrape';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? 'https://api.firecrawl.dev';\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n limit: request.maxResults ?? 5,\n sources: ['web'],\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.includeDomains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.excludeDomains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/v2/search`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Firecrawl Search');\n\n const data = (await response.json()) as FirecrawlSearchResponse;\n if (data.success === false) {\n throw new Error('Firecrawl Search returned success: false');\n }\n\n const rawItems = data.data?.web ?? [];\n const items = rawItems.map((r) => ({\n title: r.title ?? r.url,\n url: r.url,\n snippet: r.description ?? r.markdown ?? '',\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body = {\n url: request.url,\n formats: ['markdown'],\n };\n\n const response = await fetchWithTimeout(`${this.baseUrl}/v2/scrape`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Firecrawl Scrape');\n\n const data = (await response.json()) as FirecrawlScrapeResponse;\n if (data.success === false) {\n throw new Error('Firecrawl Scrape returned success: false');\n }\n\n return {\n summary: data.data?.metadata?.title ?? 'Fetched page content',\n content: data.data?.markdown ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Firecrawl API key is missing. Set FIRECRAWL_API_KEY or configure providers.firecrawl.apiKey.');\n }\n }\n}","// Codex search protocol behavior is based on dsh-codex-connect (Apache-2.0).\n// This provider only consumes an existing static credential; it never logs in,\n// refreshes tokens, or writes credential data.\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync, statSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { ResolvedProviderConfig } from '../config.js';\nimport type { SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { ProviderAuthenticationError } from './utils.js';\n\nconst CODEX_SEARCH_URL = 'https://chatgpt.com/backend-api/codex/alpha/search';\nconst MAX_CREDENTIAL_BYTES = 128 * 1024;\nconst MAX_SUCCESS_RESPONSE_BYTES = 4 * 1024 * 1024;\nconst MAX_ERROR_RESPONSE_BYTES = 64 * 1024;\n\ninterface StaticCodexCredential {\n accessToken: string;\n accountId: string;\n}\n\ninterface CodexSearchRequestBody {\n id: string;\n model: string;\n input: Array<{\n type: 'message';\n role: 'user';\n content: Array<{ type: 'input_text'; text: string }>;\n }>;\n commands: { search_query: Array<{ q: string }> };\n settings: {\n search_context_size: 'medium';\n allowed_callers: ['direct'];\n external_web_access: false;\n };\n max_output_tokens: number;\n}\n\nexport class CodexProvider implements SearchProvider {\n readonly id = 'codex';\n readonly name = 'OpenAI Codex';\n readonly description = 'ChatGPT Codex standalone web search';\n readonly searchable = true;\n readonly fetchable = false;\n\n private readonly credentialFile?: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.credentialFile = configuredCredentialFile();\n this.timeoutMs = Number.isFinite(config.timeoutMs) && config.timeoutMs > 0\n ? config.timeoutMs\n : 55_000;\n }\n\n available(): boolean {\n return this.credentialFile !== undefined\n && loadStaticCredential(this.credentialFile) !== undefined;\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n throwIfAborted(signal);\n\n const credential = this.credentialFile === undefined\n ? undefined\n : loadStaticCredential(this.credentialFile);\n if (!credential) {\n throw new Error('OpenAI Codex credential file is not configured, missing or invalid');\n }\n\n const body: CodexSearchRequestBody = {\n id: randomUUID(),\n model: 'gpt-5.6-sol',\n input: [{\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: request.query }],\n }],\n commands: { search_query: [{ q: request.query }] },\n settings: {\n search_context_size: 'medium',\n allowed_callers: ['direct'],\n external_web_access: false,\n },\n max_output_tokens: 10_000,\n };\n\n const requestTimeout = AbortSignal.timeout(this.timeoutMs);\n const requestSignal = signal ? AbortSignal.any([signal, requestTimeout]) : requestTimeout;\n let response: Response;\n try {\n response = await fetch(CODEX_SEARCH_URL, {\n method: 'POST',\n redirect: 'error',\n headers: {\n Authorization: `Bearer ${credential.accessToken}`,\n 'chatgpt-account-id': credential.accountId,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n originator: 'deepseek-harness',\n },\n body: JSON.stringify(body),\n signal: requestSignal,\n });\n } catch (error) {\n throwIfAborted(signal);\n if (requestTimeout.aborted) {\n throw new Error(`OpenAI Codex search timed out after ${this.timeoutMs}ms`, { cause: error });\n }\n throw new Error('OpenAI Codex search request failed', { cause: error });\n }\n\n let payload: unknown;\n try {\n payload = await readBoundedJson(\n response,\n response.ok ? MAX_SUCCESS_RESPONSE_BYTES : MAX_ERROR_RESPONSE_BYTES,\n requestSignal,\n );\n } catch (error) {\n throwIfAborted(signal);\n if (requestTimeout.aborted) {\n throw new Error(`OpenAI Codex search timed out after ${this.timeoutMs}ms`, { cause: error });\n }\n if (error instanceof ResponseTooLargeError) throw error;\n throw new Error(`OpenAI Codex returned invalid JSON (HTTP ${response.status})`, { cause: error });\n }\n\n if (!response.ok) {\n const detail = providerMessage(payload);\n const suffix = detail ? `: ${detail}` : '';\n if (response.status === 401 || response.status === 403) {\n throw new ProviderAuthenticationError(\n `OpenAI Codex credential is invalid or expired (HTTP ${response.status})${suffix}`,\n );\n }\n throw new Error(`OpenAI Codex search failed (HTTP ${response.status})${suffix}`);\n }\n\n return mapCodexSearchResponse(payload);\n }\n}\n\nfunction configuredCredentialFile(): string | undefined {\n const filename = readNonEmptyString(process.env.CODEX_CREDENTIAL_FILE);\n return filename === undefined ? undefined : resolve(filename);\n}\n\nfunction loadStaticCredential(filename: string): StaticCodexCredential | undefined {\n try {\n const stats = statSync(filename);\n if (!stats.isFile() || stats.size <= 0 || stats.size > MAX_CREDENTIAL_BYTES) return undefined;\n const value: unknown = JSON.parse(readFileSync(filename, 'utf8'));\n return parseStaticCredential(value);\n } catch {\n return undefined;\n }\n}\n\nfunction mapCodexSearchResponse(value: unknown): SearchResult {\n if (!isRecord(value) || typeof value.output !== 'string') {\n throw new Error('OpenAI Codex returned a search response without string output');\n }\n if (value.results !== undefined && !Array.isArray(value.results)) {\n throw new Error('OpenAI Codex returned a search response with non-array results');\n }\n\n const items: SearchResult['items'] = [];\n const seen = new Set<string>();\n for (const item of value.results ?? []) {\n if (!isRecord(item) || item.type !== 'text_result') continue;\n const url = citeableUrl(item.url);\n if (!url || seen.has(url)) continue;\n seen.add(url);\n items.push({\n title: readNonEmptyString(item.title) ?? url,\n url,\n snippet: readNonEmptyString(item.snippet) ?? '',\n });\n }\n\n const summary = value.output;\n return {\n status: summary.length > 0 ? 'ok' : 'degraded',\n source: 'OpenAI Codex',\n summary: summary || `Codex returned ${items.length} source(s) without summary text`,\n items,\n uncertainty: summary.length > 0 ? [] : ['Codex returned no summary text'],\n };\n}\n\nfunction parseStaticCredential(value: unknown): StaticCodexCredential | undefined {\n if (!isRecord(value)) return undefined;\n\n const candidates = [\n value,\n isRecord(value.credential) ? value.credential : undefined,\n isRecord(value.tokens) ? value.tokens : undefined,\n ];\n\n for (const candidate of candidates) {\n if (!candidate) continue;\n const accessToken = readNonEmptyString(candidate.access_token)\n ?? readNonEmptyString(candidate.access);\n if (!accessToken) continue;\n\n const accountId = readNonEmptyString(candidate.account_id)\n ?? readNonEmptyString(candidate.accountId)\n ?? accountIdFromToken(accessToken);\n if (!accountId) continue;\n\n return { accessToken, accountId };\n }\n\n return undefined;\n}\n\nfunction decodeJwtPayload(accessToken: string): Record<string, unknown> | undefined {\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3 || !parts[1]) return undefined;\n const payload: unknown = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));\n return isRecord(payload) ? payload : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction accountIdFromToken(accessToken: string): string | undefined {\n const auth = decodeJwtPayload(accessToken)?.['https://api.openai.com/auth'];\n return isRecord(auth) ? readNonEmptyString(auth.chatgpt_account_id) : undefined;\n}\n\nclass ResponseTooLargeError extends Error {\n constructor(limit: number) {\n super(`OpenAI Codex search response exceeded ${limit} bytes`);\n this.name = 'ResponseTooLargeError';\n }\n}\n\nasync function readBoundedJson(\n response: Response,\n maxBytes: number,\n signal: AbortSignal,\n): Promise<unknown> {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n await response.body?.cancel().catch(() => {});\n throw new ResponseTooLargeError(maxBytes);\n }\n if (!response.body) throw new SyntaxError('empty response body');\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const { done, value } = await abortable(reader.read(), signal);\n if (done) break;\n if (!value) continue;\n total += value.byteLength;\n if (total > maxBytes) throw new ResponseTooLargeError(maxBytes);\n chunks.push(value);\n }\n } catch (error) {\n await reader.cancel().catch(() => {});\n throw error;\n } finally {\n reader.releaseLock();\n }\n\n return JSON.parse(Buffer.concat(chunks, total).toString('utf8')) as unknown;\n}\n\nfunction abortable<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(abortedError(signal));\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortedError(signal));\n signal.addEventListener('abort', onAbort, { once: true });\n operation.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw abortedError(signal);\n}\n\nfunction abortedError(signal: AbortSignal): Error {\n return new Error('OpenAI Codex search aborted', { cause: signal.reason });\n}\n\nfunction providerMessage(value: unknown): string | undefined {\n if (!isRecord(value)) return undefined;\n const raw = typeof value.error === 'string'\n ? value.error\n : isRecord(value.error) && typeof value.error.message === 'string'\n ? value.error.message\n : typeof value.message === 'string'\n ? value.message\n : undefined;\n return raw\n ?.replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu, '[REDACTED]')\n .slice(0, 1_000);\n}\n\nfunction citeableUrl(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;\n}\n\n","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\n\nexport class DemoProvider implements SearchProvider {\n readonly id = 'demo';\n readonly name = 'Demo Provider';\n readonly description = 'Returns canned results for integration testing without calling any external API.';\n readonly searchable = true;\n readonly fetchable = true;\n\n constructor(_config: ResolvedProviderConfig) {}\n\n available(): boolean {\n return true;\n }\n\n async search(request: SearchRequest): Promise<SearchResult> {\n return {\n status: 'ok',\n source: this.name,\n summary: `Demo search results for \"${request.query}\"`,\n items: [\n {\n title: 'Demo Result 1',\n url: 'https://example.com/demo1',\n snippet: 'This is a demo search result used to verify the DSH plugin wiring.',\n },\n {\n title: 'Demo Result 2',\n url: 'https://example.com/demo2',\n snippet: 'Another canned result so citation cards have something to render.',\n },\n ],\n uncertainty: ['Demo mode: results are not from a real search engine.'],\n };\n }\n\n async fetch(request: FetchRequest): Promise<FetchResult> {\n return {\n summary: `Demo fetch of ${request.url}`,\n content: `# Demo Page\\n\\nThis is canned content for the URL \"${request.url}\".`,\n links: [\n { text: 'Example', url: 'https://example.com' },\n ],\n uncertainty: ['Demo mode: content is not from a real web page.'],\n warnings: [],\n };\n }\n}","export const fetchSchema = {\n type: 'object' as const,\n properties: {\n summary: { type: 'string' as const },\n content: { type: 'string' as const },\n links: {\n type: 'array' as const,\n items: {\n type: 'object' as const,\n properties: {\n text: { type: 'string' as const },\n url: { type: 'string' as const }\n },\n required: ['text', 'url'] as const\n }\n },\n uncertainty: {\n type: 'array' as const,\n items: { type: 'string' as const }\n },\n warnings: {\n type: 'array' as const,\n items: { type: 'string' as const }\n }\n },\n required: ['summary', 'content', 'uncertainty'] as const\n};\n","import type { ProviderRegistry } from '../providers/registry.js';\n\nexport const FALLBACK_ATTEMPT_TIMEOUT_MS = 20_000;\n\nexport interface AttemptContext {\n signal: AbortSignal;\n timedOut(): boolean;\n dispose(): void;\n}\n\nexport function resolveCandidateIds(\n registry: ProviderRegistry,\n fallbackChain?: string[],\n): string[] {\n let activeId: string | undefined;\n try {\n activeId = registry.getActive().id;\n } catch {\n // An empty registry has no active provider.\n }\n\n const configured = fallbackChain === undefined ? registry.list() : fallbackChain;\n const seen = new Set<string>();\n const candidates: string[] = [];\n for (const id of activeId ? [activeId, ...configured] : configured) {\n if (seen.has(id)) continue;\n seen.add(id);\n candidates.push(id);\n }\n return candidates;\n}\n\nexport function createAttemptContext(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): AttemptContext {\n const controller = new AbortController();\n let didTimeOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onParentAbort: (() => void) | undefined;\n\n if (parent?.aborted) {\n controller.abort(parent.reason);\n } else {\n if (parent) {\n onParentAbort = () => controller.abort(parent.reason);\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n timer = setTimeout(() => {\n didTimeOut = true;\n controller.abort(new Error(`fallback attempt timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n return {\n signal: controller.signal,\n timedOut: () => didTimeOut,\n dispose() {\n if (timer !== undefined) clearTimeout(timer);\n if (parent && onParentAbort) parent.removeEventListener('abort', onParentAbort);\n },\n };\n}\n\nexport function raceWithSignal<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return operation;\n if (signal.aborted) {\n operation.catch(() => {});\n return Promise.reject(signal.reason);\n }\n\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener('abort', onAbort);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n operation.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw signal.reason;\n}\n","import type { CooldownController } from '../providers/cooldown.js';\nimport type { ProviderRegistry } from '../providers/registry.js';\nimport type { FetchRequest, FetchResult } from '../providers/types.js';\nimport { ProviderAuthenticationError } from '../providers/utils.js';\nimport { fetchSchema } from '../schemas/fetch-schema.js';\nimport {\n createAttemptContext,\n FALLBACK_ATTEMPT_TIMEOUT_MS,\n raceWithSignal,\n resolveCandidateIds,\n throwIfAborted,\n} from './fallback-utils.js';\n\nconst READ_PAGE_TIMEOUT_MS = 180_000;\nconst RENDER_CONTENT_CAP = 20_000;\nconst RENDER_LINK_CAP = 20;\n\nexport function registerReadPageTool(\n ctx: any,\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain?: string[],\n): void {\n if (typeof ctx.tools?.register !== 'function') {\n console.error('[search-providers] ctx.tools.register is not available; read_page tool skipped');\n return;\n }\n\n ctx.tools.register({\n name: 'read_page',\n description:\n 'Read one web page through the configured search provider. Use when a message references a specific http(s) URL whose content matters: docs, an article, a changelog, a thread. Returns structured evidence with a summary, the extracted content, outgoing links, uncertainty, and operational warnings. Pass \"query\" to focus the reading on one question.',\n parameters: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'The http(s) URL to read',\n },\n query: {\n type: 'string',\n description: 'Optional question to focus the reading on (e.g. \"what are the rate limits\")',\n },\n },\n required: ['url'],\n },\n output: {\n schema: fetchSchema,\n render: (_args: unknown, value: Record<string, unknown>) => [\n { type: 'text', text: renderFetchEvidence(value) },\n ],\n },\n timeoutMs: READ_PAGE_TIMEOUT_MS + 20_000,\n isConcurrencySafe: () => true,\n presentCall: (args: { url: string; query?: string }) => ({\n card: 'generic',\n title: 'read_page',\n kind: 'fetch',\n rawInput: args,\n }),\n async execute(args: { url: string; query?: string }, exec: { signal?: AbortSignal }) {\n if (typeof args?.url !== 'string' || !/^https?:\\/\\//i.test(args.url.trim())) {\n throw new Error('read_page needs an http(s) \"url\".');\n }\n\n const request: FetchRequest = {\n url: args.url.trim(),\n query: args.query,\n format: 'markdown',\n };\n\n return fetchWithFallback(registry, cooldown, fallbackChain, request, exec.signal);\n },\n });\n}\n\nfunction resolveFetchCandidateIds(\n registry: ProviderRegistry,\n fallbackChain: string[] | undefined,\n): string[] {\n return resolveCandidateIds(registry, fallbackChain).filter((id) => {\n const provider = registry.get(id);\n return provider !== undefined\n && provider.fetchable !== false\n && typeof provider.fetch === 'function';\n });\n}\n\nasync function fetchWithFallback(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n request: FetchRequest,\n signal: AbortSignal | undefined,\n): Promise<FetchResult> {\n throwIfAborted(signal);\n const candidates = resolveFetchCandidateIds(registry, fallbackChain);\n const attempted: string[] = [];\n const attemptedIds = new Set<string>();\n let lastError: Error | undefined;\n\n while (true) {\n throwIfAborted(signal);\n const { ready } = cooldown.filter(candidates, 'fetch');\n const id = ready.find((candidateId) => {\n if (attemptedIds.has(candidateId)) return false;\n const provider = registry.get(candidateId);\n return provider?.available()\n && provider.fetchable !== false\n && typeof provider.fetch === 'function';\n });\n if (id === undefined) break;\n\n const provider = registry.get(id)!;\n attemptedIds.add(id);\n const attempt = attempted.length === 0\n ? undefined\n : createAttemptContext(signal, FALLBACK_ATTEMPT_TIMEOUT_MS);\n\n try {\n const attemptSignal = attempt?.signal ?? signal;\n const result = await raceWithSignal(provider.fetch!(request, attemptSignal), attemptSignal);\n throwIfAborted(signal);\n if (attempt?.timedOut()) throw attempt.signal.reason;\n const { cooling, disabled } = cooldown.filter(candidates, 'fetch');\n if (attempted.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Fallback used after ${attempted.join(', ')} failed`);\n }\n if (cooling.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Cooling providers skipped: ${cooling.join(', ')}`);\n }\n if (disabled.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Providers disabled until restart: ${disabled.join(', ')}`);\n }\n return result;\n } catch (error) {\n throwIfAborted(signal);\n const caught = error instanceof Error ? error : new Error(String(error));\n lastError = caught instanceof ProviderAuthenticationError\n ? caught\n : attempt?.timedOut()\n ? new Error(`fallback attempt timed out after ${FALLBACK_ATTEMPT_TIMEOUT_MS}ms`, { cause: caught })\n : caught;\n attempted.push(id);\n console.warn(`[search-providers] provider '${id}' fetch failed, trying next: ${lastError.message}`);\n cooldown.record(id, 'fetch', lastError);\n } finally {\n attempt?.dispose();\n }\n }\n\n const { cooling, disabled } = cooldown.filter(candidates, 'fetch');\n const coolingIds = cooling.length > 0 ? ` [cooling: ${cooling.join(', ')}]` : '';\n const disabledIds = disabled.length > 0 ? ` [disabled until restart: ${disabled.join(', ')}]` : '';\n throw new Error(\n `No fetch provider succeeded${attempted.length > 0 ? ` (tried: ${attempted.join(', ')})` : ''}${coolingIds}${disabledIds}. ${lastError?.message ?? 'No provider available.'}`,\n { cause: lastError },\n );\n}\n\nfunction renderFetchEvidence(value: Record<string, unknown>): string {\n const lines: string[] = [String(value.summary ?? '')];\n const content = String((value.content as string) ?? '').trim();\n if (content) {\n lines.push('', 'Content:', content.length > RENDER_CONTENT_CAP ? `${content.slice(0, RENDER_CONTENT_CAP)}…` : content);\n }\n const links = Array.isArray(value.links) ? value.links.slice(0, RENDER_LINK_CAP) : [];\n if (links.length > 0) {\n lines.push('', 'Links:');\n for (const link of links) {\n const text = typeof link === 'object' && link !== null ? String((link as Record<string, unknown>).text ?? '') : '';\n const url = typeof link === 'object' && link !== null ? String((link as Record<string, unknown>).url ?? '') : '';\n lines.push(`- ${text} — ${url}`);\n }\n }\n const uncertainty = Array.isArray(value.uncertainty) ? value.uncertainty : [];\n if (uncertainty.length > 0) {\n lines.push('', `Uncertain: ${uncertainty.join('; ')}`);\n }\n const warnings = Array.isArray(value.warnings) ? value.warnings : [];\n if (warnings.length > 0) {\n lines.push('', `Warnings: ${warnings.join('; ')}`);\n }\n return lines.filter(Boolean).join('\\n');\n}\n","import type { CooldownController } from '../providers/cooldown.js';\nimport type { ProviderRegistry } from '../providers/registry.js';\nimport type { SearchRequest, SearchResult } from '../providers/types.js';\nimport { ProviderAuthenticationError } from '../providers/utils.js';\nimport {\n createAttemptContext,\n FALLBACK_ATTEMPT_TIMEOUT_MS,\n raceWithSignal,\n resolveCandidateIds,\n throwIfAborted,\n} from './fallback-utils.js';\n\ninterface WebSeamSearchRequest {\n query: string;\n maxResults?: number;\n recencyDays?: number;\n purpose?: string;\n location?: string;\n language?: string;\n}\n\nexport function registerWebSearchProvider(\n ctx: any,\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain?: string[],\n): void {\n if (typeof ctx.web?.registerSearchProvider !== 'function') {\n console.error('[search-providers] ctx.web.registerSearchProvider is not available; web_search provider skipped');\n return;\n }\n\n ctx.web.registerSearchProvider({\n id: 'search-providers',\n available: () => anyProviderAvailable(registry, cooldown, fallbackChain),\n async search(request: WebSeamSearchRequest, signal: AbortSignal | undefined) {\n const searchRequest: SearchRequest = {\n query: request.query,\n maxResults: request.maxResults,\n recencyDays: request.recencyDays,\n purpose: request.purpose,\n location: request.location,\n language: request.language,\n };\n\n const { result, attempted, cooling, disabled } = await searchWithFallback(\n registry,\n cooldown,\n fallbackChain,\n searchRequest,\n signal,\n );\n\n return {\n content: renderContent(result, attempted, cooling, disabled),\n sources: result.items.map((item) => ({\n url: item.url,\n ...(item.title ? { title: item.title } : {}),\n ...(item.snippet ? { snippet: item.snippet } : {}),\n ...(item.publishedAt ? { publishedAt: item.publishedAt } : {}),\n })),\n truncated: false,\n };\n },\n });\n}\n\nfunction resolveSearchCandidateIds(\n registry: ProviderRegistry,\n fallbackChain: string[] | undefined,\n): string[] {\n return resolveCandidateIds(registry, fallbackChain).filter((id) => {\n const provider = registry.get(id);\n return provider !== undefined && provider.searchable !== false;\n });\n}\n\nfunction anyProviderAvailable(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n): boolean {\n const candidates = resolveSearchCandidateIds(registry, fallbackChain);\n const { ready } = cooldown.filter(candidates, 'search');\n return ready.some((id) => {\n const provider = registry.get(id);\n return provider?.available() && provider.searchable !== false;\n });\n}\n\ninterface FallbackOutcome {\n result: SearchResult;\n attempted: string[];\n cooling: string[];\n disabled: string[];\n}\n\nasync function searchWithFallback(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n request: SearchRequest,\n signal: AbortSignal | undefined,\n): Promise<FallbackOutcome> {\n throwIfAborted(signal);\n const candidates = resolveSearchCandidateIds(registry, fallbackChain);\n const attempted: string[] = [];\n const attemptedIds = new Set<string>();\n let lastError: Error | undefined;\n\n while (true) {\n throwIfAborted(signal);\n const { ready } = cooldown.filter(candidates, 'search');\n const id = ready.find((candidateId) => {\n if (attemptedIds.has(candidateId)) return false;\n const provider = registry.get(candidateId);\n return provider?.available() && provider.searchable !== false;\n });\n if (id === undefined) break;\n\n const provider = registry.get(id)!;\n attemptedIds.add(id);\n const attempt = attempted.length === 0\n ? undefined\n : createAttemptContext(signal, FALLBACK_ATTEMPT_TIMEOUT_MS);\n\n try {\n const attemptSignal = attempt?.signal ?? signal;\n const result = await raceWithSignal(provider.search(request, attemptSignal), attemptSignal);\n throwIfAborted(signal);\n if (attempt?.timedOut()) throw attempt.signal.reason;\n const { cooling, disabled } = cooldown.filter(candidates, 'search');\n return { result, attempted, cooling, disabled };\n } catch (error) {\n throwIfAborted(signal);\n const caught = error instanceof Error ? error : new Error(String(error));\n lastError = caught instanceof ProviderAuthenticationError\n ? caught\n : attempt?.timedOut()\n ? new Error(`fallback attempt timed out after ${FALLBACK_ATTEMPT_TIMEOUT_MS}ms`, { cause: caught })\n : caught;\n attempted.push(id);\n console.warn(`[search-providers] provider '${id}' search failed, trying next: ${lastError.message}`);\n cooldown.record(id, 'search', lastError);\n } finally {\n attempt?.dispose();\n }\n }\n\n const { cooling, disabled } = cooldown.filter(candidates, 'search');\n const coolingIds = cooling.length > 0 ? ` [cooling: ${cooling.join(', ')}]` : '';\n const disabledIds = disabled.length > 0 ? ` [disabled until restart: ${disabled.join(', ')}]` : '';\n throw new Error(\n `No search provider succeeded${attempted.length > 0 ? ` (tried: ${attempted.join(', ')})` : ''}${coolingIds}${disabledIds}. ${lastError?.message ?? 'No provider available.'}`,\n { cause: lastError },\n );\n}\n\nfunction renderContent(\n result: SearchResult,\n attempted: string[],\n cooling: string[],\n disabled: string[],\n): string {\n const lines = [`[Search provider: ${result.source}]`, result.summary];\n if (attempted.length > 0) {\n lines.push(`[Fallback used after ${attempted.join(', ')} failed]`);\n }\n if (cooling.length > 0) {\n lines.push(`[Cooling: ${cooling.join(', ')}]`);\n }\n if (disabled.length > 0) {\n lines.push(`[Disabled until restart: ${disabled.join(', ')}]`);\n }\n if (result.uncertainty.length > 0) {\n lines.push(`Uncertain: ${result.uncertainty.join('; ')}`);\n }\n return lines.filter(Boolean).join('\\n');\n}\n","import { resolveProviderConfig } from './config.js';\nimport { CooldownController } from './providers/cooldown.js';\nimport { ProviderRegistry } from './providers/registry.js';\nimport { TinyfishProvider } from './providers/tinyfish.js';\nimport { TavilyProvider } from './providers/tavily.js';\nimport { ExaProvider } from './providers/exa.js';\nimport { FirecrawlProvider } from './providers/firecrawl.js';\nimport { CodexProvider } from './providers/codex.js';\nimport { DemoProvider } from './providers/demo.js';\nimport type { PluginConfig } from './config.js';\nimport { registerReadPageTool } from './tools/read-page.js';\nimport { registerWebSearchProvider } from './tools/web-search.js';\n\nexport const name = 'search-providers';\nexport const inject = ['tools', 'web'];\n\nconst DEFAULT_PROVIDER_ORDER = ['codex', 'tinyfish', 'tavily', 'exa', 'firecrawl'];\n\nexport function apply(ctx: any, rawConfig: PluginConfig = {}): void {\n const registry = new ProviderRegistry();\n const cooldown = new CooldownController();\n const demoMode = rawConfig.provider === 'demo' || process.env.SEARCH_PROVIDERS_DEMO === '1';\n\n // Register providers in priority order.\n registry.register(new CodexProvider(resolveProviderConfig(rawConfig, 'codex', 'CODEX')));\n registry.register(new TinyfishProvider(resolveProviderConfig(rawConfig, 'tinyfish', 'TINYFISH')));\n registry.register(new TavilyProvider(resolveProviderConfig(rawConfig, 'tavily', 'TAVILY')));\n registry.register(new ExaProvider(resolveProviderConfig(rawConfig, 'exa', 'EXA')));\n registry.register(new FirecrawlProvider(resolveProviderConfig(rawConfig, 'firecrawl', 'FIRECRAWL')));\n\n if (demoMode) {\n registry.register(new DemoProvider(resolveProviderConfig(rawConfig, 'tinyfish', 'TINYFISH')));\n }\n\n // Determine active provider.\n const activeProvider = rawConfig.provider ?? (demoMode ? 'demo' : undefined);\n if (activeProvider) {\n try {\n registry.setActive(activeProvider);\n } catch (error) {\n console.warn(`[search-providers] configured provider '${activeProvider}' is not registered; falling back to '${registry.list()[0]}'`);\n }\n }\n\n // The active provider is always tried first. An explicit empty chain disables fallback.\n const fallbackChain = rawConfig.fallbackChain ?? DEFAULT_PROVIDER_ORDER;\n\n // Register tools.\n registerWebSearchProvider(ctx, registry, cooldown, fallbackChain);\n registerReadPageTool(ctx, registry, cooldown, fallbackChain);\n\n console.log(`[search-providers] loaded with active provider: ${registry.getActive().id}, fallback after active: [${fallbackChain.join(', ')}]`);\n}"],"names":["throwIfAborted","resolve","provider","cooling","disabled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/config.ts","../src/providers/utils.ts","../src/providers/cooldown.ts","../src/providers/registry.ts","../src/providers/tinyfish.ts","../src/providers/tavily.ts","../src/providers/exa.ts","../src/providers/firecrawl.ts","../src/providers/codex.ts","../src/providers/demo.ts","../src/schemas/fetch-schema.ts","../src/tools/fallback-utils.ts","../src/tools/read-page.ts","../src/tools/web-search.ts","../src/index.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\n\nexport interface ProviderConfigEntry {\n apiKey?: string;\n credentialFile?: string;\n timeoutMs?: number;\n options?: Record<string, unknown>;\n}\n\n/** Cordis only selects the file; provider settings live in that file. */\nexport interface PluginConfig {\n configFile?: string;\n}\n\nexport interface SearchProvidersConfig {\n /** Active provider id. */\n provider?: string;\n providers?: Record<string, ProviderConfigEntry>;\n /** An empty array disables fallback. */\n fallbackChain?: string[];\n}\n\nexport interface ResolvedProviderConfig {\n apiKey?: string;\n credentialFile?: string;\n timeoutMs: number;\n options: Record<string, unknown>;\n}\n\nconst DEFAULT_TIMEOUT_MS = 55_000;\n\nexport function loadPluginConfig(pluginConfig: PluginConfig = {}): SearchProvidersConfig {\n if (!isRecord(pluginConfig) || Object.keys(pluginConfig).some((key) => key !== 'configFile')) {\n throw new Error('search-providers: Cordis config only accepts configFile. Move provider, providers and fallbackChain to search-providers.json.');\n }\n if (pluginConfig.configFile !== undefined && !isNonEmptyString(pluginConfig.configFile)) {\n throw new Error('search-providers: configFile must be a non-empty string.');\n }\n\n const explicit = pluginConfig.configFile !== undefined;\n const filename = resolveConfigPath(pluginConfig.configFile ?? join(homedir(), '.dsh', 'search-providers.json'));\n let text: string;\n try {\n text = readFileSync(filename, 'utf8');\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (!explicit && code === 'ENOENT') {\n console.warn(\n '[search-providers] Config file not found: ' + filename + '. Create it to configure providers; environment variables are not used.',\n );\n return {};\n }\n throw new Error('search-providers: Cannot read config file ' + filename + ' (' + (code ?? 'read error') + ').');\n }\n\n let value: unknown;\n try {\n // Accept a UTF-8 BOM, including files written by Windows editors.\n value = JSON.parse(text.charCodeAt(0) === 0xfeff ? text.slice(1) : text);\n } catch {\n // JSON parser messages can contain secrets from the source; do not forward them.\n throw new Error('search-providers: Invalid JSON in ' + filename + '.');\n }\n validateConfig(value, filename);\n for (const entry of Object.values(value.providers ?? {})) {\n if (entry.credentialFile !== undefined) {\n entry.credentialFile = resolveConfigPath(entry.credentialFile, dirname(filename));\n }\n }\n return value;\n}\n\nexport function resolveProviderConfig(\n pluginConfig: SearchProvidersConfig,\n providerId: string,\n): ResolvedProviderConfig {\n const pc = pluginConfig.providers?.[providerId] ?? {};\n return {\n apiKey: pc.apiKey,\n credentialFile: pc.credentialFile,\n timeoutMs: pc.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n options: pc.options ?? {},\n };\n}\n\nfunction resolveConfigPath(filename: string, base = process.cwd()): string {\n const path = filename.trim();\n if (path === '~') return homedir();\n if (path.startsWith('~/') || path.startsWith('~\\\\')) {\n return resolve(homedir(), path.slice(2));\n }\n return resolve(base, path);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction validateConfig(value: unknown, filename: string): asserts value is SearchProvidersConfig {\n function invalid(field: string): never {\n throw new Error('search-providers: Invalid config in ' + filename + ': ' + field + '.');\n }\n if (!isRecord(value)) invalid('expected a JSON object');\n // No values are interpolated into validation errors (they may be credentials).\n if (Object.keys(value).some((key) => !['provider', 'providers', 'fallbackChain'].includes(key))) {\n invalid('unknown top-level field; use provider, providers or fallbackChain');\n }\n if (value.provider !== undefined && !isNonEmptyString(value.provider)) {\n invalid('provider must be a non-empty string');\n }\n if (value.fallbackChain !== undefined &&\n (!Array.isArray(value.fallbackChain) || !value.fallbackChain.every(isNonEmptyString))) {\n invalid('fallbackChain must be an array of non-empty strings');\n }\n if (value.providers === undefined) return;\n if (!isRecord(value.providers)) invalid('providers must be an object');\n for (const entry of Object.values(value.providers)) {\n if (!isRecord(entry)) invalid('each provider must be an object');\n if (Object.keys(entry).some((key) => !['apiKey', 'credentialFile', 'timeoutMs', 'options'].includes(key))) {\n invalid('unknown provider field; use apiKey, credentialFile, timeoutMs or options (official service URLs are built in)');\n }\n if (entry.apiKey !== undefined && typeof entry.apiKey !== 'string') invalid('apiKey must be a string');\n if (entry.credentialFile !== undefined && !isNonEmptyString(entry.credentialFile)) {\n invalid('credentialFile must be a non-empty string');\n }\n if (entry.timeoutMs !== undefined &&\n (typeof entry.timeoutMs !== 'number' || !Number.isSafeInteger(entry.timeoutMs) || entry.timeoutMs <= 0 || entry.timeoutMs > 2_147_483_647)) {\n invalid('timeoutMs must be a positive integer no greater than 2147483647');\n }\n if (entry.options !== undefined) {\n if (!isRecord(entry.options)) invalid('options must be an object');\n if (Object.hasOwn(entry.options, 'fetchBaseUrl')) {\n invalid('remove options.fetchBaseUrl; official service URLs are built in');\n }\n }\n }\n}\n","export class ProviderAuthenticationError extends Error {\n readonly kind = 'provider-auth' as const;\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ProviderAuthenticationError';\n }\n}\n\nexport class ProviderHttpError extends Error {\n readonly kind = 'provider-http' as const;\n\n constructor(\n message: string,\n readonly status: number,\n options?: ErrorOptions,\n ) {\n super(message, options);\n this.name = 'ProviderHttpError';\n }\n}\n\nexport async function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n): Promise<Response> {\n const controller = new AbortController();\n const timer = setTimeout(\n () => controller.abort(new Error(`request timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n try {\n const signal = init.signal\n ? combineSignals(init.signal, controller.signal)\n : controller.signal;\n return await fetch(url, { ...init, signal });\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function combineSignals(a: AbortSignal, b: AbortSignal): AbortSignal {\n return AbortSignal.any([a, b]);\n}\n\nexport async function throwOnError(response: Response, label: string): Promise<void> {\n if (response.ok) return;\n const text = await response.text().catch(() => '');\n let detail = text.slice(0, 500);\n if (response.status === 401) {\n detail = 'Invalid or missing API key';\n } else if (response.status === 429) {\n detail = 'Rate limit exceeded';\n } else if (response.status === 432) {\n detail = 'Usage limit exceeded';\n }\n const message = `${label} returned ${response.status}: ${detail}`;\n if (response.status === 401) {\n throw new ProviderAuthenticationError(message);\n }\n throw new ProviderHttpError(message, response.status);\n}\n","import { ProviderAuthenticationError, ProviderHttpError } from './utils.js';\n\nexport type ProviderCapability = 'search' | 'fetch';\n\nexport interface CooldownEntry {\n until: number;\n reason: string;\n}\n\nexport class CooldownController {\n private disabled = new Map<string, CooldownEntry>();\n private cooling = new Map<string, CooldownEntry>();\n\n /** Record one capability failure. Confirmed authentication failures are provider-global. */\n record(providerId: string, capability: ProviderCapability, error: unknown): boolean {\n if (error instanceof ProviderAuthenticationError) {\n this.disabled.set(providerId, {\n until: Number.POSITIVE_INFINITY,\n reason: 'invalid credentials',\n });\n return true;\n }\n\n this.cooling.set(this.capabilityKey(providerId, capability), {\n until: Date.now() + this.cooldownDuration(error),\n reason: this.classifyError(error),\n });\n return true;\n }\n\n /** Check whether one provider capability is temporarily cooling. */\n isCooling(providerId: string, capability: ProviderCapability): boolean {\n return this.getEntry(this.cooling, this.capabilityKey(providerId, capability)) !== undefined;\n }\n\n /** Check whether confirmed authentication failure disabled the provider until restart. */\n isDisabled(providerId: string): boolean {\n return this.getEntry(this.disabled, providerId) !== undefined;\n }\n\n /** Get the current failure reason, if any. */\n getReason(providerId: string, capability: ProviderCapability): string | undefined {\n return this.getEntry(this.disabled, providerId)?.reason\n ?? this.getEntry(this.cooling, this.capabilityKey(providerId, capability))?.reason;\n }\n\n /** Build candidates while separating global disablement from capability cooldown. */\n filter(\n chain: string[],\n capability: ProviderCapability,\n ): { ready: string[]; cooling: string[]; disabled: string[] } {\n const ready: string[] = [];\n const cooling: string[] = [];\n const disabled: string[] = [];\n for (const id of chain) {\n if (this.isDisabled(id)) {\n disabled.push(id);\n } else if (this.isCooling(id, capability)) {\n cooling.push(id);\n } else {\n ready.push(id);\n }\n }\n return { ready, cooling, disabled };\n }\n\n private capabilityKey(providerId: string, capability: ProviderCapability): string {\n return `${providerId}:${capability}`;\n }\n\n private getEntry(map: Map<string, CooldownEntry>, key: string): CooldownEntry | undefined {\n const entry = map.get(key);\n if (!entry) return undefined;\n if (Date.now() >= entry.until) {\n map.delete(key);\n return undefined;\n }\n return entry;\n }\n\n private classifyError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (this.isRateLimitError(error, message)) return 'rate limit or quota exceeded';\n if (/timeout|timed out/i.test(message)) return 'timeout';\n if (/network|ENOTFOUND|ECONNREFUSED|fetch failed/i.test(message)) return 'network error';\n return 'request failed';\n }\n\n private cooldownDuration(error: unknown): number {\n const message = error instanceof Error ? error.message : String(error);\n // Rate limits / quotas: cool for 5 minutes.\n if (this.isRateLimitError(error, message)) return 5 * 60 * 1000;\n // Timeouts / network errors: cool for 30 seconds.\n if (/timeout|timed out|network|ENOTFOUND|ECONNREFUSED|fetch failed/i.test(message)) {\n return 30 * 1000;\n }\n // Generic failures: cool for 10 seconds.\n return 10 * 1000;\n }\n\n private isRateLimitError(error: unknown, message: string): boolean {\n return (\n (error instanceof ProviderHttpError && (error.status === 429 || error.status === 432))\n || /429|432|rate.?limit|quota|usage limit|out of credits|monthly quota/i.test(message)\n );\n }\n}\n","import type { SearchProvider } from './types.js';\n\nexport class ProviderRegistry {\n private providers = new Map<string, SearchProvider>();\n private activeId: string | undefined;\n\n register(provider: SearchProvider): void {\n if (this.providers.has(provider.id)) {\n console.warn(`[search-providers] provider '${provider.id}' already registered; overwriting`);\n }\n this.providers.set(provider.id, provider);\n if (this.activeId === undefined) {\n this.activeId = provider.id;\n }\n }\n\n setActive(id: string): void {\n if (!this.providers.has(id)) {\n throw new Error(`Provider '${id}' is not registered`);\n }\n this.activeId = id;\n }\n\n getActive(): SearchProvider {\n if (this.activeId === undefined) {\n throw new Error('No search provider registered');\n }\n const provider = this.providers.get(this.activeId);\n if (!provider) {\n throw new Error(`Active provider '${this.activeId}' disappeared`);\n }\n return provider;\n }\n\n get(id: string): SearchProvider | undefined {\n return this.providers.get(id);\n }\n\n list(): string[] {\n return Array.from(this.providers.keys());\n }\n}\n","import type { ResolvedProviderConfig } from '../config.js';\nimport type {\n FetchRequest,\n FetchResult,\n SearchProvider,\n SearchRequest,\n SearchResult,\n} from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface TinyfishSearchResponse {\n query: string;\n results: Array<{\n position: number;\n site_name?: string;\n title: string;\n snippet: string;\n url: string;\n }>;\n total_results: number;\n page: number;\n}\n\ninterface TinyfishFetchResponse {\n results: Array<{\n url: string;\n final_url?: string;\n title?: string;\n description?: string;\n language?: string;\n format?: string;\n text: string;\n }>;\n errors: Array<{ url: string; error: string }>;\n}\n\nexport class TinyfishProvider implements SearchProvider {\n readonly id = 'tinyfish';\n readonly name = 'TinyFish';\n readonly description = 'TinyFish Search & Fetch (free tier, live web results)';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly searchBaseUrl = 'https://api.search.tinyfish.ai';\n private readonly fetchBaseUrl = 'https://api.fetch.tinyfish.ai';\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const url = new URL(this.searchBaseUrl);\n url.searchParams.set('query', request.query);\n if (request.maxResults && request.maxResults > 0) {\n url.searchParams.set('max_results', String(Math.floor(request.maxResults)));\n }\n if (typeof request.recencyDays === 'number' && request.recencyDays > 0) {\n url.searchParams.set('recency_minutes', String(Math.floor(request.recencyDays * 24 * 60)));\n }\n if (request.purpose) {\n url.searchParams.set('purpose', request.purpose);\n }\n if (request.location) {\n url.searchParams.set('location', request.location);\n }\n if (request.language) {\n url.searchParams.set('language', request.language);\n }\n if (request.domainType) {\n url.searchParams.set('domain_type', request.domainType);\n }\n if (request.includeDomains && request.includeDomains.length > 0) {\n url.searchParams.set('include_domains', request.includeDomains.join(','));\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n url.searchParams.set('exclude_domains', request.excludeDomains.join(','));\n }\n\n const response = await fetchWithTimeout(url.toString(), {\n method: 'GET',\n headers: {\n 'X-API-Key': this.apiKey!,\n Accept: 'application/json',\n },\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'TinyFish Search');\n\n const body = (await response.json()) as TinyfishSearchResponse;\n const items = (body.results ?? []).map((r) => ({\n title: r.title,\n url: r.url,\n snippet: r.snippet,\n source: r.site_name,\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${body.total_results ?? items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n urls: [request.url],\n format: request.format ?? 'markdown',\n };\n if (request.query) {\n body.purpose = request.query;\n }\n if (typeof request.ttl === 'number') {\n body.ttl = request.ttl;\n }\n\n const response = await fetchWithTimeout(this.fetchBaseUrl, {\n method: 'POST',\n headers: {\n 'X-API-Key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'TinyFish Fetch');\n\n const data = (await response.json()) as TinyfishFetchResponse;\n const page = data.results?.[0];\n const error = data.errors?.[0];\n\n if (!page && error) {\n throw new Error(`TinyFish Fetch failed for ${error.url}: ${error.error}`);\n }\n if (!page) {\n throw new Error('TinyFish Fetch returned no content');\n }\n\n return {\n summary: page.title ?? page.description ?? 'Fetched page content',\n content: page.text ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error(\n 'TinyFish API key is missing. Configure providers.tinyfish.apiKey in the search-providers JSON config file.',\n );\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface TavilySearchResponse {\n query: string;\n results: Array<{\n title: string;\n url: string;\n content?: string;\n raw_content?: string;\n score?: number;\n }>;\n}\n\nexport class TavilyProvider implements SearchProvider {\n readonly id = 'tavily';\n readonly name = 'Tavily';\n readonly description = 'Tavily web search';\n readonly searchable = true;\n readonly fetchable = false;\n\n private readonly apiKey?: string;\n private readonly baseUrl = 'https://api.tavily.com';\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n search_depth: 'basic',\n max_results: request.maxResults ?? 5,\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.include_domains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.exclude_domains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/search`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Tavily Search');\n\n const data = (await response.json()) as TavilySearchResponse;\n const items = (data.results ?? []).map((r) => ({\n title: r.title,\n url: r.url,\n snippet: r.content ?? r.raw_content ?? '',\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Tavily API key is missing. Configure providers.tavily.apiKey in the search-providers JSON config file.');\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface ExaSearchResponse {\n results: Array<{\n title?: string;\n url: string;\n publishedDate?: string;\n author?: string;\n text?: string;\n score?: number;\n }>;\n}\n\ninterface ExaContentsResponse {\n results: Array<{\n url: string;\n text?: string;\n title?: string;\n }>;\n}\n\nexport class ExaProvider implements SearchProvider {\n readonly id = 'exa';\n readonly name = 'Exa';\n readonly description = 'Exa neural web search and contents fetch';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly baseUrl = 'https://api.exa.ai';\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n num_results: request.maxResults ?? 5,\n type: 'auto',\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.include_domains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.exclude_domains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/search`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Exa Search');\n\n const data = (await response.json()) as ExaSearchResponse;\n const items = (data.results ?? []).map((r) => ({\n title: r.title ?? r.url,\n url: r.url,\n snippet: r.text ?? '',\n publishedAt: r.publishedDate,\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body = {\n urls: [request.url],\n text: true,\n };\n\n const response = await fetchWithTimeout(`${this.baseUrl}/contents`, {\n method: 'POST',\n headers: {\n 'x-api-key': this.apiKey!,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Exa Contents');\n\n const data = (await response.json()) as ExaContentsResponse;\n const page = data.results?.[0];\n if (!page) {\n throw new Error('Exa Contents returned no content');\n }\n\n return {\n summary: page.title ?? 'Fetched page content',\n content: page.text ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Exa API key is missing. Configure providers.exa.apiKey in the search-providers JSON config file.');\n }\n }\n}","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { fetchWithTimeout, throwOnError } from './utils.js';\n\ninterface FirecrawlSearchResponse {\n success: boolean;\n data: {\n web: Array<{\n title?: string;\n url: string;\n description?: string;\n markdown?: string;\n }>;\n };\n}\n\ninterface FirecrawlScrapeResponse {\n success: boolean;\n data: {\n markdown?: string;\n metadata?: {\n title?: string;\n sourceURL?: string;\n };\n };\n}\n\nexport class FirecrawlProvider implements SearchProvider {\n readonly id = 'firecrawl';\n readonly name = 'Firecrawl';\n readonly description = 'Firecrawl web search and page scrape';\n readonly searchable = true;\n readonly fetchable = true;\n\n private readonly apiKey?: string;\n private readonly baseUrl = 'https://api.firecrawl.dev';\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n this.apiKey = config.apiKey;\n this.timeoutMs = config.timeoutMs;\n }\n\n available(): boolean {\n return typeof this.apiKey === 'string' && this.apiKey !== '';\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n this.ensureKey();\n\n const body: Record<string, unknown> = {\n query: request.query,\n limit: request.maxResults ?? 5,\n sources: ['web'],\n };\n if (request.includeDomains && request.includeDomains.length > 0) {\n body.includeDomains = request.includeDomains;\n }\n if (request.excludeDomains && request.excludeDomains.length > 0) {\n body.excludeDomains = request.excludeDomains;\n }\n\n const response = await fetchWithTimeout(`${this.baseUrl}/v2/search`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Firecrawl Search');\n\n const data = (await response.json()) as FirecrawlSearchResponse;\n if (data.success === false) {\n throw new Error('Firecrawl Search returned success: false');\n }\n\n const rawItems = data.data?.web ?? [];\n const items = rawItems.map((r) => ({\n title: r.title ?? r.url,\n url: r.url,\n snippet: r.description ?? r.markdown ?? '',\n }));\n\n return {\n status: 'ok',\n source: this.name,\n summary: items.length > 0\n ? `Found ${items.length} result(s) for \"${request.query}\"`\n : `No results found for \"${request.query}\"`,\n items,\n uncertainty: items.length === 0 ? ['No search results returned'] : [],\n };\n }\n\n async fetch(request: FetchRequest, signal?: AbortSignal): Promise<FetchResult> {\n this.ensureKey();\n\n const body = {\n url: request.url,\n formats: ['markdown'],\n };\n\n const response = await fetchWithTimeout(`${this.baseUrl}/v2/scrape`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify(body),\n signal,\n }, this.timeoutMs);\n\n await throwOnError(response, 'Firecrawl Scrape');\n\n const data = (await response.json()) as FirecrawlScrapeResponse;\n if (data.success === false) {\n throw new Error('Firecrawl Scrape returned success: false');\n }\n\n return {\n summary: data.data?.metadata?.title ?? 'Fetched page content',\n content: data.data?.markdown ?? '',\n uncertainty: [],\n warnings: [],\n };\n }\n\n private ensureKey(): void {\n if (!this.apiKey) {\n throw new Error('Firecrawl API key is missing. Configure providers.firecrawl.apiKey in the search-providers JSON config file.');\n }\n }\n}","// Codex search protocol behavior is based on dsh-codex-connect (Apache-2.0).\n// This provider only consumes an existing static credential; it never logs in,\n// refreshes tokens, or writes credential data.\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync, statSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport type { ResolvedProviderConfig } from '../config.js';\nimport type { SearchProvider, SearchRequest, SearchResult } from './types.js';\nimport { ProviderAuthenticationError } from './utils.js';\n\nconst CODEX_SEARCH_URL = 'https://chatgpt.com/backend-api/codex/alpha/search';\nconst MAX_CREDENTIAL_BYTES = 128 * 1024;\nconst MAX_SUCCESS_RESPONSE_BYTES = 4 * 1024 * 1024;\nconst MAX_ERROR_RESPONSE_BYTES = 64 * 1024;\n\ninterface StaticCodexCredential {\n accessToken: string;\n accountId: string;\n}\n\ninterface CodexSearchRequestBody {\n id: string;\n model: string;\n input: Array<{\n type: 'message';\n role: 'user';\n content: Array<{ type: 'input_text'; text: string }>;\n }>;\n commands: { search_query: Array<{ q: string }> };\n settings: {\n search_context_size: 'medium';\n allowed_callers: ['direct'];\n external_web_access: false;\n };\n max_output_tokens: number;\n}\n\nexport class CodexProvider implements SearchProvider {\n readonly id = 'codex';\n readonly name = 'OpenAI Codex';\n readonly description = 'ChatGPT Codex standalone web search';\n readonly searchable = true;\n readonly fetchable = false;\n\n private readonly credentialFile?: string;\n private readonly timeoutMs: number;\n\n constructor(config: ResolvedProviderConfig) {\n const filename = readNonEmptyString(config.credentialFile);\n this.credentialFile = filename === undefined ? undefined : resolve(filename);\n this.timeoutMs = Number.isFinite(config.timeoutMs) && config.timeoutMs > 0\n ? config.timeoutMs\n : 55_000;\n }\n\n available(): boolean {\n return this.credentialFile !== undefined\n && loadStaticCredential(this.credentialFile) !== undefined;\n }\n\n async search(request: SearchRequest, signal?: AbortSignal): Promise<SearchResult> {\n throwIfAborted(signal);\n\n const credential = this.credentialFile === undefined\n ? undefined\n : loadStaticCredential(this.credentialFile);\n if (!credential) {\n throw new Error('OpenAI Codex credential file is not configured, missing or invalid');\n }\n\n const body: CodexSearchRequestBody = {\n id: randomUUID(),\n model: 'gpt-5.6-sol',\n input: [{\n type: 'message',\n role: 'user',\n content: [{ type: 'input_text', text: request.query }],\n }],\n commands: { search_query: [{ q: request.query }] },\n settings: {\n search_context_size: 'medium',\n allowed_callers: ['direct'],\n external_web_access: false,\n },\n max_output_tokens: 10_000,\n };\n\n const requestTimeout = AbortSignal.timeout(this.timeoutMs);\n const requestSignal = signal ? AbortSignal.any([signal, requestTimeout]) : requestTimeout;\n let response: Response;\n try {\n response = await fetch(CODEX_SEARCH_URL, {\n method: 'POST',\n redirect: 'error',\n headers: {\n Authorization: `Bearer ${credential.accessToken}`,\n 'chatgpt-account-id': credential.accountId,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n originator: 'deepseek-harness',\n },\n body: JSON.stringify(body),\n signal: requestSignal,\n });\n } catch (error) {\n throwIfAborted(signal);\n if (requestTimeout.aborted) {\n throw new Error(`OpenAI Codex search timed out after ${this.timeoutMs}ms`, { cause: error });\n }\n throw new Error('OpenAI Codex search request failed', { cause: error });\n }\n\n let payload: unknown;\n try {\n payload = await readBoundedJson(\n response,\n response.ok ? MAX_SUCCESS_RESPONSE_BYTES : MAX_ERROR_RESPONSE_BYTES,\n requestSignal,\n );\n } catch (error) {\n throwIfAborted(signal);\n if (requestTimeout.aborted) {\n throw new Error(`OpenAI Codex search timed out after ${this.timeoutMs}ms`, { cause: error });\n }\n if (error instanceof ResponseTooLargeError) throw error;\n throw new Error(`OpenAI Codex returned invalid JSON (HTTP ${response.status})`, { cause: error });\n }\n\n if (!response.ok) {\n const detail = providerMessage(payload);\n const suffix = detail ? `: ${detail}` : '';\n if (response.status === 401 || response.status === 403) {\n throw new ProviderAuthenticationError(\n `OpenAI Codex credential is invalid or expired (HTTP ${response.status})${suffix}`,\n );\n }\n throw new Error(`OpenAI Codex search failed (HTTP ${response.status})${suffix}`);\n }\n\n return mapCodexSearchResponse(payload);\n }\n}\n\nfunction loadStaticCredential(filename: string): StaticCodexCredential | undefined {\n try {\n const stats = statSync(filename);\n if (!stats.isFile() || stats.size <= 0 || stats.size > MAX_CREDENTIAL_BYTES) return undefined;\n const value: unknown = JSON.parse(readFileSync(filename, 'utf8'));\n return parseStaticCredential(value);\n } catch {\n return undefined;\n }\n}\n\nfunction mapCodexSearchResponse(value: unknown): SearchResult {\n if (!isRecord(value) || typeof value.output !== 'string') {\n throw new Error('OpenAI Codex returned a search response without string output');\n }\n if (value.results !== undefined && !Array.isArray(value.results)) {\n throw new Error('OpenAI Codex returned a search response with non-array results');\n }\n\n const items: SearchResult['items'] = [];\n const seen = new Set<string>();\n for (const item of value.results ?? []) {\n if (!isRecord(item) || item.type !== 'text_result') continue;\n const url = citeableUrl(item.url);\n if (!url || seen.has(url)) continue;\n seen.add(url);\n items.push({\n title: readNonEmptyString(item.title) ?? url,\n url,\n snippet: readNonEmptyString(item.snippet) ?? '',\n });\n }\n\n const summary = value.output;\n return {\n status: summary.length > 0 ? 'ok' : 'degraded',\n source: 'OpenAI Codex',\n summary: summary || `Codex returned ${items.length} source(s) without summary text`,\n items,\n uncertainty: summary.length > 0 ? [] : ['Codex returned no summary text'],\n };\n}\n\nfunction parseStaticCredential(value: unknown): StaticCodexCredential | undefined {\n if (!isRecord(value)) return undefined;\n\n const candidates = [\n value,\n isRecord(value.credential) ? value.credential : undefined,\n isRecord(value.tokens) ? value.tokens : undefined,\n ];\n\n for (const candidate of candidates) {\n if (!candidate) continue;\n const accessToken = readNonEmptyString(candidate.access_token)\n ?? readNonEmptyString(candidate.access);\n if (!accessToken) continue;\n\n const accountId = readNonEmptyString(candidate.account_id)\n ?? readNonEmptyString(candidate.accountId)\n ?? accountIdFromToken(accessToken);\n if (!accountId) continue;\n\n return { accessToken, accountId };\n }\n\n return undefined;\n}\n\nfunction decodeJwtPayload(accessToken: string): Record<string, unknown> | undefined {\n try {\n const parts = accessToken.split('.');\n if (parts.length !== 3 || !parts[1]) return undefined;\n const payload: unknown = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));\n return isRecord(payload) ? payload : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction accountIdFromToken(accessToken: string): string | undefined {\n const auth = decodeJwtPayload(accessToken)?.['https://api.openai.com/auth'];\n return isRecord(auth) ? readNonEmptyString(auth.chatgpt_account_id) : undefined;\n}\n\nclass ResponseTooLargeError extends Error {\n constructor(limit: number) {\n super(`OpenAI Codex search response exceeded ${limit} bytes`);\n this.name = 'ResponseTooLargeError';\n }\n}\n\nasync function readBoundedJson(\n response: Response,\n maxBytes: number,\n signal: AbortSignal,\n): Promise<unknown> {\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n await response.body?.cancel().catch(() => {});\n throw new ResponseTooLargeError(maxBytes);\n }\n if (!response.body) throw new SyntaxError('empty response body');\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n try {\n while (true) {\n const { done, value } = await abortable(reader.read(), signal);\n if (done) break;\n if (!value) continue;\n total += value.byteLength;\n if (total > maxBytes) throw new ResponseTooLargeError(maxBytes);\n chunks.push(value);\n }\n } catch (error) {\n await reader.cancel().catch(() => {});\n throw error;\n } finally {\n reader.releaseLock();\n }\n\n return JSON.parse(Buffer.concat(chunks, total).toString('utf8')) as unknown;\n}\n\nfunction abortable<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(abortedError(signal));\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => reject(abortedError(signal));\n signal.addEventListener('abort', onAbort, { once: true });\n operation.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw abortedError(signal);\n}\n\nfunction abortedError(signal: AbortSignal): Error {\n return new Error('OpenAI Codex search aborted', { cause: signal.reason });\n}\n\nfunction providerMessage(value: unknown): string | undefined {\n if (!isRecord(value)) return undefined;\n const raw = typeof value.error === 'string'\n ? value.error\n : isRecord(value.error) && typeof value.error.message === 'string'\n ? value.error.message\n : typeof value.message === 'string'\n ? value.message\n : undefined;\n return raw\n ?.replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu, '[REDACTED]')\n .slice(0, 1_000);\n}\n\nfunction citeableUrl(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined;\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction readNonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;\n}\n\n","import type { ResolvedProviderConfig } from '../config.js';\nimport type { FetchRequest, FetchResult, SearchProvider, SearchRequest, SearchResult } from './types.js';\n\nexport class DemoProvider implements SearchProvider {\n readonly id = 'demo';\n readonly name = 'Demo Provider';\n readonly description = 'Returns canned results for integration testing without calling any external API.';\n readonly searchable = true;\n readonly fetchable = true;\n\n constructor(_config: ResolvedProviderConfig) {}\n\n available(): boolean {\n return true;\n }\n\n async search(request: SearchRequest): Promise<SearchResult> {\n return {\n status: 'ok',\n source: this.name,\n summary: `Demo search results for \"${request.query}\"`,\n items: [\n {\n title: 'Demo Result 1',\n url: 'https://example.com/demo1',\n snippet: 'This is a demo search result used to verify the DSH plugin wiring.',\n },\n {\n title: 'Demo Result 2',\n url: 'https://example.com/demo2',\n snippet: 'Another canned result so citation cards have something to render.',\n },\n ],\n uncertainty: ['Demo mode: results are not from a real search engine.'],\n };\n }\n\n async fetch(request: FetchRequest): Promise<FetchResult> {\n return {\n summary: `Demo fetch of ${request.url}`,\n content: `# Demo Page\\n\\nThis is canned content for the URL \"${request.url}\".`,\n links: [\n { text: 'Example', url: 'https://example.com' },\n ],\n uncertainty: ['Demo mode: content is not from a real web page.'],\n warnings: [],\n };\n }\n}","export const fetchSchema = {\n type: 'object' as const,\n properties: {\n summary: { type: 'string' as const },\n content: { type: 'string' as const },\n links: {\n type: 'array' as const,\n items: {\n type: 'object' as const,\n properties: {\n text: { type: 'string' as const },\n url: { type: 'string' as const }\n },\n required: ['text', 'url'] as const\n }\n },\n uncertainty: {\n type: 'array' as const,\n items: { type: 'string' as const }\n },\n warnings: {\n type: 'array' as const,\n items: { type: 'string' as const }\n }\n },\n required: ['summary', 'content', 'uncertainty'] as const\n};\n","import type { ProviderRegistry } from '../providers/registry.js';\n\nexport const FALLBACK_ATTEMPT_TIMEOUT_MS = 55_000;\n\nexport interface AttemptContext {\n signal: AbortSignal;\n timedOut(): boolean;\n dispose(): void;\n}\n\nexport function resolveCandidateIds(\n registry: ProviderRegistry,\n fallbackChain?: string[],\n): string[] {\n let activeId: string | undefined;\n try {\n activeId = registry.getActive().id;\n } catch {\n // An empty registry has no active provider.\n }\n\n const configured = fallbackChain === undefined ? registry.list() : fallbackChain;\n const seen = new Set<string>();\n const candidates: string[] = [];\n for (const id of activeId ? [activeId, ...configured] : configured) {\n if (seen.has(id)) continue;\n seen.add(id);\n candidates.push(id);\n }\n return candidates;\n}\n\nexport function createAttemptContext(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): AttemptContext {\n const controller = new AbortController();\n let didTimeOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onParentAbort: (() => void) | undefined;\n\n if (parent?.aborted) {\n controller.abort(parent.reason);\n } else {\n if (parent) {\n onParentAbort = () => controller.abort(parent.reason);\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n timer = setTimeout(() => {\n didTimeOut = true;\n controller.abort(new Error(`fallback attempt timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n }\n\n return {\n signal: controller.signal,\n timedOut: () => didTimeOut,\n dispose() {\n if (timer !== undefined) clearTimeout(timer);\n if (parent && onParentAbort) parent.removeEventListener('abort', onParentAbort);\n },\n };\n}\n\nexport function raceWithSignal<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n if (!signal) return operation;\n if (signal.aborted) {\n operation.catch(() => {});\n return Promise.reject(signal.reason);\n }\n\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener('abort', onAbort);\n reject(signal.reason);\n };\n signal.addEventListener('abort', onAbort, { once: true });\n operation.then(\n (value) => {\n signal.removeEventListener('abort', onAbort);\n resolve(value);\n },\n (error) => {\n signal.removeEventListener('abort', onAbort);\n reject(error);\n },\n );\n });\n}\n\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw signal.reason;\n}\n","import type { CooldownController } from '../providers/cooldown.js';\nimport type { ProviderRegistry } from '../providers/registry.js';\nimport type { FetchRequest, FetchResult } from '../providers/types.js';\nimport { ProviderAuthenticationError } from '../providers/utils.js';\nimport { fetchSchema } from '../schemas/fetch-schema.js';\nimport {\n createAttemptContext,\n FALLBACK_ATTEMPT_TIMEOUT_MS,\n raceWithSignal,\n resolveCandidateIds,\n throwIfAborted,\n} from './fallback-utils.js';\n\nconst READ_PAGE_TIMEOUT_MS = 180_000;\nconst RENDER_CONTENT_CAP = 20_000;\nconst RENDER_LINK_CAP = 20;\n\nexport function registerReadPageTool(\n ctx: any,\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain?: string[],\n): void {\n if (typeof ctx.tools?.register !== 'function') {\n console.error('[search-providers] ctx.tools.register is not available; read_page tool skipped');\n return;\n }\n\n ctx.tools.register({\n name: 'read_page',\n description:\n 'Read one web page through the configured search provider. Use when a message references a specific http(s) URL whose content matters: docs, an article, a changelog, a thread. Returns structured evidence with a summary, the extracted content, outgoing links, uncertainty, and operational warnings. Pass \"query\" to focus the reading on one question.',\n parameters: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'The http(s) URL to read',\n },\n query: {\n type: 'string',\n description: 'Optional question to focus the reading on (e.g. \"what are the rate limits\")',\n },\n },\n required: ['url'],\n },\n output: {\n schema: fetchSchema,\n render: (_args: unknown, value: Record<string, unknown>) => [\n { type: 'text', text: renderFetchEvidence(value) },\n ],\n },\n timeoutMs: READ_PAGE_TIMEOUT_MS + 20_000,\n isConcurrencySafe: () => true,\n presentCall: (args: { url: string; query?: string }) => ({\n card: 'generic',\n title: 'read_page',\n kind: 'fetch',\n rawInput: args,\n }),\n async execute(args: { url: string; query?: string }, exec: { signal?: AbortSignal }) {\n if (typeof args?.url !== 'string' || !/^https?:\\/\\//i.test(args.url.trim())) {\n throw new Error('read_page needs an http(s) \"url\".');\n }\n\n const request: FetchRequest = {\n url: args.url.trim(),\n query: args.query,\n format: 'markdown',\n };\n\n return fetchWithFallback(registry, cooldown, fallbackChain, request, exec.signal);\n },\n });\n}\n\nfunction resolveFetchCandidateIds(\n registry: ProviderRegistry,\n fallbackChain: string[] | undefined,\n): string[] {\n return resolveCandidateIds(registry, fallbackChain).filter((id) => {\n const provider = registry.get(id);\n return provider !== undefined\n && provider.fetchable !== false\n && typeof provider.fetch === 'function';\n });\n}\n\nasync function fetchWithFallback(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n request: FetchRequest,\n signal: AbortSignal | undefined,\n): Promise<FetchResult> {\n throwIfAborted(signal);\n const candidates = resolveFetchCandidateIds(registry, fallbackChain);\n const attempted: string[] = [];\n const attemptedIds = new Set<string>();\n let lastError: Error | undefined;\n\n while (true) {\n throwIfAborted(signal);\n const { ready } = cooldown.filter(candidates, 'fetch');\n const id = ready.find((candidateId) => {\n if (attemptedIds.has(candidateId)) return false;\n const provider = registry.get(candidateId);\n return provider?.available()\n && provider.fetchable !== false\n && typeof provider.fetch === 'function';\n });\n if (id === undefined) break;\n\n const provider = registry.get(id)!;\n attemptedIds.add(id);\n const attempt = attempted.length === 0\n ? undefined\n : createAttemptContext(signal, FALLBACK_ATTEMPT_TIMEOUT_MS);\n\n try {\n const attemptSignal = attempt?.signal ?? signal;\n const result = await raceWithSignal(provider.fetch!(request, attemptSignal), attemptSignal);\n throwIfAborted(signal);\n if (attempt?.timedOut()) throw attempt.signal.reason;\n const { cooling, disabled } = cooldown.filter(candidates, 'fetch');\n if (attempted.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Fallback used after ${attempted.join(', ')} failed`);\n }\n if (cooling.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Cooling providers skipped: ${cooling.join(', ')}`);\n }\n if (disabled.length > 0) {\n result.warnings = result.warnings ?? [];\n result.warnings.push(`Providers disabled until restart: ${disabled.join(', ')}`);\n }\n return result;\n } catch (error) {\n throwIfAborted(signal);\n const caught = error instanceof Error ? error : new Error(String(error));\n lastError = caught instanceof ProviderAuthenticationError\n ? caught\n : attempt?.timedOut()\n ? new Error(`fallback attempt timed out after ${FALLBACK_ATTEMPT_TIMEOUT_MS}ms`, { cause: caught })\n : caught;\n attempted.push(id);\n console.warn(`[search-providers] provider '${id}' fetch failed, trying next: ${lastError.message}`);\n cooldown.record(id, 'fetch', lastError);\n } finally {\n attempt?.dispose();\n }\n }\n\n const { cooling, disabled } = cooldown.filter(candidates, 'fetch');\n const coolingIds = cooling.length > 0 ? ` [cooling: ${cooling.join(', ')}]` : '';\n const disabledIds = disabled.length > 0 ? ` [disabled until restart: ${disabled.join(', ')}]` : '';\n throw new Error(\n `No fetch provider succeeded${attempted.length > 0 ? ` (tried: ${attempted.join(', ')})` : ''}${coolingIds}${disabledIds}. ${lastError?.message ?? 'No provider available.'}`,\n { cause: lastError },\n );\n}\n\nfunction renderFetchEvidence(value: Record<string, unknown>): string {\n const lines: string[] = [String(value.summary ?? '')];\n const content = String((value.content as string) ?? '').trim();\n if (content) {\n lines.push('', 'Content:', content.length > RENDER_CONTENT_CAP ? `${content.slice(0, RENDER_CONTENT_CAP)}…` : content);\n }\n const links = Array.isArray(value.links) ? value.links.slice(0, RENDER_LINK_CAP) : [];\n if (links.length > 0) {\n lines.push('', 'Links:');\n for (const link of links) {\n const text = typeof link === 'object' && link !== null ? String((link as Record<string, unknown>).text ?? '') : '';\n const url = typeof link === 'object' && link !== null ? String((link as Record<string, unknown>).url ?? '') : '';\n lines.push(`- ${text} — ${url}`);\n }\n }\n const uncertainty = Array.isArray(value.uncertainty) ? value.uncertainty : [];\n if (uncertainty.length > 0) {\n lines.push('', `Uncertain: ${uncertainty.join('; ')}`);\n }\n const warnings = Array.isArray(value.warnings) ? value.warnings : [];\n if (warnings.length > 0) {\n lines.push('', `Warnings: ${warnings.join('; ')}`);\n }\n return lines.filter(Boolean).join('\\n');\n}\n","import type { CooldownController } from '../providers/cooldown.js';\nimport type { ProviderRegistry } from '../providers/registry.js';\nimport type { SearchRequest, SearchResult } from '../providers/types.js';\nimport { ProviderAuthenticationError } from '../providers/utils.js';\nimport {\n createAttemptContext,\n FALLBACK_ATTEMPT_TIMEOUT_MS,\n raceWithSignal,\n resolveCandidateIds,\n throwIfAborted,\n} from './fallback-utils.js';\n\ninterface WebSeamSearchRequest {\n query: string;\n maxResults?: number;\n recencyDays?: number;\n purpose?: string;\n location?: string;\n language?: string;\n}\n\nexport function registerWebSearchProvider(\n ctx: any,\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain?: string[],\n): void {\n if (typeof ctx.web?.registerSearchProvider !== 'function') {\n console.error('[search-providers] ctx.web.registerSearchProvider is not available; web_search provider skipped');\n return;\n }\n\n ctx.web.registerSearchProvider({\n id: 'search-providers',\n available: () => anyProviderAvailable(registry, cooldown, fallbackChain),\n async search(request: WebSeamSearchRequest, signal: AbortSignal | undefined) {\n const searchRequest: SearchRequest = {\n query: request.query,\n maxResults: request.maxResults,\n recencyDays: request.recencyDays,\n purpose: request.purpose,\n location: request.location,\n language: request.language,\n };\n\n const { result, attempted, cooling, disabled } = await searchWithFallback(\n registry,\n cooldown,\n fallbackChain,\n searchRequest,\n signal,\n );\n\n return {\n content: renderContent(result, attempted, cooling, disabled),\n sources: result.items.map((item) => ({\n url: item.url,\n ...(item.title ? { title: item.title } : {}),\n ...(item.snippet ? { snippet: item.snippet } : {}),\n ...(item.publishedAt ? { publishedAt: item.publishedAt } : {}),\n })),\n truncated: false,\n };\n },\n });\n}\n\nfunction resolveSearchCandidateIds(\n registry: ProviderRegistry,\n fallbackChain: string[] | undefined,\n): string[] {\n return resolveCandidateIds(registry, fallbackChain).filter((id) => {\n const provider = registry.get(id);\n return provider !== undefined && provider.searchable !== false;\n });\n}\n\nfunction anyProviderAvailable(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n): boolean {\n const candidates = resolveSearchCandidateIds(registry, fallbackChain);\n const { ready } = cooldown.filter(candidates, 'search');\n return ready.some((id) => {\n const provider = registry.get(id);\n return provider?.available() && provider.searchable !== false;\n });\n}\n\ninterface FallbackOutcome {\n result: SearchResult;\n attempted: string[];\n cooling: string[];\n disabled: string[];\n}\n\nasync function searchWithFallback(\n registry: ProviderRegistry,\n cooldown: CooldownController,\n fallbackChain: string[] | undefined,\n request: SearchRequest,\n signal: AbortSignal | undefined,\n): Promise<FallbackOutcome> {\n throwIfAborted(signal);\n const candidates = resolveSearchCandidateIds(registry, fallbackChain);\n const attempted: string[] = [];\n const attemptedIds = new Set<string>();\n let lastError: Error | undefined;\n\n while (true) {\n throwIfAborted(signal);\n const { ready } = cooldown.filter(candidates, 'search');\n const id = ready.find((candidateId) => {\n if (attemptedIds.has(candidateId)) return false;\n const provider = registry.get(candidateId);\n return provider?.available() && provider.searchable !== false;\n });\n if (id === undefined) break;\n\n const provider = registry.get(id)!;\n attemptedIds.add(id);\n const attempt = attempted.length === 0\n ? undefined\n : createAttemptContext(signal, FALLBACK_ATTEMPT_TIMEOUT_MS);\n\n try {\n const attemptSignal = attempt?.signal ?? signal;\n const result = await raceWithSignal(provider.search(request, attemptSignal), attemptSignal);\n throwIfAborted(signal);\n if (attempt?.timedOut()) throw attempt.signal.reason;\n const { cooling, disabled } = cooldown.filter(candidates, 'search');\n return { result, attempted, cooling, disabled };\n } catch (error) {\n throwIfAborted(signal);\n const caught = error instanceof Error ? error : new Error(String(error));\n lastError = caught instanceof ProviderAuthenticationError\n ? caught\n : attempt?.timedOut()\n ? new Error(`fallback attempt timed out after ${FALLBACK_ATTEMPT_TIMEOUT_MS}ms`, { cause: caught })\n : caught;\n attempted.push(id);\n console.warn(`[search-providers] provider '${id}' search failed, trying next: ${lastError.message}`);\n cooldown.record(id, 'search', lastError);\n } finally {\n attempt?.dispose();\n }\n }\n\n const { cooling, disabled } = cooldown.filter(candidates, 'search');\n const coolingIds = cooling.length > 0 ? ` [cooling: ${cooling.join(', ')}]` : '';\n const disabledIds = disabled.length > 0 ? ` [disabled until restart: ${disabled.join(', ')}]` : '';\n throw new Error(\n `No search provider succeeded${attempted.length > 0 ? ` (tried: ${attempted.join(', ')})` : ''}${coolingIds}${disabledIds}. ${lastError?.message ?? 'No provider available.'}`,\n { cause: lastError },\n );\n}\n\nfunction renderContent(\n result: SearchResult,\n attempted: string[],\n cooling: string[],\n disabled: string[],\n): string {\n const lines = [`[Search provider: ${result.source}]`, result.summary];\n if (attempted.length > 0) {\n lines.push(`[Fallback used after ${attempted.join(', ')} failed]`);\n }\n if (cooling.length > 0) {\n lines.push(`[Cooling: ${cooling.join(', ')}]`);\n }\n if (disabled.length > 0) {\n lines.push(`[Disabled until restart: ${disabled.join(', ')}]`);\n }\n if (result.uncertainty.length > 0) {\n lines.push(`Uncertain: ${result.uncertainty.join('; ')}`);\n }\n return lines.filter(Boolean).join('\\n');\n}\n","import { loadPluginConfig, resolveProviderConfig } from './config.js';\nimport { CooldownController } from './providers/cooldown.js';\nimport { ProviderRegistry } from './providers/registry.js';\nimport { TinyfishProvider } from './providers/tinyfish.js';\nimport { TavilyProvider } from './providers/tavily.js';\nimport { ExaProvider } from './providers/exa.js';\nimport { FirecrawlProvider } from './providers/firecrawl.js';\nimport { CodexProvider } from './providers/codex.js';\nimport { DemoProvider } from './providers/demo.js';\nimport type { PluginConfig } from './config.js';\nimport { registerReadPageTool } from './tools/read-page.js';\nimport { registerWebSearchProvider } from './tools/web-search.js';\n\nexport const name = 'search-providers';\nexport const inject = ['tools', 'web'];\n\nconst DEFAULT_PROVIDER_ORDER = ['codex', 'tinyfish', 'tavily', 'exa', 'firecrawl'];\n\nexport function apply(ctx: any, rawConfig: PluginConfig = {}): void {\n const config = loadPluginConfig(rawConfig);\n const registry = new ProviderRegistry();\n const cooldown = new CooldownController();\n const demoMode = config.provider === 'demo';\n\n // Register providers in priority order.\n registry.register(new CodexProvider(resolveProviderConfig(config, 'codex')));\n registry.register(new TinyfishProvider(resolveProviderConfig(config, 'tinyfish')));\n registry.register(new TavilyProvider(resolveProviderConfig(config, 'tavily')));\n registry.register(new ExaProvider(resolveProviderConfig(config, 'exa')));\n registry.register(new FirecrawlProvider(resolveProviderConfig(config, 'firecrawl')));\n\n if (demoMode) {\n registry.register(new DemoProvider(resolveProviderConfig(config, 'tinyfish')));\n }\n\n // Determine active provider.\n const activeProvider = config.provider;\n if (activeProvider) {\n try {\n registry.setActive(activeProvider);\n } catch (error) {\n console.warn(`[search-providers] configured provider '${activeProvider}' is not registered; falling back to '${registry.list()[0]}'`);\n }\n }\n\n // The active provider is always tried first. An explicit empty chain disables fallback.\n const fallbackChain = config.fallbackChain ?? DEFAULT_PROVIDER_ORDER;\n\n // Register tools.\n registerWebSearchProvider(ctx, registry, cooldown, fallbackChain);\n registerReadPageTool(ctx, registry, cooldown, fallbackChain);\n\n console.log(`[search-providers] loaded with active provider: ${registry.getActive().id}, fallback after active: [${fallbackChain.join(', ')}]`);\n}"],"names":["isRecord","throwIfAborted","resolve","provider","cooling","disabled"],"mappings":";;;;AA+BA,MAAM,qBAAqB;AAEpB,SAAS,iBAAiB,eAA6B,IAA2B;AACvF,MAAI,CAACA,WAAS,YAAY,KAAK,OAAO,KAAK,YAAY,EAAE,KAAK,CAAC,QAAQ,QAAQ,YAAY,GAAG;AAC5F,UAAM,IAAI,MAAM,+HAA+H;AAAA,EACjJ;AACA,MAAI,aAAa,eAAe,UAAa,CAAC,iBAAiB,aAAa,UAAU,GAAG;AACvF,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AAEA,QAAM,WAAW,aAAa,eAAe;AAC7C,QAAM,WAAW,kBAAkB,aAAa,cAAc,KAAK,QAAA,GAAW,QAAQ,uBAAuB,CAAC;AAC9G,MAAI;AACJ,MAAI;AACF,WAAO,aAAa,UAAU,MAAM;AAAA,EACtC,SAAS,OAAO;AACd,UAAM,OAAQ,MAAgC;AAC9C,QAAI,CAAC,YAAY,SAAS,UAAU;AAClC,cAAQ;AAAA,QACN,+CAA+C,WAAW;AAAA,MAAA;AAE5D,aAAO,CAAA;AAAA,IACT;AACA,UAAM,IAAI,MAAM,+CAA+C,WAAW,QAAQ,QAAQ,gBAAgB,IAAI;AAAA,EAChH;AAEA,MAAI;AACJ,MAAI;AAEF,YAAQ,KAAK,MAAM,KAAK,WAAW,CAAC,MAAM,QAAS,KAAK,MAAM,CAAC,IAAI,IAAI;AAAA,EACzE,QAAQ;AAEN,UAAM,IAAI,MAAM,uCAAuC,WAAW,GAAG;AAAA,EACvE;AACA,iBAAe,OAAO,QAAQ;AAC9B,aAAW,SAAS,OAAO,OAAO,MAAM,aAAa,CAAA,CAAE,GAAG;AACxD,QAAI,MAAM,mBAAmB,QAAW;AACtC,YAAM,iBAAiB,kBAAkB,MAAM,gBAAgB,QAAQ,QAAQ,CAAC;AAAA,IAClF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,sBACd,cACA,YACwB;AACxB,QAAM,KAAK,aAAa,YAAY,UAAU,KAAK,CAAA;AACnD,SAAO;AAAA,IACL,QAAQ,GAAG;AAAA,IACX,gBAAgB,GAAG;AAAA,IACnB,WAAW,GAAG,aAAa;AAAA,IAC3B,SAAS,GAAG,WAAW,CAAA;AAAA,EAAC;AAE5B;AAEA,SAAS,kBAAkB,UAAkB,OAAO,QAAQ,OAAe;AACzE,QAAM,OAAO,SAAS,KAAA;AACtB,MAAI,SAAS,IAAK,QAAO,QAAA;AACzB,MAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,GAAG;AACnD,WAAO,QAAQ,QAAA,GAAW,KAAK,MAAM,CAAC,CAAC;AAAA,EACzC;AACA,SAAO,QAAQ,MAAM,IAAI;AAC3B;AAEA,SAASA,WAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,iBAAiB,OAAiC;AACzD,SAAO,OAAO,UAAU,YAAY,MAAM,WAAW;AACvD;AAEA,SAAS,eAAe,OAAgB,UAA0D;AAChG,WAAS,QAAQ,OAAsB;AACrC,UAAM,IAAI,MAAM,yCAAyC,WAAW,OAAO,QAAQ,GAAG;AAAA,EACxF;AACA,MAAI,CAACA,WAAS,KAAK,WAAW,wBAAwB;AAEtD,MAAI,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,YAAY,aAAa,eAAe,EAAE,SAAS,GAAG,CAAC,GAAG;AAC/F,YAAQ,mEAAmE;AAAA,EAC7E;AACA,MAAI,MAAM,aAAa,UAAa,CAAC,iBAAiB,MAAM,QAAQ,GAAG;AACrE,YAAQ,qCAAqC;AAAA,EAC/C;AACA,MAAI,MAAM,kBAAkB,WACvB,CAAC,MAAM,QAAQ,MAAM,aAAa,KAAK,CAAC,MAAM,cAAc,MAAM,gBAAgB,IAAI;AACzF,YAAQ,qDAAqD;AAAA,EAC/D;AACA,MAAI,MAAM,cAAc,OAAW;AACnC,MAAI,CAACA,WAAS,MAAM,SAAS,WAAW,6BAA6B;AACrE,aAAW,SAAS,OAAO,OAAO,MAAM,SAAS,GAAG;AAClD,QAAI,CAACA,WAAS,KAAK,WAAW,iCAAiC;AAC/D,QAAI,OAAO,KAAK,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,UAAU,kBAAkB,aAAa,SAAS,EAAE,SAAS,GAAG,CAAC,GAAG;AACzG,cAAQ,+GAA+G;AAAA,IACzH;AACA,QAAI,MAAM,WAAW,UAAa,OAAO,MAAM,WAAW,kBAAkB,yBAAyB;AACrG,QAAI,MAAM,mBAAmB,UAAa,CAAC,iBAAiB,MAAM,cAAc,GAAG;AACjF,cAAQ,2CAA2C;AAAA,IACrD;AACA,QAAI,MAAM,cAAc,WACnB,OAAO,MAAM,cAAc,YAAY,CAAC,OAAO,cAAc,MAAM,SAAS,KAAK,MAAM,aAAa,KAAK,MAAM,YAAY,aAAgB;AAC9I,cAAQ,iEAAiE;AAAA,IAC3E;AACA,QAAI,MAAM,YAAY,QAAW;AAC/B,UAAI,CAACA,WAAS,MAAM,OAAO,WAAW,2BAA2B;AACjE,UAAI,OAAO,OAAO,MAAM,SAAS,cAAc,GAAG;AAChD,gBAAQ,iEAAiE;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AACF;AC9IO,MAAM,oCAAoC,MAAM;AAAA,EAC5C,OAAO;AAAA,EAEhB,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,0BAA0B,MAAM;AAAA,EAG3C,YACE,SACS,QACT,SACA;AACA,UAAM,SAAS,OAAO;AAHb,SAAA,SAAA;AAIT,SAAK,OAAO;AAAA,EACd;AAAA,EATS,OAAO;AAUlB;AAEA,eAAsB,iBACpB,KACA,MACA,WACmB;AACnB,QAAM,aAAa,IAAI,gBAAA;AACvB,QAAM,QAAQ;AAAA,IACZ,MAAM,WAAW,MAAM,IAAI,MAAM,2BAA2B,SAAS,IAAI,CAAC;AAAA,IAC1E;AAAA,EAAA;AAEF,MAAI;AACF,UAAM,SAAS,KAAK,SAChB,eAAe,KAAK,QAAQ,WAAW,MAAM,IAC7C,WAAW;AACf,WAAO,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ;AAAA,EAC7C,UAAA;AACE,iBAAa,KAAK;AAAA,EACpB;AACF;AAEO,SAAS,eAAe,GAAgB,GAA6B;AAC1E,SAAO,YAAY,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/B;AAEA,eAAsB,aAAa,UAAoB,OAA8B;AACnF,MAAI,SAAS,GAAI;AACjB,QAAM,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,EAAE;AACjD,MAAI,SAAS,KAAK,MAAM,GAAG,GAAG;AAC9B,MAAI,SAAS,WAAW,KAAK;AAC3B,aAAS;AAAA,EACX,WAAW,SAAS,WAAW,KAAK;AAClC,aAAS;AAAA,EACX,WAAW,SAAS,WAAW,KAAK;AAClC,aAAS;AAAA,EACX;AACA,QAAM,UAAU,GAAG,KAAK,aAAa,SAAS,MAAM,KAAK,MAAM;AAC/D,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,IAAI,4BAA4B,OAAO;AAAA,EAC/C;AACA,QAAM,IAAI,kBAAkB,SAAS,SAAS,MAAM;AACtD;ACrDO,MAAM,mBAAmB;AAAA,EACtB,+BAAe,IAAA;AAAA,EACf,8BAAc,IAAA;AAAA;AAAA,EAGtB,OAAO,YAAoB,YAAgC,OAAyB;AAClF,QAAI,iBAAiB,6BAA6B;AAChD,WAAK,SAAS,IAAI,YAAY;AAAA,QAC5B,OAAO,OAAO;AAAA,QACd,QAAQ;AAAA,MAAA,CACT;AACD,aAAO;AAAA,IACT;AAEA,SAAK,QAAQ,IAAI,KAAK,cAAc,YAAY,UAAU,GAAG;AAAA,MAC3D,OAAO,KAAK,IAAA,IAAQ,KAAK,iBAAiB,KAAK;AAAA,MAC/C,QAAQ,KAAK,cAAc,KAAK;AAAA,IAAA,CACjC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,YAAoB,YAAyC;AACrE,WAAO,KAAK,SAAS,KAAK,SAAS,KAAK,cAAc,YAAY,UAAU,CAAC,MAAM;AAAA,EACrF;AAAA;AAAA,EAGA,WAAW,YAA6B;AACtC,WAAO,KAAK,SAAS,KAAK,UAAU,UAAU,MAAM;AAAA,EACtD;AAAA;AAAA,EAGA,UAAU,YAAoB,YAAoD;AAChF,WAAO,KAAK,SAAS,KAAK,UAAU,UAAU,GAAG,UAC5C,KAAK,SAAS,KAAK,SAAS,KAAK,cAAc,YAAY,UAAU,CAAC,GAAG;AAAA,EAChF;AAAA;AAAA,EAGA,OACE,OACA,YAC4D;AAC5D,UAAM,QAAkB,CAAA;AACxB,UAAM,UAAoB,CAAA;AAC1B,UAAM,WAAqB,CAAA;AAC3B,eAAW,MAAM,OAAO;AACtB,UAAI,KAAK,WAAW,EAAE,GAAG;AACvB,iBAAS,KAAK,EAAE;AAAA,MAClB,WAAW,KAAK,UAAU,IAAI,UAAU,GAAG;AACzC,gBAAQ,KAAK,EAAE;AAAA,MACjB,OAAO;AACL,cAAM,KAAK,EAAE;AAAA,MACf;AAAA,IACF;AACA,WAAO,EAAE,OAAO,SAAS,SAAA;AAAA,EAC3B;AAAA,EAEQ,cAAc,YAAoB,YAAwC;AAChF,WAAO,GAAG,UAAU,IAAI,UAAU;AAAA,EACpC;AAAA,EAEQ,SAAS,KAAiC,KAAwC;AACxF,UAAM,QAAQ,IAAI,IAAI,GAAG;AACzB,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,KAAK,SAAS,MAAM,OAAO;AAC7B,UAAI,OAAO,GAAG;AACd,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,OAAwB;AAC5C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,QAAI,KAAK,iBAAiB,OAAO,OAAO,EAAG,QAAO;AAClD,QAAI,qBAAqB,KAAK,OAAO,EAAG,QAAO;AAC/C,QAAI,+CAA+C,KAAK,OAAO,EAAG,QAAO;AACzE,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,OAAwB;AAC/C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAErE,QAAI,KAAK,iBAAiB,OAAO,OAAO,EAAG,QAAO,IAAI,KAAK;AAE3D,QAAI,iEAAiE,KAAK,OAAO,GAAG;AAClF,aAAO,KAAK;AAAA,IACd;AAEA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,iBAAiB,OAAgB,SAA0B;AACjE,WACG,iBAAiB,sBAAsB,MAAM,WAAW,OAAO,MAAM,WAAW,QAC9E,sEAAsE,KAAK,OAAO;AAAA,EAEzF;AACF;ACxGO,MAAM,iBAAiB;AAAA,EACpB,gCAAgB,IAAA;AAAA,EAChB;AAAA,EAER,SAAS,UAAgC;AACvC,QAAI,KAAK,UAAU,IAAI,SAAS,EAAE,GAAG;AACnC,cAAQ,KAAK,gCAAgC,SAAS,EAAE,mCAAmC;AAAA,IAC7F;AACA,SAAK,UAAU,IAAI,SAAS,IAAI,QAAQ;AACxC,QAAI,KAAK,aAAa,QAAW;AAC/B,WAAK,WAAW,SAAS;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,UAAU,IAAkB;AAC1B,QAAI,CAAC,KAAK,UAAU,IAAI,EAAE,GAAG;AAC3B,YAAM,IAAI,MAAM,aAAa,EAAE,qBAAqB;AAAA,IACtD;AACA,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,YAA4B;AAC1B,QAAI,KAAK,aAAa,QAAW;AAC/B,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,UAAM,WAAW,KAAK,UAAU,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,oBAAoB,KAAK,QAAQ,eAAe;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,IAAwC;AAC1C,WAAO,KAAK,UAAU,IAAI,EAAE;AAAA,EAC9B;AAAA,EAEA,OAAiB;AACf,WAAO,MAAM,KAAK,KAAK,UAAU,MAAM;AAAA,EACzC;AACF;ACLO,MAAM,iBAA2C;AAAA,EAC7C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAEJ;AAAA,EACA,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf;AAAA,EAEjB,YAAY,QAAgC;AAC1C,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAqB;AACnB,WAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEA,MAAM,OAAO,SAAwB,QAA6C;AAChF,SAAK,UAAA;AAEL,UAAM,MAAM,IAAI,IAAI,KAAK,aAAa;AACtC,QAAI,aAAa,IAAI,SAAS,QAAQ,KAAK;AAC3C,QAAI,QAAQ,cAAc,QAAQ,aAAa,GAAG;AAChD,UAAI,aAAa,IAAI,eAAe,OAAO,KAAK,MAAM,QAAQ,UAAU,CAAC,CAAC;AAAA,IAC5E;AACA,QAAI,OAAO,QAAQ,gBAAgB,YAAY,QAAQ,cAAc,GAAG;AACtE,UAAI,aAAa,IAAI,mBAAmB,OAAO,KAAK,MAAM,QAAQ,cAAc,KAAK,EAAE,CAAC,CAAC;AAAA,IAC3F;AACA,QAAI,QAAQ,SAAS;AACnB,UAAI,aAAa,IAAI,WAAW,QAAQ,OAAO;AAAA,IACjD;AACA,QAAI,QAAQ,UAAU;AACpB,UAAI,aAAa,IAAI,YAAY,QAAQ,QAAQ;AAAA,IACnD;AACA,QAAI,QAAQ,UAAU;AACpB,UAAI,aAAa,IAAI,YAAY,QAAQ,QAAQ;AAAA,IACnD;AACA,QAAI,QAAQ,YAAY;AACtB,UAAI,aAAa,IAAI,eAAe,QAAQ,UAAU;AAAA,IACxD;AACA,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,UAAI,aAAa,IAAI,mBAAmB,QAAQ,eAAe,KAAK,GAAG,CAAC;AAAA,IAC1E;AACA,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,UAAI,aAAa,IAAI,mBAAmB,QAAQ,eAAe,KAAK,GAAG,CAAC;AAAA,IAC1E;AAEA,UAAM,WAAW,MAAM,iBAAiB,IAAI,YAAY;AAAA,MACtD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,QAAQ;AAAA,MAAA;AAAA,MAEV;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,iBAAiB;AAE9C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,UAAM,SAAS,KAAK,WAAW,CAAA,GAAI,IAAI,CAAC,OAAO;AAAA,MAC7C,OAAO,EAAE;AAAA,MACT,KAAK,EAAE;AAAA,MACP,SAAS,EAAE;AAAA,MACX,QAAQ,EAAE;AAAA,IAAA,EACV;AAEF,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,SAAS,MAAM,SAAS,IACpB,SAAS,KAAK,iBAAiB,MAAM,MAAM,mBAAmB,QAAQ,KAAK,MAC3E,yBAAyB,QAAQ,KAAK;AAAA,MAC1C;AAAA,MACA,aAAa,MAAM,WAAW,IAAI,CAAC,4BAA4B,IAAI,CAAA;AAAA,IAAC;AAAA,EAExE;AAAA,EAEA,MAAM,MAAM,SAAuB,QAA4C;AAC7E,SAAK,UAAA;AAEL,UAAM,OAAgC;AAAA,MACpC,MAAM,CAAC,QAAQ,GAAG;AAAA,MAClB,QAAQ,QAAQ,UAAU;AAAA,IAAA;AAE5B,QAAI,QAAQ,OAAO;AACjB,WAAK,UAAU,QAAQ;AAAA,IACzB;AACA,QAAI,OAAO,QAAQ,QAAQ,UAAU;AACnC,WAAK,MAAM,QAAQ;AAAA,IACrB;AAEA,UAAM,WAAW,MAAM,iBAAiB,KAAK,cAAc;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,gBAAgB;AAE7C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,UAAM,OAAO,KAAK,UAAU,CAAC;AAC7B,UAAM,QAAQ,KAAK,SAAS,CAAC;AAE7B,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,MAAM,6BAA6B,MAAM,GAAG,KAAK,MAAM,KAAK,EAAE;AAAA,IAC1E;AACA,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAEA,WAAO;AAAA,MACL,SAAS,KAAK,SAAS,KAAK,eAAe;AAAA,MAC3C,SAAS,KAAK,QAAQ;AAAA,MACtB,aAAa,CAAA;AAAA,MACb,UAAU,CAAA;AAAA,IAAC;AAAA,EAEf;AAAA,EAEQ,YAAkB;AACxB,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AC3JO,MAAM,eAAyC;AAAA,EAC3C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAEJ;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EAEjB,YAAY,QAAgC;AAC1C,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAqB;AACnB,WAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEA,MAAM,OAAO,SAAwB,QAA6C;AAChF,SAAK,UAAA;AAEL,UAAM,OAAgC;AAAA,MACpC,OAAO,QAAQ;AAAA,MACf,cAAc;AAAA,MACd,aAAa,QAAQ,cAAc;AAAA,IAAA;AAErC,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AACA,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AAEA,UAAM,WAAW,MAAM,iBAAiB,GAAG,KAAK,OAAO,WAAW;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,eAAe;AAE5C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,UAAM,SAAS,KAAK,WAAW,CAAA,GAAI,IAAI,CAAC,OAAO;AAAA,MAC7C,OAAO,EAAE;AAAA,MACT,KAAK,EAAE;AAAA,MACP,SAAS,EAAE,WAAW,EAAE,eAAe;AAAA,IAAA,EACvC;AAEF,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,SAAS,MAAM,SAAS,IACpB,SAAS,MAAM,MAAM,mBAAmB,QAAQ,KAAK,MACrD,yBAAyB,QAAQ,KAAK;AAAA,MAC1C;AAAA,MACA,aAAa,MAAM,WAAW,IAAI,CAAC,4BAA4B,IAAI,CAAA;AAAA,IAAC;AAAA,EAExE;AAAA,EAEQ,YAAkB;AACxB,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,wGAAwG;AAAA,IAC1H;AAAA,EACF;AACF;AC/DO,MAAM,YAAsC;AAAA,EACxC,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAEJ;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EAEjB,YAAY,QAAgC;AAC1C,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAqB;AACnB,WAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEA,MAAM,OAAO,SAAwB,QAA6C;AAChF,SAAK,UAAA;AAEL,UAAM,OAAgC;AAAA,MACpC,OAAO,QAAQ;AAAA,MACf,aAAa,QAAQ,cAAc;AAAA,MACnC,MAAM;AAAA,IAAA;AAER,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AACA,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AAEA,UAAM,WAAW,MAAM,iBAAiB,GAAG,KAAK,OAAO,WAAW;AAAA,MAChE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,YAAY;AAEzC,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,UAAM,SAAS,KAAK,WAAW,CAAA,GAAI,IAAI,CAAC,OAAO;AAAA,MAC7C,OAAO,EAAE,SAAS,EAAE;AAAA,MACpB,KAAK,EAAE;AAAA,MACP,SAAS,EAAE,QAAQ;AAAA,MACnB,aAAa,EAAE;AAAA,IAAA,EACf;AAEF,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,SAAS,MAAM,SAAS,IACpB,SAAS,MAAM,MAAM,mBAAmB,QAAQ,KAAK,MACrD,yBAAyB,QAAQ,KAAK;AAAA,MAC1C;AAAA,MACA,aAAa,MAAM,WAAW,IAAI,CAAC,4BAA4B,IAAI,CAAA;AAAA,IAAC;AAAA,EAExE;AAAA,EAEA,MAAM,MAAM,SAAuB,QAA4C;AAC7E,SAAK,UAAA;AAEL,UAAM,OAAO;AAAA,MACX,MAAM,CAAC,QAAQ,GAAG;AAAA,MAClB,MAAM;AAAA,IAAA;AAGR,UAAM,WAAW,MAAM,iBAAiB,GAAG,KAAK,OAAO,aAAa;AAAA,MAClE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,aAAa,KAAK;AAAA,QAClB,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,cAAc;AAE3C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,UAAM,OAAO,KAAK,UAAU,CAAC;AAC7B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAEA,WAAO;AAAA,MACL,SAAS,KAAK,SAAS;AAAA,MACvB,SAAS,KAAK,QAAQ;AAAA,MACtB,aAAa,CAAA;AAAA,MACb,UAAU,CAAA;AAAA,IAAC;AAAA,EAEf;AAAA,EAEQ,YAAkB;AACxB,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,kGAAkG;AAAA,IACpH;AAAA,EACF;AACF;ACvGO,MAAM,kBAA4C;AAAA,EAC9C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAEJ;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EAEjB,YAAY,QAAgC;AAC1C,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,YAAqB;AACnB,WAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAC5D;AAAA,EAEA,MAAM,OAAO,SAAwB,QAA6C;AAChF,SAAK,UAAA;AAEL,UAAM,OAAgC;AAAA,MACpC,OAAO,QAAQ;AAAA,MACf,OAAO,QAAQ,cAAc;AAAA,MAC7B,SAAS,CAAC,KAAK;AAAA,IAAA;AAEjB,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,iBAAiB,QAAQ;AAAA,IAChC;AACA,QAAI,QAAQ,kBAAkB,QAAQ,eAAe,SAAS,GAAG;AAC/D,WAAK,iBAAiB,QAAQ;AAAA,IAChC;AAEA,UAAM,WAAW,MAAM,iBAAiB,GAAG,KAAK,OAAO,cAAc;AAAA,MACnE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,kBAAkB;AAE/C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,QAAI,KAAK,YAAY,OAAO;AAC1B,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,UAAM,WAAW,KAAK,MAAM,OAAO,CAAA;AACnC,UAAM,QAAQ,SAAS,IAAI,CAAC,OAAO;AAAA,MACjC,OAAO,EAAE,SAAS,EAAE;AAAA,MACpB,KAAK,EAAE;AAAA,MACP,SAAS,EAAE,eAAe,EAAE,YAAY;AAAA,IAAA,EACxC;AAEF,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,SAAS,MAAM,SAAS,IACpB,SAAS,MAAM,MAAM,mBAAmB,QAAQ,KAAK,MACrD,yBAAyB,QAAQ,KAAK;AAAA,MAC1C;AAAA,MACA,aAAa,MAAM,WAAW,IAAI,CAAC,4BAA4B,IAAI,CAAA;AAAA,IAAC;AAAA,EAExE;AAAA,EAEA,MAAM,MAAM,SAAuB,QAA4C;AAC7E,SAAK,UAAA;AAEL,UAAM,OAAO;AAAA,MACX,KAAK,QAAQ;AAAA,MACb,SAAS,CAAC,UAAU;AAAA,IAAA;AAGtB,UAAM,WAAW,MAAM,iBAAiB,GAAG,KAAK,OAAO,cAAc;AAAA,MACnE,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MAAA;AAAA,MAEV,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB;AAAA,IAAA,GACC,KAAK,SAAS;AAEjB,UAAM,aAAa,UAAU,kBAAkB;AAE/C,UAAM,OAAQ,MAAM,SAAS,KAAA;AAC7B,QAAI,KAAK,YAAY,OAAO;AAC1B,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,WAAO;AAAA,MACL,SAAS,KAAK,MAAM,UAAU,SAAS;AAAA,MACvC,SAAS,KAAK,MAAM,YAAY;AAAA,MAChC,aAAa,CAAA;AAAA,MACb,UAAU,CAAA;AAAA,IAAC;AAAA,EAEf;AAAA,EAEQ,YAAkB;AACxB,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI,MAAM,8GAA8G;AAAA,IAChI;AAAA,EACF;AACF;AC/HA,MAAM,mBAAmB;AACzB,MAAM,uBAAuB,MAAM;AACnC,MAAM,6BAA6B,IAAI,OAAO;AAC9C,MAAM,2BAA2B,KAAK;AAwB/B,MAAM,cAAwC;AAAA,EAC1C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAEJ;AAAA,EACA;AAAA,EAEjB,YAAY,QAAgC;AAC1C,UAAM,WAAW,mBAAmB,OAAO,cAAc;AACzD,SAAK,iBAAiB,aAAa,SAAY,SAAY,QAAQ,QAAQ;AAC3E,SAAK,YAAY,OAAO,SAAS,OAAO,SAAS,KAAK,OAAO,YAAY,IACrE,OAAO,YACP;AAAA,EACN;AAAA,EAEA,YAAqB;AACnB,WAAO,KAAK,mBAAmB,UAC1B,qBAAqB,KAAK,cAAc,MAAM;AAAA,EACrD;AAAA,EAEA,MAAM,OAAO,SAAwB,QAA6C;AAChFC,qBAAe,MAAM;AAErB,UAAM,aAAa,KAAK,mBAAmB,SACvC,SACA,qBAAqB,KAAK,cAAc;AAC5C,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,oEAAoE;AAAA,IACtF;AAEA,UAAM,OAA+B;AAAA,MACnC,IAAI,WAAA;AAAA,MACJ,OAAO;AAAA,MACP,OAAO,CAAC;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,cAAc,MAAM,QAAQ,OAAO;AAAA,MAAA,CACtD;AAAA,MACD,UAAU,EAAE,cAAc,CAAC,EAAE,GAAG,QAAQ,MAAA,CAAO,EAAA;AAAA,MAC/C,UAAU;AAAA,QACR,qBAAqB;AAAA,QACrB,iBAAiB,CAAC,QAAQ;AAAA,QAC1B,qBAAqB;AAAA,MAAA;AAAA,MAEvB,mBAAmB;AAAA,IAAA;AAGrB,UAAM,iBAAiB,YAAY,QAAQ,KAAK,SAAS;AACzD,UAAM,gBAAgB,SAAS,YAAY,IAAI,CAAC,QAAQ,cAAc,CAAC,IAAI;AAC3E,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,kBAAkB;AAAA,QACvC,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,SAAS;AAAA,UACP,eAAe,UAAU,WAAW,WAAW;AAAA,UAC/C,sBAAsB,WAAW;AAAA,UACjC,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,YAAY;AAAA,QAAA;AAAA,QAEd,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ;AAAA,MAAA,CACT;AAAA,IACH,SAAS,OAAO;AACdA,uBAAe,MAAM;AACrB,UAAI,eAAe,SAAS;AAC1B,cAAM,IAAI,MAAM,uCAAuC,KAAK,SAAS,MAAM,EAAE,OAAO,OAAO;AAAA,MAC7F;AACA,YAAM,IAAI,MAAM,sCAAsC,EAAE,OAAO,OAAO;AAAA,IACxE;AAEA,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM;AAAA,QACd;AAAA,QACA,SAAS,KAAK,6BAA6B;AAAA,QAC3C;AAAA,MAAA;AAAA,IAEJ,SAAS,OAAO;AACdA,uBAAe,MAAM;AACrB,UAAI,eAAe,SAAS;AAC1B,cAAM,IAAI,MAAM,uCAAuC,KAAK,SAAS,MAAM,EAAE,OAAO,OAAO;AAAA,MAC7F;AACA,UAAI,iBAAiB,sBAAuB,OAAM;AAClD,YAAM,IAAI,MAAM,4CAA4C,SAAS,MAAM,KAAK,EAAE,OAAO,OAAO;AAAA,IAClG;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,SAAS,gBAAgB,OAAO;AACtC,YAAM,SAAS,SAAS,KAAK,MAAM,KAAK;AACxC,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,IAAI;AAAA,UACR,uDAAuD,SAAS,MAAM,IAAI,MAAM;AAAA,QAAA;AAAA,MAEpF;AACA,YAAM,IAAI,MAAM,oCAAoC,SAAS,MAAM,IAAI,MAAM,EAAE;AAAA,IACjF;AAEA,WAAO,uBAAuB,OAAO;AAAA,EACvC;AACF;AAEA,SAAS,qBAAqB,UAAqD;AACjF,MAAI;AACF,UAAM,QAAQ,SAAS,QAAQ;AAC/B,QAAI,CAAC,MAAM,OAAA,KAAY,MAAM,QAAQ,KAAK,MAAM,OAAO,qBAAsB,QAAO;AACpF,UAAM,QAAiB,KAAK,MAAM,aAAa,UAAU,MAAM,CAAC;AAChE,WAAO,sBAAsB,KAAK;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,uBAAuB,OAA8B;AAC5D,MAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,WAAW,UAAU;AACxD,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AACA,MAAI,MAAM,YAAY,UAAa,CAAC,MAAM,QAAQ,MAAM,OAAO,GAAG;AAChE,UAAM,IAAI,MAAM,gEAAgE;AAAA,EAClF;AAEA,QAAM,QAA+B,CAAA;AACrC,QAAM,2BAAW,IAAA;AACjB,aAAW,QAAQ,MAAM,WAAW,CAAA,GAAI;AACtC,QAAI,CAAC,SAAS,IAAI,KAAK,KAAK,SAAS,cAAe;AACpD,UAAM,MAAM,YAAY,KAAK,GAAG;AAChC,QAAI,CAAC,OAAO,KAAK,IAAI,GAAG,EAAG;AAC3B,SAAK,IAAI,GAAG;AACZ,UAAM,KAAK;AAAA,MACT,OAAO,mBAAmB,KAAK,KAAK,KAAK;AAAA,MACzC;AAAA,MACA,SAAS,mBAAmB,KAAK,OAAO,KAAK;AAAA,IAAA,CAC9C;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACtB,SAAO;AAAA,IACL,QAAQ,QAAQ,SAAS,IAAI,OAAO;AAAA,IACpC,QAAQ;AAAA,IACR,SAAS,WAAW,kBAAkB,MAAM,MAAM;AAAA,IAClD;AAAA,IACA,aAAa,QAAQ,SAAS,IAAI,CAAA,IAAK,CAAC,gCAAgC;AAAA,EAAA;AAE5E;AAEA,SAAS,sBAAsB,OAAmD;AAChF,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAE7B,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,SAAS,MAAM,UAAU,IAAI,MAAM,aAAa;AAAA,IAChD,SAAS,MAAM,MAAM,IAAI,MAAM,SAAS;AAAA,EAAA;AAG1C,aAAW,aAAa,YAAY;AAClC,QAAI,CAAC,UAAW;AAChB,UAAM,cAAc,mBAAmB,UAAU,YAAY,KACxD,mBAAmB,UAAU,MAAM;AACxC,QAAI,CAAC,YAAa;AAElB,UAAM,YAAY,mBAAmB,UAAU,UAAU,KACpD,mBAAmB,UAAU,SAAS,KACtC,mBAAmB,WAAW;AACnC,QAAI,CAAC,UAAW;AAEhB,WAAO,EAAE,aAAa,UAAA;AAAA,EACxB;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,aAA0D;AAClF,MAAI;AACF,UAAM,QAAQ,YAAY,MAAM,GAAG;AACnC,QAAI,MAAM,WAAW,KAAK,CAAC,MAAM,CAAC,EAAG,QAAO;AAC5C,UAAM,UAAmB,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,GAAG,WAAW,EAAE,SAAS,MAAM,CAAC;AACvF,WAAO,SAAS,OAAO,IAAI,UAAU;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,aAAyC;AACnE,QAAM,OAAO,iBAAiB,WAAW,IAAI,6BAA6B;AAC1E,SAAO,SAAS,IAAI,IAAI,mBAAmB,KAAK,kBAAkB,IAAI;AACxE;AAEA,MAAM,8BAA8B,MAAM;AAAA,EACxC,YAAY,OAAe;AACzB,UAAM,yCAAyC,KAAK,QAAQ;AAC5D,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,gBACb,UACA,UACA,QACkB;AAClB,QAAM,iBAAiB,OAAO,SAAS,QAAQ,IAAI,gBAAgB,CAAC;AACpE,MAAI,OAAO,SAAS,cAAc,KAAK,iBAAiB,UAAU;AAChE,UAAM,SAAS,MAAM,OAAA,EAAS,MAAM,MAAM;AAAA,IAAC,CAAC;AAC5C,UAAM,IAAI,sBAAsB,QAAQ;AAAA,EAC1C;AACA,MAAI,CAAC,SAAS,KAAM,OAAM,IAAI,YAAY,qBAAqB;AAE/D,QAAM,SAAS,SAAS,KAAK,UAAA;AAC7B,QAAM,SAAuB,CAAA;AAC7B,MAAI,QAAQ;AACZ,MAAI;AACF,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,KAAA,GAAQ,MAAM;AAC7D,UAAI,KAAM;AACV,UAAI,CAAC,MAAO;AACZ,eAAS,MAAM;AACf,UAAI,QAAQ,SAAU,OAAM,IAAI,sBAAsB,QAAQ;AAC9D,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF,SAAS,OAAO;AACd,UAAM,OAAO,SAAS,MAAM,MAAM;AAAA,IAAC,CAAC;AACpC,UAAM;AAAA,EACR,UAAA;AACE,WAAO,YAAA;AAAA,EACT;AAEA,SAAO,KAAK,MAAM,OAAO,OAAO,QAAQ,KAAK,EAAE,SAAS,MAAM,CAAC;AACjE;AAEA,SAAS,UAAa,WAAuB,QAAiC;AAC5E,MAAI,OAAO,QAAS,QAAO,QAAQ,OAAO,aAAa,MAAM,CAAC;AAC9D,SAAO,IAAI,QAAW,CAACC,UAAS,WAAW;AACzC,UAAM,UAAU,MAAM,OAAO,aAAa,MAAM,CAAC;AACjD,WAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM;AACxD,cAAU;AAAA,MACR,CAAC,UAAU;AACT,eAAO,oBAAoB,SAAS,OAAO;AAC3CA,iBAAQ,KAAK;AAAA,MACf;AAAA,MACA,CAAC,UAAU;AACT,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,KAAK;AAAA,MACd;AAAA,IAAA;AAAA,EAEJ,CAAC;AACH;AAEA,SAASD,iBAAe,QAA4B;AAClD,MAAI,QAAQ,QAAS,OAAM,aAAa,MAAM;AAChD;AAEA,SAAS,aAAa,QAA4B;AAChD,SAAO,IAAI,MAAM,+BAA+B,EAAE,OAAO,OAAO,QAAQ;AAC1E;AAEA,SAAS,gBAAgB,OAAoC;AAC3D,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,MAAM,OAAO,MAAM,UAAU,WAC/B,MAAM,QACN,SAAS,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM,YAAY,WACtD,MAAM,MAAM,UACZ,OAAO,MAAM,YAAY,WACvB,MAAM,UACN;AACR,SAAO,KACH,QAAQ,2DAA2D,YAAY,EAChF,MAAM,GAAG,GAAK;AACnB;AAEA,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,KAAK;AACzB,WAAO,IAAI,aAAa,WAAW,IAAI,aAAa,WAAW,QAAQ;AAAA,EACzE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,mBAAmB,OAAoC;AAC9D,SAAO,OAAO,UAAU,YAAY,MAAM,WAAW,KAAK,MAAM,KAAA,IAAS;AAC3E;AClUO,MAAM,aAAuC;AAAA,EACzC,KAAK;AAAA,EACL,OAAO;AAAA,EACP,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EAErB,YAAY,SAAiC;AAAA,EAAC;AAAA,EAE9C,YAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,SAA+C;AAC1D,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,SAAS,4BAA4B,QAAQ,KAAK;AAAA,MAClD,OAAO;AAAA,QACL;AAAA,UACE,OAAO;AAAA,UACP,KAAK;AAAA,UACL,SAAS;AAAA,QAAA;AAAA,QAEX;AAAA,UACE,OAAO;AAAA,UACP,KAAK;AAAA,UACL,SAAS;AAAA,QAAA;AAAA,MACX;AAAA,MAEF,aAAa,CAAC,uDAAuD;AAAA,IAAA;AAAA,EAEzE;AAAA,EAEA,MAAM,MAAM,SAA6C;AACvD,WAAO;AAAA,MACL,SAAS,iBAAiB,QAAQ,GAAG;AAAA,MACrC,SAAS;AAAA;AAAA,sCAAsD,QAAQ,GAAG;AAAA,MAC1E,OAAO;AAAA,QACL,EAAE,MAAM,WAAW,KAAK,sBAAA;AAAA,MAAsB;AAAA,MAEhD,aAAa,CAAC,iDAAiD;AAAA,MAC/D,UAAU,CAAA;AAAA,IAAC;AAAA,EAEf;AACF;AChDO,MAAM,cAAc;AAAA,EACzB,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS,EAAE,MAAM,SAAA;AAAA,IACjB,SAAS,EAAE,MAAM,SAAA;AAAA,IACjB,OAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,UACV,MAAM,EAAE,MAAM,SAAA;AAAA,UACd,KAAK,EAAE,MAAM,SAAA;AAAA,QAAkB;AAAA,QAEjC,UAAU,CAAC,QAAQ,KAAK;AAAA,MAAA;AAAA,IAC1B;AAAA,IAEF,aAAa;AAAA,MACX,MAAM;AAAA,MACN,OAAO,EAAE,MAAM,SAAA;AAAA,IAAkB;AAAA,IAEnC,UAAU;AAAA,MACR,MAAM;AAAA,MACN,OAAO,EAAE,MAAM,SAAA;AAAA,IAAkB;AAAA,EACnC;AAAA,EAEF,UAAU,CAAC,WAAW,WAAW,aAAa;AAChD;ACxBO,MAAM,8BAA8B;AAQpC,SAAS,oBACd,UACA,eACU;AACV,MAAI;AACJ,MAAI;AACF,eAAW,SAAS,YAAY;AAAA,EAClC,QAAQ;AAAA,EAER;AAEA,QAAM,aAAa,kBAAkB,SAAY,SAAS,SAAS;AACnE,QAAM,2BAAW,IAAA;AACjB,QAAM,aAAuB,CAAA;AAC7B,aAAW,MAAM,WAAW,CAAC,UAAU,GAAG,UAAU,IAAI,YAAY;AAClE,QAAI,KAAK,IAAI,EAAE,EAAG;AAClB,SAAK,IAAI,EAAE;AACX,eAAW,KAAK,EAAE;AAAA,EACpB;AACA,SAAO;AACT;AAEO,SAAS,qBACd,QACA,WACgB;AAChB,QAAM,aAAa,IAAI,gBAAA;AACvB,MAAI,aAAa;AACjB,MAAI;AACJ,MAAI;AAEJ,MAAI,QAAQ,SAAS;AACnB,eAAW,MAAM,OAAO,MAAM;AAAA,EAChC,OAAO;AACL,QAAI,QAAQ;AACV,sBAAgB,MAAM,WAAW,MAAM,OAAO,MAAM;AACpD,aAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,MAAM;AAAA,IAChE;AACA,YAAQ,WAAW,MAAM;AACvB,mBAAa;AACb,iBAAW,MAAM,IAAI,MAAM,oCAAoC,SAAS,IAAI,CAAC;AAAA,IAC/E,GAAG,SAAS;AAAA,EACd;AAEA,SAAO;AAAA,IACL,QAAQ,WAAW;AAAA,IACnB,UAAU,MAAM;AAAA,IAChB,UAAU;AACR,UAAI,UAAU,OAAW,cAAa,KAAK;AAC3C,UAAI,UAAU,cAAe,QAAO,oBAAoB,SAAS,aAAa;AAAA,IAChF;AAAA,EAAA;AAEJ;AAEO,SAAS,eAAkB,WAAuB,QAA6C;AACpG,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,SAAS;AAClB,cAAU,MAAM,MAAM;AAAA,IAAC,CAAC;AACxB,WAAO,QAAQ,OAAO,OAAO,MAAM;AAAA,EACrC;AAEA,SAAO,IAAI,QAAW,CAACC,UAAS,WAAW;AACzC,UAAM,UAAU,MAAM;AACpB,aAAO,oBAAoB,SAAS,OAAO;AAC3C,aAAO,OAAO,MAAM;AAAA,IACtB;AACA,WAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM;AACxD,cAAU;AAAA,MACR,CAAC,UAAU;AACT,eAAO,oBAAoB,SAAS,OAAO;AAC3C,QAAAA,SAAQ,KAAK;AAAA,MACf;AAAA,MACA,CAAC,UAAU;AACT,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,KAAK;AAAA,MACd;AAAA,IAAA;AAAA,EAEJ,CAAC;AACH;AAEO,SAAS,eAAe,QAAuC;AACpE,MAAI,QAAQ,QAAS,OAAM,OAAO;AACpC;AC/EA,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AAEjB,SAAS,qBACd,KACA,UACA,UACA,eACM;AACN,MAAI,OAAO,IAAI,OAAO,aAAa,YAAY;AAC7C,YAAQ,MAAM,gFAAgF;AAC9F;AAAA,EACF;AAEA,MAAI,MAAM,SAAS;AAAA,IACjB,MAAM;AAAA,IACN,aACE;AAAA,IACF,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,QACV,KAAK;AAAA,UACH,MAAM;AAAA,UACN,aAAa;AAAA,QAAA;AAAA,QAEf,OAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa;AAAA,QAAA;AAAA,MACf;AAAA,MAEF,UAAU,CAAC,KAAK;AAAA,IAAA;AAAA,IAElB,QAAQ;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,CAAC,OAAgB,UAAmC;AAAA,QAC1D,EAAE,MAAM,QAAQ,MAAM,oBAAoB,KAAK,EAAA;AAAA,MAAE;AAAA,IACnD;AAAA,IAEF,WAAW,uBAAuB;AAAA,IAClC,mBAAmB,MAAM;AAAA,IACzB,aAAa,CAAC,UAA2C;AAAA,MACvD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IAAA;AAAA,IAEZ,MAAM,QAAQ,MAAuC,MAAgC;AACnF,UAAI,OAAO,MAAM,QAAQ,YAAY,CAAC,gBAAgB,KAAK,KAAK,IAAI,KAAA,CAAM,GAAG;AAC3E,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,YAAM,UAAwB;AAAA,QAC5B,KAAK,KAAK,IAAI,KAAA;AAAA,QACd,OAAO,KAAK;AAAA,QACZ,QAAQ;AAAA,MAAA;AAGV,aAAO,kBAAkB,UAAU,UAAU,eAAe,SAAS,KAAK,MAAM;AAAA,IAClF;AAAA,EAAA,CACD;AACH;AAEA,SAAS,yBACP,UACA,eACU;AACV,SAAO,oBAAoB,UAAU,aAAa,EAAE,OAAO,CAAC,OAAO;AACjE,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,WAAO,aAAa,UACf,SAAS,cAAc,SACvB,OAAO,SAAS,UAAU;AAAA,EACjC,CAAC;AACH;AAEA,eAAe,kBACb,UACA,UACA,eACA,SACA,QACsB;AACtB,iBAAe,MAAM;AACrB,QAAM,aAAa,yBAAyB,UAAU,aAAa;AACnE,QAAM,YAAsB,CAAA;AAC5B,QAAM,mCAAmB,IAAA;AACzB,MAAI;AAEJ,SAAO,MAAM;AACX,mBAAe,MAAM;AACrB,UAAM,EAAE,MAAA,IAAU,SAAS,OAAO,YAAY,OAAO;AACrD,UAAM,KAAK,MAAM,KAAK,CAAC,gBAAgB;AACrC,UAAI,aAAa,IAAI,WAAW,EAAG,QAAO;AAC1C,YAAMC,YAAW,SAAS,IAAI,WAAW;AACzC,aAAOA,WAAU,eACZA,UAAS,cAAc,SACvB,OAAOA,UAAS,UAAU;AAAA,IACjC,CAAC;AACD,QAAI,OAAO,OAAW;AAEtB,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,iBAAa,IAAI,EAAE;AACnB,UAAM,UAAU,UAAU,WAAW,IACjC,SACA,qBAAqB,QAAQ,2BAA2B;AAE5D,QAAI;AACF,YAAM,gBAAgB,SAAS,UAAU;AACzC,YAAM,SAAS,MAAM,eAAe,SAAS,MAAO,SAAS,aAAa,GAAG,aAAa;AAC1F,qBAAe,MAAM;AACrB,UAAI,SAAS,SAAA,EAAY,OAAM,QAAQ,OAAO;AAC9C,YAAM,EAAE,SAAAC,UAAS,UAAAC,cAAa,SAAS,OAAO,YAAY,OAAO;AACjE,UAAI,UAAU,SAAS,GAAG;AACxB,eAAO,WAAW,OAAO,YAAY,CAAA;AACrC,eAAO,SAAS,KAAK,uBAAuB,UAAU,KAAK,IAAI,CAAC,SAAS;AAAA,MAC3E;AACA,UAAID,SAAQ,SAAS,GAAG;AACtB,eAAO,WAAW,OAAO,YAAY,CAAA;AACrC,eAAO,SAAS,KAAK,8BAA8BA,SAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,MACzE;AACA,UAAIC,UAAS,SAAS,GAAG;AACvB,eAAO,WAAW,OAAO,YAAY,CAAA;AACrC,eAAO,SAAS,KAAK,qCAAqCA,UAAS,KAAK,IAAI,CAAC,EAAE;AAAA,MACjF;AACA,aAAO;AAAA,IACT,SAAS,OAAO;AACd,qBAAe,MAAM;AACrB,YAAM,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACvE,kBAAY,kBAAkB,8BAC1B,SACA,SAAS,aACP,IAAI,MAAM,oCAAoC,2BAA2B,MAAM,EAAE,OAAO,OAAA,CAAQ,IAChG;AACN,gBAAU,KAAK,EAAE;AACjB,cAAQ,KAAK,gCAAgC,EAAE,gCAAgC,UAAU,OAAO,EAAE;AAClG,eAAS,OAAO,IAAI,SAAS,SAAS;AAAA,IACxC,UAAA;AACE,eAAS,QAAA;AAAA,IACX;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,SAAA,IAAa,SAAS,OAAO,YAAY,OAAO;AACjE,QAAM,aAAa,QAAQ,SAAS,IAAI,cAAc,QAAQ,KAAK,IAAI,CAAC,MAAM;AAC9E,QAAM,cAAc,SAAS,SAAS,IAAI,6BAA6B,SAAS,KAAK,IAAI,CAAC,MAAM;AAChG,QAAM,IAAI;AAAA,IACR,8BAA8B,UAAU,SAAS,IAAI,YAAY,UAAU,KAAK,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,GAAG,WAAW,KAAK,WAAW,WAAW,wBAAwB;AAAA,IAC3K,EAAE,OAAO,UAAA;AAAA,EAAU;AAEvB;AAEA,SAAS,oBAAoB,OAAwC;AACnE,QAAM,QAAkB,CAAC,OAAO,MAAM,WAAW,EAAE,CAAC;AACpD,QAAM,UAAU,OAAQ,MAAM,WAAsB,EAAE,EAAE,KAAA;AACxD,MAAI,SAAS;AACX,UAAM,KAAK,IAAI,YAAY,QAAQ,SAAS,qBAAqB,GAAG,QAAQ,MAAM,GAAG,kBAAkB,CAAC,MAAM,OAAO;AAAA,EACvH;AACA,QAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,MAAM,MAAM,GAAG,eAAe,IAAI,CAAA;AACnF,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,KAAK,IAAI,QAAQ;AACvB,eAAW,QAAQ,OAAO;AACxB,YAAM,OAAO,OAAO,SAAS,YAAY,SAAS,OAAO,OAAQ,KAAiC,QAAQ,EAAE,IAAI;AAChH,YAAM,MAAM,OAAO,SAAS,YAAY,SAAS,OAAO,OAAQ,KAAiC,OAAO,EAAE,IAAI;AAC9G,YAAM,KAAK,KAAK,IAAI,MAAM,GAAG,EAAE;AAAA,IACjC;AAAA,EACF;AACA,QAAM,cAAc,MAAM,QAAQ,MAAM,WAAW,IAAI,MAAM,cAAc,CAAA;AAC3E,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,KAAK,IAAI,cAAc,YAAY,KAAK,IAAI,CAAC,EAAE;AAAA,EACvD;AACA,QAAM,WAAW,MAAM,QAAQ,MAAM,QAAQ,IAAI,MAAM,WAAW,CAAA;AAClE,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,IAAI,aAAa,SAAS,KAAK,IAAI,CAAC,EAAE;AAAA,EACnD;AACA,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,IAAI;AACxC;ACtKO,SAAS,0BACd,KACA,UACA,UACA,eACM;AACN,MAAI,OAAO,IAAI,KAAK,2BAA2B,YAAY;AACzD,YAAQ,MAAM,iGAAiG;AAC/G;AAAA,EACF;AAEA,MAAI,IAAI,uBAAuB;AAAA,IAC7B,IAAI;AAAA,IACJ,WAAW,MAAM,qBAAqB,UAAU,UAAU,aAAa;AAAA,IACvE,MAAM,OAAO,SAA+B,QAAiC;AAC3E,YAAM,gBAA+B;AAAA,QACnC,OAAO,QAAQ;AAAA,QACf,YAAY,QAAQ;AAAA,QACpB,aAAa,QAAQ;AAAA,QACrB,SAAS,QAAQ;AAAA,QACjB,UAAU,QAAQ;AAAA,QAClB,UAAU,QAAQ;AAAA,MAAA;AAGpB,YAAM,EAAE,QAAQ,WAAW,SAAS,SAAA,IAAa,MAAM;AAAA,QACrD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGF,aAAO;AAAA,QACL,SAAS,cAAc,QAAQ,WAAW,SAAS,QAAQ;AAAA,QAC3D,SAAS,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,UACnC,KAAK,KAAK;AAAA,UACV,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAA,IAAU,CAAA;AAAA,UACzC,GAAI,KAAK,UAAU,EAAE,SAAS,KAAK,QAAA,IAAY,CAAA;AAAA,UAC/C,GAAI,KAAK,cAAc,EAAE,aAAa,KAAK,YAAA,IAAgB,CAAA;AAAA,QAAC,EAC5D;AAAA,QACF,WAAW;AAAA,MAAA;AAAA,IAEf;AAAA,EAAA,CACD;AACH;AAEA,SAAS,0BACP,UACA,eACU;AACV,SAAO,oBAAoB,UAAU,aAAa,EAAE,OAAO,CAAC,OAAO;AACjE,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,WAAO,aAAa,UAAa,SAAS,eAAe;AAAA,EAC3D,CAAC;AACH;AAEA,SAAS,qBACP,UACA,UACA,eACS;AACT,QAAM,aAAa,0BAA0B,UAAU,aAAa;AACpE,QAAM,EAAE,MAAA,IAAU,SAAS,OAAO,YAAY,QAAQ;AACtD,SAAO,MAAM,KAAK,CAAC,OAAO;AACxB,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,WAAO,UAAU,UAAA,KAAe,SAAS,eAAe;AAAA,EAC1D,CAAC;AACH;AASA,eAAe,mBACb,UACA,UACA,eACA,SACA,QAC0B;AAC1B,iBAAe,MAAM;AACrB,QAAM,aAAa,0BAA0B,UAAU,aAAa;AACpE,QAAM,YAAsB,CAAA;AAC5B,QAAM,mCAAmB,IAAA;AACzB,MAAI;AAEJ,SAAO,MAAM;AACX,mBAAe,MAAM;AACrB,UAAM,EAAE,MAAA,IAAU,SAAS,OAAO,YAAY,QAAQ;AACtD,UAAM,KAAK,MAAM,KAAK,CAAC,gBAAgB;AACrC,UAAI,aAAa,IAAI,WAAW,EAAG,QAAO;AAC1C,YAAMF,YAAW,SAAS,IAAI,WAAW;AACzC,aAAOA,WAAU,UAAA,KAAeA,UAAS,eAAe;AAAA,IAC1D,CAAC;AACD,QAAI,OAAO,OAAW;AAEtB,UAAM,WAAW,SAAS,IAAI,EAAE;AAChC,iBAAa,IAAI,EAAE;AACnB,UAAM,UAAU,UAAU,WAAW,IACjC,SACA,qBAAqB,QAAQ,2BAA2B;AAE5D,QAAI;AACF,YAAM,gBAAgB,SAAS,UAAU;AACzC,YAAM,SAAS,MAAM,eAAe,SAAS,OAAO,SAAS,aAAa,GAAG,aAAa;AAC1F,qBAAe,MAAM;AACrB,UAAI,SAAS,SAAA,EAAY,OAAM,QAAQ,OAAO;AAC9C,YAAM,EAAE,SAAAC,UAAS,UAAAC,cAAa,SAAS,OAAO,YAAY,QAAQ;AAClE,aAAO,EAAE,QAAQ,WAAW,SAAAD,UAAS,UAAAC,UAAAA;AAAAA,IACvC,SAAS,OAAO;AACd,qBAAe,MAAM;AACrB,YAAM,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACvE,kBAAY,kBAAkB,8BAC1B,SACA,SAAS,aACP,IAAI,MAAM,oCAAoC,2BAA2B,MAAM,EAAE,OAAO,OAAA,CAAQ,IAChG;AACN,gBAAU,KAAK,EAAE;AACjB,cAAQ,KAAK,gCAAgC,EAAE,iCAAiC,UAAU,OAAO,EAAE;AACnG,eAAS,OAAO,IAAI,UAAU,SAAS;AAAA,IACzC,UAAA;AACE,eAAS,QAAA;AAAA,IACX;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,SAAA,IAAa,SAAS,OAAO,YAAY,QAAQ;AAClE,QAAM,aAAa,QAAQ,SAAS,IAAI,cAAc,QAAQ,KAAK,IAAI,CAAC,MAAM;AAC9E,QAAM,cAAc,SAAS,SAAS,IAAI,6BAA6B,SAAS,KAAK,IAAI,CAAC,MAAM;AAChG,QAAM,IAAI;AAAA,IACR,+BAA+B,UAAU,SAAS,IAAI,YAAY,UAAU,KAAK,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,GAAG,WAAW,KAAK,WAAW,WAAW,wBAAwB;AAAA,IAC5K,EAAE,OAAO,UAAA;AAAA,EAAU;AAEvB;AAEA,SAAS,cACP,QACA,WACA,SACA,UACQ;AACR,QAAM,QAAQ,CAAC,qBAAqB,OAAO,MAAM,KAAK,OAAO,OAAO;AACpE,MAAI,UAAU,SAAS,GAAG;AACxB,UAAM,KAAK,wBAAwB,UAAU,KAAK,IAAI,CAAC,UAAU;AAAA,EACnE;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,aAAa,QAAQ,KAAK,IAAI,CAAC,GAAG;AAAA,EAC/C;AACA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,4BAA4B,SAAS,KAAK,IAAI,CAAC,GAAG;AAAA,EAC/D;AACA,MAAI,OAAO,YAAY,SAAS,GAAG;AACjC,UAAM,KAAK,cAAc,OAAO,YAAY,KAAK,IAAI,CAAC,EAAE;AAAA,EAC1D;AACA,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,IAAI;AACxC;ACrKO,MAAM,OAAO;AACb,MAAM,SAAS,CAAC,SAAS,KAAK;AAErC,MAAM,yBAAyB,CAAC,SAAS,YAAY,UAAU,OAAO,WAAW;AAE1E,SAAS,MAAM,KAAU,YAA0B,IAAU;AAClE,QAAM,SAAS,iBAAiB,SAAS;AACzC,QAAM,WAAW,IAAI,iBAAA;AACrB,QAAM,WAAW,IAAI,mBAAA;AACrB,QAAM,WAAW,OAAO,aAAa;AAGrC,WAAS,SAAS,IAAI,cAAc,sBAAsB,QAAQ,OAAO,CAAC,CAAC;AAC3E,WAAS,SAAS,IAAI,iBAAiB,sBAAsB,QAAQ,UAAU,CAAC,CAAC;AACjF,WAAS,SAAS,IAAI,eAAe,sBAAsB,QAAQ,QAAQ,CAAC,CAAC;AAC7E,WAAS,SAAS,IAAI,YAAY,sBAAsB,QAAQ,KAAK,CAAC,CAAC;AACvE,WAAS,SAAS,IAAI,kBAAkB,sBAAsB,QAAQ,WAAW,CAAC,CAAC;AAEnF,MAAI,UAAU;AACZ,aAAS,SAAS,IAAI,aAAa,sBAAsB,QAAQ,UAAU,CAAC,CAAC;AAAA,EAC/E;AAGA,QAAM,iBAAiB,OAAO;AAC9B,MAAI,gBAAgB;AAClB,QAAI;AACF,eAAS,UAAU,cAAc;AAAA,IACnC,SAAS,OAAO;AACd,cAAQ,KAAK,2CAA2C,cAAc,yCAAyC,SAAS,KAAA,EAAO,CAAC,CAAC,GAAG;AAAA,IACtI;AAAA,EACF;AAGA,QAAM,gBAAgB,OAAO,iBAAiB;AAG9C,4BAA0B,KAAK,UAAU,UAAU,aAAa;AAChE,uBAAqB,KAAK,UAAU,UAAU,aAAa;AAE3D,UAAQ,IAAI,mDAAmD,SAAS,UAAA,EAAY,EAAE,6BAA6B,cAAc,KAAK,IAAI,CAAC,GAAG;AAChJ;"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mini-z/dsh-search-providers",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Modular DSH
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Modular DSH search plugin with Codex-first web search, page fetching, and capability-aware automatic failover.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite build --watch",
|
|
@@ -15,19 +15,24 @@
|
|
|
15
15
|
"dsh",
|
|
16
16
|
"cordis.patch.yml",
|
|
17
17
|
"README.md",
|
|
18
|
+
"search-providers.example.json",
|
|
18
19
|
"CHANGELOG.md"
|
|
19
20
|
],
|
|
20
21
|
"keywords": [
|
|
21
22
|
"search",
|
|
22
23
|
"web-fetch",
|
|
24
|
+
"codex",
|
|
23
25
|
"tinyfish",
|
|
24
26
|
"dsh-plugin",
|
|
25
27
|
"deepseek-harness",
|
|
26
28
|
"web-search",
|
|
27
29
|
"modular-search"
|
|
28
30
|
],
|
|
29
|
-
"author": "",
|
|
31
|
+
"author": "mini-z",
|
|
30
32
|
"license": "MIT",
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
31
36
|
"engines": {
|
|
32
37
|
"node": ">=22.13"
|
|
33
38
|
},
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "codex",
|
|
3
|
+
"fallbackChain": [
|
|
4
|
+
"tinyfish",
|
|
5
|
+
"tavily",
|
|
6
|
+
"exa",
|
|
7
|
+
"firecrawl"
|
|
8
|
+
],
|
|
9
|
+
"providers": {
|
|
10
|
+
"codex": {
|
|
11
|
+
"credentialFile": "./codex-credential.json",
|
|
12
|
+
"timeoutMs": 55000
|
|
13
|
+
},
|
|
14
|
+
"tinyfish": {
|
|
15
|
+
"apiKey": "",
|
|
16
|
+
"timeoutMs": 55000
|
|
17
|
+
},
|
|
18
|
+
"tavily": {
|
|
19
|
+
"apiKey": ""
|
|
20
|
+
},
|
|
21
|
+
"exa": {
|
|
22
|
+
"apiKey": ""
|
|
23
|
+
},
|
|
24
|
+
"firecrawl": {
|
|
25
|
+
"apiKey": ""
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|