@greatwait/cli 0.1.0-alpha.3 → 0.1.0-alpha.4
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/dist/args.d.ts +2 -2
- package/dist/args.js +33 -16
- package/dist/catalog.d.ts +196 -0
- package/dist/catalog.js +1453 -0
- package/dist/cli.d.ts +11 -2
- package/dist/cli.js +42 -31
- package/dist/commands.d.ts +33 -1
- package/dist/commands.js +199 -144
- package/dist/confirm.d.ts +33 -0
- package/dist/confirm.js +17 -0
- package/dist/exit.d.ts +9 -0
- package/dist/exit.js +19 -0
- package/dist/gw.d.ts +2 -0
- package/dist/gw.js +15 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/journey.d.ts +2 -2
- package/dist/journey.js +20 -4
- package/dist/main.js +6 -16
- package/dist/node-runtime.d.ts +15 -1
- package/dist/node-runtime.js +31 -1
- package/dist/oauth.d.ts +1 -1
- package/dist/oauth.js +15 -15
- package/dist/operate.d.ts +39 -0
- package/dist/operate.js +1394 -0
- package/dist/remediation.d.ts +35 -0
- package/dist/remediation.js +73 -0
- package/dist/runtime.d.ts +21 -1
- package/dist/session.js +5 -5
- package/dist/start.d.ts +15 -0
- package/dist/start.js +26 -0
- package/dist/support.d.ts +133 -0
- package/dist/support.js +270 -0
- package/package.json +5 -4
package/dist/args.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface ParsedArguments {
|
|
|
22
22
|
* both spellings — attached and separated — deserve the same answer. Neither
|
|
23
23
|
* repeats the value back.
|
|
24
24
|
*/
|
|
25
|
-
export declare
|
|
25
|
+
export declare function secretArgumentAdvice(programName: string): string;
|
|
26
26
|
export declare function parseArguments(argv: readonly string[],
|
|
27
27
|
/**
|
|
28
28
|
* The words that begin a command, so the rule below can tell a subcommand
|
|
@@ -35,7 +35,7 @@ export declare function parseArguments(argv: readonly string[],
|
|
|
35
35
|
* than imported, because `commands.ts` reads this module and the cycle would
|
|
36
36
|
* be the wrong way round.
|
|
37
37
|
*/
|
|
38
|
-
commandWords?: ReadonlySet<string
|
|
38
|
+
commandWords?: ReadonlySet<string>, programName?: string): ParsedArguments;
|
|
39
39
|
/**
|
|
40
40
|
* A duration written the way a person writes one: `30s`, `5m`, `2h`.
|
|
41
41
|
*
|
package/dist/args.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { CANONICAL_PROGRAM_NAME, COMMAND_CATALOG } from "./catalog.js";
|
|
1
2
|
import { CliError } from "./runtime.js";
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Options that take a value. Anything else is a boolean flag.
|
|
5
|
+
*
|
|
6
|
+
* **Derived from the catalog rather than listed here.** A second list was the
|
|
7
|
+
* first design and it is a drift a test does not notice: an option documented
|
|
8
|
+
* as `--limit <count>` that this set does not know is parsed as a flag, so the
|
|
9
|
+
* count becomes a positional argument and the command refuses it as unexpected
|
|
10
|
+
* — while the reference page says it is supported. One declaration, read by
|
|
11
|
+
* the parser and by the page, cannot disagree with itself.
|
|
12
|
+
*/
|
|
13
|
+
const VALUED_OPTIONS = new Set(COMMAND_CATALOG.flatMap((command) => command.options
|
|
14
|
+
.filter((option) => option.value !== undefined)
|
|
15
|
+
.map((option) => option.flag)));
|
|
14
16
|
/**
|
|
15
17
|
* Options that must never take a value, whatever they look like.
|
|
16
18
|
*
|
|
@@ -26,7 +28,9 @@ const SECRET_BEARING_FLAGS = new Set(["--api-key"]);
|
|
|
26
28
|
* both spellings — attached and separated — deserve the same answer. Neither
|
|
27
29
|
* repeats the value back.
|
|
28
30
|
*/
|
|
29
|
-
export
|
|
31
|
+
export function secretArgumentAdvice(programName) {
|
|
32
|
+
return `A credential given as an argument is visible to every process on this machine and is written to your shell history — treat the one you just typed as exposed and replace it. Run the command with a bare --api-key and paste the secret when asked, or pipe it in: printf %s "$KEY" | ${programName} login --api-key`;
|
|
33
|
+
}
|
|
30
34
|
export function parseArguments(argv,
|
|
31
35
|
/**
|
|
32
36
|
* The words that begin a command, so the rule below can tell a subcommand
|
|
@@ -39,7 +43,8 @@ export function parseArguments(argv,
|
|
|
39
43
|
* than imported, because `commands.ts` reads this module and the cycle would
|
|
40
44
|
* be the wrong way round.
|
|
41
45
|
*/
|
|
42
|
-
commandWords = new Set()) {
|
|
46
|
+
commandWords = new Set(), programName = CANONICAL_PROGRAM_NAME) {
|
|
47
|
+
const secretAdvice = secretArgumentAdvice(programName);
|
|
43
48
|
const command = [];
|
|
44
49
|
const options = {};
|
|
45
50
|
const booleans = new Set();
|
|
@@ -55,7 +60,7 @@ commandWords = new Set()) {
|
|
|
55
60
|
const name = equals === -1 ? token : token.slice(0, equals);
|
|
56
61
|
if (SECRET_BEARING_FLAGS.has(name)) {
|
|
57
62
|
if (equals !== -1) {
|
|
58
|
-
throw new CliError(`${name} does not take a value on the command line`, "usage",
|
|
63
|
+
throw new CliError(`${name} does not take a value on the command line`, "usage", secretAdvice);
|
|
59
64
|
}
|
|
60
65
|
// **The next token is refused here, rather than left to a command.**
|
|
61
66
|
// Leaving it to become a positional argument was the first design, and a
|
|
@@ -72,13 +77,25 @@ commandWords = new Set()) {
|
|
|
72
77
|
if (following !== undefined &&
|
|
73
78
|
!following.startsWith("--") &&
|
|
74
79
|
!commandWords.has(following)) {
|
|
75
|
-
throw new CliError(`${name} does not take a value on the command line`, "usage",
|
|
80
|
+
throw new CliError(`${name} does not take a value on the command line`, "usage", secretAdvice);
|
|
76
81
|
}
|
|
77
82
|
booleans.add(name);
|
|
78
83
|
index += 1;
|
|
79
84
|
continue;
|
|
80
85
|
}
|
|
81
86
|
if (VALUED_OPTIONS.has(name)) {
|
|
87
|
+
// **A second occurrence is refused rather than overwriting the first.**
|
|
88
|
+
// Two reviewers found the same defect through it: `--scope` documented
|
|
89
|
+
// itself as repeatable, the record this parses into is one value per
|
|
90
|
+
// name, and `--scope schedules:read --scope schedules:write` therefore
|
|
91
|
+
// asked for the second and silently dropped the first — surfacing much
|
|
92
|
+
// later as a `scope_missing` with nothing to connect it back. The same
|
|
93
|
+
// shape is worse for `--data` and `--limit`, where the dropped value is
|
|
94
|
+
// the one somebody meant. Refusing costs a retype; overwriting costs a
|
|
95
|
+
// debugging session, and always the wrong one.
|
|
96
|
+
if (Object.hasOwn(options, name)) {
|
|
97
|
+
throw new CliError(`${name} was given twice`, "usage", "Give it once. An option that takes a list takes the values in one argument, separated by spaces.");
|
|
98
|
+
}
|
|
82
99
|
if (equals !== -1) {
|
|
83
100
|
options[name] = token.slice(equals + 1);
|
|
84
101
|
index += 1;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { type OperationDefinition } from "@greatwait/contracts";
|
|
2
|
+
import { EXIT_CLASSES, EXIT_CLASS_MEANINGS, type ExitClass } from "./exit.js";
|
|
3
|
+
/**
|
|
4
|
+
* What the command line covers, declared once and checked against the registry.
|
|
5
|
+
*
|
|
6
|
+
* **This module is metadata and rules, and it imports no client.** The
|
|
7
|
+
* reference page is generated from it by a script that must not drag the
|
|
8
|
+
* generated API client into the contract generator's own dependency graph —
|
|
9
|
+
* the generator writes that client — so the declarations live apart from the
|
|
10
|
+
* implementations that use them. `commands.ts` binds each declaration to a
|
|
11
|
+
* function; `coverageProblems` is what refuses a declaration that no longer
|
|
12
|
+
* matches the registry.
|
|
13
|
+
*
|
|
14
|
+
* The story's criterion 2 asks that an added, removed, renamed, scope-changed,
|
|
15
|
+
* approval-changed, or unavailable operation cannot leave the CLI and its
|
|
16
|
+
* reference silently stale. That is this file: every operation is either
|
|
17
|
+
* covered by a command or omitted on a stated ground, the grounds that can be
|
|
18
|
+
* derived from the registry are checked against it rather than believed, and
|
|
19
|
+
* the scopes `login` asks for are the union of what the covered operations
|
|
20
|
+
* need rather than a list somebody maintains beside them.
|
|
21
|
+
*/
|
|
22
|
+
/** A positional argument, in the order the command reads them. */
|
|
23
|
+
export interface CommandPositional {
|
|
24
|
+
/** As it appears in the reference: `<schedule>`. */
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly summary: string;
|
|
27
|
+
readonly optional?: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** An option. `value` absent means it is a flag and takes none. */
|
|
30
|
+
export interface CommandOption {
|
|
31
|
+
readonly flag: string;
|
|
32
|
+
readonly summary: string;
|
|
33
|
+
readonly value?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface CommandDeclaration {
|
|
36
|
+
/** The words that select it, one or two. The dispatcher tries two first. */
|
|
37
|
+
readonly words: string;
|
|
38
|
+
/** The heading it appears under in help and in the reference. */
|
|
39
|
+
readonly group: string;
|
|
40
|
+
readonly summary: string;
|
|
41
|
+
readonly positionals: readonly CommandPositional[];
|
|
42
|
+
readonly options: readonly CommandOption[];
|
|
43
|
+
/** Registry operations this command reaches. Empty for a purely local one. */
|
|
44
|
+
readonly operations: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* Previews its exact scope and asks before acting.
|
|
47
|
+
*
|
|
48
|
+
* Required wherever `confirmationGround` says so and permitted anywhere else:
|
|
49
|
+
* a command may be more careful than the registry demands, and one of them
|
|
50
|
+
* is — rotating an endpoint signing key mints a secret shown once, which the
|
|
51
|
+
* registry describes but has no hint for.
|
|
52
|
+
*/
|
|
53
|
+
readonly confirms?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Why an operation has no command.
|
|
57
|
+
*
|
|
58
|
+
* Two of the three are properties of the registry entry, so the rule below
|
|
59
|
+
* checks them rather than taking the claim at face value — a ground that
|
|
60
|
+
* cannot be wrong is a ground nobody has to re-read when the entry changes.
|
|
61
|
+
* The third carries prose because no property of the entry states it.
|
|
62
|
+
*/
|
|
63
|
+
export type OmissionGround =
|
|
64
|
+
/** No principal this command line can hold may call it. */
|
|
65
|
+
"principal-unreachable"
|
|
66
|
+
/** Not served by the API at all. */
|
|
67
|
+
| "not-on-the-api"
|
|
68
|
+
/** A product decision, stated in `because`. */
|
|
69
|
+
| "declared";
|
|
70
|
+
export interface OperationOmission {
|
|
71
|
+
readonly operation: string;
|
|
72
|
+
readonly ground: OmissionGround;
|
|
73
|
+
readonly because: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The name every written instruction uses.
|
|
77
|
+
*
|
|
78
|
+
* The package installs two executables — `greatwait` and `gw` — and each states
|
|
79
|
+
* its own name, so what a person is told to type next is what they just typed.
|
|
80
|
+
* The *published* reference, the quickstarts and the first-value journey use
|
|
81
|
+
* this one, because a written instruction has to be followable by somebody who
|
|
82
|
+
* installed either.
|
|
83
|
+
*/
|
|
84
|
+
export declare const CANONICAL_PROGRAM_NAME = "greatwait";
|
|
85
|
+
/** The principals a command line can present. Never a browser session. */
|
|
86
|
+
export declare const CLI_PRINCIPALS: readonly ["api-credential", "oauth"];
|
|
87
|
+
/** Whether any principal this command line can hold may call it. */
|
|
88
|
+
export declare function reachableByCli(operation: OperationDefinition): boolean;
|
|
89
|
+
/** Whether the API serves it at all, as opposed to the contract mock alone. */
|
|
90
|
+
export declare function servedByApi(operation: OperationDefinition): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Why a command covering this operation must confirm before acting, if it must.
|
|
93
|
+
*
|
|
94
|
+
* **Derived from the registry's own hints rather than from a list here**, so an
|
|
95
|
+
* operation that becomes destructive tomorrow makes a command that does not ask
|
|
96
|
+
* fail this gate rather than quietly keep shipping.
|
|
97
|
+
*
|
|
98
|
+
* Two grounds, and the second is the one that took a correction. Every
|
|
99
|
+
* externally visible call is *not* a confirmation: `schedules create` reaches a
|
|
100
|
+
* receiver by design and is the first thing anybody runs, so demanding a
|
|
101
|
+
* keystroke there would put a prompt in the middle of the published first-value
|
|
102
|
+
* journey. What separates it from `occurrences redeliver` — which does deserve
|
|
103
|
+
* one — is that redelivery acts on something that already exists and sends it
|
|
104
|
+
* again. So the ground is an outward-facing change to an *addressed* object,
|
|
105
|
+
* which is exactly an operation with a path parameter.
|
|
106
|
+
*/
|
|
107
|
+
export declare function confirmationGround(operation: OperationDefinition): "destructive" | "outward-facing-change" | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* The request headers the registry declares for an operation.
|
|
110
|
+
*
|
|
111
|
+
* Read off the schema's own shape rather than listed here, because the point of
|
|
112
|
+
* the rules below is that a header appearing or disappearing changes what the
|
|
113
|
+
* command line must offer. `endpoints.disable` is the case that makes it worth
|
|
114
|
+
* checking: every other endpoint write accepts `If-Match` and that one does
|
|
115
|
+
* not, so a command offering the flag there would document a guard the service
|
|
116
|
+
* ignores — which reads to somebody operating carefully as protection they do
|
|
117
|
+
* not have.
|
|
118
|
+
*/
|
|
119
|
+
export declare function declaredRequestHeaders(operation: OperationDefinition): readonly string[];
|
|
120
|
+
/**
|
|
121
|
+
* Every command, in the order the reference and `--help` present them.
|
|
122
|
+
*
|
|
123
|
+
* Ordered by what somebody does rather than alphabetically: sign in, look
|
|
124
|
+
* around, set up a destination, schedule something, watch it, then recover from
|
|
125
|
+
* it. The groups are the headings.
|
|
126
|
+
*/
|
|
127
|
+
export declare const COMMAND_CATALOG: readonly CommandDeclaration[];
|
|
128
|
+
/**
|
|
129
|
+
* Every operation with no command, and why.
|
|
130
|
+
*
|
|
131
|
+
* The first six are the same fact stated six times: they need a browser
|
|
132
|
+
* session, and a command line holds an API credential or an OAuth grant. That
|
|
133
|
+
* is a property of the registry entry, so the rule checks it rather than
|
|
134
|
+
* believing this list — and it is also why the story says credential and grant
|
|
135
|
+
* operations appear "only where the current principal rules can support them
|
|
136
|
+
* honestly". Creating a credential from a credential is the escalation the
|
|
137
|
+
* control plane refuses on purpose.
|
|
138
|
+
*/
|
|
139
|
+
export declare const OPERATION_OMISSIONS: readonly OperationOmission[];
|
|
140
|
+
/**
|
|
141
|
+
* The one-line spelling of a command, used by `--help` and by the reference.
|
|
142
|
+
*
|
|
143
|
+
* **Rendered from the declaration in both places rather than written twice.**
|
|
144
|
+
* The story asks that the reference be generated or exhaustively checked
|
|
145
|
+
* against the executable; sharing the renderer is the version of that with
|
|
146
|
+
* nothing to check, because a page and a help screen produced by the same
|
|
147
|
+
* function cannot disagree about what a command takes.
|
|
148
|
+
*/
|
|
149
|
+
export declare function usageLine(command: CommandDeclaration, programName?: string): string;
|
|
150
|
+
/** The help for one command: what it does, what it takes, what each means. */
|
|
151
|
+
export declare function commandHelp(command: CommandDeclaration, programName?: string): string;
|
|
152
|
+
/** Every command, grouped, for the overview `--help` prints. */
|
|
153
|
+
export declare function overview(commands?: readonly CommandDeclaration[]): string;
|
|
154
|
+
export interface CoverageInputs {
|
|
155
|
+
readonly commands: readonly CommandDeclaration[];
|
|
156
|
+
readonly omissions: readonly OperationOmission[];
|
|
157
|
+
readonly operations: readonly OperationDefinition[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The scopes `login` asks a person to grant.
|
|
161
|
+
*
|
|
162
|
+
* **Derived from what the commands actually call.** A list maintained beside
|
|
163
|
+
* the catalog is a list that goes stale in the direction nobody notices: a
|
|
164
|
+
* command added for an operation needing a scope nobody requested fails at
|
|
165
|
+
* runtime with `scope_missing`, against a grant the person cannot widen without
|
|
166
|
+
* signing in again. Deriving it also means a widening shows up in the generated
|
|
167
|
+
* reference's diff, which is where a reviewer will see it.
|
|
168
|
+
*/
|
|
169
|
+
export declare function requestedScopes(inputs: CoverageInputs): readonly string[];
|
|
170
|
+
/**
|
|
171
|
+
* Everything wrong with the declarations, as sentences.
|
|
172
|
+
*
|
|
173
|
+
* A pure function of its inputs so the cases below can hand it a catalog that
|
|
174
|
+
* violates one rule at a time. Empty means the CLI, its reference and the
|
|
175
|
+
* registry agree.
|
|
176
|
+
*/
|
|
177
|
+
export declare function coverageProblems(inputs: CoverageInputs): string[];
|
|
178
|
+
/** The declarations as they stand, against the real registry. */
|
|
179
|
+
export declare function catalogInputs(): CoverageInputs;
|
|
180
|
+
/**
|
|
181
|
+
* The scopes `login` asks for, from what the commands call.
|
|
182
|
+
*
|
|
183
|
+
* Asking for everything the server offers would be asking a person to grant a
|
|
184
|
+
* command line permissions it has no code to use; asking for less than the
|
|
185
|
+
* commands need is a `scope_missing` on a grant they cannot widen without
|
|
186
|
+
* signing in again. Neither is a list anybody maintains: this is that union.
|
|
187
|
+
*/
|
|
188
|
+
export declare const REQUESTED_SCOPES: readonly string[];
|
|
189
|
+
/**
|
|
190
|
+
* Re-exported so the reference generator has one import.
|
|
191
|
+
*
|
|
192
|
+
* The page publishes the exit numbers and what they mean, and both come from
|
|
193
|
+
* the module the runtime reads. A generator reaching into `./exit` separately
|
|
194
|
+
* would work; this keeps the catalog the single thing a projection depends on.
|
|
195
|
+
*/
|
|
196
|
+
export { EXIT_CLASSES, EXIT_CLASS_MEANINGS, type ExitClass };
|