@hardfin/cli 0.1.0-dev.28 → 0.1.0-dev.30
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 +18 -1
- package/dist/cli.js +11 -5
- package/dist/{program-C3SGdPbf.js → program-BZW_50HP.js} +284 -180
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,6 +87,23 @@ Three things fail locally with exit code 2, before any request is made: a value
|
|
|
87
87
|
enum, a required field that is missing, and `--unset` on a field the API will not take null
|
|
88
88
|
for.
|
|
89
89
|
|
|
90
|
+
### When a command fails
|
|
91
|
+
|
|
92
|
+
A person at a terminal reads one plain line on stderr. Anything else reads JSON, also on
|
|
93
|
+
stderr, so a pipe still carries only the result.
|
|
94
|
+
|
|
95
|
+
| Field | Holds |
|
|
96
|
+
| ----------- | ------------------------------------------------------------------------ |
|
|
97
|
+
| `code` | A fixed name for the failure, such as `not_found` or `missing_flag` |
|
|
98
|
+
| `message` | The same plain sentence a person reads |
|
|
99
|
+
| `errors` | Every error the API's envelope named, with its own numeric `statusCode` |
|
|
100
|
+
| `requestId` | The API's request ID, or null when the request never reached it |
|
|
101
|
+
| `status` | The HTTP status, or null for a failure caught before any request is made |
|
|
102
|
+
|
|
103
|
+
An agent branches on `code`. A code is never renamed and never changes meaning, while the
|
|
104
|
+
message may be reworded at any time. `hardfin agent-guide` lists every code with what it
|
|
105
|
+
means, and `src/output/failure.ts` defines them.
|
|
106
|
+
|
|
90
107
|
### Overrides
|
|
91
108
|
|
|
92
109
|
Two endpoints sometimes want one name. `DELETE /asset/{assetKey}/ownership` and
|
|
@@ -455,7 +472,7 @@ if that version is already on npm it increments the patch, so a preview always s
|
|
|
455
472
|
the last release.
|
|
456
473
|
|
|
457
474
|
Before publishing, the workflow stamps each file in `dist` and its source map with a matching
|
|
458
|
-
debug ID, and uploads the maps to Sentry under the release
|
|
475
|
+
debug ID, and uploads the maps to Sentry under the release `hardfin-cli@<version>`. An error
|
|
459
476
|
report's stack trace then reads as the TypeScript source. The maps are not in the package, and
|
|
460
477
|
publishing skips `prepublishOnly`, so the stamped files are the ones published. The upload
|
|
461
478
|
needs the `SENTRY_AUTH_TOKEN` secret.
|
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]="29560915-e7ae-5a74-b5f6-f4a6988e1b43")}catch(e){}}();
|
|
4
4
|
import module from "node:module";
|
|
5
5
|
//#region src/cli.ts
|
|
6
6
|
module.enableCompileCache?.();
|
|
@@ -14,8 +14,14 @@ 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, wasReportAttempted } = await import("./program-
|
|
18
|
-
|
|
17
|
+
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-BZW_50HP.js");
|
|
18
|
+
try {
|
|
19
|
+
await toCli().parseAsync(process.argv);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const code = toParseExitCode(error, process.argv.slice(2));
|
|
22
|
+
if (code === void 0) throw error;
|
|
23
|
+
process.exitCode = code;
|
|
24
|
+
}
|
|
19
25
|
if (wasReportAttempted()) process.exit();
|
|
20
26
|
} else process.stdout.write(`${answer}\n`);
|
|
21
27
|
} catch (error) {
|
|
@@ -27,7 +33,7 @@ async function toCrash(error) {
|
|
|
27
33
|
isCrashing = true;
|
|
28
34
|
process.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
29
35
|
try {
|
|
30
|
-
const { toReported } = await import("./program-
|
|
36
|
+
const { toReported } = await import("./program-BZW_50HP.js");
|
|
31
37
|
await toReported(error, false, void 0, "fatal");
|
|
32
38
|
} finally {
|
|
33
39
|
process.exit(1);
|
|
@@ -45,4 +51,4 @@ async function toQuickAnswer(args) {
|
|
|
45
51
|
export {};
|
|
46
52
|
|
|
47
53
|
//# sourceMappingURL=cli.js.map
|
|
48
|
-
//# debugId=
|
|
54
|
+
//# debugId=29560915-e7ae-5a74-b5f6-f4a6988e1b43
|
|
@@ -1,10 +1,10 @@
|
|
|
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]="5c189832-33ce-5032-b0e0-fc3a33cf5b1d")}catch(e){}}();
|
|
4
4
|
import { a as defineCommand, i as GLOBAL_FLAGS, r as ExitCode, t as toInvocation } from "./scrub-VtQmxap1.js";
|
|
5
5
|
import { n as toCompletion, t as toAnswer$1 } from "./completion-tree-rR_Rmeij.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
|
-
import { Command, Option } from "commander";
|
|
7
|
+
import { Command, CommanderError, Option } from "commander";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { accessSync, chmodSync, closeSync, constants, existsSync, mkdirSync, openAsBlob, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
|
|
10
10
|
import { basename, delimiter, dirname, join, resolve } from "node:path";
|
|
@@ -144,152 +144,6 @@ function toOrigin(apiUrl) {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
//#endregion
|
|
147
|
-
//#region src/output/writer.ts
|
|
148
|
-
/**
|
|
149
|
-
* toText reads a value a caller supplied, which arrives typed as unknown. An object would
|
|
150
|
-
* otherwise reach a request as the text "[object Object]".
|
|
151
|
-
*/
|
|
152
|
-
function toText(value) {
|
|
153
|
-
if (typeof value === "string") return value;
|
|
154
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
155
|
-
if (value === void 0 || value === null) return "";
|
|
156
|
-
return JSON.stringify(value);
|
|
157
|
-
}
|
|
158
|
-
/** writeData prints a command's result on stdout. */
|
|
159
|
-
function writeData(value) {
|
|
160
|
-
if (typeof value === "string") {
|
|
161
|
-
process.stdout.write(value.endsWith("\n") ? value : `${value}\n`);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
process.stdout.write(`${JSON.stringify(value ?? null, null, 2)}\n`);
|
|
165
|
-
}
|
|
166
|
-
/** writeFailure prints why a command failed on stderr, as text or as JSON. */
|
|
167
|
-
function writeFailure(message, isJSON, errors, requestId, status) {
|
|
168
|
-
if (!isJSON) {
|
|
169
|
-
process.stderr.write(`error: ${message}${status === void 0 ? "" : ` (HTTP ${status})`}\n`);
|
|
170
|
-
for (const entry of errors?.slice(1) ?? []) process.stderr.write(` ${entry.error} (${entry.statusCode})\n`);
|
|
171
|
-
if (requestId) process.stderr.write(` request ${requestId}\n`);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const payload = {
|
|
175
|
-
errors: errors ?? [{
|
|
176
|
-
error: message,
|
|
177
|
-
statusCode: 0
|
|
178
|
-
}],
|
|
179
|
-
requestId: requestId ?? null,
|
|
180
|
-
status: status ?? null
|
|
181
|
-
};
|
|
182
|
-
process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
183
|
-
}
|
|
184
|
-
/** isJSONOutput decides whether this invocation prints machine-readable output. */
|
|
185
|
-
function isJSONOutput(flags) {
|
|
186
|
-
if (flags["json"] === true) return true;
|
|
187
|
-
return !process.stdout.isTTY;
|
|
188
|
-
}
|
|
189
|
-
/** version is what this build reports, read from the published package manifest. */
|
|
190
|
-
const version$1 = createRequire(import.meta.url)("../package.json").version;
|
|
191
|
-
//#endregion
|
|
192
|
-
//#region src/command/agent-guide.ts
|
|
193
|
-
const agentGuideCommand = defineCommand({
|
|
194
|
-
name: "agent-guide",
|
|
195
|
-
summary: "Print instructions an agent can follow to use this CLI",
|
|
196
|
-
description: "Writes Markdown describing every command, its flags, and its exit codes. Save it as a skill file, an AGENTS.md section, or paste it into a system prompt.",
|
|
197
|
-
arguments: [],
|
|
198
|
-
flags: [{
|
|
199
|
-
name: "json",
|
|
200
|
-
description: "Print the command registry as JSON instead of Markdown",
|
|
201
|
-
schema: z.boolean().optional()
|
|
202
|
-
}],
|
|
203
|
-
examples: [{
|
|
204
|
-
description: "Write a skill file",
|
|
205
|
-
command: "hardfin agent-guide > SKILL.md"
|
|
206
|
-
}, {
|
|
207
|
-
description: "Inspect the registry",
|
|
208
|
-
command: "hardfin agent-guide --json"
|
|
209
|
-
}],
|
|
210
|
-
run: runAgentGuide
|
|
211
|
-
});
|
|
212
|
-
/** toGuide renders the registry as the Markdown an agent reads. */
|
|
213
|
-
function toGuide(commands, version) {
|
|
214
|
-
const lines = [
|
|
215
|
-
"# Hardfin CLI",
|
|
216
|
-
"",
|
|
217
|
-
`The \`hardfin\` command calls the Hardfin API. This guide describes version ${version} of the CLI, which speaks API version ${API_VERSION}.`,
|
|
218
|
-
"",
|
|
219
|
-
"## Before calling",
|
|
220
|
-
"",
|
|
221
|
-
"- Authenticate by setting `HARDFIN_API_KEY`, or by running `hardfin login`",
|
|
222
|
-
"- Every command prints JSON on stdout and diagnostics on stderr",
|
|
223
|
-
"- Output is JSON whenever stdout is not a terminal, so no flag is needed when calling from code",
|
|
224
|
-
"",
|
|
225
|
-
"## Exit codes",
|
|
226
|
-
"",
|
|
227
|
-
"| Code | Means |",
|
|
228
|
-
"| --- | --- |",
|
|
229
|
-
"| 0 | The command succeeded |",
|
|
230
|
-
"| 1 | The command failed |",
|
|
231
|
-
"| 2 | The command was called wrongly |",
|
|
232
|
-
"| 4 | No usable credential |",
|
|
233
|
-
"| 130 | The person pressed Ctrl-C |",
|
|
234
|
-
"",
|
|
235
|
-
"## Commands",
|
|
236
|
-
""
|
|
237
|
-
];
|
|
238
|
-
for (const command of commands.filter((command) => !command.hidden)) lines.push(...toCommandLines(command, []));
|
|
239
|
-
return lines.join("\n");
|
|
240
|
-
}
|
|
241
|
-
/** toCommandLines describes one command and everything nested under it. */
|
|
242
|
-
function toCommandLines(command, parents) {
|
|
243
|
-
const path = [...parents, command.name];
|
|
244
|
-
const lines = [
|
|
245
|
-
`### \`hardfin ${path.join(" ")}\``,
|
|
246
|
-
"",
|
|
247
|
-
command.description ?? command.summary,
|
|
248
|
-
""
|
|
249
|
-
];
|
|
250
|
-
if (command.arguments.length > 0) {
|
|
251
|
-
lines.push("| Argument | Required | Holds |", "| --- | --- | --- |");
|
|
252
|
-
for (const argument of command.arguments) lines.push(`| \`${argument.name}\` | ${argument.required ? "yes" : "no"} | ${argument.description} |`);
|
|
253
|
-
lines.push("");
|
|
254
|
-
}
|
|
255
|
-
if (command.flags.length > 0) {
|
|
256
|
-
lines.push("| Flag | Takes | Does |", "| --- | --- | --- |");
|
|
257
|
-
for (const flag of command.flags) {
|
|
258
|
-
const name = flag.short ? `-${flag.short}, --${flag.name}` : `--${flag.name}`;
|
|
259
|
-
lines.push(`| \`${name}\` | ${flag.valueName ?? "nothing"} | ${flag.description} |`);
|
|
260
|
-
}
|
|
261
|
-
lines.push("");
|
|
262
|
-
}
|
|
263
|
-
for (const example of command.examples) lines.push(`${example.description}:`, "", "```sh", example.command, "```", "");
|
|
264
|
-
for (const subcommand of command.subcommands ?? []) lines.push(...toCommandLines(subcommand, path));
|
|
265
|
-
return lines;
|
|
266
|
-
}
|
|
267
|
-
async function runAgentGuide(input) {
|
|
268
|
-
if (input.flags["json"] === true) {
|
|
269
|
-
writeData(input.commands.map(toSummary));
|
|
270
|
-
return ExitCode.OK;
|
|
271
|
-
}
|
|
272
|
-
writeData(toGuide(input.commands, version$1));
|
|
273
|
-
return ExitCode.OK;
|
|
274
|
-
}
|
|
275
|
-
function toSummary(command) {
|
|
276
|
-
return {
|
|
277
|
-
name: command.name,
|
|
278
|
-
summary: command.summary,
|
|
279
|
-
description: command.description ?? command.summary,
|
|
280
|
-
arguments: command.arguments,
|
|
281
|
-
flags: command.flags.map((flag) => ({
|
|
282
|
-
name: flag.name,
|
|
283
|
-
short: flag.short ?? null,
|
|
284
|
-
description: flag.description,
|
|
285
|
-
valueName: flag.valueName ?? null,
|
|
286
|
-
repeatable: flag.repeatable ?? false
|
|
287
|
-
})),
|
|
288
|
-
examples: command.examples,
|
|
289
|
-
subcommands: command.subcommands?.filter((entry) => !entry.hidden).map(toSummary)
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
//#endregion
|
|
293
147
|
//#region src/auth/jwt.ts
|
|
294
148
|
/**
|
|
295
149
|
* toClaims reads an access token's payload for display. Nothing here verifies the
|
|
@@ -620,6 +474,51 @@ function keep(issuer, document) {
|
|
|
620
474
|
} catch {}
|
|
621
475
|
}
|
|
622
476
|
//#endregion
|
|
477
|
+
//#region src/output/writer.ts
|
|
478
|
+
/**
|
|
479
|
+
* toText reads a value a caller supplied, which arrives typed as unknown. An object would
|
|
480
|
+
* otherwise reach a request as the text "[object Object]".
|
|
481
|
+
*/
|
|
482
|
+
function toText(value) {
|
|
483
|
+
if (typeof value === "string") return value;
|
|
484
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
485
|
+
if (value === void 0 || value === null) return "";
|
|
486
|
+
return JSON.stringify(value);
|
|
487
|
+
}
|
|
488
|
+
/** writeData prints a command's result on stdout. */
|
|
489
|
+
function writeData(value) {
|
|
490
|
+
if (typeof value === "string") {
|
|
491
|
+
process.stdout.write(value.endsWith("\n") ? value : `${value}\n`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
process.stdout.write(`${JSON.stringify(value ?? null, null, 2)}\n`);
|
|
495
|
+
}
|
|
496
|
+
/** writeFailure prints why a command failed on stderr, with a code only a machine reading JSON sees. */
|
|
497
|
+
function writeFailure(code, message, isJSON, errors, requestId, status) {
|
|
498
|
+
if (!isJSON) {
|
|
499
|
+
process.stderr.write(`error: ${message}${status === void 0 ? "" : ` (HTTP ${status})`}\n`);
|
|
500
|
+
for (const entry of errors?.slice(1) ?? []) process.stderr.write(` ${entry.error} (${entry.statusCode})\n`);
|
|
501
|
+
if (requestId) process.stderr.write(` request ${requestId}\n`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const payload = {
|
|
505
|
+
code,
|
|
506
|
+
message,
|
|
507
|
+
errors: errors ?? [{
|
|
508
|
+
error: message,
|
|
509
|
+
statusCode: 0
|
|
510
|
+
}],
|
|
511
|
+
requestId: requestId ?? null,
|
|
512
|
+
status: status ?? null
|
|
513
|
+
};
|
|
514
|
+
process.stderr.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
515
|
+
}
|
|
516
|
+
/** isJSONOutput decides whether this invocation prints machine-readable output. */
|
|
517
|
+
function isJSONOutput(flags) {
|
|
518
|
+
if (flags["json"] === true) return true;
|
|
519
|
+
return !process.stdout.isTTY;
|
|
520
|
+
}
|
|
521
|
+
//#endregion
|
|
623
522
|
//#region src/auth/grant.ts
|
|
624
523
|
const TokenResponse = z.looseObject({
|
|
625
524
|
access_token: z.string(),
|
|
@@ -980,6 +879,172 @@ function toStatusMessage(status) {
|
|
|
980
879
|
if (status === 403) return "the organization is deactivated, or its access has ended";
|
|
981
880
|
return `the request failed with status ${status}`;
|
|
982
881
|
}
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region src/output/failure.ts
|
|
884
|
+
/** FAILURE_CODES is every code a failure can carry, and none is ever renamed or given a new meaning. */
|
|
885
|
+
const FAILURE_CODES = {
|
|
886
|
+
unknown_command: "No command has that name. Run hardfin --help for the list",
|
|
887
|
+
invalid_arguments: "The command was given the wrong positional arguments",
|
|
888
|
+
missing_flag: "A flag the command needs was not given",
|
|
889
|
+
invalid_flag: "A flag was given a value it does not take",
|
|
890
|
+
invalid_input: "A file the command was given cannot be read, or does not hold JSON",
|
|
891
|
+
empty_request: "The command would change nothing, because no field was given",
|
|
892
|
+
output_required: "The answer is a file, and needs --output or a pipe",
|
|
893
|
+
not_confirmed: "The change was not confirmed. Pass --yes to confirm it without a terminal",
|
|
894
|
+
shell_unavailable: "The shell did not say where its startup file is",
|
|
895
|
+
invalid_config: "A config file or setting cannot be read",
|
|
896
|
+
not_signed_in: "No usable credential. Run hardfin login, or set HARDFIN_API_KEY",
|
|
897
|
+
sign_in_failed: "Signing in did not finish",
|
|
898
|
+
unreachable: "The CLI could not connect to the server",
|
|
899
|
+
timed_out: "Something the CLI waited on took too long",
|
|
900
|
+
busy: "Another hardfin command is holding the credential lock",
|
|
901
|
+
bad_request: "The API refused the request as written (HTTP 400)",
|
|
902
|
+
unauthorized: "The API did not accept the credential (HTTP 401)",
|
|
903
|
+
forbidden: "The credential may not do this (HTTP 403)",
|
|
904
|
+
not_found: "The record or path does not exist (HTTP 404)",
|
|
905
|
+
conflict: "The request conflicts with the record's current state (HTTP 409)",
|
|
906
|
+
too_large: "The request is larger than the API accepts (HTTP 413)",
|
|
907
|
+
invalid_request: "The API could not process the request (HTTP 422)",
|
|
908
|
+
rate_limited: "Too many requests. Wait, then try again (HTTP 429)",
|
|
909
|
+
server_error: "The API failed to answer (HTTP 5xx)",
|
|
910
|
+
api_error: "The API refused the request with another status",
|
|
911
|
+
internal_error: "A problem in hardfin itself, which has been reported"
|
|
912
|
+
};
|
|
913
|
+
/** toStatusCode names a refusal by the HTTP status the API answered with. */
|
|
914
|
+
function toStatusCode(status) {
|
|
915
|
+
switch (status) {
|
|
916
|
+
case 400: return "bad_request";
|
|
917
|
+
case 401: return "unauthorized";
|
|
918
|
+
case 403: return "forbidden";
|
|
919
|
+
case 404: return "not_found";
|
|
920
|
+
case 409: return "conflict";
|
|
921
|
+
case 413: return "too_large";
|
|
922
|
+
case 422: return "invalid_request";
|
|
923
|
+
case 429: return "rate_limited";
|
|
924
|
+
default: return status >= 500 ? "server_error" : "api_error";
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
/** toErrorCode names a failure nothing handled closer to where it happened. */
|
|
928
|
+
function toErrorCode(error) {
|
|
929
|
+
if (error instanceof RequestFailure) return toStatusCode(error.status);
|
|
930
|
+
if (error instanceof NoCredential) return "not_signed_in";
|
|
931
|
+
if (error instanceof ConfigFailure) return "invalid_config";
|
|
932
|
+
if (error instanceof Unreachable) return "unreachable";
|
|
933
|
+
if (error instanceof TimedOut) return "timed_out";
|
|
934
|
+
if (error instanceof LockHeld) return "busy";
|
|
935
|
+
return "internal_error";
|
|
936
|
+
}
|
|
937
|
+
/** version is what this build reports, read from the published package manifest. */
|
|
938
|
+
const version$1 = createRequire(import.meta.url)("../package.json").version;
|
|
939
|
+
//#endregion
|
|
940
|
+
//#region src/command/agent-guide.ts
|
|
941
|
+
const agentGuideCommand = defineCommand({
|
|
942
|
+
name: "agent-guide",
|
|
943
|
+
summary: "Print instructions an agent can follow to use this CLI",
|
|
944
|
+
description: "Writes Markdown describing every command, its flags, and its exit codes. Save it as a skill file, an AGENTS.md section, or paste it into a system prompt.",
|
|
945
|
+
arguments: [],
|
|
946
|
+
flags: [{
|
|
947
|
+
name: "json",
|
|
948
|
+
description: "Print the command registry as JSON instead of Markdown",
|
|
949
|
+
schema: z.boolean().optional()
|
|
950
|
+
}],
|
|
951
|
+
examples: [{
|
|
952
|
+
description: "Write a skill file",
|
|
953
|
+
command: "hardfin agent-guide > SKILL.md"
|
|
954
|
+
}, {
|
|
955
|
+
description: "Inspect the registry",
|
|
956
|
+
command: "hardfin agent-guide --json"
|
|
957
|
+
}],
|
|
958
|
+
run: runAgentGuide
|
|
959
|
+
});
|
|
960
|
+
/** toGuide renders the registry as the Markdown an agent reads. */
|
|
961
|
+
function toGuide(commands, version) {
|
|
962
|
+
const lines = [
|
|
963
|
+
"# Hardfin CLI",
|
|
964
|
+
"",
|
|
965
|
+
`The \`hardfin\` command calls the Hardfin API. This guide describes version ${version} of the CLI, which speaks API version ${API_VERSION}.`,
|
|
966
|
+
"",
|
|
967
|
+
"## Before calling",
|
|
968
|
+
"",
|
|
969
|
+
"- Authenticate by setting `HARDFIN_API_KEY`, or by running `hardfin login`",
|
|
970
|
+
"- Every command prints JSON on stdout and diagnostics on stderr",
|
|
971
|
+
"- Output is JSON whenever stdout is not a terminal, so no flag is needed when calling from code",
|
|
972
|
+
"",
|
|
973
|
+
"## Exit codes",
|
|
974
|
+
"",
|
|
975
|
+
"| Code | Means |",
|
|
976
|
+
"| --- | --- |",
|
|
977
|
+
"| 0 | The command succeeded |",
|
|
978
|
+
"| 1 | The command failed |",
|
|
979
|
+
"| 2 | The command was called wrongly |",
|
|
980
|
+
"| 4 | No usable credential |",
|
|
981
|
+
"| 130 | The person pressed Ctrl-C |",
|
|
982
|
+
"",
|
|
983
|
+
"## Error codes",
|
|
984
|
+
"",
|
|
985
|
+
"A failed command prints JSON on stderr with a `code`, a `message`, the API's `errors`, its `requestId` and the HTTP `status`. Branch on `code`, which never changes meaning. The message is for a person.",
|
|
986
|
+
"",
|
|
987
|
+
"| Code | Means |",
|
|
988
|
+
"| --- | --- |",
|
|
989
|
+
...Object.entries(FAILURE_CODES).map(([code, meaning]) => `| \`${code}\` | ${meaning} |`),
|
|
990
|
+
"",
|
|
991
|
+
"## Commands",
|
|
992
|
+
""
|
|
993
|
+
];
|
|
994
|
+
for (const command of commands.filter((command) => !command.hidden)) lines.push(...toCommandLines(command, []));
|
|
995
|
+
return lines.join("\n");
|
|
996
|
+
}
|
|
997
|
+
/** toCommandLines describes one command and everything nested under it. */
|
|
998
|
+
function toCommandLines(command, parents) {
|
|
999
|
+
const path = [...parents, command.name];
|
|
1000
|
+
const lines = [
|
|
1001
|
+
`### \`hardfin ${path.join(" ")}\``,
|
|
1002
|
+
"",
|
|
1003
|
+
command.description ?? command.summary,
|
|
1004
|
+
""
|
|
1005
|
+
];
|
|
1006
|
+
if (command.arguments.length > 0) {
|
|
1007
|
+
lines.push("| Argument | Required | Holds |", "| --- | --- | --- |");
|
|
1008
|
+
for (const argument of command.arguments) lines.push(`| \`${argument.name}\` | ${argument.required ? "yes" : "no"} | ${argument.description} |`);
|
|
1009
|
+
lines.push("");
|
|
1010
|
+
}
|
|
1011
|
+
if (command.flags.length > 0) {
|
|
1012
|
+
lines.push("| Flag | Takes | Does |", "| --- | --- | --- |");
|
|
1013
|
+
for (const flag of command.flags) {
|
|
1014
|
+
const name = flag.short ? `-${flag.short}, --${flag.name}` : `--${flag.name}`;
|
|
1015
|
+
lines.push(`| \`${name}\` | ${flag.valueName ?? "nothing"} | ${flag.description} |`);
|
|
1016
|
+
}
|
|
1017
|
+
lines.push("");
|
|
1018
|
+
}
|
|
1019
|
+
for (const example of command.examples) lines.push(`${example.description}:`, "", "```sh", example.command, "```", "");
|
|
1020
|
+
for (const subcommand of command.subcommands ?? []) lines.push(...toCommandLines(subcommand, path));
|
|
1021
|
+
return lines;
|
|
1022
|
+
}
|
|
1023
|
+
async function runAgentGuide(input) {
|
|
1024
|
+
if (input.flags["json"] === true) {
|
|
1025
|
+
writeData(input.commands.map(toSummary));
|
|
1026
|
+
return ExitCode.OK;
|
|
1027
|
+
}
|
|
1028
|
+
writeData(toGuide(input.commands, version$1));
|
|
1029
|
+
return ExitCode.OK;
|
|
1030
|
+
}
|
|
1031
|
+
function toSummary(command) {
|
|
1032
|
+
return {
|
|
1033
|
+
name: command.name,
|
|
1034
|
+
summary: command.summary,
|
|
1035
|
+
description: command.description ?? command.summary,
|
|
1036
|
+
arguments: command.arguments,
|
|
1037
|
+
flags: command.flags.map((flag) => ({
|
|
1038
|
+
name: flag.name,
|
|
1039
|
+
short: flag.short ?? null,
|
|
1040
|
+
description: flag.description,
|
|
1041
|
+
valueName: flag.valueName ?? null,
|
|
1042
|
+
repeatable: flag.repeatable ?? false
|
|
1043
|
+
})),
|
|
1044
|
+
examples: command.examples,
|
|
1045
|
+
subcommands: command.subcommands?.filter((entry) => !entry.hidden).map(toSummary)
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
983
1048
|
const apiCommand = defineCommand({
|
|
984
1049
|
name: "api",
|
|
985
1050
|
summary: "Call a Hardfin API endpoint and print what it answers",
|
|
@@ -1043,23 +1108,23 @@ const apiCommand = defineCommand({
|
|
|
1043
1108
|
async function runApi(input) {
|
|
1044
1109
|
const path = input.args[0];
|
|
1045
1110
|
if (!path) {
|
|
1046
|
-
writeFailure("a path is required, such as /customer", input.isJSON);
|
|
1111
|
+
writeFailure("invalid_arguments", "a path is required, such as /customer", input.isJSON);
|
|
1047
1112
|
return ExitCode.USAGE;
|
|
1048
1113
|
}
|
|
1049
1114
|
if (path.split("/").includes("..")) {
|
|
1050
|
-
writeFailure("a path stays inside the API, so it cannot contain ..", input.isJSON);
|
|
1115
|
+
writeFailure("invalid_arguments", "a path stays inside the API, so it cannot contain ..", input.isJSON);
|
|
1051
1116
|
return ExitCode.USAGE;
|
|
1052
1117
|
}
|
|
1053
1118
|
const query = toQuery$1(input.flags["field"]);
|
|
1054
1119
|
if (query === void 0) {
|
|
1055
|
-
writeFailure("each --field is key=value, such as -f limit=50", input.isJSON);
|
|
1120
|
+
writeFailure("invalid_flag", "each --field is key=value, such as -f limit=50", input.isJSON);
|
|
1056
1121
|
return ExitCode.USAGE;
|
|
1057
1122
|
}
|
|
1058
1123
|
let body;
|
|
1059
1124
|
if (typeof input.flags["input"] === "string") {
|
|
1060
1125
|
body = toBody$1(input.flags["input"]);
|
|
1061
1126
|
if (body === void 0) {
|
|
1062
|
-
writeFailure(`${input.flags["input"]} does not hold JSON`, input.isJSON);
|
|
1127
|
+
writeFailure("invalid_input", `${input.flags["input"]} does not hold JSON`, input.isJSON);
|
|
1063
1128
|
return ExitCode.USAGE;
|
|
1064
1129
|
}
|
|
1065
1130
|
}
|
|
@@ -1075,11 +1140,11 @@ async function runApi(input) {
|
|
|
1075
1140
|
return ExitCode.OK;
|
|
1076
1141
|
} catch (error) {
|
|
1077
1142
|
if (error instanceof NoCredential) {
|
|
1078
|
-
writeFailure(error.message, input.isJSON);
|
|
1143
|
+
writeFailure("not_signed_in", error.message, input.isJSON);
|
|
1079
1144
|
return ExitCode.NOT_AUTHENTICATED;
|
|
1080
1145
|
}
|
|
1081
1146
|
if (error instanceof RequestFailure) {
|
|
1082
|
-
writeFailure(error.message, input.isJSON, error.errors, error.requestId, error.status);
|
|
1147
|
+
writeFailure(toStatusCode(error.status), error.message, input.isJSON, error.errors, error.requestId, error.status);
|
|
1083
1148
|
return error.status === 401 ? ExitCode.NOT_AUTHENTICATED : ExitCode.ERROR;
|
|
1084
1149
|
}
|
|
1085
1150
|
throw error;
|
|
@@ -1485,17 +1550,17 @@ async function runChange(input, change) {
|
|
|
1485
1550
|
const place = toPlace();
|
|
1486
1551
|
const shell = input.args[0] ?? toShell(place);
|
|
1487
1552
|
if (!shell || !SHELLS.includes(shell)) {
|
|
1488
|
-
writeFailure(`name the shell to ${change} for: ${SHELLS.join(", ")}`, input.isJSON);
|
|
1553
|
+
writeFailure("invalid_arguments", `name the shell to ${change} for: ${SHELLS.join(", ")}`, input.isJSON);
|
|
1489
1554
|
return ExitCode.USAGE;
|
|
1490
1555
|
}
|
|
1491
1556
|
const plan = toPlan(shell, place);
|
|
1492
1557
|
if (change === "install" && shell === "powershell" && !plan.startup) {
|
|
1493
|
-
writeFailure("PowerShell did not report its profile, so there is nowhere to load the script from", input.isJSON);
|
|
1558
|
+
writeFailure("shell_unavailable", "PowerShell did not report its profile, so there is nowhere to load the script from", input.isJSON);
|
|
1494
1559
|
return ExitCode.ERROR;
|
|
1495
1560
|
}
|
|
1496
1561
|
process.stderr.write(toDescription(plan, change));
|
|
1497
1562
|
if (!await isConfirmed(input.flags["yes"] === true)) {
|
|
1498
|
-
writeFailure(process.stdin.isTTY ? "nothing was changed" : "nothing was changed. Pass --yes to change it without a terminal", input.isJSON);
|
|
1563
|
+
writeFailure("not_confirmed", process.stdin.isTTY ? "nothing was changed" : "nothing was changed. Pass --yes to change it without a terminal", input.isJSON);
|
|
1499
1564
|
return ExitCode.USAGE;
|
|
1500
1565
|
}
|
|
1501
1566
|
if (change === "install") {
|
|
@@ -1658,7 +1723,7 @@ function toSchemaValues(schema) {
|
|
|
1658
1723
|
async function runCompletion(input) {
|
|
1659
1724
|
const shell = input.args[0] ?? "";
|
|
1660
1725
|
if (!SHELLS.includes(shell)) {
|
|
1661
|
-
writeFailure(`${shell || "no shell"} is not one this CLI writes for. Choose ${SHELLS.join(", ")}`, input.isJSON);
|
|
1726
|
+
writeFailure("invalid_arguments", `${shell || "no shell"} is not one this CLI writes for. Choose ${SHELLS.join(", ")}`, input.isJSON);
|
|
1662
1727
|
return ExitCode.USAGE;
|
|
1663
1728
|
}
|
|
1664
1729
|
writeData(toScript(shell));
|
|
@@ -2064,25 +2129,25 @@ async function runLogin(input) {
|
|
|
2064
2129
|
}
|
|
2065
2130
|
process.stderr.write(callback.error ? "\n" : "\nApproved, finishing the sign in\n");
|
|
2066
2131
|
if (callback.error) {
|
|
2067
|
-
writeFailure(`sign in was refused: ${callback.error}${callback.errorDescription ? `, ${callback.errorDescription}` : ""}`, input.isJSON);
|
|
2132
|
+
writeFailure("sign_in_failed", `sign in was refused: ${callback.error}${callback.errorDescription ? `, ${callback.errorDescription}` : ""}`, input.isJSON);
|
|
2068
2133
|
return ExitCode.ERROR;
|
|
2069
2134
|
}
|
|
2070
2135
|
if (callback.source === "listener" ? callback.state !== state : callback.state !== void 0 && callback.state !== state) {
|
|
2071
|
-
writeFailure("the browser came back with a state this sign in did not send", input.isJSON);
|
|
2136
|
+
writeFailure("sign_in_failed", "the browser came back with a state this sign in did not send", input.isJSON);
|
|
2072
2137
|
return ExitCode.ERROR;
|
|
2073
2138
|
}
|
|
2074
2139
|
if (callback.issuer !== void 0 && callback.issuer.replace(/\/+$/, "") !== metadata.issuer.replace(/\/+$/, "")) {
|
|
2075
|
-
writeFailure(`the browser came back from ${callback.issuer}, which is not ${metadata.issuer}`, input.isJSON);
|
|
2140
|
+
writeFailure("sign_in_failed", `the browser came back from ${callback.issuer}, which is not ${metadata.issuer}`, input.isJSON);
|
|
2076
2141
|
return ExitCode.ERROR;
|
|
2077
2142
|
}
|
|
2078
2143
|
if (!callback.code) {
|
|
2079
|
-
writeFailure("the browser came back without an authorization code", input.isJSON);
|
|
2144
|
+
writeFailure("sign_in_failed", "the browser came back without an authorization code", input.isJSON);
|
|
2080
2145
|
return ExitCode.ERROR;
|
|
2081
2146
|
}
|
|
2082
2147
|
return await toSignedIn(input, metadata, await toTokensFromCode(metadata.token_endpoint, clientId, callback.code, listener.redirectUri, pkce));
|
|
2083
2148
|
} catch (error) {
|
|
2084
2149
|
if (error instanceof DiscoveryFailure || error instanceof GrantFailure) {
|
|
2085
|
-
writeFailure(error.message, input.isJSON);
|
|
2150
|
+
writeFailure("sign_in_failed", error.message, input.isJSON);
|
|
2086
2151
|
return ExitCode.ERROR;
|
|
2087
2152
|
}
|
|
2088
2153
|
throw error;
|
|
@@ -2092,7 +2157,7 @@ async function runLogin(input) {
|
|
|
2092
2157
|
async function runDeviceLogin(input, metadata, scope) {
|
|
2093
2158
|
const settings = input.resolved.settings;
|
|
2094
2159
|
if (!metadata.device_authorization_endpoint) {
|
|
2095
|
-
writeFailure(`${metadata.issuer} does not offer the device grant, so sign in without --device`, input.isJSON);
|
|
2160
|
+
writeFailure("sign_in_failed", `${metadata.issuer} does not offer the device grant, so sign in without --device`, input.isJSON);
|
|
2096
2161
|
return ExitCode.ERROR;
|
|
2097
2162
|
}
|
|
2098
2163
|
const device = await toDeviceAuthorization(metadata.device_authorization_endpoint, settings.clientId, scope);
|
|
@@ -2126,7 +2191,7 @@ async function toDeviceTokens(approved, typed) {
|
|
|
2126
2191
|
async function toSignedIn(input, metadata, tokens) {
|
|
2127
2192
|
const refreshToken = tokens.refreshToken;
|
|
2128
2193
|
if (!refreshToken) {
|
|
2129
|
-
writeFailure("the authorization server issued no refresh token, so this sign in cannot be kept", input.isJSON);
|
|
2194
|
+
writeFailure("sign_in_failed", "the authorization server issued no refresh token, so this sign in cannot be kept", input.isJSON);
|
|
2130
2195
|
return ExitCode.ERROR;
|
|
2131
2196
|
}
|
|
2132
2197
|
const backend = await withLock(() => keep$1(input.resolved.settings.issuerUrl, refreshToken, tokens.refreshExpiresAt));
|
|
@@ -2227,7 +2292,7 @@ function toTelemetry(env = process.env, isCheckout = isRunFromCheckout()) {
|
|
|
2227
2292
|
const base = {
|
|
2228
2293
|
dsn,
|
|
2229
2294
|
environment: isCheckout ? "local" : version$1.includes("-dev.") ? "preview" : "production",
|
|
2230
|
-
release:
|
|
2295
|
+
release: toRelease(version$1)
|
|
2231
2296
|
};
|
|
2232
2297
|
if (env["DO_NOT_TRACK"] && !OFF.has(env["DO_NOT_TRACK"].toLowerCase())) return {
|
|
2233
2298
|
...base,
|
|
@@ -2275,6 +2340,13 @@ function isDsn(dsn) {
|
|
|
2275
2340
|
return false;
|
|
2276
2341
|
}
|
|
2277
2342
|
}
|
|
2343
|
+
/**
|
|
2344
|
+
* toRelease names a version as Sentry knows it. Sentry refuses a slash in a release name, so
|
|
2345
|
+
* the scoped package name @hardfin/cli cannot be used, and hardfin-cli stands in for it.
|
|
2346
|
+
*/
|
|
2347
|
+
function toRelease(of) {
|
|
2348
|
+
return `hardfin-cli@${of}`;
|
|
2349
|
+
}
|
|
2278
2350
|
//#endregion
|
|
2279
2351
|
//#region src/system/host.ts
|
|
2280
2352
|
const BYTES_PER_GB = 1024 ** 3;
|
|
@@ -3052,28 +3124,28 @@ function defineOperation(operation) {
|
|
|
3052
3124
|
async function runOperation(operation, input) {
|
|
3053
3125
|
const path = toPath(operation, input.args);
|
|
3054
3126
|
if (path === void 0) {
|
|
3055
|
-
writeFailure(`this command takes ${operation.pathParameters.length} argument(s)`, input.isJSON);
|
|
3127
|
+
writeFailure("invalid_arguments", `this command takes ${operation.pathParameters.length} argument(s)`, input.isJSON);
|
|
3056
3128
|
return ExitCode.USAGE;
|
|
3057
3129
|
}
|
|
3058
3130
|
const missing = operation.bodyFlags.concat(operation.upload?.fields ?? []).filter((flag) => flag.required && input.flags[toOptionKey(flag.name)] === void 0);
|
|
3059
3131
|
if (missing.length > 0) {
|
|
3060
|
-
writeFailure(`this command needs ${missing.map((flag) => `--${flag.name}`).join(", ")}`, input.isJSON);
|
|
3132
|
+
writeFailure("missing_flag", `this command needs ${missing.map((flag) => `--${flag.name}`).join(", ")}`, input.isJSON);
|
|
3061
3133
|
return ExitCode.USAGE;
|
|
3062
3134
|
}
|
|
3063
3135
|
const body = toBody(operation, input.flags);
|
|
3064
3136
|
if (body instanceof Error) {
|
|
3065
|
-
writeFailure(body.message, input.isJSON);
|
|
3137
|
+
writeFailure("invalid_flag", body.message, input.isJSON);
|
|
3066
3138
|
return ExitCode.USAGE;
|
|
3067
3139
|
}
|
|
3068
3140
|
if (operation.bodyFlags.length > 0 && body === void 0) {
|
|
3069
|
-
writeFailure(`this command changes nothing unless a flag is given, such as ${operation.bodyFlags.slice(0, 3).map((flag) => `--${flag.name}`).join(", ")}. Run it with --help for the rest`, input.isJSON);
|
|
3141
|
+
writeFailure("empty_request", `this command changes nothing unless a flag is given, such as ${operation.bodyFlags.slice(0, 3).map((flag) => `--${flag.name}`).join(", ")}. Run it with --help for the rest`, input.isJSON);
|
|
3070
3142
|
return ExitCode.USAGE;
|
|
3071
3143
|
}
|
|
3072
3144
|
let form;
|
|
3073
3145
|
if (operation.upload) {
|
|
3074
3146
|
const built = await toForm(operation.upload, input.flags);
|
|
3075
3147
|
if (built instanceof Error) {
|
|
3076
|
-
writeFailure(built.message, input.isJSON);
|
|
3148
|
+
writeFailure("invalid_input", built.message, input.isJSON);
|
|
3077
3149
|
return ExitCode.USAGE;
|
|
3078
3150
|
}
|
|
3079
3151
|
form = built;
|
|
@@ -3094,11 +3166,11 @@ async function runOperation(operation, input) {
|
|
|
3094
3166
|
return ExitCode.OK;
|
|
3095
3167
|
} catch (error) {
|
|
3096
3168
|
if (error instanceof NoCredential) {
|
|
3097
|
-
writeFailure(error.message, input.isJSON);
|
|
3169
|
+
writeFailure("not_signed_in", error.message, input.isJSON);
|
|
3098
3170
|
return ExitCode.NOT_AUTHENTICATED;
|
|
3099
3171
|
}
|
|
3100
3172
|
if (error instanceof RequestFailure) {
|
|
3101
|
-
writeFailure(error.message, input.isJSON, error.errors, error.requestId, error.status);
|
|
3173
|
+
writeFailure(toStatusCode(error.status), error.message, input.isJSON, error.errors, error.requestId, error.status);
|
|
3102
3174
|
return error.status === 401 ? ExitCode.NOT_AUTHENTICATED : ExitCode.ERROR;
|
|
3103
3175
|
}
|
|
3104
3176
|
throw error;
|
|
@@ -3113,7 +3185,7 @@ function toWritten(download, input) {
|
|
|
3113
3185
|
const path = typeof asked === "string" ? asked : download.fileName ?? "-";
|
|
3114
3186
|
if (path === "-") {
|
|
3115
3187
|
if (process.stdout.isTTY) {
|
|
3116
|
-
writeFailure("this answer is a file, so name where to write it with --output, or send it on with a pipe", input.isJSON);
|
|
3188
|
+
writeFailure("output_required", "this answer is a file, so name where to write it with --output, or send it on with a pipe", input.isJSON);
|
|
3117
3189
|
return ExitCode.USAGE;
|
|
3118
3190
|
}
|
|
3119
3191
|
process.stdout.write(download.bytes);
|
|
@@ -5914,7 +5986,8 @@ function toRejectedFlag(command, flags) {
|
|
|
5914
5986
|
/** toCli builds the parser from the registry, which is what every surface reads. */
|
|
5915
5987
|
function toCli(entries = commands) {
|
|
5916
5988
|
const program = new Command();
|
|
5917
|
-
program.name("hardfin").description("Call the Hardfin API from a terminal or an agent").version(version$1, "-v, --version").
|
|
5989
|
+
program.name("hardfin").description("Call the Hardfin API from a terminal or an agent").version(version$1, "-v, --version").enablePositionalOptions();
|
|
5990
|
+
toQuietParser(program);
|
|
5918
5991
|
for (const flag of GLOBAL_FLAGS) program.option(`--${flag.name} <${flag.valueName}>`, flag.description);
|
|
5919
5992
|
for (const command of entries) program.addCommand(toProgram(program, command), { hidden: command.hidden });
|
|
5920
5993
|
return program;
|
|
@@ -5922,6 +5995,7 @@ function toCli(entries = commands) {
|
|
|
5922
5995
|
/** toProgram wires one registry command into the parser. */
|
|
5923
5996
|
function toProgram(root, command) {
|
|
5924
5997
|
const program = new Command(command.name).summary(command.summary).description(command.description ?? command.summary);
|
|
5998
|
+
toQuietParser(program);
|
|
5925
5999
|
for (const argument of command.arguments) {
|
|
5926
6000
|
const name = argument.variadic ? `${argument.name}...` : argument.name;
|
|
5927
6001
|
program.argument(argument.required ? `<${name}>` : `[${name}]`, argument.description);
|
|
@@ -5945,11 +6019,41 @@ function toProgram(root, command) {
|
|
|
5945
6019
|
});
|
|
5946
6020
|
return program;
|
|
5947
6021
|
}
|
|
6022
|
+
/** toQuietParser has the parser hand back its mistakes, so they are answered like any other failure. */
|
|
6023
|
+
function toQuietParser(program) {
|
|
6024
|
+
program.exitOverride().configureOutput({ outputError: () => void 0 });
|
|
6025
|
+
}
|
|
6026
|
+
/** The parser's codes for a run that only showed help or the version, which is not a failure. */
|
|
6027
|
+
const SHOWN = /* @__PURE__ */ new Set([
|
|
6028
|
+
"commander.helpDisplayed",
|
|
6029
|
+
"commander.help",
|
|
6030
|
+
"commander.version"
|
|
6031
|
+
]);
|
|
6032
|
+
/** toParseExitCode answers a mistake the parser caught before any command ran. */
|
|
6033
|
+
function toParseExitCode(error, args) {
|
|
6034
|
+
if (!(error instanceof CommanderError)) return;
|
|
6035
|
+
if (SHOWN.has(error.code)) return error.exitCode;
|
|
6036
|
+
const isJSON = args.includes("--json") || !process.stdout.isTTY;
|
|
6037
|
+
writeFailure(toParseCode(error.code), error.message.replace(/^error: /, ""), isJSON);
|
|
6038
|
+
if (!isJSON) process.stderr.write(" Run the command with --help to see what it takes\n");
|
|
6039
|
+
return ExitCode.USAGE;
|
|
6040
|
+
}
|
|
6041
|
+
function toParseCode(code) {
|
|
6042
|
+
switch (code) {
|
|
6043
|
+
case "commander.unknownCommand": return "unknown_command";
|
|
6044
|
+
case "commander.unknownOption":
|
|
6045
|
+
case "commander.optionMissingArgument":
|
|
6046
|
+
case "commander.missingMandatoryOptionValue":
|
|
6047
|
+
case "commander.conflictingOption":
|
|
6048
|
+
case "commander.invalidArgument": return "invalid_flag";
|
|
6049
|
+
default: return "invalid_arguments";
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
5948
6052
|
async function toExitCode(root, command, args, flags) {
|
|
5949
6053
|
const isJSON = isJSONOutput(flags);
|
|
5950
6054
|
const rejected = toRejectedFlag(command, flags);
|
|
5951
6055
|
if (rejected) {
|
|
5952
|
-
writeFailure(rejected, isJSON);
|
|
6056
|
+
writeFailure("invalid_flag", rejected, isJSON);
|
|
5953
6057
|
return ExitCode.USAGE;
|
|
5954
6058
|
}
|
|
5955
6059
|
let resolved;
|
|
@@ -5970,7 +6074,7 @@ async function toExitCode(root, command, args, flags) {
|
|
|
5970
6074
|
process.stderr.write("\n");
|
|
5971
6075
|
return ExitCode.INTERRUPTED;
|
|
5972
6076
|
}
|
|
5973
|
-
writeFailure(error instanceof Error ? error.message : String(error), isJSON);
|
|
6077
|
+
writeFailure(toErrorCode(error), error instanceof Error ? error.message : String(error), isJSON);
|
|
5974
6078
|
await toReported(error, isJSON, resolved);
|
|
5975
6079
|
return error instanceof ConfigFailure ? ExitCode.USAGE : ExitCode.ERROR;
|
|
5976
6080
|
}
|
|
@@ -5995,7 +6099,7 @@ async function toReported(error, isJSON, resolved, level = "error") {
|
|
|
5995
6099
|
if (id && !isJSON) process.stderr.write(`This looks like a problem in hardfin itself, and it has been reported to the Hardfin team. Reference: ${id}\n`);
|
|
5996
6100
|
}
|
|
5997
6101
|
//#endregion
|
|
5998
|
-
export { toCli, toReported, wasReportAttempted };
|
|
6102
|
+
export { toCli, toParseExitCode, toReported, wasReportAttempted };
|
|
5999
6103
|
|
|
6000
|
-
//# sourceMappingURL=program-
|
|
6001
|
-
//# debugId=
|
|
6104
|
+
//# sourceMappingURL=program-BZW_50HP.js.map
|
|
6105
|
+
//# debugId=5c189832-33ce-5032-b0e0-fc3a33cf5b1d
|