@aifabrix/builder 2.44.5 → 2.44.6
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 +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +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 +48 -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/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +134 -61
- 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.js +78 -33
- 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 +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/up-common.js +79 -19
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -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/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 +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -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 +4 -1
- 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/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/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- 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-run-containers.js +2 -2
- 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/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/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 +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- 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 +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- 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 +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +23 -12
- 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 +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- 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
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `aifabrix teardown` — full teardown of local infra and CLI state.
|
|
3
|
+
*
|
|
4
|
+
* Performs:
|
|
5
|
+
* 1. `down-infra -v` (stop infra + apps, remove all Docker volumes).
|
|
6
|
+
* 2. Remove every entry inside `~/.aifabrix/` (or the equivalent
|
|
7
|
+
* `getAifabrixSystemDir()`-resolved directory) except `config.yaml`.
|
|
8
|
+
* This includes `secrets.local.yaml`, `admin-secrets.env`,
|
|
9
|
+
* auth/token files, and any `infra-dev*` directories.
|
|
10
|
+
*
|
|
11
|
+
* Confirmation is required by default; pass `--yes` / `-y` to skip.
|
|
12
|
+
*
|
|
13
|
+
* @fileoverview aifabrix teardown handler
|
|
14
|
+
* @author AI Fabrix Team
|
|
15
|
+
* @version 2.0.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const inquirer = require('inquirer');
|
|
23
|
+
const chalk = require('chalk');
|
|
24
|
+
const ora = require('ora');
|
|
25
|
+
|
|
26
|
+
const config = require('../core/config');
|
|
27
|
+
const infra = require('../infrastructure');
|
|
28
|
+
const pathsUtil = require('../utils/paths');
|
|
29
|
+
const logger = require('../utils/logger');
|
|
30
|
+
const { withMutedLogger } = require('../utils/with-muted-logger');
|
|
31
|
+
const {
|
|
32
|
+
formatSuccessLine,
|
|
33
|
+
formatSuccessParagraph,
|
|
34
|
+
formatProgress,
|
|
35
|
+
successGlyph
|
|
36
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
37
|
+
|
|
38
|
+
/** File name kept by teardown (lowercase exact match). */
|
|
39
|
+
const PRESERVE_FILE = 'config.yaml';
|
|
40
|
+
/** Directory preserved by teardown (holds developer TLS client certs). */
|
|
41
|
+
const PRESERVE_CERTS_DIR = 'certs';
|
|
42
|
+
|
|
43
|
+
const PRESERVE_ENTRY_NAMES = new Set([PRESERVE_FILE, PRESERVE_CERTS_DIR]);
|
|
44
|
+
|
|
45
|
+
const SEPARATOR = '────────────────────────────────────────';
|
|
46
|
+
|
|
47
|
+
function title(text) {
|
|
48
|
+
return chalk.bold(text);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function shouldUseSpinner() {
|
|
52
|
+
return Boolean(process && process.stdout && process.stdout.isTTY);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function startSpinner(text) {
|
|
56
|
+
if (!shouldUseSpinner()) {
|
|
57
|
+
logger.log(formatProgress(text));
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return ora({ text, spinner: 'dots' }).start();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function stopSpinnerSuccess(spinner, text) {
|
|
64
|
+
if (!spinner) {
|
|
65
|
+
logger.log(formatSuccessLine(text));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
spinner.succeed(text);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Ask the user to confirm teardown unless `assumeYes` is true.
|
|
73
|
+
* @async
|
|
74
|
+
* @param {boolean} assumeYes
|
|
75
|
+
* @returns {Promise<boolean>}
|
|
76
|
+
*/
|
|
77
|
+
async function confirmTeardown(assumeYes) {
|
|
78
|
+
if (assumeYes) return true;
|
|
79
|
+
const { ok } = await inquirer.prompt([
|
|
80
|
+
{
|
|
81
|
+
type: 'confirm',
|
|
82
|
+
name: 'ok',
|
|
83
|
+
message:
|
|
84
|
+
'This will stop all infra + apps, DELETE every Docker volume, and remove every file in ~/.aifabrix/ except config.yaml and certs/. Continue?',
|
|
85
|
+
default: false
|
|
86
|
+
}
|
|
87
|
+
]);
|
|
88
|
+
return ok === true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Remove every entry in the AI Fabrix system directory except `config.yaml`.
|
|
93
|
+
* Each removal is logged. Errors per entry are caught and logged so the
|
|
94
|
+
* teardown surfaces partial-success information instead of bailing on the
|
|
95
|
+
* first ENOENT/EBUSY.
|
|
96
|
+
*
|
|
97
|
+
* @returns {{ removed: string[], failed: string[] }}
|
|
98
|
+
*/
|
|
99
|
+
function cleanAifabrixSystemDir() {
|
|
100
|
+
const dir = pathsUtil.getAifabrixSystemDir();
|
|
101
|
+
const removed = [];
|
|
102
|
+
const failed = [];
|
|
103
|
+
if (!fs.existsSync(dir)) {
|
|
104
|
+
return { removed, failed };
|
|
105
|
+
}
|
|
106
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
107
|
+
for (const entry of entries) {
|
|
108
|
+
if (PRESERVE_ENTRY_NAMES.has(entry.name)) continue;
|
|
109
|
+
const target = path.join(dir, entry.name);
|
|
110
|
+
try {
|
|
111
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
112
|
+
removed.push(target);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
failed.push(target);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { removed, failed };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Stop infra with `down-infra -v` semantics. Tolerates already-down state.
|
|
122
|
+
* @async
|
|
123
|
+
* @returns {Promise<void>}
|
|
124
|
+
*/
|
|
125
|
+
async function stopInfraQuietly() {
|
|
126
|
+
try {
|
|
127
|
+
const spin = startSpinner('Stopping infrastructure (down-infra -v)...');
|
|
128
|
+
await withMutedLogger(async() => {
|
|
129
|
+
await infra.stopInfraWithVolumes();
|
|
130
|
+
});
|
|
131
|
+
stopSpinnerSuccess(spin, 'Infrastructure stopped and volumes removed');
|
|
132
|
+
} catch (err) {
|
|
133
|
+
logger.log(
|
|
134
|
+
chalk.yellow(
|
|
135
|
+
`Infrastructure already down or could not be stopped cleanly: ${err.message}`
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function logFooterStart(label) {
|
|
142
|
+
logger.log('');
|
|
143
|
+
logger.log(SEPARATOR);
|
|
144
|
+
logger.log(title(label));
|
|
145
|
+
logger.log(SEPARATOR);
|
|
146
|
+
logger.log('');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function logFooterEnd() {
|
|
150
|
+
logger.log(SEPARATOR);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function logSection(label, value) {
|
|
154
|
+
logger.log(title(label));
|
|
155
|
+
logger.log(` ${value}`);
|
|
156
|
+
logger.log('');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function buildDeveloperLabel() {
|
|
160
|
+
const developerId = await config.getDeveloperId();
|
|
161
|
+
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
162
|
+
if (!Number.isFinite(idNum)) return 'unknown';
|
|
163
|
+
return `dev${String(idNum).padStart(2, '0')} (id: ${idNum})`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function logTeardownHeader() {
|
|
167
|
+
logger.log('');
|
|
168
|
+
logger.log(title('AI Fabrix Shutdown'));
|
|
169
|
+
logger.log(SEPARATOR);
|
|
170
|
+
logger.log('');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function logTeardownFooter({ devStr, removedCount, failedCount }) {
|
|
174
|
+
logFooterStart('Stopped');
|
|
175
|
+
logSection('Developer', devStr);
|
|
176
|
+
logSection(
|
|
177
|
+
'Cleaned',
|
|
178
|
+
`${successGlyph()} Removed ${removedCount} item(s) (${PRESERVE_FILE} and ${PRESERVE_CERTS_DIR}/ preserved)`
|
|
179
|
+
);
|
|
180
|
+
logger.log(chalk.yellow('⚠ Volumes removed: all local data deleted'));
|
|
181
|
+
logger.log('');
|
|
182
|
+
if (failedCount > 0) {
|
|
183
|
+
logger.log(chalk.yellow(`⚠ Could not remove ${failedCount} item(s)`));
|
|
184
|
+
logger.log('');
|
|
185
|
+
}
|
|
186
|
+
logFooterEnd();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function cleanFilesWithSpinner() {
|
|
190
|
+
const cleanSpin = startSpinner('Removing installation files...');
|
|
191
|
+
const result = cleanAifabrixSystemDir();
|
|
192
|
+
stopSpinnerSuccess(cleanSpin, `Removed ${result.removed.length} installation item(s)`);
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Run the teardown.
|
|
198
|
+
*
|
|
199
|
+
* @async
|
|
200
|
+
* @function handleTeardown
|
|
201
|
+
* @param {Object} [options] - Commander options
|
|
202
|
+
* @param {boolean} [options.yes] - Skip the confirmation prompt
|
|
203
|
+
* @returns {Promise<void>}
|
|
204
|
+
*/
|
|
205
|
+
async function handleTeardown(options = {}) {
|
|
206
|
+
const assumeYes = options.yes === true || options.assumeYes === true;
|
|
207
|
+
logTeardownHeader();
|
|
208
|
+
|
|
209
|
+
const ok = await confirmTeardown(assumeYes);
|
|
210
|
+
if (!ok) {
|
|
211
|
+
logger.log(chalk.yellow('Aborted by user.'));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
await stopInfraQuietly();
|
|
215
|
+
const { removed, failed } = cleanFilesWithSpinner();
|
|
216
|
+
const devStr = await buildDeveloperLabel();
|
|
217
|
+
logTeardownFooter({ devStr, removedCount: removed.length, failedCount: failed.length });
|
|
218
|
+
|
|
219
|
+
logger.log(formatSuccessParagraph('aifabrix teardown complete.'));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
handleTeardown,
|
|
224
|
+
cleanAifabrixSystemDir,
|
|
225
|
+
stopInfraQuietly,
|
|
226
|
+
PRESERVE_FILE,
|
|
227
|
+
PRESERVE_CERTS_DIR
|
|
228
|
+
};
|
|
@@ -20,6 +20,7 @@ const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
|
20
20
|
const { isYamlPath } = require('../utils/config-format');
|
|
21
21
|
const { copyTemplateFiles } = require('../validation/template');
|
|
22
22
|
const { ensureReadmeForAppPath, ensureReadmeForApp } = require('../app/readme');
|
|
23
|
+
const { refreshUrlsLocalRegistryFromBuilder } = require('../utils/urls-local-registry');
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Copy template to a target path if application config is missing there.
|
|
@@ -98,8 +99,9 @@ function patchEnvOutputPathInFile(configPath) {
|
|
|
98
99
|
function validateEnvOutputPathFolderOrNull(appName) {
|
|
99
100
|
if (!appName || typeof appName !== 'string') return;
|
|
100
101
|
const pathsToPatch = [pathsUtil.getBuilderPath(appName)];
|
|
102
|
+
const envBuilderRoot = process.env.AIFABRIX_BUILDER_DIR && String(process.env.AIFABRIX_BUILDER_DIR).trim();
|
|
101
103
|
const cwdBuilderPath = path.join(process.cwd(), 'builder', appName);
|
|
102
|
-
if (path.resolve(cwdBuilderPath) !== path.resolve(pathsToPatch[0])) {
|
|
104
|
+
if (envBuilderRoot && path.resolve(cwdBuilderPath) !== path.resolve(pathsToPatch[0])) {
|
|
103
105
|
pathsToPatch.push(cwdBuilderPath);
|
|
104
106
|
}
|
|
105
107
|
for (const appPath of pathsToPatch) {
|
|
@@ -176,49 +178,83 @@ function patchEnvOutputPathForDeployOnly(appName) {
|
|
|
176
178
|
*
|
|
177
179
|
* @returns {Promise<void>}
|
|
178
180
|
*/
|
|
179
|
-
async function applyUpPlatformForceConfig() {
|
|
181
|
+
async function applyUpPlatformForceConfig(opts = {}) {
|
|
182
|
+
const silent = Boolean(opts.silent);
|
|
180
183
|
const deviceCleared = await config.clearAllDeviceTokens();
|
|
181
184
|
const clientCleared = await config.clearAllClientTokens();
|
|
182
185
|
await config.setCurrentEnvironment('dev');
|
|
183
186
|
const defaultControllerUrl = await getDefaultControllerUrl();
|
|
184
187
|
await config.setControllerUrl(defaultControllerUrl);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
|
|
189
|
+
const summary = { deviceCleared, clientCleared, defaultControllerUrl, environment: 'dev' };
|
|
190
|
+
if (!silent) {
|
|
191
|
+
logger.log(
|
|
192
|
+
chalk.blue(
|
|
193
|
+
`--force: cleared ${deviceCleared} device token(s) and ${clientCleared} client token(s); ` +
|
|
194
|
+
`environment set to dev; default controller set to ${defaultControllerUrl} (run aifabrix login after platform is up)`
|
|
195
|
+
)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return summary;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* True when resolved path is the root itself or a subdirectory of an allowed builder root.
|
|
203
|
+
* @param {string} resolvedAppPath
|
|
204
|
+
* @param {string[]} allowedRoots - Absolute resolved directory roots
|
|
205
|
+
* @returns {boolean}
|
|
206
|
+
*/
|
|
207
|
+
function isUnderAllowedBuilderRoot(resolvedAppPath, allowedRoots) {
|
|
208
|
+
for (const root of allowedRoots) {
|
|
209
|
+
const r = path.resolve(root);
|
|
210
|
+
if (resolvedAppPath === r || resolvedAppPath.startsWith(r + path.sep)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
191
215
|
}
|
|
192
216
|
|
|
193
217
|
/**
|
|
194
|
-
* Removes builder app directories for the given app names. Only removes paths under
|
|
195
|
-
*
|
|
218
|
+
* Removes builder app directories for the given app names. Only removes paths under an allowed
|
|
219
|
+
* builder root (project `builder/` or `~/.aifabrix/builder/`) to prevent path traversal.
|
|
220
|
+
* Uses {@link pathsUtil.getBuilderPath} for each app (system apps may resolve under the config dir).
|
|
196
221
|
*
|
|
197
222
|
* @param {string[]} appNames - Application names (e.g. ['keycloak', 'miso-controller', 'dataplane'])
|
|
198
223
|
* @returns {Promise<void>}
|
|
199
|
-
* @throws {Error} If any path is outside builder
|
|
224
|
+
* @throws {Error} If any path is outside allowed builder roots (path traversal attempt)
|
|
200
225
|
*/
|
|
201
|
-
async function cleanBuilderAppDirs(appNames) {
|
|
226
|
+
async function cleanBuilderAppDirs(appNames, opts = {}) {
|
|
227
|
+
const silent = Boolean(opts.silent);
|
|
202
228
|
if (!Array.isArray(appNames) || appNames.length === 0) return;
|
|
203
|
-
const
|
|
229
|
+
const allowedRoots = [path.resolve(pathsUtil.getBuilderRoot()), path.resolve(pathsUtil.getSystemBuilderRoot())];
|
|
230
|
+
const cleaned = [];
|
|
204
231
|
for (const appName of appNames) {
|
|
205
232
|
if (!appName || typeof appName !== 'string') continue;
|
|
206
233
|
const appPath = path.resolve(pathsUtil.getBuilderPath(appName));
|
|
207
|
-
if (!
|
|
208
|
-
throw new Error(
|
|
234
|
+
if (!isUnderAllowedBuilderRoot(appPath, allowedRoots)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Path ${appPath} is outside allowed builder roots (${allowedRoots.join(', ')}); refusing to clean`
|
|
237
|
+
);
|
|
209
238
|
}
|
|
210
239
|
if (fs.existsSync(appPath)) {
|
|
211
240
|
fs.rmSync(appPath, { recursive: true });
|
|
212
|
-
|
|
241
|
+
cleaned.push(appName);
|
|
242
|
+
if (!silent) {
|
|
243
|
+
logger.log(chalk.blue(`Cleaned builder/${appName}`));
|
|
244
|
+
}
|
|
213
245
|
}
|
|
214
246
|
}
|
|
247
|
+
return cleaned;
|
|
215
248
|
}
|
|
216
249
|
|
|
217
250
|
/**
|
|
218
251
|
* Ensures builder app directory exists from template if application config is missing.
|
|
219
252
|
* If builder/<appName>/application config does not exist, copies from templates/applications/<appName>.
|
|
220
253
|
* Uses AIFABRIX_BUILDER_DIR when set (e.g. by up-miso/up-dataplane from config aifabrix-env-config).
|
|
221
|
-
* When
|
|
254
|
+
* When `process.env.AIFABRIX_BUILDER_DIR` is set and the primary app path differs from
|
|
255
|
+
* `cwd/builder/<appName>`, also copies into `cwd/builder/<appName>` so the repo tree is not empty
|
|
256
|
+
* while using a custom builder root. Skips that extra copy when no custom dir is in use (e.g.
|
|
257
|
+
* platform apps materialized only under the system builder root).
|
|
222
258
|
*
|
|
223
259
|
* @async
|
|
224
260
|
* @function ensureAppFromTemplate
|
|
@@ -241,8 +277,12 @@ async function ensureAppFromTemplate(appName) {
|
|
|
241
277
|
logger.log(formatSuccessLine(`Copied template for ${appName}`));
|
|
242
278
|
}
|
|
243
279
|
|
|
280
|
+
const envBuilderRoot = process.env.AIFABRIX_BUILDER_DIR && String(process.env.AIFABRIX_BUILDER_DIR).trim();
|
|
244
281
|
const cwdBuilderPath = path.join(process.cwd(), 'builder', appName);
|
|
245
|
-
if (
|
|
282
|
+
if (
|
|
283
|
+
envBuilderRoot &&
|
|
284
|
+
path.resolve(cwdBuilderPath) !== path.resolve(appPath)
|
|
285
|
+
) {
|
|
246
286
|
const cwdCopied = await ensureTemplateAtPath(appName, cwdBuilderPath);
|
|
247
287
|
if (cwdCopied) {
|
|
248
288
|
logger.log(chalk.blue(`Creating builder/${appName} in project (from template)...`));
|
|
@@ -254,11 +294,31 @@ async function ensureAppFromTemplate(appName) {
|
|
|
254
294
|
return primaryCopied;
|
|
255
295
|
}
|
|
256
296
|
|
|
297
|
+
/**
|
|
298
|
+
* For `aifabrix up-platform` only: align builder dir env with up-miso/up-dataplane, materialize all three
|
|
299
|
+
* platform apps from templates if missing, then refresh `~/.aifabrix/urls.local.yaml` so declarative
|
|
300
|
+
* `url://` expansion (e.g. cross-references between miso-controller, dataplane, keycloak) sees every
|
|
301
|
+
* app's port and pattern before Keycloak or Miso resolve their `.env` files.
|
|
302
|
+
*
|
|
303
|
+
* @returns {Promise<void>}
|
|
304
|
+
*/
|
|
305
|
+
async function prepareUrlsLocalRegistryForUpPlatform() {
|
|
306
|
+
const builderDir = await config.getAifabrixBuilderDir();
|
|
307
|
+
if (builderDir) {
|
|
308
|
+
process.env.AIFABRIX_BUILDER_DIR = path.resolve(builderDir);
|
|
309
|
+
}
|
|
310
|
+
await ensureAppFromTemplate('keycloak');
|
|
311
|
+
await ensureAppFromTemplate('miso-controller');
|
|
312
|
+
await ensureAppFromTemplate('dataplane');
|
|
313
|
+
refreshUrlsLocalRegistryFromBuilder(pathsUtil.getProjectRoot());
|
|
314
|
+
}
|
|
315
|
+
|
|
257
316
|
module.exports = {
|
|
258
317
|
applyUpPlatformForceConfig,
|
|
259
318
|
cleanBuilderAppDirs,
|
|
260
319
|
ensureAppFromTemplate,
|
|
261
320
|
patchEnvOutputPathForDeployOnly,
|
|
262
321
|
validateEnvOutputPathFolderOrNull,
|
|
263
|
-
getEnvOutputPathFolder
|
|
322
|
+
getEnvOutputPathFolder,
|
|
323
|
+
prepareUrlsLocalRegistryForUpPlatform
|
|
264
324
|
};
|
|
@@ -2,10 +2,10 @@ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test
|
|
|
2
2
|
/**
|
|
3
3
|
* AI Fabrix Builder - Up Dataplane Command
|
|
4
4
|
*
|
|
5
|
-
* Always local deployment:
|
|
6
|
-
* deployment manifest to Miso Controller, then runs
|
|
7
|
-
* (same as aifabrix deploy dataplane --local). If app is already
|
|
8
|
-
*
|
|
5
|
+
* Always local deployment: requires dev infra (same gate as up-miso), then registers or
|
|
6
|
+
* rotates dataplane in dev, sends deployment manifest to Miso Controller, then runs dataplane
|
|
7
|
+
* locally (same as aifabrix deploy dataplane --local). If app is already registered, uses
|
|
8
|
+
* rotate-secret; otherwise registers.
|
|
9
9
|
*
|
|
10
10
|
* @fileoverview up-dataplane command implementation
|
|
11
11
|
* @author AI Fabrix Team
|
|
@@ -29,6 +29,7 @@ const { checkApplicationExists } = require('../utils/app-existence');
|
|
|
29
29
|
const { checkHealthEndpoint } = require('../utils/health-check');
|
|
30
30
|
const { validateControllerUrl } = require('../utils/auth-config-validator');
|
|
31
31
|
const app = require('../app');
|
|
32
|
+
const { assertDevInfraUp } = require('./dev-infra-gate');
|
|
32
33
|
const { ensureAppFromTemplate, validateEnvOutputPathFolderOrNull } = require('./up-common');
|
|
33
34
|
|
|
34
35
|
const CONTROLLER_HEALTH_PATH = '/health';
|
|
@@ -130,7 +131,14 @@ async function registerOrRotateDataplane(options, controllerUrl, environmentKey,
|
|
|
130
131
|
*/
|
|
131
132
|
async function deployDataplaneToController(options) {
|
|
132
133
|
const imageOverride = options.image || buildDataplaneImageRef(options);
|
|
133
|
-
const deployOpts = {
|
|
134
|
+
const deployOpts = {
|
|
135
|
+
imageOverride,
|
|
136
|
+
image: imageOverride,
|
|
137
|
+
registryMode: options.registryMode,
|
|
138
|
+
// Guided up-platform already shows a top-level spinner for the dataplane step.
|
|
139
|
+
// Avoid nested deploy polling spinners/logs.
|
|
140
|
+
silentPoll: options.platformInstall === true
|
|
141
|
+
};
|
|
134
142
|
await app.deployApp('dataplane', deployOpts);
|
|
135
143
|
}
|
|
136
144
|
|
|
@@ -160,6 +168,17 @@ function buildDataplaneImageRef(options = {}) {
|
|
|
160
168
|
}
|
|
161
169
|
}
|
|
162
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Sets `AIFABRIX_BUILDER_DIR` when configured (shared by deploy/run paths).
|
|
173
|
+
* @returns {Promise<void>}
|
|
174
|
+
*/
|
|
175
|
+
async function applyAifabrixBuilderDirEnv() {
|
|
176
|
+
const builderDir = await config.getAifabrixBuilderDir();
|
|
177
|
+
if (builderDir) {
|
|
178
|
+
process.env.AIFABRIX_BUILDER_DIR = path.resolve(builderDir);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
163
182
|
/**
|
|
164
183
|
* Handle up-dataplane command: ensure logged in, environment dev, ensure dataplane,
|
|
165
184
|
* register or rotate (if already registered), deploy (send manifest to controller),
|
|
@@ -171,16 +190,18 @@ function buildDataplaneImageRef(options = {}) {
|
|
|
171
190
|
* @param {string} [options.registry] - Override registry for dataplane
|
|
172
191
|
* @param {string} [options.registryMode] - Override registry mode (acr|external)
|
|
173
192
|
* @param {string} [options.image] - Override image reference for dataplane
|
|
193
|
+
* @param {boolean} [options.skipInfraCheck] - When true, skip Postgres/Redis gate (caller already verified, e.g. guided up-dataplane).
|
|
174
194
|
* @returns {Promise<void>}
|
|
175
|
-
* @throws {Error} If not logged in, environment not dev, or any step fails
|
|
195
|
+
* @throws {Error} If infra not up, controller unavailable, not logged in, environment not dev, or any step fails
|
|
176
196
|
*/
|
|
177
197
|
async function handleUpDataplane(options = {}) {
|
|
178
|
-
|
|
179
|
-
if (builderDir) {
|
|
180
|
-
process.env.AIFABRIX_BUILDER_DIR = path.resolve(builderDir);
|
|
181
|
-
}
|
|
198
|
+
await applyAifabrixBuilderDirEnv();
|
|
182
199
|
logger.log(chalk.blue('Starting up-dataplane (register/rotate, deploy, then run dataplane locally)...\n'));
|
|
183
200
|
|
|
201
|
+
if (options.skipInfraCheck !== true) {
|
|
202
|
+
await assertDevInfraUp();
|
|
203
|
+
}
|
|
204
|
+
|
|
184
205
|
const controllerUrl = await resolveControllerUrlWithHealthCheck();
|
|
185
206
|
const environmentKey = await resolveEnvironment();
|
|
186
207
|
const authConfig = await checkAuthentication(controllerUrl, environmentKey, { throwOnFailure: true });
|
|
@@ -204,7 +225,8 @@ async function handleUpDataplane(options = {}) {
|
|
|
204
225
|
logger.log('');
|
|
205
226
|
await app.runApp('dataplane', {
|
|
206
227
|
skipEnvOutputPath: true,
|
|
207
|
-
registry: options.registry || undefined
|
|
228
|
+
registry: options.registry || undefined,
|
|
229
|
+
base: options.base !== false
|
|
208
230
|
});
|
|
209
231
|
|
|
210
232
|
logger.log(formatSuccessParagraph('up-dataplane complete. Dataplane is registered, deployed in dev, and running locally.'));
|
package/lib/commands/up-miso.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* AI Fabrix Builder - Up Miso Command
|
|
4
4
|
*
|
|
5
5
|
* Installs keycloak and miso-controller from images (no build). For dataplane, use up-dataplane.
|
|
6
|
-
*
|
|
6
|
+
* Ensures dev Postgres/Redis are up, then sets dev secrets and resolves (no force; existing .env values preserved).
|
|
7
7
|
*
|
|
8
8
|
* @fileoverview up-miso command implementation
|
|
9
9
|
* @author AI Fabrix Team
|
|
@@ -14,8 +14,8 @@ const path = require('path');
|
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const logger = require('../utils/logger');
|
|
16
16
|
const config = require('../core/config');
|
|
17
|
-
const infra = require('../infrastructure');
|
|
18
17
|
const app = require('../app');
|
|
18
|
+
const { assertDevInfraUp } = require('./dev-infra-gate');
|
|
19
19
|
const { ensureAppFromTemplate, patchEnvOutputPathForDeployOnly, validateEnvOutputPathFolderOrNull } = require('./up-common');
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -45,11 +45,13 @@ function parseImageOptions(imageOpts) {
|
|
|
45
45
|
*/
|
|
46
46
|
async function runMisoApps(options) {
|
|
47
47
|
const imageMap = parseImageOptions(options.image);
|
|
48
|
+
const useBaseImage = options.base !== false;
|
|
48
49
|
const common = {
|
|
49
50
|
registry: options.registry,
|
|
50
51
|
registryMode: options.registryMode,
|
|
51
52
|
skipEnvOutputPath: true,
|
|
52
|
-
skipInfraCheck: true
|
|
53
|
+
skipInfraCheck: true,
|
|
54
|
+
base: useBaseImage
|
|
53
55
|
};
|
|
54
56
|
const keycloakRunOpts = { ...common };
|
|
55
57
|
if (imageMap.keycloak) {
|
|
@@ -83,13 +85,7 @@ async function handleUpMiso(options = {}) {
|
|
|
83
85
|
process.env.AIFABRIX_BUILDER_DIR = path.resolve(builderDir);
|
|
84
86
|
}
|
|
85
87
|
logger.log(chalk.blue('Starting up-miso (keycloak + miso-controller from images)...\n'));
|
|
86
|
-
|
|
87
|
-
const health = await infra.checkInfraHealth(undefined, { strict: true });
|
|
88
|
-
const allHealthy = Object.values(health).every(status => status === 'healthy');
|
|
89
|
-
if (!allHealthy) {
|
|
90
|
-
throw new Error('Infrastructure is not up. Run \'aifabrix up-infra\' first.');
|
|
91
|
-
}
|
|
92
|
-
logger.log(formatSuccessLine('Infrastructure is up'));
|
|
88
|
+
await assertDevInfraUp();
|
|
93
89
|
await ensureAppFromTemplate('keycloak');
|
|
94
90
|
await ensureAppFromTemplate('miso-controller');
|
|
95
91
|
// If envOutputPath target folder does not exist, set envOutputPath to null
|