@basein/runner 0.2.11 → 0.3.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 +540 -350
- package/dist/auth/client.d.ts +65 -3
- package/dist/auth/client.js +305 -53
- package/dist/bin/bir-hooks.d.ts +72 -31
- package/dist/bin/bir-hooks.js +176 -284
- package/dist/bin/bir-proxy.d.ts +17 -2
- package/dist/bin/bir-proxy.js +222 -28
- package/dist/bin/bir-scenario.d.ts +61 -10
- package/dist/bin/bir-scenario.js +704 -135
- package/dist/bin/bir.d.ts +14 -8
- package/dist/bin/bir.js +2937 -488
- package/dist/bin/investigate.js +41 -16
- package/dist/bin/scenario-edit.d.ts +43 -10
- package/dist/bin/scenario-edit.js +281 -80
- package/dist/bin/scenario-liveness.d.ts +194 -0
- package/dist/bin/scenario-liveness.js +459 -0
- package/dist/bin/scenario-stderr.d.ts +43 -0
- package/dist/bin/scenario-stderr.js +76 -0
- package/dist/bin/service-read.d.ts +88 -0
- package/dist/bin/service-read.js +128 -0
- package/dist/bin/setup.d.ts +19 -0
- package/dist/bin/setup.js +146 -20
- package/dist/config/adapters/claude-code.d.ts +92 -14
- package/dist/config/adapters/claude-code.js +291 -101
- package/dist/config/adapters/generic.d.ts +4 -1
- package/dist/config/adapters/generic.js +13 -14
- package/dist/config/generate.d.ts +392 -14
- package/dist/config/generate.js +666 -19
- package/dist/config/mcp-approval.d.ts +76 -0
- package/dist/config/mcp-approval.js +210 -0
- package/dist/config/node-path.d.ts +42 -0
- package/dist/config/node-path.js +102 -0
- package/dist/config/overrides.d.ts +62 -0
- package/dist/config/overrides.js +136 -0
- package/dist/config/resolve.d.ts +70 -8
- package/dist/config/resolve.js +186 -15
- package/dist/control/client.d.ts +22 -0
- package/dist/control/client.js +26 -7
- package/dist/control/correlation.d.ts +23 -5
- package/dist/control/correlation.js +0 -0
- package/dist/control/daemon.d.ts +400 -18
- package/dist/control/daemon.js +1105 -102
- package/dist/control/discovery.d.ts +77 -5
- package/dist/control/discovery.js +186 -11
- package/dist/control/ensure-hook.d.ts +61 -0
- package/dist/control/ensure-hook.js +160 -8
- package/dist/control/paths.d.ts +26 -0
- package/dist/control/paths.js +34 -0
- package/dist/control/pin-guard.d.ts +83 -0
- package/dist/control/pin-guard.js +534 -0
- package/dist/control/recorder-keeper.d.ts +229 -0
- package/dist/control/recorder-keeper.js +721 -0
- package/dist/control/recorder-main.d.ts +73 -0
- package/dist/control/recorder-main.js +778 -0
- package/dist/control/server.d.ts +707 -31
- package/dist/control/server.js +2803 -306
- package/dist/control/transcript.d.ts +21 -1
- package/dist/control/transcript.js +378 -112
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/jsonrpc/framing.d.ts +76 -12
- package/dist/jsonrpc/framing.js +216 -59
- package/dist/proxy/intercept.d.ts +49 -5
- package/dist/proxy/intercept.js +188 -59
- package/dist/proxy/relay.d.ts +16 -0
- package/dist/proxy/relay.js +77 -5
- package/dist/proxy/revive.d.ts +122 -0
- package/dist/proxy/revive.js +251 -0
- package/dist/proxy/session.d.ts +222 -14
- package/dist/proxy/session.js +708 -99
- package/dist/record/housekeeping.d.ts +71 -0
- package/dist/record/housekeeping.js +417 -0
- package/dist/record/recorder.d.ts +51 -3
- package/dist/record/recorder.js +3 -0
- package/dist/record/redact.d.ts +103 -9
- package/dist/record/redact.js +659 -30
- package/dist/record/remote-recorder.d.ts +327 -32
- package/dist/record/remote-recorder.js +1517 -187
- package/dist/record/switchable-recorder.d.ts +126 -0
- package/dist/record/switchable-recorder.js +351 -0
- package/dist/record/truncate.d.ts +40 -1
- package/dist/record/truncate.js +111 -4
- package/dist/replay/bash-channel.d.ts +35 -0
- package/dist/replay/bash-channel.js +133 -0
- package/dist/replay/bundle.d.ts +10 -0
- package/dist/replay/bundle.js +10 -0
- package/dist/replay/controller.d.ts +371 -20
- package/dist/replay/controller.js +1185 -111
- package/dist/replay/derive.d.ts +1 -0
- package/dist/replay/derive.js +81 -24
- package/dist/replay/executor.d.ts +51 -16
- package/dist/replay/executor.js +114 -41
- package/dist/replay/handover.d.ts +54 -1
- package/dist/replay/handover.js +55 -5
- package/dist/replay/logic.d.ts +62 -10
- package/dist/replay/logic.js +140 -22
- package/dist/replay/plan.d.ts +201 -27
- package/dist/replay/plan.js +581 -141
- package/dist/replay/safety.d.ts +72 -0
- package/dist/replay/safety.js +303 -0
- package/dist/replay/source-run.d.ts +10 -0
- package/dist/replay/source-run.js +80 -35
- package/dist/replay/targets.d.ts +15 -1
- package/dist/replay/targets.js +62 -4
- package/dist/replay/tool-error.d.ts +15 -1
- package/dist/replay/tool-error.js +33 -1
- package/dist/replay/types.d.ts +7 -4
- package/dist/upstream/client.d.ts +56 -3
- package/dist/upstream/client.js +61 -4
- package/dist/upstream/headers.d.ts +43 -0
- package/dist/upstream/headers.js +90 -0
- package/dist/upstream/http-client.d.ts +213 -22
- package/dist/upstream/http-client.js +1034 -168
- package/dist/upstream/http-transport.d.ts +143 -0
- package/dist/upstream/http-transport.js +391 -0
- package/dist/upstream/lazy-client.d.ts +8 -0
- package/dist/upstream/lazy-client.js +29 -1
- package/dist/upstream/sse.d.ts +78 -0
- package/dist/upstream/sse.js +189 -0
- package/dist/upstream/stdio-client.d.ts +61 -1
- package/dist/upstream/stdio-client.js +356 -33
- package/dist/util/atomic-write.d.ts +29 -0
- package/dist/util/atomic-write.js +96 -0
- package/dist/util/journal.d.ts +15 -0
- package/dist/util/journal.js +48 -3
- package/dist/util/log.d.ts +10 -0
- package/dist/util/log.js +28 -1
- package/dist/util/off-switch.d.ts +46 -0
- package/dist/util/off-switch.js +91 -0
- package/dist/util/service-health.d.ts +133 -0
- package/dist/util/service-health.js +217 -0
- package/docs/BaseInstRunner.md +694 -621
- package/docs/calculatedReplay.md +1625 -1244
- package/docs/calculatedReplayGuide.md +1212 -879
- package/docs/installRun.md +1013 -772
- package/docs/loginWeb.md +627 -607
- package/docs/mcpmark.md +49 -16
- package/docs/my-first-sample.md +569 -545
- package/docs/quickstart.md +384 -271
- package/docs/t-bench.md +406 -394
- package/package.json +58 -57
- package/production.md +1371 -0
- package/scripts/install.ps1 +97 -6
- package/scripts/install.sh +104 -7
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sse — a Server-Sent Events parser for the remote upstream (http-client.ts).
|
|
3
|
+
*
|
|
4
|
+
* WHY NOT THE OBVIOUS TEN LINES. The first version appended each chunk to one
|
|
5
|
+
* string and re-ran a blank-line regex over the whole buffer on every chunk.
|
|
6
|
+
* That is quadratic in the size of one event: a 24 MB tool result (a
|
|
7
|
+
* screenshot, a file) took 2.6 s to parse as SSE against 0.18 s as JSON, all of
|
|
8
|
+
* it added to the user's tool call. It also computed the separator length by
|
|
9
|
+
* guessing (`\r\n\n` ate the next event's first byte), never recognised a lone
|
|
10
|
+
* CR — which the SSE spec allows — so a CR-only server's answers never arrived,
|
|
11
|
+
* and it had no size bound at all.
|
|
12
|
+
*
|
|
13
|
+
* So this one is line-oriented and linear:
|
|
14
|
+
*
|
|
15
|
+
* - each chunk is scanned once, from where the last one stopped; a partial
|
|
16
|
+
* line is kept as a list of pieces and joined once, when its terminator
|
|
17
|
+
* arrives (a V8 `+=` rope would be flattened by every search);
|
|
18
|
+
* - CRLF, LF and lone CR all end a line, including a CRLF split across two
|
|
19
|
+
* chunks;
|
|
20
|
+
* - the fields are the spec's (`data`, `event`, `id`, `retry`, `:` comments),
|
|
21
|
+
* so a stream's last event id and retry hint are available to reconnects;
|
|
22
|
+
* - one event's data is capped (the framing layer's 32 MiB by default). Past
|
|
23
|
+
* it `push` returns false and the caller abandons the stream and answers the
|
|
24
|
+
* request it was carrying, rather than allocating until the process dies.
|
|
25
|
+
*/
|
|
26
|
+
import type { Readable } from "node:stream";
|
|
27
|
+
export interface SseEvent {
|
|
28
|
+
/** The `event:` field; `"message"` when the event named none. */
|
|
29
|
+
event: string;
|
|
30
|
+
/** The `data:` lines joined with `\n`. */
|
|
31
|
+
data: string;
|
|
32
|
+
/** The stream's last event id at dispatch time, when it has one. */
|
|
33
|
+
id?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Raised by {@link readSseStream} when one event outgrows the cap. */
|
|
36
|
+
export declare class SseTooLargeError extends Error {
|
|
37
|
+
constructor(limit: number);
|
|
38
|
+
}
|
|
39
|
+
export declare class SseParser {
|
|
40
|
+
/** The last `id:` seen. Per the spec it persists across events. */
|
|
41
|
+
lastEventId: string;
|
|
42
|
+
/** The server's `retry:` hint in ms, when it sent one. */
|
|
43
|
+
retryMs?: number;
|
|
44
|
+
private readonly onEvent;
|
|
45
|
+
private readonly maxEventChars;
|
|
46
|
+
/** The unterminated tail of the current line, in pieces (see the header). */
|
|
47
|
+
private partial;
|
|
48
|
+
private partialChars;
|
|
49
|
+
private dataLines;
|
|
50
|
+
private dataChars;
|
|
51
|
+
private eventType;
|
|
52
|
+
/** The previous chunk ended in CR: a leading LF on the next one is its pair. */
|
|
53
|
+
private pendingCr;
|
|
54
|
+
private overflowed;
|
|
55
|
+
/** Per instance: a shared global regex would share `lastIndex`. */
|
|
56
|
+
private readonly lineBreak;
|
|
57
|
+
constructor(onEvent: (event: SseEvent) => void, maxEventChars?: number);
|
|
58
|
+
/** True once an event outgrew the cap; the parser ignores input from then on. */
|
|
59
|
+
get tooLarge(): boolean;
|
|
60
|
+
/** Feed decoded text. Returns false once one event has outgrown the cap. */
|
|
61
|
+
push(text: string): boolean;
|
|
62
|
+
private overflow;
|
|
63
|
+
private line;
|
|
64
|
+
private dispatch;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read an SSE body to its end, calling `onEvent` per complete event. Resolves
|
|
68
|
+
* when the stream ends cleanly; rejects when it errors (a reset, an abort) or
|
|
69
|
+
* with {@link SseTooLargeError} when one event outgrows the cap — the body is
|
|
70
|
+
* destroyed then, so the socket is released. An event handler that throws is
|
|
71
|
+
* isolated: one bad event must not end the stream carrying every other answer.
|
|
72
|
+
*/
|
|
73
|
+
export declare function readSseStream(body: Readable, onEvent: (event: SseEvent) => void, opts?: {
|
|
74
|
+
maxEventBytes?: number;
|
|
75
|
+
parser?: (parser: SseParser) => void;
|
|
76
|
+
onHandlerError?: (err: unknown) => void;
|
|
77
|
+
}): Promise<void>;
|
|
78
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sse — a Server-Sent Events parser for the remote upstream (http-client.ts).
|
|
3
|
+
*
|
|
4
|
+
* WHY NOT THE OBVIOUS TEN LINES. The first version appended each chunk to one
|
|
5
|
+
* string and re-ran a blank-line regex over the whole buffer on every chunk.
|
|
6
|
+
* That is quadratic in the size of one event: a 24 MB tool result (a
|
|
7
|
+
* screenshot, a file) took 2.6 s to parse as SSE against 0.18 s as JSON, all of
|
|
8
|
+
* it added to the user's tool call. It also computed the separator length by
|
|
9
|
+
* guessing (`\r\n\n` ate the next event's first byte), never recognised a lone
|
|
10
|
+
* CR — which the SSE spec allows — so a CR-only server's answers never arrived,
|
|
11
|
+
* and it had no size bound at all.
|
|
12
|
+
*
|
|
13
|
+
* So this one is line-oriented and linear:
|
|
14
|
+
*
|
|
15
|
+
* - each chunk is scanned once, from where the last one stopped; a partial
|
|
16
|
+
* line is kept as a list of pieces and joined once, when its terminator
|
|
17
|
+
* arrives (a V8 `+=` rope would be flattened by every search);
|
|
18
|
+
* - CRLF, LF and lone CR all end a line, including a CRLF split across two
|
|
19
|
+
* chunks;
|
|
20
|
+
* - the fields are the spec's (`data`, `event`, `id`, `retry`, `:` comments),
|
|
21
|
+
* so a stream's last event id and retry hint are available to reconnects;
|
|
22
|
+
* - one event's data is capped (the framing layer's 32 MiB by default). Past
|
|
23
|
+
* it `push` returns false and the caller abandons the stream and answers the
|
|
24
|
+
* request it was carrying, rather than allocating until the process dies.
|
|
25
|
+
*/
|
|
26
|
+
import { DEFAULT_MAX_LINE_BYTES } from "../jsonrpc/framing.js";
|
|
27
|
+
/** Raised by {@link readSseStream} when one event outgrows the cap. */
|
|
28
|
+
export class SseTooLargeError extends Error {
|
|
29
|
+
constructor(limit) {
|
|
30
|
+
super(`an event stream message exceeded ${limit} bytes`);
|
|
31
|
+
this.name = "SseTooLargeError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class SseParser {
|
|
35
|
+
/** The last `id:` seen. Per the spec it persists across events. */
|
|
36
|
+
lastEventId = "";
|
|
37
|
+
/** The server's `retry:` hint in ms, when it sent one. */
|
|
38
|
+
retryMs;
|
|
39
|
+
onEvent;
|
|
40
|
+
maxEventChars;
|
|
41
|
+
/** The unterminated tail of the current line, in pieces (see the header). */
|
|
42
|
+
partial = [];
|
|
43
|
+
partialChars = 0;
|
|
44
|
+
dataLines = [];
|
|
45
|
+
dataChars = 0;
|
|
46
|
+
eventType = "";
|
|
47
|
+
/** The previous chunk ended in CR: a leading LF on the next one is its pair. */
|
|
48
|
+
pendingCr = false;
|
|
49
|
+
overflowed = false;
|
|
50
|
+
/** Per instance: a shared global regex would share `lastIndex`. */
|
|
51
|
+
lineBreak = /\r\n|\r|\n/g;
|
|
52
|
+
constructor(onEvent, maxEventChars = DEFAULT_MAX_LINE_BYTES) {
|
|
53
|
+
this.onEvent = onEvent;
|
|
54
|
+
this.maxEventChars = maxEventChars;
|
|
55
|
+
}
|
|
56
|
+
/** True once an event outgrew the cap; the parser ignores input from then on. */
|
|
57
|
+
get tooLarge() {
|
|
58
|
+
return this.overflowed;
|
|
59
|
+
}
|
|
60
|
+
/** Feed decoded text. Returns false once one event has outgrown the cap. */
|
|
61
|
+
push(text) {
|
|
62
|
+
if (this.overflowed)
|
|
63
|
+
return false;
|
|
64
|
+
if (text.length === 0)
|
|
65
|
+
return true; // keep a pending CR for the next chunk
|
|
66
|
+
let start = 0;
|
|
67
|
+
if (this.pendingCr && text.charCodeAt(0) === 10)
|
|
68
|
+
start = 1;
|
|
69
|
+
this.pendingCr = false;
|
|
70
|
+
const lineBreak = this.lineBreak;
|
|
71
|
+
lineBreak.lastIndex = start;
|
|
72
|
+
let match;
|
|
73
|
+
while ((match = lineBreak.exec(text)) !== null) {
|
|
74
|
+
let line = text.slice(start, match.index);
|
|
75
|
+
if (this.partial.length > 0) {
|
|
76
|
+
this.partial.push(line);
|
|
77
|
+
line = this.partial.join("");
|
|
78
|
+
this.partial = [];
|
|
79
|
+
this.partialChars = 0;
|
|
80
|
+
}
|
|
81
|
+
start = match.index + match[0].length;
|
|
82
|
+
// A CR as the very last character may be the first half of a CRLF.
|
|
83
|
+
if (match[0] === "\r" && start === text.length)
|
|
84
|
+
this.pendingCr = true;
|
|
85
|
+
this.line(line);
|
|
86
|
+
if (this.overflowed)
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (start < text.length) {
|
|
90
|
+
const rest = text.slice(start);
|
|
91
|
+
this.partial.push(rest);
|
|
92
|
+
this.partialChars += rest.length;
|
|
93
|
+
}
|
|
94
|
+
if (this.dataChars + this.partialChars > this.maxEventChars) {
|
|
95
|
+
this.overflow();
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
overflow() {
|
|
101
|
+
this.overflowed = true;
|
|
102
|
+
this.partial = [];
|
|
103
|
+
this.dataLines = [];
|
|
104
|
+
}
|
|
105
|
+
line(line) {
|
|
106
|
+
if (line.length === 0) {
|
|
107
|
+
this.dispatch();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (line.charCodeAt(0) === 58 /* ":" */)
|
|
111
|
+
return; // a comment / keep-alive
|
|
112
|
+
const colon = line.indexOf(":");
|
|
113
|
+
let field;
|
|
114
|
+
let value;
|
|
115
|
+
if (colon === -1) {
|
|
116
|
+
field = line;
|
|
117
|
+
value = "";
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
field = line.slice(0, colon);
|
|
121
|
+
value = line.slice(colon + 1);
|
|
122
|
+
if (value.charCodeAt(0) === 32 /* " " */)
|
|
123
|
+
value = value.slice(1);
|
|
124
|
+
}
|
|
125
|
+
switch (field) {
|
|
126
|
+
case "data":
|
|
127
|
+
this.dataLines.push(value);
|
|
128
|
+
this.dataChars += value.length + 1;
|
|
129
|
+
if (this.dataChars > this.maxEventChars)
|
|
130
|
+
this.overflow();
|
|
131
|
+
break;
|
|
132
|
+
case "event":
|
|
133
|
+
this.eventType = value;
|
|
134
|
+
break;
|
|
135
|
+
case "id":
|
|
136
|
+
if (!value.includes("\0"))
|
|
137
|
+
this.lastEventId = value;
|
|
138
|
+
break;
|
|
139
|
+
case "retry":
|
|
140
|
+
if (/^\d+$/.test(value))
|
|
141
|
+
this.retryMs = Number(value);
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
// Unknown fields are ignored, per the spec.
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
dispatch() {
|
|
149
|
+
const event = this.eventType || "message";
|
|
150
|
+
this.eventType = "";
|
|
151
|
+
if (this.dataLines.length === 0)
|
|
152
|
+
return; // the spec: no data, no event
|
|
153
|
+
const data = this.dataLines.join("\n");
|
|
154
|
+
this.dataLines = [];
|
|
155
|
+
this.dataChars = 0;
|
|
156
|
+
this.onEvent(this.lastEventId ? { event, data, id: this.lastEventId } : { event, data });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Read an SSE body to its end, calling `onEvent` per complete event. Resolves
|
|
161
|
+
* when the stream ends cleanly; rejects when it errors (a reset, an abort) or
|
|
162
|
+
* with {@link SseTooLargeError} when one event outgrows the cap — the body is
|
|
163
|
+
* destroyed then, so the socket is released. An event handler that throws is
|
|
164
|
+
* isolated: one bad event must not end the stream carrying every other answer.
|
|
165
|
+
*/
|
|
166
|
+
export async function readSseStream(body, onEvent, opts = {}) {
|
|
167
|
+
const limit = opts.maxEventBytes ?? DEFAULT_MAX_LINE_BYTES;
|
|
168
|
+
const parser = new SseParser((event) => {
|
|
169
|
+
try {
|
|
170
|
+
onEvent(event);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
opts.onHandlerError?.(err);
|
|
174
|
+
}
|
|
175
|
+
}, limit);
|
|
176
|
+
opts.parser?.(parser);
|
|
177
|
+
// Decoding with `stream: true` keeps a multi-byte character split across two
|
|
178
|
+
// chunks intact; the parser only ever sees whole characters.
|
|
179
|
+
const decoder = new TextDecoder("utf-8");
|
|
180
|
+
for await (const chunk of body) {
|
|
181
|
+
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
182
|
+
if (!parser.push(text)) {
|
|
183
|
+
body.destroy();
|
|
184
|
+
throw new SseTooLargeError(limit);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
parser.push(decoder.decode());
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -14,8 +14,15 @@
|
|
|
14
14
|
* mangles JSON), {@link resolveCommand} resolves the real file on PATH and,
|
|
15
15
|
* for a batch file, invokes `cmd.exe /d /s /c` with `windowsVerbatimArguments`
|
|
16
16
|
* and its own quoting.
|
|
17
|
+
*
|
|
18
|
+
* AND ONE THING THAT BREAKS THE USER'S MACHINE: a server the proxy started must
|
|
19
|
+
* not outlive it. On Windows a batch-file upstream is `cmd.exe` → `node` (or
|
|
20
|
+
* `python`, or Chrome), and killing `cmd.exe` leaves the rest running — holding
|
|
21
|
+
* a browser profile or a database lock the next session then cannot open. So
|
|
22
|
+
* {@link StdioUpstreamClient.shutdown} closes stdin first (the MCP-spec way to
|
|
23
|
+
* ask a server to exit), waits briefly, and only then kills the whole tree.
|
|
17
24
|
*/
|
|
18
|
-
import type
|
|
25
|
+
import { type JsonRpcMessage } from "../jsonrpc/types.js";
|
|
19
26
|
import { BaseUpstreamClient } from "./client.js";
|
|
20
27
|
export interface StdioUpstreamOptions {
|
|
21
28
|
command: string;
|
|
@@ -26,6 +33,8 @@ export interface StdioUpstreamOptions {
|
|
|
26
33
|
label?: string;
|
|
27
34
|
/** Restart once with this backoff if the upstream dies unexpectedly. 0 disables. */
|
|
28
35
|
restartDelayMs?: number;
|
|
36
|
+
/** Largest upstream line parsed. Defaults to the framing default; tests lower it. */
|
|
37
|
+
maxLineBytes?: number;
|
|
29
38
|
}
|
|
30
39
|
/** What `spawn` should actually be given for `command` on this platform. */
|
|
31
40
|
export interface ResolvedCommand {
|
|
@@ -33,6 +42,37 @@ export interface ResolvedCommand {
|
|
|
33
42
|
args: string[];
|
|
34
43
|
windowsVerbatimArguments?: boolean;
|
|
35
44
|
}
|
|
45
|
+
/** Default time a closing upstream gets to exit on stdin EOF before it is killed. */
|
|
46
|
+
export declare const DEFAULT_SHUTDOWN_GRACE_MS = 1500;
|
|
47
|
+
/**
|
|
48
|
+
* Quote one argument for `cmd.exe /c` and, behind it, the C runtime's argv
|
|
49
|
+
* parser in whatever the batch file launches.
|
|
50
|
+
*
|
|
51
|
+
* Three rules, each for a case that used to arrive mangled:
|
|
52
|
+
* - An embedded `"` becomes `""`, which cmd reads as leaving and re-entering
|
|
53
|
+
* a quoted run (so its metacharacters stay quoted) and the C runtime reads
|
|
54
|
+
* as one literal quote.
|
|
55
|
+
* - Backslashes before a quote — an embedded one or our closing one — are
|
|
56
|
+
* doubled, because the C runtime reads `\"` as an escaped quote.
|
|
57
|
+
* `C:\projects\` used to swallow the next argument whole.
|
|
58
|
+
* - `%` is taken *out* of the quotes and caret-escaped: cmd expands `%NAME%`
|
|
59
|
+
* even inside quotes, and a caret is literal there. Outside them, `^%`
|
|
60
|
+
* survives expansion (the text between the two percents is no longer a
|
|
61
|
+
* variable name, because it contains a quote) and the caret is removed
|
|
62
|
+
* afterwards, leaving `%`. The C runtime then joins the pieces:
|
|
63
|
+
* `"a"%"B"%"c"` is the single argument `a%B%c`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function quoteForCmd(arg: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* Read an environment variable the way Windows does — case-insensitively.
|
|
68
|
+
*
|
|
69
|
+
* `process.env` is case-insensitive on Windows, but a *copy* of it (`{ ...process.env }`,
|
|
70
|
+
* which is what spawning with extra variables needs) is a plain object, and a
|
|
71
|
+
* normal Windows process spells the key `Path`. Reading `.PATH` off the copy
|
|
72
|
+
* found nothing, `npx` stayed unresolved, and every npx-launched server failed
|
|
73
|
+
* with `spawn npx ENOENT` whenever a host passed its environment through.
|
|
74
|
+
*/
|
|
75
|
+
export declare function envValue(env: NodeJS.ProcessEnv, key: string): string | undefined;
|
|
36
76
|
/** Find `command` on PATH, trying PATHEXT extensions. Returns undefined if absent. */
|
|
37
77
|
export declare function whichSync(command: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
38
78
|
/**
|
|
@@ -48,10 +88,30 @@ export declare class StdioUpstreamClient extends BaseUpstreamClient {
|
|
|
48
88
|
private readonly opts;
|
|
49
89
|
private restartsLeft;
|
|
50
90
|
private closing;
|
|
91
|
+
/**
|
|
92
|
+
* Set while a `--restart` is in progress: host messages wait here for the
|
|
93
|
+
* new process to be spawned and re-initialised, instead of being written
|
|
94
|
+
* into a dead pipe and lost.
|
|
95
|
+
*/
|
|
96
|
+
private restartBuffer?;
|
|
97
|
+
/** True while our replayed `initialize` is the one message allowed through. */
|
|
98
|
+
private handshaking;
|
|
99
|
+
/** The host's `initialize` params, replayed to a restarted upstream. */
|
|
100
|
+
private hostInitParams?;
|
|
101
|
+
private hostInitialized;
|
|
51
102
|
constructor(opts: StdioUpstreamOptions);
|
|
52
103
|
private start;
|
|
53
104
|
private onChildGone;
|
|
105
|
+
/** Start a new process, replay the host's handshake to it, then release held messages. */
|
|
106
|
+
private restart;
|
|
54
107
|
send(msg: JsonRpcMessage): void;
|
|
108
|
+
private write;
|
|
55
109
|
close(): void;
|
|
110
|
+
/**
|
|
111
|
+
* Close the way the MCP spec asks a client to: end stdin, give the server
|
|
112
|
+
* `graceMs` to exit on its own, then kill it — with its whole process tree on
|
|
113
|
+
* Windows. A server that exits on EOF (nearly all do) costs no wait at all.
|
|
114
|
+
*/
|
|
115
|
+
shutdown(graceMs?: number): Promise<void>;
|
|
56
116
|
}
|
|
57
117
|
//# sourceMappingURL=stdio-client.d.ts.map
|