@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
|
@@ -12,7 +12,7 @@ When you touch **CLI surface** (`bin/aifabrix.js`, `lib/cli.js`, `lib/cli/**`, `
|
|
|
12
12
|
| Document | Role |
|
|
13
13
|
| -------- | ---- |
|
|
14
14
|
| [layout.md](./layout.md) | Visual and semantic spec: colors, sections, glyphs, blocking vs warning, non-TTY/CI, helper map to `cli-test-layout-chalk.js`. |
|
|
15
|
-
| [cli-output-command-matrix.md](./cli-output-command-matrix.md) | **One row per leaf command**: expected **output profile** (layout-blocks, tty-summary, stream-logs, json-opt, stdout-only, delegate). |
|
|
15
|
+
| [cli-output-command-matrix.md](./cli-output-command-matrix.md) | **One row per leaf command**: expected **output profile** (layout-blocks, tty-summary, stream-logs, json-opt, stdout-only, delegate) and **Manifest roots (141)** (when to emit gray manifest path line — see plan 141). |
|
|
16
16
|
|
|
17
17
|
If implementation and `layout.md` disagree, **fix implementation** or **update docs** in the same change so they stay one story.
|
|
18
18
|
|
|
@@ -27,9 +27,13 @@ Use the matrix row for your command to decide UX:
|
|
|
27
27
|
- **stdout-only** — Stable, script-friendly stdout; minimal chalk; often used for diffs or piping.
|
|
28
28
|
- **delegate** — Output comes from delegated library (wizard, etc.); still avoid forbidden glyphs and spurious decoration in anything you add.
|
|
29
29
|
|
|
30
|
-
**New leaf command:** add a row to [cli-output-command-matrix.md](./cli-output-command-matrix.md) with the correct profile combination.
|
|
30
|
+
**New leaf command:** add a row to [cli-output-command-matrix.md](./cli-output-command-matrix.md) with the correct profile combination and **Manifest roots (141)** code (`141`, `141+`, `int`, `cfg`, `—`).
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## Manifest path visibility (plan 141)
|
|
33
|
+
|
|
34
|
+
When a command’s matrix cell is **141**, **141+**, or **int** and the implementation reads an `application.yaml` (or integration equivalent), print **one** gray metadata line for humans (e.g. `Manifest: cwd/builder — /abs/path/application.yaml`) using `metadata()` or a small helper from `cli-test-layout-chalk.js`. Do not spam per-substep. Follow [.cursor/plans/141-manifest-location.plan.md](../plans/141-manifest-location.plan.md).
|
|
35
|
+
|
|
36
|
+
**`aifabrix setup`:** destructive / conflict prompts that list folders to be replaced must use **resolved absolute paths** for every directory (plan § Guided setup — not only `builder/<app>/` under an otherwise absolute builder root).
|
|
33
37
|
|
|
34
38
|
From [layout.md](./layout.md) contributor appendix:
|
|
35
39
|
|
package/jest.projects.js
CHANGED
|
@@ -141,6 +141,34 @@ const defaultProject = {
|
|
|
141
141
|
'\\\\tests\\\\lib\\\\utils\\\\url-declarative-vdir-inactive-env.test.js',
|
|
142
142
|
'lib/utils/url-declarative-vdir-inactive-env.test.js',
|
|
143
143
|
'url-declarative-vdir-inactive-env\\.test\\.js',
|
|
144
|
+
'/tests/lib/utils/url-declarative-user-cfg-per-app-proxy.test.js',
|
|
145
|
+
'\\\\tests\\\\lib\\\\utils\\\\url-declarative-user-cfg-per-app-proxy.test.js',
|
|
146
|
+
'lib/utils/url-declarative-user-cfg-per-app-proxy.test.js',
|
|
147
|
+
'url-declarative-user-cfg-per-app-proxy\\.test\\.js',
|
|
148
|
+
'/tests/lib/utils/url-declarative-expand-traefik-off-no-usercfg.test.js',
|
|
149
|
+
'\\\\tests\\\\lib\\\\utils\\\\url-declarative-expand-traefik-off-no-usercfg.test.js',
|
|
150
|
+
'lib/utils/url-declarative-expand-traefik-off-no-usercfg.test.js',
|
|
151
|
+
'url-declarative-expand-traefik-off-no-usercfg\\.test\\.js',
|
|
152
|
+
'/tests/lib/core/secrets-env-declarative-show-urls.test.js',
|
|
153
|
+
'\\\\tests\\\\lib\\\\core\\\\secrets-env-declarative-show-urls.test.js',
|
|
154
|
+
'lib/core/secrets-env-declarative-show-urls.test.js',
|
|
155
|
+
'secrets-env-declarative-show-urls\\.test\\.js',
|
|
156
|
+
'/tests/lib/utils/url-declarative-registry-internal-docker-origin.test.js',
|
|
157
|
+
'\\\\tests\\\\lib\\\\utils\\\\url-declarative-registry-internal-docker-origin.test.js',
|
|
158
|
+
'lib/utils/url-declarative-registry-internal-docker-origin.test.js',
|
|
159
|
+
'url-declarative-registry-internal-docker-origin\\.test\\.js',
|
|
160
|
+
'/tests/lib/commands/platform-urls-registry.validation.test.js',
|
|
161
|
+
'\\\\tests\\\\lib\\\\commands\\\\platform-urls-registry.validation.test.js',
|
|
162
|
+
'lib/commands/platform-urls-registry.validation.test.js',
|
|
163
|
+
'platform-urls-registry\\.validation\\.test\\.js',
|
|
164
|
+
'/tests/lib/utils/env-copy-resolve-output.test.js',
|
|
165
|
+
'\\\\tests\\\\lib\\\\utils\\\\env-copy-resolve-output.test.js',
|
|
166
|
+
'lib/utils/env-copy-resolve-output.test.js',
|
|
167
|
+
'env-copy-resolve-output\\.test\\.js',
|
|
168
|
+
'/tests/lib/utils/write-env-output-reload.test.js',
|
|
169
|
+
'\\\\tests\\\\lib\\\\utils\\\\write-env-output-reload.test.js',
|
|
170
|
+
'lib/utils/write-env-output-reload.test.js',
|
|
171
|
+
'write-env-output-reload\\.test\\.js',
|
|
144
172
|
'/tests/lib/utils/app-service-env-from-builder.test.js',
|
|
145
173
|
'\\\\tests\\\\lib\\\\utils\\\\app-service-env-from-builder.test.js',
|
|
146
174
|
'lib/utils/app-service-env-from-builder.test.js',
|
|
@@ -165,6 +193,18 @@ const defaultProject = {
|
|
|
165
193
|
'\\\\tests\\\\lib\\\\utils\\\\paths-system-builder-resolution.test.js',
|
|
166
194
|
'lib/utils/paths-system-builder-resolution.test.js',
|
|
167
195
|
'paths-system-builder-resolution\\.test\\.js',
|
|
196
|
+
'/tests/lib/utils/manifest-location.test.js',
|
|
197
|
+
'\\\\tests\\\\lib\\\\utils\\\\manifest-location.test.js',
|
|
198
|
+
'lib/utils/manifest-location.test.js',
|
|
199
|
+
'manifest-location\\.test\\.js',
|
|
200
|
+
'/tests/lib/utils/installation-log.test.js',
|
|
201
|
+
'\\\\tests\\\\lib\\\\utils\\\\installation-log.test.js',
|
|
202
|
+
'lib/utils/installation-log.test.js',
|
|
203
|
+
'installation-log\\.test\\.js',
|
|
204
|
+
'/tests/lib/utils/manifest-source-emit.test.js',
|
|
205
|
+
'\\\\tests\\\\lib\\\\utils\\\\manifest-source-emit.test.js',
|
|
206
|
+
'lib/utils/manifest-source-emit.test.js',
|
|
207
|
+
'manifest-source-emit\\.test\\.js',
|
|
168
208
|
'/tests/lib/utils/secrets-ancestor-paths.test.js',
|
|
169
209
|
'\\\\tests\\\\lib\\\\utils\\\\secrets-ancestor-paths.test.js',
|
|
170
210
|
'lib/utils/secrets-ancestor-paths.test.js',
|
|
@@ -263,6 +303,8 @@ const isolatedProjects = [
|
|
|
263
303
|
makeIsolatedProject('paths-system-builder-resolution', [
|
|
264
304
|
'**/tests/lib/utils/paths-system-builder-resolution.test.js'
|
|
265
305
|
]),
|
|
306
|
+
makeIsolatedProject('manifest-location', ['**/tests/lib/utils/manifest-location.test.js']),
|
|
307
|
+
makeIsolatedProject('installation-log', ['**/tests/lib/utils/installation-log.test.js']),
|
|
266
308
|
makeIsolatedProject('secrets-ancestor-paths', ['**/tests/lib/utils/secrets-ancestor-paths.test.js']),
|
|
267
309
|
makeIsolatedProject('datasource-validation-watch', [
|
|
268
310
|
'**/tests/lib/utils/datasource-validation-watch.test.js'
|
|
@@ -272,6 +314,7 @@ const isolatedProjects = [
|
|
|
272
314
|
'**/tests/lib/datasource/log-viewer-structural.test.js',
|
|
273
315
|
'**/tests/lib/datasource/log-viewer-run.test.js'
|
|
274
316
|
]),
|
|
317
|
+
makeIsolatedProject('manifest-source-emit', ['**/tests/lib/utils/manifest-source-emit.test.js']),
|
|
275
318
|
makeIsolatedProject('register-aifabrix-shell-env', [
|
|
276
319
|
'**/tests/lib/utils/register-aifabrix-shell-env.test.js'
|
|
277
320
|
]),
|
|
@@ -314,6 +357,19 @@ const isolatedProjects = [
|
|
|
314
357
|
makeIsolatedProject('url-declarative-vdir-inactive-env', [
|
|
315
358
|
'**/tests/lib/utils/url-declarative-vdir-inactive-env.test.js'
|
|
316
359
|
]),
|
|
360
|
+
makeIsolatedProject('url-declarative-user-cfg-per-app-proxy', [
|
|
361
|
+
'**/tests/lib/utils/url-declarative-user-cfg-per-app-proxy.test.js'
|
|
362
|
+
]),
|
|
363
|
+
makeIsolatedProject('declarative-url-paths-spy-suites', [
|
|
364
|
+
'**/tests/lib/utils/url-declarative-expand-traefik-off-no-usercfg.test.js',
|
|
365
|
+
'**/tests/lib/core/secrets-env-declarative-show-urls.test.js',
|
|
366
|
+
'**/tests/lib/utils/url-declarative-registry-internal-docker-origin.test.js'
|
|
367
|
+
]),
|
|
368
|
+
makeIsolatedProject('platform-urls-registry-validation', [
|
|
369
|
+
'**/tests/lib/commands/platform-urls-registry.validation.test.js'
|
|
370
|
+
]),
|
|
371
|
+
makeIsolatedProject('env-copy-resolve-output', ['**/tests/lib/utils/env-copy-resolve-output.test.js']),
|
|
372
|
+
makeIsolatedProject('write-env-output-reload', ['**/tests/lib/utils/write-env-output-reload.test.js']),
|
|
317
373
|
makeIsolatedProject('app-service-env-from-builder', [
|
|
318
374
|
'**/tests/lib/utils/app-service-env-from-builder.test.js'
|
|
319
375
|
]),
|
package/lib/app/helpers.js
CHANGED
|
@@ -14,7 +14,7 @@ const path = require('path');
|
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const { validateTemplate, copyTemplateFiles, copyAppFiles } = require('../validation/template');
|
|
16
16
|
const logger = require('../utils/logger');
|
|
17
|
-
const
|
|
17
|
+
const paths = require('../utils/paths');
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Validates that no app or external system with this name exists in integration/ or builder/.
|
|
@@ -25,8 +25,8 @@ const { getIntegrationPath, getBuilderPath } = require('../utils/paths');
|
|
|
25
25
|
* @throws {Error} If integration/<systemKey> or builder/<appKey> already exists
|
|
26
26
|
*/
|
|
27
27
|
async function validateAppOrExternalNameNotExists(appName) {
|
|
28
|
-
const integrationPath = getIntegrationPath(appName);
|
|
29
|
-
const builderPath = getBuilderPath(appName);
|
|
28
|
+
const integrationPath = paths.getIntegrationPath(appName);
|
|
29
|
+
const builderPath = paths.getBuilderPath(appName);
|
|
30
30
|
try {
|
|
31
31
|
await fs.access(integrationPath);
|
|
32
32
|
throw new Error(
|
package/lib/app/index.js
CHANGED
|
@@ -21,7 +21,7 @@ const { loadTemplateVariables, updateTemplateVariables, mergeTemplateVariables }
|
|
|
21
21
|
const { validateTemplate } = require('../validation/template');
|
|
22
22
|
const auditLogger = require('../core/audit-logger');
|
|
23
23
|
const { downApp } = require('./down');
|
|
24
|
-
const
|
|
24
|
+
const paths = require('../utils/paths');
|
|
25
25
|
const { displaySuccessMessage } = require('./display');
|
|
26
26
|
const {
|
|
27
27
|
validateAppDirectoryNotExists,
|
|
@@ -93,7 +93,7 @@ function validateAppNameAndSetup(appName, options) {
|
|
|
93
93
|
|
|
94
94
|
const initialType = options.type || 'external';
|
|
95
95
|
const baseDir = getBaseDirForAppType(initialType);
|
|
96
|
-
const appPath = getAppPath(appName, initialType);
|
|
96
|
+
const appPath = paths.getAppPath(appName, initialType);
|
|
97
97
|
|
|
98
98
|
return { initialType, baseDir, appPath };
|
|
99
99
|
}
|
|
@@ -124,7 +124,7 @@ async function handleTemplateSetup(options) {
|
|
|
124
124
|
*/
|
|
125
125
|
async function prepareFinalAppPath(appName, config, initialAppPath) {
|
|
126
126
|
const finalBaseDir = getBaseDirForAppType(config.type);
|
|
127
|
-
const finalAppPath = getAppPath(appName, config.type);
|
|
127
|
+
const finalAppPath = paths.getAppPath(appName, config.type);
|
|
128
128
|
|
|
129
129
|
// If path changed, validate the new path
|
|
130
130
|
if (finalAppPath !== initialAppPath) {
|
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', true);
|
|
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
|
}
|
|
@@ -13,6 +13,7 @@ const config = require('../core/config');
|
|
|
13
13
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
14
14
|
const { sectionTitle, headerKeyValue, metadata } = require('../utils/cli-test-layout-chalk');
|
|
15
15
|
const { isReloadBindMountOnEngineHost } = require('../utils/docker-reload-mount');
|
|
16
|
+
const { isApplicationsReloadDefaultOn } = require('../utils/applications-config-defaults');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @param {unknown} mounts - docker inspect .Mounts
|
|
@@ -54,42 +55,72 @@ async function fetchContainerMountsJson(containerName) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
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
|
+
|
|
57
85
|
/**
|
|
58
86
|
* Log workspace transport after a successful container restart (mounts unchanged).
|
|
59
87
|
* @param {string} containerName - Docker container name
|
|
88
|
+
* @param {string|null} [appName] - Application key; when set, logs reload default from config after mount info
|
|
60
89
|
* @returns {Promise<void>}
|
|
61
90
|
*/
|
|
62
|
-
async function logRestartDevMountSummary(containerName) {
|
|
91
|
+
async function logRestartDevMountSummary(containerName, appName = null) {
|
|
63
92
|
if (!containerName || typeof containerName !== 'string') {
|
|
93
|
+
await logReloadConfigSummaryForRestart(appName);
|
|
64
94
|
return;
|
|
65
95
|
}
|
|
66
96
|
const mounts = await fetchContainerMountsJson(containerName);
|
|
67
97
|
const appBind = findAppBindMount(mounts);
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const endpoint = await config.getDockerEndpoint();
|
|
72
|
-
const localEngine = isReloadBindMountOnEngineHost(endpoint);
|
|
98
|
+
if (appBind) {
|
|
99
|
+
const endpoint = await config.getDockerEndpoint();
|
|
100
|
+
const localEngine = isReloadBindMountOnEngineHost(endpoint);
|
|
73
101
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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('');
|
|
88
118
|
}
|
|
89
|
-
|
|
119
|
+
await logReloadConfigSummaryForRestart(appName);
|
|
90
120
|
}
|
|
91
121
|
|
|
92
122
|
module.exports = {
|
|
93
123
|
findAppBindMount,
|
|
124
|
+
logReloadConfigSummaryForRestart,
|
|
94
125
|
logRestartDevMountSummary
|
|
95
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', true);
|
|
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
|
}
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -38,6 +38,7 @@ const { resolveEnvOutputPath, writeEnvOutputForReload, writeEnvOutputForLocal }
|
|
|
38
38
|
const { resolveVersionForApp } = require('../utils/image-version');
|
|
39
39
|
const healthCheckUtil = require('../utils/health-check');
|
|
40
40
|
const { computeTraefikPublicAppUrl } = require('../utils/health-check-url');
|
|
41
|
+
const { isFrontDoorRoutingEnabledInDoc } = require('../utils/url-declarative-vdir-inactive-env');
|
|
41
42
|
|
|
42
43
|
/** Template apps (keycloak, miso-controller, dataplane) - never update application config when running */
|
|
43
44
|
const TEMPLATE_APP_KEYS = ['keycloak', 'miso-controller', 'dataplane'];
|
|
@@ -72,7 +73,7 @@ function checkBuilderDirectory(appName) {
|
|
|
72
73
|
|
|
73
74
|
/**
|
|
74
75
|
* Load and validate config file exists
|
|
75
|
-
* Uses paths.getBuilderPath
|
|
76
|
+
* Uses paths.getBuilderPath (cwd / material `builder/` only; no `AIFABRIX_BUILDER_DIR` override).
|
|
76
77
|
* @param {string} appName - Application name
|
|
77
78
|
* @returns {Object} Application configuration
|
|
78
79
|
* @throws {Error} If config file not found
|
|
@@ -299,12 +300,15 @@ async function generateComposeFile(appName, appConfig, composeOptions, devDir) {
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
/**
|
|
302
|
-
* 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
|
+
*
|
|
303
307
|
* @async
|
|
304
308
|
* @param {string} appName - Application name
|
|
305
309
|
* @param {Object} appConfig - Application configuration
|
|
306
|
-
* @param {string} runEnvPath - Path to
|
|
307
|
-
* @param {Object} options - Run options (reload
|
|
310
|
+
* @param {string} runEnvPath - Path to `.env.run`
|
|
311
|
+
* @param {Object} options - Run options (`reload`, `skipEnvOutputPath`)
|
|
308
312
|
*/
|
|
309
313
|
async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, options) {
|
|
310
314
|
if (options && options.skipEnvOutputPath === true) return;
|
|
@@ -318,8 +322,8 @@ async function writeEnvOutputIfConfigured(appName, appConfig, runEnvPath, option
|
|
|
318
322
|
if (!fsSync.existsSync(outputDir)) {
|
|
319
323
|
await fs.mkdir(outputDir, { recursive: true });
|
|
320
324
|
}
|
|
321
|
-
if (options.reload) {
|
|
322
|
-
await writeEnvOutputForReload(outputPath, runEnvPath);
|
|
325
|
+
if (options && options.reload === true) {
|
|
326
|
+
await writeEnvOutputForReload(outputPath, runEnvPath, appName);
|
|
323
327
|
} else {
|
|
324
328
|
await writeEnvOutputForLocal(appName, outputPath);
|
|
325
329
|
}
|
|
@@ -362,7 +366,8 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
362
366
|
envFilePath: runEnvPath,
|
|
363
367
|
dbInitEnvFilePath: runEnvAdminPath,
|
|
364
368
|
effectiveEnvironmentScopedResources,
|
|
365
|
-
env: runEnvKey
|
|
369
|
+
env: runEnvKey,
|
|
370
|
+
omitAppTraefikLabels: userCfg.traefik === false
|
|
366
371
|
};
|
|
367
372
|
composeOptions.port = calculateComposePort(composeOptions, appConfig, developerId);
|
|
368
373
|
const composePath = await generateComposeFile(appName, appConfig, composeOptions, devDir);
|
|
@@ -383,7 +388,9 @@ async function prepareEnvironment(appName, appConfig, options) {
|
|
|
383
388
|
async function displayRunStatus(appName, port, appConfig, runScopeOpts = null, runOptions = {}) {
|
|
384
389
|
const containerName = containerHelpers.getContainerName(appName, appConfig.developerId, runScopeOpts);
|
|
385
390
|
const ro = runOptions && typeof runOptions === 'object' ? runOptions : {};
|
|
386
|
-
const
|
|
391
|
+
const frontDoorOn = isFrontDoorRoutingEnabledInDoc(appConfig);
|
|
392
|
+
const wantsPublic =
|
|
393
|
+
frontDoorOn && Boolean(ro.probeViaTraefik === true || ro.traefikEnabled === true);
|
|
387
394
|
|
|
388
395
|
let primaryAppUrl = `http://localhost:${port}`;
|
|
389
396
|
if (wantsPublic) {
|
package/lib/app/run.js
CHANGED
|
@@ -28,6 +28,7 @@ const helpers = require('./run-helpers');
|
|
|
28
28
|
const { ensureReloadSync, logReloadDevSummary } = require('./run-reload-sync');
|
|
29
29
|
const { logRestartDevMountSummary } = require('./restart-display');
|
|
30
30
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
31
|
+
const { isRunCliNoProxy } = require('../utils/run-cli-flags');
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* True if host is localhost or 127.0.0.1 (case-insensitive).
|
|
@@ -68,7 +69,7 @@ function isLocalhostEndpoint(urlOrEndpoint) {
|
|
|
68
69
|
* @async
|
|
69
70
|
* @param {string} appName - Application name
|
|
70
71
|
* @param {boolean} _debug - Debug flag (unused)
|
|
71
|
-
* @returns {Promise<boolean>} True if should continue, false if external
|
|
72
|
+
* @returns {Promise<boolean>} True if run should continue, false if app is integration-scoped or external (not a runnable container app)
|
|
72
73
|
* @throws {Error} If app name is invalid
|
|
73
74
|
*/
|
|
74
75
|
async function validateAppForRun(appName, _debug) {
|
|
@@ -88,9 +89,16 @@ async function validateAppForRun(appName, _debug) {
|
|
|
88
89
|
formatWarningLine('External integrations are not started as local Docker containers.')
|
|
89
90
|
);
|
|
90
91
|
logger.log(
|
|
91
|
-
metadata(
|
|
92
|
+
metadata(
|
|
93
|
+
'Publish config to the dataplane (upload) or through the controller (deploy), then use the integration APIs from your environment.'
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
logger.log(
|
|
97
|
+
formatNextActions([
|
|
98
|
+
`aifabrix upload ${appName}`,
|
|
99
|
+
`aifabrix deploy ${appName}`
|
|
100
|
+
])
|
|
92
101
|
);
|
|
93
|
-
logger.log(formatNextActions([`aifabrix build ${appName}`]));
|
|
94
102
|
return false;
|
|
95
103
|
}
|
|
96
104
|
} catch (error) {
|
|
@@ -263,12 +271,12 @@ async function resolveRunOptions(appName, appConfig, options, envKey, debug, eff
|
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
/**
|
|
266
|
-
* When Traefik is enabled in user config
|
|
274
|
+
* When Traefik is enabled in user config and this run should use proxy hints (`runOptions.noProxy` is false), enable Traefik/DNS URL hints.
|
|
267
275
|
* @param {Object} runOptions
|
|
268
276
|
* @param {Object} userCfg
|
|
269
277
|
*/
|
|
270
278
|
function applyTraefikFlagToRunOptions(runOptions, userCfg) {
|
|
271
|
-
if (userCfg && userCfg.traefik === true) {
|
|
279
|
+
if (userCfg && userCfg.traefik === true && !runOptions.noProxy) {
|
|
272
280
|
runOptions.traefikEnabled = true;
|
|
273
281
|
}
|
|
274
282
|
}
|
|
@@ -335,10 +343,28 @@ async function computeRunPreparationCore(appName, appConfig, options, envKey, us
|
|
|
335
343
|
return result;
|
|
336
344
|
}
|
|
337
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Gray **Manifest:** line before container start (skipped when orchestration already emitted).
|
|
348
|
+
* @param {string} appName
|
|
349
|
+
* @param {Object} options
|
|
350
|
+
* @returns {void}
|
|
351
|
+
*/
|
|
352
|
+
function emitRunManifestMetadataIfRequested(appName, options) {
|
|
353
|
+
if (options.skipManifestMetadataLine === true) return;
|
|
354
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
355
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
356
|
+
appKey: appName,
|
|
357
|
+
appPath: pathsUtil.getBuilderPath(appName),
|
|
358
|
+
envOnly: false,
|
|
359
|
+
json: false
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
338
363
|
/**
|
|
339
364
|
* Prepare run: validate app, load config, check prereqs, stop existing container, resolve port and reload, prepare env.
|
|
340
365
|
* @param {string} appName - Application name
|
|
341
366
|
* @param {Object} options - Run options
|
|
367
|
+
* @param {boolean} [options.skipManifestMetadataLine] - When true, skip gray **Manifest:** line (caller already emitted for platform orchestration)
|
|
342
368
|
* @param {boolean} debug - Debug flag
|
|
343
369
|
* @returns {Promise<{ appConfig: Object, tempComposePath: string, hostPort: number }|null>} Prepared run context or null if should not continue
|
|
344
370
|
*/
|
|
@@ -349,12 +375,25 @@ async function prepareAppRun(appName, options, debug) {
|
|
|
349
375
|
if (!shouldContinue) {
|
|
350
376
|
return null;
|
|
351
377
|
}
|
|
378
|
+
let userCfg = await config.getConfig();
|
|
379
|
+
const appsDefaults = require('../utils/applications-config-defaults');
|
|
380
|
+
await appsDefaults.persistApplicationRunProxyFlag(appName, !isRunCliNoProxy(options));
|
|
381
|
+
if (envKey === 'dev') {
|
|
382
|
+
await appsDefaults.persistApplicationReloadFlag(appName, options.reload === true);
|
|
383
|
+
}
|
|
384
|
+
userCfg = await config.getConfig();
|
|
385
|
+
const savedProxyHint = appsDefaults.getApplicationsRunProxyHint(userCfg, appName);
|
|
386
|
+
const mergedOpts = {
|
|
387
|
+
...options,
|
|
388
|
+
reload: options.reload === true,
|
|
389
|
+
noProxy: isRunCliNoProxy(options) || !savedProxyHint
|
|
390
|
+
};
|
|
352
391
|
logger.log('');
|
|
353
392
|
logger.log(sectionTitle('Run'));
|
|
354
393
|
logger.log(headerKeyValue('Application:', appName));
|
|
355
394
|
const appConfig = await loadAndConfigureApp(appName, debug);
|
|
356
|
-
|
|
357
|
-
return computeRunPreparationCore(appName, appConfig,
|
|
395
|
+
emitRunManifestMetadataIfRequested(appName, options);
|
|
396
|
+
return computeRunPreparationCore(appName, appConfig, mergedOpts, envKey, userCfg, debug);
|
|
358
397
|
}
|
|
359
398
|
|
|
360
399
|
/**
|
|
@@ -367,6 +406,7 @@ async function prepareAppRun(appName, options, debug) {
|
|
|
367
406
|
* @param {Object} options - Run options
|
|
368
407
|
* @param {number} [options.port] - Override local port
|
|
369
408
|
* @param {boolean} [options.debug] - Enable debug output
|
|
409
|
+
* @param {boolean} [options.skipManifestMetadataLine] - When true, skip gray **Manifest:** line in prepare (orchestration already emitted)
|
|
370
410
|
* @returns {Promise<void>} Resolves when app is running
|
|
371
411
|
* @throws {Error} If run fails or app is not built
|
|
372
412
|
*
|
|
@@ -388,7 +428,7 @@ async function runApp(appName, options = {}) {
|
|
|
388
428
|
if (debug) {
|
|
389
429
|
logger.log(chalk.gray(`[DEBUG] Compose file generated: ${prepared.tempComposePath}`));
|
|
390
430
|
}
|
|
391
|
-
logReloadDevSummary(Boolean(
|
|
431
|
+
logReloadDevSummary(Boolean(prepared.mergedRunOptions.reload), prepared.mergedRunOptions.reloadSyncSummary);
|
|
392
432
|
await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
|
|
393
433
|
debug,
|
|
394
434
|
runEnvPath: prepared.runEnvPath,
|
|
@@ -422,11 +462,18 @@ async function restartApp(appName) {
|
|
|
422
462
|
if (!appName || typeof appName !== 'string') {
|
|
423
463
|
throw new Error('Application name is required and must be a string');
|
|
424
464
|
}
|
|
465
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
466
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
467
|
+
appKey: appName,
|
|
468
|
+
appPath: pathsUtil.getBuilderPath(appName),
|
|
469
|
+
envOnly: false,
|
|
470
|
+
json: false
|
|
471
|
+
});
|
|
425
472
|
const developerId = await config.getDeveloperId();
|
|
426
473
|
const containerName = containerHelpers.getContainerName(appName, developerId);
|
|
427
474
|
try {
|
|
428
475
|
await execWithDockerEnv(`docker restart ${containerName}`);
|
|
429
|
-
await logRestartDevMountSummary(containerName);
|
|
476
|
+
await logRestartDevMountSummary(containerName, appName);
|
|
430
477
|
} catch (error) {
|
|
431
478
|
const msg = (error.stderr || error.stdout || error.message || '').toLowerCase();
|
|
432
479
|
if (msg.includes('no such container') || msg.includes('is not running')) {
|
|
@@ -439,6 +486,7 @@ async function restartApp(appName) {
|
|
|
439
486
|
module.exports = {
|
|
440
487
|
runApp,
|
|
441
488
|
restartApp,
|
|
489
|
+
validateAppForRun,
|
|
442
490
|
ensureReloadSync,
|
|
443
491
|
isLocalhostHost,
|
|
444
492
|
isLocalhostEndpoint,
|
package/lib/app/show-display.js
CHANGED
|
@@ -96,6 +96,13 @@ function logApplicationSection(a, summary) {
|
|
|
96
96
|
} else {
|
|
97
97
|
logApplicationFields(a);
|
|
98
98
|
}
|
|
99
|
+
if (summary.runReloadDefault && !summary.isExternal) {
|
|
100
|
+
logger.log(` ${'Run default:'.padEnd(16)} reload on (config)`);
|
|
101
|
+
}
|
|
102
|
+
if (!summary.isExternal) {
|
|
103
|
+
const proxyOn = summary.runProxyDefault === true;
|
|
104
|
+
logger.log(` ${'Run default:'.padEnd(16)} proxy ${proxyOn ? 'on' : 'off'} (config)`);
|
|
105
|
+
}
|
|
99
106
|
/* External integration is logged after Dataplane block in display() when external */
|
|
100
107
|
}
|
|
101
108
|
|