@aifabrix/builder 2.46.0 → 2.47.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/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/jest.isolated-projects.js +18 -2
- package/jest.projects.js +24 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/lifecycle.api.js +22 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/down.js +58 -26
- package/lib/app/index.js +2 -2
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +17 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +16 -4
- package/lib/cli/setup-app.js +54 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +4 -1
- package/lib/cli/setup-utility-resolve.js +8 -5
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-dataplane-version.js +97 -61
- package/lib/commands/auth-status.js +2 -7
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource.js +1 -1
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/repair-auth-apply.js +30 -4
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource.js +53 -18
- package/lib/commands/repair-env-template.js +12 -12
- package/lib/commands/repair-openapi-sync.js +26 -4
- package/lib/commands/repair-steps.js +28 -13
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/setup-modes.js +1 -1
- package/lib/commands/setup-prompts-platform-mode.js +24 -21
- package/lib/commands/test-trust-external.js +39 -90
- package/lib/commands/up-common.js +33 -1
- package/lib/commands/up-dataplane.js +8 -4
- package/lib/commands/upload.js +24 -3
- package/lib/commands/verify-trust-command-action.js +4 -1
- package/lib/commands/verify-trust-external.js +12 -20
- package/lib/core/config-setup-platform-mode.js +3 -3
- package/lib/core/setup-platform-mode.js +41 -6
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/validate.js +41 -23
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +61 -122
- package/lib/external-system/generator.js +1 -59
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +5 -2
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/lifecycle/certification-progress.js +2 -2
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +14 -13
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +16 -0
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +372 -58
- package/lib/schema/external-system.schema.json +331 -2
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-utils.js +4 -0
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +9 -1
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +52 -262
- package/lib/utils/dataplane-resolver.js +4 -115
- package/lib/utils/declarative-url-ports.js +24 -1
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/external-env-template.js +10 -3
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/help-builder.js +2 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +2 -1
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +5 -0
- package/templates/applications/dataplane/env.template +26 -13
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +37 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/python/docker-compose.hbs +56 -0
- package/templates/typescript/docker-compose.hbs +52 -0
|
@@ -60,8 +60,8 @@ async function runResolveJsonOutput(appName, appPath, envOnly, envPath, options,
|
|
|
60
60
|
*/
|
|
61
61
|
async function runResolveHumanOutput(appName, envPath, envOnly, options, environment) {
|
|
62
62
|
logger.log(`✔ Generated .env file: ${envPath}`);
|
|
63
|
-
if (options.fresh) {
|
|
64
|
-
logger.log(chalk.gray(' Wrote full resolved content (
|
|
63
|
+
if (options.fresh || options.force) {
|
|
64
|
+
logger.log(chalk.gray(' Wrote full resolved content (replaced existing .env from env.template; no merge).'));
|
|
65
65
|
}
|
|
66
66
|
if (environment === 'local') {
|
|
67
67
|
logger.log(chalk.gray(' Resolved for local use (host ports; no Docker port rewrite).'));
|
|
@@ -130,7 +130,7 @@ async function runResolveAppAction(appName, options) {
|
|
|
130
130
|
skipOutputPath: false,
|
|
131
131
|
preserveFromPath: null,
|
|
132
132
|
preferLocalEnvOutputPath,
|
|
133
|
-
freshEnv: options.fresh === true
|
|
133
|
+
freshEnv: options.fresh === true || options.force === true
|
|
134
134
|
}
|
|
135
135
|
);
|
|
136
136
|
if (jsonMode) {
|
|
@@ -148,10 +148,13 @@ function setupResolveCommand(program) {
|
|
|
148
148
|
program.command('resolve <appKey|systemKey>')
|
|
149
149
|
.description('Generate .env from template (builder/<appKey>/ or integration/<systemKey>/); optional validate after')
|
|
150
150
|
.addHelpText('after', RESOLVE_HELP_AFTER)
|
|
151
|
-
.option(
|
|
151
|
+
.option(
|
|
152
|
+
'-f, --force',
|
|
153
|
+
'Seed missing secret keys and replace .env from env.template (no merge with existing .env or envOutputPath)'
|
|
154
|
+
)
|
|
152
155
|
.option(
|
|
153
156
|
'--fresh',
|
|
154
|
-
'
|
|
157
|
+
'Same full .env replace as --force without seeding missing secret keys in the secrets file'
|
|
155
158
|
)
|
|
156
159
|
.option('--skip-validation', 'Skip file validation after generating .env')
|
|
157
160
|
.option('--json', 'Print JSON summary (envPath, manifestSource, optional validation)')
|
package/lib/commands/app-logs.js
CHANGED
|
@@ -13,6 +13,10 @@ const logger = require('../utils/logger');
|
|
|
13
13
|
const config = require('../core/config');
|
|
14
14
|
const containerHelpers = require('../utils/app-run-containers');
|
|
15
15
|
const { validateAppName } = require('../app/push');
|
|
16
|
+
const {
|
|
17
|
+
getCeleryCompanionAppKeys,
|
|
18
|
+
loadBuilderApplicationConfig
|
|
19
|
+
} = require('../utils/celery-companion-apps');
|
|
16
20
|
|
|
17
21
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
18
22
|
const { maskEnvLine } = require('../utils/log-redaction');
|
|
@@ -295,6 +299,36 @@ async function runDockerLogsFollow(containerName, tail, minLevel) {
|
|
|
295
299
|
});
|
|
296
300
|
}
|
|
297
301
|
|
|
302
|
+
function parseLogLevelOption(options) {
|
|
303
|
+
const rawLevel =
|
|
304
|
+
options.level !== undefined && options.level !== null && options.level !== ''
|
|
305
|
+
? String(options.level).trim()
|
|
306
|
+
: undefined;
|
|
307
|
+
const level = rawLevel ? rawLevel.toLowerCase() : undefined;
|
|
308
|
+
if (level !== undefined && level !== null && !LOG_LEVELS.includes(level)) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Invalid log level '${rawLevel}'; use one of: ${LOG_LEVELS.join(', ')}`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return level;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function logFollowStream(companionKeys, containerName, tail, level) {
|
|
317
|
+
await runDockerLogsFollow(containerName, tail, level);
|
|
318
|
+
if (companionKeys.length > 0) {
|
|
319
|
+
const hint = companionKeys.map((key) => `aifabrix logs ${key}`).join(' or ');
|
|
320
|
+
logger.log(chalk.gray(`Tip: sidecar logs — ${hint}`));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function logContainerSnapshot(containerName, tail, level) {
|
|
325
|
+
try {
|
|
326
|
+
await runDockerLogs(containerName, { tail, level });
|
|
327
|
+
} catch (err) {
|
|
328
|
+
logger.log(chalk.yellow(`No logs for ${containerName}: ${err.message}`));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
298
332
|
/**
|
|
299
333
|
* Run app logs command: optional env dump (masked), then docker logs
|
|
300
334
|
* @async
|
|
@@ -307,39 +341,33 @@ async function runDockerLogsFollow(containerName, tail, minLevel) {
|
|
|
307
341
|
*/
|
|
308
342
|
async function runAppLogs(appKey, options = {}) {
|
|
309
343
|
validateAppName(appKey);
|
|
310
|
-
const
|
|
311
|
-
options.level !== undefined && options.level !== null && options.level !== ''
|
|
312
|
-
? String(options.level).trim()
|
|
313
|
-
: undefined;
|
|
314
|
-
const level = rawLevel ? rawLevel.toLowerCase() : undefined;
|
|
315
|
-
if (level !== undefined && level !== null && !LOG_LEVELS.includes(level)) {
|
|
316
|
-
throw new Error(
|
|
317
|
-
`Invalid log level '${rawLevel}'; use one of: ${LOG_LEVELS.join(', ')}`
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
|
|
344
|
+
const level = parseLogLevelOption(options);
|
|
321
345
|
const developerId = await config.getDeveloperId();
|
|
322
|
-
const containerName = containerHelpers.getContainerName(appKey, developerId);
|
|
323
|
-
|
|
324
346
|
const follow = !!options.follow;
|
|
325
347
|
const tail = typeof options.tail === 'number' ? options.tail : DEFAULT_TAIL_LINES;
|
|
348
|
+
const appConfig = loadBuilderApplicationConfig(appKey);
|
|
349
|
+
const companionKeys = getCeleryCompanionAppKeys(appKey, appConfig);
|
|
350
|
+
const appKeysToLog = follow ? [appKey] : [appKey, ...companionKeys];
|
|
326
351
|
|
|
327
|
-
|
|
352
|
+
for (const key of appKeysToLog) {
|
|
353
|
+
const containerName = containerHelpers.getContainerName(key, developerId);
|
|
328
354
|
|
|
329
|
-
|
|
330
|
-
await dumpMaskedEnv(containerName);
|
|
331
|
-
}
|
|
355
|
+
logger.log(chalk.blue(`Container: ${containerName}\n`));
|
|
332
356
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
357
|
+
if (!follow) {
|
|
358
|
+
await dumpMaskedEnv(containerName);
|
|
359
|
+
}
|
|
337
360
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
361
|
+
if (follow) {
|
|
362
|
+
await logFollowStream(companionKeys, containerName, tail, level);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
await logContainerSnapshot(containerName, tail, level);
|
|
367
|
+
|
|
368
|
+
if (key !== appKeysToLog[appKeysToLog.length - 1]) {
|
|
369
|
+
logger.log('');
|
|
370
|
+
}
|
|
343
371
|
}
|
|
344
372
|
}
|
|
345
373
|
|
package/lib/commands/app.js
CHANGED
|
@@ -17,6 +17,8 @@ const { registerApplication } = require('../app/register');
|
|
|
17
17
|
const { rotateSecret } = require('../app/rotate-secret');
|
|
18
18
|
const { showApp } = require('../app/show');
|
|
19
19
|
const { runAppDeploymentList } = require('./deployment-list');
|
|
20
|
+
const { isChannelTargetKey } = require('../channels/channel-targets');
|
|
21
|
+
const { addChannelCommand } = require('../channels/add-channel');
|
|
20
22
|
|
|
21
23
|
const APP_HELP_AFTER = `
|
|
22
24
|
Subcommands:
|
|
@@ -25,6 +27,7 @@ Subcommands:
|
|
|
25
27
|
rotate-secret <key> New ClientSecret (shown once)
|
|
26
28
|
show <key> From controller (like show --online)
|
|
27
29
|
deployment <key> Deployment history for app
|
|
30
|
+
add-channel <target> Configure assistant channel install (after deploy)
|
|
28
31
|
|
|
29
32
|
Requires: aifabrix login
|
|
30
33
|
`;
|
|
@@ -118,6 +121,43 @@ function setupAppDeploymentCommand(app) {
|
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
|
|
124
|
+
function setupAppAddChannelCommand(app) {
|
|
125
|
+
app.command('add-channel <channelTarget>')
|
|
126
|
+
.description(
|
|
127
|
+
'Configure assistant channel install (teams-bot, slack, …) after aifabrix deploy <target>'
|
|
128
|
+
)
|
|
129
|
+
.requiredOption('-n, --name <name>', 'Customer-visible channel name')
|
|
130
|
+
.option('-d, --description <text>', 'Channel description (default generated)')
|
|
131
|
+
.option('--icon <path>', 'Path to channel icon asset')
|
|
132
|
+
.option('--template <name>', 'Template override (default from channel target)')
|
|
133
|
+
.option('--dry-run', 'Generate artifacts only; do not patch external system or credentials')
|
|
134
|
+
.option('--force', 'Regenerate artifacts and patch config (does not rotate secrets)')
|
|
135
|
+
.option('--probe', 'After install, validate callbacks and assistant catalog')
|
|
136
|
+
.action(async(channelTarget, options) => {
|
|
137
|
+
try {
|
|
138
|
+
if (!isChannelTargetKey(channelTarget)) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Unknown channel target "${channelTarget}". Use teams-bot, slack, microsoft-copilot, or chatgpt.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
await addChannelCommand(channelTarget, {
|
|
144
|
+
name: options.name,
|
|
145
|
+
description: options.description,
|
|
146
|
+
icon: options.icon,
|
|
147
|
+
template: options.template,
|
|
148
|
+
dryRun: options.dryRun === true,
|
|
149
|
+
force: options.force === true,
|
|
150
|
+
probe: options.probe === true
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (!error.message?.includes('Authentication required')) {
|
|
154
|
+
logger.error(formatBlockingError('Channel install failed:'), error.message);
|
|
155
|
+
}
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
121
161
|
/**
|
|
122
162
|
* Setup application management commands
|
|
123
163
|
* @param {Command} program - Commander program instance
|
|
@@ -129,6 +169,7 @@ function setupAppCommands(program) {
|
|
|
129
169
|
setupAppRotateSecretCommand(app);
|
|
130
170
|
setupAppShowCommand(app);
|
|
131
171
|
setupAppDeploymentCommand(app);
|
|
172
|
+
setupAppAddChannelCommand(app);
|
|
132
173
|
}
|
|
133
174
|
|
|
134
175
|
module.exports = { setupAppCommands };
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
const { fetchDataplaneGeneralHealth } = require('../api/dataplane-health.api');
|
|
18
18
|
const {
|
|
19
19
|
updateDeviceDataplaneVersions,
|
|
20
|
-
getDeviceDataplaneVersions
|
|
20
|
+
getDeviceDataplaneVersions,
|
|
21
|
+
isCacheStale
|
|
21
22
|
} = require('../core/config-device-dataplane');
|
|
22
23
|
const {
|
|
23
24
|
getInstalledCliVersion
|
|
@@ -25,10 +26,10 @@ const {
|
|
|
25
26
|
const { isAtLeast, isValidSemver } = require('../utils/semver-compare');
|
|
26
27
|
|
|
27
28
|
/** Max wall time for post-login version cache refresh (silent after success banner). */
|
|
28
|
-
const LOGIN_POST_REFRESH_BUDGET_MS =
|
|
29
|
+
const LOGIN_POST_REFRESH_BUDGET_MS = 8000;
|
|
29
30
|
|
|
30
|
-
/** Per health probe during login refresh (
|
|
31
|
-
const LOGIN_HEALTH_PROBE_TIMEOUT_MS =
|
|
31
|
+
/** Per health probe during login refresh (legacy /health first, then v1). */
|
|
32
|
+
const LOGIN_HEALTH_PROBE_TIMEOUT_MS = 3000;
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Resolve when `promise` settles or when `timeoutMs` elapses (returns undefined).
|
|
@@ -79,29 +80,32 @@ async function safeReadCache(controllerUrl) {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* still shows the last-known values offline.
|
|
86
|
-
*
|
|
87
|
-
* @async
|
|
88
|
-
* @param {string} controllerUrl - Controller URL (cache key)
|
|
89
|
-
* @param {string} dataplaneUrl - Dataplane base URL for the health probe
|
|
90
|
-
* @returns {Promise<{
|
|
91
|
-
* dataplaneVersion: string|undefined,
|
|
92
|
-
* minBuilderCliVersion: string|undefined,
|
|
93
|
-
* cliVersion: string,
|
|
94
|
-
* compatible: boolean
|
|
95
|
-
* }>}
|
|
83
|
+
* Build optional fetch args for {@link fetchDataplaneGeneralHealth}.
|
|
84
|
+
* @param {Object} [options]
|
|
85
|
+
* @returns {Object}
|
|
96
86
|
*/
|
|
97
|
-
|
|
98
|
-
const cliVersion = getInstalledCliVersion();
|
|
99
|
-
let dataplaneVersion;
|
|
100
|
-
let minBuilderCliVersion;
|
|
87
|
+
function buildHealthFetchOptions(options = {}) {
|
|
101
88
|
const healthOpts = {};
|
|
102
89
|
if (options.healthTimeoutMs !== undefined && options.healthTimeoutMs !== null) {
|
|
103
90
|
healthOpts.timeoutMs = options.healthTimeoutMs;
|
|
104
91
|
}
|
|
92
|
+
if (options.endpointOrder) {
|
|
93
|
+
healthOpts.endpointOrder = options.endpointOrder;
|
|
94
|
+
}
|
|
95
|
+
return healthOpts;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Probe dataplane health and persist version fields when available.
|
|
100
|
+
* @async
|
|
101
|
+
* @param {string} controllerUrl
|
|
102
|
+
* @param {string} dataplaneUrl
|
|
103
|
+
* @param {Object} healthOpts
|
|
104
|
+
* @returns {Promise<{ dataplaneVersion: string|undefined, minBuilderCliVersion: string|undefined }>}
|
|
105
|
+
*/
|
|
106
|
+
async function fetchAndPersistHealthSnapshot(controllerUrl, dataplaneUrl, healthOpts) {
|
|
107
|
+
let dataplaneVersion;
|
|
108
|
+
let minBuilderCliVersion;
|
|
105
109
|
|
|
106
110
|
try {
|
|
107
111
|
const snapshot = await fetchDataplaneGeneralHealth(dataplaneUrl, healthOpts);
|
|
@@ -121,14 +125,63 @@ async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, options
|
|
|
121
125
|
// ignore — fall through to cached values
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Fill missing version fields from the device cache when live probe failed.
|
|
133
|
+
* @async
|
|
134
|
+
* @param {string} controllerUrl
|
|
135
|
+
* @param {string|undefined} dataplaneVersion
|
|
136
|
+
* @param {string|undefined} minBuilderCliVersion
|
|
137
|
+
* @returns {Promise<{ dataplaneVersion: string|undefined, minBuilderCliVersion: string|undefined }>}
|
|
138
|
+
*/
|
|
139
|
+
async function resolveVersionFieldsFromCache(controllerUrl, dataplaneVersion, minBuilderCliVersion) {
|
|
140
|
+
if (dataplaneVersion || minBuilderCliVersion) {
|
|
141
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
130
142
|
}
|
|
131
143
|
|
|
144
|
+
const cached = await safeReadCache(controllerUrl);
|
|
145
|
+
if (!cached) {
|
|
146
|
+
return { dataplaneVersion, minBuilderCliVersion };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
dataplaneVersion: cached.version,
|
|
151
|
+
minBuilderCliVersion: cached.minBuilderCliVersion
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Refresh dataplane health, persist `device.<controllerUrl>` version cache,
|
|
157
|
+
* and derive the compatibility verdict against the installed Builder CLI.
|
|
158
|
+
* Falls back to the cached entry when the live probe fails so `auth status`
|
|
159
|
+
* still shows the last-known values offline.
|
|
160
|
+
*
|
|
161
|
+
* @async
|
|
162
|
+
* @param {string} controllerUrl - Controller URL (cache key)
|
|
163
|
+
* @param {string} dataplaneUrl - Dataplane base URL for the health probe
|
|
164
|
+
* @returns {Promise<{
|
|
165
|
+
* dataplaneVersion: string|undefined,
|
|
166
|
+
* minBuilderCliVersion: string|undefined,
|
|
167
|
+
* cliVersion: string,
|
|
168
|
+
* compatible: boolean
|
|
169
|
+
* }>}
|
|
170
|
+
*/
|
|
171
|
+
async function refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, options = {}) {
|
|
172
|
+
const cliVersion = getInstalledCliVersion();
|
|
173
|
+
const healthOpts = buildHealthFetchOptions(options);
|
|
174
|
+
let { dataplaneVersion, minBuilderCliVersion } = await fetchAndPersistHealthSnapshot(
|
|
175
|
+
controllerUrl,
|
|
176
|
+
dataplaneUrl,
|
|
177
|
+
healthOpts
|
|
178
|
+
);
|
|
179
|
+
({ dataplaneVersion, minBuilderCliVersion } = await resolveVersionFieldsFromCache(
|
|
180
|
+
controllerUrl,
|
|
181
|
+
dataplaneVersion,
|
|
182
|
+
minBuilderCliVersion
|
|
183
|
+
));
|
|
184
|
+
|
|
132
185
|
const compatible = computeCompatibility(cliVersion, minBuilderCliVersion);
|
|
133
186
|
return { dataplaneVersion, minBuilderCliVersion, cliVersion, compatible };
|
|
134
187
|
}
|
|
@@ -184,33 +237,6 @@ async function resolveAuthConfigAfterLogin(controllerUrl, environment, loginOpti
|
|
|
184
237
|
}
|
|
185
238
|
}
|
|
186
239
|
|
|
187
|
-
/**
|
|
188
|
-
* Resolve dataplane URL after login (mirrors auth-status silent resolver).
|
|
189
|
-
* @private
|
|
190
|
-
* @async
|
|
191
|
-
* @param {string} controllerUrl
|
|
192
|
-
* @param {string} environment
|
|
193
|
-
* @param {Object} authConfig
|
|
194
|
-
* @returns {Promise<string|null>}
|
|
195
|
-
*/
|
|
196
|
-
async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authConfig) {
|
|
197
|
-
const { findDataplaneServiceAppKey } = require('./wizard-dataplane');
|
|
198
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
199
|
-
try {
|
|
200
|
-
const dataplaneAppKey = await findDataplaneServiceAppKey(
|
|
201
|
-
controllerUrl,
|
|
202
|
-
environment,
|
|
203
|
-
authConfig
|
|
204
|
-
);
|
|
205
|
-
if (dataplaneAppKey) {
|
|
206
|
-
return await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
|
|
207
|
-
}
|
|
208
|
-
return await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
|
|
209
|
-
} catch (_resolveError) {
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
240
|
/**
|
|
215
241
|
* Best-effort health refresh after login when dataplane URL is discoverable.
|
|
216
242
|
* Never throws — cache failures must not block login success.
|
|
@@ -222,7 +248,13 @@ async function resolveDataplaneUrlAfterLogin(controllerUrl, environment, authCon
|
|
|
222
248
|
* @returns {Promise<void>}
|
|
223
249
|
*/
|
|
224
250
|
async function tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOptions = {}) {
|
|
251
|
+
const cached = await safeReadCache(controllerUrl);
|
|
252
|
+
if (cached && !isCacheStale(cached)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
225
256
|
const { resolveEnvironment } = require('../core/config');
|
|
257
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
226
258
|
const environment = await resolveEnvironment();
|
|
227
259
|
const authConfig = await resolveAuthConfigAfterLogin(
|
|
228
260
|
controllerUrl,
|
|
@@ -232,14 +264,18 @@ async function tryRefreshDataplaneVersionAfterLoginWork(controllerUrl, loginOpti
|
|
|
232
264
|
if (!authConfig) {
|
|
233
265
|
return;
|
|
234
266
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
environment,
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
let dataplaneUrl = null;
|
|
268
|
+
try {
|
|
269
|
+
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
|
|
270
|
+
silent: true
|
|
271
|
+
});
|
|
272
|
+
} catch (_resolveError) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
240
275
|
if (dataplaneUrl) {
|
|
241
276
|
await refreshDataplaneVersionInfo(controllerUrl, dataplaneUrl, {
|
|
242
|
-
healthTimeoutMs: LOGIN_HEALTH_PROBE_TIMEOUT_MS
|
|
277
|
+
healthTimeoutMs: LOGIN_HEALTH_PROBE_TIMEOUT_MS,
|
|
278
|
+
endpointOrder: 'legacy-first'
|
|
243
279
|
});
|
|
244
280
|
}
|
|
245
281
|
}
|
|
@@ -14,8 +14,7 @@ const logger = require('../utils/logger');
|
|
|
14
14
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
15
15
|
const { getAuthUser } = require('../api/auth.api');
|
|
16
16
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
17
|
-
const {
|
|
18
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
17
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
19
18
|
const { checkDataplaneHealth } = require('../utils/dataplane-health');
|
|
20
19
|
const {
|
|
21
20
|
displayDataplaneVersionSection,
|
|
@@ -211,11 +210,7 @@ function displayTokenInfo(tokenInfo) {
|
|
|
211
210
|
*/
|
|
212
211
|
async function resolveDataplaneUrlSilent(controllerUrl, environment, authConfig) {
|
|
213
212
|
try {
|
|
214
|
-
|
|
215
|
-
if (dataplaneAppKey) {
|
|
216
|
-
return await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
|
|
217
|
-
}
|
|
218
|
-
return await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
|
|
213
|
+
return await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent: true });
|
|
219
214
|
} catch {
|
|
220
215
|
return null;
|
|
221
216
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel CLI commands (plan 152).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Scaffold communication external systems for channel deploy step 1
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const logger = require('../utils/logger');
|
|
13
|
+
const { formatSuccessLine, formatBlockingError, sectionTitle, metadata } = require('../utils/cli-test-layout-chalk');
|
|
14
|
+
const { CHANNEL_TARGET_KEYS, isChannelTargetKey } = require('../channels/channel-targets');
|
|
15
|
+
const { scaffoldChannelIntegration } = require('../channels/channel-integration-generator');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('commander').Command} program
|
|
19
|
+
*/
|
|
20
|
+
function setupChannelCommands(program) {
|
|
21
|
+
const channel = program
|
|
22
|
+
.command('channel')
|
|
23
|
+
.description('Assistant channel integration scaffolds (plan 152)');
|
|
24
|
+
|
|
25
|
+
channel
|
|
26
|
+
.command('scaffold <channelTarget>')
|
|
27
|
+
.description('Generate integration/<target>/ communication external system for deploy step 1')
|
|
28
|
+
.option('--force', 'Overwrite generated scaffold files when folder exists')
|
|
29
|
+
.option('--allow-experimental', 'Allow web-embed scaffold')
|
|
30
|
+
.action(async(channelTarget, options) => {
|
|
31
|
+
try {
|
|
32
|
+
if (!isChannelTargetKey(channelTarget)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Unknown channel target "${channelTarget}". Expected: ${CHANNEL_TARGET_KEYS.join(', ')}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const result = scaffoldChannelIntegration(channelTarget, {
|
|
38
|
+
force: options.force === true,
|
|
39
|
+
allowExperimental: options.allowExperimental === true
|
|
40
|
+
});
|
|
41
|
+
logger.log(sectionTitle(`${channelTarget} channel scaffold`));
|
|
42
|
+
logger.log('');
|
|
43
|
+
logger.log(formatSuccessLine(`Created integration at ${result.integrationDir}`));
|
|
44
|
+
for (const file of result.files) {
|
|
45
|
+
logger.log(metadata(file));
|
|
46
|
+
}
|
|
47
|
+
logger.log('');
|
|
48
|
+
logger.log(chalk.white('Next steps:'));
|
|
49
|
+
logger.log(chalk.gray(` aifabrix deploy ${channelTarget}`));
|
|
50
|
+
logger.log(chalk.gray(` aifabrix app add-channel ${channelTarget} --name "Your Assistant"`));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error(formatBlockingError(error.message || String(error)));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { setupChannelCommands };
|
|
@@ -97,7 +97,7 @@ Omit [prefix] to list all datasources for the active environment.
|
|
|
97
97
|
|
|
98
98
|
function setupDatasourceValidateCommand(datasource) {
|
|
99
99
|
datasource.command('validate <file-or-key>')
|
|
100
|
-
.description('Validate datasource JSON (file path or datasource key under integration/<app>/)')
|
|
100
|
+
.description('Validate datasource YAML/JSON (file path or datasource key under integration/<app>/)')
|
|
101
101
|
.addHelpText('after', DATASOURCE_VALIDATE_HELP_AFTER)
|
|
102
102
|
.action(async(fileOrKey) => {
|
|
103
103
|
try {
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const logger = require('../utils/logger');
|
|
10
|
-
const {
|
|
10
|
+
const { formatSuccessLine, headerKeyValue } = require('../utils/cli-layout-chalk');
|
|
11
11
|
const {
|
|
12
12
|
resolveControllerAndAuth,
|
|
13
13
|
resolveEnvKey,
|
|
14
14
|
requireNonEmpty,
|
|
15
15
|
unwrapControllerData,
|
|
16
16
|
throwIfApiFailed,
|
|
17
|
-
assertSyncStatsOk
|
|
17
|
+
assertSyncStatsOk,
|
|
18
|
+
logIdentityCliError
|
|
18
19
|
} = require('./identity-shared');
|
|
19
20
|
const { clearAuthCache } = require('../api/auth-cache.api');
|
|
20
21
|
const { fullSyncToDataplane } = require('../api/dataplane-sync.api');
|
|
@@ -70,7 +71,7 @@ function setupIdentityApplyCommand(identityCmd) {
|
|
|
70
71
|
);
|
|
71
72
|
}
|
|
72
73
|
} catch (e) {
|
|
73
|
-
|
|
74
|
+
logIdentityCliError(e.message);
|
|
74
75
|
process.exit(1);
|
|
75
76
|
}
|
|
76
77
|
});
|
|
@@ -103,6 +104,46 @@ async function maybePurgeCache(controllerUrl, authConfig, purgeCache) {
|
|
|
103
104
|
logger.log(formatSuccessLine('Controller cache cleared'));
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* @param {string} controllerUrl
|
|
109
|
+
* @param {string} envKey
|
|
110
|
+
* @param {Object} authConfig
|
|
111
|
+
* @returns {Promise<string>}
|
|
112
|
+
*/
|
|
113
|
+
async function resolveSyncDataplaneUrl(controllerUrl, envKey, authConfig) {
|
|
114
|
+
try {
|
|
115
|
+
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
116
|
+
return await resolveDataplaneUrl(controllerUrl, envKey, authConfig, { silent: true });
|
|
117
|
+
} catch {
|
|
118
|
+
return '—';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @param {Object} params
|
|
124
|
+
* @param {string} params.controllerUrl
|
|
125
|
+
* @param {string} params.envKey
|
|
126
|
+
* @param {Object} params.authConfig
|
|
127
|
+
* @param {Object|null|undefined} params.stats
|
|
128
|
+
*/
|
|
129
|
+
async function printIdentitySyncSuccess({ controllerUrl, envKey, authConfig, stats }) {
|
|
130
|
+
const dataplaneUrl = await resolveSyncDataplaneUrl(controllerUrl, envKey, authConfig);
|
|
131
|
+
const usersProcessed = Number(stats?.usersProcessed ?? 0);
|
|
132
|
+
const groupsProcessed = Number(stats?.groupsProcessed ?? 0);
|
|
133
|
+
const syncErrors = Number(stats?.errors ?? 0);
|
|
134
|
+
|
|
135
|
+
logger.log('');
|
|
136
|
+
logger.log(formatSuccessLine(`Identity synced to dataplane (${envKey})`));
|
|
137
|
+
logger.log(headerKeyValue(' Controller:', controllerUrl));
|
|
138
|
+
logger.log(headerKeyValue(' Dataplane:', dataplaneUrl));
|
|
139
|
+
logger.log('');
|
|
140
|
+
logger.log(` Users: ${usersProcessed} processed`);
|
|
141
|
+
logger.log(` Groups: ${groupsProcessed} processed`);
|
|
142
|
+
if (syncErrors > 0) {
|
|
143
|
+
logger.log(` Errors: ${syncErrors}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
106
147
|
async function runIdentitySyncAction(options) {
|
|
107
148
|
try {
|
|
108
149
|
const { controllerUrl, authConfig } = await resolveControllerAndAuth();
|
|
@@ -116,16 +157,16 @@ async function runIdentitySyncAction(options) {
|
|
|
116
157
|
printJson(stats);
|
|
117
158
|
return;
|
|
118
159
|
}
|
|
119
|
-
|
|
120
|
-
logger.log(` usersProcessed=${stats?.usersProcessed ?? 0}`);
|
|
121
|
-
logger.log(` groupsProcessed=${stats?.groupsProcessed ?? 0}`);
|
|
160
|
+
await printIdentitySyncSuccess({ controllerUrl, envKey, authConfig, stats });
|
|
122
161
|
} catch (e) {
|
|
123
|
-
|
|
162
|
+
logIdentityCliError(e.message);
|
|
124
163
|
process.exit(1);
|
|
125
164
|
}
|
|
126
165
|
}
|
|
127
166
|
|
|
128
167
|
module.exports = {
|
|
129
168
|
setupIdentityApplyCommand,
|
|
130
|
-
setupIdentitySyncCommand
|
|
169
|
+
setupIdentitySyncCommand,
|
|
170
|
+
printIdentitySyncSuccess,
|
|
171
|
+
resolveSyncDataplaneUrl
|
|
131
172
|
};
|