@aifabrix/builder 2.45.5 → 2.46.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/.cursor/rules/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +130 -0
- package/jest.projects.js +41 -136
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/index.js +43 -3
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +3 -1
- package/lib/api/records-bulk.api.js +14 -1
- package/lib/api/validation-runner.js +3 -1
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/down.js +20 -1
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +4 -0
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +84 -81
- package/lib/cli/setup-app.help.js +0 -31
- package/lib/cli/setup-app.js +28 -2
- package/lib/cli/setup-app.test-commands.js +5 -57
- package/lib/cli/setup-auth.js +25 -0
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +25 -3
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +22 -2
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +40 -4
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +4 -239
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +58 -20
- package/lib/commands/auth-status.js +41 -47
- package/lib/commands/datasource-test-trust-cli.js +27 -23
- package/lib/commands/datasource-unified-test-cli.js +6 -3
- package/lib/commands/datasource-unified-test-cli.options.js +5 -1
- package/lib/commands/datasource.js +7 -7
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +11 -2
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -296
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +67 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +24 -8
- package/lib/commands/test-governance-external.js +3 -24
- package/lib/commands/test-trust-external.js +20 -21
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +90 -108
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +2 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +55 -0
- package/lib/datasource/agent-trust-debug-log.js +1 -1
- package/lib/datasource/agent-trust-run.js +5 -3
- package/lib/datasource/audit-evidence-extract-debug.js +30 -18
- package/lib/datasource/audit-evidence-extract.js +7 -4
- package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
- package/lib/datasource/bulk-loader-run-batches.js +15 -3
- package/lib/datasource/datasource-load-export-context.js +3 -0
- package/lib/datasource/log-cleaner.js +2 -2
- package/lib/datasource/log-viewer.js +8 -8
- package/lib/datasource/test-e2e.js +4 -1
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run.js +5 -2
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test.js +6 -6
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-run-display.js +215 -25
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display.js +471 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +10 -4
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2622
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/type/credential-field-registry.json +103 -97
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-resolver.js +129 -2
- package/lib/utils/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +11 -8
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +2 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +12 -5
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +8 -0
- package/lib/utils/validation-run-stage.js +45 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +24 -1
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +74 -17
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
- package/lib/commands/test-governance-command-action.js +0 -55
- package/lib/commands/test-trust-command-action.js +0 -118
package/lib/cli/setup-utility.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
2
1
|
/**
|
|
3
2
|
* CLI utility command setup (resolve, json, split-json, show, validate, diff).
|
|
4
3
|
*
|
|
@@ -10,12 +9,15 @@ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
|
10
9
|
const path = require('path');
|
|
11
10
|
const fs = require('fs');
|
|
12
11
|
const chalk = require('chalk');
|
|
12
|
+
const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
13
13
|
const generator = require('../generator');
|
|
14
14
|
const logger = require('../utils/logger');
|
|
15
15
|
const { handleCommandError, logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
16
16
|
const { detectAppType, getDeployJsonPath, getResolveAppPath } = require('../utils/paths');
|
|
17
17
|
const { setupResolveCommand } = require('./setup-utility-resolve');
|
|
18
|
-
const {
|
|
18
|
+
const { setupRepairCommand } = require('./setup-utility-repair');
|
|
19
|
+
const { setupValidateDiffCommands } = require('./setup-utility-validate');
|
|
20
|
+
const { JSON_HELP_AFTER } = require('./setup-utility-help-after');
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Resolve app path and type for split-json (integration first, then builder).
|
|
@@ -136,100 +138,6 @@ function setupSplitJsonCommand(program) {
|
|
|
136
138
|
});
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
function buildRepairExternalIntegrationOptions(appName, options, format) {
|
|
140
|
-
const all = options.all === true;
|
|
141
|
-
return {
|
|
142
|
-
auth: options.auth,
|
|
143
|
-
doc: options.doc || all,
|
|
144
|
-
dryRun: options.dryRun,
|
|
145
|
-
format,
|
|
146
|
-
rbac: options.rbac || all,
|
|
147
|
-
expose: options.expose || all,
|
|
148
|
-
sync: options.sync || all,
|
|
149
|
-
api: options.api || all,
|
|
150
|
-
test: options.test || all,
|
|
151
|
-
backup: options.backup,
|
|
152
|
-
noBackup: options.noBackup
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function logRepairResult(options, result) {
|
|
157
|
-
if (options.dryRun && result.updated && result.changes.length > 0) {
|
|
158
|
-
logger.log(chalk.yellow('\nWould apply:'));
|
|
159
|
-
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (Array.isArray(result.backupPaths) && result.backupPaths.length > 0) {
|
|
163
|
-
result.backupPaths.forEach((p) => logger.log(chalk.gray(`Backup: ${p}`)));
|
|
164
|
-
}
|
|
165
|
-
if (result.updated) {
|
|
166
|
-
logger.log(formatSuccessParagraph('Repaired external integration config.'));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
if (Array.isArray(result.changes) && result.changes.length > 0) {
|
|
170
|
-
logger.log(formatSuccessParagraph('Repair actions completed.'));
|
|
171
|
-
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
if (result.readmeRegenerated) {
|
|
175
|
-
logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Handler for `aifabrix repair <systemKey>`.
|
|
183
|
-
* @param {string} appName
|
|
184
|
-
* @param {object} options - Commander options
|
|
185
|
-
* @returns {Promise<void>}
|
|
186
|
-
*/
|
|
187
|
-
async function handleRepairCommand(appName, options) {
|
|
188
|
-
const { repairExternalIntegration } = require('../commands/repair');
|
|
189
|
-
const { detectAppType } = require('../utils/paths');
|
|
190
|
-
const { appPath } = await detectAppType(appName);
|
|
191
|
-
logOfflinePathWhenType(appPath);
|
|
192
|
-
let format = 'yaml';
|
|
193
|
-
try {
|
|
194
|
-
const config = require('../core/config');
|
|
195
|
-
format = (await config.getFormat()) || format;
|
|
196
|
-
} catch (_) {
|
|
197
|
-
/* default yaml when config unavailable */
|
|
198
|
-
}
|
|
199
|
-
const result = await repairExternalIntegration(
|
|
200
|
-
appName,
|
|
201
|
-
buildRepairExternalIntegrationOptions(appName, options, format)
|
|
202
|
-
);
|
|
203
|
-
logRepairResult(options, result);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function setupRepairCommand(program) {
|
|
207
|
-
program.command('repair <systemKey>')
|
|
208
|
-
.description('Fix external integration drift (files, RBAC, manifest, …)')
|
|
209
|
-
.option('--all', 'Run all repair actions (api, doc, expose, rbac, sync, test)')
|
|
210
|
-
.option(
|
|
211
|
-
'--api',
|
|
212
|
-
'Validate and sync API contracts needed for OpenAPI/MCP (uses local OpenAPI specs at integration/<systemKey>/openapi/*.json when present)'
|
|
213
|
-
)
|
|
214
|
-
.option('--auth <method>', 'Set authentication method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none); updates system file and env.template')
|
|
215
|
-
.option('--doc', 'Regenerate README.md from deployment manifest')
|
|
216
|
-
.option('--expose', 'Set exposed.schema on each datasource from all fieldMappings.attributes keys (metadata.<key>); removes deprecated exposed.attributes if present')
|
|
217
|
-
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
|
|
218
|
-
.option('--sync', 'Add default sync section to datasources that lack it')
|
|
219
|
-
.option('--test', 'Generate testPayload.payloadTemplate and testPayload.expectedResult from attributes')
|
|
220
|
-
.option('--no-backup', 'Skip timestamped copies under integration/<systemKey>/backup/')
|
|
221
|
-
.option('--dry-run', 'Report changes only; do not write')
|
|
222
|
-
.addHelpText('after', REPAIR_HELP_AFTER)
|
|
223
|
-
.action(async(appName, options) => {
|
|
224
|
-
try {
|
|
225
|
-
await handleRepairCommand(appName, options);
|
|
226
|
-
} catch (error) {
|
|
227
|
-
handleCommandError(error, 'repair');
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
141
|
async function resolveConvertTargetFormat(options) {
|
|
234
142
|
const config = require('../core/config');
|
|
235
143
|
const effectiveFormat = options.format || (await config.getFormat());
|
|
@@ -316,149 +224,6 @@ function setupSplitJsonConvertShowCommands(program) {
|
|
|
316
224
|
setupShowCommand(program);
|
|
317
225
|
}
|
|
318
226
|
|
|
319
|
-
function emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat) {
|
|
320
|
-
if (outFormat === 'json' || !result?.appPath || !appOrFile || typeof appOrFile !== 'string') {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
try {
|
|
324
|
-
if (fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile()) {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
328
|
-
emitManifestMetadataLineIfTTY(logger, {
|
|
329
|
-
appKey: appOrFile,
|
|
330
|
-
appPath: result.appPath,
|
|
331
|
-
envOnly: false,
|
|
332
|
-
json: false
|
|
333
|
-
});
|
|
334
|
-
} catch {
|
|
335
|
-
/* ignore emit failures */
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async function runValidateBatchBranch(validate, opts) {
|
|
340
|
-
const integration = opts.integration === true;
|
|
341
|
-
const builder = opts.builder === true;
|
|
342
|
-
if (!integration && !builder) {
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
const outFormat = (opts.format || 'default').toLowerCase();
|
|
346
|
-
const batchResult = integration && builder
|
|
347
|
-
? await validate.validateAll(opts)
|
|
348
|
-
: integration
|
|
349
|
-
? await validate.validateAllIntegrations(opts)
|
|
350
|
-
: await validate.validateAllBuilderApps(opts);
|
|
351
|
-
if (outFormat === 'json') {
|
|
352
|
-
logger.log(JSON.stringify(batchResult, null, 2));
|
|
353
|
-
} else {
|
|
354
|
-
validate.displayBatchValidationResults(batchResult);
|
|
355
|
-
}
|
|
356
|
-
if (!batchResult.valid) process.exit(1);
|
|
357
|
-
return true;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function runProtectionValidateBranch(appOrFile, opts) {
|
|
361
|
-
const { validateProtectionBatchCommand } = require('../protection/validate-batch');
|
|
362
|
-
const code = await validateProtectionBatchCommand(
|
|
363
|
-
{
|
|
364
|
-
env: opts.env,
|
|
365
|
-
json: (opts.format || '').toLowerCase() === 'json',
|
|
366
|
-
warningsAsErrors: opts.warningsAsErrors === true,
|
|
367
|
-
simulate: opts.simulate === true,
|
|
368
|
-
verbose: opts.verbose === true
|
|
369
|
-
},
|
|
370
|
-
logger
|
|
371
|
-
);
|
|
372
|
-
if (code !== 0) {
|
|
373
|
-
process.exit(code);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function buildValidateJsonPayload(result, appOrFile) {
|
|
378
|
-
let payload = result;
|
|
379
|
-
try {
|
|
380
|
-
if (result?.appPath && appOrFile && typeof appOrFile === 'string') {
|
|
381
|
-
if (!(fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile())) {
|
|
382
|
-
const { getManifestSourcePayload } = require('../utils/manifest-source-emit');
|
|
383
|
-
payload = { ...result, manifestSource: getManifestSourcePayload(appOrFile, result.appPath) };
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
} catch {
|
|
387
|
-
/* keep plain result */
|
|
388
|
-
}
|
|
389
|
-
return payload;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function displaySingleValidateResult(appOrFile, result, outFormat, validate) {
|
|
393
|
-
emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat);
|
|
394
|
-
if (outFormat === 'json') {
|
|
395
|
-
logger.log(JSON.stringify(buildValidateJsonPayload(result, appOrFile), null, 2));
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
validate.displayValidationResults(result);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
async function runValidateCommand(appOrFile, options) {
|
|
402
|
-
const validate = require('../validation/validate');
|
|
403
|
-
const opts = options.opts ? options.opts() : options;
|
|
404
|
-
const { isProtectionScope } = require('../protection/scope');
|
|
405
|
-
if (isProtectionScope(appOrFile)) {
|
|
406
|
-
await runProtectionValidateBranch(appOrFile, opts);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (await runValidateBatchBranch(validate, opts)) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const outFormat = (opts.format || 'default').toLowerCase();
|
|
413
|
-
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
414
|
-
logger.log(chalk.red('App name or file path is required, or use --integration / --builder'));
|
|
415
|
-
process.exit(1);
|
|
416
|
-
}
|
|
417
|
-
const result = await validate.validateAppOrFile(appOrFile, {
|
|
418
|
-
...opts,
|
|
419
|
-
certSync: opts.certSync === true
|
|
420
|
-
});
|
|
421
|
-
displaySingleValidateResult(appOrFile, result, outFormat, validate);
|
|
422
|
-
if (!result.valid) process.exit(1);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function setupValidateDiffCommands(program) {
|
|
426
|
-
program.command('validate [appKey|systemKey|file]')
|
|
427
|
-
.description('Validate appKey, systemKey, file path, .protection batch, or all under integration/ and builder/')
|
|
428
|
-
.addHelpText('after', VALIDATE_HELP_AFTER)
|
|
429
|
-
.option('--format <format>', 'Output format: json | default (human-readable)')
|
|
430
|
-
.option('--integration', 'Validate all applications under integration/')
|
|
431
|
-
.option('--builder', 'Validate all applications under builder/')
|
|
432
|
-
.option('--warnings-as-errors', 'For .protection: pass strict=true to dataplane validate')
|
|
433
|
-
.option('--simulate', 'For .protection: run simulate after validate')
|
|
434
|
-
.option(
|
|
435
|
-
'--cert-sync',
|
|
436
|
-
'After successful validation of an external integration, refresh the system file certification block from the dataplane (requires login)'
|
|
437
|
-
)
|
|
438
|
-
.action((appOrFile, options) => {
|
|
439
|
-
runValidateCommand(appOrFile, options).catch((error) => {
|
|
440
|
-
handleCommandError(error, 'validate');
|
|
441
|
-
process.exit(1);
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
program.command('diff <file1> <file2>')
|
|
446
|
-
.description('Diff two config files (optional schema validate)')
|
|
447
|
-
.option('--no-validate', 'Skip schema validation (type check still applied)')
|
|
448
|
-
.action(async(file1, file2, cmd) => {
|
|
449
|
-
try {
|
|
450
|
-
const diff = require('../core/diff');
|
|
451
|
-
const opts = cmd.opts();
|
|
452
|
-
const result = await diff.compareFiles(file1, file2, { validate: opts.validate !== false });
|
|
453
|
-
diff.formatDiffOutput(result);
|
|
454
|
-
if (!result.identical) process.exit(1);
|
|
455
|
-
} catch (error) {
|
|
456
|
-
handleCommandError(error, 'diff');
|
|
457
|
-
process.exit(1);
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
|
|
462
227
|
/**
|
|
463
228
|
* Sets up utility commands
|
|
464
229
|
* @param {Command} program - Commander program instance
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client token listing and proactive refresh for auth status.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Stored app tokens, auto-refresh when credentials exist
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const { successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
|
|
15
|
+
const {
|
|
16
|
+
getOrRefreshClientToken,
|
|
17
|
+
loadClientCredentials,
|
|
18
|
+
refreshClientToken,
|
|
19
|
+
isTokenExpired
|
|
20
|
+
} = require('../utils/token-manager');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} controllerUrl
|
|
24
|
+
* @param {string} storedController
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function controllersMatch(controllerUrl, storedController) {
|
|
28
|
+
if (!storedController) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return config.normalizeControllerUrl(storedController) === config.normalizeControllerUrl(controllerUrl);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} environment
|
|
36
|
+
* @param {string} controllerUrl
|
|
37
|
+
* @returns {Promise<Record<string, { controller?: string, token?: string, expiresAt?: string }>>}
|
|
38
|
+
*/
|
|
39
|
+
async function getClientEntriesForController(environment, controllerUrl) {
|
|
40
|
+
const configData = await config.getConfig();
|
|
41
|
+
const clients = configData.environments?.[environment]?.clients || {};
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const [appName, tokenData] of Object.entries(clients)) {
|
|
44
|
+
if (controllersMatch(controllerUrl, tokenData?.controller)) {
|
|
45
|
+
out[appName] = tokenData;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Refresh expired client tokens when secrets.local.yaml (or env) has client id/secret.
|
|
53
|
+
*
|
|
54
|
+
* @async
|
|
55
|
+
* @param {string} environment
|
|
56
|
+
* @param {string} controllerUrl
|
|
57
|
+
* @returns {Promise<number>} Count refreshed
|
|
58
|
+
*/
|
|
59
|
+
async function refreshExpiredClientTokensWherePossible(environment, controllerUrl) {
|
|
60
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
61
|
+
let refreshed = 0;
|
|
62
|
+
for (const appName of Object.keys(entries)) {
|
|
63
|
+
const tokenData = entries[appName];
|
|
64
|
+
if (!tokenData?.token || !isTokenExpired(tokenData.expiresAt)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const credentials = await loadClientCredentials(appName);
|
|
68
|
+
if (!credentials?.clientId || !credentials?.clientSecret) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await refreshClientToken(environment, appName, controllerUrl);
|
|
73
|
+
refreshed += 1;
|
|
74
|
+
} catch {
|
|
75
|
+
// Skip apps without valid credentials or controller errors
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return refreshed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} expiresAt
|
|
83
|
+
* @returns {'valid'|'expired'|'unknown'}
|
|
84
|
+
*/
|
|
85
|
+
function tokenExpiryState(expiresAt) {
|
|
86
|
+
if (!expiresAt) {
|
|
87
|
+
return 'unknown';
|
|
88
|
+
}
|
|
89
|
+
return isTokenExpired(expiresAt) ? 'expired' : 'valid';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build rows for stored client tokens table.
|
|
94
|
+
*
|
|
95
|
+
* @async
|
|
96
|
+
* @param {string} environment
|
|
97
|
+
* @param {string} controllerUrl
|
|
98
|
+
* @returns {Promise<Array<{ appName: string, expiresAt: string, state: string, logoutCmd: string }>>}
|
|
99
|
+
*/
|
|
100
|
+
async function listClientTokenRows(environment, controllerUrl) {
|
|
101
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
102
|
+
const rows = [];
|
|
103
|
+
for (const appName of Object.keys(entries).sort()) {
|
|
104
|
+
const tokenData = entries[appName];
|
|
105
|
+
const state = tokenExpiryState(tokenData.expiresAt);
|
|
106
|
+
const credentials = await loadClientCredentials(appName);
|
|
107
|
+
const canRefresh = !!(credentials?.clientId && credentials?.clientSecret);
|
|
108
|
+
let stateLabel = state;
|
|
109
|
+
if (state === 'expired' && canRefresh) {
|
|
110
|
+
stateLabel = 'expired (credentials available — refreshed on status if possible)';
|
|
111
|
+
} else if (state === 'expired' && !canRefresh) {
|
|
112
|
+
stateLabel = 'expired (no client id/secret in secrets — logout or login)';
|
|
113
|
+
}
|
|
114
|
+
rows.push({
|
|
115
|
+
appName,
|
|
116
|
+
expiresAt: tokenData.expiresAt || '—',
|
|
117
|
+
state: stateLabel,
|
|
118
|
+
logoutCmd: `aifabrix logout -e ${environment} -a ${appName}`
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return rows;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Print stored application client tokens and logout commands.
|
|
126
|
+
*
|
|
127
|
+
* @async
|
|
128
|
+
* @param {string} environment
|
|
129
|
+
* @param {string} controllerUrl
|
|
130
|
+
* @param {number} [refreshedCount]
|
|
131
|
+
* @returns {Promise<void>}
|
|
132
|
+
*/
|
|
133
|
+
async function displayStoredClientTokensSection(environment, controllerUrl, refreshedCount = 0) {
|
|
134
|
+
const rows = await listClientTokenRows(environment, controllerUrl);
|
|
135
|
+
if (rows.length === 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
logger.log('');
|
|
140
|
+
logger.log(chalk.bold('Stored application client tokens:'));
|
|
141
|
+
if (refreshedCount > 0) {
|
|
142
|
+
logger.log(
|
|
143
|
+
chalk.gray(
|
|
144
|
+
` Refreshed ${refreshedCount} expired token(s) using client id/secret from secrets.local.yaml or env.`
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
logger.log(chalk.gray(` Environment: ${environment} · Controller: ${controllerUrl}`));
|
|
149
|
+
logger.log('');
|
|
150
|
+
for (const row of rows) {
|
|
151
|
+
const icon = row.state.startsWith('valid') ? successGlyph() : failureGlyph();
|
|
152
|
+
logger.log(` ${icon} ${chalk.cyan(row.appName)}`);
|
|
153
|
+
logger.log(` Expires: ${chalk.gray(row.expiresAt)}`);
|
|
154
|
+
logger.log(` Status: ${row.state}`);
|
|
155
|
+
logger.log(` Clear: ${chalk.gray(row.logoutCmd)}`);
|
|
156
|
+
}
|
|
157
|
+
logger.log('');
|
|
158
|
+
logger.log(chalk.gray(' Clear all client tokens for this environment:'), chalk.gray(`aifabrix logout -e ${environment}`));
|
|
159
|
+
logger.log(chalk.gray(' Clear device login for this controller:'), chalk.gray(`aifabrix logout -c ${controllerUrl}`));
|
|
160
|
+
logger.log('');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Resolve a working client token (refresh when expired if credentials exist).
|
|
165
|
+
*
|
|
166
|
+
* @async
|
|
167
|
+
* @param {string} controllerUrl
|
|
168
|
+
* @param {string} environment
|
|
169
|
+
* @param {function(string, string, string, string, string): Promise<Object>} validateClientTokenFn
|
|
170
|
+
* @returns {Promise<Object|null>}
|
|
171
|
+
*/
|
|
172
|
+
async function tryResolveClientTokenAuth(controllerUrl, environment, validateClientTokenFn) {
|
|
173
|
+
const entries = await getClientEntriesForController(environment, controllerUrl);
|
|
174
|
+
const appNames = Object.keys(entries);
|
|
175
|
+
if (appNames.length === 0) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const ordered = ['dataplane', ...appNames.filter(a => a !== 'dataplane').sort()];
|
|
180
|
+
let lastFailure = null;
|
|
181
|
+
|
|
182
|
+
for (const appName of ordered) {
|
|
183
|
+
try {
|
|
184
|
+
const refreshed = await getOrRefreshClientToken(environment, appName, controllerUrl);
|
|
185
|
+
if (!refreshed?.token) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const stored = await config.getClientToken(environment, appName);
|
|
189
|
+
const result = await validateClientTokenFn(
|
|
190
|
+
refreshed.token,
|
|
191
|
+
controllerUrl,
|
|
192
|
+
environment,
|
|
193
|
+
appName,
|
|
194
|
+
stored?.expiresAt
|
|
195
|
+
);
|
|
196
|
+
if (result.authenticated) {
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
lastFailure = result;
|
|
200
|
+
} catch {
|
|
201
|
+
// Try next app
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return lastFailure;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = {
|
|
209
|
+
refreshExpiredClientTokensWherePossible,
|
|
210
|
+
displayStoredClientTokensSection,
|
|
211
|
+
tryResolveClientTokenAuth,
|
|
212
|
+
listClientTokenRows,
|
|
213
|
+
getClientEntriesForController
|
|
214
|
+
};
|
|
@@ -24,6 +24,28 @@ const {
|
|
|
24
24
|
} = require('../utils/dataplane-cli-version-gate');
|
|
25
25
|
const { isAtLeast, isValidSemver } = require('../utils/semver-compare');
|
|
26
26
|
|
|
27
|
+
/** Max wall time for post-login version cache refresh (silent after success banner). */
|
|
28
|
+
const LOGIN_POST_REFRESH_BUDGET_MS = 12000;
|
|
29
|
+
|
|
30
|
+
/** Per health probe during login refresh (two endpoints may be tried). */
|
|
31
|
+
const LOGIN_HEALTH_PROBE_TIMEOUT_MS = 6000;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve when `promise` settles or when `timeoutMs` elapses (returns undefined).
|
|
35
|
+
* @template T
|
|
36
|
+
* @param {Promise<T>} promise
|
|
37
|
+
* @param {number} timeoutMs
|
|
38
|
+
* @returns {Promise<T|undefined>}
|
|
39
|
+
*/
|
|
40
|
+
function raceWithTimeout(promise, timeoutMs) {
|
|
41
|
+
return Promise.race([
|
|
42
|
+
promise,
|
|
43
|
+
new Promise((resolve) => {
|
|
44
|
+
setTimeout(() => resolve(undefined), timeoutMs);
|
|
45
|
+
})
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
/**
|
|
28
50
|
* Per plan 142.0: invalid semver from the dataplane is logged but does **not**
|
|
29
51
|
* block the CLI; missing min version means no enforcement.
|
|
@@ -72,13 +94,17 @@ async function safeReadCache(controllerUrl) {
|
|
|
72
94
|
* compatible: boolean
|
|
73
95
|
* }>}
|
|
74
96
|
*/
|
|
75
|
-
async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl) {
|
|
97
|
+
async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, options = {}) {
|
|
76
98
|
const cliVersion = getInstalledCliVersion();
|
|
77
99
|
let dataplaneVersion;
|
|
78
100
|
let minBuilderCliVersion;
|
|
101
|
+
const healthOpts = {};
|
|
102
|
+
if (options.healthTimeoutMs !== undefined && options.healthTimeoutMs !== null) {
|
|
103
|
+
healthOpts.timeoutMs = options.healthTimeoutMs;
|
|
104
|
+
}
|
|
79
105
|
|
|
80
106
|
try {
|
|
81
|
-
const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl);
|
|
107
|
+
const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl, healthOpts);
|
|
82
108
|
if (snapshot) {
|
|
83
109
|
dataplaneVersion = snapshot.version;
|
|
84
110
|
minBuilderCliVersion = snapshot.minBuilderCliVersion;
|
|
@@ -195,29 +221,38 @@ async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authCon
|
|
|
195
221
|
* @param {string} [loginOptions.app] - App name (credentials login) for client-token auth
|
|
196
222
|
* @returns {Promise<void>}
|
|
197
223
|
*/
|
|
224
|
+
async function tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions = {}) {
|
|
225
|
+
const { resolveEnvironment } = require('../core/config');
|
|
226
|
+
const environment = await resolveEnvironment();
|
|
227
|
+
const authConfig = await resolveAuthConfigAfterLogin(
|
|
228
|
+
controllerUrl,
|
|
229
|
+
environment,
|
|
230
|
+
loginOptions
|
|
231
|
+
);
|
|
232
|
+
if (!authConfig) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const dataplaneUrl = await resolveDataplaneUrlAfterLogin(
|
|
236
|
+
controllerUrl,
|
|
237
|
+
environment,
|
|
238
|
+
authConfig
|
|
239
|
+
);
|
|
240
|
+
if (dataplaneUrl) {
|
|
241
|
+
await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, {
|
|
242
|
+
healthTimeoutMs: LOGIN_HEALTH_PROBE_TIMEOUT_MS
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
198
247
|
async function tryRefreshDataplaneVersionAfterLogin(controllerUrl, loginOptions = {}) {
|
|
199
248
|
if (!controllerUrl || typeof controllerUrl !== 'string') {
|
|
200
249
|
return;
|
|
201
250
|
}
|
|
202
251
|
try {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
controllerUrl,
|
|
207
|
-
environment,
|
|
208
|
-
loginOptions
|
|
209
|
-
);
|
|
210
|
-
if (!authConfig) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const dataplaneUrl = await resolveDataplaneUrlAfterLogin(
|
|
214
|
-
controllerUrl,
|
|
215
|
-
environment,
|
|
216
|
-
authConfig
|
|
252
|
+
await raceWithTimeout(
|
|
253
|
+
tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions),
|
|
254
|
+
LOGIN_POST_REFRESH_BUDGET_MS
|
|
217
255
|
);
|
|
218
|
-
if (dataplaneUrl) {
|
|
219
|
-
await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl);
|
|
220
|
-
}
|
|
221
256
|
} catch (_error) {
|
|
222
257
|
// best-effort only
|
|
223
258
|
}
|
|
@@ -227,5 +262,8 @@ module.exports = {
|
|
|
227
262
|
refreshDataplaneVersionInfo,
|
|
228
263
|
loadCachedVersionInfo,
|
|
229
264
|
computeCompatibility,
|
|
230
|
-
tryRefreshDataplaneVersionAfterLogin
|
|
265
|
+
tryRefreshDataplaneVersionAfterLogin,
|
|
266
|
+
LOGIN_POST_REFRESH_BUDGET_MS,
|
|
267
|
+
LOGIN_HEALTH_PROBE_TIMEOUT_MS,
|
|
268
|
+
raceWithTimeout
|
|
231
269
|
};
|