@hardfin/cli 0.1.1-dev.69 → 0.1.1-dev.70
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
CHANGED
|
@@ -199,9 +199,10 @@ A field the API does not take null for is sent the word as text. The same holds
|
|
|
199
199
|
hardfin asset accounting update ast_4f9xk2mq7plr8stz --unset useful-life
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
enum, a required field that is missing,
|
|
204
|
-
for.
|
|
202
|
+
Four things fail locally with exit code 2, before any request is made: a value outside an
|
|
203
|
+
enum, a required field that is missing, a list element missing a field every element needs,
|
|
204
|
+
and `--unset` on a field the API will not take null for. `--help` starts a required flag's
|
|
205
|
+
description with `Required.`, and names the fields every element of a repeated flag needs.
|
|
205
206
|
|
|
206
207
|
### When a command fails
|
|
207
208
|
|
|
@@ -277,16 +278,17 @@ hardfin agent-guide --json customer create | jq -r '.commands[0].flags[] | selec
|
|
|
277
278
|
The JSON holds `cli`, `apiVersion`, `exitCodes`, `errorCodes`, `globalFlags` and `commands`.
|
|
278
279
|
Each command names its `path`, its `endpoint`, whether it `runs`, and its flags.
|
|
279
280
|
|
|
280
|
-
| A flag's
|
|
281
|
-
|
|
|
282
|
-
| `required`
|
|
283
|
-
| `schema`
|
|
284
|
-
| `sends`
|
|
285
|
-
| `clearable`
|
|
286
|
-
| `repeatable`
|
|
287
|
-
| `negatable`
|
|
288
|
-
| `element`
|
|
289
|
-
| `
|
|
281
|
+
| A flag's | Holds |
|
|
282
|
+
| ----------------- | --------------------------------------------------------------------------- |
|
|
283
|
+
| `required` | Present when the command fails without it |
|
|
284
|
+
| `schema` | The `type`, and the `enum`, `format`, `minimum` or `maximum` that narrow it |
|
|
285
|
+
| `sends` | The query parameter, or the path of the body field, the value goes to |
|
|
286
|
+
| `clearable` | Present when `--unset` can clear the field |
|
|
287
|
+
| `repeatable` | Present when the flag is given once per value |
|
|
288
|
+
| `negatable` | Present when a boolean also takes a `--no-` form |
|
|
289
|
+
| `element` | The fields one value carries as `key=value`, for an array of objects |
|
|
290
|
+
| `elementRequired` | The fields of `element` every value must carry |
|
|
291
|
+
| `default` | The value used when the flag is not given |
|
|
290
292
|
|
|
291
293
|
A part that is absent means no. The description is read from the same declarations the parser
|
|
292
294
|
enforces, so it cannot say one thing while the command does another.
|
|
@@ -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]="9d637f5e-0cda-5582-80ff-432edc41bf8c")}catch(e){}}();
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -8,79 +8,6 @@ import { dirname, join, resolve, sep } from "node:path";
|
|
|
8
8
|
import { parseEnv } from "node:util";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
|
-
//#region src/output/writer.ts
|
|
12
|
-
/**
|
|
13
|
-
* toText reads a value a caller supplied, which arrives typed as unknown. An object would
|
|
14
|
-
* otherwise reach a request as the text "[object Object]".
|
|
15
|
-
*/
|
|
16
|
-
function toText(value) {
|
|
17
|
-
if (typeof value === "string") return value;
|
|
18
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
19
|
-
if (value === void 0 || value === null) return "";
|
|
20
|
-
return JSON.stringify(value);
|
|
21
|
-
}
|
|
22
|
-
/** Every control character but a newline and a tab, which are the two a line of text needs. */
|
|
23
|
-
const CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
24
|
-
/**
|
|
25
|
-
* toPrintable makes text safe to show at a terminal. A name or a message from the API can
|
|
26
|
-
* hold an escape sequence, which a terminal would act on. Each such character is written
|
|
27
|
-
* as its code, so the text reads as what it holds.
|
|
28
|
-
*/
|
|
29
|
-
function toPrintable(text) {
|
|
30
|
-
return text.replace(CONTROL, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* toJSON writes a value for whoever is reading the stream. A person at a terminal gets it
|
|
34
|
-
* indented. A program gets it on one line, because the indentation of a page of records is
|
|
35
|
-
* close to half of what an agent would otherwise have to read.
|
|
36
|
-
*/
|
|
37
|
-
function toJSON(value, stream) {
|
|
38
|
-
return stream.isTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value);
|
|
39
|
-
}
|
|
40
|
-
/** writeData prints a command's result on stdout. */
|
|
41
|
-
function writeData(value) {
|
|
42
|
-
if (typeof value === "string") {
|
|
43
|
-
const text = toPrintable(value);
|
|
44
|
-
process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
process.stdout.write(`${toJSON(value ?? null, process.stdout)}\n`);
|
|
48
|
-
}
|
|
49
|
-
/** The code of the last failure written, which decides whether the CLI checks for an update. */
|
|
50
|
-
let lastFailureCode;
|
|
51
|
-
/** toLastFailureCode answers the code of the last failure this run wrote. */
|
|
52
|
-
function toLastFailureCode() {
|
|
53
|
-
return lastFailureCode;
|
|
54
|
-
}
|
|
55
|
-
/** writeFailure prints why a command failed on stderr, with a code only a machine reading JSON sees. */
|
|
56
|
-
function writeFailure(code, message, isJSON, errors, requestId, status) {
|
|
57
|
-
lastFailureCode = code;
|
|
58
|
-
if (!isJSON) {
|
|
59
|
-
process.stderr.write(`error: ${toPrintable(message)}${status === void 0 ? "" : ` (HTTP ${status})`}\n`);
|
|
60
|
-
for (const entry of errors?.slice(1) ?? []) process.stderr.write(` ${toPrintable(entry.error)} (${entry.statusCode})\n`);
|
|
61
|
-
if (requestId) process.stderr.write(` request ${toPrintable(requestId)}\n`);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
const payload = {
|
|
65
|
-
code,
|
|
66
|
-
message,
|
|
67
|
-
errors: errors ?? [{
|
|
68
|
-
error: message,
|
|
69
|
-
statusCode: 0
|
|
70
|
-
}],
|
|
71
|
-
requestId: requestId ?? null,
|
|
72
|
-
status: status ?? null
|
|
73
|
-
};
|
|
74
|
-
process.stderr.write(`${toJSON(payload, process.stderr)}\n`);
|
|
75
|
-
}
|
|
76
|
-
/** isJSONOutput decides whether this invocation prints machine-readable output. */
|
|
77
|
-
function isJSONOutput(flags) {
|
|
78
|
-
if (flags["json"] === true) return true;
|
|
79
|
-
return !process.stdout.isTTY;
|
|
80
|
-
}
|
|
81
|
-
/** version is what this build reports, read from the published package manifest. */
|
|
82
|
-
const version$1 = createRequire(import.meta.url)("../package.json").version;
|
|
83
|
-
//#endregion
|
|
84
11
|
//#region src/report/expected.ts
|
|
85
12
|
/**
|
|
86
13
|
* ExpectedFailure is a failure the CLI anticipated: the person's input, their network, their
|
|
@@ -112,6 +39,8 @@ var TimedOut = class extends ExpectedFailure {
|
|
|
112
39
|
this.name = "TimedOut";
|
|
113
40
|
}
|
|
114
41
|
};
|
|
42
|
+
/** version is what this build reports, read from the published package manifest. */
|
|
43
|
+
const version$1 = createRequire(import.meta.url)("../package.json").version;
|
|
115
44
|
/** The values that mean no, for a variable that turns something on or off. */
|
|
116
45
|
const OFF = /* @__PURE__ */ new Set([
|
|
117
46
|
"0",
|
|
@@ -304,6 +233,77 @@ function toOrigin(apiUrl) {
|
|
|
304
233
|
}
|
|
305
234
|
}
|
|
306
235
|
//#endregion
|
|
236
|
+
//#region src/output/writer.ts
|
|
237
|
+
/**
|
|
238
|
+
* toText reads a value a caller supplied, which arrives typed as unknown. An object would
|
|
239
|
+
* otherwise reach a request as the text "[object Object]".
|
|
240
|
+
*/
|
|
241
|
+
function toText(value) {
|
|
242
|
+
if (typeof value === "string") return value;
|
|
243
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
|
|
244
|
+
if (value === void 0 || value === null) return "";
|
|
245
|
+
return JSON.stringify(value);
|
|
246
|
+
}
|
|
247
|
+
/** Every control character but a newline and a tab, which are the two a line of text needs. */
|
|
248
|
+
const CONTROL = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g;
|
|
249
|
+
/**
|
|
250
|
+
* toPrintable makes text safe to show at a terminal. A name or a message from the API can
|
|
251
|
+
* hold an escape sequence, which a terminal would act on. Each such character is written
|
|
252
|
+
* as its code, so the text reads as what it holds.
|
|
253
|
+
*/
|
|
254
|
+
function toPrintable(text) {
|
|
255
|
+
return text.replace(CONTROL, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* toJSON writes a value for whoever is reading the stream. A person at a terminal gets it
|
|
259
|
+
* indented. A program gets it on one line, because the indentation of a page of records is
|
|
260
|
+
* close to half of what an agent would otherwise have to read.
|
|
261
|
+
*/
|
|
262
|
+
function toJSON(value, stream) {
|
|
263
|
+
return stream.isTTY ? JSON.stringify(value, null, 2) : JSON.stringify(value);
|
|
264
|
+
}
|
|
265
|
+
/** writeData prints a command's result on stdout. */
|
|
266
|
+
function writeData(value) {
|
|
267
|
+
if (typeof value === "string") {
|
|
268
|
+
const text = toPrintable(value);
|
|
269
|
+
process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
process.stdout.write(`${toJSON(value ?? null, process.stdout)}\n`);
|
|
273
|
+
}
|
|
274
|
+
/** The code of the last failure written, which decides whether the CLI checks for an update. */
|
|
275
|
+
let lastFailureCode;
|
|
276
|
+
/** toLastFailureCode answers the code of the last failure this run wrote. */
|
|
277
|
+
function toLastFailureCode() {
|
|
278
|
+
return lastFailureCode;
|
|
279
|
+
}
|
|
280
|
+
/** writeFailure prints why a command failed on stderr, with a code only a machine reading JSON sees. */
|
|
281
|
+
function writeFailure(code, message, isJSON, errors, requestId, status) {
|
|
282
|
+
lastFailureCode = code;
|
|
283
|
+
if (!isJSON) {
|
|
284
|
+
process.stderr.write(`error: ${toPrintable(message)}${status === void 0 ? "" : ` (HTTP ${status})`}\n`);
|
|
285
|
+
for (const entry of errors?.slice(1) ?? []) process.stderr.write(` ${toPrintable(entry.error)} (${entry.statusCode})\n`);
|
|
286
|
+
if (requestId) process.stderr.write(` request ${toPrintable(requestId)}\n`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const payload = {
|
|
290
|
+
code,
|
|
291
|
+
message,
|
|
292
|
+
errors: errors ?? [{
|
|
293
|
+
error: message,
|
|
294
|
+
statusCode: 0
|
|
295
|
+
}],
|
|
296
|
+
requestId: requestId ?? null,
|
|
297
|
+
status: status ?? null
|
|
298
|
+
};
|
|
299
|
+
process.stderr.write(`${toJSON(payload, process.stderr)}\n`);
|
|
300
|
+
}
|
|
301
|
+
/** isJSONOutput decides whether this invocation prints machine-readable output. */
|
|
302
|
+
function isJSONOutput(flags) {
|
|
303
|
+
if (flags["json"] === true) return true;
|
|
304
|
+
return !process.stdout.isTTY;
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
307
|
//#region src/system/state.ts
|
|
308
308
|
/** toStateDirectory names the folder the CLI keeps what it remembers between runs in. */
|
|
309
309
|
function toStateDirectory() {
|
|
@@ -494,7 +494,7 @@ function writeCache(cache) {
|
|
|
494
494
|
} catch {}
|
|
495
495
|
}
|
|
496
496
|
//#endregion
|
|
497
|
-
export {
|
|
497
|
+
export { ExpectedFailure as C, TimedOut as E, version$1 as S, LockHeld as T, API_VERSION as _, isShipped as a, toSettings as b, toOutdatedTo as c, toJSON as d, toLastFailureCode as f, writeFailure as g, writeData as h, toUpdateCheckRefusal as i, toStateDirectory as l, toText as m, isReminderDue as n, toPackageDirectory as o, toPrintable as p, toCheck as r, noteNotices as s, PACKAGE as t, isJSONOutput as u, CONFIG_FILE as v, Interrupted as w, toTelemetry as x, ConfigFailure as y };
|
|
498
498
|
|
|
499
|
-
//# sourceMappingURL=check-
|
|
500
|
-
//# debugId=
|
|
499
|
+
//# sourceMappingURL=check-wgnggJ45.js.map
|
|
500
|
+
//# debugId=9d637f5e-0cda-5582-80ff-432edc41bf8c
|
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]="e43cc9b0-2707-553a-973d-9a707e329000")}catch(e){}}();
|
|
4
4
|
import module from "node:module";
|
|
5
5
|
//#region src/cli.ts
|
|
6
6
|
module.enableCompileCache?.();
|
|
@@ -14,8 +14,8 @@ 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-
|
|
18
|
-
const { startUpdateCheck, writeUpdateNotice } = await import("./notice-
|
|
17
|
+
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-CYp6XF8D.js");
|
|
18
|
+
const { startUpdateCheck, writeUpdateNotice } = await import("./notice-CQ5Nlp80.js");
|
|
19
19
|
const pending = startUpdateCheck(process.argv.slice(2));
|
|
20
20
|
try {
|
|
21
21
|
await toCli().parseAsync(process.argv);
|
|
@@ -36,7 +36,7 @@ async function toCrash(error) {
|
|
|
36
36
|
isCrashing = true;
|
|
37
37
|
const isJSON = process.argv.includes("--json") || !process.stdout.isTTY;
|
|
38
38
|
try {
|
|
39
|
-
const { toReported, writeCrash } = await import("./program-
|
|
39
|
+
const { toReported, writeCrash } = await import("./program-CYp6XF8D.js");
|
|
40
40
|
writeCrash(error, isJSON);
|
|
41
41
|
await toReported(error, isJSON, void 0, "fatal");
|
|
42
42
|
} catch {
|
|
@@ -57,4 +57,4 @@ async function toQuickAnswer(args) {
|
|
|
57
57
|
export {};
|
|
58
58
|
|
|
59
59
|
//# sourceMappingURL=cli.js.map
|
|
60
|
-
//# debugId=
|
|
60
|
+
//# debugId=e43cc9b0-2707-553a-973d-9a707e329000
|
|
@@ -1,7 +1,7 @@
|
|
|
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]="
|
|
4
|
-
import {
|
|
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]="46b26b33-c4ba-5ddd-9436-b5f91120d1da")}catch(e){}}();
|
|
4
|
+
import { _ as API_VERSION, c as toOutdatedTo, d as toJSON, f as toLastFailureCode, i as toUpdateCheckRefusal, n as isReminderDue, o as toPackageDirectory, r as toCheck, t as PACKAGE } from "./check-wgnggJ45.js";
|
|
5
5
|
//#region src/update/notice.ts
|
|
6
6
|
/**
|
|
7
7
|
* The failures a newer CLI may not have, because each can mean the API changed under this one:
|
|
@@ -130,5 +130,5 @@ function toCommandName(args) {
|
|
|
130
130
|
//#endregion
|
|
131
131
|
export { startUpdateCheck, writeUpdateNotice };
|
|
132
132
|
|
|
133
|
-
//# sourceMappingURL=notice-
|
|
134
|
-
//# debugId=
|
|
133
|
+
//# sourceMappingURL=notice-CQ5Nlp80.js.map
|
|
134
|
+
//# debugId=46b26b33-c4ba-5ddd-9436-b5f91120d1da
|
|
@@ -1,8 +1,8 @@
|
|
|
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]="2061590a-8bec-56a7-864d-cb1435ccb994")}catch(e){}}();
|
|
4
4
|
import { a as defineCommand, i as GLOBAL_FLAGS, r as ExitCode, t as toInvocation } from "./scrub-BZ0egWxC.js";
|
|
5
|
-
import { C as
|
|
5
|
+
import { C as ExpectedFailure, E as TimedOut, S as version$1, T as LockHeld, _ as API_VERSION, a as isShipped, b as toSettings, g as writeFailure, h as writeData, i as toUpdateCheckRefusal, l as toStateDirectory, m as toText, o as toPackageDirectory, p as toPrintable, r as toCheck, s as noteNotices, t as PACKAGE, u as isJSONOutput, v as CONFIG_FILE, w as Interrupted, x as toTelemetry, y as ConfigFailure } from "./check-wgnggJ45.js";
|
|
6
6
|
import { n as toAnswer$1, r as toCompletion, t as PATH_VALUES } from "./completion-tree-DLC6ARLR.js";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
8
|
import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
|
|
@@ -15,77 +15,6 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
15
15
|
import { createInterface } from "node:readline/promises";
|
|
16
16
|
import { createServer } from "node:http";
|
|
17
17
|
import { createInterface as createInterface$1 } from "node:readline";
|
|
18
|
-
//#region src/cli/parser.ts
|
|
19
|
-
/** toParser builds the parser from the registry, and hands whatever it parses to onRun. */
|
|
20
|
-
function toParser(entries, onRun, isSilent = false) {
|
|
21
|
-
const program = new Command();
|
|
22
|
-
program.name("hardfin").description("Call the Hardfin API from a terminal or an agent").version(version$1, "-v, --version").enablePositionalOptions();
|
|
23
|
-
toQuietParser(program, isSilent);
|
|
24
|
-
for (const flag of GLOBAL_FLAGS) program.option(`--${flag.name} <${flag.valueName}>`, flag.description);
|
|
25
|
-
for (const command of entries) program.addCommand(toProgram(program, command, onRun, isSilent), { hidden: command.hidden });
|
|
26
|
-
return program;
|
|
27
|
-
}
|
|
28
|
-
/** toProgram wires one registry command into the parser. */
|
|
29
|
-
function toProgram(root, command, onRun, isSilent) {
|
|
30
|
-
const program = new Command(command.name).summary(command.summary).description(command.description ?? command.summary);
|
|
31
|
-
toQuietParser(program, isSilent);
|
|
32
|
-
for (const argument of command.arguments) {
|
|
33
|
-
const name = argument.variadic ? `${argument.name}...` : argument.name;
|
|
34
|
-
program.argument(argument.required ? `<${name}>` : `[${name}]`, argument.description);
|
|
35
|
-
}
|
|
36
|
-
for (const flag of command.flags) {
|
|
37
|
-
const short = flag.short ? `-${flag.short}, ` : "";
|
|
38
|
-
const value = flag.valueName ? ` <${flag.valueName}>` : "";
|
|
39
|
-
const option = new Option(`${short}--${flag.name}${value}`, flag.description);
|
|
40
|
-
if (flag.negatable) program.addOption(new Option(`--no-${flag.name}`, `${flag.description}, turned off`));
|
|
41
|
-
if (flag.repeatable) option.argParser(collect);
|
|
42
|
-
if (!flag.repeatable && flag.valueName) option.argParser(toOnce(flag.name));
|
|
43
|
-
if (flag.defaultValue !== void 0) option.default(flag.defaultValue);
|
|
44
|
-
program.addOption(option);
|
|
45
|
-
}
|
|
46
|
-
for (const example of command.examples) program.addHelpText("after", `\n${example.description}:\n $ ${example.command}`);
|
|
47
|
-
for (const subcommand of command.subcommands ?? []) program.addCommand(toProgram(root, subcommand, onRun, isSilent), { hidden: subcommand.hidden });
|
|
48
|
-
if (!command.run) return program;
|
|
49
|
-
program.action((...parsed) => {
|
|
50
|
-
const flags = parsed[parsed.length - 2] ?? {};
|
|
51
|
-
return onRun({
|
|
52
|
-
command,
|
|
53
|
-
args: parsed.slice(0, parsed.length - 2).flatMap(toArgumentList),
|
|
54
|
-
flags,
|
|
55
|
-
globals: root.opts()
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
return program;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* toQuietParser has the parser hand back its mistakes, so they are answered like any other
|
|
62
|
-
* failure. A silent parser also keeps its help and version to itself, for a caller that only
|
|
63
|
-
* wants to know what a command line means.
|
|
64
|
-
*/
|
|
65
|
-
function toQuietParser(program, isSilent) {
|
|
66
|
-
program.exitOverride().configureOutput({ outputError: () => void 0 });
|
|
67
|
-
if (isSilent) program.configureOutput({
|
|
68
|
-
writeOut: () => void 0,
|
|
69
|
-
writeErr: () => void 0
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
function toArgumentList(value) {
|
|
73
|
-
if (Array.isArray(value)) return value.map(String);
|
|
74
|
-
return value === void 0 ? [] : [toText(value)];
|
|
75
|
-
}
|
|
76
|
-
/** toOnce refuses a flag given a second time. */
|
|
77
|
-
function toOnce(name) {
|
|
78
|
-
let isGiven = false;
|
|
79
|
-
return (value) => {
|
|
80
|
-
if (isGiven) throw new InvalidArgumentError(`--${name} takes one value and is given more than once`);
|
|
81
|
-
isGiven = true;
|
|
82
|
-
return value;
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function collect(value, previous) {
|
|
86
|
-
return [...previous ?? [], value];
|
|
87
|
-
}
|
|
88
|
-
//#endregion
|
|
89
18
|
//#region src/auth/jwt.ts
|
|
90
19
|
/**
|
|
91
20
|
* toClaims reads an access token's payload for display. Nothing here verifies the
|
|
@@ -1137,7 +1066,10 @@ function toCommandLines(command, parents) {
|
|
|
1137
1066
|
function toTaken(flag) {
|
|
1138
1067
|
const values = flag.schema.enum ?? flag.schema.items?.enum;
|
|
1139
1068
|
if (values) return `one of ${values.map((value) => `\`${value}\``).join(", ")}`;
|
|
1140
|
-
if (flag.element)
|
|
1069
|
+
if (flag.element) {
|
|
1070
|
+
const taken = flag.element.map((field) => `${field}=`).join(",");
|
|
1071
|
+
return flag.elementRequired ? `${taken}, each needing ${flag.elementRequired.map((field) => `${field}=`).join(",")}` : taken;
|
|
1072
|
+
}
|
|
1141
1073
|
return flag.valueName ?? "nothing";
|
|
1142
1074
|
}
|
|
1143
1075
|
async function runAgentGuide(input) {
|
|
@@ -1191,6 +1123,7 @@ function toDescribed(flag) {
|
|
|
1191
1123
|
elementMoney: sent.elementMoney,
|
|
1192
1124
|
elementEnums: sent.elementEnums,
|
|
1193
1125
|
elementPatterns: sent.elementPatterns,
|
|
1126
|
+
elementRequired: sent.elementRequired,
|
|
1194
1127
|
together: sent.together
|
|
1195
1128
|
};
|
|
1196
1129
|
return Object.fromEntries(Object.entries(described).filter(([, value]) => value !== void 0 && value !== false));
|
|
@@ -1247,6 +1180,84 @@ function toSummary(command, parents) {
|
|
|
1247
1180
|
};
|
|
1248
1181
|
}
|
|
1249
1182
|
//#endregion
|
|
1183
|
+
//#region src/cli/parser.ts
|
|
1184
|
+
/** toParser builds the parser from the registry, and hands whatever it parses to onRun. */
|
|
1185
|
+
function toParser(entries, onRun, isSilent = false) {
|
|
1186
|
+
const program = new Command();
|
|
1187
|
+
program.name("hardfin").description("Call the Hardfin API from a terminal or an agent").version(version$1, "-v, --version").enablePositionalOptions();
|
|
1188
|
+
toQuietParser(program, isSilent);
|
|
1189
|
+
for (const flag of GLOBAL_FLAGS) program.option(`--${flag.name} <${flag.valueName}>`, flag.description);
|
|
1190
|
+
for (const command of entries) program.addCommand(toProgram(program, command, onRun, isSilent), { hidden: command.hidden });
|
|
1191
|
+
return program;
|
|
1192
|
+
}
|
|
1193
|
+
/** toHelpDescription says in help whether a flag, or each element it carries, must be given. */
|
|
1194
|
+
function toHelpDescription(flag) {
|
|
1195
|
+
const described = toDescribed(flag);
|
|
1196
|
+
const required = described.required ? `Required. ${flag.description}` : flag.description;
|
|
1197
|
+
if (!described.elementRequired) return required;
|
|
1198
|
+
return `${required}. Each one needs ${described.elementRequired.map((field) => `${field}=`).join(", ")}`;
|
|
1199
|
+
}
|
|
1200
|
+
/** toProgram wires one registry command into the parser. */
|
|
1201
|
+
function toProgram(root, command, onRun, isSilent) {
|
|
1202
|
+
const program = new Command(command.name).summary(command.summary).description(command.description ?? command.summary);
|
|
1203
|
+
toQuietParser(program, isSilent);
|
|
1204
|
+
for (const argument of command.arguments) {
|
|
1205
|
+
const name = argument.variadic ? `${argument.name}...` : argument.name;
|
|
1206
|
+
program.argument(argument.required ? `<${name}>` : `[${name}]`, argument.description);
|
|
1207
|
+
}
|
|
1208
|
+
for (const flag of command.flags) {
|
|
1209
|
+
const short = flag.short ? `-${flag.short}, ` : "";
|
|
1210
|
+
const value = flag.valueName ? ` <${flag.valueName}>` : "";
|
|
1211
|
+
const option = new Option(`${short}--${flag.name}${value}`, toHelpDescription(flag));
|
|
1212
|
+
if (flag.negatable) program.addOption(new Option(`--no-${flag.name}`, `${flag.description}, turned off`));
|
|
1213
|
+
if (flag.repeatable) option.argParser(collect);
|
|
1214
|
+
if (!flag.repeatable && flag.valueName) option.argParser(toOnce(flag.name));
|
|
1215
|
+
if (flag.defaultValue !== void 0) option.default(flag.defaultValue);
|
|
1216
|
+
program.addOption(option);
|
|
1217
|
+
}
|
|
1218
|
+
for (const example of command.examples) program.addHelpText("after", `\n${example.description}:\n $ ${example.command}`);
|
|
1219
|
+
for (const subcommand of command.subcommands ?? []) program.addCommand(toProgram(root, subcommand, onRun, isSilent), { hidden: subcommand.hidden });
|
|
1220
|
+
if (!command.run) return program;
|
|
1221
|
+
program.action((...parsed) => {
|
|
1222
|
+
const flags = parsed[parsed.length - 2] ?? {};
|
|
1223
|
+
return onRun({
|
|
1224
|
+
command,
|
|
1225
|
+
args: parsed.slice(0, parsed.length - 2).flatMap(toArgumentList),
|
|
1226
|
+
flags,
|
|
1227
|
+
globals: root.opts()
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
return program;
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* toQuietParser has the parser hand back its mistakes, so they are answered like any other
|
|
1234
|
+
* failure. A silent parser also keeps its help and version to itself, for a caller that only
|
|
1235
|
+
* wants to know what a command line means.
|
|
1236
|
+
*/
|
|
1237
|
+
function toQuietParser(program, isSilent) {
|
|
1238
|
+
program.exitOverride().configureOutput({ outputError: () => void 0 });
|
|
1239
|
+
if (isSilent) program.configureOutput({
|
|
1240
|
+
writeOut: () => void 0,
|
|
1241
|
+
writeErr: () => void 0
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
function toArgumentList(value) {
|
|
1245
|
+
if (Array.isArray(value)) return value.map(String);
|
|
1246
|
+
return value === void 0 ? [] : [toText(value)];
|
|
1247
|
+
}
|
|
1248
|
+
/** toOnce refuses a flag given a second time. */
|
|
1249
|
+
function toOnce(name) {
|
|
1250
|
+
let isGiven = false;
|
|
1251
|
+
return (value) => {
|
|
1252
|
+
if (isGiven) throw new InvalidArgumentError(`--${name} takes one value and is given more than once`);
|
|
1253
|
+
isGiven = true;
|
|
1254
|
+
return value;
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
function collect(value, previous) {
|
|
1258
|
+
return [...previous ?? [], value];
|
|
1259
|
+
}
|
|
1260
|
+
//#endregion
|
|
1250
1261
|
//#region src/command/input.ts
|
|
1251
1262
|
/** InputFailure is a body given with --input that cannot be used, and says why. */
|
|
1252
1263
|
var InputFailure = class extends Error {
|
|
@@ -1421,6 +1432,11 @@ async function runOperation(operation, input) {
|
|
|
1421
1432
|
writeFailure("invalid_flag", built.message, input.isJSON);
|
|
1422
1433
|
return ExitCode.USAGE;
|
|
1423
1434
|
}
|
|
1435
|
+
const missingElementField = toMissingElementField(operation, built);
|
|
1436
|
+
if (missingElementField) {
|
|
1437
|
+
writeFailure("missing_flag", missingElementField, input.isJSON);
|
|
1438
|
+
return ExitCode.USAGE;
|
|
1439
|
+
}
|
|
1424
1440
|
const isBareAction = operation.method === "POST" && operation.pathParameters.length > 0 && !operation.upload;
|
|
1425
1441
|
const body = built === void 0 && isBareAction && operation.bodyFlags.length > 0 ? {} : built;
|
|
1426
1442
|
if (operation.bodyFlags.length > 0 && body === void 0) {
|
|
@@ -1581,6 +1597,29 @@ function toUnknownField(shape, value, path) {
|
|
|
1581
1597
|
if (unknown) return unknown;
|
|
1582
1598
|
}
|
|
1583
1599
|
}
|
|
1600
|
+
/**
|
|
1601
|
+
* toMissingElementField names the first element that lacks a field every element must carry,
|
|
1602
|
+
* whether the element came from its flag or from the --input body.
|
|
1603
|
+
*/
|
|
1604
|
+
function toMissingElementField(operation, body) {
|
|
1605
|
+
for (const flag of operation.bodyFlags) {
|
|
1606
|
+
const elements = toValueAt(body, flag.jsonPath);
|
|
1607
|
+
if (!flag.elementRequired || !Array.isArray(elements)) continue;
|
|
1608
|
+
for (const [index, element] of elements.entries()) {
|
|
1609
|
+
const missing = flag.elementRequired.filter((field) => !hasPath(element, [field]));
|
|
1610
|
+
if (missing.length > 0) return `${flag.jsonPath.join(".")}[${index}] needs ${missing.join(", ")}. Every --${flag.name} needs ${flag.elementRequired.map((field) => `${field}=`).join(", ")}`;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
/** toValueAt reads the value a body holds at a field's path. */
|
|
1615
|
+
function toValueAt(body, path) {
|
|
1616
|
+
let holder = body;
|
|
1617
|
+
for (const name of path) {
|
|
1618
|
+
if (typeof holder !== "object" || holder === null) return;
|
|
1619
|
+
holder = holder[name];
|
|
1620
|
+
}
|
|
1621
|
+
return holder;
|
|
1622
|
+
}
|
|
1584
1623
|
/** hasPath reports whether a body already holds a value at a field's path. */
|
|
1585
1624
|
function hasPath(body, path) {
|
|
1586
1625
|
let holder = body;
|
|
@@ -4784,7 +4823,7 @@ const surfaceCommands = [
|
|
|
4784
4823
|
defineOperation({
|
|
4785
4824
|
name: "create",
|
|
4786
4825
|
summary: "Create asset",
|
|
4787
|
-
example: "hardfin asset create --in-inventory-date <value> --item-id <uuid> --location-id <uuid>",
|
|
4826
|
+
example: "hardfin asset create --in-inventory-date <value> --item-id <uuid> --location-id <uuid> --serial <value>",
|
|
4788
4827
|
method: "POST",
|
|
4789
4828
|
path: "/asset",
|
|
4790
4829
|
pathParameters: [],
|
|
@@ -5307,6 +5346,11 @@ const surfaceCommands = [
|
|
|
5307
5346
|
"shipAt": ["string", "null"],
|
|
5308
5347
|
"shipAtTimezone": ["string", "null"]
|
|
5309
5348
|
},
|
|
5349
|
+
elementRequired: [
|
|
5350
|
+
"assetId",
|
|
5351
|
+
"destinationId",
|
|
5352
|
+
"originId"
|
|
5353
|
+
],
|
|
5310
5354
|
schema: z.array(z.string())
|
|
5311
5355
|
}],
|
|
5312
5356
|
body: { "fields": { "moves": { "items": { "fields": {
|
|
@@ -5350,6 +5394,7 @@ const surfaceCommands = [
|
|
|
5350
5394
|
"deliverAt": ["string", "null"],
|
|
5351
5395
|
"shipAt": ["string", "null"]
|
|
5352
5396
|
},
|
|
5397
|
+
elementRequired: ["assetId", "id"],
|
|
5353
5398
|
schema: z.array(z.string())
|
|
5354
5399
|
}],
|
|
5355
5400
|
body: { "fields": { "moves": { "items": { "fields": {
|
|
@@ -5441,6 +5486,7 @@ const surfaceCommands = [
|
|
|
5441
5486
|
"string",
|
|
5442
5487
|
"null"
|
|
5443
5488
|
] },
|
|
5489
|
+
elementRequired: ["fieldId"],
|
|
5444
5490
|
schema: z.array(z.string())
|
|
5445
5491
|
},
|
|
5446
5492
|
{
|
|
@@ -5941,7 +5987,7 @@ const surfaceCommands = [
|
|
|
5941
5987
|
subcommands: [defineOperation({
|
|
5942
5988
|
name: "create",
|
|
5943
5989
|
summary: "Create asset cost adjustment",
|
|
5944
|
-
example: "hardfin asset cost-adjustment create ast_4f9xk2mq7plr8stz --adjustment-type <value> --amount-value <value> --amount-iso-code <value>",
|
|
5990
|
+
example: "hardfin asset cost-adjustment create ast_4f9xk2mq7plr8stz --adjustment-type <value> --amount-value <value> --amount-iso-code <value> --effective-date <value> --reason <value>",
|
|
5945
5991
|
method: "POST",
|
|
5946
5992
|
path: "/asset/{assetKey}/cost-adjustment",
|
|
5947
5993
|
pathParameters: [{
|
|
@@ -7509,6 +7555,7 @@ const surfaceCommands = [
|
|
|
7509
7555
|
element: ["itemId", "requestedQuantity"],
|
|
7510
7556
|
elementUuids: ["itemId"],
|
|
7511
7557
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
7558
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
7512
7559
|
schema: z.array(z.string())
|
|
7513
7560
|
},
|
|
7514
7561
|
{
|
|
@@ -7619,6 +7666,7 @@ const surfaceCommands = [
|
|
|
7619
7666
|
element: ["itemId", "requestedQuantity"],
|
|
7620
7667
|
elementUuids: ["itemId"],
|
|
7621
7668
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
7669
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
7622
7670
|
schema: z.array(z.string())
|
|
7623
7671
|
},
|
|
7624
7672
|
{
|
|
@@ -8111,6 +8159,7 @@ const surfaceCommands = [
|
|
|
8111
8159
|
"order": ["integer"],
|
|
8112
8160
|
"section": ["integer"]
|
|
8113
8161
|
},
|
|
8162
|
+
elementRequired: ["fieldId"],
|
|
8114
8163
|
schema: z.array(z.string())
|
|
8115
8164
|
},
|
|
8116
8165
|
{
|
|
@@ -8594,7 +8643,7 @@ const surfaceCommands = [
|
|
|
8594
8643
|
defineOperation({
|
|
8595
8644
|
name: "create",
|
|
8596
8645
|
summary: "Create item field",
|
|
8597
|
-
example: "hardfin item field create item_7Hq2Lm9XcR4tWz8K --field-type <value> --label <value> --order <number>",
|
|
8646
|
+
example: "hardfin item field create item_7Hq2Lm9XcR4tWz8K --field-type <value> --label <value> --order <number> --section <number>",
|
|
8598
8647
|
method: "POST",
|
|
8599
8648
|
path: "/item/{itemKey}/field",
|
|
8600
8649
|
pathParameters: [{
|
|
@@ -9344,7 +9393,7 @@ const surfaceCommands = [
|
|
|
9344
9393
|
defineOperation({
|
|
9345
9394
|
name: "create",
|
|
9346
9395
|
summary: "Create project",
|
|
9347
|
-
example: "hardfin project create --line-item
|
|
9396
|
+
example: "hardfin project create --line-item itemId=<value>,transactionType=<value>,aggregationKey=<value>,quantity=<value> --name <value>",
|
|
9348
9397
|
method: "POST",
|
|
9349
9398
|
path: "/project",
|
|
9350
9399
|
pathParameters: [],
|
|
@@ -9557,6 +9606,7 @@ const surfaceCommands = [
|
|
|
9557
9606
|
"totalOverride.amount": "^[^-]",
|
|
9558
9607
|
"unitPrice.amount": "^[^-]"
|
|
9559
9608
|
},
|
|
9609
|
+
elementRequired: ["itemId", "transactionType"],
|
|
9560
9610
|
schema: z.array(z.string())
|
|
9561
9611
|
},
|
|
9562
9612
|
{
|
|
@@ -9695,7 +9745,7 @@ const surfaceCommands = [
|
|
|
9695
9745
|
defineOperation({
|
|
9696
9746
|
name: "update",
|
|
9697
9747
|
summary: "Patch project",
|
|
9698
|
-
example: "hardfin project update proj_7hq2lm9xcr4twz8k --line-item
|
|
9748
|
+
example: "hardfin project update proj_7hq2lm9xcr4twz8k --line-item itemId=<value>,transactionType=<value>,aggregationKey=<value>,quantity=<value>",
|
|
9699
9749
|
method: "PATCH",
|
|
9700
9750
|
path: "/project/{projectKey}",
|
|
9701
9751
|
pathParameters: [{
|
|
@@ -9973,6 +10023,7 @@ const surfaceCommands = [
|
|
|
9973
10023
|
"totalOverride.amount": "^[^-]",
|
|
9974
10024
|
"unitPrice.amount": "^[^-]"
|
|
9975
10025
|
},
|
|
10026
|
+
elementRequired: ["itemId", "transactionType"],
|
|
9976
10027
|
schema: z.array(z.string())
|
|
9977
10028
|
},
|
|
9978
10029
|
{
|
|
@@ -10896,6 +10947,7 @@ const surfaceCommands = [
|
|
|
10896
10947
|
element: ["itemId", "requestedQuantity"],
|
|
10897
10948
|
elementUuids: ["itemId"],
|
|
10898
10949
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
10950
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
10899
10951
|
schema: z.array(z.string())
|
|
10900
10952
|
},
|
|
10901
10953
|
{
|
|
@@ -11112,6 +11164,7 @@ const surfaceCommands = [
|
|
|
11112
11164
|
element: ["itemId", "requestedQuantity"],
|
|
11113
11165
|
elementUuids: ["itemId"],
|
|
11114
11166
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
11167
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
11115
11168
|
schema: z.array(z.string())
|
|
11116
11169
|
},
|
|
11117
11170
|
{
|
|
@@ -11510,7 +11563,7 @@ const surfaceCommands = [
|
|
|
11510
11563
|
defineOperation({
|
|
11511
11564
|
name: "create",
|
|
11512
11565
|
summary: "Create return order",
|
|
11513
|
-
example: "hardfin return-order create --from-location-id <uuid> --item itemId=<value>,requestedQuantity=<value> --return-type <value>",
|
|
11566
|
+
example: "hardfin return-order create --from-location-id <uuid> --item itemId=<value>,requestedQuantity=<value> --return-type <value> --to-location-id <uuid>",
|
|
11514
11567
|
method: "POST",
|
|
11515
11568
|
path: "/return-order",
|
|
11516
11569
|
pathParameters: [],
|
|
@@ -11636,6 +11689,7 @@ const surfaceCommands = [
|
|
|
11636
11689
|
element: ["itemId", "requestedQuantity"],
|
|
11637
11690
|
elementUuids: ["itemId"],
|
|
11638
11691
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
11692
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
11639
11693
|
schema: z.array(z.string())
|
|
11640
11694
|
},
|
|
11641
11695
|
{
|
|
@@ -11863,6 +11917,7 @@ const surfaceCommands = [
|
|
|
11863
11917
|
element: ["itemId", "requestedQuantity"],
|
|
11864
11918
|
elementUuids: ["itemId"],
|
|
11865
11919
|
elementTypes: { "requestedQuantity": ["integer"] },
|
|
11920
|
+
elementRequired: ["itemId", "requestedQuantity"],
|
|
11866
11921
|
schema: z.array(z.string())
|
|
11867
11922
|
},
|
|
11868
11923
|
{
|
|
@@ -13679,5 +13734,5 @@ async function toReported(error, isJSON, resolved, level = "error") {
|
|
|
13679
13734
|
//#endregion
|
|
13680
13735
|
export { toCli, toParseExitCode, toReported, wasReportAttempted, writeCrash };
|
|
13681
13736
|
|
|
13682
|
-
//# sourceMappingURL=program-
|
|
13683
|
-
//# debugId=
|
|
13737
|
+
//# sourceMappingURL=program-CYp6XF8D.js.map
|
|
13738
|
+
//# debugId=2061590a-8bec-56a7-864d-cb1435ccb994
|