@zeldrisho/pi-web-fetch 0.4.0 → 0.5.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 +12 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/network-transport.ts +20 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
## [0.5.0](https://github.com/zeldrisho/pi-packages/compare/pi-web-fetch-v0.4.0...pi-web-fetch-v0.5.0) (2026-07-28)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
|
|
7
|
+
- Apply CodeRabbit auto-fixes ([b9149ac](https://github.com/zeldrisho/pi-packages/commit/b9149ac91016bb25b49806af5c99b8486420dd74))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
- **web-fetch:** Support larger documentation pages ([39cdb32](https://github.com/zeldrisho/pi-packages/commit/39cdb3213a9eee9e0c905319631ef3228e31c8d8))
|
|
13
|
+
|
|
2
14
|
|
|
3
15
|
## [0.4.0](https://github.com/zeldrisho/pi-packages/compare/pi-web-fetch-v0.3.1...pi-web-fetch-v0.4.0) (2026-07-25)
|
|
4
16
|
|
package/README.md
CHANGED
|
@@ -12,9 +12,9 @@ pi install npm:@zeldrisho/pi-web-fetch
|
|
|
12
12
|
|
|
13
13
|
The `web_fetch` tool accepts public HTTP and HTTPS URLs. It supports textual content such as HTML, Markdown, plain text, JSON, and XML. HTML pages are converted to Markdown with Defuddle; a basic text extractor is used as a fallback when Defuddle cannot extract the page.
|
|
14
14
|
|
|
15
|
-
For safety, the tool blocks URLs containing credentials, local hostnames, private or reserved network targets, unsafe redirects, responses larger than
|
|
15
|
+
For safety, the tool blocks URLs containing credentials, local hostnames, private or reserved network targets, unsafe redirects, raw responses larger than 5 MiB, and unsupported content types. The `maxCharacters` parameter controls returned Markdown length; it does not change the raw download limit.
|
|
16
16
|
|
|
17
|
-
In Pi's interactive UI, fetched content uses Pi's standard collapsed preview; use the configured tool-expansion shortcut (`Ctrl+O` by default) to show all visible tool output. Output sent to the agent remains bounded. When a result is truncated, call the tool again with the returned `nextOffset` as `offset` to continue reading. Fetched and extracted pages are cached in byte-bounded memory for a limited time so continuation requests can reuse the same content. Concurrent requests for the same URL share one fetch; cancelling one caller does not cancel work still needed by another.
|
|
17
|
+
In Pi's interactive UI, fetched content uses Pi's standard collapsed preview; use the configured tool-expansion shortcut (`Ctrl+O` by default) to show all visible tool output. Output sent to the agent remains bounded. The `offset` parameter is a character offset into extracted content, not a byte range into the remote response. When a result is truncated, call the tool again with the returned `nextOffset` as `offset` to continue reading. Fetched and extracted pages are cached in byte-bounded memory for a limited time so continuation requests can reuse the same content. Concurrent requests for the same URL share one fetch; cancelling one caller does not cancel work still needed by another.
|
|
18
18
|
|
|
19
19
|
Every result includes `details.truncation`. Complete output reports `{ truncated: false, strategy: "none" }`. Truncated output reports `strategy: "continuation"` and a valid `nextOffset`. The existing top-level `details.truncated` and `details.nextOffset` fields remain available.
|
|
20
20
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import { Text } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { executeWebFetch } from "./service";
|
|
5
|
-
import { FETCH_MAX_BYTES } from "./network";
|
|
6
5
|
import { formatCollapsibleOutput } from "./render";
|
|
7
6
|
|
|
8
7
|
export { ExpiringLruCache } from "./cache";
|
|
@@ -15,6 +14,7 @@ export {
|
|
|
15
14
|
type WebFetchTruncationDetails,
|
|
16
15
|
} from "./service";
|
|
17
16
|
export {
|
|
17
|
+
FETCH_MAX_BYTES,
|
|
18
18
|
isPrivateAddress,
|
|
19
19
|
requestPinned,
|
|
20
20
|
validateRemoteUrl,
|
|
@@ -22,6 +22,7 @@ export {
|
|
|
22
22
|
} from "./network";
|
|
23
23
|
|
|
24
24
|
const FETCH_DEFAULT_MAX_CHARACTERS = 6_000;
|
|
25
|
+
const FETCH_MAX_OFFSET_CHARACTERS = 20_000_000;
|
|
25
26
|
|
|
26
27
|
export default function (pi: ExtensionAPI) {
|
|
27
28
|
pi.registerTool({
|
|
@@ -44,9 +45,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
44
45
|
offset: Type.Optional(
|
|
45
46
|
Type.Integer({
|
|
46
47
|
minimum: 0,
|
|
47
|
-
maximum:
|
|
48
|
+
maximum: FETCH_MAX_OFFSET_CHARACTERS,
|
|
48
49
|
description:
|
|
49
|
-
"
|
|
50
|
+
"Extracted-content character offset to start reading from (default: 0; use nextOffset to continue)",
|
|
50
51
|
}),
|
|
51
52
|
),
|
|
52
53
|
maxCharacters: Type.Optional(
|
package/src/network-transport.ts
CHANGED
|
@@ -4,10 +4,24 @@ import type { LookupFunction } from "node:net";
|
|
|
4
4
|
import { formatSize } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import type { ValidatedTarget } from "./network-policy";
|
|
6
6
|
|
|
7
|
-
export const FETCH_MAX_BYTES =
|
|
7
|
+
export const FETCH_MAX_BYTES = 5 * 1_024 * 1_024;
|
|
8
8
|
|
|
9
9
|
const encoder = new TextEncoder();
|
|
10
10
|
|
|
11
|
+
function responseTooLargeMessage(
|
|
12
|
+
receivedBytes: number,
|
|
13
|
+
maxBytes: number,
|
|
14
|
+
sizeIsExact: boolean,
|
|
15
|
+
): string {
|
|
16
|
+
const size = sizeIsExact
|
|
17
|
+
? `is ${formatSize(receivedBytes)}`
|
|
18
|
+
: `has reached at least ${formatSize(receivedBytes)}`;
|
|
19
|
+
return [
|
|
20
|
+
`web_fetch response ${size}, exceeding the ${formatSize(maxBytes)} raw download limit.`,
|
|
21
|
+
"maxCharacters only controls returned output.",
|
|
22
|
+
].join(" ");
|
|
23
|
+
}
|
|
24
|
+
|
|
11
25
|
export async function requestPinned(
|
|
12
26
|
target: ValidatedTarget,
|
|
13
27
|
signal: AbortSignal,
|
|
@@ -45,8 +59,10 @@ export async function readResponseBytes(
|
|
|
45
59
|
maxBytes: number,
|
|
46
60
|
): Promise<Uint8Array> {
|
|
47
61
|
const declared = Number(responseHeader(response, "content-length"));
|
|
48
|
-
if (Number.isFinite(declared) && declared > maxBytes)
|
|
49
|
-
|
|
62
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
63
|
+
response.destroy();
|
|
64
|
+
throw new Error(responseTooLargeMessage(declared, maxBytes, true));
|
|
65
|
+
}
|
|
50
66
|
const chunks: Uint8Array[] = [];
|
|
51
67
|
let total = 0;
|
|
52
68
|
for await (const value of response) {
|
|
@@ -54,7 +70,7 @@ export async function readResponseBytes(
|
|
|
54
70
|
total += chunk.byteLength;
|
|
55
71
|
if (total > maxBytes) {
|
|
56
72
|
response.destroy();
|
|
57
|
-
throw new Error(
|
|
73
|
+
throw new Error(responseTooLargeMessage(total, maxBytes, false));
|
|
58
74
|
}
|
|
59
75
|
chunks.push(chunk);
|
|
60
76
|
}
|