@qilin-agent/web-fetch-http 0.0.0-stage → 3.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkutysllb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/web/web-fetch-http/README.md
5
+ /:
6
+ en: a55f23f2363fdbd6
7
+ zh: b1fd7328e041ec52
8
+ /qilin-web-fetch-http:
9
+ en: 38829ee8fd1e419d
10
+ zh: 4db2b1a68b753c01
11
+ /qilin-web-fetch-http/summary:
12
+ en: 4ad5cf8fe6b3121f
13
+ zh: d405bb551d343807
14
+ /qilin-web-fetch-http/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /qilin-web-fetch-http/use-this-package:
18
+ en: 630b12516d53e905
19
+ zh: eed124cfe9c8dc1f
20
+ /qilin-web-fetch-http/use-this-package/when-to-choose-it:
21
+ en: eb52e77279c21d6f
22
+ zh: 0560e89caa377bfc
23
+ /qilin-web-fetch-http/use-this-package/minimal-configuration:
24
+ en: b4eecdca0c9f5e68
25
+ zh: 34f208d7ff205c20
26
+ /qilin-web-fetch-http/use-this-package/what-a-fetch-returns:
27
+ en: 2d18cdcc9b53dd0f
28
+ zh: 3bc6ca7c5933ee5d
29
+ /qilin-web-fetch-http/use-this-package/transport-behavior:
30
+ en: c432147daf2877cf
31
+ zh: c14d507dd66e124a
32
+ /qilin-web-fetch-http/use-this-package/failures-and-recovery:
33
+ en: 35407f106dc65deb
34
+ zh: 02118072fba68b8f
35
+ /qilin-web-fetch-http/understand-the-implementation:
36
+ en: 9842c69ad3636bb3
37
+ zh: 3cdc3f816cbdd0cb
38
+ /qilin-web-fetch-http/understand-the-implementation/design-philosophy:
39
+ en: b09832c1e63fee37
40
+ zh: 88427bd3fc022d34
41
+ /qilin-web-fetch-http/understand-the-implementation/source-map:
42
+ en: 023ca0c9a9ef4968
43
+ zh: 74a5785a493f5111
44
+ /qilin-web-fetch-http/understand-the-implementation/read-path:
45
+ en: 749c0ffeac145807
46
+ zh: 1468821a33a9518a
47
+ /qilin-web-fetch-http/further-exploration:
48
+ en: a74a50853efdc0c5
49
+ zh: 0d9c9b6340e07932
50
+ /qilin-web-fetch-http/model-experience:
51
+ en: 771846af95daa886
52
+ zh: 20b3cb9193b74660
53
+ /qilin-web-fetch-http/model-experience/kv-cache-effect:
54
+ en: 40294c1c31b74824
55
+ zh: d75d48f485447a74
56
+ /qilin-web-fetch-http/known-limitations-and-deferred-work:
57
+ en: 4d935fd8f466a234
58
+ zh: c398eb7aa737f5ea
59
+ /qilin-web-fetch-http/known-limitations-and-deferred-work/dev-note:
60
+ en: e67f633dc6fe7773
61
+ zh: 3bb0c20c93f16f98
package/README.md CHANGED
@@ -1,3 +1,145 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "The anonymous public HTTP(S) fetch backend for ctx.web: how deployments mount bounded, safe URL retrieval with same-origin redirects and text-only decoding."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @qilin-agent/web-fetch-http
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ With `qilin-web-fetch-http`, the harness can fetch public HTTP(S) pages through the web service (`ctx.web`) and get their status code plus bounded, decoded content without sending credentials. Choose it when a composition needs safe retrieval with URL validation, public-address resolution, connection pinning, same-origin redirects, byte and character caps, and an explicit product `User-Agent`. It returns non-2xx responses as results rather than errors, and rejects non-public destinations, binary data, and unsupported content types. The model-facing `web_fetch` tool lives in `qilin-tool-web`, which renders this provider's bodies.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount the provider in a composition that already loads the web service; it registers as the `http` fetch provider, so `ctx.web.fetch()` resolves it automatically when it is the only usable fetch backend — or pin it with `fetchProvider: http`.
29
+
30
+ ### When to choose it
31
+
32
+ Choose this backend when a deployment must fetch public pages with bounded output and safe transport: no credentials are sent, every resolved address must be public, each connection is pinned to the validated answer set, redirects cannot escape the origin, and every response is capped.
33
+
34
+ ### Minimal configuration
35
+
36
+ Load the web service and the provider; configurable limits have safe defaults and validate at plugin construction, so an invalid value fails loudly instead of building a provider with nonsensical caps. The URL security limit is fixed at 2,048 characters.
37
+
38
+ ```yaml
39
+ - name: '@qilin-agent/web'
40
+ - name: '@qilin-agent/web-fetch-http'
41
+ ```
42
+
43
+ | Field | Default | Meaning |
44
+ |---|---|---|
45
+ | `maxResponseBytes` | `5,000,000` | Maximum response body size in bytes |
46
+ | `maxBodyChars` | `100,000` | Maximum decoded body length in characters |
47
+ | `timeoutMs` | `30,000` | Fetch timeout — a resource backstop, not the model-facing tool budget |
48
+ | `maxRedirects` | `5` | Maximum same-origin redirect hops (`0` follows none) |
49
+ | `userAgent` | `deepseek-harness/…` | `User-Agent` header sent on every request |
50
+
51
+ The generated [configuration catalog](../../../docs/config-catalog.md#qilin-agentweb-fetch-http) is the exhaustive source for every accepted field and its JSDoc.
52
+
53
+ ### What a fetch returns
54
+
55
+ A successful call yields a `WebFetchResult`: the final URL after allowed redirects, the HTTP status code, a decoded body classified as `html` or `text`, and a `truncated` flag. A non-2xx response is a result, not an error — the status code is part of the fetched resource state; `WebError` is reserved for failures to safely retrieve or represent the resource.
56
+
57
+ ```text
58
+ const page = await ctx.web.fetch({ url: 'https://example.com' })
59
+ // page.body.kind === 'html' | 'text'; page.statusCode === 200 | 404 | ...
60
+ ```
61
+
62
+ ### Transport behavior
63
+
64
+ The provider keeps requests anonymous and bounded: it accepts only `http:` and `https:` URLs without embedded credentials and rejects URLs over 2,048 characters. It resolves each hostname once, rejects the complete result if any IPv4 or IPv6 address is not public unicast, and pins the connection to that validated set. IPv6 checks discover the active DNS64 prefix and reject translations to non-public IPv4. Each same-origin redirect repeats resolution and pinning; cross-origin redirects fail and require a fresh call. The provider also enforces byte, character, hop, and time caps, rejects unsupported content types, and sends an explicit product `User-Agent`.
65
+
66
+ ### Failures and recovery
67
+
68
+ Failures throw `WebError` with a machine-routable code: `WEB_INVALID_URL`, `WEB_BLOCKED_URL`, `WEB_FETCH_TOO_LARGE`, `WEB_FETCH_TIMEOUT`, `WEB_REDIRECT_BLOCKED`, `WEB_UNSUPPORTED_CONTENT_TYPE`, `WEB_ABORTED`, or `WEB_PROVIDER_ERROR`. Direct callers can route on the code; the model-facing `web_fetch` tool surfaces the failure text to the model under its own error wrapper.
69
+
70
+ -----
71
+
72
+ <a id="understand-the-implementation"></a>
73
+ ## Understand the implementation
74
+
75
+ <details>
76
+ <summary>Implementation internals — click to expand</summary>
77
+
78
+ This section explains the design decisions behind the provider; the observable behavior is fully covered in [Use this package](#use-this-package).
79
+
80
+ ### Design philosophy
81
+
82
+ The package is built on one separation and one layered timeout:
83
+
84
+ - **Safe retrieval vs. presentation.** This provider owns URL validation, public-address enforcement, connection pinning, HTTP transport, redirect policy, caps, charset decoding, and binary rejection; `qilin-tool-web` owns HTML→markdown and truncation formatting. A non-2xx response is data, not failure.
85
+ - **Two timeout layers.** The provider's `timeoutMs` is a resource backstop for direct `ctx.web.fetch()` callers; the model-facing tool-call budget belongs to `qilin-tool-call-timeout-policy`, which arms `exec.signal`. When the outer deadline fires first the provider reports `WEB_ABORTED` and the policy replaces it with `TOOL_TIMEOUT`; `WEB_FETCH_TIMEOUT` therefore identifies a direct service caller whose provider budget elapsed.
86
+
87
+ ### Source map
88
+
89
+ | File | Role |
90
+ |---|---|
91
+ | [`src/index.ts`](src/index.ts) | Plugin entry: config schema, limit validation, provider registration |
92
+ | [`src/provider.ts`](src/provider.ts) | The `HttpFetchProvider`: pinned transport, redirect following, capped reads, charset decoding |
93
+ | [`src/network.ts`](src/network.ts) | Public-address resolution, DNS64 discovery, and connection pinning |
94
+ | [`src/policy.ts`](src/policy.ts) | URL validation, same-origin checks, content-type classification, charset parsing |
95
+
96
+ ### Read path
97
+
98
+ A fetch validates the URL, resolves the hostname once, rejects the complete answer set when any address is not public, and pins the connection to the accepted addresses. It repeats that check for each same-origin redirect; a cross-origin redirect or non-public target fails before response bytes are accepted. The final response is classified by `Content-Type`, decoded from its declared charset, and read under the byte cap; the decoded text is then truncated to the character cap.
99
+
100
+ </details>
101
+
102
+ -----
103
+
104
+ <a id="further-exploration"></a>
105
+ ## Further Exploration
106
+
107
+ Read these pages when the package-level contract is not enough. They move from the shared vocabulary to the service, the model-facing tools, and the design rationale.
108
+
109
+ - [Web subsystem](../../../docs/subsystems/web.md) — the exhaustive fetch request/result vocabulary and error codes.
110
+ - [Web package map](../README.md) — the six-package family and each role.
111
+ - [qilin-web](../web/README.md) — the web service this provider registers into.
112
+ - [qilin-tool-web](../tool-web/README.md) — the model-facing `web_fetch` tool that renders this provider's bodies.
113
+ - [Generated configuration catalog](../../../docs/config-catalog.md#qilin-agentweb-fetch-http) — every accepted config field and its source declaration.
114
+ - [Web capability seam decision](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md) — why search and fetch share one provider-selection service.
115
+
116
+ -----
117
+
118
+ <a id="model-experience"></a>
119
+ ## Model Experience
120
+
121
+ Indirectly, through `qilin-tool-web`, which renders this provider's `maxBodyChars`-bounded decoded text or markdown-shaped HTML under its fetch-result wrapper while redirects, headers, and transport limits remain hidden.
122
+
123
+ #### KV Cache effect
124
+
125
+ No direct invalidation; the named consumer owns any request-prefix changes.
126
+
127
+ ## Known Limitations and Deferred Work
128
+
129
+ <a id="known-limitations-and-deferred-work"></a>
130
+
131
+
132
+ These limits define when the provider is unsafe or a poor fit. They are current package constraints.
133
+
134
+ - **Only textual content decodes** — html/xhtml and `text/*` plus JSON/XML families; a missing `Content-Type` or any binary type throws `WEB_UNSUPPORTED_CONTENT_TYPE`, and text-extractable PDF decoding is named deferred work.
135
+ - **Charset comes only from the `Content-Type` header** (UTF-8 default) — an HTML `<meta charset>` declaration is ignored, and a declared-but-unrecognized charset label throws rather than falling back.
136
+
137
+ <a id="dev-note"></a>
138
+ ### Dev Note
139
+
140
+ <details>
141
+ <summary>Working context for maintainers — click to expand</summary>
142
+
143
+ None.
144
+
145
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,145 @@
1
+ ---
2
+ description: "ctx.web 的匿名公共 HTTP(S) 抓取后端:部署方如何挂载有界、安全的 URL 抓取,含同源重定向与仅文本解码。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @qilin-agent/web-fetch-http
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 有了 `qilin-web-fetch-http`,harness 可以通过 web 服务(`ctx.web`)抓取公共 HTTP(S) 页面,并在不发送凭据的情况下获得状态码与有界、解码后的内容。当组合需要 URL 校验、公开地址解析、连接固定、仅同源重定向、字节和字符上限及显式产品 `User-Agent` 时选择它。它把非 2xx 响应作为结果而非错误返回,并拒绝非公开目标、二进制数据与不受支持的内容类型。面向模型的 `web_fetch` 工具位于 `qilin-tool-web`,由它渲染本提供方的正文。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在已加载 web 服务的组合中挂载本提供方;它以 `http` 抓取提供方身份注册,因此当它是唯一可用的抓取后端时,`ctx.web.fetch()` 会自动解析到它——也可以用 `fetchProvider: http` 固定。
29
+
30
+ ### 何时选择
31
+
32
+ 当部署必须以有界输出和安全传输抓取公共页面时选择此后端:不发送凭据,每个已解析地址必须是公共地址,每次连接都固定到已校验的地址集合,重定向无法逃出源站,每个响应都有上限。
33
+
34
+ ### 最小配置
35
+
36
+ 加载 web 服务与本提供方;可配置上限都有安全默认值,并在插件构造时验证,因此无效值会直接报错,而不是构造出上限荒谬的提供方。URL 安全上限固定为 2,048 个字符。
37
+
38
+ ```yaml
39
+ - name: '@qilin-agent/web'
40
+ - name: '@qilin-agent/web-fetch-http'
41
+ ```
42
+
43
+ | 字段 | 默认值 | 含义 |
44
+ |---|---|---|
45
+ | `maxResponseBytes` | `5,000,000` | 响应主体最大字节数 |
46
+ | `maxBodyChars` | `100,000` | 解码主体最大字符数 |
47
+ | `timeoutMs` | `30,000` | 抓取超时——资源兜底,不是面向模型的工具预算 |
48
+ | `maxRedirects` | `5` | 同源重定向最大跳数(`0` 表示不跟随) |
49
+ | `userAgent` | `deepseek-harness/…` | 每次请求发送的 `User-Agent` 标头 |
50
+
51
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#qilin-agentweb-fetch-http)是每个受支持字段及其 JSDoc 的穷尽式真源。
52
+
53
+ ### 抓取返回什么
54
+
55
+ 成功调用产生 `WebFetchResult`:允许的重定向之后的最终 URL、HTTP 状态码、分类为 `html` 或 `text` 的解码正文,以及 `truncated` 标志。非 2xx 响应是结果而非错误——状态码是被抓取资源状态的一部分;`WebError` 只用于无法安全获取或表示资源的失败。
56
+
57
+ ```text
58
+ const page = await ctx.web.fetch({ url: 'https://example.com' })
59
+ // page.body.kind === 'html' | 'text'; page.statusCode === 200 | 404 | ...
60
+ ```
61
+
62
+ ### 传输行为
63
+
64
+ 提供方保持请求匿名且有界:只接受不含内嵌凭据且不超过 2,048 个字符的 `http:` 与 `https:` URL。它只解析一次主机名;只要结果中有任何 IPv4 或 IPv6 地址不是公共单播地址,就拒绝整个结果,并把连接固定到已校验的地址集合。IPv6 检查会发现活动 DNS64 前缀,并拒绝指向非公开 IPv4 的转换地址。每次同源重定向都会重复解析与固定;跨源重定向会失败并要求重新调用。提供方还强制执行字节、字符、跳数和时间上限,拒绝不支持的内容类型,并发送显式产品 `User-Agent`。
65
+
66
+ ### 失败与恢复
67
+
68
+ 失败会抛出 `WebError`,其中包含可供程序路由的错误码:`WEB_INVALID_URL`、`WEB_BLOCKED_URL`、`WEB_FETCH_TOO_LARGE`、`WEB_FETCH_TIMEOUT`、`WEB_REDIRECT_BLOCKED`、`WEB_UNSUPPORTED_CONTENT_TYPE`、`WEB_ABORTED` 或 `WEB_PROVIDER_ERROR`。直接调用方可以按错误码路由;面向模型的 `web_fetch` 工具会在自己的错误包装层内把失败文本呈现给模型。
69
+
70
+ -----
71
+
72
+ <a id="understand-the-implementation"></a>
73
+ ## 理解实现
74
+
75
+ <details>
76
+ <summary>实现细节——点击展开</summary>
77
+
78
+ 本节解释提供方背后的设计决策;可观察行为已在[使用本包](#use-this-package)中完整说明。
79
+
80
+ ### 设计理念
81
+
82
+ 本包基于一项职责分离和一套分层超时机制:
83
+
84
+ - **安全获取与呈现分离。** 本提供方拥有 URL 校验、公开地址强制规则、连接固定、HTTP 传输、重定向策略、上限、charset 解码与二进制拒绝;`qilin-tool-web` 拥有 HTML→markdown 与截断格式化。非 2xx 响应是数据,不是失败。
85
+ - **两层超时。** 提供方的 `timeoutMs` 是直接 `ctx.web.fetch()` 调用方的资源兜底;面向模型的工具调用预算属于 `qilin-tool-call-timeout-policy`,由它触发 `exec.signal`。外层截止期限先到时,提供方报告 `WEB_ABORTED`,策略再以 `TOOL_TIMEOUT` 替换;因此 `WEB_FETCH_TIMEOUT` 标识的是提供方预算耗尽的直接服务调用方。
86
+
87
+ ### 源码地图
88
+
89
+ | 文件 | 职责 |
90
+ |---|---|
91
+ | [`src/index.ts`](src/index.ts) | 插件入口:配置 schema、上限验证、提供方注册 |
92
+ | [`src/provider.ts`](src/provider.ts) | `HttpFetchProvider`:固定连接、重定向跟随、有界读取、charset 解码 |
93
+ | [`src/network.ts`](src/network.ts) | 公开地址解析、DNS64 发现与连接固定 |
94
+ | [`src/policy.ts`](src/policy.ts) | URL 校验、同源检查、内容类型分类、charset 解析 |
95
+
96
+ ### 读取路径
97
+
98
+ 抓取先校验 URL,只解析一次主机名,结果中只要有非公开地址就拒绝,并把连接固定到已接受地址。每次同源重定向都重复该检查;跨源重定向或非公开目标在接收响应字节前失败。最终响应按 `Content-Type` 分类、依声明的 charset 解码,并在字节上限内读取;解码后的文本再截断到字符上限。
99
+
100
+ </details>
101
+
102
+ -----
103
+
104
+ <a id="further-exploration"></a>
105
+ ## 进一步探索
106
+
107
+ 当包级约定不够用时阅读以下页面。它们从共享词汇逐步进入服务、面向模型的工具与设计依据。
108
+
109
+ - [web 子系统](../../../docs/subsystems/web.zh.md)——穷尽式的抓取请求/结果词汇与错误码。
110
+ - [web 包映射](../README.zh.md)——六包家族与各角色。
111
+ - [qilin-web](../web/README.zh.md)——本提供方注册进入的 web 服务。
112
+ - [qilin-tool-web](../tool-web/README.zh.md)——渲染本提供方正文的面向模型 `web_fetch` 工具。
113
+ - [生成配置目录](../../../docs/config-catalog.zh.md#qilin-agentweb-fetch-http)——每个受支持配置字段及其源声明。
114
+ - [web 能力 seam 决策](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.zh.md)——搜索与抓取为何共用一项提供方选择服务。
115
+
116
+ -----
117
+
118
+ <a id="model-experience"></a>
119
+ ## 模型体验
120
+
121
+ 间接地,通过 `qilin-tool-web`:该工具把本提供方经 `maxBodyChars` 限制的解码文本或由 HTML 转换得到的 markdown 置于抓取结果包装层内,而重定向、标头与传输上限保持隐藏。
122
+
123
+ #### KV Cache 影响
124
+
125
+ 不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
126
+
127
+ ## 已知限制与延期工作
128
+
129
+ <a id="known-limitations-and-deferred-work"></a>
130
+
131
+
132
+ 这些限制说明提供方何时不安全或不合适。它们是当前包约束。
133
+
134
+ - **只解码文本内容**——包括 html/xhtml 与 `text/*` 加 JSON/XML 家族;缺少 `Content-Type` 或任何二进制类型都会抛出 `WEB_UNSUPPORTED_CONTENT_TYPE`,可提取文本的 PDF 解码属于明确的延期工作。
135
+ - **charset 只来自 `Content-Type` 标头**(默认 UTF-8)——HTML `<meta charset>` 声明会被忽略;声明但无法识别的 charset 标签会抛出异常,而非回退。
136
+
137
+ <a id="dev-note"></a>
138
+ ### 开发备注
139
+
140
+ <details>
141
+ <summary>维护者的工作上下文——点击展开</summary>
142
+
143
+ 无。
144
+
145
+ </details>