donsetch 4.1.1 → 4.2.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/LICENSE +662 -0
- package/README.md +20 -0
- package/bin/donsetch.js +21 -3
- package/install.js +229 -178
- package/package.json +4 -7
- package/pi-extension.ts +52 -7
package/pi-extension.ts
CHANGED
|
@@ -27,10 +27,37 @@ import { join } from "node:path";
|
|
|
27
27
|
import { Text } from "@earendil-works/pi-tui";
|
|
28
28
|
|
|
29
29
|
// ── Constants ──
|
|
30
|
-
const INIT_TIMEOUT_MS =
|
|
30
|
+
const INIT_TIMEOUT_MS = 30_000;
|
|
31
31
|
const CALL_TIMEOUT_MS = 120_000;
|
|
32
32
|
const SHUTDOWN_GRACE_MS = 2_000;
|
|
33
33
|
|
|
34
|
+
// The client timeout must outlive the server's OWN budget, or a call the
|
|
35
|
+
// binary still considers legal gets killed here first. web_crawl accepts
|
|
36
|
+
// deadline_s up to 600 and web_fetch deadline_ms up to 600000, so a fixed
|
|
37
|
+
// 120s client cap cut legal crawls off at exactly the binary's default
|
|
38
|
+
// deadline (deadline_s defaults to 120 : the two raced). Derive it from
|
|
39
|
+
// the call's own budget plus slack instead, capped at the largest budget
|
|
40
|
+
// the schemas allow so a hostile arg cannot hold a slot forever.
|
|
41
|
+
const CALL_DEADLINE_SLACK_MS = 20_000;
|
|
42
|
+
const MAX_CALL_TIMEOUT_MS = 620_000; // 600s budget + slack
|
|
43
|
+
|
|
44
|
+
function num(v: unknown): number | null {
|
|
45
|
+
const n = typeof v === "string" ? Number(v) : typeof v === "number" ? v : NaN;
|
|
46
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function callTimeoutFor(name: string, args: any): number {
|
|
50
|
+
let budgetMs: number | null = null;
|
|
51
|
+
if (name === "web_crawl") {
|
|
52
|
+
const secs = num(args?.deadline_s);
|
|
53
|
+
budgetMs = secs !== null ? secs * 1000 : 120_000; // the binary's default
|
|
54
|
+
} else if (name === "web_fetch") {
|
|
55
|
+
budgetMs = num(args?.deadline_ms);
|
|
56
|
+
}
|
|
57
|
+
if (budgetMs === null) return CALL_TIMEOUT_MS;
|
|
58
|
+
return Math.min(budgetMs + CALL_DEADLINE_SLACK_MS, MAX_CALL_TIMEOUT_MS);
|
|
59
|
+
}
|
|
60
|
+
|
|
34
61
|
// A raw process.stderr.write bypasses pi's TUI paint cycle and
|
|
35
62
|
// corrupts the viewport (worse across parallel agents). Gate it here.
|
|
36
63
|
const DEBUG_MODE =
|
|
@@ -56,6 +83,7 @@ const ICONS: Record<string, string> = {
|
|
|
56
83
|
web_fetch: "\u{1F310}", // 🌐
|
|
57
84
|
web_search: "\u{1F50E}", // 🔎
|
|
58
85
|
web_crawl: "\u{1F577}\u{FE0F}", // 🕷️
|
|
86
|
+
web_screenshot: "\u{1F4F7}", // 📷
|
|
59
87
|
};
|
|
60
88
|
|
|
61
89
|
// ── MCP client state ──
|
|
@@ -89,10 +117,10 @@ function ensureBinary(): string {
|
|
|
89
117
|
}
|
|
90
118
|
|
|
91
119
|
try {
|
|
92
|
-
execFileSync(
|
|
120
|
+
execFileSync(process.execPath, [installScript], {
|
|
93
121
|
stdio: "inherit",
|
|
94
122
|
cwd: __dirname,
|
|
95
|
-
timeout:
|
|
123
|
+
timeout: 300_000,
|
|
96
124
|
});
|
|
97
125
|
} catch (err: any) {
|
|
98
126
|
throw new Error(`Failed to download donsetch binary: ${err.message}`);
|
|
@@ -312,8 +340,13 @@ function sendNotification(method: string, params: any): void {
|
|
|
312
340
|
}
|
|
313
341
|
}
|
|
314
342
|
|
|
315
|
-
async function callMcpTool(
|
|
316
|
-
|
|
343
|
+
async function callMcpTool(
|
|
344
|
+
name: string,
|
|
345
|
+
args: any,
|
|
346
|
+
signal?: AbortSignal,
|
|
347
|
+
timeoutMs = CALL_TIMEOUT_MS
|
|
348
|
+
): Promise<any> {
|
|
349
|
+
return sendRequest("tools/call", { name, arguments: args ?? {} }, timeoutMs, signal);
|
|
317
350
|
}
|
|
318
351
|
|
|
319
352
|
function killServer(): void {
|
|
@@ -433,7 +466,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
433
466
|
try {
|
|
434
467
|
await startServer();
|
|
435
468
|
} catch (err: any) {
|
|
436
|
-
|
|
469
|
+
const message = String(err.message || err);
|
|
470
|
+
const hint = message.includes("initialize") && message.includes("timeout")
|
|
471
|
+
? " Run `donsetch doctor` to diagnose the installation and runtime."
|
|
472
|
+
: "";
|
|
473
|
+
process.stderr.write(`[donsetch] failed to start MCP server: ${message}.${hint}\n`);
|
|
437
474
|
return;
|
|
438
475
|
}
|
|
439
476
|
|
|
@@ -473,13 +510,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
473
510
|
} catch (err: any) {
|
|
474
511
|
return {
|
|
475
512
|
content: [{ type: "text", text: `donsetch MCP server crashed and could not restart: ${err.message}` }],
|
|
513
|
+
// Every other result path carries `details`; pi's
|
|
514
|
+
// AgentToolResult requires it, and renderResult reads it.
|
|
515
|
+
details: { mcpTool: toolName, isError: true, error: err.message },
|
|
476
516
|
isError: true,
|
|
477
517
|
};
|
|
478
518
|
}
|
|
479
519
|
}
|
|
480
520
|
|
|
481
521
|
try {
|
|
482
|
-
const result = await callMcpTool(
|
|
522
|
+
const result = await callMcpTool(
|
|
523
|
+
toolName,
|
|
524
|
+
params,
|
|
525
|
+
_signal,
|
|
526
|
+
callTimeoutFor(toolName, params)
|
|
527
|
+
);
|
|
483
528
|
// Join all content text blocks, skipping [meta] blocks.
|
|
484
529
|
// [meta] blocks contain compact metadata for clients
|
|
485
530
|
// (Claude Code, VSCode) that drop text when
|