donsetch 5.0.0-beta.3 → 5.1.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 +3 -3
- package/install.js +27 -4
- package/package.json +1 -1
- package/pi-extension.ts +8 -10
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**The web, for AI agents.** Fetch, search, crawl and screenshot from one local binary. Zero API keys, zero accounts.
|
|
4
4
|
|
|
5
|
-
**v5
|
|
5
|
+
**v5:** a new fetch engine. No browser in the fetch path: a wall is answered by the right client identity, the site's own challenge program run in-process, or the site's own public interface.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/dondai44423/donsetch/releases)
|
|
8
8
|
[](https://github.com/dondai44423/donsetch/actions/workflows/ci.yml)
|
|
@@ -103,7 +103,7 @@ donsetch update
|
|
|
103
103
|
| `web_fetch` | Any URL as clean markdown, or only the part that answers: `must_contain` probes, `focus` passages, `toc` → `section`. Walls answered in-process, never by a browser. PDFs with OCR and per-page confidence, site adapters, page memory, batches of up to 12. |
|
|
104
104
|
| `web_search` | Keyless multi-engine search: 10+ backends, consensus plus semantic reranking, query-aware official-source placement. Ranked URLs and snippets, never a scraped article dump. |
|
|
105
105
|
| `web_crawl` | Best-first same-domain crawl. Sitemap plus frontier, `focus` ranking, elastic pacing, resume tokens, honest stop reasons. |
|
|
106
|
-
| `web_screenshot` | Rendered PNG of any URL through DonSeTch's browser (used only for screenshots
|
|
106
|
+
| `web_screenshot` | Rendered PNG of any URL through DonSeTch's browser (used only for screenshots), with the usual URL safety guards. |
|
|
107
107
|
|
|
108
108
|
MCP results default to self-contained text: leading `[meta]` JSON followed by source evidence, including for unknown client bridges. PNGs remain image blocks.
|
|
109
109
|
|
|
@@ -115,7 +115,7 @@ Every failure is structured: a stable `code` (`wall.challenge`, `guard.ssrf`, `d
|
|
|
115
115
|
- **PDFs done properly.** A custom PDFium FFI, no Python subprocess. Tables and borders come from the rendered bitmap, text from the glyph stream, and the trust audit flags exactly the regions that need OCR.
|
|
116
116
|
- **Crawl with manners.** Per-host adaptive pacing that honors `Retry-After` and robots `Crawl-delay`, cross-process politeness so two crawls do not double a site's rate, near-duplicate detection, resume tokens that survive restarts, and honest stop reasons.
|
|
117
117
|
- **Fetch that answers honestly.** `content_ok`, `thin`, `changed` with section diffs, and `must_contain` says `unverified` instead of a false "not found" when part of a page was unread. No fake success.
|
|
118
|
-
- **Walls named, not guessed.** A bot wall names its vendor and what it asks for; a login wall is a declared `wall.auth` (fetch carries no session
|
|
118
|
+
- **Walls named, not guessed.** A bot wall names its vendor and what it asks for; a login wall is a declared `wall.auth` (the fetch carries no session; authenticated work belongs to a browser driver).
|
|
119
119
|
- **Works everywhere.** Linux, macOS, Windows. npm, Homebrew, the Pi agent as a native extension, and the DeepSeek Harness as a first-class plugin.
|
|
120
120
|
|
|
121
121
|
## Sponsors
|
package/install.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const http = require('http');
|
|
8
8
|
const https = require('https');
|
|
9
|
+
const net = require('net');
|
|
9
10
|
const tls = require('tls');
|
|
10
11
|
const crypto = require('crypto');
|
|
11
12
|
const fs = require('fs');
|
|
@@ -159,15 +160,30 @@ function requestDirect(target) {
|
|
|
159
160
|
});
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
// Basic credentials from the proxy URL's user:pass@ part, or null when
|
|
164
|
+
// it has none. The URL parser keeps them percent-encoded.
|
|
165
|
+
function proxyAuthorization(proxy) {
|
|
166
|
+
if (!proxy.username && !proxy.password) return null;
|
|
167
|
+
const decode = (part) => {
|
|
168
|
+
try { return decodeURIComponent(part); } catch (_) { return part; }
|
|
169
|
+
};
|
|
170
|
+
const pair = `${decode(proxy.username)}:${decode(proxy.password)}`;
|
|
171
|
+
return `Basic ${Buffer.from(pair).toString('base64')}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
162
174
|
function requestThroughProxy(target, proxy) {
|
|
163
175
|
return new Promise((resolve, reject) => {
|
|
176
|
+
const authority = `${target.hostname}:${target.port || 443}`;
|
|
177
|
+
const connectHeaders = { Host: authority };
|
|
178
|
+
const authorization = proxyAuthorization(proxy);
|
|
179
|
+
if (authorization) connectHeaders['Proxy-Authorization'] = authorization;
|
|
164
180
|
const connectTransport = proxy.protocol === 'https:' ? https : http;
|
|
165
181
|
const proxyReq = connectTransport.request({
|
|
166
182
|
hostname: proxy.hostname,
|
|
167
183
|
port: proxy.port || (proxy.protocol === 'https:' ? 443 : 80),
|
|
168
184
|
method: 'CONNECT',
|
|
169
|
-
path:
|
|
170
|
-
headers:
|
|
185
|
+
path: authority,
|
|
186
|
+
headers: connectHeaders,
|
|
171
187
|
});
|
|
172
188
|
proxyReq.once('connect', (response, socket) => {
|
|
173
189
|
if (response.statusCode !== 200) {
|
|
@@ -184,10 +200,17 @@ function requestThroughProxy(target, proxy) {
|
|
|
184
200
|
Accept: 'application/octet-stream',
|
|
185
201
|
'User-Agent': 'donsetch-npm-installer',
|
|
186
202
|
},
|
|
187
|
-
agent:
|
|
203
|
+
// No `agent` key: Node uses createConnection only when no agent
|
|
204
|
+
// is in use, and `agent: false` means a fresh default Agent,
|
|
205
|
+
// which dials the host directly and leaves the tunnel unused.
|
|
206
|
+
// Without an agent the request also closes its connection, and
|
|
207
|
+
// with it the tunnel, once the response ends.
|
|
208
|
+
// The certificate is verified (tls.connect's default) against
|
|
209
|
+
// the release host; SNI takes host names only (RFC 6066 3).
|
|
188
210
|
createConnection: () => tls.connect({
|
|
189
211
|
socket,
|
|
190
|
-
|
|
212
|
+
host: target.hostname,
|
|
213
|
+
servername: net.isIP(target.hostname) ? undefined : target.hostname,
|
|
191
214
|
}),
|
|
192
215
|
}, resolve);
|
|
193
216
|
req.setTimeout(REQUEST_TIMEOUT_MS, () => req.destroy(new Error('request timeout')));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "donsetch",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "MCP server that gives AI agents the web: fetch, search, crawl and screenshot from one local Rust binary. Zero API keys, zero accounts, Chrome-true TLS.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"author": "Bishesh Bhandari",
|
package/pi-extension.ts
CHANGED
|
@@ -199,8 +199,8 @@ function startServer(): Promise<void> {
|
|
|
199
199
|
}
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
// The daemon writes diagnostics to stderr (
|
|
203
|
-
//
|
|
202
|
+
// The daemon writes diagnostics to stderr (config warnings,
|
|
203
|
+
// rerank and OCR notices). Pi's TUI surfaces stderr
|
|
204
204
|
// as user-facing output: raw forwarding used to pop non-fatal
|
|
205
205
|
// warnings up (issue #95). Gate it:
|
|
206
206
|
// - DONSETCH_DEBUG / DEBUG set: forward everything (dev mode)
|
|
@@ -449,14 +449,12 @@ function getSearchProvider(sc: any, dbg: any): string {
|
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
/**
|
|
452
|
-
* Extract the fetch source label from
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
* fresh fetch), "ghost" when tier starts with "2" (browser
|
|
459
|
-
* escalation), or "" for a normal tier-1 fetch.
|
|
452
|
+
* Extract the fetch source label from the `tier` field that servers
|
|
453
|
+
* before 5.0 report, in _meta["com.donsetch/fetch-debug"] or on
|
|
454
|
+
* structuredContent. Returns "cache" when tier is "1(warm)" (warm
|
|
455
|
+
* cookies, not a fresh fetch), "ghost" when tier starts with "2"
|
|
456
|
+
* (their browser tier), or "" otherwise. A 5.x server sends no
|
|
457
|
+
* tier and has no browser tier, so the result is always "".
|
|
460
458
|
*/
|
|
461
459
|
function getFetchSource(sc: any, dbg: any): string {
|
|
462
460
|
const tier = String(
|