donsetch 5.0.0-beta.2 → 5.0.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 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 beta:** 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.
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
  [![Release](https://img.shields.io/github/v/release/dondai44423/donsetch?color=00d4aa&style=flat-square)](https://github.com/dondai44423/donsetch/releases)
8
8
  [![CI](https://img.shields.io/github/actions/workflow/status/dondai44423/donsetch/ci.yml?label=CI&style=flat-square)](https://github.com/dondai44423/donsetch/actions/workflows/ci.yml)
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: `${target.hostname}:${target.port || 443}`,
170
- headers: { Host: `${target.hostname}:${target.port || 443}` },
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: false,
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
- servername: target.hostname,
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.0.0-beta.2",
3
+ "version": "5.0.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 (Xvfb warnings,
203
- // fallback notices, ghost telemetry). Pi's TUI surfaces stderr
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 model state with a
453
- * client-only diagnostics fallback. Compact MCP contracts keep
454
- * transport telemetry in _meta["com.donsetch/fetch-debug"], so the
455
- * tier can live there; older servers still expose it on
456
- * structuredContent.
457
- * Returns "cache" when tier is "1(warm)" (warm cookies, not a
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(