@aifabrix/builder 2.32.2 → 2.32.3
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/lib/api/index.js +10 -5
- package/lib/api/wizard.api.js +52 -21
- package/lib/app/list.js +62 -28
- package/lib/app/prompts.js +9 -5
- package/lib/app/rotate-secret.js +2 -1
- package/lib/cli.js +33 -4
- package/lib/commands/auth-status.js +262 -0
- package/lib/commands/login-device.js +17 -12
- package/lib/commands/login.js +16 -9
- package/lib/commands/wizard.js +63 -69
- package/lib/datasource/deploy.js +5 -2
- package/lib/external-system/deploy.js +3 -2
- package/lib/external-system/download.js +2 -1
- package/lib/external-system/test-auth.js +2 -1
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/external-datasource.schema.json +301 -15
- package/lib/schema/external-system.schema.json +1 -1
- package/lib/utils/api.js +20 -7
- package/lib/utils/app-register-display.js +2 -1
- package/lib/utils/cli-utils.js +3 -1
- package/lib/utils/controller-url.js +67 -0
- package/lib/utils/env-map.js +2 -1
- package/lib/utils/error-formatter.js +100 -28
- package/lib/utils/token-manager.js +60 -0
- package/lib/validation/validator.js +2 -1
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +2 -2
- package/templates/external-system/external-system.json.hbs +1 -1
package/lib/api/index.js
CHANGED
|
@@ -97,7 +97,8 @@ class ApiClient {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
|
|
100
|
-
|
|
100
|
+
// Pass full authConfig to enable proper token refresh using controller URL
|
|
101
|
+
return await authenticatedApiCall(url, { method: 'GET', headers }, this.authConfig);
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
return await makeApiCall(url, { method: 'GET', headers });
|
|
@@ -141,7 +142,8 @@ class ApiClient {
|
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
|
|
144
|
-
|
|
145
|
+
// Pass full authConfig to enable proper token refresh using controller URL
|
|
146
|
+
return await authenticatedApiCall(url, requestOptions, this.authConfig);
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
return await makeApiCall(url, requestOptions);
|
|
@@ -170,7 +172,8 @@ class ApiClient {
|
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
|
|
173
|
-
|
|
175
|
+
// Pass full authConfig to enable proper token refresh using controller URL
|
|
176
|
+
return await authenticatedApiCall(url, requestOptions, this.authConfig);
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
return await makeApiCall(url, requestOptions);
|
|
@@ -199,7 +202,8 @@ class ApiClient {
|
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
|
|
202
|
-
|
|
205
|
+
// Pass full authConfig to enable proper token refresh using controller URL
|
|
206
|
+
return await authenticatedApiCall(url, requestOptions, this.authConfig);
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
return await makeApiCall(url, requestOptions);
|
|
@@ -223,7 +227,8 @@ class ApiClient {
|
|
|
223
227
|
};
|
|
224
228
|
|
|
225
229
|
if (this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token') {
|
|
226
|
-
|
|
230
|
+
// Pass full authConfig to enable proper token refresh using controller URL
|
|
231
|
+
return await authenticatedApiCall(url, requestOptions, this.authConfig);
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
return await makeApiCall(url, requestOptions);
|
package/lib/api/wizard.api.js
CHANGED
|
@@ -8,42 +8,72 @@ const { ApiClient } = require('./index');
|
|
|
8
8
|
const { uploadFile } = require('../utils/file-upload');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* POST /api/v1/wizard/
|
|
11
|
+
* Create wizard session
|
|
12
|
+
* POST /api/v1/wizard/sessions
|
|
13
13
|
* @async
|
|
14
|
-
* @function
|
|
14
|
+
* @function createWizardSession
|
|
15
15
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
16
16
|
* @param {Object} authConfig - Authentication configuration
|
|
17
17
|
* @param {string} mode - Wizard mode ('create-system' | 'add-datasource')
|
|
18
|
-
* @
|
|
18
|
+
* @param {string} [systemId] - Existing system ID (required when mode='add-datasource')
|
|
19
|
+
* @returns {Promise<Object>} Session creation response with sessionId
|
|
19
20
|
* @throws {Error} If request fails
|
|
20
21
|
*/
|
|
21
|
-
async function
|
|
22
|
+
async function createWizardSession(dataplaneUrl, authConfig, mode, systemId = null) {
|
|
22
23
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const body = { mode };
|
|
25
|
+
if (systemId) {
|
|
26
|
+
body.systemId = systemId;
|
|
27
|
+
}
|
|
28
|
+
return await client.post('/api/v1/wizard/sessions', {
|
|
29
|
+
body
|
|
25
30
|
});
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
34
|
+
* Get wizard session
|
|
35
|
+
* GET /api/v1/wizard/sessions/{sessionId}
|
|
31
36
|
* @async
|
|
32
|
-
* @function
|
|
37
|
+
* @function getWizardSession
|
|
33
38
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
39
|
+
* @param {string} sessionId - Session ID
|
|
34
40
|
* @param {Object} authConfig - Authentication configuration
|
|
35
|
-
* @
|
|
36
|
-
* @param {string} [sourceData] - Source data (file path, URL, etc.)
|
|
37
|
-
* @returns {Promise<Object>} Source selection response
|
|
41
|
+
* @returns {Promise<Object>} Session state response
|
|
38
42
|
* @throws {Error} If request fails
|
|
39
43
|
*/
|
|
40
|
-
async function
|
|
44
|
+
async function getWizardSession(dataplaneUrl, sessionId, authConfig) {
|
|
41
45
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
42
|
-
return await client.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
return await client.get(`/api/v1/wizard/sessions/${sessionId}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Update wizard session
|
|
51
|
+
* PATCH /api/v1/wizard/sessions/{sessionId}
|
|
52
|
+
* @async
|
|
53
|
+
* @function updateWizardSession
|
|
54
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
55
|
+
* @param {string} sessionId - Session ID
|
|
56
|
+
* @param {Object} authConfig - Authentication configuration
|
|
57
|
+
* @param {Object} updateData - Session update data
|
|
58
|
+
* @param {number} [updateData.currentStep] - Current wizard step (0-6)
|
|
59
|
+
* @param {string} [updateData.credentialIdOrKey] - Selected credential ID or key
|
|
60
|
+
* @param {Object} [updateData.openapiSpec] - Parsed OpenAPI specification
|
|
61
|
+
* @param {string} [updateData.mcpServerUrl] - MCP server URL
|
|
62
|
+
* @param {Array} [updateData.detectedTypes] - Detected API types
|
|
63
|
+
* @param {string} [updateData.selectedType] - Selected API type
|
|
64
|
+
* @param {string} [updateData.intent] - User intent
|
|
65
|
+
* @param {string} [updateData.fieldOnboardingLevel] - Field onboarding level
|
|
66
|
+
* @param {boolean} [updateData.enableOpenAPIGeneration] - Enable OpenAPI generation
|
|
67
|
+
* @param {Object} [updateData.systemConfig] - Generated system configuration
|
|
68
|
+
* @param {Object} [updateData.datasourceConfig] - Generated datasource configuration
|
|
69
|
+
* @param {Object} [updateData.validationResults] - Validation results
|
|
70
|
+
* @returns {Promise<Object>} Updated session state response
|
|
71
|
+
* @throws {Error} If request fails
|
|
72
|
+
*/
|
|
73
|
+
async function updateWizardSession(dataplaneUrl, sessionId, authConfig, updateData) {
|
|
74
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
75
|
+
return await client.patch(`/api/v1/wizard/sessions/${sessionId}`, {
|
|
76
|
+
body: updateData
|
|
47
77
|
});
|
|
48
78
|
}
|
|
49
79
|
|
|
@@ -165,8 +195,9 @@ async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
|
|
|
165
195
|
}
|
|
166
196
|
|
|
167
197
|
module.exports = {
|
|
168
|
-
|
|
169
|
-
|
|
198
|
+
createWizardSession,
|
|
199
|
+
getWizardSession,
|
|
200
|
+
updateWizardSession,
|
|
170
201
|
parseOpenApi,
|
|
171
202
|
detectType,
|
|
172
203
|
generateConfig,
|
package/lib/app/list.js
CHANGED
|
@@ -126,14 +126,35 @@ async function findDeviceTokenFromConfig(deviceConfig) {
|
|
|
126
126
|
return null;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Format URL and port for display
|
|
131
|
+
* @param {Object} app - Application object
|
|
132
|
+
* @returns {string} Formatted URL and port string
|
|
133
|
+
*/
|
|
134
|
+
function formatUrlAndPort(app) {
|
|
135
|
+
const url = app.url || app.dataplaneUrl || app.dataplane?.url || app.configuration?.dataplaneUrl || null;
|
|
136
|
+
const port = app.port || app.configuration?.port || null;
|
|
137
|
+
|
|
138
|
+
const parts = [];
|
|
139
|
+
if (url) {
|
|
140
|
+
parts.push(`URL: ${chalk.blue(url)}`);
|
|
141
|
+
}
|
|
142
|
+
if (port) {
|
|
143
|
+
parts.push(`Port: ${chalk.blue(port)}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return parts.length > 0 ? ` (${parts.join(', ')})` : '';
|
|
147
|
+
}
|
|
148
|
+
|
|
129
149
|
/**
|
|
130
150
|
* Display applications list
|
|
131
151
|
* @param {Array} applications - Array of application objects
|
|
132
152
|
* @param {string} environment - Environment name or key
|
|
153
|
+
* @param {string} controllerUrl - Controller URL
|
|
133
154
|
*/
|
|
134
|
-
function displayApplications(applications, environment) {
|
|
155
|
+
function displayApplications(applications, environment, controllerUrl) {
|
|
135
156
|
const environmentName = environment || 'miso';
|
|
136
|
-
const header = `Applications in ${environmentName} environment`;
|
|
157
|
+
const header = `Applications in ${environmentName} environment (${controllerUrl})`;
|
|
137
158
|
|
|
138
159
|
if (applications.length === 0) {
|
|
139
160
|
logger.log(chalk.bold(`\n📱 ${header}:\n`));
|
|
@@ -144,7 +165,8 @@ function displayApplications(applications, environment) {
|
|
|
144
165
|
logger.log(chalk.bold(`\n📱 ${header}:\n`));
|
|
145
166
|
applications.forEach((app) => {
|
|
146
167
|
const hasPipeline = app.configuration?.pipeline?.isActive ? '✓' : '✗';
|
|
147
|
-
|
|
168
|
+
const urlAndPort = formatUrlAndPort(app);
|
|
169
|
+
logger.log(`${hasPipeline} ${chalk.cyan(app.key)} - ${app.displayName} (${app.status || 'unknown'})${urlAndPort}`);
|
|
148
170
|
});
|
|
149
171
|
logger.log('');
|
|
150
172
|
}
|
|
@@ -152,23 +174,19 @@ function displayApplications(applications, environment) {
|
|
|
152
174
|
/**
|
|
153
175
|
* Try to get device token from controller URL
|
|
154
176
|
* @async
|
|
155
|
-
* @param {string} controllerUrl - Controller URL
|
|
156
|
-
* @returns {Promise<Object|null>} Object with token and controllerUrl, or null
|
|
177
|
+
* @param {string} controllerUrl - Controller URL (explicitly provided by user)
|
|
178
|
+
* @returns {Promise<Object|null>} Object with token and controllerUrl, or null if token not found
|
|
179
|
+
* @throws {Error} If authentication/refresh fails
|
|
157
180
|
*/
|
|
158
181
|
async function tryGetTokenFromController(controllerUrl) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.error(chalk.red(`❌ Failed to authenticate with controller: ${controllerUrl}`));
|
|
170
|
-
logger.error(chalk.gray(`Error: ${error.message}`));
|
|
171
|
-
process.exit(1);
|
|
182
|
+
const normalizedUrl = normalizeControllerUrl(controllerUrl);
|
|
183
|
+
const deviceToken = await getOrRefreshDeviceToken(normalizedUrl);
|
|
184
|
+
if (deviceToken && deviceToken.token) {
|
|
185
|
+
// Always use the provided controller URL (normalized) to ensure we use the exact URL the user specified
|
|
186
|
+
return {
|
|
187
|
+
token: deviceToken.token,
|
|
188
|
+
actualControllerUrl: normalizedUrl
|
|
189
|
+
};
|
|
172
190
|
}
|
|
173
191
|
return null;
|
|
174
192
|
}
|
|
@@ -215,25 +233,36 @@ function validateAuthToken(token, actualControllerUrl, controllerUrl) {
|
|
|
215
233
|
/**
|
|
216
234
|
* Get authentication token for listing applications
|
|
217
235
|
* @async
|
|
218
|
-
* @param {string} [controllerUrl] - Optional controller URL
|
|
236
|
+
* @param {string} [controllerUrl] - Optional controller URL (if provided, must use this specific URL)
|
|
219
237
|
* @param {Object} config - Configuration object
|
|
220
238
|
* @returns {Promise<Object>} Object with token and actualControllerUrl
|
|
221
239
|
* @throws {Error} If authentication fails
|
|
222
240
|
*/
|
|
223
241
|
async function getListAuthToken(controllerUrl, config) {
|
|
224
|
-
//
|
|
225
|
-
let authResult = null;
|
|
242
|
+
// If controller URL is explicitly provided, use only that URL (no fallback)
|
|
226
243
|
if (controllerUrl) {
|
|
227
|
-
authResult = await tryGetTokenFromController(controllerUrl);
|
|
244
|
+
const authResult = await tryGetTokenFromController(controllerUrl);
|
|
245
|
+
if (!authResult || !authResult.token) {
|
|
246
|
+
// No token found for explicitly provided controller URL
|
|
247
|
+
logger.error(chalk.red(`❌ No authentication token found for controller: ${controllerUrl}`));
|
|
248
|
+
logger.error(chalk.gray('Please login to this controller using: aifabrix login'));
|
|
249
|
+
process.exit(1);
|
|
250
|
+
// Return to prevent further execution in tests where process.exit is mocked
|
|
251
|
+
return { token: null, actualControllerUrl: null };
|
|
252
|
+
}
|
|
253
|
+
return validateAuthToken(authResult.token, authResult.actualControllerUrl, controllerUrl);
|
|
228
254
|
}
|
|
229
255
|
|
|
230
|
-
// If no
|
|
231
|
-
if (
|
|
232
|
-
authResult = await tryGetTokenFromConfig(config.device);
|
|
256
|
+
// If no controller URL provided, try to find device token from config
|
|
257
|
+
if (config.device) {
|
|
258
|
+
const authResult = await tryGetTokenFromConfig(config.device);
|
|
259
|
+
if (authResult && authResult.token) {
|
|
260
|
+
return validateAuthToken(authResult.token, authResult.actualControllerUrl, null);
|
|
261
|
+
}
|
|
233
262
|
}
|
|
234
263
|
|
|
235
|
-
//
|
|
236
|
-
return validateAuthToken(
|
|
264
|
+
// No token found anywhere
|
|
265
|
+
return validateAuthToken(null, null, null);
|
|
237
266
|
}
|
|
238
267
|
|
|
239
268
|
/**
|
|
@@ -271,12 +300,17 @@ async function listApplications(options) {
|
|
|
271
300
|
const controllerUrl = options.controller || null;
|
|
272
301
|
const { token, actualControllerUrl } = await getListAuthToken(controllerUrl, config);
|
|
273
302
|
|
|
303
|
+
// Check if authentication succeeded (may be null after process.exit in tests)
|
|
304
|
+
if (!token || !actualControllerUrl) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
274
308
|
// Use centralized API client
|
|
275
309
|
const authConfig = { type: 'bearer', token: token };
|
|
276
310
|
try {
|
|
277
311
|
const response = await listEnvironmentApplications(actualControllerUrl, options.environment, authConfig);
|
|
278
312
|
const applications = handleListResponse(response, actualControllerUrl);
|
|
279
|
-
displayApplications(applications, options.environment);
|
|
313
|
+
displayApplications(applications, options.environment, actualControllerUrl);
|
|
280
314
|
} catch (error) {
|
|
281
315
|
logger.error(chalk.red(`❌ Failed to list applications from controller: ${actualControllerUrl}`));
|
|
282
316
|
logger.error(chalk.gray(`Error: ${error.message}`));
|
package/lib/app/prompts.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const inquirer = require('inquirer');
|
|
12
|
+
const { getDefaultControllerUrl } = require('../utils/controller-url');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Builds basic questions (port, language)
|
|
@@ -223,10 +224,11 @@ function buildExternalSystemQuestions(options, appName) {
|
|
|
223
224
|
|
|
224
225
|
/**
|
|
225
226
|
* Builds workflow questions (GitHub, Controller)
|
|
227
|
+
* @async
|
|
226
228
|
* @param {Object} options - Provided options
|
|
227
|
-
* @returns {Array} Array of question objects
|
|
229
|
+
* @returns {Promise<Array>} Array of question objects
|
|
228
230
|
*/
|
|
229
|
-
function buildWorkflowQuestions(options) {
|
|
231
|
+
async function buildWorkflowQuestions(options) {
|
|
230
232
|
const questions = [];
|
|
231
233
|
|
|
232
234
|
// GitHub workflows
|
|
@@ -255,11 +257,13 @@ function buildWorkflowQuestions(options) {
|
|
|
255
257
|
if (!options.controllerUrl && options.controller &&
|
|
256
258
|
!Object.prototype.hasOwnProperty.call(options, 'controllerUrl')) {
|
|
257
259
|
const misoHost = process.env.MISO_HOST || 'localhost';
|
|
260
|
+
const defaultControllerUrl = await getDefaultControllerUrl();
|
|
261
|
+
const defaultUrl = defaultControllerUrl.replace('http://localhost:', `http://${misoHost}:`);
|
|
258
262
|
questions.push({
|
|
259
263
|
type: 'input',
|
|
260
264
|
name: 'controllerUrl',
|
|
261
265
|
message: 'Enter Controller URL:',
|
|
262
|
-
default:
|
|
266
|
+
default: defaultUrl,
|
|
263
267
|
when: (answers) => answers.controller === true
|
|
264
268
|
});
|
|
265
269
|
}
|
|
@@ -424,14 +428,14 @@ async function promptForOptions(appName, options) {
|
|
|
424
428
|
// For external type, prompt for external system configuration
|
|
425
429
|
questions = [
|
|
426
430
|
...buildExternalSystemQuestions(options, appName),
|
|
427
|
-
...buildWorkflowQuestions(options)
|
|
431
|
+
...(await buildWorkflowQuestions(options))
|
|
428
432
|
];
|
|
429
433
|
} else {
|
|
430
434
|
// For regular apps, use standard prompts
|
|
431
435
|
questions = [
|
|
432
436
|
...buildBasicQuestions(options, appType),
|
|
433
437
|
...buildServiceQuestions(options, appType),
|
|
434
|
-
...buildWorkflowQuestions(options)
|
|
438
|
+
...(await buildWorkflowQuestions(options))
|
|
435
439
|
];
|
|
436
440
|
}
|
|
437
441
|
|
package/lib/app/rotate-secret.js
CHANGED
|
@@ -152,7 +152,8 @@ function displayRotationResults(appKey, environment, credentials, apiUrl, messag
|
|
|
152
152
|
logger.log(chalk.green('✅ Secret rotated successfully!\n'));
|
|
153
153
|
logger.log(chalk.bold('📋 Application Details:'));
|
|
154
154
|
logger.log(` Key: ${appKey}`);
|
|
155
|
-
logger.log(` Environment: ${environment}
|
|
155
|
+
logger.log(` Environment: ${environment}`);
|
|
156
|
+
logger.log(` Controller: ${apiUrl}\n`);
|
|
156
157
|
|
|
157
158
|
logger.log(chalk.bold.yellow('🔑 NEW CREDENTIALS:'));
|
|
158
159
|
logger.log(chalk.yellow(` Client ID: ${credentials.clientId}`));
|
package/lib/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ const logger = require('./utils/logger');
|
|
|
22
22
|
const { validateCommand, handleCommandError } = require('./utils/cli-utils');
|
|
23
23
|
const { handleLogin } = require('./commands/login');
|
|
24
24
|
const { handleLogout } = require('./commands/logout');
|
|
25
|
+
const { handleAuthStatus } = require('./commands/auth-status');
|
|
25
26
|
const { handleSecure } = require('./commands/secure');
|
|
26
27
|
const { handleSecretsSet } = require('./commands/secrets-set');
|
|
27
28
|
|
|
@@ -32,14 +33,14 @@ const { handleSecretsSet } = require('./commands/secrets-set');
|
|
|
32
33
|
function setupAuthCommands(program) {
|
|
33
34
|
program.command('login')
|
|
34
35
|
.description('Authenticate with Miso Controller')
|
|
35
|
-
.option('-c, --controller <url>', 'Controller URL
|
|
36
|
-
.option('-m, --method <method>', 'Authentication method (device|credentials)')
|
|
36
|
+
.option('-c, --controller <url>', 'Controller URL (default: calculated based on developer ID: http://localhost:${3000 + (developerId * 100)})')
|
|
37
|
+
.option('-m, --method <method>', 'Authentication method (device|credentials)', 'device')
|
|
37
38
|
.option('-a, --app <app>', 'Application name (required for credentials method, reads from secrets.local.yaml)')
|
|
38
39
|
.option('--client-id <id>', 'Client ID (for credentials method, overrides secrets.local.yaml)')
|
|
39
40
|
.option('--client-secret <secret>', 'Client Secret (for credentials method, overrides secrets.local.yaml)')
|
|
40
41
|
.option('-e, --environment <env>', 'Environment key (updates root-level environment in config.yaml, e.g., miso, dev, tst, pro)')
|
|
41
|
-
.option('--
|
|
42
|
-
.option('--scope <scopes>', 'Custom OAuth2 scope string (device flow only, default: "openid profile email")')
|
|
42
|
+
.option('--online', 'Request online-only token (excludes offline_access scope, device flow only)')
|
|
43
|
+
.option('--scope <scopes>', 'Custom OAuth2 scope string (device flow only, default: "openid profile email offline_access")')
|
|
43
44
|
.action(async(options) => {
|
|
44
45
|
try {
|
|
45
46
|
await handleLogin(options);
|
|
@@ -62,6 +63,34 @@ function setupAuthCommands(program) {
|
|
|
62
63
|
process.exit(1);
|
|
63
64
|
}
|
|
64
65
|
});
|
|
66
|
+
|
|
67
|
+
const authStatusHandler = async(options) => {
|
|
68
|
+
try {
|
|
69
|
+
await handleAuthStatus(options);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
handleCommandError(error, 'auth status');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Use nested command pattern for multi-word commands (like environment deploy)
|
|
77
|
+
const auth = program
|
|
78
|
+
.command('auth')
|
|
79
|
+
.description('Authentication commands');
|
|
80
|
+
|
|
81
|
+
auth
|
|
82
|
+
.command('status')
|
|
83
|
+
.description('Display authentication status for current controller and environment')
|
|
84
|
+
.option('-c, --controller <url>', 'Check status for specific controller (uses developer ID-based default if not provided)')
|
|
85
|
+
.option('-e, --environment <env>', 'Check status for specific environment')
|
|
86
|
+
.action(authStatusHandler);
|
|
87
|
+
|
|
88
|
+
// Alias: status (register as separate command since Commander.js doesn't support multi-word aliases)
|
|
89
|
+
program.command('status')
|
|
90
|
+
.description('Display authentication status (alias for auth status)')
|
|
91
|
+
.option('-c, --controller <url>', 'Check status for specific controller (uses developer ID-based default if not provided)')
|
|
92
|
+
.option('-e, --environment <env>', 'Check status for specific environment')
|
|
93
|
+
.action(authStatusHandler);
|
|
65
94
|
}
|
|
66
95
|
|
|
67
96
|
/**
|