harness-dispatch 0.4.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 +21 -0
- package/README.md +582 -0
- package/config.default.yaml +397 -0
- package/data/coding_benchmarks.json +431 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +73 -0
- package/dist/auth.js.map +1 -0
- package/dist/billing.d.ts +7 -0
- package/dist/billing.d.ts.map +1 -0
- package/dist/billing.js +201 -0
- package/dist/billing.js.map +1 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +488 -0
- package/dist/bin.js.map +1 -0
- package/dist/breaker-store.d.ts +31 -0
- package/dist/breaker-store.d.ts.map +1 -0
- package/dist/breaker-store.js +103 -0
- package/dist/breaker-store.js.map +1 -0
- package/dist/circuit-breaker.d.ts +47 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +102 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +1248 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/live.d.ts +49 -0
- package/dist/dashboard/live.d.ts.map +1 -0
- package/dist/dashboard/live.js +149 -0
- package/dist/dashboard/live.js.map +1 -0
- package/dist/dispatch-log.d.ts +46 -0
- package/dist/dispatch-log.d.ts.map +1 -0
- package/dist/dispatch-log.js +89 -0
- package/dist/dispatch-log.js.map +1 -0
- package/dist/dispatchers/base.d.ts +55 -0
- package/dist/dispatchers/base.d.ts.map +1 -0
- package/dist/dispatchers/base.js +80 -0
- package/dist/dispatchers/base.js.map +1 -0
- package/dist/dispatchers/generic-cli.d.ts +30 -0
- package/dist/dispatchers/generic-cli.d.ts.map +1 -0
- package/dist/dispatchers/generic-cli.js +471 -0
- package/dist/dispatchers/generic-cli.js.map +1 -0
- package/dist/dispatchers/openai-compatible.d.ts +57 -0
- package/dist/dispatchers/openai-compatible.d.ts.map +1 -0
- package/dist/dispatchers/openai-compatible.js +662 -0
- package/dist/dispatchers/openai-compatible.js.map +1 -0
- package/dist/dispatchers/shared/kill-tree.d.ts +21 -0
- package/dist/dispatchers/shared/kill-tree.d.ts.map +1 -0
- package/dist/dispatchers/shared/kill-tree.js +35 -0
- package/dist/dispatchers/shared/kill-tree.js.map +1 -0
- package/dist/dispatchers/shared/rate-limit-headers.d.ts +22 -0
- package/dist/dispatchers/shared/rate-limit-headers.d.ts.map +1 -0
- package/dist/dispatchers/shared/rate-limit-headers.js +124 -0
- package/dist/dispatchers/shared/rate-limit-headers.js.map +1 -0
- package/dist/dispatchers/shared/stream-subprocess.d.ts +53 -0
- package/dist/dispatchers/shared/stream-subprocess.d.ts.map +1 -0
- package/dist/dispatchers/shared/stream-subprocess.js +274 -0
- package/dist/dispatchers/shared/stream-subprocess.js.map +1 -0
- package/dist/dispatchers/shared/subprocess.d.ts +25 -0
- package/dist/dispatchers/shared/subprocess.d.ts.map +1 -0
- package/dist/dispatchers/shared/subprocess.js +110 -0
- package/dist/dispatchers/shared/subprocess.js.map +1 -0
- package/dist/dispatchers/shared/which-available.d.ts +2 -0
- package/dist/dispatchers/shared/which-available.d.ts.map +1 -0
- package/dist/dispatchers/shared/which-available.js +8 -0
- package/dist/dispatchers/shared/which-available.js.map +1 -0
- package/dist/dispatchers/shared/windows-cmd.d.ts +38 -0
- package/dist/dispatchers/shared/windows-cmd.d.ts.map +1 -0
- package/dist/dispatchers/shared/windows-cmd.js +91 -0
- package/dist/dispatchers/shared/windows-cmd.js.map +1 -0
- package/dist/http/server.d.ts +15 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +495 -0
- package/dist/http/server.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/job-runner.d.ts +19 -0
- package/dist/job-runner.d.ts.map +1 -0
- package/dist/job-runner.js +42 -0
- package/dist/job-runner.js.map +1 -0
- package/dist/jobs.d.ts +116 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +542 -0
- package/dist/jobs.js.map +1 -0
- package/dist/leaderboard.d.ts +62 -0
- package/dist/leaderboard.d.ts.map +1 -0
- package/dist/leaderboard.js +287 -0
- package/dist/leaderboard.js.map +1 -0
- package/dist/mcp/config-hot-reload.d.ts +64 -0
- package/dist/mcp/config-hot-reload.d.ts.map +1 -0
- package/dist/mcp/config-hot-reload.js +144 -0
- package/dist/mcp/config-hot-reload.js.map +1 -0
- package/dist/mcp/dispatcher-factory.d.ts +20 -0
- package/dist/mcp/dispatcher-factory.d.ts.map +1 -0
- package/dist/mcp/dispatcher-factory.js +30 -0
- package/dist/mcp/dispatcher-factory.js.map +1 -0
- package/dist/mcp/resources.d.ts +8 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +45 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/server.d.ts +36 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +80 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +171 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +658 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/observability/index.d.ts +52 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +111 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/observability/spans.d.ts +47 -0
- package/dist/observability/spans.d.ts.map +1 -0
- package/dist/observability/spans.js +72 -0
- package/dist/observability/spans.js.map +1 -0
- package/dist/quota.d.ts +91 -0
- package/dist/quota.d.ts.map +1 -0
- package/dist/quota.js +331 -0
- package/dist/quota.js.map +1 -0
- package/dist/route-policy.d.ts +24 -0
- package/dist/route-policy.d.ts.map +1 -0
- package/dist/route-policy.js +92 -0
- package/dist/route-policy.js.map +1 -0
- package/dist/router.d.ts +162 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +878 -0
- package/dist/router.js.map +1 -0
- package/dist/safety.d.ts +7 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +33 -0
- package/dist/safety.js.map +1 -0
- package/dist/status.d.ts +100 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +228 -0
- package/dist/status.js.map +1 -0
- package/dist/types.d.ts +402 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/dist/working-dir.d.ts +15 -0
- package/dist/working-dir.d.ts.map +1 -0
- package/dist/working-dir.js +22 -0
- package/dist/working-dir.js.map +1 -0
- package/dist/workspaces.d.ts +19 -0
- package/dist/workspaces.d.ts.map +1 -0
- package/dist/workspaces.js +352 -0
- package/dist/workspaces.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async subprocess runner.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `child_process.spawn` with:
|
|
5
|
+
* - hard timeout (SIGTERM, then SIGKILL after grace period)
|
|
6
|
+
* - output byte cap (protects the agent from pathological CLI output)
|
|
7
|
+
* - DEVNULL stdin by default (prevents interactive auth prompts from hanging)
|
|
8
|
+
* - UTF-8 decoding with replacement on invalid sequences
|
|
9
|
+
*/
|
|
10
|
+
import spawn from "cross-spawn";
|
|
11
|
+
import { killTree } from "./kill-tree.js";
|
|
12
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
13
|
+
const DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
14
|
+
const KILL_GRACE_MS = 2_000;
|
|
15
|
+
export function runSubprocess(command, args, opts = {}) {
|
|
16
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
17
|
+
const maxOutputBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const spawnOpts = {
|
|
21
|
+
stdio: [opts.stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
|
|
22
|
+
windowsHide: true,
|
|
23
|
+
env: opts.env ? { ...process.env, ...opts.env } : process.env,
|
|
24
|
+
};
|
|
25
|
+
if (opts.cwd !== undefined)
|
|
26
|
+
spawnOpts.cwd = opts.cwd;
|
|
27
|
+
let child;
|
|
28
|
+
try {
|
|
29
|
+
child = spawn(command, args, spawnOpts);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (opts.stdin !== undefined) {
|
|
36
|
+
child.stdin?.end(opts.stdin);
|
|
37
|
+
}
|
|
38
|
+
const stdoutChunks = [];
|
|
39
|
+
const stderrChunks = [];
|
|
40
|
+
let stdoutBytes = 0;
|
|
41
|
+
let stderrBytes = 0;
|
|
42
|
+
let truncated = false;
|
|
43
|
+
let timedOut = false;
|
|
44
|
+
let settled = false;
|
|
45
|
+
const appendStdout = (chunk) => {
|
|
46
|
+
if (truncated)
|
|
47
|
+
return;
|
|
48
|
+
if (stdoutBytes + chunk.length > maxOutputBytes) {
|
|
49
|
+
const remaining = Math.max(0, maxOutputBytes - stdoutBytes);
|
|
50
|
+
if (remaining > 0)
|
|
51
|
+
stdoutChunks.push(chunk.subarray(0, remaining));
|
|
52
|
+
stdoutBytes = maxOutputBytes;
|
|
53
|
+
truncated = true;
|
|
54
|
+
killTree(child, "SIGTERM");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
stdoutChunks.push(chunk);
|
|
58
|
+
stdoutBytes += chunk.length;
|
|
59
|
+
};
|
|
60
|
+
const appendStderr = (chunk) => {
|
|
61
|
+
if (truncated)
|
|
62
|
+
return;
|
|
63
|
+
if (stderrBytes + chunk.length > maxOutputBytes) {
|
|
64
|
+
const remaining = Math.max(0, maxOutputBytes - stderrBytes);
|
|
65
|
+
if (remaining > 0)
|
|
66
|
+
stderrChunks.push(chunk.subarray(0, remaining));
|
|
67
|
+
stderrBytes = maxOutputBytes;
|
|
68
|
+
truncated = true;
|
|
69
|
+
killTree(child, "SIGTERM");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
stderrChunks.push(chunk);
|
|
73
|
+
stderrBytes += chunk.length;
|
|
74
|
+
};
|
|
75
|
+
child.stdout?.on("data", (chunk) => appendStdout(chunk));
|
|
76
|
+
child.stderr?.on("data", (chunk) => appendStderr(chunk));
|
|
77
|
+
const timer = setTimeout(() => {
|
|
78
|
+
timedOut = true;
|
|
79
|
+
killTree(child, "SIGTERM");
|
|
80
|
+
// Force-kill if the child ignores SIGTERM.
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
if (!settled)
|
|
83
|
+
killTree(child, "SIGKILL");
|
|
84
|
+
}, KILL_GRACE_MS).unref();
|
|
85
|
+
}, timeoutMs);
|
|
86
|
+
timer.unref();
|
|
87
|
+
child.on("error", (err) => {
|
|
88
|
+
if (settled)
|
|
89
|
+
return;
|
|
90
|
+
settled = true;
|
|
91
|
+
clearTimeout(timer);
|
|
92
|
+
reject(err);
|
|
93
|
+
});
|
|
94
|
+
child.on("close", (code, signal) => {
|
|
95
|
+
if (settled)
|
|
96
|
+
return;
|
|
97
|
+
settled = true;
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
const exitCode = code ?? (signal ? 128 : -1);
|
|
100
|
+
resolve({
|
|
101
|
+
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
102
|
+
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
103
|
+
exitCode,
|
|
104
|
+
durationMs: Date.now() - start,
|
|
105
|
+
timedOut,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=subprocess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subprocess.js","sourceRoot":"","sources":["../../../src/dispatchers/shared/subprocess.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"which-available.d.ts","sourceRoot":"","sources":["../../../src/dispatchers/shared/which-available.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMzD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import which from "which";
|
|
2
|
+
export function commandAvailable(command) {
|
|
3
|
+
const candidate = which;
|
|
4
|
+
if (typeof candidate.sync !== "function")
|
|
5
|
+
return true;
|
|
6
|
+
return Boolean(candidate.sync(command, { nothrow: true }));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=which-available.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"which-available.js","sourceRoot":"","sources":["../../../src/dispatchers/shared/which-available.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,SAAS,GAAG,KAEjB,CAAC;IACF,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform CLI binary resolution.
|
|
3
|
+
*
|
|
4
|
+
* On Windows, CLI tools installed via npm/scoop/winget are typically `.cmd` or
|
|
5
|
+
* `.bat` wrappers. Node's `spawn` cannot execute those directly without a
|
|
6
|
+
* shell — attempting to do so throws ENOENT.
|
|
7
|
+
*
|
|
8
|
+
* This used to hand back `{ command: "cmd", prefixArgs: ["/c", path] }` and
|
|
9
|
+
* let dispatchers spawn that directly via node:child_process. That's exactly
|
|
10
|
+
* the shape of a real Windows command-injection bug: Node's spawn() only
|
|
11
|
+
* safely escapes cmd.exe metacharacters in an argument when IT decides
|
|
12
|
+
* cmd.exe indirection is needed (i.e. when the target path itself ends in
|
|
13
|
+
* .bat/.cmd) — manually pre-constructing the "cmd /c <path>" invocation
|
|
14
|
+
* ourselves bypasses that. Confirmed empirically on Node 24.14.1: an
|
|
15
|
+
* argument containing a literal `"` character breaks out of the quoting and
|
|
16
|
+
* lets a subsequent `&`-chained command execute for real. `%ENV_VAR%`
|
|
17
|
+
* sequences also got expanded regardless (a separate info-leak/argument-
|
|
18
|
+
* splitting bug, since the CLI's own model/prompt text is never meant to be
|
|
19
|
+
* shell-interpreted at all).
|
|
20
|
+
*
|
|
21
|
+
* Fix: resolveCliCommand no longer constructs the cmd.exe wrapper itself.
|
|
22
|
+
* For the one case worth the extra step — npm's own generated .cmd shim,
|
|
23
|
+
* which just re-invokes `node <script>.js` — we skip cmd.exe entirely by
|
|
24
|
+
* spawning node directly on the underlying script (faster, and immune to
|
|
25
|
+
* shell metacharacters since there's no shell in the loop at all). For every
|
|
26
|
+
* other .cmd/.bat shape (pnpm, yarn, scoop, hand-rolled), we hand the
|
|
27
|
+
* resolved path straight to cross-spawn (see subprocess.ts/
|
|
28
|
+
* stream-subprocess.ts), which is a widely-used, purpose-built library for
|
|
29
|
+
* exactly this problem — it detects the .bat/.cmd target itself and applies
|
|
30
|
+
* correct, tested escaping, verified against the same injection payload
|
|
31
|
+
* above (no command execution, no %VAR% expansion).
|
|
32
|
+
*/
|
|
33
|
+
export interface ResolvedCommand {
|
|
34
|
+
command: string;
|
|
35
|
+
prefixArgs: string[];
|
|
36
|
+
}
|
|
37
|
+
export declare function resolveCliCommand(bin: string): Promise<ResolvedCommand>;
|
|
38
|
+
//# sourceMappingURL=windows-cmd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-cmd.d.ts","sourceRoot":"","sources":["../../../src/dispatchers/shared/windows-cmd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAsCD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAuB7E"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform CLI binary resolution.
|
|
3
|
+
*
|
|
4
|
+
* On Windows, CLI tools installed via npm/scoop/winget are typically `.cmd` or
|
|
5
|
+
* `.bat` wrappers. Node's `spawn` cannot execute those directly without a
|
|
6
|
+
* shell — attempting to do so throws ENOENT.
|
|
7
|
+
*
|
|
8
|
+
* This used to hand back `{ command: "cmd", prefixArgs: ["/c", path] }` and
|
|
9
|
+
* let dispatchers spawn that directly via node:child_process. That's exactly
|
|
10
|
+
* the shape of a real Windows command-injection bug: Node's spawn() only
|
|
11
|
+
* safely escapes cmd.exe metacharacters in an argument when IT decides
|
|
12
|
+
* cmd.exe indirection is needed (i.e. when the target path itself ends in
|
|
13
|
+
* .bat/.cmd) — manually pre-constructing the "cmd /c <path>" invocation
|
|
14
|
+
* ourselves bypasses that. Confirmed empirically on Node 24.14.1: an
|
|
15
|
+
* argument containing a literal `"` character breaks out of the quoting and
|
|
16
|
+
* lets a subsequent `&`-chained command execute for real. `%ENV_VAR%`
|
|
17
|
+
* sequences also got expanded regardless (a separate info-leak/argument-
|
|
18
|
+
* splitting bug, since the CLI's own model/prompt text is never meant to be
|
|
19
|
+
* shell-interpreted at all).
|
|
20
|
+
*
|
|
21
|
+
* Fix: resolveCliCommand no longer constructs the cmd.exe wrapper itself.
|
|
22
|
+
* For the one case worth the extra step — npm's own generated .cmd shim,
|
|
23
|
+
* which just re-invokes `node <script>.js` — we skip cmd.exe entirely by
|
|
24
|
+
* spawning node directly on the underlying script (faster, and immune to
|
|
25
|
+
* shell metacharacters since there's no shell in the loop at all). For every
|
|
26
|
+
* other .cmd/.bat shape (pnpm, yarn, scoop, hand-rolled), we hand the
|
|
27
|
+
* resolved path straight to cross-spawn (see subprocess.ts/
|
|
28
|
+
* stream-subprocess.ts), which is a widely-used, purpose-built library for
|
|
29
|
+
* exactly this problem — it detects the .bat/.cmd target itself and applies
|
|
30
|
+
* correct, tested escaping, verified against the same injection payload
|
|
31
|
+
* above (no command execution, no %VAR% expansion).
|
|
32
|
+
*/
|
|
33
|
+
import path from "node:path";
|
|
34
|
+
import fs from "node:fs/promises";
|
|
35
|
+
import which from "which";
|
|
36
|
+
async function resolveWindowsCandidate(bin, first) {
|
|
37
|
+
try {
|
|
38
|
+
const all = (await which(bin, {
|
|
39
|
+
all: true,
|
|
40
|
+
nothrow: true,
|
|
41
|
+
}));
|
|
42
|
+
if (Array.isArray(all)) {
|
|
43
|
+
const native = all.find((candidate) => path.extname(candidate).toLowerCase() === ".exe" &&
|
|
44
|
+
!candidate.toLowerCase().includes("\\windowsapps\\"));
|
|
45
|
+
if (native)
|
|
46
|
+
return native;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Fall back to the first candidate resolved by which.
|
|
51
|
+
}
|
|
52
|
+
return first;
|
|
53
|
+
}
|
|
54
|
+
async function resolveNpmCmdShim(cmdPath) {
|
|
55
|
+
try {
|
|
56
|
+
const text = await fs.readFile(cmdPath, "utf8");
|
|
57
|
+
const match = text.match(/"%dp0%\\([^"]+?\.js)"/i);
|
|
58
|
+
if (!match?.[1])
|
|
59
|
+
return null;
|
|
60
|
+
const scriptPath = path.join(path.dirname(cmdPath), match[1].replace(/\\/g, path.sep));
|
|
61
|
+
await fs.access(scriptPath);
|
|
62
|
+
return { command: process.execPath, prefixArgs: [scriptPath] };
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function resolveCliCommand(bin) {
|
|
69
|
+
const resolved = await which(bin, { nothrow: true });
|
|
70
|
+
if (!resolved) {
|
|
71
|
+
// Let spawn surface the ENOENT — caller may be running in a sandbox where
|
|
72
|
+
// PATH resolution is deliberately stubbed.
|
|
73
|
+
return { command: bin, prefixArgs: [] };
|
|
74
|
+
}
|
|
75
|
+
if (process.platform !== "win32") {
|
|
76
|
+
return { command: resolved, prefixArgs: [] };
|
|
77
|
+
}
|
|
78
|
+
const windowsResolved = await resolveWindowsCandidate(bin, resolved);
|
|
79
|
+
const ext = path.extname(windowsResolved).toLowerCase();
|
|
80
|
+
if (ext === ".cmd") {
|
|
81
|
+
const npmShim = await resolveNpmCmdShim(windowsResolved);
|
|
82
|
+
if (npmShim)
|
|
83
|
+
return npmShim;
|
|
84
|
+
}
|
|
85
|
+
// Any other .cmd/.bat shape (pnpm, yarn, scoop, hand-rolled) — or a native
|
|
86
|
+
// .exe — is handed straight to cross-spawn as prefixArgs: []. It detects
|
|
87
|
+
// .bat/.cmd targets itself and applies correct escaping; native binaries
|
|
88
|
+
// pass through unchanged, same as before.
|
|
89
|
+
return { command: windowsResolved, prefixArgs: [] };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=windows-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows-cmd.js","sourceRoot":"","sources":["../../../src/dispatchers/shared/windows-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type BuildMcpOptions, type McpHandle } from "../mcp/server.js";
|
|
2
|
+
export interface HttpServerHandle extends McpHandle {
|
|
3
|
+
port: number;
|
|
4
|
+
host: string;
|
|
5
|
+
token: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface StartHttpOptions extends BuildMcpOptions {
|
|
8
|
+
port?: number;
|
|
9
|
+
host?: string;
|
|
10
|
+
mcpRoute?: string;
|
|
11
|
+
token?: string | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function startHttpServer(opts?: StartHttpOptions): Promise<HttpServerHandle>;
|
|
14
|
+
export declare const startMcpHttpServer: typeof startHttpServer;
|
|
15
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/http/server.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAO1B,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AA4ND,wBAAsB,eAAe,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyX5F;AAED,eAAO,MAAM,kBAAkB,wBAAkB,CAAC"}
|