@reliverse/rempts 1.7.0 → 1.7.2
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 +346 -105
- package/bin/components/launcher/deprecated/launcher-mod.ts.txt +1 -1
- package/bin/components/launcher/deprecated/usage.ts.txt +1 -1
- package/bin/components/launcher/launcher-mod.d.ts +75 -7
- package/bin/components/launcher/launcher-mod.js +226 -126
- package/bin/components/msg-fmt/messages.js +6 -3
- package/bin/components/select/multiselect-prompt.js +1 -1
- package/bin/components/select/select-prompt.js +1 -1
- package/bin/components/select/toggle-prompt.js +1 -1
- package/bin/components/st-end/start.js +2 -2
- package/bin/components/visual/ascii-art/ascii-art.js +1 -1
- package/bin/mod.d.ts +1 -1
- package/bin/types.d.ts +1 -1
- package/bin/utils/prompt-end.js +8 -8
- package/package.json +5 -5
|
@@ -44,10 +44,10 @@ type CommandMeta = {
|
|
|
44
44
|
* 2) A lazy import function returning a Promise that resolves to
|
|
45
45
|
* { default: Command<any> } or directly to a Command instance.
|
|
46
46
|
*/
|
|
47
|
-
type
|
|
47
|
+
type CommandSpec = string | (() => Promise<{
|
|
48
48
|
default: Command<any>;
|
|
49
49
|
} | Command<any>>);
|
|
50
|
-
export type
|
|
50
|
+
export type CommandsMap = Record<string, CommandSpec>;
|
|
51
51
|
type CommandContext<ARGS> = {
|
|
52
52
|
args: ARGS;
|
|
53
53
|
raw: string[];
|
|
@@ -57,17 +57,75 @@ type DefineCommandOptions<A extends ArgDefinitions = EmptyArgs> = {
|
|
|
57
57
|
meta?: CommandMeta;
|
|
58
58
|
args?: A;
|
|
59
59
|
run?: CommandRun<InferArgTypes<A>>;
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Object subcommands for this command.
|
|
62
|
+
*/
|
|
63
|
+
commands?: CommandsMap;
|
|
64
|
+
/**
|
|
65
|
+
* @deprecated Use `commands` instead. Will be removed in a future major version.
|
|
66
|
+
*/
|
|
67
|
+
subCommands?: CommandsMap;
|
|
68
|
+
/**
|
|
69
|
+
* Called before the command runs
|
|
70
|
+
*/
|
|
71
|
+
onCmdStart?: () => void | Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Called after the command finishes
|
|
74
|
+
*/
|
|
75
|
+
onCmdEnd?: () => void | Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* @deprecated Use onCmdStart instead
|
|
78
|
+
*/
|
|
61
79
|
setup?: () => void | Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* @deprecated Use onCmdEnd instead
|
|
82
|
+
*/
|
|
62
83
|
cleanup?: () => void | Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Called once per CLI process, before any command/run() is executed
|
|
86
|
+
*/
|
|
87
|
+
onLauncherStart?: () => void | Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Called once per CLI process, after all command/run() logic is finished
|
|
90
|
+
*/
|
|
91
|
+
onLauncherEnd?: () => void | Promise<void>;
|
|
63
92
|
};
|
|
64
93
|
export type Command<A extends ArgDefinitions = EmptyArgs> = {
|
|
65
94
|
meta?: CommandMeta;
|
|
66
95
|
args: A;
|
|
67
96
|
run?: CommandRun<InferArgTypes<A>>;
|
|
68
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Object subcommands for this command.
|
|
99
|
+
*/
|
|
100
|
+
commands?: CommandsMap;
|
|
101
|
+
/**
|
|
102
|
+
* @deprecated Use `commands` instead. Will be removed in a future major version.
|
|
103
|
+
*/
|
|
104
|
+
subCommands?: CommandsMap;
|
|
105
|
+
/**
|
|
106
|
+
* Called before the command runs
|
|
107
|
+
*/
|
|
108
|
+
onCmdStart?: () => void | Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Called after the command finishes
|
|
111
|
+
*/
|
|
112
|
+
onCmdEnd?: () => void | Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* @deprecated Use onCmdStart instead
|
|
115
|
+
*/
|
|
69
116
|
setup?: () => void | Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated Use onCmdEnd instead
|
|
119
|
+
*/
|
|
70
120
|
cleanup?: () => void | Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Called once per CLI process, before any command/run() is executed
|
|
123
|
+
*/
|
|
124
|
+
onLauncherStart?: () => void | Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Called once per CLI process, after all command/run() logic is finished
|
|
127
|
+
*/
|
|
128
|
+
onLauncherEnd?: () => void | Promise<void>;
|
|
71
129
|
};
|
|
72
130
|
export type InferArgTypes<A extends ArgDefinitions> = {
|
|
73
131
|
[K in keyof A]: A[K] extends PositionalArgDefinition ? string : A[K] extends BooleanArgDefinition ? boolean : A[K] extends StringArgDefinition ? string : A[K] extends NumberArgDefinition ? number : A[K] extends {
|
|
@@ -108,11 +166,11 @@ export declare function showUsage<A extends ArgDefinitions>(command: Command<A>,
|
|
|
108
166
|
/**
|
|
109
167
|
* Primary entry point to run a command. This function supports:
|
|
110
168
|
*
|
|
111
|
-
* - File-based
|
|
112
|
-
* -
|
|
169
|
+
* - File-based Commands: scanning for commands within a given commands root.
|
|
170
|
+
* - Commands defined within the command object.
|
|
113
171
|
* - Standard flags like --help, --version, and --debug.
|
|
114
172
|
*
|
|
115
|
-
* This function passes along remaining arguments to
|
|
173
|
+
* This function passes along remaining arguments to command runners to ensure
|
|
116
174
|
* consistent parsing.
|
|
117
175
|
*/
|
|
118
176
|
export declare function runMain<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, parserOptions?: ReliArgParserOptions & {
|
|
@@ -130,4 +188,14 @@ export declare function runMain<A extends ArgDefinitions = EmptyArgs>(command: C
|
|
|
130
188
|
* precise default value validation (e.g., `options: ["a", "b"] as const`).
|
|
131
189
|
*/
|
|
132
190
|
export declare function defineArgs<A extends ArgDefinitions>(args: A & ValidateArrayDefaults<A>): A;
|
|
191
|
+
/**
|
|
192
|
+
* Programmatically run a command's run() handler with parsed arguments.
|
|
193
|
+
* Does not handle subcommands, file-based commands, or global hooks.
|
|
194
|
+
* Suitable for use in demos, tests, or programmatic invocation.
|
|
195
|
+
*
|
|
196
|
+
* @param command The command definition (from defineCommand)
|
|
197
|
+
* @param argv The argv array to parse (default: [])
|
|
198
|
+
* @param parserOptions Optional reliArgParser options
|
|
199
|
+
*/
|
|
200
|
+
export declare function runCmd<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, argv?: string[], parserOptions?: ReliArgParserOptions): Promise<void>;
|
|
133
201
|
export {};
|
|
@@ -50,21 +50,42 @@ function buildExampleArgs(args) {
|
|
|
50
50
|
const isDebugMode = process.argv.includes("--debug");
|
|
51
51
|
function debugLog(...args) {
|
|
52
52
|
if (isDebugMode) {
|
|
53
|
-
relinka("
|
|
53
|
+
relinka("log", "[DEBUG]", ...args);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
function isFlag(str) {
|
|
57
57
|
return str.startsWith("-");
|
|
58
58
|
}
|
|
59
59
|
export function defineCommand(options) {
|
|
60
|
-
|
|
60
|
+
const onCmdStart = options.onCmdStart || options.setup;
|
|
61
|
+
const onCmdEnd = options.onCmdEnd || options.cleanup;
|
|
62
|
+
const onLauncherStart = options.onLauncherStart;
|
|
63
|
+
const onLauncherEnd = options.onLauncherEnd;
|
|
64
|
+
let commands = options.commands;
|
|
65
|
+
if (!commands) {
|
|
66
|
+
commands = options.subCommands;
|
|
67
|
+
}
|
|
68
|
+
const cmdObj = {
|
|
61
69
|
meta: options.meta,
|
|
62
70
|
args: options.args || {},
|
|
63
71
|
run: options.run,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
commands,
|
|
73
|
+
onCmdStart,
|
|
74
|
+
onCmdEnd,
|
|
75
|
+
onLauncherStart,
|
|
76
|
+
onLauncherEnd,
|
|
77
|
+
// Backward-compatible aliases
|
|
78
|
+
setup: onCmdStart,
|
|
79
|
+
cleanup: onCmdEnd
|
|
67
80
|
};
|
|
81
|
+
Object.defineProperty(cmdObj, "subCommands", {
|
|
82
|
+
get() {
|
|
83
|
+
return this.commands;
|
|
84
|
+
},
|
|
85
|
+
enumerable: false,
|
|
86
|
+
configurable: true
|
|
87
|
+
});
|
|
88
|
+
return cmdObj;
|
|
68
89
|
}
|
|
69
90
|
let _cachedDefaultCliName;
|
|
70
91
|
let _cachedDefaultCliVersion;
|
|
@@ -88,7 +109,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
88
109
|
const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
|
|
89
110
|
const cliName = command.meta?.name || fallbackName;
|
|
90
111
|
const cliVersion = command.meta?.version || fallbackVersion;
|
|
91
|
-
relinka("
|
|
112
|
+
relinka("log", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
|
|
92
113
|
if (parserOptions.metaSettings?.showDescriptionOnMain) {
|
|
93
114
|
let description = command.meta?.description;
|
|
94
115
|
if (!description) {
|
|
@@ -146,7 +167,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
146
167
|
);
|
|
147
168
|
}
|
|
148
169
|
if (subCommandNames.length > 0) {
|
|
149
|
-
relinka("
|
|
170
|
+
relinka("log", "Available commands (run with `help` to see more):");
|
|
150
171
|
subCommandDefs.forEach(({ name, def }) => {
|
|
151
172
|
const desc = def?.meta?.description ?? "";
|
|
152
173
|
relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
|
|
@@ -164,8 +185,9 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
164
185
|
} else {
|
|
165
186
|
const subCommandNames = [];
|
|
166
187
|
const subCommandDefs = [];
|
|
167
|
-
|
|
168
|
-
|
|
188
|
+
const objectCommands = command.commands;
|
|
189
|
+
if (objectCommands) {
|
|
190
|
+
for (const [name, spec] of Object.entries(objectCommands)) {
|
|
169
191
|
try {
|
|
170
192
|
const cmd = await loadSubCommand(spec);
|
|
171
193
|
if (!cmd?.meta?.hidden) {
|
|
@@ -174,7 +196,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
174
196
|
subCommandDefs.push({ name, def: cmd });
|
|
175
197
|
}
|
|
176
198
|
} catch (err) {
|
|
177
|
-
debugLog(`Error loading
|
|
199
|
+
debugLog(`Error loading command ${name}:`, err);
|
|
178
200
|
}
|
|
179
201
|
}
|
|
180
202
|
}
|
|
@@ -195,14 +217,14 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
195
217
|
);
|
|
196
218
|
}
|
|
197
219
|
if (subCommandNames.length > 0) {
|
|
198
|
-
relinka("
|
|
220
|
+
relinka("log", "Available commands (run with `help` to see more):");
|
|
199
221
|
subCommandDefs.forEach(({ name, def }) => {
|
|
200
222
|
const desc = def?.meta?.description ?? "";
|
|
201
223
|
relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
|
|
202
224
|
});
|
|
203
225
|
}
|
|
204
226
|
}
|
|
205
|
-
relinka("
|
|
227
|
+
relinka("log", "Available options:");
|
|
206
228
|
relinka("log", "\u2022 -h, --help | Show help");
|
|
207
229
|
relinka("log", "\u2022 -v, --version | Show version");
|
|
208
230
|
relinka("log", "\u2022 --debug | Enable debug mode");
|
|
@@ -228,146 +250,153 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
228
250
|
}
|
|
229
251
|
}
|
|
230
252
|
export async function runMain(command, parserOptions = {}) {
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
253
|
+
if (typeof command.onLauncherStart === "function")
|
|
254
|
+
await command.onLauncherStart();
|
|
255
|
+
try {
|
|
256
|
+
if (!parserOptions.fileBasedCmds && !command.commands) {
|
|
257
|
+
let callerDir = process.cwd();
|
|
258
|
+
let callerFile;
|
|
259
|
+
try {
|
|
260
|
+
const err = new Error();
|
|
261
|
+
const stack = err.stack?.split("\n");
|
|
262
|
+
if (stack) {
|
|
263
|
+
for (const line of stack) {
|
|
264
|
+
const match = /\((.*):(\d+):(\d+)\)/.exec(line) || /at (.*):(\d+):(\d+)/.exec(line);
|
|
265
|
+
if (match?.[1] && !match[1].includes("launcher-mod")) {
|
|
266
|
+
callerFile = match[1];
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
243
269
|
}
|
|
244
270
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
`runMain() should not be called from a file-based subcommand: ${rel}
|
|
271
|
+
if (callerFile) {
|
|
272
|
+
callerDir = path.dirname(callerFile);
|
|
273
|
+
const rel = path.relative(process.cwd(), callerFile);
|
|
274
|
+
if (/app[/][^/]+[/]cmd\.(ts|js)$/.test(rel)) {
|
|
275
|
+
relinka(
|
|
276
|
+
"error",
|
|
277
|
+
`runMain() should not be called from a file-based subcommand: ${rel}
|
|
253
278
|
This can cause recursion or unexpected behavior.
|
|
254
279
|
Move your runMain() call to your main CLI entry file.`
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
280
|
+
);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
const mainEntry = process.argv[1] ? path.resolve(process.argv[1]) : void 0;
|
|
284
|
+
if (mainEntry && path.resolve(callerFile) !== mainEntry) {
|
|
285
|
+
relinka(
|
|
286
|
+
"error",
|
|
287
|
+
`runMain() should only be called from your main CLI entry file.
|
|
263
288
|
Detected: ${callerFile}
|
|
264
289
|
Main entry: ${mainEntry}
|
|
265
290
|
This can cause recursion or unexpected behavior.`
|
|
266
|
-
|
|
267
|
-
|
|
291
|
+
);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
268
294
|
}
|
|
295
|
+
} catch (_e) {
|
|
269
296
|
}
|
|
270
|
-
|
|
297
|
+
const defaultCmdsRoot = path.resolve(callerDir, "app");
|
|
298
|
+
parserOptions.fileBasedCmds = {
|
|
299
|
+
enable: true,
|
|
300
|
+
cmdsRootPath: defaultCmdsRoot
|
|
301
|
+
};
|
|
271
302
|
}
|
|
272
|
-
const
|
|
273
|
-
parserOptions.
|
|
274
|
-
|
|
275
|
-
cmdsRootPath: defaultCmdsRoot
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
const rawArgv = process.argv.slice(2);
|
|
279
|
-
const autoExit = parserOptions.autoExit !== false;
|
|
280
|
-
if (!(parserOptions.fileBasedCmds?.enable || command.subCommands && Object.keys(command.subCommands).length > 0 || command.run)) {
|
|
281
|
-
relinka(
|
|
282
|
-
"error",
|
|
283
|
-
"Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand or a run() handler."
|
|
284
|
-
);
|
|
285
|
-
process.exit(1);
|
|
286
|
-
}
|
|
287
|
-
if (rawArgv[0] === "help") {
|
|
288
|
-
await showUsage(command, parserOptions);
|
|
289
|
-
if (autoExit) process.exit(0);
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
await relinkaConfig;
|
|
293
|
-
if (checkHelp(rawArgv)) {
|
|
294
|
-
await showUsage(command, parserOptions);
|
|
295
|
-
if (autoExit) process.exit(0);
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
if (checkVersion(rawArgv)) {
|
|
299
|
-
if (command.meta?.name) {
|
|
303
|
+
const rawArgv = process.argv.slice(2);
|
|
304
|
+
const autoExit = parserOptions.autoExit !== false;
|
|
305
|
+
if (!(parserOptions.fileBasedCmds?.enable || command.commands && Object.keys(command.commands).length > 0 || command.run)) {
|
|
300
306
|
relinka(
|
|
301
|
-
"
|
|
302
|
-
|
|
307
|
+
"error",
|
|
308
|
+
"Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand or a run() handler."
|
|
303
309
|
);
|
|
310
|
+
process.exit(1);
|
|
304
311
|
}
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
const fileBasedEnabled = parserOptions.fileBasedCmds?.enable;
|
|
309
|
-
if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
310
|
-
const [subName, ...subCmdArgv] = rawArgv;
|
|
311
|
-
try {
|
|
312
|
-
if (typeof command.setup === "function") await command.setup();
|
|
313
|
-
await runFileBasedSubCmd(
|
|
314
|
-
subName,
|
|
315
|
-
subCmdArgv,
|
|
316
|
-
parserOptions.fileBasedCmds,
|
|
317
|
-
parserOptions,
|
|
318
|
-
command.cleanup
|
|
319
|
-
);
|
|
312
|
+
if (rawArgv[0] === "help") {
|
|
313
|
+
await showUsage(command, parserOptions);
|
|
320
314
|
if (autoExit) process.exit(0);
|
|
321
315
|
return;
|
|
322
|
-
} catch (err) {
|
|
323
|
-
relinka("error", "Error loading file-based subcommand:", err.message);
|
|
324
|
-
if (autoExit) process.exit(1);
|
|
325
|
-
throw err;
|
|
326
316
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
subSpec = spec;
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
} catch (err) {
|
|
343
|
-
debugLog(`Error checking alias for subcommand ${key}:`, err);
|
|
317
|
+
await relinkaConfig;
|
|
318
|
+
if (checkHelp(rawArgv)) {
|
|
319
|
+
await showUsage(command, parserOptions);
|
|
320
|
+
if (autoExit) process.exit(0);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (checkVersion(rawArgv)) {
|
|
324
|
+
if (command.meta?.name) {
|
|
325
|
+
relinka(
|
|
326
|
+
"log",
|
|
327
|
+
`${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
|
|
328
|
+
);
|
|
344
329
|
}
|
|
330
|
+
if (autoExit) process.exit(0);
|
|
331
|
+
return;
|
|
345
332
|
}
|
|
346
|
-
|
|
333
|
+
const fileBasedEnabled = parserOptions.fileBasedCmds?.enable;
|
|
334
|
+
if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
335
|
+
const [subName, ...subCmdArgv] = rawArgv;
|
|
347
336
|
try {
|
|
348
|
-
if (typeof command.
|
|
349
|
-
|
|
350
|
-
|
|
337
|
+
if (typeof command.onCmdStart === "function")
|
|
338
|
+
await command.onCmdStart();
|
|
339
|
+
await runFileBasedSubCmd(
|
|
340
|
+
subName,
|
|
351
341
|
subCmdArgv,
|
|
342
|
+
parserOptions.fileBasedCmds,
|
|
352
343
|
parserOptions,
|
|
353
|
-
command.
|
|
344
|
+
command.onCmdEnd
|
|
354
345
|
);
|
|
355
346
|
if (autoExit) process.exit(0);
|
|
356
347
|
return;
|
|
357
348
|
} catch (err) {
|
|
358
|
-
relinka("error", "Error
|
|
349
|
+
relinka("error", "Error loading file-based subcommand:", err.message);
|
|
359
350
|
if (autoExit) process.exit(1);
|
|
360
351
|
throw err;
|
|
361
352
|
}
|
|
362
353
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
354
|
+
if (!fileBasedEnabled && command.commands && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
355
|
+
const [maybeSub, ...subCmdArgv] = rawArgv;
|
|
356
|
+
let subSpec;
|
|
357
|
+
for (const [key, spec] of Object.entries(command.commands)) {
|
|
358
|
+
if (key === maybeSub) {
|
|
359
|
+
subSpec = spec;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
const cmd = await loadSubCommand(spec);
|
|
364
|
+
if (cmd.meta.aliases?.includes(maybeSub)) {
|
|
365
|
+
subSpec = spec;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
} catch (err) {
|
|
369
|
+
debugLog(`Error checking alias for command ${key}:`, err);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (subSpec) {
|
|
373
|
+
try {
|
|
374
|
+
if (typeof command.onCmdStart === "function")
|
|
375
|
+
await command.onCmdStart();
|
|
376
|
+
await runSubCommand(
|
|
377
|
+
subSpec,
|
|
378
|
+
subCmdArgv,
|
|
379
|
+
parserOptions,
|
|
380
|
+
command.onCmdEnd
|
|
381
|
+
);
|
|
382
|
+
if (autoExit) process.exit(0);
|
|
383
|
+
return;
|
|
384
|
+
} catch (err) {
|
|
385
|
+
relinka("error", "Error running subcommand:", err.message);
|
|
386
|
+
if (autoExit) process.exit(1);
|
|
387
|
+
throw err;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
try {
|
|
392
|
+
await runCommandWithArgs(command, rawArgv, parserOptions);
|
|
393
|
+
} finally {
|
|
394
|
+
}
|
|
395
|
+
await relinkaShutdown();
|
|
367
396
|
} finally {
|
|
368
|
-
if (typeof command.
|
|
397
|
+
if (typeof command.onLauncherEnd === "function")
|
|
398
|
+
await command.onLauncherEnd();
|
|
369
399
|
}
|
|
370
|
-
await relinkaShutdown();
|
|
371
400
|
}
|
|
372
401
|
function checkHelp(argv) {
|
|
373
402
|
return argv.includes("--help") || argv.includes("-h");
|
|
@@ -392,7 +421,7 @@ async function loadSubCommand(spec) {
|
|
|
392
421
|
}
|
|
393
422
|
throw new Error("Subcommand import did not return a valid command");
|
|
394
423
|
}
|
|
395
|
-
async function runFileBasedSubCmd(subName, argv, fileCmdOpts, parserOptions,
|
|
424
|
+
async function runFileBasedSubCmd(subName, argv, fileCmdOpts, parserOptions, parentFinish) {
|
|
396
425
|
const subPathDir = path.join(fileCmdOpts.cmdsRootPath, subName);
|
|
397
426
|
let importPath;
|
|
398
427
|
const possibleFiles = [
|
|
@@ -447,15 +476,15 @@ Info for this CLI's developer: No valid command directory found, expected: ${exp
|
|
|
447
476
|
try {
|
|
448
477
|
await runCommandWithArgs(subCommand, argv, parserOptions);
|
|
449
478
|
} finally {
|
|
450
|
-
if (typeof
|
|
479
|
+
if (typeof parentFinish === "function") await parentFinish();
|
|
451
480
|
}
|
|
452
481
|
}
|
|
453
|
-
async function runSubCommand(spec, argv, parserOptions,
|
|
482
|
+
async function runSubCommand(spec, argv, parserOptions, parentFinish) {
|
|
454
483
|
const subCommand = await loadSubCommand(spec);
|
|
455
484
|
try {
|
|
456
485
|
await runCommandWithArgs(subCommand, argv, parserOptions);
|
|
457
486
|
} finally {
|
|
458
|
-
if (typeof
|
|
487
|
+
if (typeof parentFinish === "function") await parentFinish();
|
|
459
488
|
}
|
|
460
489
|
}
|
|
461
490
|
async function runCommandWithArgs(command, argv, parserOptions) {
|
|
@@ -540,7 +569,7 @@ async function runCommandWithArgs(command, argv, parserOptions) {
|
|
|
540
569
|
if (command.run) {
|
|
541
570
|
await command.run(ctx);
|
|
542
571
|
} else {
|
|
543
|
-
const isDispatcher = parserOptions.fileBasedCmds?.enable || command.
|
|
572
|
+
const isDispatcher = parserOptions.fileBasedCmds?.enable || command.commands && Object.keys(command.commands).length > 0;
|
|
544
573
|
const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
|
|
545
574
|
if (isDispatcher && noSubcommandArgInCurrentCall) {
|
|
546
575
|
relinka("warn", "Please specify a command");
|
|
@@ -608,3 +637,74 @@ function renderPositional(args) {
|
|
|
608
637
|
export function defineArgs(args) {
|
|
609
638
|
return args;
|
|
610
639
|
}
|
|
640
|
+
export async function runCmd(command, argv = [], parserOptions = {}) {
|
|
641
|
+
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
642
|
+
(k) => command.args[k].type === "boolean"
|
|
643
|
+
);
|
|
644
|
+
const defaultMap = {};
|
|
645
|
+
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
646
|
+
if (def.default !== void 0) {
|
|
647
|
+
if (def.type === "array" && typeof def.default === "string") {
|
|
648
|
+
defaultMap[argKey] = [def.default];
|
|
649
|
+
} else {
|
|
650
|
+
defaultMap[argKey] = def.default;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const mergedParserOptions = {
|
|
655
|
+
...parserOptions,
|
|
656
|
+
boolean: [...parserOptions.boolean || [], ...booleanKeys],
|
|
657
|
+
defaults: { ...defaultMap, ...parserOptions.defaults || {} }
|
|
658
|
+
};
|
|
659
|
+
const parsed = reliArgParser(argv, mergedParserOptions);
|
|
660
|
+
debugLog("Parsed arguments (runCmd):", parsed);
|
|
661
|
+
const finalArgs = {};
|
|
662
|
+
const positionalKeys = Object.keys(command.args || {}).filter(
|
|
663
|
+
(k) => command.args[k].type === "positional"
|
|
664
|
+
);
|
|
665
|
+
const leftoverPositionals = [...parsed._ || []];
|
|
666
|
+
for (let i = 0; i < positionalKeys.length; i++) {
|
|
667
|
+
const key = positionalKeys[i];
|
|
668
|
+
const def = command.args?.[key];
|
|
669
|
+
const val = leftoverPositionals[i];
|
|
670
|
+
if (val == null && def.required) {
|
|
671
|
+
throw new Error(`Missing required positional argument: <${key}>`);
|
|
672
|
+
}
|
|
673
|
+
finalArgs[key] = val != null ? castArgValue(def, val, key) : def.default;
|
|
674
|
+
}
|
|
675
|
+
const otherKeys = Object.keys(command.args || {}).filter(
|
|
676
|
+
(k) => command.args[k].type !== "positional"
|
|
677
|
+
);
|
|
678
|
+
for (const key of otherKeys) {
|
|
679
|
+
const def = command.args?.[key];
|
|
680
|
+
let rawVal = parsed[key];
|
|
681
|
+
if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
|
|
682
|
+
rawVal = [rawVal];
|
|
683
|
+
}
|
|
684
|
+
const valueOrDefault = rawVal ?? defaultMap[key];
|
|
685
|
+
if (valueOrDefault == null && def.required) {
|
|
686
|
+
throw new Error(`Missing required argument: --${key}`);
|
|
687
|
+
}
|
|
688
|
+
finalArgs[key] = castArgValue(def, rawVal, key);
|
|
689
|
+
if (def.type === "array" && def.options && finalArgs[key]) {
|
|
690
|
+
const values = finalArgs[key];
|
|
691
|
+
const invalidOptions = values.filter(
|
|
692
|
+
(v) => def.options && !def.options.includes(v)
|
|
693
|
+
);
|
|
694
|
+
if (invalidOptions.length > 0) {
|
|
695
|
+
throw new Error(
|
|
696
|
+
`Invalid choice(s) for --${key}: ${invalidOptions.join(", ")}. Allowed options: ${def.options.join(", ")}`
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const ctx = {
|
|
702
|
+
args: finalArgs,
|
|
703
|
+
raw: argv
|
|
704
|
+
};
|
|
705
|
+
if (typeof command.run === "function") {
|
|
706
|
+
await command.run(ctx);
|
|
707
|
+
} else {
|
|
708
|
+
throw new Error("Command has no run() handler.");
|
|
709
|
+
}
|
|
710
|
+
}
|
|
@@ -21,7 +21,10 @@ export const symbols = {
|
|
|
21
21
|
step_active: u("\u25C6", "\u2666"),
|
|
22
22
|
step_error: u("\u{1F5F4}", "x"),
|
|
23
23
|
info: u("\u2139", "i"),
|
|
24
|
-
|
|
24
|
+
log: u("\u2502", "|"),
|
|
25
|
+
success: u("\u2705", "\u2713"),
|
|
26
|
+
warn: u("\u26A0", "!"),
|
|
27
|
+
error: u("\u274C", "x")
|
|
25
28
|
};
|
|
26
29
|
function wrapThenStyle(input, wrap, typographyName, colorName, variantName, borderColor) {
|
|
27
30
|
if (!input) return "";
|
|
@@ -309,8 +312,8 @@ export function msgUndoAll() {
|
|
|
309
312
|
}
|
|
310
313
|
export function printLineBar(text, indent = 2) {
|
|
311
314
|
if (text === "") {
|
|
312
|
-
relinka("
|
|
315
|
+
relinka("log", re.dim("\u2502"));
|
|
313
316
|
} else {
|
|
314
|
-
relinka("
|
|
317
|
+
relinka("log", `${re.dim("\u2502")}${" ".repeat(indent)}${text}`);
|
|
315
318
|
}
|
|
316
319
|
}
|