harness-dispatch 0.8.0 → 0.10.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 +1074 -2
- package/README.md +92 -36
- package/config.default.yaml +19 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/billing.d.ts +24 -0
- package/dist/billing.d.ts.map +1 -1
- package/dist/billing.js +24 -0
- package/dist/billing.js.map +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +299 -48
- package/dist/bin.js.map +1 -1
- package/dist/circuit-breaker.d.ts +9 -1
- package/dist/circuit-breaker.d.ts.map +1 -1
- package/dist/circuit-breaker.js +15 -1
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/client-register.d.ts +11 -1
- package/dist/client-register.d.ts.map +1 -1
- package/dist/client-register.js +15 -6
- package/dist/client-register.js.map +1 -1
- package/dist/config/coercions.d.ts +5 -0
- package/dist/config/coercions.d.ts.map +1 -1
- package/dist/config/coercions.js +8 -0
- package/dist/config/coercions.js.map +1 -1
- package/dist/config/protocol.js.map +1 -1
- package/dist/config/validation.d.ts +12 -0
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +133 -7
- package/dist/config/validation.js.map +1 -1
- package/dist/config.d.ts +15 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +199 -152
- package/dist/config.js.map +1 -1
- package/dist/configure-yaml.d.ts +2 -0
- package/dist/configure-yaml.d.ts.map +1 -1
- package/dist/configure-yaml.js +114 -4
- package/dist/configure-yaml.js.map +1 -1
- package/dist/dispatch-log.d.ts.map +1 -1
- package/dist/dispatch-log.js +3 -1
- package/dist/dispatch-log.js.map +1 -1
- package/dist/dispatchers/generic-cli.d.ts.map +1 -1
- package/dist/dispatchers/generic-cli.js +281 -112
- package/dist/dispatchers/generic-cli.js.map +1 -1
- package/dist/dispatchers/openai-compatible.d.ts +0 -5
- package/dist/dispatchers/openai-compatible.d.ts.map +1 -1
- package/dist/dispatchers/openai-compatible.js +150 -83
- package/dist/dispatchers/openai-compatible.js.map +1 -1
- package/dist/dispatchers/shared/harness-login.d.ts +25 -0
- package/dist/dispatchers/shared/harness-login.d.ts.map +1 -0
- package/dist/dispatchers/shared/harness-login.js +73 -0
- package/dist/dispatchers/shared/harness-login.js.map +1 -0
- package/dist/dispatchers/shared/stream-subprocess.d.ts.map +1 -1
- package/dist/dispatchers/shared/stream-subprocess.js +71 -58
- package/dist/dispatchers/shared/stream-subprocess.js.map +1 -1
- package/dist/file-lock.d.ts +29 -1
- package/dist/file-lock.d.ts.map +1 -1
- package/dist/file-lock.js +53 -20
- package/dist/file-lock.js.map +1 -1
- package/dist/harness-presets.d.ts +51 -0
- package/dist/harness-presets.d.ts.map +1 -0
- package/dist/harness-presets.js +154 -0
- package/dist/harness-presets.js.map +1 -0
- package/dist/http/parse.d.ts +8 -1
- package/dist/http/parse.d.ts.map +1 -1
- package/dist/http/parse.js +10 -3
- package/dist/http/parse.js.map +1 -1
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +188 -16
- package/dist/http/server.js.map +1 -1
- package/dist/job-runner.js +6 -0
- package/dist/job-runner.js.map +1 -1
- package/dist/jobs/context.d.ts +0 -7
- package/dist/jobs/context.d.ts.map +1 -1
- package/dist/jobs/context.js +95 -7
- package/dist/jobs/context.js.map +1 -1
- package/dist/jobs/lifecycle.d.ts +100 -0
- package/dist/jobs/lifecycle.d.ts.map +1 -0
- package/dist/jobs/lifecycle.js +263 -0
- package/dist/jobs/lifecycle.js.map +1 -0
- package/dist/jobs/read.d.ts +17 -0
- package/dist/jobs/read.d.ts.map +1 -0
- package/dist/jobs/read.js +103 -0
- package/dist/jobs/read.js.map +1 -0
- package/dist/jobs/run.d.ts +45 -0
- package/dist/jobs/run.d.ts.map +1 -0
- package/dist/jobs/run.js +351 -0
- package/dist/jobs/run.js.map +1 -0
- package/dist/jobs/start.d.ts +9 -0
- package/dist/jobs/start.d.ts.map +1 -0
- package/dist/jobs/start.js +126 -0
- package/dist/jobs/start.js.map +1 -0
- package/dist/jobs/store.d.ts.map +1 -1
- package/dist/jobs/store.js +13 -3
- package/dist/jobs/store.js.map +1 -1
- package/dist/jobs/supervisor.d.ts +151 -0
- package/dist/jobs/supervisor.d.ts.map +1 -0
- package/dist/jobs/supervisor.js +679 -0
- package/dist/jobs/supervisor.js.map +1 -0
- package/dist/jobs/types.d.ts +8 -1
- package/dist/jobs/types.d.ts.map +1 -1
- package/dist/jobs.d.ts +15 -218
- package/dist/jobs.d.ts.map +1 -1
- package/dist/jobs.js +14 -1317
- package/dist/jobs.js.map +1 -1
- package/dist/leaderboard.d.ts.map +1 -1
- package/dist/leaderboard.js +44 -28
- package/dist/leaderboard.js.map +1 -1
- package/dist/mcp/config-hot-reload.d.ts +14 -3
- package/dist/mcp/config-hot-reload.d.ts.map +1 -1
- package/dist/mcp/config-hot-reload.js +27 -6
- package/dist/mcp/config-hot-reload.js.map +1 -1
- package/dist/mcp/dispatcher-factory.d.ts.map +1 -1
- package/dist/mcp/dispatcher-factory.js +15 -4
- package/dist/mcp/dispatcher-factory.js.map +1 -1
- package/dist/mcp/resources.d.ts.map +1 -1
- package/dist/mcp/resources.js +3 -2
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tool-schemas.d.ts +1 -1
- package/dist/mcp/tool-schemas.d.ts.map +1 -1
- package/dist/mcp/tool-schemas.js +6 -3
- package/dist/mcp/tool-schemas.js.map +1 -1
- package/dist/mcp/tools.d.ts +20 -6
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +92 -14
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp-clients.d.ts +9 -0
- package/dist/mcp-clients.d.ts.map +1 -1
- package/dist/mcp-clients.js +4 -0
- package/dist/mcp-clients.js.map +1 -1
- package/dist/observability/spans.d.ts.map +1 -1
- package/dist/observability/spans.js +10 -2
- package/dist/observability/spans.js.map +1 -1
- package/dist/quota.d.ts.map +1 -1
- package/dist/quota.js +16 -2
- package/dist/quota.js.map +1 -1
- package/dist/redaction.d.ts +90 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +221 -0
- package/dist/redaction.js.map +1 -0
- package/dist/route-policy.d.ts +2 -1
- package/dist/route-policy.d.ts.map +1 -1
- package/dist/route-policy.js +54 -0
- package/dist/route-policy.js.map +1 -1
- package/dist/router.d.ts +12 -23
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +86 -223
- package/dist/router.js.map +1 -1
- package/dist/safety.d.ts.map +1 -1
- package/dist/safety.js +7 -1
- package/dist/safety.js.map +1 -1
- package/dist/state-dir.d.ts +10 -0
- package/dist/state-dir.d.ts.map +1 -1
- package/dist/state-dir.js +27 -1
- package/dist/state-dir.js.map +1 -1
- package/dist/status.d.ts +14 -3
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +36 -3
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +24 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace-lock.d.ts +20 -0
- package/dist/workspace-lock.d.ts.map +1 -1
- package/dist/workspace-lock.js +63 -0
- package/dist/workspace-lock.js.map +1 -1
- package/dist/workspace-resolve.d.ts +0 -9
- package/dist/workspace-resolve.d.ts.map +1 -1
- package/dist/workspace-resolve.js +115 -183
- package/dist/workspace-resolve.js.map +1 -1
- package/dist/workspaces.d.ts +15 -0
- package/dist/workspaces.d.ts.map +1 -1
- package/dist/workspaces.js +330 -90
- package/dist/workspaces.js.map +1 -1
- package/package.json +5 -4
|
@@ -1,6 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming subprocess runner.
|
|
3
|
+
*
|
|
4
|
+
* Companion to `runSubprocess` — same spawn/kill/timeout semantics but emits
|
|
5
|
+
* stdout and stderr chunks as the child writes them via an `AsyncIterable`.
|
|
6
|
+
* A bounded internal queue protects against runaway processes flooding memory
|
|
7
|
+
* (exceeding the bound kills the child).
|
|
8
|
+
*
|
|
9
|
+
* There is NO backpressure, and this said there was ("consumers drive
|
|
10
|
+
* backpressure by how fast they iterate"). Nothing pauses the child's stdout,
|
|
11
|
+
* so a slow consumer does not slow the producer — the queue grows instead,
|
|
12
|
+
* and past `maxBufferedChunks` the child is killed and the iterator REJECTS,
|
|
13
|
+
* which in the job runner marks a healthy run failed and discards its result.
|
|
14
|
+
* Not reached at the shipped bound of 1000 in measurement, but the behaviour
|
|
15
|
+
* when it is reached is wrong, and the reader deserves to know which of the
|
|
16
|
+
* two it is relying on.
|
|
17
|
+
*
|
|
18
|
+
* The iterator yields `{ stream, chunk }` tuples until the child exits,
|
|
19
|
+
* whereupon it yields a single terminal `{ kind: "end", exitCode, timedOut,
|
|
20
|
+
* durationMs }` event before signalling completion to the consumer.
|
|
21
|
+
*
|
|
22
|
+
* Cancellation: calling `.return()` on the iterator (which `for await ... of`
|
|
23
|
+
* does automatically when you `break` or throw) sends SIGTERM to the child
|
|
24
|
+
* and drains any remaining buffered chunks. If the child doesn't exit within
|
|
25
|
+
* a grace window, SIGKILL is sent.
|
|
26
|
+
*
|
|
27
|
+
* This module used to branch on whether `runSubprocess` had been replaced by
|
|
28
|
+
* a vitest mock, and if so synthesise events from a buffered result instead.
|
|
29
|
+
* The comment claimed that arrangement still exercised the streaming path in
|
|
30
|
+
* production; it did not exercise it ANYWHERE, because every dispatcher suite
|
|
31
|
+
* mocked `runSubprocess` and therefore ran the adapter. The adapter now lives
|
|
32
|
+
* in `tests/support/buffered-stream.ts`, where a suite opts into it
|
|
33
|
+
* explicitly, and nothing in this file knows whether it is under test.
|
|
34
|
+
*/
|
|
35
|
+
import { StringDecoder } from "node:string_decoder";
|
|
1
36
|
import spawn from "cross-spawn";
|
|
2
37
|
import { killTree } from "./kill-tree.js";
|
|
3
|
-
import { runSubprocess } from "./subprocess.js";
|
|
4
38
|
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
5
39
|
const DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
6
40
|
const DEFAULT_MAX_BUFFERED_CHUNKS = 1000;
|
|
@@ -12,59 +46,6 @@ const DEFAULT_KILL_GRACE_MS = 2_000;
|
|
|
12
46
|
* which the iterator closes.
|
|
13
47
|
*/
|
|
14
48
|
export function streamSubprocess(command, args, opts = {}) {
|
|
15
|
-
// Test-compatibility: when `runSubprocess` is replaced by a vi.fn() mock
|
|
16
|
-
// (every dispatcher test does this), delegate to the mock and synthesize
|
|
17
|
-
// stream events from the buffered result. This keeps existing tests
|
|
18
|
-
// passing without modification.
|
|
19
|
-
//
|
|
20
|
-
// We detect the mock by looking at `runSubprocess.mock` (the vitest fn shape).
|
|
21
|
-
// In production the function is a regular async function and `.mock` is
|
|
22
|
-
// undefined — we fall through to the real streaming implementation.
|
|
23
|
-
const rs = runSubprocess;
|
|
24
|
-
if (rs._isMockFunction || rs.mock) {
|
|
25
|
-
return adaptBufferedToStream(command, args, opts);
|
|
26
|
-
}
|
|
27
|
-
return realStreamSubprocess(command, args, opts);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* When `runSubprocess` is mocked in tests, call it and synthesize a stream
|
|
31
|
-
* from the buffered result. The ordering is stdout-chunk → stderr-chunk →
|
|
32
|
-
* end, matching what a real subprocess would emit when stdin closes and
|
|
33
|
-
* it flushes both streams before exit.
|
|
34
|
-
*/
|
|
35
|
-
function adaptBufferedToStream(command, args, opts) {
|
|
36
|
-
const deferredOpts = {};
|
|
37
|
-
if (opts.cwd !== undefined)
|
|
38
|
-
deferredOpts.cwd = opts.cwd;
|
|
39
|
-
if (opts.env !== undefined)
|
|
40
|
-
deferredOpts.env = opts.env;
|
|
41
|
-
if (opts.stdin !== undefined)
|
|
42
|
-
deferredOpts.stdin = opts.stdin;
|
|
43
|
-
if (opts.timeoutMs !== undefined)
|
|
44
|
-
deferredOpts.timeoutMs = opts.timeoutMs;
|
|
45
|
-
if (opts.maxOutputBytes !== undefined)
|
|
46
|
-
deferredOpts.maxOutputBytes = opts.maxOutputBytes;
|
|
47
|
-
async function* gen() {
|
|
48
|
-
const res = await runSubprocess(command, args, deferredOpts);
|
|
49
|
-
if (res.stdout) {
|
|
50
|
-
yield { stream: "stdout", chunk: res.stdout };
|
|
51
|
-
}
|
|
52
|
-
if (res.stderr) {
|
|
53
|
-
yield { stream: "stderr", chunk: res.stderr };
|
|
54
|
-
}
|
|
55
|
-
yield {
|
|
56
|
-
kind: "end",
|
|
57
|
-
exitCode: res.exitCode,
|
|
58
|
-
timedOut: res.timedOut,
|
|
59
|
-
durationMs: res.durationMs,
|
|
60
|
-
totalStdoutBytes: Buffer.byteLength(res.stdout, "utf8"),
|
|
61
|
-
totalStderrBytes: Buffer.byteLength(res.stderr, "utf8"),
|
|
62
|
-
truncated: false,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
return gen();
|
|
66
|
-
}
|
|
67
|
-
function realStreamSubprocess(command, args, opts) {
|
|
68
49
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
69
50
|
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
70
51
|
const maxBufferedChunks = opts.maxBufferedChunks ?? DEFAULT_MAX_BUFFERED_CHUNKS;
|
|
@@ -96,7 +77,39 @@ function realStreamSubprocess(command, args, opts) {
|
|
|
96
77
|
}
|
|
97
78
|
}
|
|
98
79
|
}
|
|
80
|
+
// One decoder per stream, held across chunks.
|
|
81
|
+
//
|
|
82
|
+
// Each `data` buffer used to be decoded on its own with `buf.toString()`, so
|
|
83
|
+
// any multi-byte character straddling two reads became replacement
|
|
84
|
+
// characters. Reproduced: a child writing "price: € done", split inside
|
|
85
|
+
// the euro sign, arrived as "price: ��� done".
|
|
86
|
+
//
|
|
87
|
+
// This is the path every MCP job uses — partialOutput, stdout.log, result.md
|
|
88
|
+
// — so accented text, CJK, emoji and box-drawing were being corrupted in
|
|
89
|
+
// delivered work product. The buffered sibling in subprocess.ts concatenates
|
|
90
|
+
// before decoding and was always correct, which is why only the path nothing
|
|
91
|
+
// but tests exercise looked right.
|
|
92
|
+
//
|
|
93
|
+
// StringDecoder holds an incomplete sequence back until the bytes that
|
|
94
|
+
// finish it arrive; `end()` at teardown flushes whatever never completed.
|
|
95
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
96
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
99
97
|
function finish() {
|
|
98
|
+
// Flush whatever the decoders were holding back.
|
|
99
|
+
//
|
|
100
|
+
// A StringDecoder withholds an incomplete multi-byte sequence until the
|
|
101
|
+
// bytes completing it arrive. If the child exits mid-character — a
|
|
102
|
+
// truncated run, a kill, a crash partway through a write — those bytes
|
|
103
|
+
// would otherwise be dropped silently, which is the same class of loss the
|
|
104
|
+
// decoders were added to prevent, just at the end instead of the middle.
|
|
105
|
+
// `end()` returns them as replacement characters: visibly wrong beats
|
|
106
|
+
// invisibly absent.
|
|
107
|
+
const tailOut = stdoutDecoder.end();
|
|
108
|
+
if (tailOut !== "")
|
|
109
|
+
push({ stream: "stdout", chunk: tailOut });
|
|
110
|
+
const tailErr = stderrDecoder.end();
|
|
111
|
+
if (tailErr !== "")
|
|
112
|
+
push({ stream: "stderr", chunk: tailErr });
|
|
100
113
|
done = true;
|
|
101
114
|
while (waiters.length > 0) {
|
|
102
115
|
const w = waiters.shift();
|
|
@@ -160,7 +173,7 @@ function realStreamSubprocess(command, args, opts) {
|
|
|
160
173
|
if (stdoutBytes + buf.length > maxOutputBytes) {
|
|
161
174
|
const remaining = Math.max(0, maxOutputBytes - stdoutBytes);
|
|
162
175
|
if (remaining > 0) {
|
|
163
|
-
push({ stream: "stdout", chunk: buf.subarray(0, remaining)
|
|
176
|
+
push({ stream: "stdout", chunk: stdoutDecoder.write(buf.subarray(0, remaining)) });
|
|
164
177
|
}
|
|
165
178
|
stdoutBytes = maxOutputBytes;
|
|
166
179
|
truncated = true;
|
|
@@ -168,7 +181,7 @@ function realStreamSubprocess(command, args, opts) {
|
|
|
168
181
|
return;
|
|
169
182
|
}
|
|
170
183
|
stdoutBytes += buf.length;
|
|
171
|
-
push({ stream: "stdout", chunk:
|
|
184
|
+
push({ stream: "stdout", chunk: stdoutDecoder.write(buf) });
|
|
172
185
|
});
|
|
173
186
|
child.stderr?.on("data", (buf) => {
|
|
174
187
|
if (truncated)
|
|
@@ -176,7 +189,7 @@ function realStreamSubprocess(command, args, opts) {
|
|
|
176
189
|
if (stderrBytes + buf.length > maxOutputBytes) {
|
|
177
190
|
const remaining = Math.max(0, maxOutputBytes - stderrBytes);
|
|
178
191
|
if (remaining > 0) {
|
|
179
|
-
push({ stream: "stderr", chunk: buf.subarray(0, remaining)
|
|
192
|
+
push({ stream: "stderr", chunk: stderrDecoder.write(buf.subarray(0, remaining)) });
|
|
180
193
|
}
|
|
181
194
|
stderrBytes = maxOutputBytes;
|
|
182
195
|
truncated = true;
|
|
@@ -184,7 +197,7 @@ function realStreamSubprocess(command, args, opts) {
|
|
|
184
197
|
return;
|
|
185
198
|
}
|
|
186
199
|
stderrBytes += buf.length;
|
|
187
|
-
push({ stream: "stderr", chunk:
|
|
200
|
+
push({ stream: "stderr", chunk: stderrDecoder.write(buf) });
|
|
188
201
|
});
|
|
189
202
|
child.on("error", (err) => {
|
|
190
203
|
if (settled)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-subprocess.js","sourceRoot":"","sources":["../../../src/dispatchers/shared/stream-subprocess.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stream-subprocess.js","sourceRoot":"","sources":["../../../src/dispatchers/shared/stream-subprocess.ts"],"names":[],"mappings":"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"}
|
package/dist/file-lock.d.ts
CHANGED
|
@@ -13,5 +13,33 @@
|
|
|
13
13
|
* section. Both callers here do; getting that wrong is what made the first
|
|
14
14
|
* attempt at each of these fixes ineffective.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Run `fn` holding an exclusive cross-process lock on one route's file.
|
|
18
|
+
*
|
|
19
|
+
* mkdir is the atomic test-and-set here: it fails if the directory exists, on
|
|
20
|
+
* every platform, and unlike `writeFile` with `wx` it needs no cleanup path
|
|
21
|
+
* distinct from the directory itself. Synchronous on purpose — the callers
|
|
22
|
+
* (BreakerStore.update via Router.handleResult, and CLI paths that exit
|
|
23
|
+
* immediately afterwards) are sync, and making them async to acquire a lock
|
|
24
|
+
* would ripple through the whole dispatch return path for no benefit.
|
|
25
|
+
*
|
|
26
|
+
* Failing to acquire runs `fn` anyway rather than dropping the update: an
|
|
27
|
+
* un-serialised write is what we had before, so the fallback is no worse than
|
|
28
|
+
* the old behaviour, while a dropped failure would be strictly worse.
|
|
29
|
+
*
|
|
30
|
+
* That reasoning holds for a caller with NOTHING to fall back on, and it is
|
|
31
|
+
* why BreakerStore still uses the default. It does not hold for a caller that
|
|
32
|
+
* can retry: QuotaCache accumulates a pending delta and only clears it once a
|
|
33
|
+
* write succeeds, so for it a deferred write loses nothing while an
|
|
34
|
+
* unserialised one silently loses counts — it clears the delta believing the
|
|
35
|
+
* write was serialised, and the real lock holder then overwrites the file with
|
|
36
|
+
* a value computed before that write existed. `requireLock` is for that case:
|
|
37
|
+
* throw instead of running unserialised, and let the caller try again.
|
|
38
|
+
*/
|
|
39
|
+
export declare class LockNotAcquiredError extends Error {
|
|
40
|
+
constructor(file: string);
|
|
41
|
+
}
|
|
42
|
+
export declare function withFileLock<T>(file: string, fn: () => T, opts?: {
|
|
43
|
+
requireLock?: boolean;
|
|
44
|
+
}): T;
|
|
17
45
|
//# sourceMappingURL=file-lock.d.ts.map
|
package/dist/file-lock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgDH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,IAAI,EAAE,MAAM;CAOzB;AAiBD,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,EACX,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,CAAC,CA+DH"}
|
package/dist/file-lock.js
CHANGED
|
@@ -22,6 +22,36 @@ import path from "node:path";
|
|
|
22
22
|
* because the critical section is one read and one write.
|
|
23
23
|
*/
|
|
24
24
|
const LOCK_STALE_MS = 10_000;
|
|
25
|
+
/**
|
|
26
|
+
* Take a lock judged stale, by RENAME rather than delete.
|
|
27
|
+
*
|
|
28
|
+
* stat-then-rmdir let two waiters both judge the same lock stale — the slower
|
|
29
|
+
* one's rmdir then removed the faster one's FRESHLY CREATED lock, and both
|
|
30
|
+
* entered the critical section, recreating the unserialised
|
|
31
|
+
* read-modify-write this lock exists to prevent. Rename is atomic: exactly
|
|
32
|
+
* one waiter wins it, the loser gets ENOENT and goes round the loop again.
|
|
33
|
+
*
|
|
34
|
+
* Both failures are expected and neither is worth reporting: losing the
|
|
35
|
+
* rename means another waiter got there first, and a leftover tombstone is
|
|
36
|
+
* inert because nothing reads `*.stale-*` names. Lifted out of the acquire
|
|
37
|
+
* loop, where two nested try/catch blocks inside an if inside a try took it
|
|
38
|
+
* to seven levels.
|
|
39
|
+
*/
|
|
40
|
+
function stealStaleLock(lockDir) {
|
|
41
|
+
const tomb = `${lockDir}.stale-${process.pid}-${Date.now().toString(36)}`;
|
|
42
|
+
try {
|
|
43
|
+
renameSync(lockDir, tomb);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return; // lost the steal race to another waiter
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
rmdirSync(tomb);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Leftover tombstone; nothing reads `*.stale-*` names.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
25
55
|
/** Pause between acquisition attempts, so waiting costs no CPU. */
|
|
26
56
|
const RETRY_MS = 25;
|
|
27
57
|
/** Give up rather than block a dispatch indefinitely. */
|
|
@@ -39,7 +69,23 @@ const LOCK_TIMEOUT_MS = 2_000;
|
|
|
39
69
|
* Failing to acquire runs `fn` anyway rather than dropping the update: an
|
|
40
70
|
* un-serialised write is what we had before, so the fallback is no worse than
|
|
41
71
|
* the old behaviour, while a dropped failure would be strictly worse.
|
|
72
|
+
*
|
|
73
|
+
* That reasoning holds for a caller with NOTHING to fall back on, and it is
|
|
74
|
+
* why BreakerStore still uses the default. It does not hold for a caller that
|
|
75
|
+
* can retry: QuotaCache accumulates a pending delta and only clears it once a
|
|
76
|
+
* write succeeds, so for it a deferred write loses nothing while an
|
|
77
|
+
* unserialised one silently loses counts — it clears the delta believing the
|
|
78
|
+
* write was serialised, and the real lock holder then overwrites the file with
|
|
79
|
+
* a value computed before that write existed. `requireLock` is for that case:
|
|
80
|
+
* throw instead of running unserialised, and let the caller try again.
|
|
42
81
|
*/
|
|
82
|
+
export class LockNotAcquiredError extends Error {
|
|
83
|
+
constructor(file) {
|
|
84
|
+
super(`Could not acquire the lock on ${file} within ${LOCK_TIMEOUT_MS}ms — not running ` +
|
|
85
|
+
`unserialised, because this caller can retry.`);
|
|
86
|
+
this.name = "LockNotAcquiredError";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
43
89
|
/**
|
|
44
90
|
* Block this thread briefly without spinning.
|
|
45
91
|
*
|
|
@@ -56,7 +102,7 @@ function sleepSync(ms) {
|
|
|
56
102
|
// immediately; the deadline still bounds the loop.
|
|
57
103
|
}
|
|
58
104
|
}
|
|
59
|
-
export function withFileLock(file, fn) {
|
|
105
|
+
export function withFileLock(file, fn, opts = {}) {
|
|
60
106
|
const lockDir = `${file}.lock`;
|
|
61
107
|
// Ensure the parent exists before trying to lock inside it: a caller whose
|
|
62
108
|
// state directory has not been created yet would otherwise spin against an
|
|
@@ -83,25 +129,7 @@ export function withFileLock(file, fn) {
|
|
|
83
129
|
try {
|
|
84
130
|
const age = Date.now() - statSync(lockDir).mtimeMs;
|
|
85
131
|
if (age > LOCK_STALE_MS) {
|
|
86
|
-
|
|
87
|
-
// judge the same lock stale — the slower one's rmdir then removed
|
|
88
|
-
// the faster one's FRESHLY CREATED lock, and both entered the
|
|
89
|
-
// critical section, recreating the unserialised read-modify-write
|
|
90
|
-
// this lock exists to prevent. Rename is atomic: exactly one waiter
|
|
91
|
-
// wins it, the loser gets ENOENT and goes round the loop again.
|
|
92
|
-
const tomb = `${lockDir}.stale-${process.pid}-${Date.now().toString(36)}`;
|
|
93
|
-
try {
|
|
94
|
-
renameSync(lockDir, tomb);
|
|
95
|
-
try {
|
|
96
|
-
rmdirSync(tomb);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
// Leftover tombstone; nothing reads `*.stale-*` names.
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
// Lost the steal race to another waiter; retry normally.
|
|
104
|
-
}
|
|
132
|
+
stealStaleLock(lockDir);
|
|
105
133
|
continue;
|
|
106
134
|
}
|
|
107
135
|
}
|
|
@@ -126,6 +154,11 @@ export function withFileLock(file, fn) {
|
|
|
126
154
|
sleepSync(RETRY_MS);
|
|
127
155
|
}
|
|
128
156
|
}
|
|
157
|
+
if (!held && opts.requireLock === true) {
|
|
158
|
+
// Nothing to clean up: the lock was never taken, and the directory that
|
|
159
|
+
// blocked us belongs to whoever is holding it.
|
|
160
|
+
throw new LockNotAcquiredError(file);
|
|
161
|
+
}
|
|
129
162
|
try {
|
|
130
163
|
return fn();
|
|
131
164
|
}
|
package/dist/file-lock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;GAKG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC;AAE7B,mEAAmE;AACnE,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB,yDAAyD;AACzD,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../src/file-lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shipped harness presets: what `config.default.yaml` says a built-in
|
|
3
|
+
* harness is, and how a route of that harness behaves before the user
|
|
4
|
+
* changes anything.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS IS ITS OWN MODULE. "A harness preset" had no home. It was a
|
|
7
|
+
* protocol block in YAML, loaded and parsed inside config.ts, interpreted by
|
|
8
|
+
* dispatchers/generic-cli.ts, read again by mcp/dispatcher-factory.ts to
|
|
9
|
+
* collect API-key env vars, and asserted by four per-harness test files that
|
|
10
|
+
* each imported PROTOCOL_PRESETS and drove the generic dispatcher. A
|
|
11
|
+
* co-change check over 300 commits reported the result as shotgun surgery:
|
|
12
|
+
* ten files moving together across six directories, because every change to
|
|
13
|
+
* a shipped protocol had to touch all of them.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here loads a user's config; config.ts owns that and imports this.
|
|
16
|
+
* The dependency runs one way — presets know nothing about the file the user
|
|
17
|
+
* wrote, which is what keeps the parse order honest.
|
|
18
|
+
*/
|
|
19
|
+
import type { AuthSource, BillingKind, BillingProvider, BillingSurface, CliProtocolConfig, SafetyProfile } from "./types.js";
|
|
20
|
+
export interface CliDefaults {
|
|
21
|
+
command: string;
|
|
22
|
+
harness: string;
|
|
23
|
+
leaderboardModel: string;
|
|
24
|
+
cliCapability: number;
|
|
25
|
+
tier: number;
|
|
26
|
+
thinkingLevel?: "low" | "medium" | "high";
|
|
27
|
+
capabilities: {
|
|
28
|
+
execute: number;
|
|
29
|
+
plan: number;
|
|
30
|
+
review: number;
|
|
31
|
+
};
|
|
32
|
+
maxOutputTokens?: number;
|
|
33
|
+
maxInputTokens?: number;
|
|
34
|
+
provider: BillingProvider;
|
|
35
|
+
surface: BillingSurface;
|
|
36
|
+
authSource: AuthSource;
|
|
37
|
+
billingKind?: BillingKind;
|
|
38
|
+
paidUsagePossible?: boolean;
|
|
39
|
+
/** Safety level this harness actually runs at (capability floor) — see ServiceConfig.effectiveSafety. */
|
|
40
|
+
effectiveSafety?: SafetyProfile | Partial<Record<SafetyProfile, SafetyProfile>>;
|
|
41
|
+
/** Operator-declared known-good model ids — see ServiceConfig.models. */
|
|
42
|
+
models?: string[];
|
|
43
|
+
/** Where this harness's real model catalog lives — see ServiceConfig.modelHint. */
|
|
44
|
+
modelHint?: string;
|
|
45
|
+
/** Default dispatch protocol for this harness — see the shipped config.default.yaml. */
|
|
46
|
+
protocol?: CliProtocolConfig;
|
|
47
|
+
}
|
|
48
|
+
export declare const CLI_DEFAULTS: Record<string, CliDefaults>;
|
|
49
|
+
/** Named, selectable protocols — every harness in CLI_DEFAULTS that defines one, keyed by its harness name (see protocolFrom()'s string/`extends:` handling). */
|
|
50
|
+
export declare const PROTOCOL_PRESETS: Record<string, CliProtocolConfig>;
|
|
51
|
+
//# sourceMappingURL=harness-presets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-presets.d.ts","sourceRoot":"","sources":["../src/harness-presets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAoBH,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC1C,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,yGAAyG;IACzG,eAAe,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;IAChF,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAgID,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAA0B,CAAC;AAEhF,iKAAiK;AACjK,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAI9D,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shipped harness presets: what `config.default.yaml` says a built-in
|
|
3
|
+
* harness is, and how a route of that harness behaves before the user
|
|
4
|
+
* changes anything.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS IS ITS OWN MODULE. "A harness preset" had no home. It was a
|
|
7
|
+
* protocol block in YAML, loaded and parsed inside config.ts, interpreted by
|
|
8
|
+
* dispatchers/generic-cli.ts, read again by mcp/dispatcher-factory.ts to
|
|
9
|
+
* collect API-key env vars, and asserted by four per-harness test files that
|
|
10
|
+
* each imported PROTOCOL_PRESETS and drove the generic dispatcher. A
|
|
11
|
+
* co-change check over 300 commits reported the result as shotgun surgery:
|
|
12
|
+
* ten files moving together across six directories, because every change to
|
|
13
|
+
* a shipped protocol had to touch all of them.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here loads a user's config; config.ts owns that and imports this.
|
|
16
|
+
* The dependency runs one way — presets know nothing about the file the user
|
|
17
|
+
* wrote, which is what keeps the parse order honest.
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import yaml from "js-yaml";
|
|
23
|
+
import { authSourceFrom, billingKindFrom, capsFrom, int, num, providerFrom, str, surfaceFrom, } from "./config/coercions.js";
|
|
24
|
+
import { parseProtocolFields } from "./config/protocol.js";
|
|
25
|
+
import { resolveSharedRouteFields } from "./config/route-fields.js";
|
|
26
|
+
/**
|
|
27
|
+
* `harness: generic` is not a harness definition — it's the escape hatch a
|
|
28
|
+
* user's own entry selects when adding a wholly new CLI, so its "defaults"
|
|
29
|
+
* are parser fallbacks, not example config. Billing is unknown/blocked until
|
|
30
|
+
* the operator classifies it, since there's no way to know an arbitrary
|
|
31
|
+
* CLI's real billing model. Command is empty so it can never be
|
|
32
|
+
* auto-detected; the user's entry must supply command + protocol.
|
|
33
|
+
*/
|
|
34
|
+
const GENERIC_DEFAULTS = {
|
|
35
|
+
command: "",
|
|
36
|
+
harness: "generic",
|
|
37
|
+
leaderboardModel: "",
|
|
38
|
+
cliCapability: 1.0,
|
|
39
|
+
tier: 3,
|
|
40
|
+
capabilities: { execute: 1.0, plan: 1.0, review: 1.0 },
|
|
41
|
+
provider: "custom",
|
|
42
|
+
surface: "custom",
|
|
43
|
+
authSource: "unknown",
|
|
44
|
+
billingKind: "unknown",
|
|
45
|
+
paidUsagePossible: true,
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the package's own bundled config.default.yaml relative to this
|
|
49
|
+
* module — same "walk up" approach as leaderboard.ts's benchmark file, so
|
|
50
|
+
* it works whether this is running compiled (dist/) or via tsx (src/). This
|
|
51
|
+
* is a DIFFERENT file from a user's own config.yaml (never tracked, may
|
|
52
|
+
* hold secrets/local overrides) — separating them means a package update to
|
|
53
|
+
* the shipped defaults never collides with or gets clobbered by a user's
|
|
54
|
+
* live instance, and vice versa.
|
|
55
|
+
*/
|
|
56
|
+
function resolveShippedConfigPath() {
|
|
57
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
58
|
+
let dir = here;
|
|
59
|
+
for (let i = 0; i < 6; i += 1) {
|
|
60
|
+
const candidate = path.join(dir, "config.default.yaml");
|
|
61
|
+
if (existsSync(candidate))
|
|
62
|
+
return candidate;
|
|
63
|
+
const parent = path.dirname(dir);
|
|
64
|
+
if (parent === dir)
|
|
65
|
+
break;
|
|
66
|
+
dir = parent;
|
|
67
|
+
}
|
|
68
|
+
return path.join(here, "..", "config.default.yaml");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse one `clis:` entry of the shipped config into CliDefaults. Uses the
|
|
72
|
+
* exact same field parsers (str/num/int/capsFrom/parseProtocolFields/...)
|
|
73
|
+
* as a user's own `clis:` entry — the shipped file IS a config.yaml, parsed
|
|
74
|
+
* the same way, not a special hardcoded path or bespoke shape.
|
|
75
|
+
*/
|
|
76
|
+
function cliDefaultsFrom(raw, warnings) {
|
|
77
|
+
const harness = str(raw.harness);
|
|
78
|
+
if (!harness)
|
|
79
|
+
return undefined;
|
|
80
|
+
const protocolRaw = raw.protocol;
|
|
81
|
+
const protocol = protocolRaw !== undefined
|
|
82
|
+
? parseProtocolFields(protocolRaw, `shipped config.default.yaml clis "${harness}"`, warnings, undefined)
|
|
83
|
+
: undefined;
|
|
84
|
+
const billingKind = billingKindFrom(raw.billing_kind);
|
|
85
|
+
return [
|
|
86
|
+
harness,
|
|
87
|
+
{
|
|
88
|
+
// The shipped defaults are the FOURTH consumer of the shared field
|
|
89
|
+
// contract, and were the one still hand-writing its own list. It had
|
|
90
|
+
// already drifted: `thinking_level` on a shipped harness entry was
|
|
91
|
+
// dropped on the floor, while route-fields.ts reads `d.thinkingLevel`
|
|
92
|
+
// as the fallback for every user route of that harness — the exact
|
|
93
|
+
// silent-drop shape the table exists to retire, one layer down.
|
|
94
|
+
// Resolving through the table means a row added there works here too,
|
|
95
|
+
// instead of needing to be remembered in a second place.
|
|
96
|
+
...resolveSharedRouteFields(raw),
|
|
97
|
+
// This shape's own required identity fields, after the spread so they
|
|
98
|
+
// win: the table cannot supply them, and `leaderboardModel` is a
|
|
99
|
+
// required string here while the table leaves it optional.
|
|
100
|
+
command: str(raw.command) ?? "",
|
|
101
|
+
harness,
|
|
102
|
+
leaderboardModel: str(raw.leaderboard_model) ?? "",
|
|
103
|
+
cliCapability: num(raw.cli_capability, 1.0),
|
|
104
|
+
tier: int(raw.tier, 1),
|
|
105
|
+
capabilities: capsFrom(raw.capabilities),
|
|
106
|
+
provider: providerFrom(raw.provider) ?? "custom",
|
|
107
|
+
surface: surfaceFrom(raw.surface) ?? "custom",
|
|
108
|
+
authSource: authSourceFrom(raw.auth_source) ?? "unknown",
|
|
109
|
+
...(billingKind !== undefined ? { billingKind } : {}),
|
|
110
|
+
...(typeof raw.paid_usage_possible === "boolean" ? { paidUsagePossible: raw.paid_usage_possible } : {}),
|
|
111
|
+
...(protocol !== undefined ? { protocol } : {}),
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load the built-in harness defaults from the shipped config's `clis:` list
|
|
117
|
+
* — the same shape as a `clis:` entry in a user's own config.yaml, keyed by
|
|
118
|
+
* each entry's `harness:` value. Failure (missing/malformed file) degrades
|
|
119
|
+
* to just the generic escape hatch — auto-detect simply finds nothing
|
|
120
|
+
* built-in, rather than crashing the whole router; a user's own
|
|
121
|
+
* `clis:`/`services:` entries are unaffected either way.
|
|
122
|
+
*/
|
|
123
|
+
function loadDefaultHarnesses() {
|
|
124
|
+
const warnings = [];
|
|
125
|
+
const out = { generic: GENERIC_DEFAULTS };
|
|
126
|
+
try {
|
|
127
|
+
const raw = yaml.load(readFileSync(resolveShippedConfigPath(), "utf8"));
|
|
128
|
+
for (const entry of raw?.clis ?? []) {
|
|
129
|
+
const parsed = cliDefaultsFrom(entry, warnings);
|
|
130
|
+
if (parsed && parsed[0] !== "generic")
|
|
131
|
+
out[parsed[0]] = parsed[1];
|
|
132
|
+
}
|
|
133
|
+
if (warnings.length > 0) {
|
|
134
|
+
// Shipped-config warnings indicate a packaging/build problem, not a
|
|
135
|
+
// user config mistake — surface loudly rather than folding into
|
|
136
|
+
// configWarnings (which a user would reasonably assume is about
|
|
137
|
+
// their own file).
|
|
138
|
+
for (const w of warnings)
|
|
139
|
+
console.error(`harness-dispatch: shipped config.default.yaml: ${w}`);
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
console.error(`harness-dispatch: failed to load the package's shipped config.default.yaml (${err instanceof Error ? err.message : String(err)}) ` +
|
|
145
|
+
"— no built-in harnesses will be available; your own clis:/services: entries are unaffected.");
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export const CLI_DEFAULTS = loadDefaultHarnesses();
|
|
150
|
+
/** Named, selectable protocols — every harness in CLI_DEFAULTS that defines one, keyed by its harness name (see protocolFrom()'s string/`extends:` handling). */
|
|
151
|
+
export const PROTOCOL_PRESETS = Object.fromEntries(Object.entries(CLI_DEFAULTS)
|
|
152
|
+
.filter((entry) => entry[1].protocol !== undefined)
|
|
153
|
+
.map(([name, defaults]) => [name, defaults.protocol]));
|
|
154
|
+
//# sourceMappingURL=harness-presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-presets.js","sourceRoot":"","sources":["../src/harness-presets.ts"],"names":[],"mappings":"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"}
|
package/dist/http/parse.d.ts
CHANGED
|
@@ -54,7 +54,14 @@ export declare const MAX_REQUEST_BODY_BYTES: number;
|
|
|
54
54
|
*/
|
|
55
55
|
export declare class BadRequestError extends Error {
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* The MCP surface's own limit, imported rather than copied.
|
|
59
|
+
*
|
|
60
|
+
* This was a second `= 64` under a comment saying "mirrors MAX_CONTEXT_FILES
|
|
61
|
+
* in mcp/tools.ts — the two surfaces must agree". The constant does not live
|
|
62
|
+
* in that file, and a comment cannot make two numbers agree; the module next
|
|
63
|
+
* door was already importing MAX_TIMEOUT_MS from the same place.
|
|
64
|
+
*/
|
|
58
65
|
export declare const MAX_CONTEXT_FILES_HTTP = 64;
|
|
59
66
|
export declare function readJson(req: IncomingMessage, maxBytes?: number): Promise<unknown>;
|
|
60
67
|
/**
|
package/dist/http/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/http/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/http/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,gFAAgF;AAChF,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAElD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAGhB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAKD,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD;;;;;;;;GAQG;AACH,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,KAAoB,CAAC;AAExD,wBAAsB,QAAQ,CAC5B,GAAG,EAAE,eAAe,EACpB,QAAQ,GAAE,MAA+B,GACxC,OAAO,CAAC,OAAO,CAAC,CAwBlB;AA2ED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIxD;AA+QD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;CACnB,CA4EA;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAezB"}
|