@aifabrix/builder 2.44.6 → 2.45.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 +7 -3
- package/jest.projects.js +56 -0
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +52 -21
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-helpers.js +15 -8
- package/lib/app/run.js +57 -9
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +9 -5
- package/lib/cli/infra-guided.js +42 -27
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +11 -1
- package/lib/cli/setup-auth.js +94 -49
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +60 -119
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +65 -51
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/setup-modes.js +19 -6
- package/lib/commands/setup-prompts.js +41 -8
- package/lib/commands/setup.js +114 -9
- package/lib/commands/teardown.js +54 -5
- package/lib/commands/up-common.js +48 -14
- package/lib/commands/up-dataplane.js +21 -18
- package/lib/commands/up-miso.js +12 -8
- package/lib/commands/upload.js +5 -3
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/secrets-ensure-infra.js +1 -1
- package/lib/core/secrets-env-content.js +86 -90
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +2 -0
- package/lib/core/secrets-load.js +106 -102
- package/lib/external-system/deploy.js +5 -1
- package/lib/internal/node-fs.js +2 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/infra.parameter.yaml +10 -0
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +1 -1
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/health-check-url.js +18 -15
- package/lib/utils/health-check.js +7 -5
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/paths.js +238 -89
- package/lib/utils/remote-secrets-loader.js +7 -1
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-path.js +3 -4
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +10 -2
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -393
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +21 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +161 -90
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +4 -0
- package/templates/applications/miso-controller/application.yaml +2 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/.npmrc.token +0 -1
package/lib/app/show.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const logger = require('../utils/logger');
|
|
17
|
-
const { detectAppType, resolveApplicationConfigPath } = require('../utils/paths');
|
|
17
|
+
const { detectAppType, resolveApplicationConfigPath, getBuilderPath } = require('../utils/paths');
|
|
18
18
|
const { loadConfigFile } = require('../utils/config-format');
|
|
19
19
|
const generator = require('../generator');
|
|
20
20
|
const { getConfig, normalizeControllerUrl } = require('../core/config');
|
|
@@ -38,6 +38,58 @@ const {
|
|
|
38
38
|
sanitizeCertificationForJson
|
|
39
39
|
} = require('./certification-show-enrich');
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Attach `runReloadDefault` / `runProxyDefault` from ~/.aifabrix `applications.<appKey>`.
|
|
43
|
+
* @param {Object} summary
|
|
44
|
+
* @param {string} appKey
|
|
45
|
+
* @param {Object} userCfg
|
|
46
|
+
*/
|
|
47
|
+
function attachRunDefaultsFromUserConfig(summary, appKey, userCfg) {
|
|
48
|
+
const { isApplicationsReloadDefaultOn, getApplicationsRunProxyHint } = require('../utils/applications-config-defaults');
|
|
49
|
+
if (isApplicationsReloadDefaultOn(userCfg, appKey)) {
|
|
50
|
+
summary.runReloadDefault = true;
|
|
51
|
+
}
|
|
52
|
+
summary.runProxyDefault = getApplicationsRunProxyHint(userCfg, appKey);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Overlay `application.url` / `application.internalUrl` from declarative url:// rules (same profile as run `.env`, default `docker`).
|
|
57
|
+
* Used for offline and **online** show so controller metadata does not stale local proxy/TLS hints.
|
|
58
|
+
* @param {Object} summary
|
|
59
|
+
* @param {string} appKey
|
|
60
|
+
*/
|
|
61
|
+
async function attachDeclarativeUrlsToShowApplication(summary, appKey) {
|
|
62
|
+
if (!summary || summary.isExternal) return;
|
|
63
|
+
const t = summary.application && summary.application.type;
|
|
64
|
+
if (String(t || 'webapp').toLowerCase() === 'external') return;
|
|
65
|
+
try {
|
|
66
|
+
const { resolveDeclarativeShowUrlsForApp } = require('../core/secrets-env-declarative-expand');
|
|
67
|
+
const appPath = getBuilderPath(appKey);
|
|
68
|
+
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
69
|
+
const urls = await resolveDeclarativeShowUrlsForApp(appKey, appPath, variablesPath, 'docker');
|
|
70
|
+
if (!urls) return;
|
|
71
|
+
summary.application.url = urls.publicUrl;
|
|
72
|
+
summary.application.internalUrl = urls.internalUrl;
|
|
73
|
+
} catch {
|
|
74
|
+
/* display-only */
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Local manifest provenance for `--json` (plan 141); null when the app is not on disk locally.
|
|
80
|
+
* @param {string} appKey
|
|
81
|
+
* @returns {Promise<{ tier: string, tierLabel: string, configPath: string }|null>}
|
|
82
|
+
*/
|
|
83
|
+
async function tryGetLocalManifestSourceForJson(appKey) {
|
|
84
|
+
try {
|
|
85
|
+
const { getManifestSourcePayload } = require('../utils/manifest-source-emit');
|
|
86
|
+
const { appPath } = await detectAppType(appKey);
|
|
87
|
+
return getManifestSourcePayload(appKey, appPath);
|
|
88
|
+
} catch {
|
|
89
|
+
return { tier: 'unknown', tierLabel: 'unknown', configPath: '' };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
41
93
|
/** Truncate deployment key for display */
|
|
42
94
|
const DEPLOYMENT_KEY_TRUNCATE_LEN = 12;
|
|
43
95
|
|
|
@@ -651,16 +703,34 @@ async function loadOfflineShowSummary(appKey) {
|
|
|
651
703
|
}
|
|
652
704
|
}
|
|
653
705
|
|
|
706
|
+
async function emitShowOfflineManifestLine(appKey, json) {
|
|
707
|
+
if (json) return;
|
|
708
|
+
try {
|
|
709
|
+
const { detectAppType } = require('../utils/paths');
|
|
710
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
711
|
+
const { appPath } = await detectAppType(appKey);
|
|
712
|
+
emitManifestMetadataLineIfTTY(logger, { appKey, appPath, envOnly: false, json: false });
|
|
713
|
+
} catch {
|
|
714
|
+
/* ignore */
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
654
718
|
async function runOffline(appKey, json, permissionsOnly, verifyCert = false) {
|
|
655
719
|
const summary = await loadOfflineShowSummary(appKey);
|
|
720
|
+
await emitShowOfflineManifestLine(appKey, json);
|
|
721
|
+
const userCfg = await getConfig();
|
|
722
|
+
attachRunDefaultsFromUserConfig(summary, appKey, userCfg);
|
|
723
|
+
await attachDeclarativeUrlsToShowApplication(summary, appKey);
|
|
656
724
|
|
|
657
725
|
if (json) {
|
|
726
|
+
const manifestSource = await tryGetLocalManifestSourceForJson(appKey);
|
|
658
727
|
if (permissionsOnly) {
|
|
659
728
|
const out = {
|
|
660
729
|
source: summary.source,
|
|
661
730
|
path: summary.path,
|
|
662
731
|
appKey: summary.appKey,
|
|
663
|
-
permissions: summary.permissions || []
|
|
732
|
+
permissions: summary.permissions || [],
|
|
733
|
+
manifestSource
|
|
664
734
|
};
|
|
665
735
|
logger.log(JSON.stringify(out, null, 2));
|
|
666
736
|
return;
|
|
@@ -669,6 +739,9 @@ async function runOffline(appKey, json, permissionsOnly, verifyCert = false) {
|
|
|
669
739
|
source: summary.source,
|
|
670
740
|
path: summary.path,
|
|
671
741
|
appKey: summary.appKey,
|
|
742
|
+
runReloadDefault: Boolean(summary.runReloadDefault),
|
|
743
|
+
runProxyDefault: Boolean(summary.runProxyDefault),
|
|
744
|
+
manifestSource,
|
|
672
745
|
application: {
|
|
673
746
|
...summary.application,
|
|
674
747
|
roles: summary.roles,
|
|
@@ -717,13 +790,14 @@ async function fetchExternalSystemForOnline(controllerUrl, appKey, authConfig) {
|
|
|
717
790
|
}
|
|
718
791
|
}
|
|
719
792
|
|
|
720
|
-
function outputOnlineJson(summary, permissionsOnly) {
|
|
793
|
+
function outputOnlineJson(summary, permissionsOnly, manifestSource) {
|
|
721
794
|
if (permissionsOnly) {
|
|
722
795
|
const out = {
|
|
723
796
|
source: summary.source,
|
|
724
797
|
controllerUrl: summary.controllerUrl,
|
|
725
798
|
appKey: summary.appKey,
|
|
726
|
-
permissions: summary.permissions || []
|
|
799
|
+
permissions: summary.permissions || [],
|
|
800
|
+
manifestSource
|
|
727
801
|
};
|
|
728
802
|
logger.log(JSON.stringify(out, null, 2));
|
|
729
803
|
return;
|
|
@@ -733,6 +807,7 @@ function outputOnlineJson(summary, permissionsOnly) {
|
|
|
733
807
|
source: summary.source,
|
|
734
808
|
controllerUrl: summary.controllerUrl,
|
|
735
809
|
appKey: summary.appKey,
|
|
810
|
+
manifestSource,
|
|
736
811
|
application: {
|
|
737
812
|
key: app.key,
|
|
738
813
|
displayName: app.displayName,
|
|
@@ -751,6 +826,10 @@ function outputOnlineJson(summary, permissionsOnly) {
|
|
|
751
826
|
}
|
|
752
827
|
};
|
|
753
828
|
if (app.version !== undefined && app.version !== null) out.application.version = app.version;
|
|
829
|
+
if (summary.runReloadDefault) {
|
|
830
|
+
out.runReloadDefault = true;
|
|
831
|
+
}
|
|
832
|
+
out.runProxyDefault = Boolean(summary.runProxyDefault);
|
|
754
833
|
if (summary.externalSystem !== undefined && summary.externalSystem !== null) {
|
|
755
834
|
out.externalSystem = summary.externalSystem && summary.externalSystem.error
|
|
756
835
|
? { error: summary.externalSystem.error }
|
|
@@ -787,8 +866,11 @@ async function runOnline(appKey, json, permissionsOnly, verifyCert = false) {
|
|
|
787
866
|
token: authConfig.token,
|
|
788
867
|
controllerUrl: authResult.actualControllerUrl
|
|
789
868
|
});
|
|
869
|
+
attachRunDefaultsFromUserConfig(summary, appKey, await getConfig());
|
|
870
|
+
await attachDeclarativeUrlsToShowApplication(summary, appKey);
|
|
790
871
|
if (json) {
|
|
791
|
-
|
|
872
|
+
const manifestSource = await tryGetLocalManifestSourceForJson(appKey);
|
|
873
|
+
outputOnlineJson(summary, permissionsOnly, manifestSource);
|
|
792
874
|
return;
|
|
793
875
|
}
|
|
794
876
|
displayShow(summary, { permissionsOnly: !!permissionsOnly });
|
package/lib/build/index.js
CHANGED
|
@@ -196,12 +196,13 @@ async function generateDockerfile(appNameOrPath, language, config, buildConfig =
|
|
|
196
196
|
|
|
197
197
|
async function postBuildTasks(appName, buildConfig) {
|
|
198
198
|
try {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
|
|
199
|
+
// Validate that env.template + secrets resolve cleanly, but never write <appPath>/.env or
|
|
200
|
+
// envOutputPath. Build args still flow through resolveAndGetEnvMap (in-memory). Run
|
|
201
|
+
// `aifabrix resolve <app>` to materialize an on-disk .env.
|
|
202
|
+
await secrets.generateEnvFile(appName, buildConfig.secrets, 'docker', false, { noWrite: true });
|
|
203
|
+
logger.log(formatSuccessLine('Env resolution validated (in-memory only; run "aifabrix resolve ' + appName + '" for on-disk .env)'));
|
|
203
204
|
} catch (error) {
|
|
204
|
-
logger.log(formatWarningLine(`Could not
|
|
205
|
+
logger.log(formatWarningLine(`Could not resolve env: ${error.message}`));
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -411,6 +412,9 @@ async function resolveDockerBuildArgsForApp(appName) {
|
|
|
411
412
|
*/
|
|
412
413
|
async function runStandardDockerBuild(appName, options) {
|
|
413
414
|
const { buildConfig } = await buildHelpers.loadAndValidateConfig(appName);
|
|
415
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
416
|
+
const { appPath } = await detectAppType(appName);
|
|
417
|
+
emitManifestMetadataLineIfTTY(logger, { appKey: appName, appPath, envOnly: false, json: false });
|
|
414
418
|
const { devDir, effectiveImageName, imageName, appConfig } = await prepareDevDirectory(appName, buildConfig, options);
|
|
415
419
|
const contextPath = prepareBuildContext(buildConfig, devDir);
|
|
416
420
|
const dockerfilePath = await handleDockerfileGeneration(appName, {
|
package/lib/cli/infra-guided.js
CHANGED
|
@@ -23,6 +23,9 @@ const { handleLogin } = require('../commands/login');
|
|
|
23
23
|
const healthCheck = require('../utils/health-check');
|
|
24
24
|
const { prepareUrlsLocalRegistryForUpPlatform } = require('../commands/up-common');
|
|
25
25
|
const { assertDevInfraUp } = require('../commands/dev-infra-gate');
|
|
26
|
+
const {
|
|
27
|
+
emitSystemBuilderAppManifestLineIfTTY
|
|
28
|
+
} = require('../utils/manifest-source-emit');
|
|
26
29
|
|
|
27
30
|
const SEPARATOR = '────────────────────────────────────────';
|
|
28
31
|
|
|
@@ -67,22 +70,23 @@ async function hasErrorLogs(appName, opts = {}) {
|
|
|
67
70
|
|
|
68
71
|
async function validateAppErrorLogs(appNames) {
|
|
69
72
|
const names = Array.isArray(appNames) ? appNames : [];
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return { name, hasErrors: false };
|
|
73
|
+
const bad = [];
|
|
74
|
+
for (const name of names) {
|
|
75
|
+
try {
|
|
76
|
+
const hasErrors = await hasErrorLogs(name, { tailLines: 300 });
|
|
77
|
+
if (hasErrors) {
|
|
78
|
+
bad.push(name);
|
|
77
79
|
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore per-app log failures
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (bad.length === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
83
87
|
logger.log(chalk.yellow('⚠ Some services reported error logs'));
|
|
84
|
-
for (const
|
|
85
|
-
logger.log(chalk.gray(` Run: aifabrix logs ${
|
|
88
|
+
for (const name of bad) {
|
|
89
|
+
logger.log(chalk.gray(` Run: aifabrix logs ${name} -l error`));
|
|
86
90
|
}
|
|
87
91
|
logger.log('');
|
|
88
92
|
}
|
|
@@ -158,7 +162,7 @@ async function computeAppBaseUrl(appName) {
|
|
|
158
162
|
return joinUrlPath(publicBase, mount);
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
// No Traefik front-door routing: publicBase
|
|
165
|
+
// No Traefik front-door routing: publicBase is localhost + published port unless Traefik/proxy opts into remote.
|
|
162
166
|
return String(publicBase).replace(/\/+$/, '');
|
|
163
167
|
}
|
|
164
168
|
|
|
@@ -226,7 +230,9 @@ async function getInfraHostAndPorts() {
|
|
|
226
230
|
const developerId = await config.getDeveloperId();
|
|
227
231
|
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
228
232
|
const ports = devConfig.getDevPorts(idNum);
|
|
229
|
-
const
|
|
233
|
+
const cfg = await config.getConfig();
|
|
234
|
+
const traefikOn = Boolean(cfg && cfg.traefik === true);
|
|
235
|
+
const remoteServer = traefikOn ? await config.getRemoteServer() : null;
|
|
230
236
|
const host = resolveInfraHost(remoteServer);
|
|
231
237
|
return { idNum, ports, host };
|
|
232
238
|
}
|
|
@@ -308,6 +314,7 @@ async function runGuidedUpDataplane(options, handleUpDataplane) {
|
|
|
308
314
|
await runGuidedAuthStep(handleLogin);
|
|
309
315
|
|
|
310
316
|
logger.log('');
|
|
317
|
+
emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
|
|
311
318
|
const dpSpin = startSpinner('Starting Dataplane...');
|
|
312
319
|
await withMutedLogger(() =>
|
|
313
320
|
handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
|
|
@@ -346,6 +353,7 @@ function logGuidedPlatformSetupHeader() {
|
|
|
346
353
|
logger.log('');
|
|
347
354
|
logger.log(title('AI Fabrix Platform Setup'));
|
|
348
355
|
logger.log(SEPARATOR);
|
|
356
|
+
logger.log(chalk.gray('This may take a few minutes...'));
|
|
349
357
|
logger.log('');
|
|
350
358
|
}
|
|
351
359
|
|
|
@@ -364,32 +372,39 @@ async function runGuidedAuthStep(handleLogin) {
|
|
|
364
372
|
if (spin) spin.stop();
|
|
365
373
|
}
|
|
366
374
|
|
|
367
|
-
async function
|
|
368
|
-
|
|
369
|
-
if (forceCleanSummary && forceCleanSummary.forceSummary) {
|
|
370
|
-
logUpPlatformForceCleanSummary(forceCleanSummary.forceSummary, forceCleanSummary.cleanedApps);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
await withMutedLogger(() => prepareUrlsLocalRegistryForUpPlatform());
|
|
374
|
-
|
|
375
|
+
async function runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso) {
|
|
376
|
+
emitSystemBuilderAppManifestLineIfTTY(logger, 'keycloak');
|
|
375
377
|
const kcSpin = startSpinner('Starting Keycloak...');
|
|
376
378
|
await withMutedLogger(() => handleUpMiso({ ...options, platformInstall: true }));
|
|
377
379
|
stopSpinnerSuccess(kcSpin, 'Keycloak ready');
|
|
378
380
|
logger.log('');
|
|
379
|
-
|
|
381
|
+
emitSystemBuilderAppManifestLineIfTTY(logger, 'miso-controller');
|
|
380
382
|
const mcSpin = startSpinner('Starting Miso Controller...');
|
|
381
383
|
await waitForAppReady('miso-controller', { timeoutSeconds: 120 });
|
|
382
384
|
stopSpinnerSuccess(mcSpin, 'Miso Controller ready');
|
|
383
385
|
logger.log('');
|
|
386
|
+
}
|
|
384
387
|
|
|
385
|
-
|
|
386
|
-
|
|
388
|
+
async function runGuidedDataplaneInstallPhase(options, handleUpDataplane) {
|
|
387
389
|
logger.log('');
|
|
390
|
+
emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
|
|
388
391
|
const dpSpin = startSpinner('Starting Dataplane...');
|
|
389
392
|
await withMutedLogger(() =>
|
|
390
393
|
handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
|
|
391
394
|
);
|
|
392
395
|
stopSpinnerSuccess(dpSpin, 'Dataplane ready');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, handleLogin, forceCleanSummary = null) {
|
|
399
|
+
logGuidedPlatformSetupHeader();
|
|
400
|
+
if (forceCleanSummary && forceCleanSummary.forceSummary) {
|
|
401
|
+
logUpPlatformForceCleanSummary(forceCleanSummary.forceSummary, forceCleanSummary.cleanedApps);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
await withMutedLogger(() => prepareUrlsLocalRegistryForUpPlatform());
|
|
405
|
+
await runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso);
|
|
406
|
+
await runGuidedAuthStep(handleLogin);
|
|
407
|
+
await runGuidedDataplaneInstallPhase(options, handleUpDataplane);
|
|
393
408
|
await validateAppErrorLogs(['miso-controller', 'dataplane']);
|
|
394
409
|
await logPlatformReadyFooter();
|
|
395
410
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared completion hook for infra/platform CLI commands (installation.log).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview installation.log CLI helper
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const config = require('../core/config');
|
|
12
|
+
const installationLog = require('../utils/installation-log');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {Object} params
|
|
16
|
+
* @param {string} params.command
|
|
17
|
+
* @param {Object} params.options
|
|
18
|
+
* @param {Date} params.startedAt
|
|
19
|
+
* @param {'success'|'failure'} params.outcome
|
|
20
|
+
* @param {Error} [params.error]
|
|
21
|
+
* @param {string[]} [params.platformAppList]
|
|
22
|
+
* @param {Object} [params.cleanup]
|
|
23
|
+
* @param {boolean} [params.upPlatformForce]
|
|
24
|
+
* @param {boolean} [params.omitInfraSection]
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
27
|
+
async function recordInfraInstallationCommand(params) {
|
|
28
|
+
const {
|
|
29
|
+
command,
|
|
30
|
+
options,
|
|
31
|
+
startedAt,
|
|
32
|
+
outcome,
|
|
33
|
+
error,
|
|
34
|
+
platformAppList,
|
|
35
|
+
cleanup,
|
|
36
|
+
upPlatformForce,
|
|
37
|
+
omitInfraSection
|
|
38
|
+
} = params;
|
|
39
|
+
|
|
40
|
+
const completedAt = new Date();
|
|
41
|
+
let cfg = {};
|
|
42
|
+
try {
|
|
43
|
+
cfg = await config.getConfig();
|
|
44
|
+
} catch {
|
|
45
|
+
cfg = {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await installationLog.appendInstallationRecord({
|
|
50
|
+
command,
|
|
51
|
+
outcome,
|
|
52
|
+
startedAt,
|
|
53
|
+
completedAt,
|
|
54
|
+
options,
|
|
55
|
+
infra: !omitInfraSection && cfg ? { cfg, options } : undefined,
|
|
56
|
+
platformAppList,
|
|
57
|
+
cleanup,
|
|
58
|
+
upPlatformForce: upPlatformForce === true,
|
|
59
|
+
configExtra: {
|
|
60
|
+
controllerUrl: await installationLog.resolveControllerUrlForLog(),
|
|
61
|
+
adminEmail: await installationLog.resolveAdminEmailPresence()
|
|
62
|
+
},
|
|
63
|
+
error,
|
|
64
|
+
errorCode: error && error.code ? String(error.code) : undefined
|
|
65
|
+
});
|
|
66
|
+
} catch {
|
|
67
|
+
// never block CLI on log failure
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
recordInfraInstallationCommand
|
|
73
|
+
};
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -207,7 +207,8 @@ Examples:
|
|
|
207
207
|
$ aifabrix run myapp --env tst
|
|
208
208
|
$ aifabrix run myapp --tag v1.0.0
|
|
209
209
|
$ aifabrix run myapp --base
|
|
210
|
-
$ aifabrix run myapp --reload
|
|
210
|
+
$ aifabrix run myapp --reload
|
|
211
|
+
$ aifabrix run myapp --no-proxy # same as --proxy false: localhost for Docker declarative public URLs; saves applications.<app>.proxy: false`;
|
|
211
212
|
program.command('run <app>')
|
|
212
213
|
.description('Run app locally or on remote Docker host')
|
|
213
214
|
.option('-p, --port <port>', 'Override local port')
|
|
@@ -216,6 +217,15 @@ Examples:
|
|
|
216
217
|
.option('-e, --env <env>', 'Environment: dev (default), tst, or pro', 'dev')
|
|
217
218
|
.option('--base', 'Use manifest base image only (skip local developer-scoped tag preference)')
|
|
218
219
|
.option('--reload', 'In dev: mount workspace into container (Mutagen only if docker-endpoint is a remote host)')
|
|
220
|
+
.option(
|
|
221
|
+
'--proxy',
|
|
222
|
+
'Use Traefik/front-door public URL hints when infra has Traefik and application.yaml enables frontDoorRouting (default: on)',
|
|
223
|
+
true
|
|
224
|
+
)
|
|
225
|
+
.option(
|
|
226
|
+
'--no-proxy',
|
|
227
|
+
'Docker declarative public url://* use localhost + published port (saves applications.<app>.proxy: false); overrides --proxy'
|
|
228
|
+
)
|
|
219
229
|
.addHelpText('after', runHelp)
|
|
220
230
|
.action(async(appName, options) => {
|
|
221
231
|
try {
|
package/lib/cli/setup-auth.js
CHANGED
|
@@ -23,12 +23,97 @@ Examples:
|
|
|
23
23
|
const AUTH_HELP_AFTER = `
|
|
24
24
|
Without options: show auth status (same as: aifabrix auth status).
|
|
25
25
|
With --set-controller or --set-environment: write defaults to config.yaml.
|
|
26
|
+
Omit the URL on --set-controller to pick from controllers already stored in config (device logins + default).
|
|
27
|
+
For details on omitting the URL: aifabrix auth set-controller -h
|
|
26
28
|
Aliases:
|
|
27
|
-
aifabrix auth set-controller
|
|
29
|
+
aifabrix auth set-controller [url]
|
|
28
30
|
aifabrix auth set-environment <env>
|
|
29
31
|
Subcommand: auth status [--validate] for CI/scripts.
|
|
30
32
|
`;
|
|
31
33
|
|
|
34
|
+
const AUTH_SET_CONTROLLER_HELP_AFTER = `
|
|
35
|
+
Argument:
|
|
36
|
+
[url] Optional. When omitted, the CLI uses controllers already known in your config file
|
|
37
|
+
(the default controller value plus each device-token key). Requires an interactive TTY.
|
|
38
|
+
|
|
39
|
+
Behavior:
|
|
40
|
+
- No saved controllers: error; run aifabrix login first, or pass a URL.
|
|
41
|
+
- One saved controller: if it is already the default, prints confirmation; otherwise sets it.
|
|
42
|
+
- Several saved controllers: interactive list to choose the default.
|
|
43
|
+
- Non-interactive shell: pass the URL explicitly (piped/CI shells cannot pick without a URL).
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
$ aifabrix auth set-controller
|
|
47
|
+
Pick default controller from config (TTY), or set the only saved one.
|
|
48
|
+
|
|
49
|
+
$ aifabrix auth set-controller http://localhost:3600
|
|
50
|
+
Set default controller to that URL (validated; same rules as aifabrix auth --set-controller).
|
|
51
|
+
|
|
52
|
+
$ aifabrix auth --set-controller
|
|
53
|
+
Same as omitting the URL on this subcommand (parent command flag).
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
function createAuthStatusAction() {
|
|
57
|
+
return async(options) => {
|
|
58
|
+
try {
|
|
59
|
+
await handleAuthStatus(options);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
handleCommandError(error, 'auth status');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createAuthParentAction() {
|
|
68
|
+
return async(options) => {
|
|
69
|
+
try {
|
|
70
|
+
const setController = options.setController || options['set-controller'];
|
|
71
|
+
const setEnvironment = options.setEnvironment || options['set-environment'];
|
|
72
|
+
if (setController || setEnvironment) {
|
|
73
|
+
await handleAuthConfig({
|
|
74
|
+
setController,
|
|
75
|
+
setEnvironment
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await handleAuthStatus(options);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
handleCommandError(error, 'auth');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function registerAuthSetControllerSubcommand(auth) {
|
|
88
|
+
const setControllerCmd = auth.command('set-controller [url]').description(
|
|
89
|
+
'Set config.controller to a URL, or omit the URL to pick from controllers already stored in config'
|
|
90
|
+
);
|
|
91
|
+
if (typeof setControllerCmd.summary === 'function') {
|
|
92
|
+
setControllerCmd.summary('Set or pick default controller URL in config');
|
|
93
|
+
}
|
|
94
|
+
setControllerCmd.addHelpText('after', AUTH_SET_CONTROLLER_HELP_AFTER).action(async(url) => {
|
|
95
|
+
try {
|
|
96
|
+
await handleAuthConfig({ setController: url || true });
|
|
97
|
+
} catch (error) {
|
|
98
|
+
handleCommandError(error, 'auth set-controller');
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function registerAuthSetEnvironmentSubcommand(auth) {
|
|
105
|
+
auth.command('set-environment <env>')
|
|
106
|
+
.description('Set default environment in config (alias for auth --set-environment)')
|
|
107
|
+
.action(async(env) => {
|
|
108
|
+
try {
|
|
109
|
+
await handleAuthConfig({ setEnvironment: env });
|
|
110
|
+
} catch (error) {
|
|
111
|
+
handleCommandError(error, 'auth set-environment');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
32
117
|
function setupLoginCommand(program) {
|
|
33
118
|
program.command('login')
|
|
34
119
|
.description('Sign in to Miso Controller (device or credentials flow)')
|
|
@@ -68,63 +153,23 @@ function setupLogoutCommand(program) {
|
|
|
68
153
|
}
|
|
69
154
|
|
|
70
155
|
function setupAuthSubcommands(program) {
|
|
71
|
-
const authStatusHandler = async(options) => {
|
|
72
|
-
try {
|
|
73
|
-
await handleAuthStatus(options);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
handleCommandError(error, 'auth status');
|
|
76
|
-
process.exit(1);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
156
|
const auth = program.command('auth')
|
|
80
157
|
.description('Show auth status or set default controller/environment')
|
|
81
158
|
.addHelpText('after', AUTH_HELP_AFTER)
|
|
82
|
-
.option(
|
|
159
|
+
.option(
|
|
160
|
+
'--set-controller [url]',
|
|
161
|
+
'Set default controller URL in config; omit url to choose from controllers registered in config'
|
|
162
|
+
)
|
|
83
163
|
.option('--set-environment <env>', 'Set default environment in config')
|
|
84
|
-
.action(
|
|
85
|
-
try {
|
|
86
|
-
const setController = options.setController || options['set-controller'];
|
|
87
|
-
const setEnvironment = options.setEnvironment || options['set-environment'];
|
|
88
|
-
if (setController || setEnvironment) {
|
|
89
|
-
await handleAuthConfig({
|
|
90
|
-
setController,
|
|
91
|
-
setEnvironment
|
|
92
|
-
});
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
await handleAuthStatus(options);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
handleCommandError(error, 'auth');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
164
|
+
.action(createAuthParentAction());
|
|
101
165
|
|
|
102
|
-
auth
|
|
103
|
-
|
|
104
|
-
.action(async(url) => {
|
|
105
|
-
try {
|
|
106
|
-
await handleAuthConfig({ setController: url });
|
|
107
|
-
} catch (error) {
|
|
108
|
-
handleCommandError(error, 'auth set-controller');
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
auth.command('set-environment <env>')
|
|
114
|
-
.description('Set default environment in config (alias for auth --set-environment)')
|
|
115
|
-
.action(async(env) => {
|
|
116
|
-
try {
|
|
117
|
-
await handleAuthConfig({ setEnvironment: env });
|
|
118
|
-
} catch (error) {
|
|
119
|
-
handleCommandError(error, 'auth set-environment');
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
166
|
+
registerAuthSetControllerSubcommand(auth);
|
|
167
|
+
registerAuthSetEnvironmentSubcommand(auth);
|
|
123
168
|
|
|
124
169
|
auth.command('status')
|
|
125
170
|
.description('Show tokens/session for current controller and environment')
|
|
126
171
|
.option('--validate', 'Exit with code 1 when not authenticated (for scripted use, e.g. manual test setup)')
|
|
127
|
-
.action(
|
|
172
|
+
.action(createAuthStatusAction());
|
|
128
173
|
}
|
|
129
174
|
|
|
130
175
|
/**
|