@aifabrix/builder 2.44.5 → 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 +8 -4
- package/.cursor/rules/project-rules.mdc +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +104 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -3
- 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 +132 -118
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- 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/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- 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 +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- 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/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- 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/integration-file-backup.js +74 -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/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -0
- 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 -388
- 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 +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
package/lib/app/push.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatSuccessParagraph,
|
|
4
|
+
sectionTitle,
|
|
5
|
+
headerKeyValue,
|
|
6
|
+
metadata,
|
|
7
|
+
formatWarningLine,
|
|
8
|
+
formatNextActions
|
|
9
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
10
|
/**
|
|
3
11
|
* Application Push Utilities
|
|
4
12
|
*
|
|
@@ -9,9 +17,9 @@ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test
|
|
|
9
17
|
* @version 2.0.0
|
|
10
18
|
*/
|
|
11
19
|
|
|
12
|
-
const chalk = require('chalk');
|
|
13
20
|
const pushUtils = require('../deployment/push');
|
|
14
21
|
const logger = require('../utils/logger');
|
|
22
|
+
const { detectAppType } = require('../utils/paths');
|
|
15
23
|
|
|
16
24
|
/**
|
|
17
25
|
* Validate application name format
|
|
@@ -150,6 +158,33 @@ async function authenticateWithRegistry(registry) {
|
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Layout-aligned notice when push is skipped for external integrations.
|
|
163
|
+
* @param {string} appName
|
|
164
|
+
*/
|
|
165
|
+
function logExternalIntegrationPushNotice(appName) {
|
|
166
|
+
logger.log('');
|
|
167
|
+
logger.log(sectionTitle('Push'));
|
|
168
|
+
logger.log(headerKeyValue('Application:', appName));
|
|
169
|
+
logger.log(formatWarningLine('External integrations have no application image to push.'));
|
|
170
|
+
logger.log(metadata('Use upload or deploy for integration artifacts.'));
|
|
171
|
+
logger.log(formatNextActions([`aifabrix upload ${appName}`, `aifabrix deploy ${appName}`]));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Header after validation passes for a regular (non-external) push.
|
|
176
|
+
* @param {string} appName
|
|
177
|
+
* @param {string} registry
|
|
178
|
+
* @param {string} imageName
|
|
179
|
+
*/
|
|
180
|
+
function logPushCommandHeader(appName, registry, imageName) {
|
|
181
|
+
logger.log('');
|
|
182
|
+
logger.log(sectionTitle('Push'));
|
|
183
|
+
logger.log(headerKeyValue('Application:', appName));
|
|
184
|
+
logger.log(metadata(`Registry: ${registry}`));
|
|
185
|
+
logger.log(metadata(`Repository: ${imageName}`));
|
|
186
|
+
}
|
|
187
|
+
|
|
153
188
|
/**
|
|
154
189
|
* Pushes image tags to registry
|
|
155
190
|
* @async
|
|
@@ -171,7 +206,9 @@ async function pushImageTags(imageName, registry, tags) {
|
|
|
171
206
|
(errorMessage.includes('authentication') && errorMessage.includes('401'));
|
|
172
207
|
|
|
173
208
|
if (isAuthError) {
|
|
174
|
-
logger.log(
|
|
209
|
+
logger.log(
|
|
210
|
+
formatWarningLine('Registry authentication expired; re-authenticating, then retrying push.')
|
|
211
|
+
);
|
|
175
212
|
await authenticateWithRegistry(registry);
|
|
176
213
|
// Retry push after re-authentication
|
|
177
214
|
await Promise.all(tags.map(async(tag) => {
|
|
@@ -190,9 +227,9 @@ async function pushImageTags(imageName, registry, tags) {
|
|
|
190
227
|
* @param {Array<string>} tags - Image tags
|
|
191
228
|
*/
|
|
192
229
|
function displayPushResults(registry, imageName, tags) {
|
|
193
|
-
logger.log(formatSuccessParagraph(`
|
|
194
|
-
logger.log(
|
|
195
|
-
logger.log(
|
|
230
|
+
logger.log(formatSuccessParagraph(`Pushed ${tags.length} tag(s) to ${registry}`));
|
|
231
|
+
logger.log(headerKeyValue('Image:', `${registry}/${imageName}`));
|
|
232
|
+
logger.log(headerKeyValue('Tags:', tags.join(', ')));
|
|
196
233
|
}
|
|
197
234
|
|
|
198
235
|
/**
|
|
@@ -204,38 +241,25 @@ function displayPushResults(registry, imageName, tags) {
|
|
|
204
241
|
* @returns {Promise<void>} Resolves when push is complete
|
|
205
242
|
*/
|
|
206
243
|
async function pushApp(appName, options = {}) {
|
|
207
|
-
// Check if app type is external - skip push
|
|
208
|
-
const { detectAppType } = require('../utils/paths');
|
|
209
244
|
try {
|
|
210
245
|
const { isExternal } = await detectAppType(appName);
|
|
211
246
|
if (isExternal) {
|
|
212
|
-
|
|
247
|
+
logExternalIntegrationPushNotice(appName);
|
|
213
248
|
return;
|
|
214
249
|
}
|
|
215
|
-
} catch (
|
|
250
|
+
} catch (_error) {
|
|
216
251
|
// If detection fails, continue with normal push
|
|
217
252
|
// (detectAppType throws if app doesn't exist, which is fine for push command)
|
|
218
253
|
}
|
|
219
254
|
try {
|
|
220
|
-
// Validate app name
|
|
221
255
|
validateAppName(appName);
|
|
222
|
-
|
|
223
|
-
// Load configuration
|
|
224
256
|
const { registry, imageName } = await loadPushConfig(appName, options);
|
|
225
|
-
|
|
226
|
-
// Validate push configuration
|
|
227
257
|
await validatePushConfig(registry, imageName, appName);
|
|
228
|
-
|
|
229
|
-
// Authenticate with registry
|
|
258
|
+
logPushCommandHeader(appName, registry, imageName);
|
|
230
259
|
await authenticateWithRegistry(registry);
|
|
231
|
-
|
|
232
|
-
// Push image tags
|
|
233
260
|
const tags = options.tag ? options.tag.split(',').map(t => t.trim()) : ['latest'];
|
|
234
261
|
await pushImageTags(imageName, registry, tags);
|
|
235
|
-
|
|
236
|
-
// Display results
|
|
237
262
|
displayPushResults(registry, imageName, tags);
|
|
238
|
-
|
|
239
263
|
} catch (error) {
|
|
240
264
|
throw new Error(`Failed to push application: ${error.message}`);
|
|
241
265
|
}
|
|
@@ -245,4 +269,3 @@ module.exports = {
|
|
|
245
269
|
pushApp,
|
|
246
270
|
validateAppName
|
|
247
271
|
};
|
|
248
|
-
|
package/lib/app/register.js
CHANGED
|
@@ -94,19 +94,20 @@ async function saveLocalCredentials(responseData, apiUrl) {
|
|
|
94
94
|
await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
|
|
95
95
|
await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
|
|
96
96
|
|
|
97
|
-
// Update env.template
|
|
97
|
+
// Update env.template (kv:// refs only; no resolved values touch disk)
|
|
98
98
|
await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, apiUrl);
|
|
99
99
|
|
|
100
|
-
//
|
|
100
|
+
// Resolve in-memory so any missing-secret / kv:// error surfaces here, but never
|
|
101
|
+
// materialize <appPath>/.env or envOutputPath — that is only done by `aifabrix resolve`.
|
|
101
102
|
try {
|
|
102
|
-
await generateEnvFile(registeredAppKey, null, 'local');
|
|
103
|
-
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
103
|
+
await generateEnvFile(registeredAppKey, null, 'local', true, { noWrite: true });
|
|
104
104
|
} catch (error) {
|
|
105
|
-
logger.warn(chalk.yellow(`⚠ Could not
|
|
105
|
+
logger.warn(chalk.yellow(`⚠ Could not validate env resolution: ${error.message}`));
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
109
|
-
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL
|
|
109
|
+
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL'));
|
|
110
|
+
logger.log(formatSuccessLine('Run "aifabrix resolve ' + registeredAppKey + '" to materialize an on-disk .env\n'));
|
|
110
111
|
} catch (error) {
|
|
111
112
|
logger.warn(chalk.yellow(`⚠ Could not save credentials locally: ${error.message}`));
|
|
112
113
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* After `docker restart`, describe dev workspace mounts for developer clarity.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Bind mount vs remote-engine hints (aligns with run --reload messaging)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
14
|
+
const { sectionTitle, headerKeyValue, metadata } = require('../utils/cli-test-layout-chalk');
|
|
15
|
+
const { isReloadBindMountOnEngineHost } = require('../utils/docker-reload-mount');
|
|
16
|
+
const { isApplicationsReloadDefaultOn } = require('../utils/applications-config-defaults');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown} mounts - docker inspect .Mounts
|
|
20
|
+
* @returns {{ Type: string, Source: string, Destination: string }|null}
|
|
21
|
+
*/
|
|
22
|
+
function findAppBindMount(mounts) {
|
|
23
|
+
if (!Array.isArray(mounts)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const hit = mounts.find((m) => m && m.Type === 'bind' && m.Destination === '/app');
|
|
27
|
+
return hit && typeof hit.Source === 'string' ? hit : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} stdout
|
|
32
|
+
* @returns {unknown|null}
|
|
33
|
+
*/
|
|
34
|
+
function parseInspectMountsStdout(stdout) {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(String(stdout || '').trim());
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} containerName
|
|
44
|
+
* @returns {Promise<unknown|null>}
|
|
45
|
+
*/
|
|
46
|
+
async function fetchContainerMountsJson(containerName) {
|
|
47
|
+
try {
|
|
48
|
+
const { stdout } = await execWithDockerEnv(
|
|
49
|
+
`docker inspect --format '{{json .Mounts}}' ${containerName}`,
|
|
50
|
+
{ maxBuffer: 2 * 1024 * 1024 }
|
|
51
|
+
);
|
|
52
|
+
return parseInspectMountsStdout(stdout);
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Log saved reload default for the app (same source as `aifabrix run` dev persist / `aifabrix show`).
|
|
60
|
+
* @param {string|null|undefined} appName - Application key (e.g. miso-controller)
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
async function logReloadConfigSummaryForRestart(appName) {
|
|
64
|
+
if (!appName || typeof appName !== 'string') {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const userCfg = await config.getConfig();
|
|
68
|
+
const reloadOn = isApplicationsReloadDefaultOn(userCfg, appName);
|
|
69
|
+
logger.log('');
|
|
70
|
+
logger.log(sectionTitle('Reload (config)'));
|
|
71
|
+
logger.log(
|
|
72
|
+
headerKeyValue(
|
|
73
|
+
'Next dev run:',
|
|
74
|
+
reloadOn ? 'reload on (applications.<app>.reload in config)' : 'reload off'
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
logger.log(
|
|
78
|
+
metadata(
|
|
79
|
+
'Persisted when you last ran this app in dev with or without --reload. Use aifabrix show <app> to inspect.'
|
|
80
|
+
)
|
|
81
|
+
);
|
|
82
|
+
logger.log('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Log workspace transport after a successful container restart (mounts unchanged).
|
|
87
|
+
* @param {string} containerName - Docker container name
|
|
88
|
+
* @param {string|null} [appName] - Application key; when set, logs reload default from config after mount info
|
|
89
|
+
* @returns {Promise<void>}
|
|
90
|
+
*/
|
|
91
|
+
async function logRestartDevMountSummary(containerName, appName = null) {
|
|
92
|
+
if (!containerName || typeof containerName !== 'string') {
|
|
93
|
+
await logReloadConfigSummaryForRestart(appName);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const mounts = await fetchContainerMountsJson(containerName);
|
|
97
|
+
const appBind = findAppBindMount(mounts);
|
|
98
|
+
if (appBind) {
|
|
99
|
+
const endpoint = await config.getDockerEndpoint();
|
|
100
|
+
const localEngine = isReloadBindMountOnEngineHost(endpoint);
|
|
101
|
+
|
|
102
|
+
logger.log('');
|
|
103
|
+
logger.log(sectionTitle('Dev workspace (unchanged by restart)'));
|
|
104
|
+
if (localEngine) {
|
|
105
|
+
logger.log(headerKeyValue('Transport:', 'Direct bind mount on the Docker host (no Mutagen).'));
|
|
106
|
+
logger.log(headerKeyValue('Host path → container:', `${appBind.Source} → /app`));
|
|
107
|
+
logger.log(metadata('Edits under the host path are visible inside the container immediately.'));
|
|
108
|
+
} else {
|
|
109
|
+
logger.log(
|
|
110
|
+
headerKeyValue(
|
|
111
|
+
'Transport:',
|
|
112
|
+
'Bind mount on the Docker engine (see path below; Mutagen may sync to this path when using --reload).'
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
logger.log(headerKeyValue('Engine path → container:', `${appBind.Source} → /app`));
|
|
116
|
+
}
|
|
117
|
+
logger.log('');
|
|
118
|
+
}
|
|
119
|
+
await logReloadConfigSummaryForRestart(appName);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
findAppBindMount,
|
|
124
|
+
logReloadConfigSummaryForRestart,
|
|
125
|
+
logRestartDevMountSummary
|
|
126
|
+
};
|
package/lib/app/rotate-secret.js
CHANGED
|
@@ -281,20 +281,21 @@ async function saveCredentialsLocally(appKey, credentials, actualControllerUrl)
|
|
|
281
281
|
await saveLocalSecret(clientIdKey, credentials.clientId);
|
|
282
282
|
await saveLocalSecret(clientSecretKey, credentials.clientSecret);
|
|
283
283
|
|
|
284
|
-
// Update env.template if localhost
|
|
284
|
+
// Update env.template if localhost (kv:// refs only; no resolved values touch disk)
|
|
285
285
|
if (isLocalhost(actualControllerUrl)) {
|
|
286
286
|
await updateEnvTemplate(appKey, clientIdKey, clientSecretKey, actualControllerUrl);
|
|
287
287
|
|
|
288
|
-
//
|
|
288
|
+
// Resolve in-memory so any missing-secret / kv:// error surfaces here, but never
|
|
289
|
+
// materialize <appPath>/.env or envOutputPath — that is only done by `aifabrix resolve`.
|
|
289
290
|
try {
|
|
290
|
-
await generateEnvFile(appKey, null, 'local');
|
|
291
|
-
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
291
|
+
await generateEnvFile(appKey, null, 'local', true, { noWrite: true });
|
|
292
292
|
} catch (error) {
|
|
293
|
-
logger.warn(chalk.yellow(`⚠ Could not
|
|
293
|
+
logger.warn(chalk.yellow(`⚠ Could not validate env resolution: ${error.message}`));
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
297
|
-
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL
|
|
297
|
+
logger.log(formatSuccessLine('env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL'));
|
|
298
|
+
logger.log(formatSuccessLine('Run "aifabrix resolve ' + appKey + '" to materialize an on-disk .env\n'));
|
|
298
299
|
} else {
|
|
299
300
|
logger.log(formatSuccessParagraph('Credentials saved to ~/.aifabrix/secrets.local.yaml\n'));
|
|
300
301
|
}
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
formatSuccessLine,
|
|
10
|
+
formatProgress,
|
|
11
|
+
formatWarningLine
|
|
12
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
9
13
|
|
|
10
14
|
const fs = require('fs').promises;
|
|
11
15
|
const chalk = require('chalk');
|
|
@@ -31,8 +35,8 @@ const execAsync = promisify(exec);
|
|
|
31
35
|
async function emitAndRunDockerFallback(appName, appConfig, port, opts) {
|
|
32
36
|
const { debug, runEnvPath, runOptions, misoEnvironment } = opts;
|
|
33
37
|
logger.log(
|
|
34
|
-
|
|
35
|
-
'Docker Compose not found; using docker run (apps without
|
|
38
|
+
formatWarningLine(
|
|
39
|
+
'Docker Compose not found; using docker run (apps without Postgres/Redis only). ' +
|
|
36
40
|
'Install docker-compose-plugin for full compose support.'
|
|
37
41
|
)
|
|
38
42
|
);
|
|
@@ -116,7 +120,7 @@ async function waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, o) {
|
|
|
116
120
|
const displayUrl = (healthUrl && typeof healthUrl === 'string')
|
|
117
121
|
? healthUrl
|
|
118
122
|
: `http://localhost:${port}${healthCheckPath}`;
|
|
119
|
-
logger.log(
|
|
123
|
+
logger.log(formatProgress(`Waiting for healthy: ${displayUrl}…`));
|
|
120
124
|
await healthCheck.waitForHealthCheck(appName, 90, port, appConfig, debug, ro);
|
|
121
125
|
|
|
122
126
|
for (const p of [runEnvPath, runEnvAdminPath]) {
|
|
@@ -124,7 +128,9 @@ async function waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, o) {
|
|
|
124
128
|
try {
|
|
125
129
|
await fs.unlink(p);
|
|
126
130
|
} catch (err) {
|
|
127
|
-
if (err.code !== 'ENOENT')
|
|
131
|
+
if (err.code !== 'ENOENT') {
|
|
132
|
+
logger.log(formatWarningLine(`Could not remove run .env: ${err.message}`));
|
|
133
|
+
}
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
}
|
|
@@ -148,7 +154,7 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
148
154
|
devMountPath = null,
|
|
149
155
|
misoEnvironment = 'dev'
|
|
150
156
|
} = opts;
|
|
151
|
-
logger.log(
|
|
157
|
+
logger.log(formatProgress(`Starting ${appName}…`));
|
|
152
158
|
|
|
153
159
|
let composeCmdBase;
|
|
154
160
|
let usedDockerRunFallback = false;
|
|
@@ -14,9 +14,36 @@ const fs = require('fs').promises;
|
|
|
14
14
|
const fsSync = require('fs');
|
|
15
15
|
const pathsUtil = require('../utils/paths');
|
|
16
16
|
const adminSecrets = require('../core/admin-secrets');
|
|
17
|
+
const secretsEnsure = require('../core/secrets-ensure');
|
|
17
18
|
const secretsEnvWrite = require('../core/secrets-env-write');
|
|
18
19
|
const { getContainerPort } = require('../utils/port-resolver');
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Backfill only secrets needed for `aifabrix run <app>`: kv:// keys from that app's env.template
|
|
23
|
+
* (catalog defaults via placeholderContext). Does not run full `ensureInfraSecrets`.
|
|
24
|
+
*
|
|
25
|
+
* Docker Postgres admin password for compose/db-init comes from **admin-secrets.env** (see
|
|
26
|
+
* `ensureAdminSecrets` / `readAndDecryptAdminSecrets`), not from `postgres-passwordKeyVault` on every run.
|
|
27
|
+
* The kv key is used when **generating** admin-secrets (e.g. first `up-infra`) via `generateAdminSecretsEnv`;
|
|
28
|
+
* we do not duplicate it here when an admin file already exists.
|
|
29
|
+
*
|
|
30
|
+
* @async
|
|
31
|
+
* @param {string} appName - Builder application key
|
|
32
|
+
* @returns {Promise<void>}
|
|
33
|
+
*/
|
|
34
|
+
async function ensureRunSecretsForApp(appName) {
|
|
35
|
+
const placeholderContext = secretsEnsure.buildInfraPlaceholderContext({});
|
|
36
|
+
const userSecretsPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
37
|
+
const templatePath = path.join(pathsUtil.getBuilderPath(appName), 'env.template');
|
|
38
|
+
if (fsSync.existsSync(templatePath)) {
|
|
39
|
+
await secretsEnsure.ensureSecretsFromEnvTemplate(templatePath, {
|
|
40
|
+
placeholderContext,
|
|
41
|
+
preferredFilePath: userSecretsPath,
|
|
42
|
+
useMergedSecretsForMissingKeys: true
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
20
47
|
/**
|
|
21
48
|
* Clean applications directory: remove generated docker-compose.yaml and .env.* files.
|
|
22
49
|
* @param {string|number} developerId - Developer ID
|
|
@@ -204,6 +231,7 @@ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId
|
|
|
204
231
|
const ensureAdminSecretsFn = typeof infra.ensureAdminSecrets === 'function'
|
|
205
232
|
? infra.ensureAdminSecrets
|
|
206
233
|
: require('../infrastructure/helpers').ensureAdminSecrets;
|
|
234
|
+
await ensureRunSecretsForApp(appName);
|
|
207
235
|
await ensureAdminSecretsFn();
|
|
208
236
|
const adminObj = await adminSecrets.readAndDecryptAdminSecrets();
|
|
209
237
|
const runEnvKey = scopeOpts && scopeOpts.runEnvKey ? String(scopeOpts.runEnvKey).toLowerCase() : 'dev';
|
|
@@ -248,5 +276,6 @@ function assertNoPasswordLiteralsInCompose(composeContent) {
|
|
|
248
276
|
module.exports = {
|
|
249
277
|
cleanApplicationsDir,
|
|
250
278
|
buildMergedRunEnvAndWrite,
|
|
251
|
-
assertNoPasswordLiteralsInCompose
|
|
279
|
+
assertNoPasswordLiteralsInCompose,
|
|
280
|
+
ensureRunSecretsForApp
|
|
252
281
|
};
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatSuccessParagraph,
|
|
4
|
+
formatProgress,
|
|
5
|
+
formatNextActions,
|
|
6
|
+
headerKeyValue,
|
|
7
|
+
metadata,
|
|
8
|
+
infoLine
|
|
9
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
10
|
/**
|
|
3
11
|
* AI Fabrix Builder - App Run Helpers
|
|
4
12
|
*
|
|
@@ -28,6 +36,9 @@ const { resolveRunImage } = require('./run-resolve-image');
|
|
|
28
36
|
const { startContainer } = require('./run-container-start');
|
|
29
37
|
const { resolveEnvOutputPath, writeEnvOutputForReload, writeEnvOutputForLocal } = require('../utils/env-copy');
|
|
30
38
|
const { resolveVersionForApp } = require('../utils/image-version');
|
|
39
|
+
const healthCheckUtil = require('../utils/health-check');
|
|
40
|
+
const { computeTraefikPublicAppUrl } = require('../utils/health-check-url');
|
|
41
|
+
const { isFrontDoorRoutingEnabledInDoc } = require('../utils/url-declarative-vdir-inactive-env');
|
|
31
42
|
|
|
32
43
|
/** Template apps (keycloak, miso-controller, dataplane) - never update application config when running */
|
|
33
44
|
const TEMPLATE_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
|
|
@@ -62,7 +73,7 @@ function checkBuilderDirectory(appName) {
|
|
|
62
73
|
|
|
63
74
|
/**
|
|
64
75
|
* Load and validate config file exists
|
|
65
|
-
* Uses paths.getBuilderPath
|
|
76
|
+
* Uses paths.getBuilderPath (cwd / material `builder/` only; no `AIFABRIX_BUILDER_DIR` override).
|
|
66
77
|
* @param {string} appName - Application name
|
|
67
78
|
* @returns {Object} Application configuration
|
|
68
79
|
* @throws {Error} If config file not found
|
|
@@ -179,7 +190,7 @@ async function checkPrerequisites(appName, appConfig, debug = false, skipInfraCh
|
|
|
179
190
|
logger.log(chalk.gray(`[DEBUG] Image name: ${imageName}, tag: ${imageTag}`));
|
|
180
191
|
}
|
|
181
192
|
|
|
182
|
-
logger.log(
|
|
193
|
+
logger.log(formatProgress(`Checking image ${fullImageName}…`));
|
|
183
194
|
const imageExists = await checkImageExists(imageName, imageTag, debug);
|
|
184
195
|
if (!imageExists) {
|
|
185
196
|
const isTemplateApp = TEMPLATE_APP_KEYS.includes(appName);
|
|
@@ -208,12 +219,12 @@ async function checkInfraHealthOrThrow(debug, appConfig) {
|
|
|
208
219
|
const requirements = getAppInfraRequirements(appConfig);
|
|
209
220
|
if (requirements && !requirements.needsPostgres && !requirements.needsRedis) {
|
|
210
221
|
logger.log(
|
|
211
|
-
|
|
222
|
+
infoLine('Skipping Postgres/Redis health check (not required by this application).')
|
|
212
223
|
);
|
|
213
224
|
return;
|
|
214
225
|
}
|
|
215
226
|
|
|
216
|
-
logger.log(
|
|
227
|
+
logger.log(formatProgress('Checking infrastructure health…'));
|
|
217
228
|
const infra = require('../infrastructure');
|
|
218
229
|
const healthOptions =
|
|
219
230
|
requirements === null
|
|
@@ -280,7 +291,7 @@ function calculateComposePort(options, appConfig, developerId) {
|
|
|
280
291
|
* @returns {Promise<string>} Path to compose file
|
|
281
292
|
*/
|
|
282
293
|
async function generateComposeFile(appName, appConfig, composeOptions, devDir) {
|
|
283
|
-
logger.log(
|
|
294
|
+
logger.log(formatProgress('Generating Docker Compose configuration…'));
|
|
284
295
|
const composeContent = await composeGenerator.generateDockerCompose(appName, appConfig, composeOptions);
|
|
285
296
|
runEnvCompose.assertNoPasswordLiteralsInCompose(composeContent);
|
|
286
297
|
const tempComposePath = path.join(devDir, 'docker-compose.yaml');
|
|
@@ -289,12 +300,15 @@ async function generateComposeFile(appName, appConfig, composeOptions, devDir) {
|
|
|
289
300
|
}
|
|
290
301
|
|
|
291
302
|
/**
|
|
292
|
-
* Writes .
|
|
303
|
+
* Writes `build.envOutputPath` after each `aifabrix run`: **local**-flavored env for the host/IDE
|
|
304
|
+
* when not using `--reload`; with **`--reload`**, merges container `.env.run` into the existing file
|
|
305
|
+
* (preserves resolve comments) without appending run-only keys missing from the template.
|
|
306
|
+
*
|
|
293
307
|
* @async
|
|
294
308
|
* @param {string} appName - Application name
|
|
295
309
|
* @param {Object} appConfig - Application configuration
|
|
296
|
-
* @param {string} runEnvPath - Path to
|
|
297
|
-
* @param {Object} options - Run options (reload
|
|
310
|
+
* @param {string} runEnvPath - Path to `.env.run`
|
|
311
|
+
* @param {Object} options - Run options (`reload`, `skipEnvOutputPath`)
|
|
298
312
|
*/
|
|
299
313
|
async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, options) {
|
|
300
314
|
if (options && options.skipEnvOutputPath === true) return;
|
|
@@ -308,8 +322,8 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
|
|
|
308
322
|
if (!fsSync.existsSync(outputDir)) {
|
|
309
323
|
await fs.mkdir(outputDir, { recursive: true });
|
|
310
324
|
}
|
|
311
|
-
if (options.reload) {
|
|
312
|
-
await writeEnvOutputForReload(outputPath, runEnvPath);
|
|
325
|
+
if (options && options.reload === true) {
|
|
326
|
+
await writeEnvOutputForReload(outputPath, runEnvPath, appName);
|
|
313
327
|
} else {
|
|
314
328
|
await writeEnvOutputForLocal(appName, outputPath);
|
|
315
329
|
}
|
|
@@ -338,7 +352,7 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
338
352
|
);
|
|
339
353
|
|
|
340
354
|
runEnvCompose.cleanApplicationsDir(developerId);
|
|
341
|
-
logger.log(
|
|
355
|
+
logger.log(formatProgress('Building merged .env (admin + app secrets)…'));
|
|
342
356
|
const { runEnvPath, runEnvAdminPath } = await runEnvCompose.buildMergedRunEnvAndWrite(
|
|
343
357
|
appName,
|
|
344
358
|
appConfig,
|
|
@@ -352,7 +366,8 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
352
366
|
envFilePath: runEnvPath,
|
|
353
367
|
dbInitEnvFilePath: runEnvAdminPath,
|
|
354
368
|
effectiveEnvironmentScopedResources,
|
|
355
|
-
env: runEnvKey
|
|
369
|
+
env: runEnvKey,
|
|
370
|
+
omitAppTraefikLabels: userCfg.traefik === false
|
|
356
371
|
};
|
|
357
372
|
composeOptions.port = calculateComposePort(composeOptions, appConfig, developerId);
|
|
358
373
|
const composePath = await generateComposeFile(appName, appConfig, composeOptions, devDir);
|
|
@@ -367,15 +382,39 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
367
382
|
* @param {string} appName - Application name
|
|
368
383
|
* @param {number} port - Application port
|
|
369
384
|
* @param {Object} appConfig - Application configuration (with developerId property)
|
|
385
|
+
* @param {Object|null} [runScopeOpts] - Optional env-scoped container naming
|
|
386
|
+
* @param {Object} [runOptions] - Run options (traefikEnabled / probeViaTraefik for public URL)
|
|
370
387
|
*/
|
|
371
|
-
async function displayRunStatus(appName, port, appConfig, runScopeOpts = null) {
|
|
388
|
+
async function displayRunStatus(appName, port, appConfig, runScopeOpts = null, runOptions = {}) {
|
|
372
389
|
const containerName = containerHelpers.getContainerName(appName, appConfig.developerId, runScopeOpts);
|
|
373
|
-
const
|
|
374
|
-
const
|
|
390
|
+
const ro = runOptions && typeof runOptions === 'object' ? runOptions : {};
|
|
391
|
+
const frontDoorOn = isFrontDoorRoutingEnabledInDoc(appConfig);
|
|
392
|
+
const wantsPublic =
|
|
393
|
+
frontDoorOn && Boolean(ro.probeViaTraefik === true || ro.traefikEnabled === true);
|
|
394
|
+
|
|
395
|
+
let primaryAppUrl = `http://localhost:${port}`;
|
|
396
|
+
if (wantsPublic) {
|
|
397
|
+
const pub = await computeTraefikPublicAppUrl(appName, port, appConfig);
|
|
398
|
+
if (pub) primaryAppUrl = pub;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const healthTipUrl = await healthCheckUtil.computeHealthCheckUrl(appName, port, appConfig, {
|
|
402
|
+
runOptions: ro
|
|
403
|
+
});
|
|
375
404
|
|
|
376
|
-
logger.log(formatSuccessParagraph(`App running at
|
|
377
|
-
|
|
378
|
-
|
|
405
|
+
logger.log(formatSuccessParagraph(`App running at ${primaryAppUrl}`));
|
|
406
|
+
if (wantsPublic && primaryAppUrl.indexOf('localhost') === -1) {
|
|
407
|
+
logger.log(metadata(`Local direct: http://localhost:${port}`));
|
|
408
|
+
}
|
|
409
|
+
logger.log(headerKeyValue('Health check:', healthTipUrl));
|
|
410
|
+
logger.log(headerKeyValue('Container:', containerName));
|
|
411
|
+
logger.log('');
|
|
412
|
+
logger.log(
|
|
413
|
+
formatNextActions([
|
|
414
|
+
`Validate errors: aifabrix logs ${appName} -l error`,
|
|
415
|
+
`Follow output: aifabrix logs ${appName} -f -t 100`
|
|
416
|
+
])
|
|
417
|
+
);
|
|
379
418
|
}
|
|
380
419
|
|
|
381
420
|
module.exports = {
|