@aifabrix/builder 2.48.0 → 2.49.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/.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 +3 -0
- package/jest.projects.js +12 -0
- package/lib/api/applications.api.js +23 -0
- package/lib/api/dev.api.js +1 -1
- package/lib/api/index.js +5 -2
- package/lib/api/types/dev.types.js +3 -2
- package/lib/app/deploy-config.js +6 -4
- package/lib/app/register.js +1 -1
- package/lib/app/run-container-start.js +0 -9
- package/lib/app/run-helpers.js +8 -4
- package/lib/app/run-reload-sync.js +16 -18
- package/lib/app/run-stop-containers.js +48 -5
- package/lib/app/run.js +6 -3
- package/lib/app/show.js +2 -2
- package/lib/cli/infra-guided-auth.js +42 -6
- package/lib/cli/infra-guided-footers.js +164 -0
- package/lib/cli/infra-guided-spinners.js +57 -0
- package/lib/cli/infra-guided.js +41 -179
- package/lib/cli/setup-app.js +1 -1
- package/lib/cli/setup-dev.js +3 -2
- package/lib/cli/setup-infra.js +3 -2
- package/lib/cli/setup-platform.js +3 -0
- package/lib/commands/login-device.js +6 -2
- package/lib/commands/login.js +4 -1
- package/lib/commands/setup-modes.js +19 -3
- package/lib/commands/setup-platform-auth.js +25 -0
- package/lib/commands/setup-prompts-admin.js +9 -0
- package/lib/commands/setup.js +60 -8
- package/lib/commands/up-dataplane-bootstrap.js +149 -0
- package/lib/commands/up-dataplane-credentials.js +19 -4
- package/lib/commands/up-dataplane.js +74 -11
- package/lib/core/secrets-env-declarative-expand.js +35 -23
- package/lib/core/setup-platform-mode.js +43 -0
- package/lib/deployment/deployer-status.js +26 -3
- package/lib/deployment/deployer.js +7 -14
- package/lib/external-system/download-helpers.js +10 -1
- package/lib/infrastructure/compose.js +40 -12
- package/lib/infrastructure/services.js +15 -3
- package/lib/infrastructure/traefik-tls-dynamic.js +88 -0
- package/lib/utils/app-bootstrap-api.js +53 -0
- package/lib/utils/app-run-containers.js +14 -3
- package/lib/utils/applications-config-defaults.js +19 -3
- package/lib/utils/compose-generate-docker-compose.js +16 -5
- package/lib/utils/compose-generator.js +34 -15
- package/lib/utils/compose-traefik-ingress-base.js +13 -14
- package/lib/utils/config-paths.js +6 -1
- package/lib/utils/dev-user-groups.js +1 -0
- package/lib/utils/docker-daemon-tls-ca.js +94 -57
- package/lib/utils/docker-reload-mount.js +12 -1
- package/lib/utils/external-env-template.js +53 -18
- package/lib/utils/health-check-poll-ui.js +84 -0
- package/lib/utils/health-check-url.js +54 -34
- package/lib/utils/health-check.js +43 -16
- package/lib/utils/infra-optional-service-flags.js +31 -2
- package/lib/utils/mutagen.js +7 -4
- package/lib/utils/platform-controller-url.js +23 -1
- package/lib/utils/refresh-remote-settings.js +67 -0
- package/lib/utils/remote-host-for-front-door.js +43 -0
- package/lib/utils/run-cli-flags.js +37 -1
- package/lib/utils/url-declarative-public-base.js +11 -4
- package/lib/utils/url-declarative-resolve-build-urls.js +11 -2
- package/lib/utils/url-declarative-resolve-expand-token.js +12 -1
- package/lib/utils/url-declarative-resolve-load-doc.js +30 -11
- package/lib/utils/url-declarative-resolve.js +2 -0
- package/lib/utils/urls-local-registry-scan.js +95 -10
- package/lib/utils/urls-local-registry.js +24 -1
- package/lib/utils/worker-sidecars.js +2 -2
- package/lib/validation/external-system-auth-rules.js +45 -7
- package/package.json +2 -1
- package/scripts/test-dataplane-bootstrap.js +331 -0
- package/templates/applications/dataplane/env.template +22 -5
- package/templates/applications/dataplane/rbac.yaml +11 -0
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +3 -0
- package/templates/infra/compose.yaml.hbs +13 -9
- package/templates/python/docker-compose.hbs +3 -0
- package/templates/typescript/docker-compose.hbs +3 -0
|
@@ -16,6 +16,7 @@ const { withMutedLogger } = require('../utils/with-muted-logger');
|
|
|
16
16
|
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
17
17
|
const { isAuthenticationError } = require('../utils/cli-utils');
|
|
18
18
|
const { resolveControllerUrl, isPlatformAuthValidForController } = require('../utils/controller-url');
|
|
19
|
+
const { canUseBootstrapProvision } = require('../commands/up-dataplane-bootstrap');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* @param {Object} authOpts
|
|
@@ -59,7 +60,10 @@ async function runGuidedDeviceLogin(handleLoginFn, controllerUrl, opts = {}) {
|
|
|
59
60
|
compact: true,
|
|
60
61
|
skipControllerUrlPersist: Boolean(opts.tokenControllerUrl),
|
|
61
62
|
skipDataplaneVersionRefresh: opts.skipDataplaneVersionRefresh === true,
|
|
62
|
-
exitOnFailure: false
|
|
63
|
+
exitOnFailure: false,
|
|
64
|
+
guidedInstall: opts.guidedInstall === true,
|
|
65
|
+
onProgressText: opts.onProgressText,
|
|
66
|
+
suppressSuccessLine: opts.suppressSuccessLine === true
|
|
63
67
|
});
|
|
64
68
|
}
|
|
65
69
|
|
|
@@ -74,12 +78,17 @@ async function runGuidedAuthStep(handleLoginFn, authOpts, spinners) {
|
|
|
74
78
|
const spin = spinners.startSpinner('Authenticating...');
|
|
75
79
|
const { apiUrl, tokenUrl } = await resolveGuidedAuthControllerUrls(authOpts);
|
|
76
80
|
const loginOpts = { skipDataplaneVersionRefresh: true, tokenControllerUrl: tokenUrl };
|
|
81
|
+
const bootstrapEligible = await canUseBootstrapProvision({ skipInfraCheck: true });
|
|
77
82
|
const skipLogin =
|
|
83
|
+
bootstrapEligible ||
|
|
78
84
|
authOpts.skipLoginIfAuthenticated === true ||
|
|
79
85
|
(await isPlatformAuthValidForController(tokenUrl));
|
|
80
86
|
try {
|
|
81
87
|
if (skipLogin) {
|
|
82
|
-
spinners.stopSpinnerSuccess(
|
|
88
|
+
spinners.stopSpinnerSuccess(
|
|
89
|
+
spin,
|
|
90
|
+
bootstrapEligible ? 'Bootstrap provision (no device login)' : 'Already authenticated'
|
|
91
|
+
);
|
|
83
92
|
return;
|
|
84
93
|
}
|
|
85
94
|
await runGuidedDeviceLogin(handleLoginFn, apiUrl, loginOpts);
|
|
@@ -103,9 +112,17 @@ async function runGuidedAuthStep(handleLoginFn, authOpts, spinners) {
|
|
|
103
112
|
* @param {Function} handleUpDataplane
|
|
104
113
|
* @param {Function} handleLoginFn
|
|
105
114
|
* @param {string} controllerUrl
|
|
115
|
+
* @param {{ onSpinnerText?: (text: string) => void }} [ui]
|
|
106
116
|
* @returns {Promise<void>}
|
|
107
117
|
*/
|
|
108
|
-
async function runGuidedDataplaneWithAuthRecovery(
|
|
118
|
+
async function runGuidedDataplaneWithAuthRecovery(
|
|
119
|
+
options,
|
|
120
|
+
handleUpDataplane,
|
|
121
|
+
handleLoginFn,
|
|
122
|
+
controllerUrl,
|
|
123
|
+
ui = {}
|
|
124
|
+
) {
|
|
125
|
+
const onSpinnerText = typeof ui.onSpinnerText === 'function' ? ui.onSpinnerText : null;
|
|
109
126
|
try {
|
|
110
127
|
await withMutedLogger(() =>
|
|
111
128
|
handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
|
|
@@ -114,10 +131,29 @@ async function runGuidedDataplaneWithAuthRecovery(options, handleUpDataplane, ha
|
|
|
114
131
|
if (!isAuthenticationError(error)) {
|
|
115
132
|
throw error;
|
|
116
133
|
}
|
|
134
|
+
const bootstrapEligible = await canUseBootstrapProvision({
|
|
135
|
+
skipInfraCheck: true,
|
|
136
|
+
allowNonDev: options.allowNonDev
|
|
137
|
+
});
|
|
138
|
+
if (bootstrapEligible) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
117
141
|
const loginUrl = error.controllerUrl || controllerUrl || (await resolveControllerUrl());
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
142
|
+
if (onSpinnerText) {
|
|
143
|
+
onSpinnerText('Authenticating...');
|
|
144
|
+
} else {
|
|
145
|
+
logger.log('');
|
|
146
|
+
logger.log(chalk.yellow('Device login required to register and deploy Dataplane.'));
|
|
147
|
+
}
|
|
148
|
+
await runGuidedDeviceLogin(handleLoginFn, loginUrl, {
|
|
149
|
+
skipDataplaneVersionRefresh: false,
|
|
150
|
+
compact: true,
|
|
151
|
+
guidedInstall: true,
|
|
152
|
+
onProgressText: onSpinnerText || undefined
|
|
153
|
+
});
|
|
154
|
+
if (onSpinnerText) {
|
|
155
|
+
onSpinnerText('Validating Dataplane...');
|
|
156
|
+
}
|
|
121
157
|
await withMutedLogger(() =>
|
|
122
158
|
handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
|
|
123
159
|
);
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Footer and header layout for guided infra/platform CLI flows.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const config = require('../core/config');
|
|
10
|
+
const { computeAppBaseUrl, resolvePlatformControllerUrl } = require('../utils/platform-controller-url');
|
|
11
|
+
const { successGlyph } = require('../utils/cli-test-layout-chalk');
|
|
12
|
+
|
|
13
|
+
const SEPARATOR = '────────────────────────────────────────';
|
|
14
|
+
|
|
15
|
+
function title(text) {
|
|
16
|
+
return chalk.bold(text);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function logSection(label, value) {
|
|
20
|
+
logger.log(title(label));
|
|
21
|
+
logger.log(` ${value}`);
|
|
22
|
+
logger.log('');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function logFooterStart(label) {
|
|
26
|
+
logger.log('');
|
|
27
|
+
logger.log(SEPARATOR);
|
|
28
|
+
logger.log(title(label));
|
|
29
|
+
logger.log(SEPARATOR);
|
|
30
|
+
logger.log('');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function logFooterEnd() {
|
|
34
|
+
logger.log(SEPARATOR);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @async
|
|
39
|
+
* @returns {Promise<string>}
|
|
40
|
+
*/
|
|
41
|
+
async function resolvePlatformControllerUrlForDisplay() {
|
|
42
|
+
const stored = await config.getPlatformControllerUrl();
|
|
43
|
+
if (stored) {
|
|
44
|
+
return stored;
|
|
45
|
+
}
|
|
46
|
+
return resolvePlatformControllerUrl();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function logPlatformReadyFooter() {
|
|
50
|
+
const [controllerUrl, dataplaneUrl, keycloakUrl] = await Promise.all([
|
|
51
|
+
resolvePlatformControllerUrlForDisplay(),
|
|
52
|
+
computeAppBaseUrl('dataplane'),
|
|
53
|
+
computeAppBaseUrl('keycloak')
|
|
54
|
+
]);
|
|
55
|
+
const cfg = await config.getConfig();
|
|
56
|
+
const env = cfg && cfg.environment ? cfg.environment : 'dev';
|
|
57
|
+
|
|
58
|
+
logFooterStart('Platform Ready');
|
|
59
|
+
logSection('Environment', env);
|
|
60
|
+
logSection('Miso Controller', controllerUrl);
|
|
61
|
+
logSection('Dataplane (DEV)', dataplaneUrl);
|
|
62
|
+
logSection('Keycloak', keycloakUrl);
|
|
63
|
+
logSection('API Documentation', `${dataplaneUrl}/api/docs`);
|
|
64
|
+
logSection('Knowledge Base', 'https://docs.aifabrix.ai');
|
|
65
|
+
logSection('Getting Started', 'https://docs.aifabrix.ai/get-started');
|
|
66
|
+
logFooterEnd();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function logMisoReadyFooter() {
|
|
70
|
+
const [controllerUrl, keycloakUrl] = await Promise.all([
|
|
71
|
+
resolvePlatformControllerUrlForDisplay(),
|
|
72
|
+
computeAppBaseUrl('keycloak')
|
|
73
|
+
]);
|
|
74
|
+
logFooterStart('Miso Ready');
|
|
75
|
+
logSection('Miso Controller', controllerUrl);
|
|
76
|
+
logSection('Keycloak', keycloakUrl);
|
|
77
|
+
logFooterEnd();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function logDataplaneReadyFooter() {
|
|
81
|
+
const dataplaneUrl = await computeAppBaseUrl('dataplane');
|
|
82
|
+
const cfg = await config.getConfig();
|
|
83
|
+
const env = cfg && cfg.environment ? cfg.environment : 'dev';
|
|
84
|
+
logFooterStart('Dataplane Ready');
|
|
85
|
+
logSection('Environment', env);
|
|
86
|
+
logSection('Dataplane (DEV)', dataplaneUrl);
|
|
87
|
+
logSection('API Documentation', `${dataplaneUrl}/api/docs`);
|
|
88
|
+
logFooterEnd();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {'platform'|'dataplane'} [scope]
|
|
93
|
+
*/
|
|
94
|
+
function logPlatformSetupDurationHint(scope = 'platform') {
|
|
95
|
+
const line =
|
|
96
|
+
scope === 'dataplane'
|
|
97
|
+
? 'Installation may take a few minutes. Authenticating and starting Dataplane — please wait.'
|
|
98
|
+
: 'Installation may take a few minutes. Keycloak, Miso Controller, and Dataplane are starting — please wait.';
|
|
99
|
+
logger.log(chalk.yellow(`⚠ ${line}`));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function logGuidedPlatformSetupHeader() {
|
|
103
|
+
logger.log('');
|
|
104
|
+
logger.log(title('AI Fabrix Platform Setup'));
|
|
105
|
+
logger.log(SEPARATOR);
|
|
106
|
+
logger.log('');
|
|
107
|
+
logPlatformSetupDurationHint('platform');
|
|
108
|
+
logger.log('');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {{ deviceCleared: number, clientCleared: number, environment: string, defaultControllerUrl: string }} forceSummary
|
|
113
|
+
* @param {string[]} cleanedApps
|
|
114
|
+
*/
|
|
115
|
+
function logUpPlatformForceCleanSummary(forceSummary, cleanedApps) {
|
|
116
|
+
const { isSetupQuietOutput } = require('../utils/setup-quiet-output');
|
|
117
|
+
if (isSetupQuietOutput()) return;
|
|
118
|
+
const check = successGlyph();
|
|
119
|
+
logger.log(chalk.bold('Clean installation files'));
|
|
120
|
+
logger.log(
|
|
121
|
+
chalk.gray(
|
|
122
|
+
` ${check} Cleared ${forceSummary.deviceCleared} device token(s) and ${forceSummary.clientCleared} client token(s)`
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
logger.log(chalk.gray(` ${check} Environment set to ${forceSummary.environment}`));
|
|
126
|
+
logger.log(
|
|
127
|
+
chalk.gray(
|
|
128
|
+
` ${check} Default controller set to ${forceSummary.defaultControllerUrl} (run aifabrix login after platform is up)`
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
for (const appName of cleanedApps || []) {
|
|
132
|
+
logger.log(chalk.gray(` ${check} Cleaned builder/${appName}`));
|
|
133
|
+
}
|
|
134
|
+
logger.log('');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @param {Object} cfg
|
|
139
|
+
*/
|
|
140
|
+
function logStoppedServices(cfg) {
|
|
141
|
+
logger.log(title('Stopped'));
|
|
142
|
+
logger.log(' postgres');
|
|
143
|
+
logger.log(' redis');
|
|
144
|
+
if (cfg.pgadmin !== false) logger.log(' pgadmin');
|
|
145
|
+
if (cfg.redisCommander !== false) logger.log(' redis-commander');
|
|
146
|
+
if (cfg.traefik) logger.log(' traefik');
|
|
147
|
+
logger.log('');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
SEPARATOR,
|
|
152
|
+
title,
|
|
153
|
+
logSection,
|
|
154
|
+
logFooterStart,
|
|
155
|
+
logFooterEnd,
|
|
156
|
+
resolvePlatformControllerUrlForDisplay,
|
|
157
|
+
logPlatformReadyFooter,
|
|
158
|
+
logMisoReadyFooter,
|
|
159
|
+
logDataplaneReadyFooter,
|
|
160
|
+
logPlatformSetupDurationHint,
|
|
161
|
+
logGuidedPlatformSetupHeader,
|
|
162
|
+
logUpPlatformForceCleanSummary,
|
|
163
|
+
logStoppedServices
|
|
164
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Spinner helpers for guided infra/platform CLI flows.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const ora = require('ora');
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const { formatProgress, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
10
|
+
|
|
11
|
+
function shouldUseSpinner() {
|
|
12
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} text
|
|
17
|
+
* @returns {import('ora').Ora|null}
|
|
18
|
+
*/
|
|
19
|
+
function startSpinner(text) {
|
|
20
|
+
if (!shouldUseSpinner()) {
|
|
21
|
+
logger.log(formatProgress(text));
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return ora({ text, spinner: 'dots' }).start();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {import('ora').Ora|null} spinner
|
|
29
|
+
* @param {string} text
|
|
30
|
+
*/
|
|
31
|
+
function stopSpinnerSuccess(spinner, text) {
|
|
32
|
+
if (!spinner) {
|
|
33
|
+
logger.log(formatSuccessLine(text));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
spinner.succeed(text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {import('ora').Ora|null} spinner
|
|
41
|
+
* @returns {(text: string) => void}
|
|
42
|
+
*/
|
|
43
|
+
function createSpinnerTextUpdater(spinner) {
|
|
44
|
+
return (text) => {
|
|
45
|
+
if (spinner) {
|
|
46
|
+
spinner.text = text;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
logger.log(formatProgress(text));
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
startSpinner,
|
|
55
|
+
stopSpinnerSuccess,
|
|
56
|
+
createSpinnerTextUpdater
|
|
57
|
+
};
|
package/lib/cli/infra-guided.js
CHANGED
|
@@ -10,10 +10,7 @@ const config = require('../core/config');
|
|
|
10
10
|
const devConfig = require('../utils/dev-config');
|
|
11
11
|
const pathsUtil = require('../utils/paths');
|
|
12
12
|
const { loadConfigFile } = require('../utils/config-format');
|
|
13
|
-
const { computeAppBaseUrl, resolvePlatformControllerUrl } = require('../utils/platform-controller-url');
|
|
14
13
|
const { withMutedLogger } = require('../utils/with-muted-logger');
|
|
15
|
-
const { formatProgress, formatSuccessLine, successGlyph } = require('../utils/cli-test-layout-chalk');
|
|
16
|
-
const ora = require('ora');
|
|
17
14
|
const infra = require('../infrastructure');
|
|
18
15
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
19
16
|
const { getLogLevel, passesLevelFilter } = require('../commands/app-logs');
|
|
@@ -23,6 +20,26 @@ const {
|
|
|
23
20
|
runGuidedAuthStep,
|
|
24
21
|
runGuidedDataplaneWithAuthRecovery
|
|
25
22
|
} = require('./infra-guided-auth');
|
|
23
|
+
const {
|
|
24
|
+
startSpinner,
|
|
25
|
+
stopSpinnerSuccess,
|
|
26
|
+
createSpinnerTextUpdater
|
|
27
|
+
} = require('./infra-guided-spinners');
|
|
28
|
+
const {
|
|
29
|
+
SEPARATOR,
|
|
30
|
+
title,
|
|
31
|
+
logSection,
|
|
32
|
+
logFooterStart,
|
|
33
|
+
logFooterEnd,
|
|
34
|
+
resolvePlatformControllerUrlForDisplay,
|
|
35
|
+
logPlatformReadyFooter,
|
|
36
|
+
logMisoReadyFooter,
|
|
37
|
+
logDataplaneReadyFooter,
|
|
38
|
+
logPlatformSetupDurationHint,
|
|
39
|
+
logGuidedPlatformSetupHeader,
|
|
40
|
+
logUpPlatformForceCleanSummary,
|
|
41
|
+
logStoppedServices
|
|
42
|
+
} = require('./infra-guided-footers');
|
|
26
43
|
const healthCheck = require('../utils/health-check');
|
|
27
44
|
const { prepareUrlsLocalRegistryForUpPlatform } = require('../commands/up-common');
|
|
28
45
|
const { assertDevInfraUp } = require('../commands/dev-infra-gate');
|
|
@@ -30,32 +47,6 @@ const {
|
|
|
30
47
|
emitSystemBuilderAppManifestLineIfTTY
|
|
31
48
|
} = require('../utils/manifest-source-emit');
|
|
32
49
|
|
|
33
|
-
const SEPARATOR = '────────────────────────────────────────';
|
|
34
|
-
|
|
35
|
-
function title(text) {
|
|
36
|
-
return chalk.bold(text);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function shouldUseSpinner() {
|
|
40
|
-
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function startSpinner(text) {
|
|
44
|
-
if (!shouldUseSpinner()) {
|
|
45
|
-
logger.log(formatProgress(text));
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
return ora({ text, spinner: 'dots' }).start();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function stopSpinnerSuccess(spinner, text) {
|
|
52
|
-
if (!spinner) {
|
|
53
|
-
logger.log(formatSuccessLine(text));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
spinner.succeed(text);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
50
|
async function getRunningContainerNameForApp(appName) {
|
|
60
51
|
const apps = await infra.getAppStatus();
|
|
61
52
|
const hit = (apps || []).find(a => a && a.name === appName && a.container);
|
|
@@ -124,84 +115,10 @@ function resolveInfraHost(remoteServer) {
|
|
|
124
115
|
const u = new URL(withScheme);
|
|
125
116
|
return u.hostname || 'localhost';
|
|
126
117
|
} catch {
|
|
127
|
-
// Fallback for non-URL strings.
|
|
128
118
|
return raw.replace(/^https?:\/\//i, '').split('/')[0].split(':')[0] || 'localhost';
|
|
129
119
|
}
|
|
130
120
|
}
|
|
131
121
|
|
|
132
|
-
/**
|
|
133
|
-
* Prefer stored platform controller URL, then compute from routing rules.
|
|
134
|
-
* @async
|
|
135
|
-
* @returns {Promise<string>}
|
|
136
|
-
*/
|
|
137
|
-
async function resolvePlatformControllerUrlForDisplay() {
|
|
138
|
-
const stored = await config.getPlatformControllerUrl();
|
|
139
|
-
if (stored) {
|
|
140
|
-
return stored;
|
|
141
|
-
}
|
|
142
|
-
return resolvePlatformControllerUrl();
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function logSection(label, value) {
|
|
146
|
-
logger.log(title(label));
|
|
147
|
-
logger.log(` ${value}`);
|
|
148
|
-
logger.log('');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function logFooterStart(label) {
|
|
152
|
-
logger.log('');
|
|
153
|
-
logger.log(SEPARATOR);
|
|
154
|
-
logger.log(title(label));
|
|
155
|
-
logger.log(SEPARATOR);
|
|
156
|
-
logger.log('');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function logFooterEnd() {
|
|
160
|
-
logger.log(SEPARATOR);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function logPlatformReadyFooter() {
|
|
164
|
-
const [controllerUrl, dataplaneUrl, keycloakUrl] = await Promise.all([
|
|
165
|
-
resolvePlatformControllerUrlForDisplay(),
|
|
166
|
-
computeAppBaseUrl('dataplane'),
|
|
167
|
-
computeAppBaseUrl('keycloak')
|
|
168
|
-
]);
|
|
169
|
-
const cfg = await config.getConfig();
|
|
170
|
-
const env = (cfg && cfg.environment) ? cfg.environment : 'dev';
|
|
171
|
-
|
|
172
|
-
logFooterStart('Platform Ready');
|
|
173
|
-
logSection('Environment', env);
|
|
174
|
-
logSection('Miso Controller', controllerUrl);
|
|
175
|
-
logSection('Dataplane (DEV)', dataplaneUrl);
|
|
176
|
-
logSection('Keycloak', keycloakUrl);
|
|
177
|
-
logSection('API Documentation', `${dataplaneUrl}/api/docs`);
|
|
178
|
-
logSection('Knowledge Base', 'https://docs.aifabrix.ai');
|
|
179
|
-
logSection('Getting Started', 'https://docs.aifabrix.ai/get-started');
|
|
180
|
-
logFooterEnd();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
async function logMisoReadyFooter() {
|
|
184
|
-
const [controllerUrl, keycloakUrl] = await Promise.all([
|
|
185
|
-
resolvePlatformControllerUrlForDisplay(),
|
|
186
|
-
computeAppBaseUrl('keycloak')
|
|
187
|
-
]);
|
|
188
|
-
logFooterStart('Miso Ready');
|
|
189
|
-
logSection('Miso Controller', controllerUrl);
|
|
190
|
-
logSection('Keycloak', keycloakUrl);
|
|
191
|
-
logFooterEnd();
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async function logDataplaneReadyFooter() {
|
|
195
|
-
const dataplaneUrl = await computeAppBaseUrl('dataplane');
|
|
196
|
-
const cfg = await config.getConfig();
|
|
197
|
-
const env = (cfg && cfg.environment) ? cfg.environment : 'dev';
|
|
198
|
-
logFooterStart('Dataplane Ready');
|
|
199
|
-
logSection('Environment', env);
|
|
200
|
-
logSection('Dataplane (DEV)', dataplaneUrl);
|
|
201
|
-
logSection('API Documentation', `${dataplaneUrl}/api/docs`);
|
|
202
|
-
logFooterEnd();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
122
|
async function getInfraHostAndPorts() {
|
|
206
123
|
const developerId = await config.getDeveloperId();
|
|
207
124
|
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
@@ -269,17 +186,12 @@ async function runGuidedUpMiso(options, handleUpMiso) {
|
|
|
269
186
|
await validateAppErrorLogs(['miso-controller']);
|
|
270
187
|
}
|
|
271
188
|
|
|
272
|
-
/**
|
|
273
|
-
* Infra must be checked before auth: device login talks to the controller, which needs
|
|
274
|
-
* Postgres/Redis when the controller runs locally — a down infra looks like "network error".
|
|
275
|
-
*/
|
|
276
189
|
async function assertDevInfraForGuidedDataplane() {
|
|
277
190
|
const infraSpin = startSpinner('Checking infrastructure...');
|
|
278
191
|
try {
|
|
279
192
|
await assertDevInfraUp({ quietSuccess: true });
|
|
280
193
|
stopSpinnerSuccess(infraSpin, 'Infrastructure is up');
|
|
281
194
|
} catch (infraErr) {
|
|
282
|
-
// Match up-miso: one error block from handleCommandError only (no spinner.fail / extra hints).
|
|
283
195
|
if (infraSpin) infraSpin.stop();
|
|
284
196
|
throw infraErr;
|
|
285
197
|
}
|
|
@@ -293,17 +205,18 @@ async function runGuidedUpDataplane(options, handleUpDataplane) {
|
|
|
293
205
|
logPlatformSetupDurationHint('dataplane');
|
|
294
206
|
logger.log('');
|
|
295
207
|
await assertDevInfraForGuidedDataplane();
|
|
296
|
-
// Avoid an outer long-running spinner here: up-dataplane has multiple internal
|
|
297
|
-
// phases and may print some output. An outer spinner causes "spinner over spinner"
|
|
298
|
-
// rendering artifacts. Instead, do the same single auth step as up-platform, then
|
|
299
|
-
// show one spinner for the dataplane install/run phase.
|
|
300
208
|
await runGuidedAuthStep(handleLogin, {}, { startSpinner, stopSpinnerSuccess });
|
|
301
209
|
|
|
302
210
|
logger.log('');
|
|
303
211
|
emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
|
|
304
212
|
const dpSpin = startSpinner('Starting Dataplane...');
|
|
305
|
-
await
|
|
306
|
-
|
|
213
|
+
const controllerUrl = await resolveGuidedAuthControllerUrl({});
|
|
214
|
+
await runGuidedDataplaneWithAuthRecovery(
|
|
215
|
+
{ ...options, platformInstall: true, skipInfraCheck: true },
|
|
216
|
+
handleUpDataplane,
|
|
217
|
+
handleLogin,
|
|
218
|
+
controllerUrl,
|
|
219
|
+
{ onSpinnerText: createSpinnerTextUpdater(dpSpin) }
|
|
307
220
|
);
|
|
308
221
|
stopSpinnerSuccess(dpSpin, 'Dataplane ready');
|
|
309
222
|
await refreshDataplaneVersionAfterPlatformInstall();
|
|
@@ -311,54 +224,6 @@ async function runGuidedUpDataplane(options, handleUpDataplane) {
|
|
|
311
224
|
await logDataplaneReadyFooter();
|
|
312
225
|
}
|
|
313
226
|
|
|
314
|
-
/**
|
|
315
|
-
* Guided-mode summary after `up-platform --force` (printed under the platform header).
|
|
316
|
-
* @param {{ deviceCleared: number, clientCleared: number, environment: string, defaultControllerUrl: string }} forceSummary
|
|
317
|
-
* @param {string[]} cleanedApps
|
|
318
|
-
*/
|
|
319
|
-
function logUpPlatformForceCleanSummary(forceSummary, cleanedApps) {
|
|
320
|
-
const { isSetupQuietOutput } = require('../utils/setup-quiet-output');
|
|
321
|
-
if (isSetupQuietOutput()) return;
|
|
322
|
-
const check = successGlyph();
|
|
323
|
-
logger.log(chalk.bold('Clean installation files'));
|
|
324
|
-
logger.log(
|
|
325
|
-
chalk.gray(
|
|
326
|
-
` ${check} Cleared ${forceSummary.deviceCleared} device token(s) and ${forceSummary.clientCleared} client token(s)`
|
|
327
|
-
)
|
|
328
|
-
);
|
|
329
|
-
logger.log(chalk.gray(` ${check} Environment set to ${forceSummary.environment}`));
|
|
330
|
-
logger.log(
|
|
331
|
-
chalk.gray(
|
|
332
|
-
` ${check} Default controller set to ${forceSummary.defaultControllerUrl} (run aifabrix login after platform is up)`
|
|
333
|
-
)
|
|
334
|
-
);
|
|
335
|
-
for (const appName of cleanedApps || []) {
|
|
336
|
-
logger.log(chalk.gray(` ${check} Cleaned builder/${appName}`));
|
|
337
|
-
}
|
|
338
|
-
logger.log('');
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Yellow notice before long-running platform/dataplane install steps (setup and guided up-*).
|
|
343
|
-
* @param {'platform'|'dataplane'} [scope]
|
|
344
|
-
*/
|
|
345
|
-
function logPlatformSetupDurationHint(scope = 'platform') {
|
|
346
|
-
const line =
|
|
347
|
-
scope === 'dataplane'
|
|
348
|
-
? 'Installation may take a few minutes. Authenticating and starting Dataplane — please wait.'
|
|
349
|
-
: 'Installation may take a few minutes. Keycloak, Miso Controller, and Dataplane are starting — please wait.';
|
|
350
|
-
logger.log(chalk.yellow(`⚠ ${line}`));
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function logGuidedPlatformSetupHeader() {
|
|
354
|
-
logger.log('');
|
|
355
|
-
logger.log(title('AI Fabrix Platform Setup'));
|
|
356
|
-
logger.log(SEPARATOR);
|
|
357
|
-
logger.log('');
|
|
358
|
-
logPlatformSetupDurationHint('platform');
|
|
359
|
-
logger.log('');
|
|
360
|
-
}
|
|
361
|
-
|
|
362
227
|
async function refreshDataplaneVersionAfterPlatformInstall() {
|
|
363
228
|
try {
|
|
364
229
|
const { tryRefreshDataplaneVersionAfterLogin } = require('../commands/auth-status-dataplane-version');
|
|
@@ -386,20 +251,27 @@ async function runGuidedDataplaneInstallPhase(options, handleUpDataplane, handle
|
|
|
386
251
|
logger.log('');
|
|
387
252
|
emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
|
|
388
253
|
const dpSpin = startSpinner('Starting Dataplane...');
|
|
254
|
+
const onSpinnerText = createSpinnerTextUpdater(dpSpin);
|
|
389
255
|
const platformMode = await config.getSetupPlatformMode();
|
|
390
256
|
if (platformMode === 'full') {
|
|
391
|
-
// Full platform: three dataplane containers + per-env registrations.
|
|
392
257
|
const envKeys = ['pro', 'dev', 'tst'];
|
|
393
258
|
for (const envKey of envKeys) {
|
|
394
259
|
await runGuidedDataplaneWithAuthRecovery(
|
|
395
260
|
{ ...options, envKey, runEnvKey: envKey, allowNonDev: true },
|
|
396
261
|
handleUpDataplane,
|
|
397
262
|
handleLoginFn,
|
|
398
|
-
controllerUrl
|
|
263
|
+
controllerUrl,
|
|
264
|
+
{ onSpinnerText }
|
|
399
265
|
);
|
|
400
266
|
}
|
|
401
267
|
} else {
|
|
402
|
-
await runGuidedDataplaneWithAuthRecovery(
|
|
268
|
+
await runGuidedDataplaneWithAuthRecovery(
|
|
269
|
+
options,
|
|
270
|
+
handleUpDataplane,
|
|
271
|
+
handleLoginFn,
|
|
272
|
+
controllerUrl,
|
|
273
|
+
{ onSpinnerText }
|
|
274
|
+
);
|
|
403
275
|
}
|
|
404
276
|
stopSpinnerSuccess(dpSpin, 'Dataplane ready');
|
|
405
277
|
await refreshDataplaneVersionAfterPlatformInstall();
|
|
@@ -423,6 +295,7 @@ async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, han
|
|
|
423
295
|
const realmSpin = startSpinner('Waiting for Keycloak realm (aifabrix)...');
|
|
424
296
|
await waitForKeycloakRealmReady({ timeoutSeconds: 180 });
|
|
425
297
|
stopSpinnerSuccess(realmSpin, 'Keycloak realm ready');
|
|
298
|
+
logger.log('');
|
|
426
299
|
}
|
|
427
300
|
await runGuidedAuthStep(handleLogin, authOpts, { startSpinner, stopSpinnerSuccess });
|
|
428
301
|
const controllerUrl = await resolveGuidedAuthControllerUrl(authOpts);
|
|
@@ -431,17 +304,7 @@ async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, han
|
|
|
431
304
|
await logPlatformReadyFooter();
|
|
432
305
|
}
|
|
433
306
|
|
|
434
|
-
function
|
|
435
|
-
logger.log(title('Stopped'));
|
|
436
|
-
logger.log(' postgres');
|
|
437
|
-
logger.log(' redis');
|
|
438
|
-
if (cfg.pgadmin !== false) logger.log(' pgadmin');
|
|
439
|
-
if (cfg.redisCommander !== false) logger.log(' redis-commander');
|
|
440
|
-
if (cfg.traefik) logger.log(' traefik');
|
|
441
|
-
logger.log('');
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
async function runGuidedDownInfra(appName, options, infra, appLib) {
|
|
307
|
+
async function runGuidedDownInfra(appName, options, infraMod, appLib) {
|
|
445
308
|
logger.log('');
|
|
446
309
|
logger.log(title('AI Fabrix Shutdown'));
|
|
447
310
|
logger.log(SEPARATOR);
|
|
@@ -453,8 +316,8 @@ async function runGuidedDownInfra(appName, options, infra, appLib) {
|
|
|
453
316
|
await appLib.downApp(appName, { volumes: !!options.volumes });
|
|
454
317
|
return;
|
|
455
318
|
}
|
|
456
|
-
if (options.volumes) await
|
|
457
|
-
else await
|
|
319
|
+
if (options.volumes) await infraMod.stopInfraWithVolumes();
|
|
320
|
+
else await infraMod.stopInfra();
|
|
458
321
|
});
|
|
459
322
|
stopSpinnerSuccess(spin, 'Infrastructure stopped');
|
|
460
323
|
|
|
@@ -479,4 +342,3 @@ module.exports = {
|
|
|
479
342
|
runGuidedUpPlatform,
|
|
480
343
|
runGuidedDownInfra
|
|
481
344
|
};
|
|
482
|
-
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -297,7 +297,7 @@ Examples:
|
|
|
297
297
|
.option('--reload', 'In dev: mount workspace into container (Mutagen only if docker-endpoint is a remote host)')
|
|
298
298
|
.option(
|
|
299
299
|
'--proxy',
|
|
300
|
-
'Use Traefik/front-door public URL hints when infra has Traefik
|
|
300
|
+
'Use Traefik/front-door public URL hints when infra has Traefik (ignored when traefik: false in config)',
|
|
301
301
|
true
|
|
302
302
|
)
|
|
303
303
|
.option(
|
package/lib/cli/setup-dev.js
CHANGED
|
@@ -39,6 +39,7 @@ Examples:
|
|
|
39
39
|
$ aifabrix dev add --developer-id 02 --name "Jane Doe" --email jane@example.com
|
|
40
40
|
$ aifabrix dev add --developer-id 03 --name "Build admin" --email admin@example.com --groups admin,developer
|
|
41
41
|
$ aifabrix dev add --developer-id 04 --name "CI user" --email ci@example.com --groups secret-manager,developer
|
|
42
|
+
$ aifabrix dev add --developer-id 05 --name "Integrator" --email integrator@example.com --groups developer,secret-reader
|
|
42
43
|
|
|
43
44
|
Requires a configured remote Builder Server and an admin client certificate (same machine setup as dev list / dev pin). After add, run dev pin <id> once to create a PIN for aifabrix dev init.
|
|
44
45
|
`;
|
|
@@ -274,7 +275,7 @@ function setupDevListAddCommands(dev) {
|
|
|
274
275
|
.requiredOption('--developer-id <id>', 'Unique id, digits only (e.g. 02); used with dev init --developer-id')
|
|
275
276
|
.requiredOption('--name <name>', 'Display name')
|
|
276
277
|
.requiredOption('--email <email>', 'Email address')
|
|
277
|
-
.option('--groups <items>', 'Comma-separated roles: admin, secret-manager, developer, docker', 'developer')
|
|
278
|
+
.option('--groups <items>', 'Comma-separated roles: admin, secret-manager, secret-reader, developer, docker', 'developer')
|
|
278
279
|
.addHelpText('after', DEV_ADD_HELP_AFTER)
|
|
279
280
|
.action(async(options) => {
|
|
280
281
|
try {
|
|
@@ -297,7 +298,7 @@ function setupDevUpdatePinDeleteCommands(dev) {
|
|
|
297
298
|
.option('--developer-id <id>', 'Developer ID (same as dev add)')
|
|
298
299
|
.option('--name <name>', 'Display name')
|
|
299
300
|
.option('--email <email>', 'Email address')
|
|
300
|
-
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, developer, docker)')
|
|
301
|
+
.option('--groups <items>', 'Comma-separated groups (admin, secret-manager, secret-reader, developer, docker)')
|
|
301
302
|
.addHelpText('after', DEV_UPDATE_HELP_AFTER)
|
|
302
303
|
.action(async(developerId, options) => {
|
|
303
304
|
try {
|
package/lib/cli/setup-infra.js
CHANGED
|
@@ -34,7 +34,8 @@ const {
|
|
|
34
34
|
|
|
35
35
|
const {
|
|
36
36
|
computeEffectiveInfraOptionalFlags,
|
|
37
|
-
persistMissingInfraOptionalServiceFlags
|
|
37
|
+
persistMissingInfraOptionalServiceFlags,
|
|
38
|
+
resolveEffectiveTlsEnabled
|
|
38
39
|
} = require('../utils/infra-optional-service-flags');
|
|
39
40
|
|
|
40
41
|
const UP_INFRA_HELP_AFTER = `
|
|
@@ -137,7 +138,7 @@ async function runUpInfraCommand(options) {
|
|
|
137
138
|
const { passwordBundleFromCliOptions } = require('../core/admin-secrets-bundle');
|
|
138
139
|
const bundle = passwordBundleFromCliOptions(options);
|
|
139
140
|
const adminPass = options.adminPassword || options.adminPwd;
|
|
140
|
-
const tlsEnabledEffective = cfg
|
|
141
|
+
const tlsEnabledEffective = resolveEffectiveTlsEnabled(cfg, options);
|
|
141
142
|
const effective = computeEffectiveInfraOptionalFlags(cfg, options);
|
|
142
143
|
await infra.startInfra(developerId, {
|
|
143
144
|
traefik: effective.traefik,
|