@agentuity/cli 0.0.43 → 0.0.45
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/AGENTS.md +1 -1
- package/README.md +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/auth.d.ts +10 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/banner.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cmd/auth/api.d.ts +4 -4
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/index.d.ts.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/signup.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/add.d.ts +2 -0
- package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/api.d.ts +16 -0
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/delete.d.ts +2 -0
- package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/index.d.ts +3 -0
- package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
- package/dist/cmd/auth/ssh/list.d.ts +2 -0
- package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
- package/dist/cmd/auth/whoami.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.d.ts +14 -3
- package/dist/cmd/bundle/ast.d.ts.map +1 -1
- package/dist/cmd/bundle/ast.test.d.ts +2 -0
- package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
- package/dist/cmd/bundle/bundler.d.ts +6 -1
- package/dist/cmd/bundle/bundler.d.ts.map +1 -1
- package/dist/cmd/bundle/file.d.ts.map +1 -1
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
- package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
- package/dist/cmd/bundle/plugin.d.ts +2 -0
- package/dist/cmd/bundle/plugin.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/domain.d.ts +17 -0
- package/dist/cmd/cloud/domain.d.ts.map +1 -0
- package/dist/cmd/cloud/index.d.ts.map +1 -1
- package/dist/cmd/cloud/resource/add.d.ts +2 -0
- package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/delete.d.ts +2 -0
- package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/index.d.ts +3 -0
- package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
- package/dist/cmd/cloud/resource/list.d.ts +2 -0
- package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/download.d.ts +2 -0
- package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/index.d.ts +3 -0
- package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
- package/dist/cmd/cloud/scp/upload.d.ts +2 -0
- package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
- package/dist/cmd/cloud/ssh.d.ts +2 -0
- package/dist/cmd/cloud/ssh.d.ts.map +1 -0
- package/dist/cmd/dev/api.d.ts +18 -0
- package/dist/cmd/dev/api.d.ts.map +1 -0
- package/dist/cmd/dev/download.d.ts +11 -0
- package/dist/cmd/dev/download.d.ts.map +1 -0
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/templates.d.ts +3 -0
- package/dist/cmd/dev/templates.d.ts.map +1 -0
- package/dist/cmd/env/delete.d.ts.map +1 -1
- package/dist/cmd/env/get.d.ts.map +1 -1
- package/dist/cmd/env/import.d.ts.map +1 -1
- package/dist/cmd/env/list.d.ts.map +1 -1
- package/dist/cmd/env/pull.d.ts.map +1 -1
- package/dist/cmd/env/push.d.ts.map +1 -1
- package/dist/cmd/env/set.d.ts.map +1 -1
- package/dist/cmd/profile/show.d.ts.map +1 -1
- package/dist/cmd/project/create.d.ts.map +1 -1
- package/dist/cmd/project/delete.d.ts.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.d.ts +4 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/secret/delete.d.ts.map +1 -1
- package/dist/cmd/secret/get.d.ts.map +1 -1
- package/dist/cmd/secret/import.d.ts.map +1 -1
- package/dist/cmd/secret/list.d.ts.map +1 -1
- package/dist/cmd/secret/pull.d.ts.map +1 -1
- package/dist/cmd/secret/push.d.ts.map +1 -1
- package/dist/cmd/secret/set.d.ts.map +1 -1
- package/dist/config.d.ts +9 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/crypto/box.d.ts +65 -0
- package/dist/crypto/box.d.ts.map +1 -0
- package/dist/crypto/box.test.d.ts +2 -0
- package/dist/crypto/box.test.d.ts.map +1 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/steps.d.ts +4 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/tui.d.ts +31 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/types.d.ts +249 -126
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/detectSubagent.d.ts +15 -0
- package/dist/utils/detectSubagent.d.ts.map +1 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/package.json +11 -3
- package/src/api-errors.md +2 -2
- package/src/api.ts +12 -7
- package/src/auth.ts +116 -7
- package/src/banner.ts +13 -6
- package/src/cli.ts +695 -63
- package/src/cmd/auth/api.ts +10 -16
- package/src/cmd/auth/index.ts +2 -1
- package/src/cmd/auth/login.ts +24 -8
- package/src/cmd/auth/signup.ts +15 -11
- package/src/cmd/auth/ssh/add.ts +263 -0
- package/src/cmd/auth/ssh/api.ts +94 -0
- package/src/cmd/auth/ssh/delete.ts +102 -0
- package/src/cmd/auth/ssh/index.ts +10 -0
- package/src/cmd/auth/ssh/list.ts +74 -0
- package/src/cmd/auth/whoami.ts +13 -13
- package/src/cmd/bundle/ast.test.ts +565 -0
- package/src/cmd/bundle/ast.ts +457 -44
- package/src/cmd/bundle/bundler.ts +255 -57
- package/src/cmd/bundle/file.ts +6 -12
- package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
- package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
- package/src/cmd/bundle/index.ts +9 -9
- package/src/cmd/bundle/patch/aisdk.ts +1 -1
- package/src/cmd/bundle/plugin.ts +373 -53
- package/src/cmd/cloud/deploy.ts +300 -93
- package/src/cmd/cloud/domain.ts +92 -0
- package/src/cmd/cloud/index.ts +4 -1
- package/src/cmd/cloud/resource/add.ts +56 -0
- package/src/cmd/cloud/resource/delete.ts +120 -0
- package/src/cmd/cloud/resource/index.ts +11 -0
- package/src/cmd/cloud/resource/list.ts +69 -0
- package/src/cmd/cloud/scp/download.ts +59 -0
- package/src/cmd/cloud/scp/index.ts +9 -0
- package/src/cmd/cloud/scp/upload.ts +62 -0
- package/src/cmd/cloud/ssh.ts +68 -0
- package/src/cmd/dev/api.ts +46 -0
- package/src/cmd/dev/download.ts +111 -0
- package/src/cmd/dev/index.ts +360 -34
- package/src/cmd/dev/templates.ts +84 -0
- package/src/cmd/env/delete.ts +5 -20
- package/src/cmd/env/get.ts +5 -18
- package/src/cmd/env/import.ts +5 -20
- package/src/cmd/env/list.ts +5 -18
- package/src/cmd/env/pull.ts +10 -23
- package/src/cmd/env/push.ts +5 -23
- package/src/cmd/env/set.ts +5 -20
- package/src/cmd/index.ts +2 -2
- package/src/cmd/profile/show.ts +15 -6
- package/src/cmd/project/create.ts +7 -2
- package/src/cmd/project/delete.ts +75 -18
- package/src/cmd/project/download.ts +2 -2
- package/src/cmd/project/list.ts +8 -8
- package/src/cmd/project/show.ts +3 -7
- package/src/cmd/project/template-flow.ts +170 -72
- package/src/cmd/secret/delete.ts +5 -20
- package/src/cmd/secret/get.ts +5 -18
- package/src/cmd/secret/import.ts +5 -20
- package/src/cmd/secret/list.ts +5 -18
- package/src/cmd/secret/pull.ts +10 -23
- package/src/cmd/secret/push.ts +5 -23
- package/src/cmd/secret/set.ts +5 -20
- package/src/config.ts +224 -24
- package/src/crypto/box.test.ts +431 -0
- package/src/crypto/box.ts +477 -0
- package/src/download.ts +1 -0
- package/src/env-util.test.ts +1 -1
- package/src/steps.ts +65 -6
- package/src/terminal.ts +24 -23
- package/src/tui.ts +192 -61
- package/src/types.ts +291 -201
- package/src/utils/detectSubagent.ts +31 -0
- package/src/utils/zip.ts +38 -0
- package/dist/cmd/example/create-user.d.ts +0 -2
- package/dist/cmd/example/create-user.d.ts.map +0 -1
- package/dist/cmd/example/create.d.ts +0 -2
- package/dist/cmd/example/create.d.ts.map +0 -1
- package/dist/cmd/example/deploy.d.ts +0 -2
- package/dist/cmd/example/deploy.d.ts.map +0 -1
- package/dist/cmd/example/index.d.ts +0 -2
- package/dist/cmd/example/index.d.ts.map +0 -1
- package/dist/cmd/example/list.d.ts +0 -2
- package/dist/cmd/example/list.d.ts.map +0 -1
- package/dist/cmd/example/optional-auth.d.ts +0 -3
- package/dist/cmd/example/optional-auth.d.ts.map +0 -1
- package/dist/cmd/example/run-command.d.ts +0 -2
- package/dist/cmd/example/run-command.d.ts.map +0 -1
- package/dist/cmd/example/sound.d.ts +0 -3
- package/dist/cmd/example/sound.d.ts.map +0 -1
- package/dist/cmd/example/spinner.d.ts +0 -2
- package/dist/cmd/example/spinner.d.ts.map +0 -1
- package/dist/cmd/example/steps.d.ts +0 -2
- package/dist/cmd/example/steps.d.ts.map +0 -1
- package/dist/cmd/example/version.d.ts +0 -2
- package/dist/cmd/example/version.d.ts.map +0 -1
- package/src/cmd/example/create-user.ts +0 -38
- package/src/cmd/example/create.ts +0 -31
- package/src/cmd/example/deploy.ts +0 -36
- package/src/cmd/example/index.ts +0 -29
- package/src/cmd/example/list.ts +0 -32
- package/src/cmd/example/optional-auth.ts +0 -38
- package/src/cmd/example/run-command.ts +0 -45
- package/src/cmd/example/sound.ts +0 -14
- package/src/cmd/example/spinner.ts +0 -44
- package/src/cmd/example/steps.ts +0 -66
- package/src/cmd/example/version.ts +0 -13
package/src/cli.ts
CHANGED
|
@@ -1,8 +1,116 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
CommandDefinition,
|
|
4
|
+
SubcommandDefinition,
|
|
5
|
+
CommandContext,
|
|
6
|
+
ProjectConfig,
|
|
7
|
+
Config,
|
|
8
|
+
Requires,
|
|
9
|
+
Optional,
|
|
10
|
+
Logger,
|
|
11
|
+
AuthData,
|
|
12
|
+
} from './types';
|
|
3
13
|
import { showBanner } from './banner';
|
|
4
|
-
import { requireAuth, optionalAuth } from './auth';
|
|
14
|
+
import { requireAuth, optionalAuth, requireOrg, optionalOrg as selectOptionalOrg } from './auth';
|
|
15
|
+
import { listRegions, type RegionList } from '@agentuity/server';
|
|
16
|
+
import enquirer from 'enquirer';
|
|
17
|
+
import * as tui from './tui';
|
|
5
18
|
import { parseArgsSchema, parseOptionsSchema, buildValidationInput } from './schema-parser';
|
|
19
|
+
import { defaultProfileName, loadProjectConfig } from './config';
|
|
20
|
+
import { APIClient, getAPIBaseURL, type APIClient as APIClientType } from './api';
|
|
21
|
+
|
|
22
|
+
function createAPIClient(baseCtx: CommandContext, config: Config | null): APIClient {
|
|
23
|
+
try {
|
|
24
|
+
const apiUrl = getAPIBaseURL(config);
|
|
25
|
+
const apiClient = new APIClient(apiUrl, baseCtx.logger, config);
|
|
26
|
+
|
|
27
|
+
if (!apiClient) {
|
|
28
|
+
throw new Error('APIClient constructor returned null/undefined');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof apiClient.request !== 'function') {
|
|
32
|
+
throw new Error('APIClient instance is missing request method');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return apiClient;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
baseCtx.logger.error('Failed to create API client:', error);
|
|
38
|
+
throw new Error(
|
|
39
|
+
`API client initialization failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type Normalized = {
|
|
45
|
+
requiresAuth: boolean;
|
|
46
|
+
optionalAuth: false | string;
|
|
47
|
+
requiresProject: boolean;
|
|
48
|
+
optionalProject: boolean;
|
|
49
|
+
requiresAPIClient: boolean;
|
|
50
|
+
requiresOrg: boolean;
|
|
51
|
+
optionalOrg: boolean;
|
|
52
|
+
requiresRegions: boolean;
|
|
53
|
+
requiresRegion: boolean;
|
|
54
|
+
optionalRegion: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function normalizeReqs(def: CommandDefinition | SubcommandDefinition): Normalized {
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
|
+
const d: any = def as any;
|
|
60
|
+
const requires = d.requires as Requires | undefined;
|
|
61
|
+
const optional = d.optional as Optional | undefined;
|
|
62
|
+
|
|
63
|
+
const requiresAuth = requires?.auth === true;
|
|
64
|
+
const optionalAuthValue = optional?.auth;
|
|
65
|
+
const optionalAuth: false | string =
|
|
66
|
+
optionalAuthValue === true ? 'Continue without authentication' : optionalAuthValue || false;
|
|
67
|
+
|
|
68
|
+
const requiresProject = requires?.project === true;
|
|
69
|
+
const optionalProject = optional?.project === true;
|
|
70
|
+
|
|
71
|
+
const requiresOrg = requires?.org === true;
|
|
72
|
+
const optionalOrg = optional?.org === true;
|
|
73
|
+
const requiresRegions = requires?.regions === true;
|
|
74
|
+
const requiresRegion = requires?.region === true;
|
|
75
|
+
const optionalRegion = optional?.region === true;
|
|
76
|
+
|
|
77
|
+
// Implicitly require apiClient if org or region is required or optional
|
|
78
|
+
const requiresAPIClient =
|
|
79
|
+
requires?.apiClient === true ||
|
|
80
|
+
requiresOrg ||
|
|
81
|
+
optionalOrg ||
|
|
82
|
+
requiresRegion ||
|
|
83
|
+
optionalRegion ||
|
|
84
|
+
requiresRegions;
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
requiresAuth,
|
|
88
|
+
optionalAuth,
|
|
89
|
+
requiresProject,
|
|
90
|
+
optionalProject,
|
|
91
|
+
requiresAPIClient,
|
|
92
|
+
requiresOrg,
|
|
93
|
+
optionalOrg,
|
|
94
|
+
requiresRegions,
|
|
95
|
+
requiresRegion,
|
|
96
|
+
optionalRegion,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function handleProjectConfigError(error: unknown, requiresProject: boolean, logger: Logger): never {
|
|
101
|
+
if (
|
|
102
|
+
requiresProject &&
|
|
103
|
+
error &&
|
|
104
|
+
typeof error === 'object' &&
|
|
105
|
+
'name' in error &&
|
|
106
|
+
error.name === 'ProjectConfigNotFoundExpection'
|
|
107
|
+
) {
|
|
108
|
+
logger.fatal(
|
|
109
|
+
'invalid project folder. use --dir to specify a different directory or change to a project folder'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
6
114
|
|
|
7
115
|
export async function createCLI(version: string): Promise<Command> {
|
|
8
116
|
const program = new Command();
|
|
@@ -11,13 +119,20 @@ export async function createCLI(version: string): Promise<Command> {
|
|
|
11
119
|
.name('agentuity')
|
|
12
120
|
.description('Agentuity CLI')
|
|
13
121
|
.version(version, '-V, --version', 'Display version')
|
|
14
|
-
.helpOption('-h, --help', 'Display help')
|
|
122
|
+
.helpOption('-h, --help', 'Display help')
|
|
123
|
+
.allowUnknownOption(false)
|
|
124
|
+
.allowExcessArguments(false);
|
|
15
125
|
|
|
16
126
|
program
|
|
17
|
-
.option('--config <path>', 'Config file path'
|
|
127
|
+
.option('--config <path>', 'Config file path')
|
|
18
128
|
.option('--log-level <level>', 'Log level', process.env.AGENTUITY_LOG_LEVEL ?? 'info')
|
|
19
129
|
.option('--log-timestamp', 'Show timestamps in log output', false)
|
|
20
|
-
.option('--no-log-prefix', 'Hide log level prefixes',
|
|
130
|
+
.option('--no-log-prefix', 'Hide log level prefixes', true)
|
|
131
|
+
.option(
|
|
132
|
+
'--org-id <id>',
|
|
133
|
+
'Use a specific organization when performing operations',
|
|
134
|
+
process.env.AGENTUITY_CLOUD_ORG_ID
|
|
135
|
+
)
|
|
21
136
|
.option('--color-scheme <scheme>', 'Color scheme: light or dark');
|
|
22
137
|
|
|
23
138
|
const skipVersionCheckOption = program.createOption(
|
|
@@ -32,9 +147,121 @@ export async function createCLI(version: string): Promise<Command> {
|
|
|
32
147
|
program.help();
|
|
33
148
|
});
|
|
34
149
|
|
|
150
|
+
// Handle unknown commands
|
|
151
|
+
program.on('command:*', (operands: string[]) => {
|
|
152
|
+
const unknownCommand = operands[0];
|
|
153
|
+
console.error(`error: unknown command '${unknownCommand}'`);
|
|
154
|
+
console.error();
|
|
155
|
+
const availableCommands = program.commands.map((cmd) => cmd.name());
|
|
156
|
+
if (availableCommands.length > 0) {
|
|
157
|
+
console.error('Available commands:');
|
|
158
|
+
availableCommands.forEach((name) => {
|
|
159
|
+
console.error(` ${name}`);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
console.error();
|
|
163
|
+
console.error(`Run 'agentuity --help' for usage information.`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Custom error handling for argument/command parsing errors
|
|
168
|
+
program.configureOutput({
|
|
169
|
+
outputError: (str, write) => {
|
|
170
|
+
// Intercept commander.js error messages
|
|
171
|
+
if (str.includes('too many arguments') || str.includes('unknown command')) {
|
|
172
|
+
// Extract potential command name from error context
|
|
173
|
+
const match = str.match(/got (\d+)/);
|
|
174
|
+
if (match) {
|
|
175
|
+
write(`error: unknown command or subcommand\n`);
|
|
176
|
+
write(`\nRun 'agentuity --help' for available commands.\n`);
|
|
177
|
+
} else {
|
|
178
|
+
write(str);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
write(str);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
35
186
|
return program;
|
|
36
187
|
}
|
|
37
188
|
|
|
189
|
+
async function getRegion(regions: RegionList): Promise<string> {
|
|
190
|
+
if (regions.length === 1) {
|
|
191
|
+
return regions[0].region;
|
|
192
|
+
} else {
|
|
193
|
+
const response = await enquirer.prompt<{ region: string }>({
|
|
194
|
+
type: 'select',
|
|
195
|
+
name: 'region',
|
|
196
|
+
message: 'Select a cloud region:',
|
|
197
|
+
choices: regions.map((r) => ({
|
|
198
|
+
name: r.region,
|
|
199
|
+
message: `${r.description.padEnd(15, ' ')} ${tui.muted(r.region)}`,
|
|
200
|
+
})),
|
|
201
|
+
});
|
|
202
|
+
return response.region;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
interface ResolveRegionOptions {
|
|
207
|
+
options: Record<string, unknown>;
|
|
208
|
+
apiClient: APIClientType;
|
|
209
|
+
logger: Logger;
|
|
210
|
+
required: boolean;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function resolveRegion(opts: ResolveRegionOptions): Promise<string | undefined> {
|
|
214
|
+
const { options, apiClient, logger, required } = opts;
|
|
215
|
+
|
|
216
|
+
// Fetch regions
|
|
217
|
+
const regions = await listRegions(apiClient);
|
|
218
|
+
|
|
219
|
+
// No regions available
|
|
220
|
+
if (regions.length === 0) {
|
|
221
|
+
if (required) {
|
|
222
|
+
logger.fatal('No cloud regions available');
|
|
223
|
+
}
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check if region was provided via flag
|
|
228
|
+
let region = options.region as string | undefined;
|
|
229
|
+
|
|
230
|
+
// Validate --region flag if provided
|
|
231
|
+
if (region) {
|
|
232
|
+
const found = regions.find((r) => r.region === region);
|
|
233
|
+
if (!found) {
|
|
234
|
+
logger.fatal(
|
|
235
|
+
`Invalid region '${region}'. Use one of: ${regions.map((r) => r.region).join(', ')}`
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return region;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Auto-select if only one region available
|
|
242
|
+
if (regions.length === 1) {
|
|
243
|
+
region = regions[0].region;
|
|
244
|
+
if (!process.stdin.isTTY) {
|
|
245
|
+
logger.trace('auto-selected region (non-TTY): %s', region);
|
|
246
|
+
}
|
|
247
|
+
return region;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// No flag provided - handle TTY vs non-TTY
|
|
251
|
+
if (required && !process.stdin.isTTY) {
|
|
252
|
+
logger.fatal('--region flag is required in non-interactive mode');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (process.stdin.isTTY) {
|
|
256
|
+
// Interactive mode - prompt user
|
|
257
|
+
region = await getRegion(regions);
|
|
258
|
+
return region;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Non-interactive, optional region - return undefined
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
38
265
|
async function registerSubcommand(
|
|
39
266
|
parent: Command,
|
|
40
267
|
subcommand: SubcommandDefinition,
|
|
@@ -47,7 +274,37 @@ async function registerSubcommand(
|
|
|
47
274
|
cmd.aliases(subcommand.aliases);
|
|
48
275
|
}
|
|
49
276
|
|
|
50
|
-
//
|
|
277
|
+
// Check if this subcommand has its own subcommands (nested subcommands)
|
|
278
|
+
const subDef = subcommand as unknown as { subcommands?: SubcommandDefinition[] };
|
|
279
|
+
if (subDef.subcommands && subDef.subcommands.length > 0) {
|
|
280
|
+
// Register nested subcommands recursively
|
|
281
|
+
for (const nestedSub of subDef.subcommands) {
|
|
282
|
+
await registerSubcommand(cmd, nestedSub, baseCtx);
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const {
|
|
288
|
+
requiresProject,
|
|
289
|
+
optionalProject,
|
|
290
|
+
requiresOrg,
|
|
291
|
+
optionalOrg,
|
|
292
|
+
requiresRegion,
|
|
293
|
+
optionalRegion,
|
|
294
|
+
} = normalizeReqs(subcommand);
|
|
295
|
+
|
|
296
|
+
if (requiresProject || optionalProject) {
|
|
297
|
+
cmd.option('--dir <path>', 'project directory (default: current directory)');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (requiresOrg || optionalOrg) {
|
|
301
|
+
cmd.option('--org-id <id>', 'organization ID');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (requiresRegion || optionalRegion) {
|
|
305
|
+
cmd.option('--region <region>', 'cloud region');
|
|
306
|
+
}
|
|
307
|
+
|
|
51
308
|
if (subcommand.schema?.args) {
|
|
52
309
|
const parsed = parseArgsSchema(subcommand.schema.args);
|
|
53
310
|
for (const argMeta of parsed.metadata) {
|
|
@@ -67,9 +324,7 @@ async function registerSubcommand(
|
|
|
67
324
|
const flag = opt.name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
68
325
|
const desc = opt.description || '';
|
|
69
326
|
if (opt.type === 'boolean') {
|
|
70
|
-
// Support negatable boolean options (--no-flag) when they have a default
|
|
71
327
|
if (opt.hasDefault) {
|
|
72
|
-
// Evaluate default value (could be a function)
|
|
73
328
|
const defaultValue =
|
|
74
329
|
typeof opt.defaultValue === 'function' ? opt.defaultValue() : opt.defaultValue;
|
|
75
330
|
cmd.option(`--no-${flag}`, desc);
|
|
@@ -90,23 +345,112 @@ async function registerSubcommand(
|
|
|
90
345
|
const options = cmdObj.opts();
|
|
91
346
|
const args = rawArgs.slice(0, -1);
|
|
92
347
|
|
|
93
|
-
if (subcommand.
|
|
94
|
-
|
|
348
|
+
if (subcommand.banner) {
|
|
349
|
+
showBanner();
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const normalized = normalizeReqs(subcommand);
|
|
353
|
+
|
|
354
|
+
let project: ProjectConfig | undefined;
|
|
355
|
+
let projectDir: string | undefined;
|
|
356
|
+
const dirNeeded = normalized.requiresProject || normalized.optionalProject;
|
|
357
|
+
|
|
358
|
+
if (dirNeeded) {
|
|
359
|
+
const dir = (options.dir as string | undefined) ?? process.cwd();
|
|
360
|
+
projectDir = dir;
|
|
361
|
+
try {
|
|
362
|
+
project = await loadProjectConfig(dir, baseCtx.config);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
if (normalized.requiresProject) {
|
|
365
|
+
if (
|
|
366
|
+
error &&
|
|
367
|
+
typeof error === 'object' &&
|
|
368
|
+
'name' in error &&
|
|
369
|
+
error.name === 'ProjectConfigNotFoundExpection'
|
|
370
|
+
) {
|
|
371
|
+
baseCtx.logger.fatal(
|
|
372
|
+
'invalid project folder. use --dir to specify a different directory or change to a project folder'
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
// For optional projects, silently continue without project config
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (normalized.requiresAuth) {
|
|
382
|
+
// Create apiClient before requireAuth since login command needs it
|
|
383
|
+
if (normalized.requiresAPIClient) {
|
|
384
|
+
(baseCtx as Record<string, unknown>).apiClient = createAPIClient(
|
|
385
|
+
baseCtx,
|
|
386
|
+
baseCtx.config ?? null
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const auth = await requireAuth(baseCtx as CommandContext<undefined>);
|
|
95
391
|
|
|
96
392
|
if (subcommand.schema) {
|
|
97
393
|
try {
|
|
98
394
|
const input = buildValidationInput(subcommand.schema, args, options);
|
|
99
395
|
const ctx: Record<string, unknown> = {
|
|
100
396
|
...baseCtx,
|
|
397
|
+
config: {
|
|
398
|
+
...(baseCtx.config ?? {}),
|
|
399
|
+
auth: {
|
|
400
|
+
api_key: auth.apiKey,
|
|
401
|
+
user_id: auth.userId,
|
|
402
|
+
expires: auth.expires.getTime(),
|
|
403
|
+
},
|
|
404
|
+
},
|
|
101
405
|
auth,
|
|
102
406
|
};
|
|
407
|
+
if (project || projectDir) {
|
|
408
|
+
if (project) {
|
|
409
|
+
ctx.project = project;
|
|
410
|
+
}
|
|
411
|
+
ctx.projectDir = projectDir;
|
|
412
|
+
}
|
|
103
413
|
if (subcommand.schema.args) {
|
|
104
414
|
ctx.args = subcommand.schema.args.parse(input.args);
|
|
105
415
|
}
|
|
106
416
|
if (subcommand.schema.options) {
|
|
107
417
|
ctx.opts = subcommand.schema.options.parse(input.options);
|
|
108
418
|
}
|
|
109
|
-
|
|
419
|
+
if (normalized.requiresAPIClient) {
|
|
420
|
+
// Recreate apiClient with auth credentials
|
|
421
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
422
|
+
}
|
|
423
|
+
if (normalized.requiresOrg) {
|
|
424
|
+
ctx.orgId = await requireOrg(
|
|
425
|
+
ctx as CommandContext & { apiClient: APIClientType }
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
if (normalized.optionalOrg && ctx.auth) {
|
|
429
|
+
ctx.orgId = await requireOrg(
|
|
430
|
+
ctx as CommandContext & { apiClient: APIClientType }
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
434
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
435
|
+
const region = await tui.spinner({
|
|
436
|
+
message: 'Fetching cloud regions',
|
|
437
|
+
clearOnSuccess: true,
|
|
438
|
+
callback: async () => {
|
|
439
|
+
return resolveRegion({
|
|
440
|
+
options: options as Record<string, unknown>,
|
|
441
|
+
apiClient,
|
|
442
|
+
logger: baseCtx.logger,
|
|
443
|
+
required: !!normalized.requiresRegion,
|
|
444
|
+
});
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
if (region) {
|
|
448
|
+
ctx.region = region;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (subcommand.handler) {
|
|
452
|
+
await subcommand.handler(ctx as CommandContext);
|
|
453
|
+
}
|
|
110
454
|
} catch (error) {
|
|
111
455
|
if (error && typeof error === 'object' && 'issues' in error) {
|
|
112
456
|
baseCtx.logger.error('Validation error:');
|
|
@@ -119,44 +463,142 @@ async function registerSubcommand(
|
|
|
119
463
|
}
|
|
120
464
|
process.exit(1);
|
|
121
465
|
}
|
|
122
|
-
|
|
123
|
-
error &&
|
|
124
|
-
typeof error === 'object' &&
|
|
125
|
-
'name' in error &&
|
|
126
|
-
error.name === 'ProjectConfigNotFoundExpection'
|
|
127
|
-
) {
|
|
128
|
-
baseCtx.logger.fatal(
|
|
129
|
-
'invalid project folder. use --dir to specify a different directory or change to a project folder'
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
throw error;
|
|
466
|
+
handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
|
|
133
467
|
}
|
|
134
468
|
} else {
|
|
135
|
-
const ctx:
|
|
469
|
+
const ctx: Record<string, unknown> = {
|
|
136
470
|
...baseCtx,
|
|
471
|
+
config: baseCtx.config
|
|
472
|
+
? {
|
|
473
|
+
...baseCtx.config,
|
|
474
|
+
name: baseCtx.config.name ?? defaultProfileName,
|
|
475
|
+
auth: {
|
|
476
|
+
api_key: auth.apiKey,
|
|
477
|
+
user_id: auth.userId,
|
|
478
|
+
expires: auth.expires.getTime(),
|
|
479
|
+
},
|
|
480
|
+
}
|
|
481
|
+
: null,
|
|
137
482
|
auth,
|
|
138
483
|
};
|
|
139
|
-
|
|
484
|
+
if (project || projectDir) {
|
|
485
|
+
if (project) {
|
|
486
|
+
ctx.project = project;
|
|
487
|
+
}
|
|
488
|
+
ctx.projectDir = projectDir;
|
|
489
|
+
}
|
|
490
|
+
if (normalized.requiresAPIClient) {
|
|
491
|
+
// Recreate apiClient with auth credentials
|
|
492
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
493
|
+
}
|
|
494
|
+
if (normalized.requiresOrg) {
|
|
495
|
+
ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
|
|
496
|
+
}
|
|
497
|
+
if (normalized.optionalOrg && ctx.auth) {
|
|
498
|
+
ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
|
|
499
|
+
}
|
|
500
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
501
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
502
|
+
const region = await tui.spinner('Fetching cloud regions', async () => {
|
|
503
|
+
return resolveRegion({
|
|
504
|
+
options: options as Record<string, unknown>,
|
|
505
|
+
apiClient,
|
|
506
|
+
logger: baseCtx.logger,
|
|
507
|
+
required: !!normalized.requiresRegion,
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
if (region) {
|
|
511
|
+
ctx.region = region;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (subcommand.handler) {
|
|
515
|
+
await subcommand.handler(ctx as CommandContext);
|
|
516
|
+
}
|
|
140
517
|
}
|
|
141
|
-
} else if (
|
|
518
|
+
} else if (normalized.optionalAuth) {
|
|
142
519
|
const continueText =
|
|
143
|
-
typeof
|
|
144
|
-
|
|
520
|
+
typeof normalized.optionalAuth === 'string' ? normalized.optionalAuth : undefined;
|
|
521
|
+
|
|
522
|
+
// Create apiClient before optionalAuth since login command needs it
|
|
523
|
+
if (normalized.requiresAPIClient) {
|
|
524
|
+
(baseCtx as Record<string, unknown>).apiClient = createAPIClient(
|
|
525
|
+
baseCtx,
|
|
526
|
+
baseCtx.config ?? null
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const auth = await optionalAuth(baseCtx as CommandContext<undefined>, continueText);
|
|
145
531
|
|
|
146
532
|
if (subcommand.schema) {
|
|
147
533
|
try {
|
|
148
534
|
const input = buildValidationInput(subcommand.schema, args, options);
|
|
149
535
|
const ctx: Record<string, unknown> = {
|
|
150
536
|
...baseCtx,
|
|
537
|
+
config: auth
|
|
538
|
+
? {
|
|
539
|
+
...(baseCtx.config ?? {}),
|
|
540
|
+
auth: {
|
|
541
|
+
api_key: auth.apiKey,
|
|
542
|
+
user_id: auth.userId,
|
|
543
|
+
expires: auth.expires.getTime(),
|
|
544
|
+
},
|
|
545
|
+
}
|
|
546
|
+
: baseCtx.config,
|
|
151
547
|
auth,
|
|
152
548
|
};
|
|
549
|
+
if (project || projectDir) {
|
|
550
|
+
if (project) {
|
|
551
|
+
ctx.project = project;
|
|
552
|
+
}
|
|
553
|
+
ctx.projectDir = projectDir;
|
|
554
|
+
}
|
|
153
555
|
if (subcommand.schema.args) {
|
|
154
556
|
ctx.args = subcommand.schema.args.parse(input.args);
|
|
155
557
|
}
|
|
156
558
|
if (subcommand.schema.options) {
|
|
157
559
|
ctx.opts = subcommand.schema.options.parse(input.options);
|
|
158
560
|
}
|
|
159
|
-
|
|
561
|
+
if (normalized.requiresAPIClient) {
|
|
562
|
+
// Recreate apiClient with auth credentials
|
|
563
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
564
|
+
}
|
|
565
|
+
baseCtx.logger.trace(
|
|
566
|
+
'optionalAuth path: org=%s, region=%s, hasApiClient=%s, hasAuth=%s',
|
|
567
|
+
normalized.optionalOrg,
|
|
568
|
+
normalized.optionalRegion,
|
|
569
|
+
!!ctx.apiClient,
|
|
570
|
+
!!auth
|
|
571
|
+
);
|
|
572
|
+
if (normalized.requiresOrg && ctx.apiClient) {
|
|
573
|
+
ctx.orgId = await requireOrg(
|
|
574
|
+
ctx as CommandContext & { apiClient: APIClientType }
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
if (normalized.optionalOrg && ctx.apiClient && auth) {
|
|
578
|
+
ctx.orgId = await selectOptionalOrg(
|
|
579
|
+
ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
|
|
580
|
+
);
|
|
581
|
+
baseCtx.logger.trace('selected orgId: %s', ctx.orgId);
|
|
582
|
+
}
|
|
583
|
+
if (
|
|
584
|
+
(normalized.requiresRegion || normalized.optionalRegion) &&
|
|
585
|
+
ctx.apiClient &&
|
|
586
|
+
auth
|
|
587
|
+
) {
|
|
588
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
589
|
+
const region = await resolveRegion({
|
|
590
|
+
options: options as Record<string, unknown>,
|
|
591
|
+
apiClient,
|
|
592
|
+
logger: baseCtx.logger,
|
|
593
|
+
required: !!normalized.requiresRegion,
|
|
594
|
+
});
|
|
595
|
+
if (region) {
|
|
596
|
+
ctx.region = region;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (subcommand.handler) {
|
|
600
|
+
await subcommand.handler(ctx as CommandContext);
|
|
601
|
+
}
|
|
160
602
|
} catch (error) {
|
|
161
603
|
if (error && typeof error === 'object' && 'issues' in error) {
|
|
162
604
|
baseCtx.logger.error('Validation error:');
|
|
@@ -169,21 +611,56 @@ async function registerSubcommand(
|
|
|
169
611
|
}
|
|
170
612
|
process.exit(1);
|
|
171
613
|
}
|
|
172
|
-
|
|
173
|
-
error &&
|
|
174
|
-
typeof error === 'object' &&
|
|
175
|
-
'name' in error &&
|
|
176
|
-
error.name === 'ProjectConfigNotFoundExpection'
|
|
177
|
-
) {
|
|
178
|
-
baseCtx.logger.fatal(
|
|
179
|
-
'invalid project folder. use --dir to specify a different directory or change to a project folder'
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
throw error;
|
|
614
|
+
handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
|
|
183
615
|
}
|
|
184
616
|
} else {
|
|
185
|
-
const ctx = {
|
|
186
|
-
|
|
617
|
+
const ctx: Record<string, unknown> = {
|
|
618
|
+
...baseCtx,
|
|
619
|
+
config: auth
|
|
620
|
+
? {
|
|
621
|
+
...(baseCtx.config ?? {}),
|
|
622
|
+
auth: {
|
|
623
|
+
api_key: auth.apiKey,
|
|
624
|
+
user_id: auth.userId,
|
|
625
|
+
expires: auth.expires.getTime(),
|
|
626
|
+
},
|
|
627
|
+
}
|
|
628
|
+
: baseCtx.config,
|
|
629
|
+
auth,
|
|
630
|
+
};
|
|
631
|
+
if (project || projectDir) {
|
|
632
|
+
if (project) {
|
|
633
|
+
ctx.project = project;
|
|
634
|
+
}
|
|
635
|
+
ctx.projectDir = projectDir;
|
|
636
|
+
}
|
|
637
|
+
if (normalized.requiresAPIClient) {
|
|
638
|
+
// Recreate apiClient with auth credentials if auth was provided
|
|
639
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
640
|
+
}
|
|
641
|
+
if (normalized.requiresOrg && ctx.apiClient) {
|
|
642
|
+
ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
|
|
643
|
+
}
|
|
644
|
+
if (normalized.optionalOrg && ctx.apiClient) {
|
|
645
|
+
ctx.orgId = await selectOptionalOrg(
|
|
646
|
+
ctx as CommandContext & { apiClient?: APIClientType; auth?: AuthData }
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
650
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
651
|
+
const region = await resolveRegion({
|
|
652
|
+
options: options as Record<string, unknown>,
|
|
653
|
+
apiClient,
|
|
654
|
+
logger: baseCtx.logger,
|
|
655
|
+
required: !!normalized.requiresRegion,
|
|
656
|
+
});
|
|
657
|
+
if (region) {
|
|
658
|
+
ctx.region = region;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (subcommand.handler) {
|
|
662
|
+
await subcommand.handler(ctx as CommandContext);
|
|
663
|
+
}
|
|
187
664
|
}
|
|
188
665
|
} else {
|
|
189
666
|
if (subcommand.schema) {
|
|
@@ -192,13 +669,34 @@ async function registerSubcommand(
|
|
|
192
669
|
const ctx: Record<string, unknown> = {
|
|
193
670
|
...baseCtx,
|
|
194
671
|
};
|
|
672
|
+
if (project || projectDir) {
|
|
673
|
+
if (project) {
|
|
674
|
+
ctx.project = project;
|
|
675
|
+
}
|
|
676
|
+
ctx.projectDir = projectDir;
|
|
677
|
+
}
|
|
195
678
|
if (subcommand.schema.args) {
|
|
196
679
|
ctx.args = subcommand.schema.args.parse(input.args);
|
|
197
680
|
}
|
|
198
681
|
if (subcommand.schema.options) {
|
|
199
682
|
ctx.opts = subcommand.schema.options.parse(input.options);
|
|
200
683
|
}
|
|
201
|
-
|
|
684
|
+
if (normalized.requiresAPIClient && !ctx.apiClient) {
|
|
685
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
686
|
+
}
|
|
687
|
+
if (normalized.requiresOrg && ctx.apiClient) {
|
|
688
|
+
ctx.orgId = await requireOrg(
|
|
689
|
+
ctx as CommandContext & { apiClient: APIClientType }
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (normalized.optionalOrg && ctx.apiClient && ctx.auth) {
|
|
693
|
+
ctx.orgId = await requireOrg(
|
|
694
|
+
ctx as CommandContext & { apiClient: APIClientType }
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
if (subcommand.handler) {
|
|
698
|
+
await subcommand.handler(ctx as CommandContext);
|
|
699
|
+
}
|
|
202
700
|
} catch (error) {
|
|
203
701
|
if (error && typeof error === 'object' && 'issues' in error) {
|
|
204
702
|
baseCtx.logger.error('Validation error:');
|
|
@@ -211,20 +709,42 @@ async function registerSubcommand(
|
|
|
211
709
|
}
|
|
212
710
|
process.exit(1);
|
|
213
711
|
}
|
|
214
|
-
|
|
215
|
-
error &&
|
|
216
|
-
typeof error === 'object' &&
|
|
217
|
-
'name' in error &&
|
|
218
|
-
error.name === 'ProjectConfigNotFoundExpection'
|
|
219
|
-
) {
|
|
220
|
-
baseCtx.logger.fatal(
|
|
221
|
-
'invalid project folder. use --dir to specify a different directory or change to a project folder'
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
throw error;
|
|
712
|
+
handleProjectConfigError(error, normalized.requiresProject, baseCtx.logger);
|
|
225
713
|
}
|
|
226
714
|
} else {
|
|
227
|
-
|
|
715
|
+
const ctx: Record<string, unknown> = {
|
|
716
|
+
...baseCtx,
|
|
717
|
+
};
|
|
718
|
+
if (project || projectDir) {
|
|
719
|
+
if (project) {
|
|
720
|
+
ctx.project = project;
|
|
721
|
+
}
|
|
722
|
+
ctx.projectDir = projectDir;
|
|
723
|
+
}
|
|
724
|
+
if (normalized.requiresAPIClient && !ctx.apiClient) {
|
|
725
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
726
|
+
}
|
|
727
|
+
if (normalized.requiresOrg && ctx.apiClient) {
|
|
728
|
+
ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
|
|
729
|
+
}
|
|
730
|
+
if (normalized.optionalOrg && ctx.apiClient && ctx.auth) {
|
|
731
|
+
ctx.orgId = await requireOrg(ctx as CommandContext & { apiClient: APIClientType });
|
|
732
|
+
}
|
|
733
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
734
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
735
|
+
const region = await resolveRegion({
|
|
736
|
+
options: options as Record<string, unknown>,
|
|
737
|
+
apiClient,
|
|
738
|
+
logger: baseCtx.logger,
|
|
739
|
+
required: !!normalized.requiresRegion,
|
|
740
|
+
});
|
|
741
|
+
if (region) {
|
|
742
|
+
ctx.region = region;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
if (subcommand.handler) {
|
|
746
|
+
await subcommand.handler(ctx as CommandContext);
|
|
747
|
+
}
|
|
228
748
|
}
|
|
229
749
|
}
|
|
230
750
|
});
|
|
@@ -247,18 +767,130 @@ export async function registerCommands(
|
|
|
247
767
|
|
|
248
768
|
if (cmdDef.handler) {
|
|
249
769
|
cmd.action(async () => {
|
|
250
|
-
if (cmdDef.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
770
|
+
if (cmdDef.banner) {
|
|
771
|
+
showBanner();
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
const normalized = normalizeReqs(cmdDef);
|
|
775
|
+
if (normalized.requiresAuth) {
|
|
776
|
+
// Create apiClient before requireAuth since login command needs it
|
|
777
|
+
if (normalized.requiresAPIClient) {
|
|
778
|
+
(baseCtx as Record<string, unknown>).apiClient = createAPIClient(
|
|
779
|
+
baseCtx,
|
|
780
|
+
baseCtx.config ?? null
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const auth = await requireAuth(baseCtx as CommandContext<undefined>);
|
|
785
|
+
const ctx: Record<string, unknown> = {
|
|
786
|
+
...baseCtx,
|
|
787
|
+
config: baseCtx.config
|
|
788
|
+
? {
|
|
789
|
+
...baseCtx.config,
|
|
790
|
+
name: baseCtx.config.name ?? defaultProfileName,
|
|
791
|
+
auth: {
|
|
792
|
+
api_key: auth.apiKey,
|
|
793
|
+
user_id: auth.userId,
|
|
794
|
+
expires: auth.expires.getTime(),
|
|
795
|
+
},
|
|
796
|
+
}
|
|
797
|
+
: null,
|
|
798
|
+
auth,
|
|
799
|
+
};
|
|
800
|
+
if (normalized.requiresAPIClient) {
|
|
801
|
+
// Recreate apiClient with auth credentials
|
|
802
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
803
|
+
}
|
|
804
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
805
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
806
|
+
const region = await resolveRegion({
|
|
807
|
+
options: baseCtx.options as unknown as Record<string, unknown>,
|
|
808
|
+
apiClient,
|
|
809
|
+
logger: baseCtx.logger,
|
|
810
|
+
required: !!normalized.requiresRegion,
|
|
811
|
+
});
|
|
812
|
+
if (region) {
|
|
813
|
+
ctx.region = region;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
await cmdDef.handler!(ctx as CommandContext);
|
|
817
|
+
} else if (normalized.optionalAuth) {
|
|
255
818
|
const continueText =
|
|
256
|
-
typeof
|
|
257
|
-
|
|
258
|
-
|
|
819
|
+
typeof normalized.optionalAuth === 'string'
|
|
820
|
+
? normalized.optionalAuth
|
|
821
|
+
: undefined;
|
|
822
|
+
|
|
823
|
+
// Create apiClient before optionalAuth since login command needs it
|
|
824
|
+
if (normalized.requiresAPIClient) {
|
|
825
|
+
(baseCtx as Record<string, unknown>).apiClient = createAPIClient(
|
|
826
|
+
baseCtx,
|
|
827
|
+
baseCtx.config ?? null
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
const auth = await optionalAuth(
|
|
832
|
+
baseCtx as CommandContext<undefined>,
|
|
833
|
+
continueText
|
|
834
|
+
);
|
|
835
|
+
const ctx: Record<string, unknown> = {
|
|
836
|
+
...baseCtx,
|
|
837
|
+
config: auth
|
|
838
|
+
? baseCtx.config
|
|
839
|
+
? {
|
|
840
|
+
...baseCtx.config,
|
|
841
|
+
auth: {
|
|
842
|
+
api_key: auth.apiKey,
|
|
843
|
+
user_id: auth.userId,
|
|
844
|
+
expires: auth.expires.getTime(),
|
|
845
|
+
},
|
|
846
|
+
}
|
|
847
|
+
: {
|
|
848
|
+
auth: {
|
|
849
|
+
api_key: auth.apiKey,
|
|
850
|
+
user_id: auth.userId,
|
|
851
|
+
expires: auth.expires.getTime(),
|
|
852
|
+
},
|
|
853
|
+
}
|
|
854
|
+
: baseCtx.config,
|
|
855
|
+
auth,
|
|
856
|
+
};
|
|
857
|
+
if (normalized.requiresAPIClient) {
|
|
858
|
+
// Recreate apiClient with auth credentials if auth was provided
|
|
859
|
+
ctx.apiClient = createAPIClient(baseCtx, ctx.config as Config | null);
|
|
860
|
+
}
|
|
861
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
862
|
+
const apiClient: APIClientType = ctx.apiClient as APIClientType;
|
|
863
|
+
const region = await resolveRegion({
|
|
864
|
+
options: baseCtx.options as unknown as Record<string, unknown>,
|
|
865
|
+
apiClient,
|
|
866
|
+
logger: baseCtx.logger,
|
|
867
|
+
required: !!normalized.requiresRegion,
|
|
868
|
+
});
|
|
869
|
+
if (region) {
|
|
870
|
+
ctx.region = region;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
259
873
|
await cmdDef.handler!(ctx as CommandContext);
|
|
260
874
|
} else {
|
|
261
|
-
|
|
875
|
+
const ctx: Record<string, unknown> = {
|
|
876
|
+
...baseCtx,
|
|
877
|
+
};
|
|
878
|
+
if (normalized.requiresAPIClient && !(ctx as CommandContext).apiClient) {
|
|
879
|
+
ctx.apiClient = createAPIClient(baseCtx, baseCtx.config);
|
|
880
|
+
}
|
|
881
|
+
if ((normalized.requiresRegion || normalized.optionalRegion) && ctx.apiClient) {
|
|
882
|
+
const apiClient = ctx.apiClient as APIClientType;
|
|
883
|
+
const region = await resolveRegion({
|
|
884
|
+
options: baseCtx.options as unknown as Record<string, unknown>,
|
|
885
|
+
apiClient,
|
|
886
|
+
logger: baseCtx.logger,
|
|
887
|
+
required: !!normalized.requiresRegion,
|
|
888
|
+
});
|
|
889
|
+
if (region) {
|
|
890
|
+
ctx.region = region;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
await cmdDef.handler!(ctx as CommandContext);
|
|
262
894
|
}
|
|
263
895
|
});
|
|
264
896
|
} else {
|