@hardfin/cli 0.1.0-dev.31 → 0.1.0-dev.33

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
@@ -77,6 +77,15 @@ hardfin file get file_7hq2mx9pkr4stz8w --output photo.jpg
77
77
  hardfin file get file_7hq2mx9pkr4stz8w --output - | wc -c
78
78
  ```
79
79
 
80
+ Send a whole body as JSON with `--input`, from a file or from standard input. A flag given as
81
+ well replaces its field, and a required field the body carries needs no flag. A value with a
82
+ comma in it, which a `key=value` flag cannot carry, goes through `--input`.
83
+
84
+ ```sh
85
+ hardfin location create --input location.json
86
+ echo '{"name": "Dock 4, north side"}' | hardfin location create --input -
87
+ ```
88
+
80
89
  Clear a field with `--unset`, naming the flag:
81
90
 
82
91
  ```sh
@@ -199,6 +208,21 @@ On WSL the browser is opened through Windows, by `wslview`, then PowerShell, the
199
208
  The port changes every time, so Hardfin matches a loopback redirect by everything except its
200
209
  port, which is what RFC 8252 asks of an authorization server.
201
210
 
211
+ ### When an agent signs you in
212
+
213
+ An agent that runs `hardfin login` reads its stderr, and you only see what the agent passes
214
+ on. So when output is JSON, which it is whenever stdout is not a terminal, the command writes
215
+ one line of JSON on stderr before it starts waiting.
216
+
217
+ | `event` | Also carries | The agent should |
218
+ | ------------- | -------------------------------------------------------------------- | --------------------------------------------------- |
219
+ | `sign_in_url` | `url`, `browserOpened`, `waitSeconds` | Show you `url`, saying whether a browser tab opened |
220
+ | `device_code` | `userCode`, `verificationUri`, `url`, `browserOpened`, `waitSeconds` | Show you the code and where to enter it |
221
+
222
+ The command then waits up to `waitSeconds` for you to approve, so the agent has to let it run
223
+ that long. An agent with no way to open your browser, such as one in a cloud sandbox, signs
224
+ in with `--device`.
225
+
202
226
  ### Signing in from a machine with no browser
203
227
 
204
228
  `hardfin login --device` prints a short code and a page to enter it on, and waits. Approve
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]="bb3026cb-ab06-5af0-9743-290b61ec91a3")}catch(e){}}();
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]="3f5ddeb4-6ae9-5ded-b447-15c8483fc583")}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-B0vISolG.js");
17
+ const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-ByvcKNri.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-B0vISolG.js");
36
+ const { toReported } = await import("./program-ByvcKNri.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=bb3026cb-ab06-5af0-9743-290b61ec91a3
54
+ //# debugId=3f5ddeb4-6ae9-5ded-b447-15c8483fc583
@@ -351,6 +351,11 @@
351
351
  "valueName": "field",
352
352
  "repeatable": true
353
353
  },
354
+ {
355
+ "name": "input",
356
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
357
+ "valueName": "path"
358
+ },
354
359
  {
355
360
  "name": "json",
356
361
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -384,6 +389,11 @@
384
389
  "valueName": "assetId=,deliverAt=,deliverAtTimezone=,destinationId=,originId=,shipAt=,shipAtTimezone=",
385
390
  "repeatable": true
386
391
  },
392
+ {
393
+ "name": "input",
394
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
395
+ "valueName": "path"
396
+ },
387
397
  {
388
398
  "name": "json",
389
399
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -412,6 +422,11 @@
412
422
  "valueName": "assetId=,deliverAt=,id=,shipAt=",
413
423
  "repeatable": true
414
424
  },
425
+ {
426
+ "name": "input",
427
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
428
+ "valueName": "path"
429
+ },
415
430
  {
416
431
  "name": "json",
417
432
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -487,6 +502,11 @@
487
502
  "valueName": "field",
488
503
  "repeatable": true
489
504
  },
505
+ {
506
+ "name": "input",
507
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
508
+ "valueName": "path"
509
+ },
490
510
  {
491
511
  "name": "json",
492
512
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -596,6 +616,11 @@
596
616
  "valueName": "field",
597
617
  "repeatable": true
598
618
  },
619
+ {
620
+ "name": "input",
621
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
622
+ "valueName": "path"
623
+ },
599
624
  {
600
625
  "name": "json",
601
626
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -623,6 +648,11 @@
623
648
  "description": "Whether Hardfin sets the asset's in-service date itself",
624
649
  "negatable": true
625
650
  },
651
+ {
652
+ "name": "input",
653
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
654
+ "valueName": "path"
655
+ },
626
656
  {
627
657
  "name": "json",
628
658
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -695,6 +725,11 @@
695
725
  "valueName": "field",
696
726
  "repeatable": true
697
727
  },
728
+ {
729
+ "name": "input",
730
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
731
+ "valueName": "path"
732
+ },
698
733
  {
699
734
  "name": "json",
700
735
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -893,6 +928,11 @@
893
928
  "valueName": "field",
894
929
  "repeatable": true
895
930
  },
931
+ {
932
+ "name": "input",
933
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
934
+ "valueName": "path"
935
+ },
896
936
  {
897
937
  "name": "json",
898
938
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -913,6 +953,11 @@
913
953
  "description": "The day your organization takes the asset back, which cannot be in the future",
914
954
  "valueName": "value"
915
955
  },
956
+ {
957
+ "name": "input",
958
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
959
+ "valueName": "path"
960
+ },
916
961
  {
917
962
  "name": "json",
918
963
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -965,6 +1010,11 @@
965
1010
  "valueName": "field",
966
1011
  "repeatable": true
967
1012
  },
1013
+ {
1014
+ "name": "input",
1015
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1016
+ "valueName": "path"
1017
+ },
968
1018
  {
969
1019
  "name": "json",
970
1020
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1027,6 +1077,11 @@
1027
1077
  "valueName": "field",
1028
1078
  "repeatable": true
1029
1079
  },
1080
+ {
1081
+ "name": "input",
1082
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1083
+ "valueName": "path"
1084
+ },
1030
1085
  {
1031
1086
  "name": "json",
1032
1087
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1106,6 +1161,11 @@
1106
1161
  "valueName": "field",
1107
1162
  "repeatable": true
1108
1163
  },
1164
+ {
1165
+ "name": "input",
1166
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1167
+ "valueName": "path"
1168
+ },
1109
1169
  {
1110
1170
  "name": "json",
1111
1171
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1165,6 +1225,11 @@
1165
1225
  "valueName": "field",
1166
1226
  "repeatable": true
1167
1227
  },
1228
+ {
1229
+ "name": "input",
1230
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1231
+ "valueName": "path"
1232
+ },
1168
1233
  {
1169
1234
  "name": "json",
1170
1235
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1313,6 +1378,11 @@
1313
1378
  "valueName": "field",
1314
1379
  "repeatable": true
1315
1380
  },
1381
+ {
1382
+ "name": "input",
1383
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1384
+ "valueName": "path"
1385
+ },
1316
1386
  {
1317
1387
  "name": "json",
1318
1388
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1402,6 +1472,11 @@
1402
1472
  "valueName": "field",
1403
1473
  "repeatable": true
1404
1474
  },
1475
+ {
1476
+ "name": "input",
1477
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1478
+ "valueName": "path"
1479
+ },
1405
1480
  {
1406
1481
  "name": "json",
1407
1482
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1664,6 +1739,11 @@
1664
1739
  "valueName": "field",
1665
1740
  "repeatable": true
1666
1741
  },
1742
+ {
1743
+ "name": "input",
1744
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1745
+ "valueName": "path"
1746
+ },
1667
1747
  {
1668
1748
  "name": "json",
1669
1749
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1739,6 +1819,11 @@
1739
1819
  "valueName": "field",
1740
1820
  "repeatable": true
1741
1821
  },
1822
+ {
1823
+ "name": "input",
1824
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1825
+ "valueName": "path"
1826
+ },
1742
1827
  {
1743
1828
  "name": "json",
1744
1829
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1838,6 +1923,11 @@
1838
1923
  "valueName": "field",
1839
1924
  "repeatable": true
1840
1925
  },
1926
+ {
1927
+ "name": "input",
1928
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1929
+ "valueName": "path"
1930
+ },
1841
1931
  {
1842
1932
  "name": "json",
1843
1933
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1898,6 +1988,11 @@
1898
1988
  "valueName": "field",
1899
1989
  "repeatable": true
1900
1990
  },
1991
+ {
1992
+ "name": "input",
1993
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
1994
+ "valueName": "path"
1995
+ },
1901
1996
  {
1902
1997
  "name": "json",
1903
1998
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -1924,6 +2019,11 @@
1924
2019
  "valueName": "field",
1925
2020
  "repeatable": true
1926
2021
  },
2022
+ {
2023
+ "name": "input",
2024
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
2025
+ "valueName": "path"
2026
+ },
1927
2027
  {
1928
2028
  "name": "json",
1929
2029
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -2141,6 +2241,11 @@
2141
2241
  "valueName": "field",
2142
2242
  "repeatable": true
2143
2243
  },
2244
+ {
2245
+ "name": "input",
2246
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
2247
+ "valueName": "path"
2248
+ },
2144
2249
  {
2145
2250
  "name": "json",
2146
2251
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -2260,6 +2365,11 @@
2260
2365
  "valueName": "field",
2261
2366
  "repeatable": true
2262
2367
  },
2368
+ {
2369
+ "name": "input",
2370
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
2371
+ "valueName": "path"
2372
+ },
2263
2373
  {
2264
2374
  "name": "json",
2265
2375
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -2371,6 +2481,11 @@
2371
2481
  "valueName": "field",
2372
2482
  "repeatable": true
2373
2483
  },
2484
+ {
2485
+ "name": "input",
2486
+ "description": "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
2487
+ "valueName": "path"
2488
+ },
2374
2489
  {
2375
2490
  "name": "json",
2376
2491
  "description": "Print machine-readable output, which is the default when stdout is not a terminal"
@@ -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]="ab584cf3-443f-569e-8167-fa0cbab9ab68")}catch(e){}}();
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]="93b4bc8d-ebeb-59ca-967b-9fd10484e4a5")}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";
@@ -987,6 +987,7 @@ function toGuide(commands, version) {
987
987
  "## Before calling",
988
988
  "",
989
989
  "- Authenticate with `--api-key`, by setting `HARDFIN_API_KEY`, or by running `hardfin login`",
990
+ "- `hardfin login` writes one JSON line on stderr before it waits. Show the person its `url`, or its `userCode` and `verificationUri`, and let the command run for `waitSeconds`",
990
991
  "- Every command prints JSON on stdout and diagnostics on stderr",
991
992
  "- Output is JSON whenever stdout is not a terminal, so no flag is needed when calling from code",
992
993
  "",
@@ -1065,6 +1066,31 @@ function toSummary(command) {
1065
1066
  subcommands: command.subcommands?.filter((entry) => !entry.hidden).map(toSummary)
1066
1067
  };
1067
1068
  }
1069
+ //#endregion
1070
+ //#region src/command/input.ts
1071
+ /** InputFailure is a body given with --input that cannot be used, and says why. */
1072
+ var InputFailure = class extends Error {
1073
+ constructor(message) {
1074
+ super(message);
1075
+ this.name = "InputFailure";
1076
+ }
1077
+ };
1078
+ /** toJSONInput reads the JSON a command was given with --input, from a file or from standard input. */
1079
+ function toJSONInput(source) {
1080
+ const name = source === "-" ? "standard input" : source;
1081
+ let text;
1082
+ try {
1083
+ text = readFileSync(source === "-" ? 0 : source, "utf8");
1084
+ } catch {
1085
+ return new InputFailure(`${name} cannot be read`);
1086
+ }
1087
+ if (text.trim() === "") return new InputFailure(`${name} is empty, and a body needs JSON`);
1088
+ try {
1089
+ return JSON.parse(text);
1090
+ } catch {
1091
+ return new InputFailure(`${name} does not hold JSON`);
1092
+ }
1093
+ }
1068
1094
  const apiCommand = defineCommand({
1069
1095
  name: "api",
1070
1096
  summary: "Call a Hardfin API endpoint and print what it answers",
@@ -1142,9 +1168,9 @@ async function runApi(input) {
1142
1168
  }
1143
1169
  let body;
1144
1170
  if (typeof input.flags["input"] === "string") {
1145
- body = toBody$1(input.flags["input"]);
1146
- if (body === void 0) {
1147
- writeFailure("invalid_input", `${input.flags["input"]} does not hold JSON`, input.isJSON);
1171
+ body = toJSONInput(input.flags["input"]);
1172
+ if (body instanceof InputFailure) {
1173
+ writeFailure("invalid_input", body.message, input.isJSON);
1148
1174
  return ExitCode.USAGE;
1149
1175
  }
1150
1176
  }
@@ -1180,14 +1206,6 @@ function toQuery$1(fields) {
1180
1206
  }
1181
1207
  return query;
1182
1208
  }
1183
- function toBody$1(source) {
1184
- const text = source === "-" ? readFileSync(0, "utf8") : readFileSync(source, "utf8");
1185
- try {
1186
- return JSON.parse(text);
1187
- } catch {
1188
- return;
1189
- }
1190
- }
1191
1209
  //#endregion
1192
1210
  //#region src/auth/browser.ts
1193
1211
  /** isWSL reports whether this Linux is running under Windows. */
@@ -2137,7 +2155,13 @@ async function runLogin(input) {
2137
2155
  challenge: pkce.challenge
2138
2156
  });
2139
2157
  const opened = input.flags["noBrowser"] !== true && !process.env["HARDFIN_NO_BROWSER"] && openBrowser(url);
2140
- process.stderr.write(opened ? `Opening your browser to sign in. If it did not open:\n\n${url}\n\n` : `Open this URL to sign in:\n\n${url}\n\n`);
2158
+ if (input.isJSON) writeEvent({
2159
+ event: "sign_in_url",
2160
+ url,
2161
+ browserOpened: opened,
2162
+ waitSeconds: WAIT_MS / 1e3
2163
+ });
2164
+ else process.stderr.write(opened ? `Opening your browser to sign in. If it did not open:\n\n${url}\n\n` : `Open this URL to sign in:\n\n${url}\n\n`);
2141
2165
  const prompt = toPrompt(url);
2142
2166
  if (process.stdin.isTTY) process.stderr.write("Press c to copy the URL, or paste the code or redirect URL here: ");
2143
2167
  let callback;
@@ -2147,7 +2171,7 @@ async function runLogin(input) {
2147
2171
  listener.close();
2148
2172
  prompt.close();
2149
2173
  }
2150
- process.stderr.write(callback.error ? "\n" : "\nApproved, finishing the sign in\n");
2174
+ if (!input.isJSON) process.stderr.write(callback.error ? "\n" : "\nApproved, finishing the sign in\n");
2151
2175
  if (callback.error) {
2152
2176
  writeFailure("sign_in_failed", `sign in was refused: ${callback.error}${callback.errorDescription ? `, ${callback.errorDescription}` : ""}`, input.isJSON);
2153
2177
  return ExitCode.ERROR;
@@ -2183,7 +2207,15 @@ async function runDeviceLogin(input, metadata, scope) {
2183
2207
  const device = await toDeviceAuthorization(metadata.device_authorization_endpoint, settings.clientId, scope);
2184
2208
  const url = device.verificationUriComplete ?? device.verificationUri;
2185
2209
  const opened = input.flags["noBrowser"] !== true && !process.env["HARDFIN_NO_BROWSER"] && openBrowser(url);
2186
- process.stderr.write([
2210
+ if (input.isJSON) writeEvent({
2211
+ event: "device_code",
2212
+ userCode: device.userCode,
2213
+ verificationUri: device.verificationUri,
2214
+ url,
2215
+ browserOpened: opened,
2216
+ waitSeconds: Math.max(0, Math.round((device.expiresAt - Date.now()) / 1e3))
2217
+ });
2218
+ else process.stderr.write([
2187
2219
  "",
2188
2220
  ` Code ${device.userCode}`,
2189
2221
  ` At ${device.verificationUri}`,
@@ -2235,6 +2267,10 @@ async function toSignedIn(input, metadata, tokens) {
2235
2267
  ].join("\n"));
2236
2268
  return ExitCode.OK;
2237
2269
  }
2270
+ /** writeEvent tells a program what a sign in is waiting on, as one line of JSON on stderr. */
2271
+ function writeEvent(event) {
2272
+ process.stderr.write(`${JSON.stringify(event)}\n`);
2273
+ }
2238
2274
  /** toAuthorizationUrl builds the URL the person approves this CLI at. */
2239
2275
  function toAuthorizationUrl(endpoint, request) {
2240
2276
  const url = new URL(endpoint);
@@ -3097,6 +3133,12 @@ const UNSET_FLAG = {
3097
3133
  repeatable: true,
3098
3134
  schema: z.array(z.string())
3099
3135
  };
3136
+ const INPUT_FLAG = {
3137
+ name: "input",
3138
+ description: "A file holding the JSON request body, or - for standard input. A flag given as well replaces its field",
3139
+ valueName: "path",
3140
+ schema: z.string()
3141
+ };
3100
3142
  const FILE_FLAG = {
3101
3143
  name: "file",
3102
3144
  description: "The file to upload",
@@ -3120,6 +3162,7 @@ function defineOperation(operation) {
3120
3162
  ...operation.queryFlags,
3121
3163
  ...operation.bodyFlags,
3122
3164
  ...operation.bodyFlags.some((flag) => flag.nullable) ? [UNSET_FLAG] : [],
3165
+ ...operation.bodyFlags.length > 0 ? [INPUT_FLAG] : [],
3123
3166
  ...operation.upload ? [...operation.upload.fields, FILE_FLAG] : [],
3124
3167
  ...operation.downloads ? [OUTPUT_FLAG] : [],
3125
3168
  JSON_FLAG
@@ -3147,12 +3190,17 @@ async function runOperation(operation, input) {
3147
3190
  writeFailure("invalid_arguments", `this command takes ${operation.pathParameters.length} argument(s)`, input.isJSON);
3148
3191
  return ExitCode.USAGE;
3149
3192
  }
3150
- const missing = operation.bodyFlags.concat(operation.upload?.fields ?? []).filter((flag) => flag.required && input.flags[toOptionKey(flag.name)] === void 0);
3193
+ const given = toGivenBody(input.flags["input"]);
3194
+ if (given instanceof InputFailure) {
3195
+ writeFailure("invalid_input", given.message, input.isJSON);
3196
+ return ExitCode.USAGE;
3197
+ }
3198
+ const missing = operation.bodyFlags.concat(operation.upload?.fields ?? []).filter((flag) => flag.required && input.flags[toOptionKey(flag.name)] === void 0 && !hasPath(given, flag.jsonPath));
3151
3199
  if (missing.length > 0) {
3152
3200
  writeFailure("missing_flag", `this command needs ${missing.map((flag) => `--${flag.name}`).join(", ")}`, input.isJSON);
3153
3201
  return ExitCode.USAGE;
3154
3202
  }
3155
- const body = toBody(operation, input.flags);
3203
+ const body = toBody(operation, input.flags, given);
3156
3204
  if (body instanceof Error) {
3157
3205
  writeFailure("invalid_flag", body.message, input.isJSON);
3158
3206
  return ExitCode.USAGE;
@@ -3261,10 +3309,27 @@ async function toForm(upload, flags) {
3261
3309
  }
3262
3310
  return form;
3263
3311
  }
3264
- /** toBody builds the request body from the flags, one field at a time. */
3265
- function toBody(operation, flags) {
3266
- const body = {};
3267
- let hasField = false;
3312
+ /** toGivenBody reads the --input body, which a generated command takes only as a JSON object. */
3313
+ function toGivenBody(source) {
3314
+ if (typeof source !== "string") return;
3315
+ const read = toJSONInput(source);
3316
+ if (read instanceof InputFailure) return read;
3317
+ if (typeof read !== "object" || read === null || Array.isArray(read)) return new InputFailure(`the body in ${source === "-" ? "standard input" : source} must be a JSON object, such as {"name": "Acme"}`);
3318
+ return read;
3319
+ }
3320
+ /** hasPath reports whether a body already holds a value at a field's path. */
3321
+ function hasPath(body, path) {
3322
+ let holder = body;
3323
+ for (const name of path) {
3324
+ if (typeof holder !== "object" || holder === null || !(name in holder)) return false;
3325
+ holder = holder[name];
3326
+ }
3327
+ return true;
3328
+ }
3329
+ /** toBody builds the request body from the --input body and the flags, a flag winning over its field. */
3330
+ function toBody(operation, flags, given) {
3331
+ const body = given ? structuredClone(given) : {};
3332
+ let hasField = given !== void 0;
3268
3333
  for (const flag of operation.bodyFlags) {
3269
3334
  const value = flags[toOptionKey(flag.name)];
3270
3335
  if (value === void 0) continue;
@@ -6122,5 +6187,5 @@ async function toReported(error, isJSON, resolved, level = "error") {
6122
6187
  //#endregion
6123
6188
  export { toCli, toParseExitCode, toReported, wasReportAttempted };
6124
6189
 
6125
- //# sourceMappingURL=program-B0vISolG.js.map
6126
- //# debugId=ab584cf3-443f-569e-8167-fa0cbab9ab68
6190
+ //# sourceMappingURL=program-ByvcKNri.js.map
6191
+ //# debugId=93b4bc8d-ebeb-59ca-967b-9fd10484e4a5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardfin/cli",
3
- "version": "0.1.0-dev.31",
3
+ "version": "0.1.0-dev.33",
4
4
  "description": "Command line interface for the Hardfin API",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Hardfin, Inc.",