@hardfin/cli 0.1.0-dev.33 → 0.1.0-dev.34
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 +21 -10
- package/dist/cli.js +4 -4
- package/dist/{program-ByvcKNri.js → program-DTsxnjRj.js} +183 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -320,22 +320,33 @@ other client can run Hardfin commands.
|
|
|
320
320
|
claude mcp add hardfin -- hardfin mcp
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
###
|
|
323
|
+
### Two tools, and resources for the rest
|
|
324
324
|
|
|
325
325
|
Every tool a server lists sits in the agent's context for the entire session, so this server
|
|
326
|
-
offers
|
|
326
|
+
offers two. They differ only in what they may run, which lets a client run a read without
|
|
327
|
+
asking and ask before anything changes.
|
|
327
328
|
|
|
328
|
-
| Offered as | Holds
|
|
329
|
-
| -------------------- |
|
|
330
|
-
|
|
|
331
|
-
| `
|
|
332
|
-
| `hardfin://
|
|
329
|
+
| Offered as | Holds | Costs context |
|
|
330
|
+
| -------------------- | ---------------------------------------------------------------------- | ---------------------------- |
|
|
331
|
+
| `hardfin_read` | A command that only reads, such as `["asset", "list", "--limit", "5"]` | Always, about 800 bytes |
|
|
332
|
+
| `hardfin_write` | A command that creates, changes or deletes, with an optional JSON body | Always, about 800 bytes |
|
|
333
|
+
| `hardfin://guide` | Every command, its flags, the exit codes and the error codes | Only when the agent reads it |
|
|
334
|
+
| `hardfin://commands` | The command tree as JSON | Only when the agent reads it |
|
|
333
335
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
+
A command is a read when its endpoint's method is GET, or for `hardfin api` when no other
|
|
337
|
+
`--method` is given. Anything else is a write, and `hardfin_read` refuses it by naming
|
|
338
|
+
`hardfin_write`. Both tools carry a title, the read-only or destructive hint, and a link to
|
|
339
|
+
the API reference, which Claude's connector directory requires.
|
|
336
340
|
|
|
337
341
|
A tool call runs the CLI itself, so an agent and a person get identical parsing, output and
|
|
338
|
-
exit codes.
|
|
342
|
+
exit codes. Each call has these limits:
|
|
343
|
+
|
|
344
|
+
| Limit | Value | Past it |
|
|
345
|
+
| ---------------- | --------------------------------------- | ------------------------------------------ |
|
|
346
|
+
| Time | 120 seconds | The command is stopped, and the call fails |
|
|
347
|
+
| Commands running | 4 at once | The next call waits for one to finish |
|
|
348
|
+
| One argument | 128 KiB | Refused, pointing at the `input` argument |
|
|
349
|
+
| Standard input | The call's `input` argument, or nothing | Always closed, so `--input -` never waits |
|
|
339
350
|
|
|
340
351
|
### What it signs in as
|
|
341
352
|
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f2529e6e-d7d4-5fe2-b460-7e86ca865c25")}catch(e){}}();
|
|
4
4
|
import module from "node:module";
|
|
5
5
|
//#region src/cli.ts
|
|
6
6
|
module.enableCompileCache?.();
|
|
@@ -14,7 +14,7 @@ process.on("unhandledRejection", (error) => void toCrash(error));
|
|
|
14
14
|
try {
|
|
15
15
|
const answer = await toQuickAnswer(process.argv.slice(2));
|
|
16
16
|
if (answer === void 0) {
|
|
17
|
-
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-
|
|
17
|
+
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-DTsxnjRj.js");
|
|
18
18
|
try {
|
|
19
19
|
await toCli().parseAsync(process.argv);
|
|
20
20
|
} catch (error) {
|
|
@@ -33,7 +33,7 @@ async function toCrash(error) {
|
|
|
33
33
|
isCrashing = true;
|
|
34
34
|
process.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
35
35
|
try {
|
|
36
|
-
const { toReported } = await import("./program-
|
|
36
|
+
const { toReported } = await import("./program-DTsxnjRj.js");
|
|
37
37
|
await toReported(error, false, void 0, "fatal");
|
|
38
38
|
} finally {
|
|
39
39
|
process.exit(1);
|
|
@@ -51,4 +51,4 @@ async function toQuickAnswer(args) {
|
|
|
51
51
|
export {};
|
|
52
52
|
|
|
53
53
|
//# sourceMappingURL=cli.js.map
|
|
54
|
-
//# debugId=
|
|
54
|
+
//# debugId=f2529e6e-d7d4-5fe2-b460-7e86ca865c25
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c7f8ce05-d638-5d0f-a088-655ddade6f45")}catch(e){}}();
|
|
4
4
|
import { a as defineCommand, i as GLOBAL_FLAGS, r as ExitCode, t as toInvocation } from "./scrub-BN3gyBpR.js";
|
|
5
5
|
import { n as toCompletion, t as toAnswer$1 } from "./completion-tree-rR_Rmeij.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -2797,23 +2797,52 @@ function wasReportAttempted() {
|
|
|
2797
2797
|
const PROTOCOL_VERSION = "2025-06-18";
|
|
2798
2798
|
const GUIDE_URI = "hardfin://guide";
|
|
2799
2799
|
const COMMANDS_URI = "hardfin://commands";
|
|
2800
|
-
/**
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
description: "Run a Hardfin CLI command against the Hardfin API. Pass the arguments as a list, such as [\"asset\", \"list\", \"--limit\", \"5\"]. Run [\"--help\"] for the commands, or read the hardfin://guide resource.",
|
|
2807
|
-
inputSchema: {
|
|
2808
|
-
type: "object",
|
|
2809
|
-
properties: { args: {
|
|
2800
|
+
/** The public reference for the API every command calls. */
|
|
2801
|
+
const API_REFERENCE = "https://docs.hardfin.com";
|
|
2802
|
+
const ARGUMENTS_SCHEMA = {
|
|
2803
|
+
type: "object",
|
|
2804
|
+
properties: {
|
|
2805
|
+
args: {
|
|
2810
2806
|
type: "array",
|
|
2811
2807
|
items: { type: "string" },
|
|
2812
2808
|
description: "The arguments to hardfin, without the program name"
|
|
2813
|
-
}
|
|
2814
|
-
|
|
2809
|
+
},
|
|
2810
|
+
input: {
|
|
2811
|
+
type: "string",
|
|
2812
|
+
description: "JSON sent to the command on standard input, which it reads when given --input -"
|
|
2813
|
+
}
|
|
2814
|
+
},
|
|
2815
|
+
required: ["args"]
|
|
2816
|
+
};
|
|
2817
|
+
/**
|
|
2818
|
+
* Two tools, so a client can let a read run without asking and ask before anything changes.
|
|
2819
|
+
* The commands themselves are resources, which cost nothing until one is read.
|
|
2820
|
+
*/
|
|
2821
|
+
const READ_TOOL = {
|
|
2822
|
+
name: "hardfin_read",
|
|
2823
|
+
title: "Read Hardfin data",
|
|
2824
|
+
description: `Runs a Hardfin CLI command that only reads, against the Hardfin API (${API_REFERENCE}). Takes the arguments as a list, such as ["asset", "list", "--limit", "5"]. ["--help"] lists the commands, and the hardfin://guide resource describes each one.`,
|
|
2825
|
+
inputSchema: ARGUMENTS_SCHEMA,
|
|
2826
|
+
annotations: {
|
|
2827
|
+
title: "Read Hardfin data",
|
|
2828
|
+
readOnlyHint: true,
|
|
2829
|
+
destructiveHint: false,
|
|
2830
|
+
openWorldHint: true
|
|
2815
2831
|
}
|
|
2816
2832
|
};
|
|
2833
|
+
const WRITE_TOOL = {
|
|
2834
|
+
name: "hardfin_write",
|
|
2835
|
+
title: "Change Hardfin data",
|
|
2836
|
+
description: `Runs a Hardfin CLI command that creates, changes or deletes records through the Hardfin API (${API_REFERENCE}). Takes the arguments as a list, such as ["customer", "create", "--name", "Acme"]. A JSON body goes in the input argument, with --input - in the list.`,
|
|
2837
|
+
inputSchema: ARGUMENTS_SCHEMA,
|
|
2838
|
+
annotations: {
|
|
2839
|
+
title: "Change Hardfin data",
|
|
2840
|
+
readOnlyHint: false,
|
|
2841
|
+
destructiveHint: true,
|
|
2842
|
+
openWorldHint: true
|
|
2843
|
+
}
|
|
2844
|
+
};
|
|
2845
|
+
const INSTRUCTIONS = "hardfin_read looks records up and hardfin_write changes them. Both run Hardfin CLI commands, so the arguments are what a person would type after hardfin. The hardfin://guide resource lists every command, flag and error code.";
|
|
2817
2846
|
/** ProtocolFailure is a line this server answers with a JSON-RPC error rather than a result. */
|
|
2818
2847
|
var ProtocolFailure = class extends Error {
|
|
2819
2848
|
code;
|
|
@@ -2841,10 +2870,12 @@ async function toResponse(request, commands, run) {
|
|
|
2841
2870
|
},
|
|
2842
2871
|
serverInfo: {
|
|
2843
2872
|
name: "hardfin",
|
|
2873
|
+
title: "Hardfin",
|
|
2844
2874
|
version: version$1
|
|
2845
|
-
}
|
|
2875
|
+
},
|
|
2876
|
+
instructions: INSTRUCTIONS
|
|
2846
2877
|
});
|
|
2847
|
-
case "tools/list": return answer({ tools: [
|
|
2878
|
+
case "tools/list": return answer({ tools: [READ_TOOL, WRITE_TOOL] });
|
|
2848
2879
|
case "resources/list": return answer({ resources: [{
|
|
2849
2880
|
uri: GUIDE_URI,
|
|
2850
2881
|
name: "Hardfin CLI guide",
|
|
@@ -2857,7 +2888,8 @@ async function toResponse(request, commands, run) {
|
|
|
2857
2888
|
mimeType: "application/json"
|
|
2858
2889
|
}] });
|
|
2859
2890
|
case "resources/read": {
|
|
2860
|
-
const uri =
|
|
2891
|
+
const uri = request.params?.["uri"];
|
|
2892
|
+
if (typeof uri !== "string" || uri === "") throw new ProtocolFailure(-32602, "resources/read needs a uri, such as hardfin://guide", request.id ?? null);
|
|
2861
2893
|
const resource = toResource(uri, commands);
|
|
2862
2894
|
if (!resource) return {
|
|
2863
2895
|
jsonrpc: "2.0",
|
|
@@ -2869,7 +2901,7 @@ async function toResponse(request, commands, run) {
|
|
|
2869
2901
|
};
|
|
2870
2902
|
return answer(resource);
|
|
2871
2903
|
}
|
|
2872
|
-
case "tools/call": return answer(await toToolResult(request
|
|
2904
|
+
case "tools/call": return answer(await toToolResult(request, commands, run));
|
|
2873
2905
|
case "ping": return answer({});
|
|
2874
2906
|
default:
|
|
2875
2907
|
if (request.id === void 0 || request.id === null) return;
|
|
@@ -2943,80 +2975,170 @@ function toCommandName(args) {
|
|
|
2943
2975
|
function toIsAskingForHelp(args) {
|
|
2944
2976
|
return args.includes("--help") || args.includes("-h") || args[0] === "help";
|
|
2945
2977
|
}
|
|
2946
|
-
async function toToolResult(
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
};
|
|
2954
|
-
const args = (params?.["arguments"])?.args;
|
|
2955
|
-
if (!Array.isArray(args) || args.some((entry) => typeof entry !== "string")) return {
|
|
2956
|
-
content: [{
|
|
2957
|
-
type: "text",
|
|
2958
|
-
text: "args must be a list of strings, such as [\"asset\", \"list\"]"
|
|
2959
|
-
}],
|
|
2960
|
-
isError: true
|
|
2961
|
-
};
|
|
2978
|
+
async function toToolResult(request, commands, run) {
|
|
2979
|
+
const params = request.params;
|
|
2980
|
+
const name = params?.["name"];
|
|
2981
|
+
if (name !== READ_TOOL.name && name !== WRITE_TOOL.name) throw new ProtocolFailure(-32602, `a call names its tool, and this server offers ${READ_TOOL.name} and ${WRITE_TOOL.name}`, request.id ?? null);
|
|
2982
|
+
const called = params?.["arguments"];
|
|
2983
|
+
const args = called?.args;
|
|
2984
|
+
if (!Array.isArray(args) || args.some((entry) => typeof entry !== "string")) return toError("args must be a list of strings, such as [\"asset\", \"list\"]");
|
|
2985
|
+
if (called?.input !== void 0 && typeof called.input !== "string") return toError("input must be a string holding JSON, such as {\"name\": \"Acme\"}");
|
|
2962
2986
|
const refused = toRefusedArgument(args);
|
|
2963
|
-
if (refused) return
|
|
2964
|
-
content: [{
|
|
2965
|
-
type: "text",
|
|
2966
|
-
text: refused
|
|
2967
|
-
}],
|
|
2968
|
-
isError: true
|
|
2969
|
-
};
|
|
2987
|
+
if (refused) return toError(refused);
|
|
2970
2988
|
const asked = args;
|
|
2971
2989
|
const command = toCommandName(asked) ?? "";
|
|
2972
|
-
if (REFUSED_COMMANDS.has(command) && !toIsAskingForHelp(asked)) return {
|
|
2990
|
+
if (REFUSED_COMMANDS.has(command) && !toIsAskingForHelp(asked)) return toError(`${command} is run by a person at a terminal, not through this server. Run hardfin ${command} yourself, then call this tool again`);
|
|
2991
|
+
if (name === READ_TOOL.name && toAccess(asked, commands) === "write") return toError(`this command changes data, so it runs through ${WRITE_TOOL.name}`);
|
|
2992
|
+
const outcome = await run(asked, called?.input);
|
|
2993
|
+
return {
|
|
2973
2994
|
content: [{
|
|
2974
2995
|
type: "text",
|
|
2975
|
-
text:
|
|
2996
|
+
text: [outcome.stdout, outcome.stderr].filter((part) => part.trim() !== "").join("\n") || `hardfin exited ${outcome.code}`
|
|
2976
2997
|
}],
|
|
2977
|
-
isError:
|
|
2998
|
+
isError: outcome.code !== 0
|
|
2978
2999
|
};
|
|
2979
|
-
|
|
3000
|
+
}
|
|
3001
|
+
function toError(text) {
|
|
2980
3002
|
return {
|
|
2981
3003
|
content: [{
|
|
2982
3004
|
type: "text",
|
|
2983
|
-
text
|
|
3005
|
+
text
|
|
2984
3006
|
}],
|
|
2985
|
-
isError:
|
|
3007
|
+
isError: true
|
|
2986
3008
|
};
|
|
2987
3009
|
}
|
|
2988
|
-
/**
|
|
2989
|
-
const
|
|
3010
|
+
/** Commands with no endpoint that only show something, which a read may run. */
|
|
3011
|
+
const LOCAL_READS = /* @__PURE__ */ new Set([
|
|
3012
|
+
"status",
|
|
3013
|
+
"config",
|
|
3014
|
+
"agent-guide",
|
|
3015
|
+
"help"
|
|
3016
|
+
]);
|
|
3017
|
+
/**
|
|
3018
|
+
* toAccess decides whether a call reads or changes data. A generated command answers by its
|
|
3019
|
+
* endpoint's method, hardfin api by the method it was given, and anything it cannot place is
|
|
3020
|
+
* treated as a change, so it is asked about.
|
|
3021
|
+
*/
|
|
3022
|
+
function toAccess(args, commands) {
|
|
3023
|
+
if (toIsAskingForHelp(args)) return "read";
|
|
3024
|
+
let level = commands;
|
|
3025
|
+
let found;
|
|
3026
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3027
|
+
const word = args[index] ?? "";
|
|
3028
|
+
if (GLOBAL_OPTIONS_WITH_VALUES.has(word)) {
|
|
3029
|
+
index += 1;
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
if (word.startsWith("-")) continue;
|
|
3033
|
+
const next = level.find((entry) => entry.name === word);
|
|
3034
|
+
if (!next) break;
|
|
3035
|
+
found = next;
|
|
3036
|
+
level = next.subcommands ?? [];
|
|
3037
|
+
if (level.length === 0) break;
|
|
3038
|
+
}
|
|
3039
|
+
if (!found) return "read";
|
|
3040
|
+
if (found.endpoint) return found.endpoint.method === "GET" ? "read" : "write";
|
|
3041
|
+
if (found.name === "api") return toApiMethod(args) === "GET" ? "read" : "write";
|
|
3042
|
+
if (LOCAL_READS.has(found.name) || (found.subcommands?.length ?? 0) > 0) return "read";
|
|
3043
|
+
return "write";
|
|
3044
|
+
}
|
|
3045
|
+
/** toApiMethod reads the method hardfin api was given, which is GET when none was. */
|
|
3046
|
+
function toApiMethod(args) {
|
|
3047
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3048
|
+
const word = args[index] ?? "";
|
|
3049
|
+
if (word === "-X" || word === "--method") return (args[index + 1] ?? "GET").toUpperCase();
|
|
3050
|
+
if (word.startsWith("--method=")) return word.slice(9).toUpperCase();
|
|
3051
|
+
if (word.startsWith("-X") && word.length > 2) return word.slice(2).toUpperCase();
|
|
3052
|
+
}
|
|
3053
|
+
return "GET";
|
|
3054
|
+
}
|
|
3055
|
+
/** The most one argument can hold, which Linux refuses past 128 KiB. */
|
|
3056
|
+
const ARGUMENT_BYTES = 131071;
|
|
3057
|
+
/** The most a whole command line can carry, below what the operating system refuses outright. */
|
|
3058
|
+
const COMMAND_LINE_BYTES = 1e6;
|
|
2990
3059
|
/** toRefusedArgument names an argument a command line cannot carry, rather than failing later. */
|
|
2991
3060
|
function toRefusedArgument(args) {
|
|
2992
3061
|
const withNul = args.findIndex((argument) => argument.includes("\0"));
|
|
2993
3062
|
if (withNul >= 0) return `args[${withNul}] holds a NUL byte, which a command line cannot carry`;
|
|
2994
|
-
const
|
|
2995
|
-
if (
|
|
2996
|
-
|
|
3063
|
+
const oversized = args.findIndex((argument) => Buffer.byteLength(argument) > ARGUMENT_BYTES);
|
|
3064
|
+
if (oversized >= 0) return `args[${oversized}] is ${Buffer.byteLength(args[oversized] ?? "")} bytes, and one argument carries at most ${ARGUMENT_BYTES}. Send a large body in the input argument instead`;
|
|
3065
|
+
const total = args.reduce((carried, argument) => carried + Buffer.byteLength(argument), 0);
|
|
3066
|
+
if (total > COMMAND_LINE_BYTES) return `these arguments are ${total} bytes, and a command line carries at most ${COMMAND_LINE_BYTES}`;
|
|
3067
|
+
}
|
|
3068
|
+
/** How long one command may run before it is stopped, so a hung call cannot hold its client. */
|
|
3069
|
+
const CALL_TIMEOUT_MS = 12e4;
|
|
3070
|
+
/** How many commands run at once, so a burst of calls cannot start a process each. */
|
|
3071
|
+
const MAX_RUNNING = 4;
|
|
2997
3072
|
/** toCliRunner runs the CLI itself, so a tool call parses exactly as a terminal would. */
|
|
2998
|
-
function toCliRunner() {
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3073
|
+
function toCliRunner(timeoutMs = CALL_TIMEOUT_MS, entry = process.argv[1] ?? "") {
|
|
3074
|
+
const waiting = [];
|
|
3075
|
+
let running = 0;
|
|
3076
|
+
const acquire = async () => {
|
|
3077
|
+
if (running < MAX_RUNNING) {
|
|
3078
|
+
running += 1;
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
3082
|
+
};
|
|
3083
|
+
const release = () => {
|
|
3084
|
+
const next = waiting.shift();
|
|
3085
|
+
if (next) next();
|
|
3086
|
+
else running -= 1;
|
|
3087
|
+
};
|
|
3088
|
+
return async (args, input) => {
|
|
3089
|
+
await acquire();
|
|
3090
|
+
try {
|
|
3091
|
+
return await toRun(entry, args, input, timeoutMs);
|
|
3092
|
+
} finally {
|
|
3093
|
+
release();
|
|
3094
|
+
}
|
|
3095
|
+
};
|
|
3096
|
+
}
|
|
3097
|
+
/** toRun runs one command, feeding it input on stdin and stopping it at the deadline. */
|
|
3098
|
+
function toRun(entry, args, input, timeoutMs) {
|
|
3099
|
+
return new Promise((resolve) => {
|
|
3100
|
+
let child;
|
|
3101
|
+
try {
|
|
3102
|
+
child = spawn(process.execPath, [entry, ...args], { env: {
|
|
3103
|
+
...process.env,
|
|
3104
|
+
HARDFIN_NO_BROWSER: "1",
|
|
3105
|
+
HARDFIN_MCP: "1"
|
|
3106
|
+
} });
|
|
3107
|
+
} catch (error) {
|
|
3108
|
+
resolve({
|
|
3109
|
+
stdout: "",
|
|
3110
|
+
stderr: `the command could not start: ${error instanceof Error ? error.message : String(error)}`,
|
|
3111
|
+
code: 1
|
|
3112
|
+
});
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3005
3115
|
let stdout = "";
|
|
3006
3116
|
let stderr = "";
|
|
3117
|
+
let isTimedOut = false;
|
|
3118
|
+
const timer = setTimeout(() => {
|
|
3119
|
+
isTimedOut = true;
|
|
3120
|
+
child.kill("SIGKILL");
|
|
3121
|
+
}, timeoutMs);
|
|
3007
3122
|
child.stdout.on("data", (chunk) => {
|
|
3008
3123
|
stdout += chunk.toString();
|
|
3009
3124
|
});
|
|
3010
3125
|
child.stderr.on("data", (chunk) => {
|
|
3011
3126
|
stderr += chunk.toString();
|
|
3012
3127
|
});
|
|
3128
|
+
child.on("error", (error) => {
|
|
3129
|
+
stderr += `the command could not start: ${error.message}\n`;
|
|
3130
|
+
});
|
|
3013
3131
|
child.on("close", (code) => {
|
|
3132
|
+
clearTimeout(timer);
|
|
3133
|
+
if (isTimedOut) stderr += `this command took longer than ${timeoutMs / 1e3} seconds, so it was stopped\n`;
|
|
3014
3134
|
resolve({
|
|
3015
3135
|
stdout,
|
|
3016
3136
|
stderr,
|
|
3017
|
-
code: code ?? 1
|
|
3137
|
+
code: isTimedOut ? 1 : code ?? 1
|
|
3018
3138
|
});
|
|
3019
3139
|
});
|
|
3140
|
+
child.stdin.on("error", () => void 0);
|
|
3141
|
+
child.stdin.end(input ?? "");
|
|
3020
3142
|
});
|
|
3021
3143
|
}
|
|
3022
3144
|
/** serve answers requests on stdin until the client closes it. */
|
|
@@ -6187,5 +6309,5 @@ async function toReported(error, isJSON, resolved, level = "error") {
|
|
|
6187
6309
|
//#endregion
|
|
6188
6310
|
export { toCli, toParseExitCode, toReported, wasReportAttempted };
|
|
6189
6311
|
|
|
6190
|
-
//# sourceMappingURL=program-
|
|
6191
|
-
//# debugId=
|
|
6312
|
+
//# sourceMappingURL=program-DTsxnjRj.js.map
|
|
6313
|
+
//# debugId=c7f8ce05-d638-5d0f-a088-655ddade6f45
|