@aifabrix/builder 2.55.0 → 2.56.0
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/agent-kit/host-approve.js +48 -6
- package/lib/agent-kit/init.js +2 -2
- package/lib/agent-kit/remote-prompts.js +46 -13
- package/lib/agent-kit/run.js +1 -1
- package/lib/agent-kit/start.js +310 -0
- package/lib/api/pipeline.api.js +32 -3
- package/lib/app/deploy.js +13 -9
- package/lib/app/offer-create-from-template.js +184 -0
- package/lib/cli/setup-app.js +15 -7
- package/lib/cli/setup-auth.js +11 -2
- package/lib/cli/setup-external-system-help.js +15 -3
- package/lib/cli/setup-external-system.js +21 -3
- package/lib/commands/agent-kit-help.js +74 -4
- package/lib/commands/agent-kit.js +83 -12
- package/lib/commands/auth-status-user.js +99 -0
- package/lib/commands/auth-status.js +14 -4
- package/lib/commands/datasource-load-export-cli.js +7 -2
- package/lib/commands/datasource-test-trust-cli.js +5 -1
- package/lib/commands/governance-command-action.js +5 -4
- package/lib/commands/identity-membership-role-cache.js +6 -1
- package/lib/commands/lifecycle-command-action.js +16 -15
- package/lib/commands/login-credentials.js +8 -3
- package/lib/commands/login-device.js +14 -5
- package/lib/commands/login.js +14 -1
- package/lib/commands/logout.js +50 -15
- package/lib/commands/repair-datasource-auto-rbac.js +4 -9
- package/lib/commands/role-assistant.js +24 -12
- package/lib/commands/upload-openapi-sync-followup.js +59 -0
- package/lib/commands/upload-qualification-client.js +131 -0
- package/lib/commands/upload-qualification-followups.js +104 -0
- package/lib/commands/upload-qualification-poll.js +143 -0
- package/lib/commands/upload.js +37 -156
- package/lib/commands/verify-governance-command-action.js +9 -7
- package/lib/commands/verify-operations-command-action.js +9 -8
- package/lib/commands/verify-trust-command-action.js +6 -5
- package/lib/deployment/deploy-poll-ui.js +14 -10
- package/lib/deployment/deployer-status.js +4 -4
- package/lib/external-system/deploy.js +46 -51
- package/lib/external-system/download.js +1 -2
- package/lib/generator/helpers.js +14 -46
- package/lib/generator/index.js +0 -4
- package/lib/integration-definition/apply-static-enterprise.js +5 -2
- package/lib/programmatic/builder-api-request.js +8 -4
- package/lib/programmatic/capability-registry.js +2 -0
- package/lib/programmatic/get-deploy-status.js +45 -0
- package/lib/programmatic/get-upload-status.js +45 -0
- package/lib/programmatic/http-accepted-response.js +31 -0
- package/lib/programmatic/openapi-descriptions.js +14 -4
- package/lib/programmatic/request-timeout.js +21 -15
- package/lib/programmatic/route-handler-map.js +6 -0
- package/lib/programmatic/run-deploy.js +7 -3
- package/lib/programmatic/run-upload.js +11 -7
- package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
- package/lib/role-assistant/knowledge-sync.js +9 -126
- package/lib/role-assistant/test-cases-evaluate.js +12 -1
- package/lib/role-assistant/test-cases-evidence.js +44 -5
- package/lib/role-assistant/test-cases-lifecycle.js +9 -3
- package/lib/role-assistant/test-runner.js +3 -0
- package/lib/schema/infra-parameter.schema.json +51 -3
- package/lib/schema/infra.parameter.yaml +482 -47
- package/lib/schema/type/document-storage.json +1 -1
- package/lib/utils/api-required-permissions.js +271 -0
- package/lib/utils/api.js +16 -13
- package/lib/utils/cli-json-exit.js +41 -0
- package/lib/utils/cli-utils.js +26 -7
- package/lib/utils/controller-url.js +78 -4
- package/lib/utils/deployment-errors.js +15 -0
- package/lib/utils/deployment-validation-helpers.js +4 -1
- package/lib/utils/device-code.js +31 -10
- package/lib/utils/error-formatters/error-parser.js +16 -1
- package/lib/utils/error-formatters/permission-errors.js +19 -5
- package/lib/utils/keycloak-realm-ready.js +5 -2
- package/lib/utils/logger.js +1 -0
- package/lib/utils/paths.js +26 -5
- package/lib/utils/qualification-poll-ui.js +136 -0
- package/lib/utils/secrets-helpers.js +2 -2
- package/lib/utils/write-stream-and-wait.js +63 -0
- package/package.json +22 -7
- package/templates/agent-kit/agent-kit.yaml +1 -1
- package/templates/agent-kit/instructions/AGENTKIT.md +25 -4
- package/templates/agent-kit/instructions/root.AGENTS.md +1 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +2 -1
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +1 -1
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +2 -1
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +1 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +1 -0
- package/templates/agent-kit/skills/shared/hosts.md +13 -26
- package/templates/agent-kit/skills/shared/login.md +16 -0
- package/templates/agent-kit/workspace/README.md +1 -1
- package/templates/applications/builder-api/env.template +4 -0
- package/templates/applications/dataplane/application.yaml +1 -2
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/keycloak/Dockerfile +12 -5
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +3 -3
- package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +6 -6
- package/templates/applications/keycloak/env.template +13 -1
- package/templates/applications/miso-controller/application.yaml +21 -2
- package/templates/applications/miso-controller/env.template +97 -28
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/lib/generator/deploy-manifest-azure-kv.js +0 -81
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `aifabrix agent-kit` command group.
|
|
3
|
-
* @fileoverview Install, init, status, update, validate, remote create
|
|
3
|
+
* @fileoverview Install, init, status, update, validate, remote create, start
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
@@ -9,6 +9,7 @@ const logger = require('../utils/logger');
|
|
|
9
9
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
10
10
|
const { formatSuccessLine, formatBlockingError, formatWarningLine } = require('../utils/cli-layout-chalk');
|
|
11
11
|
const { runInstall, runUpdate } = require('../agent-kit/run');
|
|
12
|
+
const { runStart } = require('../agent-kit/start');
|
|
12
13
|
const path = require('path');
|
|
13
14
|
const { runInit, runRemoteCreate } = require('../agent-kit/init');
|
|
14
15
|
const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
|
|
@@ -26,7 +27,11 @@ const {
|
|
|
26
27
|
UPDATE_HELP,
|
|
27
28
|
VALIDATE_HELP,
|
|
28
29
|
INIT_HELP,
|
|
29
|
-
|
|
30
|
+
REMOTE_GROUP_HELP,
|
|
31
|
+
REMOTE_HELP,
|
|
32
|
+
START_HELP,
|
|
33
|
+
HOST_OPTION_HELP,
|
|
34
|
+
APPROVE_ALL_OPTION_HELP
|
|
30
35
|
} = require('./agent-kit-help');
|
|
31
36
|
|
|
32
37
|
/**
|
|
@@ -91,6 +96,34 @@ function emitRemoteReport(json, asJson) {
|
|
|
91
96
|
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
|
|
92
97
|
}
|
|
93
98
|
|
|
99
|
+
/**
|
|
100
|
+
* @param {object} json
|
|
101
|
+
* @param {boolean} asJson
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
function emitStartReport(json, asJson) {
|
|
105
|
+
if (asJson) {
|
|
106
|
+
logger.log(JSON.stringify(json, null, 2));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
logger.log(`App ${json.app}`);
|
|
110
|
+
logger.log(`Session ${json.session}`);
|
|
111
|
+
if (json.remoteControlName) {
|
|
112
|
+
logger.log(`Remote ${json.remoteControlName}`);
|
|
113
|
+
}
|
|
114
|
+
logger.log(`Path ${json.path}`);
|
|
115
|
+
logger.log(`Launch ${json.launch.join(' ')}`);
|
|
116
|
+
if (json.executed && json.detach) {
|
|
117
|
+
logger.log(formatSuccessLine('Session started in background'));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (json.executed) {
|
|
121
|
+
logger.log(formatSuccessLine('Session ended'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Ready'));
|
|
125
|
+
}
|
|
126
|
+
|
|
94
127
|
/**
|
|
95
128
|
* @param {Function} runner
|
|
96
129
|
* @param {object} options
|
|
@@ -125,6 +158,7 @@ function setupAgentKitCommands(program) {
|
|
|
125
158
|
addUpdateCommand(group);
|
|
126
159
|
addValidateCommand(group);
|
|
127
160
|
addRemoteCommand(group);
|
|
161
|
+
addStartCommand(group);
|
|
128
162
|
}
|
|
129
163
|
|
|
130
164
|
/**
|
|
@@ -134,16 +168,16 @@ function addInitCommand(group) {
|
|
|
134
168
|
group
|
|
135
169
|
.command('init [nameOrPath]')
|
|
136
170
|
.description('Create a customer workspace skeleton, git repo, and Agent Kit')
|
|
137
|
-
.option('--host <target>',
|
|
171
|
+
.option('--host <target>', HOST_OPTION_HELP, 'all')
|
|
138
172
|
.option('--scm <kind>', 'git or none', 'git')
|
|
139
|
-
.option('--remote', 'Create private
|
|
173
|
+
.option('--remote', 'Create private GitHub remote and push (requires auth)')
|
|
140
174
|
.option('--no-remote', 'Force local-only (skip remote prompt on TTY)')
|
|
141
175
|
.option('--org <owner>', 'Hosting org or user (required with --remote when non-TTY)')
|
|
142
176
|
.option('--visibility <mode>', 'private or public', 'private')
|
|
143
177
|
.option('--license <kind>', 'mit or none', 'none')
|
|
144
178
|
.option('--force', 'Allow non-empty target directory')
|
|
145
179
|
.option('--yes', 'Silent: skip TTY confirm (pair with --host and --org); also auto-approves host permissions')
|
|
146
|
-
.option('--approve-all',
|
|
180
|
+
.option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
|
|
147
181
|
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
148
182
|
.option('--dry-run', 'Plan writes without changing files')
|
|
149
183
|
.option('--json', 'Machine-readable output')
|
|
@@ -177,10 +211,13 @@ function addInitCommand(group) {
|
|
|
177
211
|
* @param {import('commander').Command} group
|
|
178
212
|
*/
|
|
179
213
|
function addRemoteCommand(group) {
|
|
180
|
-
const remote = group
|
|
214
|
+
const remote = group
|
|
215
|
+
.command('remote')
|
|
216
|
+
.description('Create a GitHub repository for this workspace and push')
|
|
217
|
+
.addHelpText('after', REMOTE_GROUP_HELP);
|
|
181
218
|
remote
|
|
182
219
|
.command('create')
|
|
183
|
-
.description('Create a private GitHub
|
|
220
|
+
.description('Create a private GitHub repository (origin) and push this workspace')
|
|
184
221
|
.option('--org <owner>', 'Hosting org or user (required when non-TTY)')
|
|
185
222
|
.option('--name <repo>', 'Remote repository name (default: directory basename)')
|
|
186
223
|
.option('--visibility <mode>', 'private or public', 'private')
|
|
@@ -215,8 +252,8 @@ function addInstallCommand(group) {
|
|
|
215
252
|
group
|
|
216
253
|
.command('install')
|
|
217
254
|
.description('Install the Agent Kit into the current git repository')
|
|
218
|
-
.option('--host <target>',
|
|
219
|
-
.option('--approve-all',
|
|
255
|
+
.option('--host <target>', HOST_OPTION_HELP, 'all')
|
|
256
|
+
.option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
|
|
220
257
|
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
221
258
|
.option('--yes', 'Silent: auto-approve host permission configs without asking')
|
|
222
259
|
.option('--dry-run', 'Plan writes without changing files')
|
|
@@ -225,7 +262,8 @@ function addInstallCommand(group) {
|
|
|
225
262
|
.action(async(options) => {
|
|
226
263
|
try {
|
|
227
264
|
const interactive = Boolean(process.stdout.isTTY) && !options.json;
|
|
228
|
-
const
|
|
265
|
+
const withHost = await enrichInitHostOptions(options, { interactive });
|
|
266
|
+
const merged = await enrichApproveAllOptions(withHost, { interactive });
|
|
229
267
|
await runLeaf(runInstall, merged, true);
|
|
230
268
|
} catch (error) {
|
|
231
269
|
handleCommandError(error, 'agent-kit install');
|
|
@@ -287,8 +325,8 @@ function addUpdateCommand(group) {
|
|
|
287
325
|
group
|
|
288
326
|
.command('update')
|
|
289
327
|
.description('Update managed Agent Kit files from this Builder release')
|
|
290
|
-
.option('--host <target>',
|
|
291
|
-
.option('--approve-all',
|
|
328
|
+
.option('--host <target>', `${HOST_OPTION_HELP} (omit to keep recorded hosts)`)
|
|
329
|
+
.option('--approve-all [host]', APPROVE_ALL_OPTION_HELP)
|
|
292
330
|
.option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
|
|
293
331
|
.option('--yes', 'Silent: auto-approve host permission configs without asking')
|
|
294
332
|
.option('--dry-run', 'Plan writes without changing files')
|
|
@@ -343,4 +381,37 @@ async function runValidateLeaf(options) {
|
|
|
343
381
|
}
|
|
344
382
|
}
|
|
345
383
|
|
|
384
|
+
/**
|
|
385
|
+
* @param {import('commander').Command} group
|
|
386
|
+
*/
|
|
387
|
+
function addStartCommand(group) {
|
|
388
|
+
group
|
|
389
|
+
.command('start <app>')
|
|
390
|
+
.description('Start Claude Code or Codex in tmux (does not install skills)')
|
|
391
|
+
.option('--approve-all', 'Write auto-approve config for this app only')
|
|
392
|
+
.option('--skip-approve-all', 'Do not write auto-approve configs')
|
|
393
|
+
.option('--yes', 'Same as --approve-all')
|
|
394
|
+
.option('--detach', 'Create the tmux session in the background (no attach)')
|
|
395
|
+
.option(
|
|
396
|
+
'--name <session>',
|
|
397
|
+
'tmux session name (default: last folder of this git workspace)'
|
|
398
|
+
)
|
|
399
|
+
.option(
|
|
400
|
+
'--remote-control',
|
|
401
|
+
'Claude Code only: same session on this PC, claude.ai/code, and the Claude mobile app'
|
|
402
|
+
)
|
|
403
|
+
.option('--dry-run', 'Print the launch command without starting it')
|
|
404
|
+
.option('--json', 'Machine-readable output')
|
|
405
|
+
.addHelpText('after', START_HELP)
|
|
406
|
+
.action(async(app, options) => {
|
|
407
|
+
try {
|
|
408
|
+
const json = runStart({ ...options, app });
|
|
409
|
+
emitStartReport(json, Boolean(options.json));
|
|
410
|
+
} catch (error) {
|
|
411
|
+
handleCommandError(error, 'agent-kit start');
|
|
412
|
+
process.exitCode = 1;
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
346
417
|
module.exports = { setupAgentKitCommands };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the signed-in username for `aifabrix auth status`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Unwrap controller auth/user payloads and pick a display username
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { unwrapControllerData } = require('../identity/identity-response-helpers');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {...*} values
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function firstNonEmptyString(...values) {
|
|
18
|
+
for (const value of values) {
|
|
19
|
+
if (typeof value === 'string' && value.trim()) {
|
|
20
|
+
return value.trim();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decode a JWT payload for display only. Does not verify the signature.
|
|
28
|
+
* @param {string} token
|
|
29
|
+
* @returns {Object|null}
|
|
30
|
+
*/
|
|
31
|
+
function decodeJwtPayload(token) {
|
|
32
|
+
if (!token || typeof token !== 'string') {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const parts = token.split('.');
|
|
36
|
+
if (parts.length < 2) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
41
|
+
const padded = b64.padEnd(Math.ceil(b64.length / 4) * 4, '=');
|
|
42
|
+
const payload = JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
|
|
43
|
+
return payload && typeof payload === 'object' ? payload : null;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {Object|null|undefined} user
|
|
51
|
+
* @param {string} [token]
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function resolveDisplayUsername(user, token) {
|
|
55
|
+
const fromUser = firstNonEmptyString(
|
|
56
|
+
user && user.username,
|
|
57
|
+
user && user.preferredUsername,
|
|
58
|
+
user && user.preferred_username,
|
|
59
|
+
user && user.email
|
|
60
|
+
);
|
|
61
|
+
if (fromUser) {
|
|
62
|
+
return fromUser;
|
|
63
|
+
}
|
|
64
|
+
const payload = decodeJwtPayload(token);
|
|
65
|
+
return firstNonEmptyString(
|
|
66
|
+
payload && payload.preferred_username,
|
|
67
|
+
payload && payload.preferredUsername,
|
|
68
|
+
payload && payload.email,
|
|
69
|
+
payload && payload.upn
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {Object|null|undefined} response
|
|
75
|
+
* @param {string} [token]
|
|
76
|
+
* @returns {{ authenticated: boolean, user: Object|null, username: string }}
|
|
77
|
+
*/
|
|
78
|
+
function parseAuthUserResponse(response, token) {
|
|
79
|
+
const payload = unwrapControllerData(response);
|
|
80
|
+
if (!payload || typeof payload !== 'object') {
|
|
81
|
+
return { authenticated: false, user: null, username: '' };
|
|
82
|
+
}
|
|
83
|
+
const rawUser = payload.user && typeof payload.user === 'object' ? payload.user : null;
|
|
84
|
+
const username = resolveDisplayUsername(rawUser, token);
|
|
85
|
+
const user = username && (!rawUser || !rawUser.username)
|
|
86
|
+
? { ...(rawUser || {}), username }
|
|
87
|
+
: rawUser;
|
|
88
|
+
return {
|
|
89
|
+
authenticated: payload.authenticated !== false,
|
|
90
|
+
user,
|
|
91
|
+
username
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
decodeJwtPayload,
|
|
97
|
+
resolveDisplayUsername,
|
|
98
|
+
parseAuthUserResponse
|
|
99
|
+
};
|
|
@@ -34,6 +34,7 @@ const {
|
|
|
34
34
|
displayStoredClientTokensSection,
|
|
35
35
|
tryResolveClientTokenAuth
|
|
36
36
|
} = require('./auth-status-client-tokens');
|
|
37
|
+
const { parseAuthUserResponse } = require('./auth-status-user');
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* Format expiration date for display
|
|
@@ -71,11 +72,13 @@ async function checkDeviceToken(controllerUrl) {
|
|
|
71
72
|
const response = await getAuthUser(controllerUrl, authConfig);
|
|
72
73
|
|
|
73
74
|
if (response.success && response.data) {
|
|
75
|
+
const parsed = parseAuthUserResponse(response, deviceToken.token);
|
|
74
76
|
return {
|
|
75
77
|
type: 'Device Token',
|
|
76
78
|
token: deviceToken.token,
|
|
77
|
-
authenticated:
|
|
78
|
-
user:
|
|
79
|
+
authenticated: parsed.authenticated,
|
|
80
|
+
user: parsed.user,
|
|
81
|
+
username: parsed.username,
|
|
79
82
|
expiresAt: deviceToken.expiresAt
|
|
80
83
|
};
|
|
81
84
|
}
|
|
@@ -113,11 +116,13 @@ async function validateClientToken(token, controllerUrl, environment, appName, e
|
|
|
113
116
|
const response = await getAuthUser(controllerUrl, authConfig);
|
|
114
117
|
|
|
115
118
|
if (response.success && response.data) {
|
|
119
|
+
const parsed = parseAuthUserResponse(response, token);
|
|
116
120
|
return {
|
|
117
121
|
type: 'Client Token',
|
|
118
122
|
token: token,
|
|
119
|
-
authenticated:
|
|
120
|
-
user:
|
|
123
|
+
authenticated: parsed.authenticated,
|
|
124
|
+
user: parsed.user,
|
|
125
|
+
username: parsed.username,
|
|
121
126
|
expiresAt: expiresAt,
|
|
122
127
|
appName: appName
|
|
123
128
|
};
|
|
@@ -185,6 +190,11 @@ function displayTokenInfo(tokenInfo) {
|
|
|
185
190
|
logger.log(` Status: ${statusIcon} ${statusText}`);
|
|
186
191
|
logger.log(` Token Type: ${chalk.cyan(tokenInfo.type)}`);
|
|
187
192
|
|
|
193
|
+
if (tokenInfo.authenticated) {
|
|
194
|
+
const username = tokenInfo.username || (tokenInfo.user && tokenInfo.user.username);
|
|
195
|
+
logger.log(` User: ${username ? chalk.cyan(username) : chalk.gray('—')}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
188
198
|
if (tokenInfo.appName) {
|
|
189
199
|
logger.log(` Application: ${chalk.cyan(tokenInfo.appName)}`);
|
|
190
200
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const logger = require('../utils/logger');
|
|
10
|
+
const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
|
|
10
11
|
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
11
12
|
const { runDatasourceLoad } = require('../datasource/bulk-loader-service');
|
|
12
13
|
const { runDatasourceExport } = require('../datasource/datasource-exporter-service');
|
|
@@ -82,7 +83,9 @@ async function datasourceLoadAction(datasourceKey, options) {
|
|
|
82
83
|
|
|
83
84
|
if (options.json) {
|
|
84
85
|
const { context: _ctx, ...payload } = result;
|
|
85
|
-
|
|
86
|
+
// exitCode 0 — write and resolve, never exit here; this action always
|
|
87
|
+
// exits unconditionally below with the real computed code.
|
|
88
|
+
await writeJsonEnvelopeAndExit(payload, 0);
|
|
86
89
|
} else {
|
|
87
90
|
displayDatasourceLoadTTY(result, { environment: options.env, verbose: options.verbose });
|
|
88
91
|
}
|
|
@@ -112,7 +115,9 @@ async function datasourceExportAction(datasourceKey, options) {
|
|
|
112
115
|
|
|
113
116
|
if (options.json) {
|
|
114
117
|
const { context: _ctx, ...payload } = result;
|
|
115
|
-
|
|
118
|
+
// exitCode 0 — write and resolve, never exit here; this action always
|
|
119
|
+
// exits unconditionally below with the real computed code.
|
|
120
|
+
await writeJsonEnvelopeAndExit(payload, 0);
|
|
116
121
|
} else {
|
|
117
122
|
displayDatasourceExportTTY(result, { environment: options.env, verbose: options.verbose });
|
|
118
123
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const logger = require('../utils/logger');
|
|
10
|
+
const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
|
|
10
11
|
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
11
12
|
const { runDatasourceAgentTrust } = require('../datasource/agent-trust-run');
|
|
12
13
|
const { writeTrustDebugLogAndPrint } = require('../datasource/agent-trust-debug-log');
|
|
@@ -52,7 +53,10 @@ async function finalizeDatasourceVerifyTrust(datasourceKey, options, result) {
|
|
|
52
53
|
strict: options.strict === true
|
|
53
54
|
};
|
|
54
55
|
if (options.json) {
|
|
55
|
-
|
|
56
|
+
// exitCode 0 here means "write and resolve, never exit" — the real exit
|
|
57
|
+
// code is decided below and the caller exits unconditionally, even on
|
|
58
|
+
// success, so process.exit() must stay solely the caller's job.
|
|
59
|
+
await writeJsonEnvelopeAndExit(result.trustRun, 0);
|
|
56
60
|
} else if (options.summary) {
|
|
57
61
|
const tr = result.trustRun;
|
|
58
62
|
logger.log(
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const logger = require('../utils/logger');
|
|
8
8
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
9
|
+
const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
|
|
9
10
|
const { runGovernanceGenerateForExternalSystem } = require('./governance-generate-external');
|
|
10
11
|
const { runGovernanceVerifyForExternalSystem } = require('./governance-verify-external');
|
|
11
12
|
const { runGovernanceUploadForExternalSystem } = require('./governance-upload-external');
|
|
@@ -75,12 +76,12 @@ async function governanceVerifyHandler(systemKey, options, cmd) {
|
|
|
75
76
|
try {
|
|
76
77
|
const opts = normalizeGovernanceOptions(options, cmd);
|
|
77
78
|
const result = await runGovernanceVerifyForExternalSystem(systemKey, opts);
|
|
79
|
+
const exitCode = exitCodeFromVerdict(result.verdict);
|
|
78
80
|
if (opts.json) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
|
|
81
|
+
await writeJsonEnvelopeAndExit(buildJsonEnvelope(result, opts.verbose), exitCode);
|
|
82
|
+
return;
|
|
82
83
|
}
|
|
83
|
-
|
|
84
|
+
displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
|
|
84
85
|
if (exitCode !== 0) {
|
|
85
86
|
process.exit(exitCode);
|
|
86
87
|
}
|
|
@@ -35,7 +35,12 @@ const ROLE_LIST_COLUMNS = [
|
|
|
35
35
|
label: 'Groups',
|
|
36
36
|
width: 44,
|
|
37
37
|
value: (r) => {
|
|
38
|
-
const groups = Array.isArray(r.groups)
|
|
38
|
+
const groups = Array.isArray(r.groups)
|
|
39
|
+
? r.groups.filter(Boolean)
|
|
40
|
+
: (r.mappings || [])
|
|
41
|
+
.filter((mapping) => mapping?.isActive !== false)
|
|
42
|
+
.map((mapping) => mapping?.groupName)
|
|
43
|
+
.filter(Boolean);
|
|
39
44
|
return groups.length > 0 ? groups.join(', ') : '—';
|
|
40
45
|
}
|
|
41
46
|
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
-
const logger = require('../utils/logger');
|
|
10
9
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
10
|
+
const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
|
|
11
11
|
const { cliOptsSkipPublish } = require('../utils/cli-sync-options');
|
|
12
12
|
const { runLifecycleForExternalSystem } = require('./lifecycle-external');
|
|
13
13
|
const { displayLifecycleReportTTY } = require('../lifecycle/report-display');
|
|
@@ -65,6 +65,12 @@ async function runLifecycleCommandAction(appName, options, cmd) {
|
|
|
65
65
|
const result = await runLifecycleForExternalSystem(appName, opts);
|
|
66
66
|
const report = result.report;
|
|
67
67
|
|
|
68
|
+
const pillarVerdict = lifecycleVerdictFromReport(report);
|
|
69
|
+
const exitCode =
|
|
70
|
+
pillarVerdict === VERDICT.VERIFIED
|
|
71
|
+
? 0
|
|
72
|
+
: exitCodeFromVerdict(pillarVerdict, { partial: pillarVerdict === VERDICT.NOT_VERIFIED });
|
|
73
|
+
|
|
68
74
|
if (opts.json) {
|
|
69
75
|
const envelope = {
|
|
70
76
|
systemKey: result.systemKey,
|
|
@@ -82,22 +88,17 @@ async function runLifecycleCommandAction(appName, options, cmd) {
|
|
|
82
88
|
if (opts.verbose) {
|
|
83
89
|
envelope.datasources = report.datasources;
|
|
84
90
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
displayLifecycleReportTTY(result.systemKey, report, {
|
|
88
|
-
details: opts.verbose,
|
|
89
|
-
openApiPublished: result.openApiPublished === true,
|
|
90
|
-
certificationProgress: result.certificationProgress ?? null,
|
|
91
|
-
dataplaneUrl: result.dataplaneUrl ?? null,
|
|
92
|
-
dataplaneUiBaseUrl: result.dataplaneUiBaseUrl ?? null
|
|
93
|
-
});
|
|
91
|
+
await writeJsonEnvelopeAndExit(envelope, exitCode);
|
|
92
|
+
return;
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
displayLifecycleReportTTY(result.systemKey, report, {
|
|
96
|
+
details: opts.verbose,
|
|
97
|
+
openApiPublished: result.openApiPublished === true,
|
|
98
|
+
certificationProgress: result.certificationProgress ?? null,
|
|
99
|
+
dataplaneUrl: result.dataplaneUrl ?? null,
|
|
100
|
+
dataplaneUiBaseUrl: result.dataplaneUiBaseUrl ?? null
|
|
101
|
+
});
|
|
101
102
|
if (exitCode !== 0) {
|
|
102
103
|
process.exit(exitCode);
|
|
103
104
|
}
|
|
@@ -15,6 +15,7 @@ const { getToken } = require('../api/auth.api');
|
|
|
15
15
|
const { formatApiError } = require('../utils/api-error-handler');
|
|
16
16
|
const { loadClientCredentials } = require('../utils/token-manager');
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
|
+
const { writeStreamAndWait } = require('../utils/write-stream-and-wait');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Tries to load credentials from secrets file
|
|
@@ -136,7 +137,7 @@ function handleLoginError(response, appName) {
|
|
|
136
137
|
* @param {Object} response - API response
|
|
137
138
|
* @returns {Object} Token data object
|
|
138
139
|
*/
|
|
139
|
-
function extractTokenData(response) {
|
|
140
|
+
async function extractTokenData(response) {
|
|
140
141
|
// OpenAPI spec response: { success: boolean, token: string, expiresIn: number, expiresAt: string, ... }
|
|
141
142
|
// Handle both flat and nested response structures (some APIs wrap in data field)
|
|
142
143
|
// If response.data exists, use it; otherwise use response directly
|
|
@@ -146,7 +147,10 @@ function extractTokenData(response) {
|
|
|
146
147
|
if (!responseData || !responseData.token) {
|
|
147
148
|
logger.error(formatBlockingError('Invalid response: missing token'));
|
|
148
149
|
if (responseData) {
|
|
149
|
-
|
|
150
|
+
await writeStreamAndWait(
|
|
151
|
+
process.stderr,
|
|
152
|
+
`${chalk.gray(`Response structure: ${JSON.stringify(responseData, null, 2)}`)}\n`
|
|
153
|
+
);
|
|
150
154
|
}
|
|
151
155
|
process.exit(1);
|
|
152
156
|
}
|
|
@@ -190,9 +194,10 @@ async function handleCredentialsLogin(controllerUrl, appName, clientId, clientSe
|
|
|
190
194
|
|
|
191
195
|
if (!response.success) {
|
|
192
196
|
handleLoginError(response, appName);
|
|
197
|
+
return;
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
const responseData = extractTokenData(response);
|
|
200
|
+
const responseData = await extractTokenData(response);
|
|
196
201
|
const expiresAt = calculateTokenExpiration(responseData);
|
|
197
202
|
|
|
198
203
|
return {
|
|
@@ -15,6 +15,7 @@ const ora = require('ora');
|
|
|
15
15
|
const { setCurrentEnvironment, saveDeviceToken, setControllerUrl } = require('../core/config');
|
|
16
16
|
const { initiateDeviceCodeFlow } = require('../api/auth.api');
|
|
17
17
|
const { pollDeviceCodeToken, displayDeviceCodeInfo } = require('../utils/api');
|
|
18
|
+
const { resolveDeviceVisitUrl } = require('../utils/device-code');
|
|
18
19
|
const logger = require('../utils/logger');
|
|
19
20
|
const { formatSuccessLine } = require('../utils/cli-layout-chalk');
|
|
20
21
|
|
|
@@ -42,6 +43,10 @@ async function getEnvironmentKey(environment) {
|
|
|
42
43
|
return envKey;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
if (!process.stdin || process.stdin.isTTY === false) {
|
|
47
|
+
return 'dev';
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
const envPrompt = await inquirer.prompt([{
|
|
46
51
|
type: 'input',
|
|
47
52
|
name: 'environment',
|
|
@@ -255,10 +260,8 @@ function logDeviceFlowError(deviceError, opts) {
|
|
|
255
260
|
}
|
|
256
261
|
|
|
257
262
|
function printCompactDeviceUrl(deviceCodeResponse) {
|
|
258
|
-
const visitUrl =
|
|
259
|
-
|
|
260
|
-
`${deviceCodeResponse.verification_uri}?user_code=${deviceCodeResponse.user_code}`;
|
|
261
|
-
logger.log('\nOpen browser to authenticate:');
|
|
263
|
+
const visitUrl = resolveDeviceVisitUrl(deviceCodeResponse);
|
|
264
|
+
logger.log('\nOpen this URL to authenticate:');
|
|
262
265
|
logger.log(chalk.yellow(`${visitUrl}\n`));
|
|
263
266
|
}
|
|
264
267
|
|
|
@@ -289,7 +292,13 @@ async function handleDeviceCodeLogin(controllerUrl, environment, online, scope,
|
|
|
289
292
|
const deviceCodeResponse = convertDeviceCodeResponse(apiResponse);
|
|
290
293
|
|
|
291
294
|
if (!opts.compact) {
|
|
292
|
-
displayDeviceCodeInfo(
|
|
295
|
+
displayDeviceCodeInfo(
|
|
296
|
+
deviceCodeResponse.user_code,
|
|
297
|
+
deviceCodeResponse.verification_uri,
|
|
298
|
+
logger,
|
|
299
|
+
chalk,
|
|
300
|
+
deviceCodeResponse.verification_uri_complete
|
|
301
|
+
);
|
|
293
302
|
} else {
|
|
294
303
|
printCompactDeviceUrl(deviceCodeResponse);
|
|
295
304
|
}
|
package/lib/commands/login.js
CHANGED
|
@@ -16,9 +16,13 @@ const { setCurrentEnvironment, saveClientToken, setControllerUrl } = require('..
|
|
|
16
16
|
const logger = require('../utils/logger');
|
|
17
17
|
const { handleCredentialsLogin } = require('./login-credentials');
|
|
18
18
|
const { handleDeviceCodeLogin } = require('./login-device');
|
|
19
|
-
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
19
|
+
const { resolveControllerUrl, discoverControllerBaseUrl } = require('../utils/controller-url');
|
|
20
20
|
const { tryRefreshDataplaneVersionAfterLogin } = require('./auth-status-dataplane-version');
|
|
21
21
|
|
|
22
|
+
function isInteractiveStdin() {
|
|
23
|
+
return Boolean(process.stdin && process.stdin.isTTY);
|
|
24
|
+
}
|
|
25
|
+
|
|
22
26
|
/**
|
|
23
27
|
* Determine and validate authentication method
|
|
24
28
|
* @async
|
|
@@ -34,6 +38,10 @@ async function determineAuthMethod(method) {
|
|
|
34
38
|
return method;
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
if (!isInteractiveStdin()) {
|
|
42
|
+
return 'device';
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
const authMethod = await inquirer.prompt([{
|
|
38
46
|
type: 'list',
|
|
39
47
|
name: 'method',
|
|
@@ -86,6 +94,8 @@ async function normalizeControllerUrl(options) {
|
|
|
86
94
|
controllerUrl = await resolveControllerUrl();
|
|
87
95
|
}
|
|
88
96
|
controllerUrl = String(controllerUrl).trim().replace(/\/+$/, '');
|
|
97
|
+
const inputUrl = controllerUrl;
|
|
98
|
+
controllerUrl = await discoverControllerBaseUrl(controllerUrl);
|
|
89
99
|
if (!options.skipControllerUrlPersist) {
|
|
90
100
|
const persistUrl = options.tokenControllerUrl
|
|
91
101
|
? String(options.tokenControllerUrl).trim().replace(/\/+$/, '')
|
|
@@ -93,6 +103,9 @@ async function normalizeControllerUrl(options) {
|
|
|
93
103
|
await setControllerUrl(persistUrl);
|
|
94
104
|
}
|
|
95
105
|
if (!options.compact) {
|
|
106
|
+
if (controllerUrl !== inputUrl) {
|
|
107
|
+
logger.log(chalk.gray(`Using controller ${controllerUrl}`));
|
|
108
|
+
}
|
|
96
109
|
logger.log(chalk.gray(`Controller URL: ${controllerUrl}`));
|
|
97
110
|
}
|
|
98
111
|
return controllerUrl;
|