@hardfin/cli 0.1.1-dev.49 → 0.1.1-dev.51
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 +33 -0
- package/dist/cli.js +4 -4
- package/dist/completion-tree.json +6 -2
- package/dist/{program-Da7dZSnW.js → program-CXKDQ-E9.js} +137 -34
- package/package.json +1 -1
- package/skills/hardfin-cli/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -129,6 +129,35 @@ An agent branches on `code`. A code is never renamed and never changes meaning,
|
|
|
129
129
|
message may be reworded at any time. `hardfin agent-guide` lists every code with what it
|
|
130
130
|
means, and `src/output/failure.ts` defines them.
|
|
131
131
|
|
|
132
|
+
### Reading the commands from a program
|
|
133
|
+
|
|
134
|
+
`hardfin agent-guide` describes the CLI to an agent, as Markdown or as JSON. Name a command to
|
|
135
|
+
get that command alone, which is a few kilobytes where the whole guide is two hundred.
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
hardfin agent-guide # every command, as Markdown
|
|
139
|
+
hardfin agent-guide --json # every command, as JSON
|
|
140
|
+
hardfin agent-guide --json customer create # one command, and what is nested under it
|
|
141
|
+
hardfin agent-guide --json customer create | jq -r '.commands[0].flags[] | select(.required) | .name'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The JSON holds `cli`, `apiVersion`, `exitCodes`, `errorCodes`, `globalFlags` and `commands`.
|
|
145
|
+
Each command names its `path`, its `endpoint`, whether it `runs`, and its flags.
|
|
146
|
+
|
|
147
|
+
| A flag's | Holds |
|
|
148
|
+
| ------------ | --------------------------------------------------------------------------- |
|
|
149
|
+
| `required` | Present when the command fails without it |
|
|
150
|
+
| `schema` | The `type`, and the `enum`, `format`, `minimum` or `maximum` that narrow it |
|
|
151
|
+
| `sends` | The query parameter, or the path of the body field, the value goes to |
|
|
152
|
+
| `clearable` | Present when `--unset` can clear the field |
|
|
153
|
+
| `repeatable` | Present when the flag is given once per value |
|
|
154
|
+
| `negatable` | Present when a boolean also takes a `--no-` form |
|
|
155
|
+
| `element` | The fields one value carries as `key=value`, for an array of objects |
|
|
156
|
+
| `default` | The value used when the flag is not given |
|
|
157
|
+
|
|
158
|
+
A part that is absent means no. The description is read from the same declarations the parser
|
|
159
|
+
enforces, so it cannot say one thing while the command does another.
|
|
160
|
+
|
|
132
161
|
### Overrides
|
|
133
162
|
|
|
134
163
|
Two endpoints sometimes want one name. `DELETE /asset/{assetKey}/ownership` and
|
|
@@ -480,6 +509,10 @@ Nothing secret is printed. An API key and a refresh token are each reported as a
|
|
|
480
509
|
`sha256:` fingerprint, which identifies a credential across two machines without disclosing
|
|
481
510
|
it.
|
|
482
511
|
|
|
512
|
+
`authorizationServer.scopesOffered` is every scope the server can grant to anyone. The scopes
|
|
513
|
+
this sign in was granted are `api.accessTokenScopes`, and `api.identity.scopes` is what the
|
|
514
|
+
API says the credential holds.
|
|
515
|
+
|
|
483
516
|
`api.authenticated` is `true` only when the API answered who the credential is. A 401 or a
|
|
484
517
|
403 makes it `false`, and `hardfin status` then exits with code 4. It is `null` when the API
|
|
485
518
|
could not be asked, because no answer says nothing about the credential.
|
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]="d68fe726-8a0c-5521-8d64-6a70ad73fa4f")}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-CXKDQ-E9.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
|
const isJSON = process.argv.includes("--json") || !process.stdout.isTTY;
|
|
35
35
|
try {
|
|
36
|
-
const { toReported, writeCrash } = await import("./program-
|
|
36
|
+
const { toReported, writeCrash } = await import("./program-CXKDQ-E9.js");
|
|
37
37
|
writeCrash(error, isJSON);
|
|
38
38
|
await toReported(error, isJSON, void 0, "fatal");
|
|
39
39
|
} catch {
|
|
@@ -54,4 +54,4 @@ async function toQuickAnswer(args) {
|
|
|
54
54
|
export {};
|
|
55
55
|
|
|
56
56
|
//# sourceMappingURL=cli.js.map
|
|
57
|
-
//# debugId=
|
|
57
|
+
//# debugId=d68fe726-8a0c-5521-8d64-6a70ad73fa4f
|
|
@@ -2610,10 +2610,14 @@
|
|
|
2610
2610
|
"flags": [
|
|
2611
2611
|
{
|
|
2612
2612
|
"name": "json",
|
|
2613
|
-
"description": "Print
|
|
2613
|
+
"description": "Print every command as JSON, with what each flag takes, instead of Markdown"
|
|
2614
|
+
}
|
|
2615
|
+
],
|
|
2616
|
+
"arguments": [
|
|
2617
|
+
{
|
|
2618
|
+
"variadic": true
|
|
2614
2619
|
}
|
|
2615
2620
|
],
|
|
2616
|
-
"arguments": [],
|
|
2617
2621
|
"subcommands": []
|
|
2618
2622
|
},
|
|
2619
2623
|
{
|
|
@@ -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]="f3469c6b-5453-551f-9090-b9d18cbba487")}catch(e){}}();
|
|
4
4
|
import { a as defineCommand, i as GLOBAL_FLAGS, r as ExitCode, t as toInvocation } from "./scrub-BZ0egWxC.js";
|
|
5
5
|
import { n as toAnswer$1, r as toCompletion, t as PATH_VALUES } from "./completion-tree-DLC6ARLR.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -1296,23 +1296,47 @@ const agentGuideCommand = defineCommand({
|
|
|
1296
1296
|
name: "agent-guide",
|
|
1297
1297
|
summary: "Print instructions an agent can follow to use this CLI",
|
|
1298
1298
|
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.",
|
|
1299
|
-
arguments: [
|
|
1299
|
+
arguments: [{
|
|
1300
|
+
name: "command",
|
|
1301
|
+
description: "The words of one command, such as customer create, to describe it and what is nested under it alone",
|
|
1302
|
+
required: false,
|
|
1303
|
+
variadic: true
|
|
1304
|
+
}],
|
|
1300
1305
|
flags: [{
|
|
1301
1306
|
name: "json",
|
|
1302
|
-
description: "Print
|
|
1307
|
+
description: "Print every command as JSON, with what each flag takes, instead of Markdown",
|
|
1303
1308
|
schema: z.boolean().optional()
|
|
1304
1309
|
}],
|
|
1305
|
-
examples: [
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1310
|
+
examples: [
|
|
1311
|
+
{
|
|
1312
|
+
description: "Write a skill file",
|
|
1313
|
+
command: "hardfin agent-guide > SKILL.md"
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
description: "Read what every flag takes",
|
|
1317
|
+
command: "hardfin agent-guide --json"
|
|
1318
|
+
},
|
|
1319
|
+
{
|
|
1320
|
+
description: "Read what one command takes, and nothing else",
|
|
1321
|
+
command: "hardfin agent-guide --json customer create"
|
|
1322
|
+
},
|
|
1323
|
+
{
|
|
1324
|
+
description: "List the flags one command needs",
|
|
1325
|
+
command: "hardfin agent-guide --json customer create | jq -r '.commands[0].flags[] | select(.required) | .name'"
|
|
1326
|
+
}
|
|
1327
|
+
],
|
|
1312
1328
|
run: runAgentGuide
|
|
1313
1329
|
});
|
|
1330
|
+
/** EXIT_CODES is what each exit code means, which both forms of the guide print. */
|
|
1331
|
+
const EXIT_CODES = {
|
|
1332
|
+
[ExitCode.OK]: "The command succeeded",
|
|
1333
|
+
[ExitCode.ERROR]: "The command failed",
|
|
1334
|
+
[ExitCode.USAGE]: "The command was called wrongly",
|
|
1335
|
+
[ExitCode.NOT_AUTHENTICATED]: "No usable credential",
|
|
1336
|
+
[ExitCode.INTERRUPTED]: "The person pressed Ctrl-C"
|
|
1337
|
+
};
|
|
1314
1338
|
/** toGuide renders the registry as the Markdown an agent reads. */
|
|
1315
|
-
function toGuide(commands, version) {
|
|
1339
|
+
function toGuide(commands, version, parents = []) {
|
|
1316
1340
|
const lines = [
|
|
1317
1341
|
"# Hardfin CLI",
|
|
1318
1342
|
"",
|
|
@@ -1330,11 +1354,7 @@ function toGuide(commands, version) {
|
|
|
1330
1354
|
"",
|
|
1331
1355
|
"| Code | Means |",
|
|
1332
1356
|
"| --- | --- |",
|
|
1333
|
-
|
|
1334
|
-
"| 1 | The command failed |",
|
|
1335
|
-
"| 2 | The command was called wrongly |",
|
|
1336
|
-
"| 4 | No usable credential |",
|
|
1337
|
-
"| 130 | The person pressed Ctrl-C |",
|
|
1357
|
+
...Object.entries(EXIT_CODES).map(([code, meaning]) => `| ${code} | ${meaning} |`),
|
|
1338
1358
|
"",
|
|
1339
1359
|
"## Error codes",
|
|
1340
1360
|
"",
|
|
@@ -1347,7 +1367,7 @@ function toGuide(commands, version) {
|
|
|
1347
1367
|
"## Commands",
|
|
1348
1368
|
""
|
|
1349
1369
|
];
|
|
1350
|
-
for (const command of commands.filter((command) => !command.hidden)) lines.push(...toCommandLines(command,
|
|
1370
|
+
for (const command of commands.filter((command) => !command.hidden)) lines.push(...toCommandLines(command, parents));
|
|
1351
1371
|
return lines.join("\n");
|
|
1352
1372
|
}
|
|
1353
1373
|
/** toCommandLines describes one command and everything nested under it. */
|
|
@@ -1365,10 +1385,11 @@ function toCommandLines(command, parents) {
|
|
|
1365
1385
|
lines.push("");
|
|
1366
1386
|
}
|
|
1367
1387
|
if (command.flags.length > 0) {
|
|
1368
|
-
lines.push("| Flag | Takes | Does |", "| --- | --- | --- |");
|
|
1388
|
+
lines.push("| Flag | Required | Takes | Does |", "| --- | --- | --- | --- |");
|
|
1369
1389
|
for (const flag of command.flags) {
|
|
1370
1390
|
const name = flag.short ? `-${flag.short}, --${flag.name}` : `--${flag.name}`;
|
|
1371
|
-
|
|
1391
|
+
const described = toDescribed(flag);
|
|
1392
|
+
lines.push(`| \`${name}\` | ${described.required ? "yes" : "no"} | ${toTaken(described)} | ${flag.description} |`);
|
|
1372
1393
|
}
|
|
1373
1394
|
lines.push("");
|
|
1374
1395
|
}
|
|
@@ -1376,29 +1397,111 @@ function toCommandLines(command, parents) {
|
|
|
1376
1397
|
for (const subcommand of command.subcommands ?? []) lines.push(...toCommandLines(subcommand, path));
|
|
1377
1398
|
return lines;
|
|
1378
1399
|
}
|
|
1400
|
+
/** toTaken says in a few words what a flag takes, naming the values when only some are allowed. */
|
|
1401
|
+
function toTaken(flag) {
|
|
1402
|
+
const values = flag.schema.enum ?? flag.schema.items?.enum;
|
|
1403
|
+
if (values) return `one of ${values.map((value) => `\`${value}\``).join(", ")}`;
|
|
1404
|
+
if (flag.element) return flag.element.map((field) => `${field}=`).join(",");
|
|
1405
|
+
return flag.valueName ?? "nothing";
|
|
1406
|
+
}
|
|
1379
1407
|
async function runAgentGuide(input) {
|
|
1408
|
+
const found = toNamed(input.commands, input.args);
|
|
1409
|
+
if (found === void 0) {
|
|
1410
|
+
writeFailure("invalid_arguments", `no command is called ${input.args.join(" ")}. Run hardfin --help for the list`, input.isJSON);
|
|
1411
|
+
return ExitCode.USAGE;
|
|
1412
|
+
}
|
|
1380
1413
|
if (input.flags["json"] === true) {
|
|
1381
|
-
writeData(
|
|
1414
|
+
writeData(toDescription$1(found.commands, version$1, found.parents));
|
|
1382
1415
|
return ExitCode.OK;
|
|
1383
1416
|
}
|
|
1384
|
-
writeData(toGuide(
|
|
1417
|
+
writeData(toGuide(found.commands, version$1, found.parents));
|
|
1385
1418
|
return ExitCode.OK;
|
|
1386
1419
|
}
|
|
1387
|
-
|
|
1420
|
+
/** toNamed finds the one command a caller named by its words, or every command when none was named. */
|
|
1421
|
+
function toNamed(commands, words) {
|
|
1422
|
+
let level = commands.filter((command) => !command.hidden);
|
|
1423
|
+
let found;
|
|
1424
|
+
for (const word of words) {
|
|
1425
|
+
found = level.find((command) => command.name === word);
|
|
1426
|
+
if (!found) return;
|
|
1427
|
+
level = (found.subcommands ?? []).filter((command) => !command.hidden);
|
|
1428
|
+
}
|
|
1429
|
+
return found ? {
|
|
1430
|
+
commands: [found],
|
|
1431
|
+
parents: words.slice(0, -1)
|
|
1432
|
+
} : {
|
|
1433
|
+
commands: level,
|
|
1434
|
+
parents: []
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
/** toDescribed reads one flag from its declaration, which is also what the parser enforces. */
|
|
1438
|
+
function toDescribed(flag) {
|
|
1439
|
+
const sent = flag;
|
|
1440
|
+
const described = {
|
|
1441
|
+
name: flag.name,
|
|
1442
|
+
short: flag.short,
|
|
1443
|
+
description: flag.description,
|
|
1444
|
+
valueName: flag.valueName,
|
|
1445
|
+
required: sent.required === true,
|
|
1446
|
+
repeatable: flag.repeatable === true,
|
|
1447
|
+
negatable: flag.negatable === true,
|
|
1448
|
+
clearable: sent.nullable === true,
|
|
1449
|
+
default: flag.defaultValue,
|
|
1450
|
+
schema: toFlagSchema(flag),
|
|
1451
|
+
sends: sent.queryName !== void 0 ? { query: sent.queryName } : sent.jsonPath !== void 0 ? { body: sent.jsonPath } : void 0,
|
|
1452
|
+
element: sent.element
|
|
1453
|
+
};
|
|
1454
|
+
return Object.fromEntries(Object.entries(described).filter(([, value]) => value !== void 0 && value !== false));
|
|
1455
|
+
}
|
|
1456
|
+
/** The bounds zod states for any whole number, which say nothing about a particular flag. */
|
|
1457
|
+
const ANY_INTEGER = [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER];
|
|
1458
|
+
/** toFlagSchema states what a value must be, keeping only the parts that narrow it. */
|
|
1459
|
+
function toFlagSchema(flag) {
|
|
1460
|
+
try {
|
|
1461
|
+
return toNarrowed(z.toJSONSchema(flag.schema, {
|
|
1462
|
+
io: "output",
|
|
1463
|
+
unrepresentable: "any"
|
|
1464
|
+
}));
|
|
1465
|
+
} catch {
|
|
1466
|
+
return {};
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
function toNarrowed(schema) {
|
|
1470
|
+
const narrowed = {};
|
|
1471
|
+
if (typeof schema["type"] === "string") narrowed.type = schema["type"];
|
|
1472
|
+
if (typeof schema["format"] === "string") narrowed.format = schema["format"];
|
|
1473
|
+
if (Array.isArray(schema["enum"])) narrowed.enum = schema["enum"].map(String);
|
|
1474
|
+
for (const bound of ["minimum", "maximum"]) {
|
|
1475
|
+
const value = schema[bound];
|
|
1476
|
+
if (typeof value === "number" && !ANY_INTEGER.includes(value)) narrowed[bound] = value;
|
|
1477
|
+
}
|
|
1478
|
+
if (typeof schema["items"] === "object" && schema["items"] !== null) narrowed.items = toNarrowed(schema["items"]);
|
|
1479
|
+
return narrowed;
|
|
1480
|
+
}
|
|
1481
|
+
/** toDescription is the whole CLI as data: the codes, and every command with what it takes. */
|
|
1482
|
+
function toDescription$1(commands, version, parents = []) {
|
|
1483
|
+
return {
|
|
1484
|
+
cli: version,
|
|
1485
|
+
apiVersion: API_VERSION,
|
|
1486
|
+
exitCodes: EXIT_CODES,
|
|
1487
|
+
errorCodes: FAILURE_CODES,
|
|
1488
|
+
globalFlags: GLOBAL_FLAGS.map(toDescribed),
|
|
1489
|
+
commands: commands.filter((command) => !command.hidden).map((command) => toSummary(command, parents))
|
|
1490
|
+
};
|
|
1491
|
+
}
|
|
1492
|
+
function toSummary(command, parents) {
|
|
1493
|
+
const path = [...parents, command.name];
|
|
1388
1494
|
return {
|
|
1389
1495
|
name: command.name,
|
|
1496
|
+
path,
|
|
1390
1497
|
summary: command.summary,
|
|
1391
1498
|
description: command.description ?? command.summary,
|
|
1499
|
+
runs: command.run !== void 0,
|
|
1500
|
+
endpoint: command.endpoint ?? null,
|
|
1392
1501
|
arguments: command.arguments,
|
|
1393
|
-
flags: command.flags.map(
|
|
1394
|
-
name: flag.name,
|
|
1395
|
-
short: flag.short ?? null,
|
|
1396
|
-
description: flag.description,
|
|
1397
|
-
valueName: flag.valueName ?? null,
|
|
1398
|
-
repeatable: flag.repeatable ?? false
|
|
1399
|
-
})),
|
|
1502
|
+
flags: command.flags.map(toDescribed),
|
|
1400
1503
|
examples: command.examples,
|
|
1401
|
-
subcommands: command.subcommands?.filter((entry) => !entry.hidden).map(toSummary)
|
|
1504
|
+
subcommands: command.subcommands?.filter((entry) => !entry.hidden).map((entry) => toSummary(entry, path))
|
|
1402
1505
|
};
|
|
1403
1506
|
}
|
|
1404
1507
|
//#endregion
|
|
@@ -3456,8 +3559,8 @@ function toServerReport(metadata) {
|
|
|
3456
3559
|
tokenEndpoint: metadata.token_endpoint,
|
|
3457
3560
|
revocationEndpoint: metadata.revocation_endpoint ?? null,
|
|
3458
3561
|
deviceEndpoint: metadata.device_authorization_endpoint ?? null,
|
|
3459
|
-
|
|
3460
|
-
|
|
3562
|
+
grantTypesOffered: metadata.grant_types_supported ?? [],
|
|
3563
|
+
scopesOffered: metadata.scopes_supported ?? []
|
|
3461
3564
|
};
|
|
3462
3565
|
}
|
|
3463
3566
|
/** The statuses the API answers when it will not take the credential. */
|
|
@@ -6934,5 +7037,5 @@ async function toReported(error, isJSON, resolved, level = "error") {
|
|
|
6934
7037
|
//#endregion
|
|
6935
7038
|
export { toCli, toParseExitCode, toReported, wasReportAttempted, writeCrash };
|
|
6936
7039
|
|
|
6937
|
-
//# sourceMappingURL=program-
|
|
6938
|
-
//# debugId=
|
|
7040
|
+
//# sourceMappingURL=program-CXKDQ-E9.js.map
|
|
7041
|
+
//# debugId=f3469c6b-5453-551f-9090-b9d18cbba487
|
package/package.json
CHANGED
|
@@ -55,6 +55,7 @@ hardfin --help # the nouns
|
|
|
55
55
|
hardfin asset --help # what you can do with assets
|
|
56
56
|
hardfin asset create --help # every flag that command takes
|
|
57
57
|
hardfin agent-guide # every command, flag, exit code and error code, as Markdown
|
|
58
|
+
hardfin agent-guide --json asset create # one command as JSON: required flags, allowed values, the endpoint
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
| Noun | Holds |
|