@aifabrix/builder 2.44.4 → 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 +68 -17
- 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/types/wizard.types.js +2 -1
- 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.help.js +1 -1
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-app.test-commands.js +9 -5
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +138 -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 +97 -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 +225 -19
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -354
- 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/test-e2e-external.js +4 -3
- package/lib/commands/up-common.js +97 -12
- 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 +58 -15
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +72 -14
- package/lib/commands/wizard-headless.js +7 -3
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +210 -61
- 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/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -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 +2 -2
- 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 +117 -4
- 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 +73 -20
- 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 +7 -7
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +89 -102
- 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
|
+
};
|
|
@@ -85,13 +85,14 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
88
|
+
* Publish local integration files to the dataplane before E2E (same path as `aifabrix upload <systemKey>`),
|
|
89
|
+
* unless ``options.noSync`` is true.
|
|
89
90
|
* @param {string} systemKey
|
|
90
91
|
* @param {Object} options
|
|
91
92
|
* @returns {Promise<void>}
|
|
92
93
|
*/
|
|
93
94
|
async function syncLocalIfRequested(systemKey, options) {
|
|
94
|
-
if (options.
|
|
95
|
+
if (options.noSync === true) return;
|
|
95
96
|
logger.log(chalk.cyan('Syncing local config to dataplane…'));
|
|
96
97
|
const { uploadExternalSystem } = require('./upload');
|
|
97
98
|
await uploadExternalSystem(systemKey, {
|
|
@@ -112,7 +113,7 @@ async function syncLocalIfRequested(systemKey, options) {
|
|
|
112
113
|
* @param {boolean} [options.debug] - Include debug, write log
|
|
113
114
|
* @param {boolean} [options.verbose] - Verbose output
|
|
114
115
|
* @param {boolean} [options.async] - If false, sync mode (default true)
|
|
115
|
-
* @param {boolean} [options.
|
|
116
|
+
* @param {boolean} [options.noSync] - When true, skip upload (E2E uses dataplane config already deployed)
|
|
116
117
|
* @returns {Promise<{ success: boolean, results: Array<{ key: string, success: boolean, error?: string }> }>}
|
|
117
118
|
*/
|
|
118
119
|
async function runTestE2EForExternalSystem(externalSystem, options = {}) {
|
|
@@ -13,11 +13,14 @@ const path = require('path');
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const logger = require('../utils/logger');
|
|
16
|
+
const config = require('../core/config');
|
|
17
|
+
const { getDefaultControllerUrl } = require('../utils/controller-url');
|
|
16
18
|
const pathsUtil = require('../utils/paths');
|
|
17
19
|
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
18
20
|
const { isYamlPath } = require('../utils/config-format');
|
|
19
21
|
const { copyTemplateFiles } = require('../validation/template');
|
|
20
22
|
const { ensureReadmeForAppPath, ensureReadmeForApp } = require('../app/readme');
|
|
23
|
+
const { refreshUrlsLocalRegistryFromBuilder } = require('../utils/urls-local-registry');
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* Copy template to a target path if application config is missing there.
|
|
@@ -96,8 +99,9 @@ function patchEnvOutputPathInFile(configPath) {
|
|
|
96
99
|
function validateEnvOutputPathFolderOrNull(appName) {
|
|
97
100
|
if (!appName || typeof appName !== 'string') return;
|
|
98
101
|
const pathsToPatch = [pathsUtil.getBuilderPath(appName)];
|
|
102
|
+
const envBuilderRoot = process.env.AIFABRIX_BUILDER_DIR && String(process.env.AIFABRIX_BUILDER_DIR).trim();
|
|
99
103
|
const cwdBuilderPath = path.join(process.cwd(), 'builder', appName);
|
|
100
|
-
if (path.resolve(cwdBuilderPath) !== path.resolve(pathsToPatch[0])) {
|
|
104
|
+
if (envBuilderRoot && path.resolve(cwdBuilderPath) !== path.resolve(pathsToPatch[0])) {
|
|
101
105
|
pathsToPatch.push(cwdBuilderPath);
|
|
102
106
|
}
|
|
103
107
|
for (const appPath of pathsToPatch) {
|
|
@@ -167,34 +171,90 @@ function patchEnvOutputPathForDeployOnly(appName) {
|
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
174
|
+
* For ``up-platform --force`` only: clear all stored auth tokens, set ``environment`` to ``dev``,
|
|
175
|
+
* set ``controller`` to the default URL for the current ``developer-id`` (``http://localhost`` +
|
|
176
|
+
* app port = 3000 + developerId × 100), and persist config. Does not change ``developer-id``.
|
|
177
|
+
* Builder dirs (keycloak, miso-controller, dataplane) are cleaned separately.
|
|
178
|
+
*
|
|
179
|
+
* @returns {Promise<void>}
|
|
180
|
+
*/
|
|
181
|
+
async function applyUpPlatformForceConfig(opts = {}) {
|
|
182
|
+
const silent = Boolean(opts.silent);
|
|
183
|
+
const deviceCleared = await config.clearAllDeviceTokens();
|
|
184
|
+
const clientCleared = await config.clearAllClientTokens();
|
|
185
|
+
await config.setCurrentEnvironment('dev');
|
|
186
|
+
const defaultControllerUrl = await getDefaultControllerUrl();
|
|
187
|
+
await config.setControllerUrl(defaultControllerUrl);
|
|
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;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
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).
|
|
172
221
|
*
|
|
173
222
|
* @param {string[]} appNames - Application names (e.g. ['keycloak', 'miso-controller', 'dataplane'])
|
|
174
223
|
* @returns {Promise<void>}
|
|
175
|
-
* @throws {Error} If any path is outside builder
|
|
224
|
+
* @throws {Error} If any path is outside allowed builder roots (path traversal attempt)
|
|
176
225
|
*/
|
|
177
|
-
async function cleanBuilderAppDirs(appNames) {
|
|
226
|
+
async function cleanBuilderAppDirs(appNames, opts = {}) {
|
|
227
|
+
const silent = Boolean(opts.silent);
|
|
178
228
|
if (!Array.isArray(appNames) || appNames.length === 0) return;
|
|
179
|
-
const
|
|
229
|
+
const allowedRoots = [path.resolve(pathsUtil.getBuilderRoot()), path.resolve(pathsUtil.getSystemBuilderRoot())];
|
|
230
|
+
const cleaned = [];
|
|
180
231
|
for (const appName of appNames) {
|
|
181
232
|
if (!appName || typeof appName !== 'string') continue;
|
|
182
233
|
const appPath = path.resolve(pathsUtil.getBuilderPath(appName));
|
|
183
|
-
if (!
|
|
184
|
-
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
|
+
);
|
|
185
238
|
}
|
|
186
239
|
if (fs.existsSync(appPath)) {
|
|
187
240
|
fs.rmSync(appPath, { recursive: true });
|
|
188
|
-
|
|
241
|
+
cleaned.push(appName);
|
|
242
|
+
if (!silent) {
|
|
243
|
+
logger.log(chalk.blue(`Cleaned builder/${appName}`));
|
|
244
|
+
}
|
|
189
245
|
}
|
|
190
246
|
}
|
|
247
|
+
return cleaned;
|
|
191
248
|
}
|
|
192
249
|
|
|
193
250
|
/**
|
|
194
251
|
* Ensures builder app directory exists from template if application config is missing.
|
|
195
252
|
* If builder/<appName>/application config does not exist, copies from templates/applications/<appName>.
|
|
196
253
|
* Uses AIFABRIX_BUILDER_DIR when set (e.g. by up-miso/up-dataplane from config aifabrix-env-config).
|
|
197
|
-
* 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).
|
|
198
258
|
*
|
|
199
259
|
* @async
|
|
200
260
|
* @function ensureAppFromTemplate
|
|
@@ -217,8 +277,12 @@ async function ensureAppFromTemplate(appName) {
|
|
|
217
277
|
logger.log(formatSuccessLine(`Copied template for ${appName}`));
|
|
218
278
|
}
|
|
219
279
|
|
|
280
|
+
const envBuilderRoot = process.env.AIFABRIX_BUILDER_DIR && String(process.env.AIFABRIX_BUILDER_DIR).trim();
|
|
220
281
|
const cwdBuilderPath = path.join(process.cwd(), 'builder', appName);
|
|
221
|
-
if (
|
|
282
|
+
if (
|
|
283
|
+
envBuilderRoot &&
|
|
284
|
+
path.resolve(cwdBuilderPath) !== path.resolve(appPath)
|
|
285
|
+
) {
|
|
222
286
|
const cwdCopied = await ensureTemplateAtPath(appName, cwdBuilderPath);
|
|
223
287
|
if (cwdCopied) {
|
|
224
288
|
logger.log(chalk.blue(`Creating builder/${appName} in project (from template)...`));
|
|
@@ -230,10 +294,31 @@ async function ensureAppFromTemplate(appName) {
|
|
|
230
294
|
return primaryCopied;
|
|
231
295
|
}
|
|
232
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
|
+
|
|
233
316
|
module.exports = {
|
|
317
|
+
applyUpPlatformForceConfig,
|
|
234
318
|
cleanBuilderAppDirs,
|
|
235
319
|
ensureAppFromTemplate,
|
|
236
320
|
patchEnvOutputPathForDeployOnly,
|
|
237
321
|
validateEnvOutputPathFolderOrNull,
|
|
238
|
-
getEnvOutputPathFolder
|
|
322
|
+
getEnvOutputPathFolder,
|
|
323
|
+
prepareUrlsLocalRegistryForUpPlatform
|
|
239
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
|