@hardfin/cli 0.1.0-dev.30 → 0.1.0-dev.31
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 +13 -3
- package/dist/cli.js +4 -4
- package/dist/completion-tree.json +5 -0
- package/dist/{program-BZW_50HP.js → program-B0vISolG.js} +43 -22
- package/dist/{scrub-VtQmxap1.js → scrub-BN3gyBpR.js} +23 -14
- package/dist/{sentry-D5lKx0nm.js → sentry-CLaeAWNa.js} +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -159,8 +159,18 @@ hardfin status # what the CLI is configured with and signed in as
|
|
|
159
159
|
hardfin logout # forget this machine, and ask Hardfin to revoke it
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
An API key is still the way to run unattended. `HARDFIN_API_KEY
|
|
163
|
-
so CI and an agent sandbox need no browser.
|
|
162
|
+
An API key is still the way to run unattended. `--api-key`, or `HARDFIN_API_KEY`, wins over
|
|
163
|
+
a stored sign in, so CI and an agent sandbox need no browser. The flag beats the variable.
|
|
164
|
+
|
|
165
|
+
An API key is held to two rules before it is sent:
|
|
166
|
+
|
|
167
|
+
| Rule | Why |
|
|
168
|
+
| --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
169
|
+
| It goes only over https, or to this machine | Anything between here and the host could read it |
|
|
170
|
+
| A key you set, by flag or exported variable, never goes to an API URL that `config.local.json` or `.env` in the working directory chose | A repository you cloned could otherwise send your key to its own server |
|
|
171
|
+
|
|
172
|
+
A key and an API URL that come from the same folder's files still work together, which is
|
|
173
|
+
how a local build is set up. Pass `--api-url` to send a key you set to that folder's address.
|
|
164
174
|
|
|
165
175
|
### What login does
|
|
166
176
|
|
|
@@ -373,7 +383,7 @@ settings, and the one nearest the top wins.
|
|
|
373
383
|
|
|
374
384
|
| Layer | Where | Beats |
|
|
375
385
|
| ----------- | --------------------------------------------------------------------- | ---------------- |
|
|
376
|
-
| Flag | `--api-url`
|
|
386
|
+
| Flag | `--api-url`, `--api-key`, `--issuer-url` | everything below |
|
|
377
387
|
| Environment | an exported `HARDFIN_*` variable | the files below |
|
|
378
388
|
| Env file | `.env` in the working directory, or the file `HARDFIN_ENV_FILE` names | the config file |
|
|
379
389
|
| Config file | `config.local.json` in the working directory | the defaults |
|
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]="bb3026cb-ab06-5af0-9743-290b61ec91a3")}catch(e){}}();
|
|
4
4
|
import module from "node:module";
|
|
5
5
|
//#region src/cli.ts
|
|
6
6
|
module.enableCompileCache?.();
|
|
@@ -14,7 +14,7 @@ process.on("unhandledRejection", (error) => void toCrash(error));
|
|
|
14
14
|
try {
|
|
15
15
|
const answer = await toQuickAnswer(process.argv.slice(2));
|
|
16
16
|
if (answer === void 0) {
|
|
17
|
-
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-
|
|
17
|
+
const { toCli, toParseExitCode, wasReportAttempted } = await import("./program-B0vISolG.js");
|
|
18
18
|
try {
|
|
19
19
|
await toCli().parseAsync(process.argv);
|
|
20
20
|
} catch (error) {
|
|
@@ -33,7 +33,7 @@ async function toCrash(error) {
|
|
|
33
33
|
isCrashing = true;
|
|
34
34
|
process.stderr.write(`error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
35
35
|
try {
|
|
36
|
-
const { toReported } = await import("./program-
|
|
36
|
+
const { toReported } = await import("./program-B0vISolG.js");
|
|
37
37
|
await toReported(error, false, void 0, "fatal");
|
|
38
38
|
} finally {
|
|
39
39
|
process.exit(1);
|
|
@@ -51,4 +51,4 @@ async function toQuickAnswer(args) {
|
|
|
51
51
|
export {};
|
|
52
52
|
|
|
53
53
|
//# sourceMappingURL=cli.js.map
|
|
54
|
-
//# debugId=
|
|
54
|
+
//# debugId=bb3026cb-ab06-5af0-9743-290b61ec91a3
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"description": "The API to call, whose host also holds the authorization server",
|
|
6
6
|
"valueName": "url"
|
|
7
7
|
},
|
|
8
|
+
{
|
|
9
|
+
"name": "api-key",
|
|
10
|
+
"description": "An API key to call with, in place of HARDFIN_API_KEY or a sign in",
|
|
11
|
+
"valueName": "key"
|
|
12
|
+
},
|
|
8
13
|
{
|
|
9
14
|
"name": "issuer-url",
|
|
10
15
|
"description": "The authorization server, when it does not sit at the API's host",
|
|
@@ -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 { a as defineCommand, i as GLOBAL_FLAGS, r as ExitCode, t as toInvocation } from "./scrub-
|
|
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){}}();
|
|
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";
|
|
7
7
|
import { Command, CommanderError, Option } from "commander";
|
|
@@ -694,12 +694,15 @@ const held = /* @__PURE__ */ new Map();
|
|
|
694
694
|
* unattended caller sets it deliberately, and otherwise the stored refresh token buys an
|
|
695
695
|
* access token that lives only in memory.
|
|
696
696
|
*/
|
|
697
|
-
async function toRequestCredential(settings) {
|
|
698
|
-
if (settings.apiKey)
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
697
|
+
async function toRequestCredential(settings, sources) {
|
|
698
|
+
if (settings.apiKey) {
|
|
699
|
+
refuseForeignKey(settings, sources);
|
|
700
|
+
return {
|
|
701
|
+
header: "X-API-Key",
|
|
702
|
+
value: settings.apiKey,
|
|
703
|
+
kind: "api key"
|
|
704
|
+
};
|
|
705
|
+
}
|
|
703
706
|
refuseForeignHost(settings);
|
|
704
707
|
const stored = toCredential(settings.issuerUrl);
|
|
705
708
|
if (!stored) throw new NoCredential("not authenticated. Run hardfin login, or set HARDFIN_API_KEY");
|
|
@@ -775,6 +778,20 @@ function refuseForeignHost(settings) {
|
|
|
775
778
|
if (api.host !== issuer.host) throw new NoCredential(`this sign in belongs to ${issuer.host}, so it is not sent to ${api.host}. Set HARDFIN_API_KEY to call another host, or point --issuer-url at the server that signs you in`);
|
|
776
779
|
if (api.protocol !== "https:" && !isLoopback(api.hostname)) throw new NoCredential(`${settings.apiUrl} is not https, so a sign in is not sent to it`);
|
|
777
780
|
}
|
|
781
|
+
/** The layers a file in the working directory sets, which a cloned repository can carry. */
|
|
782
|
+
const DIRECTORY_SOURCES = /* @__PURE__ */ new Set(["config file", "env file"]);
|
|
783
|
+
/** refuseForeignKey keeps an API key off plain connections and off hosts a stray file chose. */
|
|
784
|
+
function refuseForeignKey(settings, sources) {
|
|
785
|
+
const api = toHost$1(settings.apiUrl);
|
|
786
|
+
if (api === void 0) throw new NoCredential(`${settings.apiUrl} is not a URL this CLI can call`);
|
|
787
|
+
if (api.protocol !== "https:" && !isLoopback(api.hostname)) throw new NoCredential(`Your API key was not sent, because ${api.host} does not use a secure connection.`);
|
|
788
|
+
const isKeyChosen = sources?.apiKey !== void 0 && !DIRECTORY_SOURCES.has(sources.apiKey);
|
|
789
|
+
const isUrlFromFolder = sources?.apiUrl !== void 0 && DIRECTORY_SOURCES.has(sources.apiUrl);
|
|
790
|
+
if (isKeyChosen && isUrlFromFolder) {
|
|
791
|
+
const file = sources.apiUrl === "config file" ? CONFIG_FILE : ".env";
|
|
792
|
+
throw new NoCredential(`Your API key was not sent to ${api.host}, because that address comes from a file in this folder, not from you.\nDetails: the API URL is set in ${file}. Pass --api-url to use it anyway`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
778
795
|
function toHost$1(url) {
|
|
779
796
|
try {
|
|
780
797
|
return new URL(url);
|
|
@@ -790,12 +807,15 @@ function isLoopback(hostname) {
|
|
|
790
807
|
* an access token this process already holds. An error report uses it, because a refresh
|
|
791
808
|
* cut short by the process ending would spend the refresh token and sign the person out.
|
|
792
809
|
*/
|
|
793
|
-
function toHeldCredential(settings) {
|
|
794
|
-
if (settings.apiKey)
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
810
|
+
function toHeldCredential(settings, sources) {
|
|
811
|
+
if (settings.apiKey) {
|
|
812
|
+
refuseForeignKey(settings, sources);
|
|
813
|
+
return {
|
|
814
|
+
header: "X-API-Key",
|
|
815
|
+
value: settings.apiKey,
|
|
816
|
+
kind: "api key"
|
|
817
|
+
};
|
|
818
|
+
}
|
|
799
819
|
const current = held.get(settings.issuerUrl);
|
|
800
820
|
if (!current || (current.expiresAt ?? 0) - EXPIRY_MARGIN_MS <= Date.now()) return;
|
|
801
821
|
return {
|
|
@@ -966,7 +986,7 @@ function toGuide(commands, version) {
|
|
|
966
986
|
"",
|
|
967
987
|
"## Before calling",
|
|
968
988
|
"",
|
|
969
|
-
"- Authenticate by setting `HARDFIN_API_KEY`, or by running `hardfin login`",
|
|
989
|
+
"- Authenticate with `--api-key`, by setting `HARDFIN_API_KEY`, or by running `hardfin login`",
|
|
970
990
|
"- Every command prints JSON on stdout and diagnostics on stderr",
|
|
971
991
|
"- Output is JSON whenever stdout is not a terminal, so no flag is needed when calling from code",
|
|
972
992
|
"",
|
|
@@ -1131,7 +1151,7 @@ async function runApi(input) {
|
|
|
1131
1151
|
try {
|
|
1132
1152
|
writeData((await request$1({
|
|
1133
1153
|
apiUrl: input.resolved.settings.apiUrl,
|
|
1134
|
-
credential: await toRequestCredential(input.resolved.settings),
|
|
1154
|
+
credential: await toRequestCredential(input.resolved.settings, input.resolved.sources),
|
|
1135
1155
|
method: toText(input.flags["method"] ?? "GET").toUpperCase(),
|
|
1136
1156
|
path,
|
|
1137
1157
|
query,
|
|
@@ -2543,7 +2563,7 @@ function toServerReport(metadata) {
|
|
|
2543
2563
|
async function toApiReport(resolved, options) {
|
|
2544
2564
|
const settings = resolved.settings;
|
|
2545
2565
|
try {
|
|
2546
|
-
const credential = options.isHeldOnly ? toHeldCredential(settings) : await toRequestCredential(settings);
|
|
2566
|
+
const credential = options.isHeldOnly ? toHeldCredential(settings, resolved.sources) : await toRequestCredential(settings, resolved.sources);
|
|
2547
2567
|
if (!credential) return {
|
|
2548
2568
|
checked: false,
|
|
2549
2569
|
url: settings.apiUrl,
|
|
@@ -2675,7 +2695,7 @@ async function report(error, occasion, resolved) {
|
|
|
2675
2695
|
const settings = resolved ?? toResolved();
|
|
2676
2696
|
const status = settings ? await toStatus(settings) : void 0;
|
|
2677
2697
|
const secrets = [settings?.settings.apiKey, settings ? toCredential(settings.settings.issuerUrl)?.refreshToken : void 0];
|
|
2678
|
-
return await (await import("./sentry-
|
|
2698
|
+
return await (await import("./sentry-CLaeAWNa.js")).send({
|
|
2679
2699
|
error,
|
|
2680
2700
|
occasion,
|
|
2681
2701
|
telemetry,
|
|
@@ -2867,7 +2887,7 @@ const REFUSED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
2867
2887
|
"mcp"
|
|
2868
2888
|
]);
|
|
2869
2889
|
/** The global options, which sit before a command and take a value of their own. */
|
|
2870
|
-
const GLOBAL_OPTIONS_WITH_VALUES =
|
|
2890
|
+
const GLOBAL_OPTIONS_WITH_VALUES = new Set(GLOBAL_FLAGS.filter((flag) => flag.valueName).map((flag) => `--${flag.name}`));
|
|
2871
2891
|
/**
|
|
2872
2892
|
* toCommandName reads which command a call names. A global option sits before it, so
|
|
2873
2893
|
* reading the first argument alone would let --issuer-url x logout through.
|
|
@@ -3153,7 +3173,7 @@ async function runOperation(operation, input) {
|
|
|
3153
3173
|
try {
|
|
3154
3174
|
const envelope = await request$1({
|
|
3155
3175
|
apiUrl: input.resolved.settings.apiUrl,
|
|
3156
|
-
credential: await toRequestCredential(input.resolved.settings),
|
|
3176
|
+
credential: await toRequestCredential(input.resolved.settings, input.resolved.sources),
|
|
3157
3177
|
method: operation.method,
|
|
3158
3178
|
path,
|
|
3159
3179
|
query: toQuery(operation, input.flags),
|
|
@@ -6060,6 +6080,7 @@ async function toExitCode(root, command, args, flags) {
|
|
|
6060
6080
|
try {
|
|
6061
6081
|
resolved = toSettings({
|
|
6062
6082
|
apiUrl: root.opts()["apiUrl"],
|
|
6083
|
+
apiKey: root.opts()["apiKey"],
|
|
6063
6084
|
issuerUrl: root.opts()["issuerUrl"]
|
|
6064
6085
|
});
|
|
6065
6086
|
return await command.run?.({
|
|
@@ -6101,5 +6122,5 @@ async function toReported(error, isJSON, resolved, level = "error") {
|
|
|
6101
6122
|
//#endregion
|
|
6102
6123
|
export { toCli, toParseExitCode, toReported, wasReportAttempted };
|
|
6103
6124
|
|
|
6104
|
-
//# sourceMappingURL=program-
|
|
6105
|
-
//# debugId=
|
|
6125
|
+
//# sourceMappingURL=program-B0vISolG.js.map
|
|
6126
|
+
//# debugId=ab584cf3-443f-569e-8167-fa0cbab9ab68
|
|
@@ -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]="3af5060a-b6fe-5f2e-9661-01f4460a7b04")}catch(e){}}();
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
//#region src/command/registry.ts
|
|
@@ -17,17 +17,26 @@ function defineCommand(command) {
|
|
|
17
17
|
return command;
|
|
18
18
|
}
|
|
19
19
|
/** GLOBAL_FLAGS are the options the CLI takes before any command. */
|
|
20
|
-
const GLOBAL_FLAGS = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const GLOBAL_FLAGS = [
|
|
21
|
+
{
|
|
22
|
+
name: "api-url",
|
|
23
|
+
description: "The API to call, whose host also holds the authorization server",
|
|
24
|
+
valueName: "url",
|
|
25
|
+
schema: z.string()
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "api-key",
|
|
29
|
+
description: "An API key to call with, in place of HARDFIN_API_KEY or a sign in",
|
|
30
|
+
valueName: "key",
|
|
31
|
+
schema: z.string()
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "issuer-url",
|
|
35
|
+
description: "The authorization server, when it does not sit at the API's host",
|
|
36
|
+
valueName: "url",
|
|
37
|
+
schema: z.string()
|
|
38
|
+
}
|
|
39
|
+
];
|
|
31
40
|
//#endregion
|
|
32
41
|
//#region src/report/scrub.ts
|
|
33
42
|
const REDACTED = "[redacted]";
|
|
@@ -109,5 +118,5 @@ function toFlag(flags, word) {
|
|
|
109
118
|
//#endregion
|
|
110
119
|
export { defineCommand as a, GLOBAL_FLAGS as i, toScrubbed as n, ExitCode as r, toInvocation as t };
|
|
111
120
|
|
|
112
|
-
//# sourceMappingURL=scrub-
|
|
113
|
-
//# debugId=
|
|
121
|
+
//# sourceMappingURL=scrub-BN3gyBpR.js.map
|
|
122
|
+
//# debugId=3af5060a-b6fe-5f2e-9661-01f4460a7b04
|
|
@@ -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 { n as toScrubbed } from "./scrub-
|
|
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]="853940c3-9e72-566b-ad70-9f043e6aba8d")}catch(e){}}();
|
|
4
|
+
import { n as toScrubbed } from "./scrub-BN3gyBpR.js";
|
|
5
5
|
import { dirname } from "node:path";
|
|
6
6
|
import { request } from "node:http";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -4385,5 +4385,5 @@ function toTags(delivery) {
|
|
|
4385
4385
|
//#endregion
|
|
4386
4386
|
export { send };
|
|
4387
4387
|
|
|
4388
|
-
//# sourceMappingURL=sentry-
|
|
4389
|
-
//# debugId=
|
|
4388
|
+
//# sourceMappingURL=sentry-CLaeAWNa.js.map
|
|
4389
|
+
//# debugId=853940c3-9e72-566b-ad70-9f043e6aba8d
|