alepha 0.13.8 → 0.14.1
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/api/audits/index.d.ts +418 -338
- package/dist/api/audits/index.d.ts.map +1 -0
- package/dist/api/files/index.d.ts +81 -1
- package/dist/api/files/index.d.ts.map +1 -0
- package/dist/api/jobs/index.d.ts +107 -27
- package/dist/api/jobs/index.d.ts.map +1 -0
- package/dist/api/notifications/index.d.ts +21 -1
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/parameters/index.d.ts +455 -8
- package/dist/api/parameters/index.d.ts.map +1 -0
- package/dist/api/users/index.d.ts +844 -840
- package/dist/api/users/index.d.ts.map +1 -0
- package/dist/api/verifications/index.d.ts.map +1 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/bucket/index.d.ts.map +1 -0
- package/dist/cache/core/index.d.ts.map +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -0
- package/dist/cli/index.d.ts +254 -59
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +499 -127
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +217 -10
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +350 -74
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1334 -1318
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +76 -72
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1337 -1321
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1337 -1321
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/fake/index.d.ts.map +1 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/file/index.js.map +1 -1
- package/dist/lock/core/index.d.ts.map +1 -0
- package/dist/lock/redis/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +820 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +978 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/orm/index.d.ts +234 -107
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +376 -316
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -0
- package/dist/queue/redis/index.d.ts.map +1 -0
- package/dist/queue/redis/index.js +2 -4
- package/dist/queue/redis/index.js.map +1 -1
- package/dist/redis/index.d.ts +400 -29
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/index.js +412 -21
- package/dist/redis/index.js.map +1 -1
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts +6 -6
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/security/index.d.ts +28 -28
- package/dist/security/index.d.ts.map +1 -0
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/cache/index.d.ts.map +1 -0
- package/dist/server/compress/index.d.ts.map +1 -0
- package/dist/server/cookies/index.d.ts.map +1 -0
- package/dist/server/core/index.d.ts +0 -1
- package/dist/server/core/index.d.ts.map +1 -0
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/cors/index.d.ts.map +1 -0
- package/dist/server/health/index.d.ts +17 -17
- package/dist/server/health/index.d.ts.map +1 -0
- package/dist/server/helmet/index.d.ts +4 -1
- package/dist/server/helmet/index.d.ts.map +1 -0
- package/dist/server/links/index.d.ts +33 -33
- package/dist/server/links/index.d.ts.map +1 -0
- package/dist/server/metrics/index.d.ts.map +1 -0
- package/dist/server/multipart/index.d.ts.map +1 -0
- package/dist/server/multipart/index.js.map +1 -1
- package/dist/server/proxy/index.d.ts.map +1 -0
- package/dist/server/proxy/index.js.map +1 -1
- package/dist/server/rate-limit/index.d.ts.map +1 -0
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.d.ts.map +1 -0
- package/dist/server/static/index.d.ts.map +1 -0
- package/dist/server/swagger/index.d.ts.map +1 -0
- package/dist/sms/index.d.ts.map +1 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/topic/core/index.d.ts.map +1 -0
- package/dist/topic/redis/index.d.ts.map +1 -0
- package/dist/topic/redis/index.js +3 -3
- package/dist/topic/redis/index.js.map +1 -1
- package/dist/vite/index.d.ts +10 -2
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +45 -20
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/cli/apps/AlephaCli.ts +10 -3
- package/src/cli/apps/AlephaPackageBuilderCli.ts +15 -8
- package/src/cli/assets/mainTs.ts +9 -10
- package/src/cli/atoms/changelogOptions.ts +45 -0
- package/src/cli/commands/ChangelogCommands.ts +259 -0
- package/src/cli/commands/DeployCommands.ts +118 -0
- package/src/cli/commands/DrizzleCommands.ts +230 -10
- package/src/cli/commands/ViteCommands.ts +47 -23
- package/src/cli/defineConfig.ts +15 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/services/AlephaCliUtils.ts +10 -154
- package/src/cli/services/GitMessageParser.ts +77 -0
- package/src/command/helpers/EnvUtils.ts +37 -0
- package/src/command/index.ts +3 -1
- package/src/command/primitives/$command.ts +172 -6
- package/src/command/providers/CliProvider.ts +499 -95
- package/src/core/Alepha.ts +1 -1
- package/src/core/providers/SchemaValidator.ts +23 -1
- package/src/file/providers/NodeFileSystemProvider.ts +3 -1
- package/src/mcp/errors/McpError.ts +72 -0
- package/src/mcp/helpers/jsonrpc.ts +163 -0
- package/src/mcp/index.ts +132 -0
- package/src/mcp/interfaces/McpTypes.ts +248 -0
- package/src/mcp/primitives/$prompt.ts +188 -0
- package/src/mcp/primitives/$resource.ts +171 -0
- package/src/mcp/primitives/$tool.ts +285 -0
- package/src/mcp/providers/McpServerProvider.ts +382 -0
- package/src/mcp/transports/SseMcpTransport.ts +172 -0
- package/src/mcp/transports/StdioMcpTransport.ts +126 -0
- package/src/orm/index.ts +20 -4
- package/src/orm/interfaces/PgQueryWhere.ts +1 -26
- package/src/orm/providers/drivers/BunPostgresProvider.ts +225 -0
- package/src/orm/providers/drivers/BunSqliteProvider.ts +180 -0
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
- package/src/orm/providers/drivers/DatabaseProvider.ts +25 -0
- package/src/orm/providers/drivers/NodePostgresProvider.ts +0 -25
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
- package/src/orm/services/QueryManager.ts +10 -125
- package/src/queue/redis/providers/RedisQueueProvider.ts +2 -7
- package/src/redis/index.ts +65 -3
- package/src/redis/providers/BunRedisProvider.ts +304 -0
- package/src/redis/providers/BunRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/NodeRedisProvider.ts +280 -0
- package/src/redis/providers/NodeRedisSubscriberProvider.ts +94 -0
- package/src/redis/providers/RedisProvider.ts +134 -140
- package/src/redis/providers/RedisSubscriberProvider.ts +58 -49
- package/src/server/core/providers/BunHttpServerProvider.ts +0 -3
- package/src/server/core/providers/ServerBodyParserProvider.ts +3 -1
- package/src/server/core/providers/ServerProvider.ts +7 -4
- package/src/server/multipart/providers/ServerMultipartProvider.ts +3 -1
- package/src/server/proxy/providers/ServerProxyProvider.ts +1 -1
- package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
- package/src/vite/plugins/viteAlephaBuild.ts +8 -2
- package/src/vite/plugins/viteAlephaDev.ts +6 -2
- package/src/vite/tasks/buildServer.ts +2 -1
- package/src/vite/tasks/generateCloudflare.ts +43 -15
- package/src/vite/tasks/runAlepha.ts +1 -0
- package/src/orm/services/PgJsonQueryManager.ts +0 -511
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { $logger } from "alepha/logger";
|
|
17
17
|
import { CommandError } from "../errors/CommandError.ts";
|
|
18
18
|
import { Asker } from "../helpers/Asker.ts";
|
|
19
|
+
import { EnvUtils } from "../helpers/EnvUtils.ts";
|
|
19
20
|
import { Runner } from "../helpers/Runner.ts";
|
|
20
21
|
import {
|
|
21
22
|
$command,
|
|
@@ -81,6 +82,7 @@ export class CliProvider {
|
|
|
81
82
|
protected readonly log = $logger();
|
|
82
83
|
protected readonly runner = $inject(Runner);
|
|
83
84
|
protected readonly asker = $inject(Asker);
|
|
85
|
+
protected readonly envUtils = $inject(EnvUtils);
|
|
84
86
|
|
|
85
87
|
protected readonly options = $use(cliOptions);
|
|
86
88
|
|
|
@@ -111,8 +113,12 @@ export class CliProvider {
|
|
|
111
113
|
on: "ready",
|
|
112
114
|
handler: async () => {
|
|
113
115
|
const argv = [...this.argv];
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
|
|
117
|
+
// Extract positional arguments (potential command path)
|
|
118
|
+
const positionalArgs = argv.filter((arg) => !arg.startsWith("-"));
|
|
119
|
+
|
|
120
|
+
// Resolve command using the new resolution logic
|
|
121
|
+
const { command, consumedArgs } = this.resolveCommand(positionalArgs);
|
|
116
122
|
|
|
117
123
|
const globalFlags = this.parseFlags(
|
|
118
124
|
argv,
|
|
@@ -128,73 +134,199 @@ export class CliProvider {
|
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
if (!command) {
|
|
131
|
-
//
|
|
137
|
+
// Check if there's a root command (name === "")
|
|
132
138
|
const rootCommand = this.findCommand("");
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
139
|
+
|
|
140
|
+
// If we have positional args but no matching command, show error
|
|
141
|
+
const commandName = positionalArgs[0] ?? "";
|
|
142
|
+
if (commandName !== "" && !rootCommand?.options.args) {
|
|
143
|
+
this.log.error(`Unknown command: '${commandName}'`);
|
|
144
|
+
this.printHelp();
|
|
140
145
|
return;
|
|
141
146
|
}
|
|
142
|
-
}
|
|
143
147
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
command.name === "",
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
await this.alepha.context.run(async () => {
|
|
152
|
-
this.log.debug(`Executing command '${command.name}'...`, {
|
|
153
|
-
flags: commandFlags,
|
|
154
|
-
args: commandArgs,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const runner = this.runner;
|
|
158
|
-
|
|
159
|
-
// Start command session for pretty print
|
|
160
|
-
runner.startCommand(this.name, command.name);
|
|
161
|
-
|
|
162
|
-
const args = {
|
|
163
|
-
flags: commandFlags,
|
|
164
|
-
args: commandArgs,
|
|
165
|
-
run: runner.run,
|
|
166
|
-
ask: this.asker.ask,
|
|
167
|
-
fs,
|
|
168
|
-
glob,
|
|
169
|
-
root: process.cwd(),
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// Execute pre-hooks
|
|
173
|
-
const preHooks = this.findPreHooks(command.name);
|
|
174
|
-
for (const hook of preHooks) {
|
|
175
|
-
this.log.debug(`Executing pre-hook for '${command.name}'...`);
|
|
176
|
-
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
148
|
+
// Execute root command if it exists
|
|
149
|
+
if (rootCommand) {
|
|
150
|
+
await this.executeCommand(rootCommand, argv, true);
|
|
151
|
+
return;
|
|
177
152
|
}
|
|
178
153
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// Execute post-hooks
|
|
183
|
-
const postHooks = this.findPostHooks(command.name);
|
|
184
|
-
for (const hook of postHooks) {
|
|
185
|
-
this.log.debug(`Executing post-hook for '${command.name}'...`);
|
|
186
|
-
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
187
|
-
}
|
|
154
|
+
// No command found and no root command
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
188
157
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
158
|
+
// Remove consumed command path args from argv for argument parsing
|
|
159
|
+
const remainingArgv = this.removeConsumedArgs(argv, consumedArgs);
|
|
192
160
|
|
|
193
|
-
|
|
194
|
-
|
|
161
|
+
// Since we've removed the command path, treat it like a root command for parsing
|
|
162
|
+
await this.executeCommand(command, remainingArgv, true);
|
|
195
163
|
},
|
|
196
164
|
});
|
|
197
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Execute a command with the given argv.
|
|
168
|
+
*/
|
|
169
|
+
protected async executeCommand(
|
|
170
|
+
command: CommandPrimitive<TObject>,
|
|
171
|
+
argv: string[],
|
|
172
|
+
isRootCommand: boolean,
|
|
173
|
+
): Promise<void> {
|
|
174
|
+
const root = process.cwd();
|
|
175
|
+
|
|
176
|
+
// Handle --mode flag if command has mode option enabled
|
|
177
|
+
let modeValue: string | undefined;
|
|
178
|
+
if (command.options.mode) {
|
|
179
|
+
modeValue = this.parseModeFlag(argv);
|
|
180
|
+
// Use default mode if not provided and mode is a string
|
|
181
|
+
if (modeValue === undefined && typeof command.options.mode === "string") {
|
|
182
|
+
modeValue = command.options.mode;
|
|
183
|
+
}
|
|
184
|
+
await this.loadModeEnv(root, modeValue);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const commandFlags = this.parseCommandFlags(argv, command.flags);
|
|
188
|
+
const commandArgs = this.parseCommandArgs(
|
|
189
|
+
argv,
|
|
190
|
+
command.options.args,
|
|
191
|
+
isRootCommand,
|
|
192
|
+
command.flags,
|
|
193
|
+
);
|
|
194
|
+
const commandEnv = this.parseCommandEnv(command.env, command.name);
|
|
195
|
+
|
|
196
|
+
await this.alepha.context.run(async () => {
|
|
197
|
+
this.log.debug(`Executing command '${command.name}'...`, {
|
|
198
|
+
flags: commandFlags,
|
|
199
|
+
args: commandArgs,
|
|
200
|
+
mode: modeValue,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const runner = this.runner;
|
|
204
|
+
|
|
205
|
+
// Start command session for pretty print
|
|
206
|
+
runner.startCommand(this.name, command.name);
|
|
207
|
+
|
|
208
|
+
const args = {
|
|
209
|
+
flags: commandFlags,
|
|
210
|
+
args: commandArgs,
|
|
211
|
+
env: commandEnv,
|
|
212
|
+
run: runner.run,
|
|
213
|
+
ask: this.asker.ask,
|
|
214
|
+
fs,
|
|
215
|
+
glob,
|
|
216
|
+
root,
|
|
217
|
+
help: () => this.printHelp(command),
|
|
218
|
+
mode: modeValue,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Execute pre-hooks
|
|
222
|
+
const preHooks = this.findPreHooks(command.name);
|
|
223
|
+
for (const hook of preHooks) {
|
|
224
|
+
this.log.debug(`Executing pre-hook for '${command.name}'...`);
|
|
225
|
+
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Execute main command
|
|
229
|
+
await command.options.handler(args as CommandHandlerArgs<TObject>);
|
|
230
|
+
|
|
231
|
+
// Execute post-hooks
|
|
232
|
+
const postHooks = this.findPostHooks(command.name);
|
|
233
|
+
for (const hook of postHooks) {
|
|
234
|
+
this.log.debug(`Executing post-hook for '${command.name}'...`);
|
|
235
|
+
await hook.options.handler(args as CommandHandlerArgs<TObject>);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (command.options.summary !== false) {
|
|
239
|
+
runner.summary();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
this.log.debug(`Command '${command.name}' executed successfully.`);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Remove consumed command path arguments from argv.
|
|
248
|
+
*/
|
|
249
|
+
protected removeConsumedArgs(
|
|
250
|
+
argv: string[],
|
|
251
|
+
consumedArgs: string[],
|
|
252
|
+
): string[] {
|
|
253
|
+
const result: string[] = [];
|
|
254
|
+
let consumedIndex = 0;
|
|
255
|
+
|
|
256
|
+
for (const arg of argv) {
|
|
257
|
+
if (arg.startsWith("-")) {
|
|
258
|
+
result.push(arg);
|
|
259
|
+
} else if (
|
|
260
|
+
consumedIndex < consumedArgs.length &&
|
|
261
|
+
arg === consumedArgs[consumedIndex]
|
|
262
|
+
) {
|
|
263
|
+
consumedIndex++;
|
|
264
|
+
// Skip this arg, it's part of the command path
|
|
265
|
+
} else {
|
|
266
|
+
result.push(arg);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Resolve a command from positional arguments.
|
|
275
|
+
*
|
|
276
|
+
* Supports:
|
|
277
|
+
* 1. Space-separated subcommands: `deploy vercel` -> finds deploy command, then vercel child
|
|
278
|
+
* 2. Colon notation (backwards compat): `deploy:vercel` -> finds command with name "deploy:vercel"
|
|
279
|
+
* 3. Simple commands: `build` -> finds command with name "build"
|
|
280
|
+
*/
|
|
281
|
+
protected resolveCommand(positionalArgs: string[]): {
|
|
282
|
+
command: CommandPrimitive<TObject> | undefined;
|
|
283
|
+
consumedArgs: string[];
|
|
284
|
+
} {
|
|
285
|
+
if (positionalArgs.length === 0) {
|
|
286
|
+
return { command: undefined, consumedArgs: [] };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const firstArg = positionalArgs[0];
|
|
290
|
+
|
|
291
|
+
// First, try colon notation for backwards compatibility (e.g., "deploy:vercel")
|
|
292
|
+
if (firstArg.includes(":")) {
|
|
293
|
+
const command = this.findCommand(firstArg);
|
|
294
|
+
if (command) {
|
|
295
|
+
return { command, consumedArgs: [firstArg] };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Try to find command with space-separated subcommand path
|
|
300
|
+
let currentCommand = this.findCommand(firstArg);
|
|
301
|
+
const consumedArgs: string[] = [];
|
|
302
|
+
|
|
303
|
+
if (!currentCommand) {
|
|
304
|
+
return { command: undefined, consumedArgs: [] };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
consumedArgs.push(firstArg);
|
|
308
|
+
|
|
309
|
+
// Walk through remaining args to find nested subcommands
|
|
310
|
+
for (let i = 1; i < positionalArgs.length; i++) {
|
|
311
|
+
const arg = positionalArgs[i];
|
|
312
|
+
|
|
313
|
+
if (!currentCommand.hasChildren) {
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const childCommand = currentCommand.findChild(arg);
|
|
318
|
+
if (childCommand) {
|
|
319
|
+
currentCommand = childCommand;
|
|
320
|
+
consumedArgs.push(arg);
|
|
321
|
+
} else {
|
|
322
|
+
// No matching child, stop here
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return { command: currentCommand, consumedArgs };
|
|
328
|
+
}
|
|
329
|
+
|
|
198
330
|
public get commands(): CommandPrimitive<any>[] {
|
|
199
331
|
return this.alepha.primitives($command);
|
|
200
332
|
}
|
|
@@ -220,35 +352,13 @@ export class CliProvider {
|
|
|
220
352
|
}
|
|
221
353
|
|
|
222
354
|
/**
|
|
223
|
-
* Get
|
|
355
|
+
* Get global flags (help only, root command flags are NOT global).
|
|
224
356
|
*/
|
|
225
357
|
protected getAllGlobalFlags(): Record<
|
|
226
358
|
string,
|
|
227
359
|
{ aliases: string[]; description?: string; schema: TSchema }
|
|
228
360
|
> {
|
|
229
|
-
|
|
230
|
-
const allGlobalFlags: Record<
|
|
231
|
-
string,
|
|
232
|
-
{ aliases: string[]; description?: string; schema: TSchema }
|
|
233
|
-
> = { ...this.globalFlags };
|
|
234
|
-
|
|
235
|
-
if (rootCommand) {
|
|
236
|
-
// Add root command flags to global flags
|
|
237
|
-
for (const [key, value] of Object.entries(rootCommand.flags.properties)) {
|
|
238
|
-
allGlobalFlags[key] = {
|
|
239
|
-
aliases: [
|
|
240
|
-
key,
|
|
241
|
-
...((value as any).aliases ??
|
|
242
|
-
((value as any).alias ? [(value as any).alias] : undefined) ??
|
|
243
|
-
[]),
|
|
244
|
-
],
|
|
245
|
-
description: (value as any).description,
|
|
246
|
-
schema: value as TSchema,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return allGlobalFlags;
|
|
361
|
+
return { ...this.globalFlags };
|
|
252
362
|
}
|
|
253
363
|
|
|
254
364
|
protected parseCommandFlags(
|
|
@@ -291,15 +401,99 @@ export class CliProvider {
|
|
|
291
401
|
}
|
|
292
402
|
}
|
|
293
403
|
|
|
404
|
+
protected parseCommandEnv(
|
|
405
|
+
schema: TObject,
|
|
406
|
+
commandName: string,
|
|
407
|
+
): Record<string, any> {
|
|
408
|
+
const result: Record<string, any> = {};
|
|
409
|
+
const missing: string[] = [];
|
|
410
|
+
|
|
411
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
412
|
+
const value = process.env[key];
|
|
413
|
+
|
|
414
|
+
if (value !== undefined) {
|
|
415
|
+
result[key] = value;
|
|
416
|
+
} else if (t.schema.isOptional(propSchema)) {
|
|
417
|
+
// Check for default value
|
|
418
|
+
if ("default" in propSchema) {
|
|
419
|
+
result[key] = propSchema.default;
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
missing.push(key);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (missing.length > 0) {
|
|
427
|
+
const vars = missing.join(", ");
|
|
428
|
+
throw new CommandError(
|
|
429
|
+
`Missing required environment variable${missing.length > 1 ? "s" : ""}: ${vars}`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
return this.alepha.codec.decode(schema, result);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
if (error instanceof TypeBoxError) {
|
|
437
|
+
throw new CommandError(
|
|
438
|
+
`Invalid environment variable: ${error.cause.instancePath || "env"} ${error.cause.message}`,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Parse --mode or -m flag from argv.
|
|
447
|
+
*/
|
|
448
|
+
protected parseModeFlag(argv: string[]): string | undefined {
|
|
449
|
+
for (let i = 0; i < argv.length; i++) {
|
|
450
|
+
const arg = argv[i];
|
|
451
|
+
|
|
452
|
+
// Handle --mode=value or -m=value
|
|
453
|
+
if (arg.startsWith("--mode=") || arg.startsWith("-m=")) {
|
|
454
|
+
return arg.split("=")[1];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Handle --mode value or -m value
|
|
458
|
+
if (arg === "--mode" || arg === "-m") {
|
|
459
|
+
const nextArg = argv[i + 1];
|
|
460
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
461
|
+
return nextArg;
|
|
462
|
+
}
|
|
463
|
+
throw new CommandError("Flag --mode requires a value.");
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Load environment files based on mode.
|
|
472
|
+
*/
|
|
473
|
+
protected async loadModeEnv(
|
|
474
|
+
root: string,
|
|
475
|
+
mode: string | undefined,
|
|
476
|
+
): Promise<void> {
|
|
477
|
+
const envFiles = [".env"];
|
|
478
|
+
if (mode) {
|
|
479
|
+
envFiles.push(`.env.${mode}`);
|
|
480
|
+
}
|
|
481
|
+
this.log.debug(`Loading env files: ${envFiles.join(", ")}`);
|
|
482
|
+
await this.envUtils.loadEnv(root, envFiles);
|
|
483
|
+
}
|
|
484
|
+
|
|
294
485
|
protected parseFlags(
|
|
295
486
|
argv: string[],
|
|
296
487
|
flagDefs: { key: string; aliases: string[]; schema: TSchema }[],
|
|
297
488
|
): Record<string, any> {
|
|
298
489
|
const result: Record<string, any> = {};
|
|
299
490
|
|
|
300
|
-
for (
|
|
491
|
+
for (let i = 0; i < argv.length; i++) {
|
|
492
|
+
const arg = argv[i];
|
|
493
|
+
if (!arg.startsWith("-")) continue;
|
|
494
|
+
|
|
301
495
|
const [rawKey, ...valueParts] = arg.replace(/^-{1,2}/, "").split("=");
|
|
302
|
-
|
|
496
|
+
let value = valueParts.join("=");
|
|
303
497
|
|
|
304
498
|
const def = flagDefs.find((d) => d.aliases.includes(rawKey));
|
|
305
499
|
if (!def) continue;
|
|
@@ -307,6 +501,7 @@ export class CliProvider {
|
|
|
307
501
|
if (t.schema.isBoolean(def.schema)) {
|
|
308
502
|
result[def.key] = true;
|
|
309
503
|
} else if (value) {
|
|
504
|
+
// Value provided via --flag=value syntax
|
|
310
505
|
try {
|
|
311
506
|
if (t.schema.isObject(def.schema) || t.schema.isArray(def.schema)) {
|
|
312
507
|
result[def.key] = JSON.parse(value);
|
|
@@ -317,24 +512,90 @@ export class CliProvider {
|
|
|
317
512
|
throw new CommandError(`Invalid JSON value for flag --${rawKey}`);
|
|
318
513
|
}
|
|
319
514
|
} else {
|
|
320
|
-
|
|
515
|
+
// Check for space-separated value: --flag value
|
|
516
|
+
const nextArg = argv[i + 1];
|
|
517
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
518
|
+
value = nextArg;
|
|
519
|
+
try {
|
|
520
|
+
if (t.schema.isObject(def.schema) || t.schema.isArray(def.schema)) {
|
|
521
|
+
result[def.key] = JSON.parse(value);
|
|
522
|
+
} else {
|
|
523
|
+
result[def.key] = value;
|
|
524
|
+
}
|
|
525
|
+
} catch {
|
|
526
|
+
throw new CommandError(`Invalid JSON value for flag --${rawKey}`);
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
throw new CommandError(`Flag --${rawKey} requires a value.`);
|
|
530
|
+
}
|
|
321
531
|
}
|
|
322
532
|
}
|
|
323
533
|
|
|
324
534
|
return result;
|
|
325
535
|
}
|
|
326
536
|
|
|
537
|
+
/**
|
|
538
|
+
* Get indices of argv elements that are consumed by flags (including space-separated values).
|
|
539
|
+
*/
|
|
540
|
+
protected getFlagConsumedIndices(
|
|
541
|
+
argv: string[],
|
|
542
|
+
flagDefs: { key: string; aliases: string[]; schema: TSchema }[],
|
|
543
|
+
): Set<number> {
|
|
544
|
+
const consumed = new Set<number>();
|
|
545
|
+
|
|
546
|
+
for (let i = 0; i < argv.length; i++) {
|
|
547
|
+
const arg = argv[i];
|
|
548
|
+
if (!arg.startsWith("-")) continue;
|
|
549
|
+
|
|
550
|
+
consumed.add(i);
|
|
551
|
+
|
|
552
|
+
const [rawKey, ...valueParts] = arg.replace(/^-{1,2}/, "").split("=");
|
|
553
|
+
const hasEqualValue = valueParts.length > 0;
|
|
554
|
+
|
|
555
|
+
const def = flagDefs.find((d) => d.aliases.includes(rawKey));
|
|
556
|
+
if (!def) continue;
|
|
557
|
+
|
|
558
|
+
// If not a boolean flag and no = value, the next arg is consumed as the value
|
|
559
|
+
if (!t.schema.isBoolean(def.schema) && !hasEqualValue) {
|
|
560
|
+
const nextArg = argv[i + 1];
|
|
561
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
562
|
+
consumed.add(i + 1);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return consumed;
|
|
568
|
+
}
|
|
569
|
+
|
|
327
570
|
protected parseCommandArgs(
|
|
328
571
|
argv: string[],
|
|
329
572
|
schema?: TSchema,
|
|
330
573
|
isRootCommand = false,
|
|
574
|
+
flagSchema?: TObject,
|
|
331
575
|
): any {
|
|
332
576
|
if (!schema) {
|
|
333
577
|
return undefined;
|
|
334
578
|
}
|
|
335
579
|
|
|
336
|
-
//
|
|
337
|
-
const
|
|
580
|
+
// Get indices consumed by flags (including space-separated values)
|
|
581
|
+
const flagDefs = flagSchema
|
|
582
|
+
? Object.entries(flagSchema.properties).map(([key, value]) => ({
|
|
583
|
+
key,
|
|
584
|
+
aliases: [
|
|
585
|
+
key,
|
|
586
|
+
...((value as any).aliases ??
|
|
587
|
+
((value as any).alias ? [(value as any).alias] : undefined) ??
|
|
588
|
+
[]),
|
|
589
|
+
],
|
|
590
|
+
schema: value as TSchema,
|
|
591
|
+
}))
|
|
592
|
+
: [];
|
|
593
|
+
const consumedIndices = this.getFlagConsumedIndices(argv, flagDefs);
|
|
594
|
+
|
|
595
|
+
// Extract positional arguments (non-flag arguments that aren't consumed as flag values)
|
|
596
|
+
const positionalArgs = argv.filter(
|
|
597
|
+
(arg, idx) => !arg.startsWith("-") && !consumedIndices.has(idx),
|
|
598
|
+
);
|
|
338
599
|
// For root commands, there's no command name to remove; otherwise slice off the command name
|
|
339
600
|
const argsOnly = isRootCommand ? positionalArgs : positionalArgs.slice(1);
|
|
340
601
|
|
|
@@ -451,8 +712,12 @@ export class CliProvider {
|
|
|
451
712
|
|
|
452
713
|
if (command?.name) {
|
|
453
714
|
// Command-specific help
|
|
454
|
-
const
|
|
455
|
-
const
|
|
715
|
+
const hasChildren = command.hasChildren;
|
|
716
|
+
const argsUsage = hasChildren
|
|
717
|
+
? " <command>"
|
|
718
|
+
: this.generateArgsUsage(command.options.args);
|
|
719
|
+
const commandPath = this.getCommandPath(command);
|
|
720
|
+
const usage = `${cliName} ${commandPath}${argsUsage}`.trim();
|
|
456
721
|
this.log.info(`Usage: \`${usage}\``);
|
|
457
722
|
|
|
458
723
|
if (command.options.description) {
|
|
@@ -460,6 +725,25 @@ export class CliProvider {
|
|
|
460
725
|
this.log.info(`\t${command.options.description}`);
|
|
461
726
|
}
|
|
462
727
|
|
|
728
|
+
// Show subcommands if this is a parent command
|
|
729
|
+
if (hasChildren) {
|
|
730
|
+
this.log.info("");
|
|
731
|
+
this.log.info("Commands:");
|
|
732
|
+
const maxSubCmdLength = this.getMaxChildCmdLength(command.children);
|
|
733
|
+
|
|
734
|
+
for (const child of command.children) {
|
|
735
|
+
if (child.options.hide) {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
const childArgsUsage = this.generateArgsUsage(child.options.args);
|
|
739
|
+
const cmdStr = [child.name, ...child.aliases].join(", ");
|
|
740
|
+
const fullCmdStr = `${cmdStr}${childArgsUsage}`;
|
|
741
|
+
this.log.info(
|
|
742
|
+
` ${cliName} ${command.name} ${fullCmdStr.padEnd(maxSubCmdLength)} # ${child.options.description ?? ""}`,
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
463
747
|
this.log.info("");
|
|
464
748
|
this.log.info("Flags:");
|
|
465
749
|
|
|
@@ -470,6 +754,19 @@ export class CliProvider {
|
|
|
470
754
|
aliases: (value as any).alias ?? [key],
|
|
471
755
|
description: (value as any).description,
|
|
472
756
|
})),
|
|
757
|
+
// Add --mode flag if command has mode option enabled
|
|
758
|
+
...(command.options.mode
|
|
759
|
+
? [
|
|
760
|
+
{
|
|
761
|
+
key: "mode",
|
|
762
|
+
aliases: ["m", "mode"],
|
|
763
|
+
description:
|
|
764
|
+
typeof command.options.mode === "string"
|
|
765
|
+
? `Environment mode - loads .env.{mode} (default: ${command.options.mode})`
|
|
766
|
+
: "Environment mode (e.g., production, staging) - loads .env.{mode}",
|
|
767
|
+
},
|
|
768
|
+
]
|
|
769
|
+
: []),
|
|
473
770
|
...Object.entries(this.getAllGlobalFlags()).map(([key, value]) => ({
|
|
474
771
|
key,
|
|
475
772
|
...value,
|
|
@@ -485,21 +782,42 @@ export class CliProvider {
|
|
|
485
782
|
` ${flagStr.padEnd(maxFlagLength)} # ${description ?? ""}`,
|
|
486
783
|
);
|
|
487
784
|
}
|
|
785
|
+
|
|
786
|
+
// Show environment variables if defined
|
|
787
|
+
const envVars = Object.entries(command.env.properties);
|
|
788
|
+
if (envVars.length > 0) {
|
|
789
|
+
this.log.info("");
|
|
790
|
+
this.log.info("Env:");
|
|
791
|
+
const maxEnvLength = Math.max(...envVars.map(([key]) => key.length));
|
|
792
|
+
for (const [key, schema] of envVars) {
|
|
793
|
+
const isOptional = t.schema.isOptional(schema as TSchema);
|
|
794
|
+
const description = (schema as any).description ?? "";
|
|
795
|
+
const optionalStr = isOptional ? " (optional)" : "";
|
|
796
|
+
this.log.info(
|
|
797
|
+
` ${key.padEnd(maxEnvLength)} # ${description}${optionalStr}`,
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
488
801
|
} else {
|
|
489
802
|
// general help
|
|
490
803
|
this.log.info(this.description || "Available commands:");
|
|
491
804
|
this.log.info("");
|
|
492
805
|
this.log.info("Commands:");
|
|
493
|
-
const maxCmdLength = this.getMaxCmdLength(this.commands);
|
|
494
806
|
|
|
495
|
-
|
|
807
|
+
// Get top-level commands (commands that are not children of other commands)
|
|
808
|
+
const topLevelCommands = this.getTopLevelCommands();
|
|
809
|
+
const maxCmdLength = this.getMaxCmdLength(topLevelCommands);
|
|
810
|
+
|
|
811
|
+
for (const command of topLevelCommands) {
|
|
496
812
|
// skip root command and hooks in list
|
|
497
813
|
if (command.name === "" || command.options.hide) {
|
|
498
814
|
continue;
|
|
499
815
|
}
|
|
500
816
|
|
|
501
817
|
const cmdStr = [command.name, ...command.aliases].join(", ");
|
|
502
|
-
const argsUsage =
|
|
818
|
+
const argsUsage = command.hasChildren
|
|
819
|
+
? " <command>"
|
|
820
|
+
: this.generateArgsUsage(command.options.args);
|
|
503
821
|
const fullCmdStr = `${cmdStr}${argsUsage}`;
|
|
504
822
|
this.log.info(
|
|
505
823
|
` ${cliName} ${fullCmdStr.padEnd(maxCmdLength)} # ${command.options.description ?? ""}`,
|
|
@@ -508,7 +826,26 @@ export class CliProvider {
|
|
|
508
826
|
|
|
509
827
|
this.log.info("");
|
|
510
828
|
this.log.info("Flags:");
|
|
511
|
-
|
|
829
|
+
|
|
830
|
+
// In general help, also show root command flags
|
|
831
|
+
const rootCommand = this.commands.find((cmd) => cmd.name === "");
|
|
832
|
+
const rootFlags = rootCommand
|
|
833
|
+
? Object.entries(rootCommand.flags.properties).map(([key, value]) => ({
|
|
834
|
+
key,
|
|
835
|
+
aliases: [
|
|
836
|
+
key,
|
|
837
|
+
...((value as any).aliases ??
|
|
838
|
+
((value as any).alias ? [(value as any).alias] : undefined) ??
|
|
839
|
+
[]),
|
|
840
|
+
],
|
|
841
|
+
description: (value as any).description,
|
|
842
|
+
}))
|
|
843
|
+
: [];
|
|
844
|
+
|
|
845
|
+
const globalFlags = [
|
|
846
|
+
...rootFlags,
|
|
847
|
+
...Object.values(this.getAllGlobalFlags()),
|
|
848
|
+
];
|
|
512
849
|
const maxFlagLength = this.getMaxFlagLength(globalFlags);
|
|
513
850
|
for (const { aliases, description } of globalFlags) {
|
|
514
851
|
const flagStr = aliases
|
|
@@ -522,13 +859,80 @@ export class CliProvider {
|
|
|
522
859
|
this.log.info(""); // Newline
|
|
523
860
|
}
|
|
524
861
|
|
|
525
|
-
|
|
862
|
+
/**
|
|
863
|
+
* Get the full command path (e.g., "deploy vercel" for a child command).
|
|
864
|
+
*/
|
|
865
|
+
protected getCommandPath(command: CommandPrimitive<any>): string {
|
|
866
|
+
const path: string[] = [command.name];
|
|
867
|
+
let current = command;
|
|
868
|
+
|
|
869
|
+
// Walk up the tree to find parents
|
|
870
|
+
while (true) {
|
|
871
|
+
const parent = this.findParentCommand(current);
|
|
872
|
+
if (!parent) break;
|
|
873
|
+
path.unshift(parent.name);
|
|
874
|
+
current = parent;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
return path.join(" ");
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Find the parent command of a given command.
|
|
882
|
+
*/
|
|
883
|
+
protected findParentCommand(
|
|
884
|
+
command: CommandPrimitive<any>,
|
|
885
|
+
): CommandPrimitive<any> | undefined {
|
|
886
|
+
for (const cmd of this.commands) {
|
|
887
|
+
if (cmd.children.includes(command)) {
|
|
888
|
+
return cmd;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return undefined;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Get top-level commands (commands that are not children of other commands).
|
|
896
|
+
*/
|
|
897
|
+
protected getTopLevelCommands(): CommandPrimitive<any>[] {
|
|
898
|
+
const allChildren = new Set<CommandPrimitive<any>>();
|
|
899
|
+
|
|
900
|
+
// Collect all children
|
|
901
|
+
for (const command of this.commands) {
|
|
902
|
+
for (const child of command.children) {
|
|
903
|
+
allChildren.add(child);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Return commands that are not children
|
|
908
|
+
return this.commands.filter((cmd) => !allChildren.has(cmd));
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Get max length for child command display.
|
|
913
|
+
*/
|
|
914
|
+
protected getMaxChildCmdLength(children: CommandPrimitive<any>[]): number {
|
|
915
|
+
return Math.max(
|
|
916
|
+
...children
|
|
917
|
+
.filter((c) => !c.options.hide)
|
|
918
|
+
.map((c) => {
|
|
919
|
+
const cmdStr = [c.name, ...c.aliases].join(", ");
|
|
920
|
+
const argsUsage = this.generateArgsUsage(c.options.args);
|
|
921
|
+
return `${cmdStr}${argsUsage}`.length;
|
|
922
|
+
}),
|
|
923
|
+
0,
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
protected getMaxCmdLength(commands: CommandPrimitive[]): number {
|
|
526
928
|
return Math.max(
|
|
527
929
|
...commands
|
|
528
930
|
.filter((c) => !c.options.hide && c.name !== "")
|
|
529
931
|
.map((c) => {
|
|
530
932
|
const cmdStr = [c.name, ...c.aliases].join(", ");
|
|
531
|
-
const argsUsage =
|
|
933
|
+
const argsUsage = c.hasChildren
|
|
934
|
+
? " <command>"
|
|
935
|
+
: this.generateArgsUsage(c.options.args);
|
|
532
936
|
return `${cmdStr}${argsUsage}`.length;
|
|
533
937
|
}),
|
|
534
938
|
);
|