web-access-cli 0.5.0 → 0.7.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/README.md +104 -207
- package/README_CN.md +106 -209
- package/THIRD_PARTY_NOTICES.md +0 -13
- package/dist/config/config.js +10 -2
- package/dist/config/config.js.map +1 -1
- package/dist/core/router.js +4 -2
- package/dist/core/router.js.map +1 -1
- package/dist/core/schema.js +1 -1
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -1
- package/dist/gateway/admin.js +670 -0
- package/dist/gateway/admin.js.map +1 -0
- package/dist/gateway/panel.js +2405 -0
- package/dist/gateway/panel.js.map +1 -0
- package/dist/gateway/records.js +464 -0
- package/dist/gateway/records.js.map +1 -0
- package/dist/gateway/schema.js +115 -0
- package/dist/gateway/schema.js.map +1 -0
- package/dist/gateway/server.js +663 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/gateway/service.js +607 -0
- package/dist/gateway/service.js.map +1 -0
- package/dist/providers/anysearch.js +5 -3
- package/dist/providers/anysearch.js.map +1 -1
- package/dist/providers/deepseek.js +173 -106
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/registry.js +2 -0
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/search.js +4 -2
- package/dist/providers/search.js.map +1 -1
- package/dist/providers/xai.js +16 -8
- package/dist/providers/xai.js.map +1 -1
- package/dist/providers/xcrawl.js +1 -1
- package/dist/providers/xcrawl.js.map +1 -1
- package/dist/providers/yuanbao.js +214 -0
- package/dist/providers/yuanbao.js.map +1 -0
- package/dist/server.js +127 -0
- package/dist/server.js.map +1 -0
- package/dist/transport/http.js +8 -0
- package/dist/transport/http.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -2
- package/schemas/config.schema.json +4 -0
- package/schemas/gatewayError.schema.json +33 -0
- package/schemas/gatewayFetchRequest.schema.json +13 -0
- package/schemas/gatewayFetchResponse.schema.json +16 -0
- package/schemas/gatewaySearchRequest.schema.json +37 -0
- package/schemas/gatewaySearchResponse.schema.json +81 -0
- package/schemas/output.schema.json +52 -0
- package/schemas/searchRequest.schema.json +1 -1
package/README.md
CHANGED
|
@@ -5,277 +5,174 @@ English | [简体中文](README_CN.md)
|
|
|
5
5
|
[](https://github.com/Juexe/web-access-cli/actions/workflows/ci.yml)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Two entry points over one core: a **CLI** for direct invocation and a **DeepSeek-compatible HTTP gateway** for long-running agent runtimes. Both expose the same two capabilities — search and content extraction — and normalize Tavily, Exa, Bocha, Yuanbao Search, Brave, SearXNG, AnySearch, XCrawl, DeepSeek, Firecrawl, Jina, and others behind one schema.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Neither entry point is tied to Pi, Claude Code, Codex, Cursor, OpenCode, or any other agent. Skills, MCP integrations, and agent plugins are adapters on top; they never enter the core. Requires Node.js 22.19+.
|
|
11
11
|
|
|
12
|
-
## Capabilities
|
|
12
|
+
## Capabilities
|
|
13
13
|
|
|
14
14
|
| Capability | Provider types | Normalized output |
|
|
15
15
|
| --- | --- | --- |
|
|
16
|
-
| `search` | Tavily, Exa, Bocha, Brave, SearXNG, AnySearch, XCrawl, DeepSeek, xAI x_search, xAI web_search | `rank`, `title`, `url`, `snippet` |
|
|
17
|
-
| `extract` | Firecrawl v2, Jina Reader, Exa Contents, AnySearch, XCrawl, HTTP | Markdown `Document` |
|
|
16
|
+
| `search` | Tavily, Exa, Bocha, Yuanbao Search, Brave, SearXNG, AnySearch, XCrawl, DeepSeek, xAI x_search, xAI web_search | `rank`, `title`, `url`, `snippet` |
|
|
17
|
+
| `extract` | Firecrawl v2, Jina Reader, Exa Contents, AnySearch, XCrawl, DeepSeek Fetch, HTTP | Markdown `Document` |
|
|
18
18
|
|
|
19
|
-
A provider type
|
|
19
|
+
A provider type is an implementation; a provider instance is a configured instance of that type, so `exa_team` and `exa_personal` can coexist. Each capability has a primary `providers` route and a `providers_fallback` route; both define the enabled instances and their `auto` order.
|
|
20
20
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
Node.js 22.19 or later is required.
|
|
24
|
-
|
|
25
|
-
Install the CLI from npm:
|
|
21
|
+
## Install
|
|
26
22
|
|
|
27
23
|
```sh
|
|
28
|
-
npm install --global web-access-cli
|
|
24
|
+
npm install --global web-access-cli # or: pnpm add --global web-access-cli
|
|
29
25
|
web-access --help
|
|
30
26
|
```
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
From a checkout: `pnpm install --frozen-lockfile && pnpm check`. The gateway runs from the same checkout (`node --import tsx src/server.ts`) or built output (`pnpm start:gateway`); production deployment uses the GHCR image, whose Compose template and secret conventions are in [deploy/README.md](deploy/README.md).
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
pnpm add --global web-access-cli
|
|
36
|
-
web-access --help
|
|
37
|
-
```
|
|
30
|
+
Layout: `src/cli.ts` and `src/server.ts` are the two entries, `src/gateway/` holds the tool protocol, and `src/core/`, `src/providers/`, `src/transport/` are shared by both. Adapters map protocols only; fallback, deadlines, envelopes, and exit codes live in `src/core`.
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
## CLI
|
|
40
33
|
|
|
41
34
|
```sh
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
pnpm pack:check
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Project structure
|
|
35
|
+
web-access search "TypeScript release" --provider exa --limit 10 --freshness month
|
|
36
|
+
web-access search "browser automation" --include-domain example.com --exclude-domain blocked.example.com
|
|
37
|
+
web-access extract https://example.com/article --provider http --timeout 30000
|
|
38
|
+
web-access extract https://example.com/article --json
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
src/providers/ Provider adapters, registry, and HTTP/RSC extraction
|
|
56
|
-
src/transport/ Proxies, timeouts, redirects, and response size limits
|
|
57
|
-
schemas/ Generated JSON Schemas
|
|
58
|
-
test/ node:test unit tests and local HTTP integration tests
|
|
40
|
+
web-access providers # route, capability, credential and URL status
|
|
41
|
+
web-access doctor # local configuration checks, no network
|
|
42
|
+
web-access config init # scaffold from credential env vars
|
|
43
|
+
web-access config edit # open in $VISUAL/$EDITOR
|
|
59
44
|
```
|
|
60
45
|
|
|
61
|
-
|
|
46
|
+
Global options: `--config <path>`, `--json`, `--help`, `--version`. Capability commands print human-readable Markdown, `config init` prints the created path, and every failure prints one JSON envelope. Exit codes: `0` success, `2` invalid input/config or a disabled provider, `1` runtime or doctor failure, `130` cancelled.
|
|
62
47
|
|
|
63
|
-
|
|
48
|
+
Not provided: batch/all, answer, a PDF-only path, a Node SDK, or generic MCP. An optional [Agent Skill](skills/web-access-cli/SKILL.md) exists for source checkouts and is not published to npm.
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
pnpm install
|
|
67
|
-
pnpm lint
|
|
68
|
-
pnpm typecheck
|
|
69
|
-
pnpm test
|
|
70
|
-
pnpm build
|
|
71
|
-
pnpm pack:check
|
|
72
|
-
```
|
|
50
|
+
## HTTP gateway
|
|
73
51
|
|
|
74
|
-
|
|
52
|
+
A long-running process that serves the same providers, routing and transport over DeepSeek's [Search and Fetch API](https://api-docs.deepseek.com/zh-cn/guides/search): same endpoints, auth header, request fields and error envelope, so a client written for the official service only changes its base URL. It changes nothing about the CLI contract.
|
|
75
53
|
|
|
76
|
-
|
|
54
|
+
Run it as a Docker image — `ghcr.io/juexe/web-access-cli`, multi-arch `linux/amd64` and `linux/arm64`, published on every GitHub Release with exact version tags plus `latest` for stable releases. Deployment needs Docker only, no Node.js or pnpm:
|
|
77
55
|
|
|
78
56
|
```sh
|
|
79
|
-
web-access
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
web-access extract https://example.com/article
|
|
84
|
-
web-access extract https://example.com/article --provider http --timeout 30000
|
|
85
|
-
web-access extract https://example.com/article --json
|
|
57
|
+
docker pull ghcr.io/juexe/web-access-cli:0.7.0
|
|
58
|
+
docker compose -f deploy/compose.yaml --env-file personal/deploy.env up -d --wait gateway
|
|
59
|
+
```
|
|
86
60
|
|
|
87
|
-
|
|
88
|
-
web-access doctor
|
|
61
|
+
Or from source; it listens on `127.0.0.1:8080` by default:
|
|
89
62
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
web-access
|
|
93
|
-
|
|
63
|
+
```sh
|
|
64
|
+
# Source
|
|
65
|
+
WEB_ACCESS_CONFIG=~/.config/web-access-cli/config.json \
|
|
66
|
+
WEB_ACCESS_GATEWAY_API_KEY_FILE=/path/to/gateway-api-key \
|
|
67
|
+
node --import tsx src/server.ts
|
|
94
68
|
```
|
|
95
69
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
70
|
+
| Method | Path | Notes |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `POST` | `/v1/tools/search` | DeepSeek Search compatible |
|
|
73
|
+
| `POST` | `/v1/tools/fetch` | DeepSeek Fetch compatible |
|
|
74
|
+
| `GET` | `/healthz`, `/readyz` | Liveness and configuration readiness, no auth |
|
|
75
|
+
| `GET` | `/panel` | Admin UI shell, no auth and no data |
|
|
76
|
+
| `GET`/`DELETE` | `/admin/api/records` | Request records, Bearer key required |
|
|
77
|
+
| `GET` | `/admin/api/overview` | Runtime stats, route readiness, per-instance counters |
|
|
78
|
+
| `PUT` | `/admin/api/config` | Patch routes, budgets, instances; hot-applies and persists |
|
|
79
|
+
| `POST` | `/admin/api/config/reload` | Reload the config file from disk |
|
|
80
|
+
|
|
81
|
+
- The Bearer key is read from a file only, compared as a fixed-length digest, and duplicate `Authorization` headers are rejected. Requests are single-user private, so the gateway adds no local admission, body size limit, or body read deadline and never returns 413 or 408 itself. Responses use `Cache-Control: no-store`.
|
|
82
|
+
- Search fields: `query` (1–128 characters), `count` (1–50, default 10), `ori_question` (1–128 when set), `localization` (an IP address when non-empty), `need_content` (boolean, default `false`). Fetch takes one absolute HTTP(S) URL of at most 8192 characters. Unknown fields are ignored and cannot override provider, credentials, timeout, or configuration.
|
|
83
|
+
- Responses are protocol-compatible, not equal to the official engine: `summary` reuses `snippet`, `rerank_score` is always `0`, `authority` is always `-1`, and `site_name`/`site_icon`/`date` are `null`. Clients filtering on score or authority must change strategy, and no result count, ranking, or quality is promised.
|
|
84
|
+
- `ori_question` and `localization` are validated but have no core equivalent, so they never reach providers.
|
|
85
|
+
- `need_content=true` backfills by rank with two concurrent tasks, a 30-second window, 10 seconds per item, and a 3-second attempt cap; failures keep the result with empty content. Backfill and responses have no size cap, and **backfill spends extract quota**. Fetch and backfill derive their own config with a 1-character minimum so short pages succeed, while the source Markdown probe still requires 500 characters. Client disconnects cancel in-flight work.
|
|
86
|
+
- Errors are always `{ "error": { "message", "type": "…", "param": null, "code" } }`: 400 `invalid_request_error`, 401 `invalid_api_key`, 402 `insufficient_quota`, 404 `no_usable_content` or an unknown route, 405 wrong method, 503 while shutting down, 504 `upstream_timeout`, and 500 for provider, credential, or configuration failures. 429 only ever means the upstream provider rate-limited.
|
|
87
|
+
- Tool requests never rewrite provider order: concurrent traffic does not trigger config write-back. The panel's config workspace and `/admin/api/config*` are the only writers, and they hot-apply plus persist atomically (with a read-only fallback when the file is mounted as a secret).
|
|
88
|
+
- The panel keeps the last 500 requests in memory: status, duration, error, provider, query/URL, attempt chain, snippets, and truncated content previews (2 000 chars for search results, 4 000 for Fetch) — never full bodies, headers, or credentials. A `#apikey=<key>` fragment logs in, is stripped from the address bar, and stores the key in `sessionStorage`.
|
|
89
|
+
|
|
90
|
+
### Use it from DSH
|
|
91
|
+
|
|
92
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) ships `@deepseek-ai/dsh-web-deepseek-api`, a provider that speaks exactly this API, so pointing `web_search` and `web_fetch` here needs no extra plugin — only a base URL. In `~/.dsh/cordis.patch.yml`:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
- id: web
|
|
96
|
+
name: "@deepseek-ai/dsh-web"
|
|
97
|
+
config:
|
|
98
|
+
searchProvider: deepseek-search-api
|
|
99
|
+
fetchProvider: deepseek-fetch-api
|
|
100
|
+
- id: web-deepseek-api
|
|
101
|
+
name: "@deepseek-ai/dsh-web-deepseek-api"
|
|
102
|
+
config:
|
|
103
|
+
apiKeyEnv: DEEPSEEK_API_KEY # must hold the gateway key, not a DeepSeek key
|
|
104
|
+
baseURL: http://127.0.0.1:8080/v1/tools
|
|
105
|
+
```
|
|
99
106
|
|
|
100
|
-
|
|
101
|
-
- `--json`: Output a compact schema v2 JSON envelope. Without it, successful commands write human-readable Markdown; all failures always write JSON.
|
|
102
|
-
- `--help`, `--version`: Print standard CLI help or version text.
|
|
107
|
+
`baseURL` is the full base including the path prefix and excluding `/search` or `/fetch`; the default `apiKeyEnv` is `DEEPSEEK_API_KEY`, so point it at a variable holding the gateway key. `web_search` and `web_fetch` then hit this gateway with no other change — only the placeholder score/authority fields differ, as noted above. Because the gateway is private, keep it on a trusted network rather than exposing it publicly.
|
|
103
108
|
|
|
104
|
-
|
|
109
|
+
Deployment, secrets, upgrades and rollback: [deploy/README.md](deploy/README.md). Decision record: [ADR 0019](docs/adr/0019-deepseek-tool-gateway.md).
|
|
105
110
|
|
|
106
111
|
## Configuration
|
|
107
112
|
|
|
108
|
-
|
|
113
|
+
Strict JSON; unknown fields are rejected. Resolution order is `--config`, then `WEB_ACCESS_CONFIG`, then `~/.config/web-access-cli/config.json`. A missing default file falls back to built-in defaults; a missing explicit file is a `config_error`. No parent directories are scanned.
|
|
109
114
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`web-access config edit` creates parent directories and a complete default configuration when the file is missing, then opens it with the command in `VISUAL`, or `EDITOR` when `VISUAL` is empty. The value may contain a quoted executable path and arguments; it is parsed without shell evaluation and the configuration path is appended as a separate argument. An existing file is opened byte-for-byte as-is, even when it is temporarily invalid JSON; it is never overwritten or reformatted. The command waits only for the editor process to start, not for it to close. When neither variable is set, the command returns `open_failed`; a configuration file that was created successfully remains available for manual editing.
|
|
113
|
-
|
|
114
|
-
`config init` creates a new configuration from standard credential environment variables and refuses to overwrite an existing file. It keeps all built-in instance definitions, writes detected Search providers to the primary route, leaves Search fallback empty, writes detected remote Extract providers to the primary route, and places `http` in the Extract fallback route. After an `auto` capability call succeeds or encounters fallback-eligible failures, the CLI atomically reorders the matching `providers` or `providers_fallback` array in place. Members never move between groups. The legacy `_providers` and `_providers_fallback` fields are rejected as unknown configuration fields.
|
|
115
|
-
|
|
116
|
-
The complete JSON Schema is available at [schemas/config.schema.json](schemas/config.schema.json). Example:
|
|
115
|
+
`config edit` creates a full default file when absent and opens it with `VISUAL`, else `EDITOR`, without shell evaluation or reformatting; `config init` scaffolds from standard credential environment variables and refuses to overwrite. Environment variables override plaintext keys. The full schema is [schemas/config.schema.json](schemas/config.schema.json):
|
|
117
116
|
|
|
118
117
|
```json
|
|
119
118
|
{
|
|
120
|
-
"$schema": "https://unpkg.com/web-access-cli@0.
|
|
119
|
+
"$schema": "https://unpkg.com/web-access-cli@0.7.0/schemas/config.schema.json",
|
|
121
120
|
"providers": [
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
"type": "exa",
|
|
125
|
-
"apiKeyEnv": "TEAM_EXA_API_KEY",
|
|
126
|
-
"headers": {
|
|
127
|
-
"X-Team": "docs"
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
"id": "searx_local",
|
|
132
|
-
"type": "searxng",
|
|
133
|
-
"baseUrl": "http://127.0.0.1:8080"
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"id": "firecrawl_local",
|
|
137
|
-
"type": "firecrawl",
|
|
138
|
-
"baseUrl": "http://127.0.0.1:3002"
|
|
139
|
-
}
|
|
121
|
+
{ "id": "exa_team", "type": "exa", "apiKeyEnv": "TEAM_EXA_API_KEY" },
|
|
122
|
+
{ "id": "firecrawl_local", "type": "firecrawl", "baseUrl": "http://127.0.0.1:3002" }
|
|
140
123
|
],
|
|
141
|
-
"search": {
|
|
142
|
-
|
|
143
|
-
"providers_fallback": ["deepseek"],
|
|
144
|
-
"limit": 5,
|
|
145
|
-
"timeoutMs": 120000,
|
|
146
|
-
"attemptTimeoutMs": 60000,
|
|
147
|
-
"maxResponseBytes": 5242880
|
|
148
|
-
},
|
|
149
|
-
"extract": {
|
|
150
|
-
"providers": ["firecrawl_local", "jina", "exa_team"],
|
|
151
|
-
"providers_fallback": ["http"],
|
|
152
|
-
"timeoutMs": 120000,
|
|
153
|
-
"attemptTimeoutMs": 45000,
|
|
154
|
-
"maxResponseBytes": 5242880,
|
|
155
|
-
"minContentCharacters": 500
|
|
156
|
-
}
|
|
124
|
+
"search": { "providers": ["exa_team"], "providers_fallback": ["brave"] },
|
|
125
|
+
"extract": { "providers": ["firecrawl_local"], "providers_fallback": ["http"] }
|
|
157
126
|
}
|
|
158
127
|
```
|
|
159
128
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Default routes:
|
|
163
|
-
|
|
164
|
-
- Search primary: `tavily -> exa -> bocha -> brave -> searxng -> anysearch -> xcrawl`
|
|
165
|
-
- Search fallback: `deepseek -> xai_x_search -> xai_web_search`
|
|
166
|
-
- Extract primary: `firecrawl -> jina -> exa -> anysearch -> xcrawl`
|
|
167
|
-
- Extract fallback: `http`
|
|
168
|
-
|
|
169
|
-
The default routes split built-in providers into a primary group and a fallback group. Custom instance IDs are merged into the instance list but must still be added to a route explicitly. An omitted capability route uses the defaults above; an explicit Search primary empty array is invalid, while Extract may be explicitly disabled with an empty primary route. In `auto` mode, incompletely configured instances are skipped. AnySearch and XCrawl accept `searchFilterMode`: `strict` (default; freshness skips the provider) or `best_effort` (rewrites freshness into a query fragment). Domain constraints are rewritten into the query and strictly re-applied locally. XCrawl Extract always uses synchronous Scrape with Markdown output; Map, Crawl, and asynchronous jobs are outside the current CLI capabilities.
|
|
129
|
+
Built-in instances: `tavily`, `exa`, `bocha`, `yuanbao`, `brave`, `searxng`, `anysearch`, `xcrawl`, `deepseek`, `firecrawl`, `jina`, `http`, `xai_x_search`, `xai_web_search`. Reusing a built-in ID overrides that instance; a new ID adds another of the same type. An instance is enabled only when listed in a route. Default routes:
|
|
170
130
|
|
|
171
|
-
|
|
131
|
+
- Search: `tavily -> exa -> bocha -> yuanbao -> brave -> searxng -> anysearch -> xcrawl -> deepseek`, fallback `xai_x_search -> xai_web_search`
|
|
132
|
+
- Extract: `firecrawl -> jina -> exa -> anysearch -> xcrawl -> deepseek`, fallback `http`
|
|
172
133
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
### Credentials and URLs
|
|
176
|
-
|
|
177
|
-
Environment variables take precedence over plaintext keys in JSON. Built-in instances support these standard variables:
|
|
134
|
+
An explicit empty Search primary route is invalid; Extract can be disabled with an empty primary route. AnySearch and XCrawl take `searchFilterMode`: `strict` (skip on freshness) or `best_effort` (rewrite freshness into the query). Yuanbao posts `Query`/`Site`/`Freshness` to `/SearchPro` and keeps in-body errors fallback-eligible. DeepSeek uses `/v1/tools/search` and `/v1/tools/fetch`, rewriting freshness and domains into `after:`/`site:` hints that are re-applied locally. xAI reads an OAuth JSON from `XAI_AUTH_JSON` without refreshing it and needs a raised `attemptTimeoutMs`.
|
|
178
135
|
|
|
179
136
|
| Type | API key | Base URL |
|
|
180
137
|
| --- | --- | --- |
|
|
181
|
-
| Tavily | `
|
|
182
|
-
| Exa | `EXA_API_KEY` | `EXA_BASE_URL` |
|
|
138
|
+
| Tavily / Exa / Brave / Firecrawl / XCrawl | `<NAME>_API_KEY` | `<NAME>_BASE_URL` |
|
|
183
139
|
| Bocha | `BOCHA_API_KEY` | `BOCHA_BASE_URL`, default `https://api.bocha.cn` |
|
|
184
|
-
| Brave | `BRAVE_API_KEY` | `BRAVE_BASE_URL` |
|
|
185
140
|
| SearXNG | None | `SEARXNG_BASE_URL`, required |
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
| AnySearch | `ANYSEARCH_API_KEY`, optional | `ANYSEARCH_BASE_URL`, default `https://api.anysearch.com` |
|
|
190
|
-
| XCrawl | `XCRAWL_API_KEY` | `XCRAWL_BASE_URL`, default `https://run.xcrawl.com` |
|
|
191
|
-
| DeepSeek | `DEEPSEEK_API_KEY` | No standard environment variable; default `https://api.deepseek.com/anthropic/v1` |
|
|
192
|
-
| xAI Search | `XAI_AUTH_JSON` | No standard API-key variable; default `https://cli-chat-proxy.grok.com/v1`, model `grok-4.6` |
|
|
193
|
-
|
|
194
|
-
Custom instances use `apiKeyEnv` and `baseUrlEnv` to name their environment variables. Every remote provider can define a `baseUrl` and additional `headers`. The public `api.firecrawl.dev` service requires a key; a self-hosted Firecrawl v2 instance can run without one.
|
|
141
|
+
| Jina / AnySearch | `JINA_API_KEY` / `ANYSEARCH_API_KEY`, optional | `<NAME>_BASE_URL` |
|
|
142
|
+
| DeepSeek | `DEEPSEEK_API_KEY` | fixed `https://api.deepseek.com` |
|
|
143
|
+
| xAI Search | `XAI_AUTH_JSON` (OAuth file) | default `https://cli-chat-proxy.grok.com/v1`, `grok-4.6` |
|
|
195
144
|
|
|
196
|
-
|
|
145
|
+
Custom instances name their variables with `apiKeyEnv`/`baseUrlEnv` and may set `baseUrl` and `headers`. Public Firecrawl Cloud needs a key; self-hosted Firecrawl v2 does not.
|
|
197
146
|
|
|
198
|
-
|
|
147
|
+
## Routing and limits
|
|
199
148
|
|
|
200
|
-
|
|
201
|
-
2. A final provider HTTP response outside 2xx, including authentication, request, rate-limit, and server errors, causes the router to try the next instance.
|
|
202
|
-
3. Other recoverable errors, including network errors, timeouts, oversized responses, and missing usable content, also cause the router to continue.
|
|
203
|
-
4. Non-recoverable failures without a final non-2xx response, including invalid input and unsupported content detected in a successful response, stop execution immediately.
|
|
204
|
-
5. If every instance in both groups fails, the command returns `provider_exhausted`. The best short content produced during extraction is preserved in `partial`.
|
|
149
|
+
Both entry points call the same router and differ only in output shape, per-command options, and the gateway's fixed Fetch/backfill thresholds.
|
|
205
150
|
|
|
206
|
-
|
|
151
|
+
`auto` walks the primary route, then the fallback route once the primary group has failed in a way that is fallback-eligible. Incomplete instances are recorded and skipped; non-2xx provider responses and recoverable errors (network, timeout, oversized, no usable content) continue to the next instance; invalid input and unsupported content stop immediately; full exhaustion returns `provider_exhausted` with the best short body in `partial`. An explicit `--provider` runs once with no fallback and returns `provider_disabled` if not routed. `retryable` describes retrying the same provider, not the switch condition.
|
|
207
152
|
|
|
208
|
-
|
|
153
|
+
`extract --provider auto` first probes one Markdown variant when HTTP is enabled — `.md` for file-like paths, `index.md` for directories — accepting only sufficiently long non-HTML text. Misses fall through transparently; hits are attributed to the HTTP instance without reordering.
|
|
209
154
|
|
|
210
|
-
|
|
155
|
+
In the CLI, after each `auto` call the successful instance moves to the front of its group and fallback-eligible failures move to the back; nothing crosses groups. Search and Extract learn independently. Explicit instances, non-fallback failures, and cancellations never reorder, and writes are atomic with last-writer-wins. The gateway never rewrites routes.
|
|
211
156
|
|
|
212
|
-
|
|
157
|
+
Defaults: 120 s total and 60 s per attempt for Search; 120 s and 45 s for Extract; 5 MiB per response. `--timeout` overrides only the total timeout.
|
|
213
158
|
|
|
214
|
-
|
|
159
|
+
## Output
|
|
215
160
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Capability commands use output schema version 2. Their default envelope is intentionally small:
|
|
161
|
+
Capability commands emit schema version 2. With `--json`, output contains `schemaVersion`, `ok`, the final instance and `data`; failures add a compact `error`, `attempts` (instance, code, optional status), and possibly a `partial` document. A failed order write adds `warnings` without replacing the result. Human-readable output is Markdown: a `provider` front matter block plus either a numbered result list or the extracted document; diagnostics print a heading plus JSON. Scripts should pass `--json`.
|
|
219
162
|
|
|
220
163
|
```json
|
|
221
|
-
{
|
|
222
|
-
"
|
|
223
|
-
"ok": true,
|
|
224
|
-
"provider": "exa",
|
|
225
|
-
"data": {
|
|
226
|
-
"results": [
|
|
227
|
-
{
|
|
228
|
-
"rank": 1,
|
|
229
|
-
"title": "Example",
|
|
230
|
-
"url": "https://example.com/",
|
|
231
|
-
"snippet": "Example snippet"
|
|
232
|
-
}
|
|
233
|
-
]
|
|
234
|
-
}
|
|
235
|
-
}
|
|
164
|
+
{ "schemaVersion": 2, "ok": true, "provider": "exa",
|
|
165
|
+
"data": { "results": [{ "rank": 1, "title": "Example", "url": "https://example.com/", "snippet": "..." }] } }
|
|
236
166
|
```
|
|
237
167
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
For human-readable output, extraction starts with YAML front matter and then the provider-normalized Markdown body. Search results use a numbered Markdown list; diagnostics and config edit use a heading plus pretty-printed JSON data. Metadata values use JSON double-quoted scalar encoding and the body is not modified. Existing scripts and other machine consumers should append `--json` explicitly.
|
|
241
|
-
|
|
242
|
-
Example default extract output:
|
|
243
|
-
|
|
244
|
-
```markdown
|
|
245
|
-
---
|
|
246
|
-
provider: "local_http"
|
|
247
|
-
url: "https://example.com/article"
|
|
248
|
-
title: "Example title"
|
|
249
|
-
---
|
|
250
|
-
|
|
251
|
-
# Article content
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
All success and failure envelopes use schema version 2. Stable schemas are available in [schemas](schemas).
|
|
255
|
-
|
|
256
|
-
Exit codes:
|
|
257
|
-
|
|
258
|
-
- `0`: Success
|
|
259
|
-
- `2`: Invalid input or configuration, or an unknown or disabled provider
|
|
260
|
-
- `1`: Runtime, provider, doctor, or editor launch failure
|
|
261
|
-
- `130`: User cancellation
|
|
262
|
-
|
|
263
|
-
`providers` lists each instance's type, capabilities, route status, credential source, and base URL source. Its `searchRoute`/`searchFallbackRoute` and `extractRoute`/`extractFallbackRoute` fields show the two effective orders for the next `auto` call. `doctor` performs local configuration checks only and does not call remote APIs. If an enabled route contains an unconfigured instance, the command returns `doctor_failed` with exit code `1`.
|
|
264
|
-
|
|
265
|
-
## HTTP and security boundaries
|
|
266
|
-
|
|
267
|
-
- Honors `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and `NO_PROXY` by default.
|
|
268
|
-
- Follows up to five redirects. Cross-origin redirects remove authentication, Cookie, and common token headers.
|
|
269
|
-
- Enforces a streaming byte limit on every response instead of buffering an unbounded response first.
|
|
270
|
-
- HTTP Extract uses LinkeDOM, Mozilla Readability, and Turndown, with fallback parsing for Next.js RSC payloads.
|
|
271
|
-
- HTTP Extract prefers an enabled source Markdown variant (`.md` or directory `index.md`) before provider routes when that variant returns usable Markdown.
|
|
272
|
-
- Returns `unsupported_content` for PDF, image, audio, video, zip, and general binary content.
|
|
273
|
-
- By design, the CLI does not block localhost, private network addresses, or cloud metadata URLs. For untrusted input, callers must enforce their own URL allowlist, network isolation, or outbound proxy policy.
|
|
168
|
+
`providers` reports each instance's type, capabilities, route status, and credential/URL sources together with the effective orders for the next call; `doctor` fails with `1` when an enabled route contains an unconfigured instance.
|
|
274
169
|
|
|
275
|
-
##
|
|
170
|
+
## Security boundaries
|
|
276
171
|
|
|
277
|
-
|
|
172
|
+
- Honors `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and `NO_PROXY`; follows up to five redirects and strips authentication, Cookie, and token headers across origins.
|
|
173
|
+
- Enforces a streaming 5 MiB limit rather than buffering first. HTTP extraction uses LinkeDOM, Readability, and Turndown with an RSC fallback; binaries and media return `unsupported_content`.
|
|
174
|
+
- Neither entry point blocks localhost, private addresses, or cloud metadata URLs by design. Untrusted callers must add their own allowlist, isolation, or egress policy; the gateway targets a private single-user deployment behind its Bearer key.
|
|
278
175
|
|
|
279
|
-
|
|
176
|
+
## Scope
|
|
280
177
|
|
|
281
|
-
|
|
178
|
+
Provider and extraction implementations draw heavily on `pi-web-access`; Pi-specific registration, tool protocols, and UI are excluded. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md). Licensed under MIT with attribution in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|