@norskvideo/ctl-commands 0.1.8 → 0.1.10
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/commands.d.ts +44 -143
- package/commands.js +71 -51
- package/format.d.ts +1 -1
- package/format.js +10 -106
- package/package.json +1 -1
package/commands.d.ts
CHANGED
|
@@ -3,6 +3,16 @@ import type { CertSource, HardwareProfile, NetworkMode, ProductNetworkMode, Rest
|
|
|
3
3
|
export type LeafCmd<F extends (...args: never[]) => unknown> = F & {
|
|
4
4
|
readonly __sig: string;
|
|
5
5
|
};
|
|
6
|
+
/** What a leaf declares about itself, and all `toArgv` needs to render it. */
|
|
7
|
+
export interface CommandSpec {
|
|
8
|
+
readonly kind: string;
|
|
9
|
+
/** argv path segments, e.g. `["instance", "launch-template"]`. */
|
|
10
|
+
readonly path: readonly string[];
|
|
11
|
+
/** Command fields emitted as bare positionals, in argv order. */
|
|
12
|
+
readonly positionals: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/** Every leaf's spec, by kind. */
|
|
15
|
+
export declare const commandSpecs: ReadonlyMap<string, CommandSpec>;
|
|
6
16
|
export interface RelaunchOpts {
|
|
7
17
|
/** Raw `SPEC` / `SPEC@service` flag values; parsed into HostPort[] by the coercer. */
|
|
8
18
|
hostPorts?: string[];
|
|
@@ -80,6 +90,16 @@ export interface SideloadFetchOpts {
|
|
|
80
90
|
export interface TemplateImportOpts {
|
|
81
91
|
name: string;
|
|
82
92
|
}
|
|
93
|
+
export interface TemplateBuildOpts {
|
|
94
|
+
/** Registered product to build from; its `/api/product-template` renders the tar. */
|
|
95
|
+
product: string;
|
|
96
|
+
/** Path to a JSON file of the product's template inputs, forwarded verbatim. */
|
|
97
|
+
input: string;
|
|
98
|
+
/** Overwrite an existing template of this name instead of failing. What makes
|
|
99
|
+
* a re-run of a bring-up script idempotent without removing the only copy
|
|
100
|
+
* first. Refused while an instance references the template. */
|
|
101
|
+
replace?: boolean;
|
|
102
|
+
}
|
|
83
103
|
export interface InstanceLaunchTemplateOpts {
|
|
84
104
|
template: string;
|
|
85
105
|
param?: string[];
|
|
@@ -103,148 +123,12 @@ export interface InstanceLaunchTemplateOpts {
|
|
|
103
123
|
memoryLimit?: string;
|
|
104
124
|
restartPolicy?: RestartPolicy;
|
|
105
125
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
kind: "fetch.mkcert";
|
|
113
|
-
} | {
|
|
114
|
-
kind: "get";
|
|
115
|
-
resource: GetResource;
|
|
116
|
-
} | {
|
|
117
|
-
kind: "init";
|
|
118
|
-
opts: InitOpts;
|
|
119
|
-
} | {
|
|
120
|
-
kind: "inspect";
|
|
121
|
-
} | {
|
|
122
|
-
kind: "instance.delete";
|
|
123
|
-
id: string;
|
|
124
|
-
opts: InstanceDeleteOpts;
|
|
125
|
-
} | {
|
|
126
|
-
kind: "instance.describe";
|
|
127
|
-
id: string;
|
|
128
|
-
} | {
|
|
129
|
-
kind: "instance.restart";
|
|
130
|
-
id: string;
|
|
131
|
-
} | {
|
|
132
|
-
kind: "instance.stop";
|
|
133
|
-
id: string;
|
|
134
|
-
} | {
|
|
135
|
-
kind: "instance.start";
|
|
136
|
-
id: string;
|
|
137
|
-
} | {
|
|
138
|
-
kind: "instance.relaunch";
|
|
139
|
-
id: string;
|
|
140
|
-
opts: RelaunchOpts;
|
|
141
|
-
} | {
|
|
142
|
-
kind: "instance.list";
|
|
143
|
-
} | {
|
|
144
|
-
kind: "instance.exists";
|
|
145
|
-
id: string;
|
|
146
|
-
} | {
|
|
147
|
-
kind: "instance.launchTemplate";
|
|
148
|
-
id: string;
|
|
149
|
-
opts: InstanceLaunchTemplateOpts;
|
|
150
|
-
} | {
|
|
151
|
-
kind: "orphaned-dirs.list";
|
|
152
|
-
opts: OrphanListOpts;
|
|
153
|
-
} | {
|
|
154
|
-
kind: "orphaned-dirs.clear";
|
|
155
|
-
id: string;
|
|
156
|
-
} | {
|
|
157
|
-
kind: "shm.list";
|
|
158
|
-
} | {
|
|
159
|
-
kind: "sideload.status";
|
|
160
|
-
} | {
|
|
161
|
-
kind: "sideload.fetch";
|
|
162
|
-
opts: SideloadFetchOpts;
|
|
163
|
-
} | {
|
|
164
|
-
kind: "template.list";
|
|
165
|
-
} | {
|
|
166
|
-
kind: "template.show";
|
|
167
|
-
name: string;
|
|
168
|
-
} | {
|
|
169
|
-
kind: "template.default";
|
|
170
|
-
product: string;
|
|
171
|
-
} | {
|
|
172
|
-
kind: "template.import";
|
|
173
|
-
file: string;
|
|
174
|
-
opts: TemplateImportOpts;
|
|
175
|
-
} | {
|
|
176
|
-
kind: "template.refresh";
|
|
177
|
-
name: string;
|
|
178
|
-
} | {
|
|
179
|
-
kind: "template.remove";
|
|
180
|
-
name: string;
|
|
181
|
-
} | {
|
|
182
|
-
kind: "product.add";
|
|
183
|
-
opts: ProductAddOpts;
|
|
184
|
-
} | {
|
|
185
|
-
kind: "product.list";
|
|
186
|
-
} | {
|
|
187
|
-
kind: "product.exists";
|
|
188
|
-
name: string;
|
|
189
|
-
} | {
|
|
190
|
-
kind: "product.remove";
|
|
191
|
-
name: string;
|
|
192
|
-
} | {
|
|
193
|
-
kind: "product.reload";
|
|
194
|
-
name: string;
|
|
195
|
-
} | {
|
|
196
|
-
kind: "product.pull";
|
|
197
|
-
name: string;
|
|
198
|
-
opts: ProductPullOpts;
|
|
199
|
-
} | {
|
|
200
|
-
kind: "mcp";
|
|
201
|
-
} | {
|
|
202
|
-
kind: "proxy.pull";
|
|
203
|
-
} | {
|
|
204
|
-
kind: "proxy.start";
|
|
205
|
-
} | {
|
|
206
|
-
kind: "proxy.stop";
|
|
207
|
-
} | {
|
|
208
|
-
kind: "proxy.reload";
|
|
209
|
-
} | {
|
|
210
|
-
kind: "proxy.status";
|
|
211
|
-
} | {
|
|
212
|
-
kind: "proxy.logs";
|
|
213
|
-
opts: ProxyLogsOpts;
|
|
214
|
-
} | {
|
|
215
|
-
kind: "serve";
|
|
216
|
-
} | {
|
|
217
|
-
kind: "source.start";
|
|
218
|
-
instanceId: string;
|
|
219
|
-
preset: string;
|
|
220
|
-
opts: SourceStartOpts;
|
|
221
|
-
} | {
|
|
222
|
-
kind: "source.stop";
|
|
223
|
-
instanceId: string;
|
|
224
|
-
name: string;
|
|
225
|
-
} | {
|
|
226
|
-
kind: "source.list";
|
|
227
|
-
} | {
|
|
228
|
-
kind: "source.presets";
|
|
229
|
-
} | {
|
|
230
|
-
kind: "stats";
|
|
231
|
-
opts: StatsOpts;
|
|
232
|
-
} | {
|
|
233
|
-
kind: "restart";
|
|
234
|
-
} | {
|
|
235
|
-
kind: "shutdown";
|
|
236
|
-
opts: ShutdownOpts;
|
|
237
|
-
} | {
|
|
238
|
-
kind: "upgrade";
|
|
239
|
-
opts: UpgradeOpts;
|
|
240
|
-
} | {
|
|
241
|
-
kind: "user.set";
|
|
242
|
-
name: string;
|
|
243
|
-
opts: UserSetOpts;
|
|
244
|
-
} | {
|
|
245
|
-
kind: "user.delete";
|
|
246
|
-
name: string;
|
|
247
|
-
};
|
|
126
|
+
/** Runtime source for the `get` vocabulary — the CLI's yargs `choices` and
|
|
127
|
+
* help text derive from it, so the type and the accepted words cannot drift.
|
|
128
|
+
* (`versions` was removed when core's Studio version surface went away; its
|
|
129
|
+
* route no longer exists.) */
|
|
130
|
+
export declare const GET_RESOURCES: readonly ["hardware", "status", "prerequisites"];
|
|
131
|
+
export type GetResource = (typeof GET_RESOURCES)[number];
|
|
248
132
|
export declare const commands: {
|
|
249
133
|
readonly config: {
|
|
250
134
|
readonly set: LeafCmd<() => {
|
|
@@ -261,7 +145,7 @@ export declare const commands: {
|
|
|
261
145
|
};
|
|
262
146
|
readonly get: LeafCmd<(resource: GetResource) => {
|
|
263
147
|
readonly kind: "get";
|
|
264
|
-
readonly resource:
|
|
148
|
+
readonly resource: "hardware" | "status" | "prerequisites";
|
|
265
149
|
}>;
|
|
266
150
|
readonly init: LeafCmd<(opts: InitOpts) => {
|
|
267
151
|
readonly kind: "init";
|
|
@@ -377,6 +261,11 @@ export declare const commands: {
|
|
|
377
261
|
readonly file: string;
|
|
378
262
|
readonly opts: TemplateImportOpts;
|
|
379
263
|
}>;
|
|
264
|
+
readonly build: LeafCmd<(name: string, opts: TemplateBuildOpts) => {
|
|
265
|
+
readonly kind: "template.build";
|
|
266
|
+
readonly name: string;
|
|
267
|
+
readonly opts: TemplateBuildOpts;
|
|
268
|
+
}>;
|
|
380
269
|
readonly refresh: LeafCmd<(name: string) => {
|
|
381
270
|
readonly kind: "template.refresh";
|
|
382
271
|
readonly name: string;
|
|
@@ -459,3 +348,15 @@ export declare const commands: {
|
|
|
459
348
|
}>;
|
|
460
349
|
};
|
|
461
350
|
};
|
|
351
|
+
/** Every leaf in a factory subtree, as a union of its `LeafCmd` types. */
|
|
352
|
+
type LeavesOf<T> = T extends LeafCmd<(...args: never[]) => unknown> ? T : T extends object ? {
|
|
353
|
+
[K in keyof T]: LeavesOf<T[K]>;
|
|
354
|
+
}[keyof T] : never;
|
|
355
|
+
/** The constructors build `as const` objects, so their properties come back
|
|
356
|
+
* readonly; `Command` has always been the mutable view of them. */
|
|
357
|
+
type Mutable<T> = {
|
|
358
|
+
-readonly [K in keyof T]: T[K];
|
|
359
|
+
};
|
|
360
|
+
/** Everything the CLI can be asked to do, discriminated by `kind`. */
|
|
361
|
+
export type Command = Mutable<ReturnType<LeavesOf<typeof commands>>>;
|
|
362
|
+
export {};
|
package/commands.js
CHANGED
|
@@ -3,92 +3,112 @@
|
|
|
3
3
|
// of stringly-typed argv arrays. Renaming or removing a CLI verb is a
|
|
4
4
|
// typecheck error at the call site, not a runtime "Unknown arguments" surprise.
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// A leaf is declared ONCE, by its kind, its constructor and its positional
|
|
7
|
+
// names. Everything else falls out: the argv path is the kind's dot-segments
|
|
8
|
+
// kebab-cased, the `__sig` help string is the last segment plus the
|
|
9
|
+
// positionals, the `Command` union is the union of the constructors' return
|
|
10
|
+
// types, and `format.toArgv` renders from the same spec. Adding a verb here
|
|
11
|
+
// leaves nothing else in this package to update.
|
|
12
|
+
//
|
|
13
|
+
// `describeCommands` (in norsk-ctl's cli/src/typed/help.ts) reads `__sig` to
|
|
14
|
+
// build HelpTrees, and the `DescriptionsFor<T>` type forces every per-module
|
|
15
|
+
// description object to enumerate exactly the leaves of its factory subtree.
|
|
11
16
|
//
|
|
12
17
|
// Pair with `format.toArgv(cmd)` to render to a string[] for execution and
|
|
13
18
|
// `format.commandToString(cmd)` for human-readable error messages.
|
|
14
|
-
|
|
19
|
+
const specs = new Map();
|
|
20
|
+
/** Every leaf's spec, by kind. */
|
|
21
|
+
export const commandSpecs = specs;
|
|
22
|
+
const camelToKebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
23
|
+
function leaf(kind, fn, ...positionals) {
|
|
24
|
+
// `instance.launchTemplate` is the only kind whose argv differs from its
|
|
25
|
+
// segments, and it differs exactly by camelCase — the same transform the
|
|
26
|
+
// flag renderer applies.
|
|
27
|
+
const path = kind.split(".").map(camelToKebab);
|
|
28
|
+
const sig = [path[path.length - 1], ...positionals.map((p) => `<${String(p)}>`)].join(" ");
|
|
29
|
+
specs.set(kind, { kind, path, positionals: positionals.map(String) });
|
|
15
30
|
return Object.assign(fn, { __sig: sig });
|
|
16
31
|
}
|
|
32
|
+
/** Runtime source for the `get` vocabulary — the CLI's yargs `choices` and
|
|
33
|
+
* help text derive from it, so the type and the accepted words cannot drift.
|
|
34
|
+
* (`versions` was removed when core's Studio version surface went away; its
|
|
35
|
+
* route no longer exists.) */
|
|
36
|
+
export const GET_RESOURCES = ["hardware", "status", "prerequisites"];
|
|
17
37
|
// ---- Factory tree -----------------------------------------------------------
|
|
18
|
-
// Mirrors cli-builder.ts. Adding a new CLI verb
|
|
19
|
-
//
|
|
20
|
-
// owning command module's helpEntries.
|
|
38
|
+
// Mirrors cli-builder.ts. Adding a new CLI verb is one leaf here, plus (via
|
|
39
|
+
// DescriptionsFor) a description in the owning command module's helpEntries.
|
|
21
40
|
export const commands = {
|
|
22
41
|
config: {
|
|
23
|
-
set: leaf("set", () => ({ kind: "config.set" })),
|
|
24
|
-
show: leaf("show", () => ({ kind: "config.show" })),
|
|
42
|
+
set: leaf("config.set", () => ({ kind: "config.set" })),
|
|
43
|
+
show: leaf("config.show", () => ({ kind: "config.show" })),
|
|
25
44
|
},
|
|
26
45
|
fetch: {
|
|
27
|
-
mkcert: leaf("mkcert", () => ({ kind: "fetch.mkcert" })),
|
|
46
|
+
mkcert: leaf("fetch.mkcert", () => ({ kind: "fetch.mkcert" })),
|
|
28
47
|
},
|
|
29
|
-
get: leaf("get
|
|
48
|
+
get: leaf("get", (resource) => ({ kind: "get", resource }), "resource"),
|
|
30
49
|
init: leaf("init", (opts) => ({ kind: "init", opts })),
|
|
31
50
|
inspect: leaf("inspect", () => ({ kind: "inspect" })),
|
|
32
51
|
instance: {
|
|
33
|
-
delete: leaf("delete
|
|
34
|
-
describe: leaf("describe
|
|
35
|
-
restart: leaf("restart
|
|
36
|
-
stop: leaf("stop
|
|
37
|
-
start: leaf("start
|
|
38
|
-
relaunch: leaf("relaunch
|
|
39
|
-
list: leaf("list", () => ({ kind: "instance.list" })),
|
|
40
|
-
exists: leaf("exists
|
|
41
|
-
launchTemplate: leaf("
|
|
52
|
+
delete: leaf("instance.delete", (id, opts = {}) => ({ kind: "instance.delete", id, opts }), "id"),
|
|
53
|
+
describe: leaf("instance.describe", (id) => ({ kind: "instance.describe", id }), "id"),
|
|
54
|
+
restart: leaf("instance.restart", (id) => ({ kind: "instance.restart", id }), "id"),
|
|
55
|
+
stop: leaf("instance.stop", (id) => ({ kind: "instance.stop", id }), "id"),
|
|
56
|
+
start: leaf("instance.start", (id) => ({ kind: "instance.start", id }), "id"),
|
|
57
|
+
relaunch: leaf("instance.relaunch", (id, opts = {}) => ({ kind: "instance.relaunch", id, opts }), "id"),
|
|
58
|
+
list: leaf("instance.list", () => ({ kind: "instance.list" })),
|
|
59
|
+
exists: leaf("instance.exists", (id) => ({ kind: "instance.exists", id }), "id"),
|
|
60
|
+
launchTemplate: leaf("instance.launchTemplate", (id, opts) => ({ kind: "instance.launchTemplate", id, opts }), "id"),
|
|
42
61
|
},
|
|
43
62
|
orphanedDirs: {
|
|
44
|
-
list: leaf("list", (opts = {}) => ({ kind: "orphaned-dirs.list", opts })),
|
|
45
|
-
clear: leaf("clear
|
|
63
|
+
list: leaf("orphaned-dirs.list", (opts = {}) => ({ kind: "orphaned-dirs.list", opts })),
|
|
64
|
+
clear: leaf("orphaned-dirs.clear", (id) => ({ kind: "orphaned-dirs.clear", id }), "id"),
|
|
46
65
|
},
|
|
47
66
|
product: {
|
|
48
|
-
add: leaf("add", (opts = {}) => ({ kind: "product.add", opts })),
|
|
49
|
-
list: leaf("list", () => ({ kind: "product.list" })),
|
|
50
|
-
exists: leaf("exists
|
|
51
|
-
remove: leaf("remove
|
|
52
|
-
reload: leaf("reload
|
|
53
|
-
pull: leaf("pull
|
|
67
|
+
add: leaf("product.add", (opts = {}) => ({ kind: "product.add", opts })),
|
|
68
|
+
list: leaf("product.list", () => ({ kind: "product.list" })),
|
|
69
|
+
exists: leaf("product.exists", (name) => ({ kind: "product.exists", name }), "name"),
|
|
70
|
+
remove: leaf("product.remove", (name) => ({ kind: "product.remove", name }), "name"),
|
|
71
|
+
reload: leaf("product.reload", (name) => ({ kind: "product.reload", name }), "name"),
|
|
72
|
+
pull: leaf("product.pull", (name, opts = {}) => ({ kind: "product.pull", name, opts }), "name"),
|
|
54
73
|
},
|
|
55
74
|
shm: {
|
|
56
|
-
list: leaf("list", () => ({ kind: "shm.list" })),
|
|
75
|
+
list: leaf("shm.list", () => ({ kind: "shm.list" })),
|
|
57
76
|
},
|
|
58
77
|
sideload: {
|
|
59
|
-
status: leaf("status", () => ({ kind: "sideload.status" })),
|
|
60
|
-
fetch: leaf("fetch", (opts = {}) => ({ kind: "sideload.fetch", opts })),
|
|
78
|
+
status: leaf("sideload.status", () => ({ kind: "sideload.status" })),
|
|
79
|
+
fetch: leaf("sideload.fetch", (opts = {}) => ({ kind: "sideload.fetch", opts })),
|
|
61
80
|
},
|
|
62
81
|
template: {
|
|
63
|
-
list: leaf("list", () => ({ kind: "template.list" })),
|
|
64
|
-
show: leaf("show
|
|
65
|
-
default: leaf("default
|
|
66
|
-
import: leaf("import
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
list: leaf("template.list", () => ({ kind: "template.list" })),
|
|
83
|
+
show: leaf("template.show", (name) => ({ kind: "template.show", name }), "name"),
|
|
84
|
+
default: leaf("template.default", (product) => ({ kind: "template.default", product }), "product"),
|
|
85
|
+
import: leaf("template.import", (file, opts) => ({ kind: "template.import", file, opts }), "file"),
|
|
86
|
+
build: leaf("template.build", (name, opts) => ({ kind: "template.build", name, opts }), "name"),
|
|
87
|
+
refresh: leaf("template.refresh", (name) => ({ kind: "template.refresh", name }), "name"),
|
|
88
|
+
remove: leaf("template.remove", (name) => ({ kind: "template.remove", name }), "name"),
|
|
69
89
|
},
|
|
70
90
|
proxy: {
|
|
71
|
-
pull: leaf("pull", () => ({ kind: "proxy.pull" })),
|
|
72
|
-
start: leaf("start", () => ({ kind: "proxy.start" })),
|
|
73
|
-
stop: leaf("stop", () => ({ kind: "proxy.stop" })),
|
|
74
|
-
reload: leaf("reload", () => ({ kind: "proxy.reload" })),
|
|
75
|
-
status: leaf("status", () => ({ kind: "proxy.status" })),
|
|
76
|
-
logs: leaf("logs", (opts = {}) => ({ kind: "proxy.logs", opts })),
|
|
91
|
+
pull: leaf("proxy.pull", () => ({ kind: "proxy.pull" })),
|
|
92
|
+
start: leaf("proxy.start", () => ({ kind: "proxy.start" })),
|
|
93
|
+
stop: leaf("proxy.stop", () => ({ kind: "proxy.stop" })),
|
|
94
|
+
reload: leaf("proxy.reload", () => ({ kind: "proxy.reload" })),
|
|
95
|
+
status: leaf("proxy.status", () => ({ kind: "proxy.status" })),
|
|
96
|
+
logs: leaf("proxy.logs", (opts = {}) => ({ kind: "proxy.logs", opts })),
|
|
77
97
|
},
|
|
78
98
|
mcp: leaf("mcp", () => ({ kind: "mcp" })),
|
|
79
99
|
serve: leaf("serve", () => ({ kind: "serve" })),
|
|
80
100
|
source: {
|
|
81
|
-
start: leaf("start
|
|
82
|
-
stop: leaf("stop
|
|
83
|
-
list: leaf("list", () => ({ kind: "source.list" })),
|
|
84
|
-
presets: leaf("presets", () => ({ kind: "source.presets" })),
|
|
101
|
+
start: leaf("source.start", (instanceId, preset, opts = {}) => ({ kind: "source.start", instanceId, preset, opts }), "instanceId", "preset"),
|
|
102
|
+
stop: leaf("source.stop", (instanceId, name) => ({ kind: "source.stop", instanceId, name }), "instanceId", "name"),
|
|
103
|
+
list: leaf("source.list", () => ({ kind: "source.list" })),
|
|
104
|
+
presets: leaf("source.presets", () => ({ kind: "source.presets" })),
|
|
85
105
|
},
|
|
86
106
|
stats: leaf("stats", (opts = {}) => ({ kind: "stats", opts })),
|
|
87
107
|
restart: leaf("restart", () => ({ kind: "restart" })),
|
|
88
108
|
shutdown: leaf("shutdown", (opts = {}) => ({ kind: "shutdown", opts })),
|
|
89
109
|
upgrade: leaf("upgrade", (opts = {}) => ({ kind: "upgrade", opts })),
|
|
90
110
|
user: {
|
|
91
|
-
set: leaf("set
|
|
92
|
-
delete: leaf("delete
|
|
111
|
+
set: leaf("user.set", (name, opts) => ({ kind: "user.set", name, opts }), "name"),
|
|
112
|
+
delete: leaf("user.delete", (name) => ({ kind: "user.delete", name }), "name"),
|
|
93
113
|
},
|
|
94
114
|
};
|
package/format.d.ts
CHANGED
package/format.js
CHANGED
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
// docs). Mirrors the CLI's flag conventions: camelCase option keys become
|
|
4
4
|
// --kebab-case flags; arrays become repeated --flag value pairs; boolean true
|
|
5
5
|
// becomes a bare --flag, boolean false is omitted (yargs default).
|
|
6
|
+
import { commandSpecs } from "./commands.js";
|
|
6
7
|
const camelToKebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
7
|
-
/** Self-referential constraint: every key in T must map to an OptValue. Lets
|
|
8
|
-
* callers pass typed interfaces (LaunchOpts, InitOpts, …) without polluting
|
|
9
|
-
* them with index signatures, while still rejecting an opts object that
|
|
10
|
-
* carries an unsupported field shape (e.g. a Date or nested object). */
|
|
11
8
|
function formatOpts(opts) {
|
|
12
9
|
if (!opts)
|
|
13
10
|
return [];
|
|
@@ -31,108 +28,15 @@ function formatOpts(opts) {
|
|
|
31
28
|
return out;
|
|
32
29
|
}
|
|
33
30
|
export function toArgv(cmd) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
case "init":
|
|
44
|
-
return ["init", ...formatOpts(cmd.opts)];
|
|
45
|
-
case "inspect":
|
|
46
|
-
return ["inspect"];
|
|
47
|
-
case "instance.delete":
|
|
48
|
-
return ["instance", "delete", cmd.id, ...formatOpts(cmd.opts)];
|
|
49
|
-
case "instance.describe":
|
|
50
|
-
return ["instance", "describe", cmd.id];
|
|
51
|
-
case "instance.restart":
|
|
52
|
-
return ["instance", "restart", cmd.id];
|
|
53
|
-
case "instance.stop":
|
|
54
|
-
return ["instance", "stop", cmd.id];
|
|
55
|
-
case "instance.start":
|
|
56
|
-
return ["instance", "start", cmd.id];
|
|
57
|
-
case "instance.relaunch":
|
|
58
|
-
return ["instance", "relaunch", cmd.id, ...formatOpts(cmd.opts)];
|
|
59
|
-
case "instance.list":
|
|
60
|
-
return ["instance", "list"];
|
|
61
|
-
case "instance.exists":
|
|
62
|
-
return ["instance", "exists", cmd.id];
|
|
63
|
-
case "instance.launchTemplate":
|
|
64
|
-
return ["instance", "launch-template", cmd.id, ...formatOpts(cmd.opts)];
|
|
65
|
-
case "orphaned-dirs.list":
|
|
66
|
-
return ["orphaned-dirs", "list", ...formatOpts(cmd.opts)];
|
|
67
|
-
case "orphaned-dirs.clear":
|
|
68
|
-
return ["orphaned-dirs", "clear", cmd.id];
|
|
69
|
-
case "product.add":
|
|
70
|
-
return ["product", "add", ...formatOpts(cmd.opts)];
|
|
71
|
-
case "product.list":
|
|
72
|
-
return ["product", "list"];
|
|
73
|
-
case "product.exists":
|
|
74
|
-
return ["product", "exists", cmd.name];
|
|
75
|
-
case "product.remove":
|
|
76
|
-
return ["product", "remove", cmd.name];
|
|
77
|
-
case "product.reload":
|
|
78
|
-
return ["product", "reload", cmd.name];
|
|
79
|
-
case "product.pull":
|
|
80
|
-
return ["product", "pull", cmd.name, ...formatOpts(cmd.opts)];
|
|
81
|
-
case "shm.list":
|
|
82
|
-
return ["shm", "list"];
|
|
83
|
-
case "sideload.status":
|
|
84
|
-
return ["sideload", "status"];
|
|
85
|
-
case "sideload.fetch":
|
|
86
|
-
return ["sideload", "fetch", ...formatOpts(cmd.opts)];
|
|
87
|
-
case "template.list":
|
|
88
|
-
return ["template", "list"];
|
|
89
|
-
case "template.show":
|
|
90
|
-
return ["template", "show", cmd.name];
|
|
91
|
-
case "template.default":
|
|
92
|
-
return ["template", "default", cmd.product];
|
|
93
|
-
case "template.import":
|
|
94
|
-
return ["template", "import", cmd.file, ...formatOpts(cmd.opts)];
|
|
95
|
-
case "template.refresh":
|
|
96
|
-
return ["template", "refresh", cmd.name];
|
|
97
|
-
case "template.remove":
|
|
98
|
-
return ["template", "remove", cmd.name];
|
|
99
|
-
case "mcp":
|
|
100
|
-
return ["mcp"];
|
|
101
|
-
case "proxy.pull":
|
|
102
|
-
return ["proxy", "pull"];
|
|
103
|
-
case "proxy.start":
|
|
104
|
-
return ["proxy", "start"];
|
|
105
|
-
case "proxy.stop":
|
|
106
|
-
return ["proxy", "stop"];
|
|
107
|
-
case "proxy.reload":
|
|
108
|
-
return ["proxy", "reload"];
|
|
109
|
-
case "proxy.status":
|
|
110
|
-
return ["proxy", "status"];
|
|
111
|
-
case "proxy.logs":
|
|
112
|
-
return ["proxy", "logs", ...formatOpts(cmd.opts)];
|
|
113
|
-
case "serve":
|
|
114
|
-
return ["serve"];
|
|
115
|
-
case "source.start":
|
|
116
|
-
return ["source", "start", cmd.instanceId, cmd.preset, ...formatOpts(cmd.opts)];
|
|
117
|
-
case "source.stop":
|
|
118
|
-
return ["source", "stop", cmd.instanceId, cmd.name];
|
|
119
|
-
case "source.list":
|
|
120
|
-
return ["source", "list"];
|
|
121
|
-
case "source.presets":
|
|
122
|
-
return ["source", "presets"];
|
|
123
|
-
case "stats":
|
|
124
|
-
return ["stats", ...formatOpts(cmd.opts)];
|
|
125
|
-
case "restart":
|
|
126
|
-
return ["restart"];
|
|
127
|
-
case "shutdown":
|
|
128
|
-
return ["shutdown", ...formatOpts(cmd.opts)];
|
|
129
|
-
case "upgrade":
|
|
130
|
-
return ["upgrade", ...formatOpts(cmd.opts)];
|
|
131
|
-
case "user.set":
|
|
132
|
-
return ["user", "set", cmd.name, ...formatOpts(cmd.opts)];
|
|
133
|
-
case "user.delete":
|
|
134
|
-
return ["user", "delete", cmd.name];
|
|
135
|
-
}
|
|
31
|
+
const spec = commandSpecs.get(cmd.kind);
|
|
32
|
+
if (!spec)
|
|
33
|
+
throw new Error(`toArgv: '${cmd.kind}' is not a declared command kind`);
|
|
34
|
+
const fields = cmd;
|
|
35
|
+
return [
|
|
36
|
+
...spec.path,
|
|
37
|
+
...spec.positionals.map((name) => String(fields[name])),
|
|
38
|
+
...formatOpts(fields.opts),
|
|
39
|
+
];
|
|
136
40
|
}
|
|
137
41
|
export function commandToString(cmd) {
|
|
138
42
|
return ["norsk-ctl", ...toArgv(cmd)].join(" ");
|