@aifabrix/builder 2.40.2 → 2.42.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/docs-rules.mdc +30 -0
- package/README.md +7 -5
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/integration/hubspot/test.js +1 -1
- package/jest.config.manual.js +2 -1
- package/lib/api/credential.api.js +40 -0
- package/lib/api/dev.api.js +423 -0
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/credential.types.js +23 -0
- package/lib/api/types/dev.types.js +140 -0
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +44 -11
- package/lib/app/down.js +2 -1
- package/lib/app/index.js +12 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/push.js +36 -12
- package/lib/app/readme.js +9 -6
- package/lib/app/run-env-compose.js +264 -0
- package/lib/app/run-helpers.js +121 -118
- package/lib/app/run.js +148 -28
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +5 -2
- package/lib/build/index.js +11 -3
- package/lib/cli/setup-app.js +172 -15
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +206 -16
- package/lib/cli/setup-environment.js +16 -6
- package/lib/cli/setup-external-system.js +89 -24
- package/lib/cli/setup-infra.js +82 -15
- package/lib/cli/setup-secrets.js +52 -5
- package/lib/cli/setup-utility.js +129 -24
- package/lib/commands/app-install.js +172 -0
- package/lib/commands/app-shell.js +75 -0
- package/lib/commands/app-test.js +282 -0
- package/lib/commands/app.js +1 -1
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-cli-handlers.js +141 -0
- package/lib/commands/dev-down.js +114 -0
- package/lib/commands/dev-init.js +347 -0
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/secrets-list.js +118 -0
- package/lib/commands/secrets-remove.js +97 -0
- package/lib/commands/secrets-set.js +30 -17
- package/lib/commands/secrets-validate.js +50 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/commands/up-miso.js +0 -25
- package/lib/commands/upload.js +96 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/admin-secrets.js +96 -0
- package/lib/core/config.js +7 -1
- package/lib/core/secrets-ensure.js +378 -0
- package/lib/core/secrets-env-write.js +157 -0
- package/lib/core/secrets.js +176 -89
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/field-reference-validator.js +91 -0
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/datasource/validate.js +21 -3
- package/lib/deployment/deployer.js +7 -5
- package/lib/deployment/environment-config.js +137 -0
- package/lib/deployment/environment.js +21 -98
- package/lib/deployment/push.js +32 -2
- package/lib/external-system/download.js +188 -203
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-auth.js +7 -3
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +56 -19
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +177 -25
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +155 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/helpers.js +103 -20
- package/lib/infrastructure/index.js +98 -12
- package/lib/infrastructure/services.js +88 -22
- package/lib/schema/application-schema.json +32 -8
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +509 -411
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +41 -13
- package/lib/utils/app-register-auth.js +25 -3
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/cli-utils.js +20 -0
- package/lib/utils/compose-generator.js +77 -76
- package/lib/utils/compose-handlebars-helpers.js +54 -0
- package/lib/utils/compose-vector-helper.js +18 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/config-paths.js +127 -2
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +357 -0
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/dev-cert-helper.js +122 -0
- package/lib/utils/device-code-helpers.js +224 -0
- package/lib/utils/device-code.js +37 -336
- package/lib/utils/docker-build.js +40 -8
- package/lib/utils/env-copy.js +103 -13
- package/lib/utils/env-map.js +35 -5
- package/lib/utils/env-template.js +6 -5
- package/lib/utils/error-formatters/http-status-errors.js +20 -2
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +16 -2
- package/lib/utils/infra-status.js +80 -45
- package/lib/utils/local-secrets.js +7 -52
- package/lib/utils/mutagen-install.js +195 -0
- package/lib/utils/mutagen.js +146 -0
- package/lib/utils/paths.js +128 -37
- package/lib/utils/port-resolver.js +28 -16
- package/lib/utils/remote-dev-auth.js +38 -0
- package/lib/utils/remote-docker-env.js +43 -0
- package/lib/utils/remote-secrets-loader.js +60 -0
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +114 -6
- package/lib/utils/secrets-helpers.js +108 -114
- package/lib/utils/secrets-path.js +2 -2
- package/lib/utils/secrets-utils.js +52 -1
- package/lib/utils/secrets-validation.js +84 -0
- package/lib/utils/ssh-key-helper.js +116 -0
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager-messages.js +90 -0
- package/lib/utils/token-manager.js +29 -36
- package/lib/utils/variable-transformer.js +3 -3
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +72 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +8 -3
- package/scripts/install-local.js +34 -15
- package/templates/README.md +0 -1
- package/templates/applications/README.md.hbs +4 -4
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +15 -10
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/keycloak/env.template +2 -0
- package/templates/applications/miso-controller/application.yaml +1 -0
- package/templates/applications/miso-controller/env.template +12 -10
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +49 -23
- package/templates/typescript/docker-compose.hbs +48 -22
- package/integration/hubspot/application.yaml +0 -37
package/lib/app/run.js
CHANGED
|
@@ -14,14 +14,50 @@ const { exec } = require('child_process');
|
|
|
14
14
|
const { promisify } = require('util');
|
|
15
15
|
const config = require('../core/config');
|
|
16
16
|
const logger = require('../utils/logger');
|
|
17
|
+
const pathsUtil = require('../utils/paths');
|
|
17
18
|
const { checkPortAvailable, waitForHealthCheck } = require('../utils/health-check');
|
|
18
19
|
const composeGenerator = require('../utils/compose-generator');
|
|
19
20
|
const containerHelpers = require('../utils/app-run-containers');
|
|
21
|
+
const mutagen = require('../utils/mutagen');
|
|
20
22
|
// Helper functions extracted to reduce file size and complexity
|
|
21
23
|
const helpers = require('./run-helpers');
|
|
22
24
|
|
|
23
25
|
const execAsync = promisify(exec);
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* True if host is localhost or 127.0.0.1 (case-insensitive).
|
|
29
|
+
* @param {string} host - Hostname or empty
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
function isLocalhostHost(host) {
|
|
33
|
+
if (!host || typeof host !== 'string') return false;
|
|
34
|
+
const h = host.trim().toLowerCase();
|
|
35
|
+
return h === 'localhost' || h === '127.0.0.1';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* True if the given URL or endpoint string refers to localhost (host is localhost or 127.0.0.1).
|
|
40
|
+
* Handles tcp://localhost:2376, https://127.0.0.1:8443, etc.
|
|
41
|
+
* @param {string} urlOrEndpoint - URL (e.g. https://localhost:8443) or Docker endpoint (e.g. tcp://localhost:2376)
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
function isLocalhostEndpoint(urlOrEndpoint) {
|
|
45
|
+
if (!urlOrEndpoint || typeof urlOrEndpoint !== 'string') return false;
|
|
46
|
+
const s = urlOrEndpoint.trim();
|
|
47
|
+
if (!s) return false;
|
|
48
|
+
try {
|
|
49
|
+
if (s.startsWith('tcp://') || s.startsWith('unix://')) {
|
|
50
|
+
const rest = s.replace(/^tcp:\/\//i, '').replace(/^unix:\/\//i, '');
|
|
51
|
+
const host = rest.split('/')[0].split(':')[0];
|
|
52
|
+
return isLocalhostHost(host);
|
|
53
|
+
}
|
|
54
|
+
const u = new URL(s);
|
|
55
|
+
return isLocalhostHost(u.hostname);
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
25
61
|
/**
|
|
26
62
|
* Validate app for run and check if it's an external system
|
|
27
63
|
* @async
|
|
@@ -106,6 +142,48 @@ async function calculateHostPort(appConfig, options, debug) {
|
|
|
106
142
|
return hostPort;
|
|
107
143
|
}
|
|
108
144
|
|
|
145
|
+
/**
|
|
146
|
+
* When run --reload in dev with remote: ensure Mutagen sync session; return remote path for compose mount.
|
|
147
|
+
* Uses codePath (resolved build.context) as Mutagen local path so one config field drives both local and remote.
|
|
148
|
+
* When Docker endpoint, remote-server, or sync-ssh-host is localhost/127.0.0.1, returns null (no sync; use local path).
|
|
149
|
+
*
|
|
150
|
+
* @param {string} appName - Application name
|
|
151
|
+
* @param {string} developerId - Developer ID
|
|
152
|
+
* @param {boolean} debug - Debug flag
|
|
153
|
+
* @param {string} codePath - Resolved build.context (absolute path to app code)
|
|
154
|
+
* @param {string} [remoteSyncPath] - Optional relative path under user-mutagen-folder (from build.remoteSyncPath); when unset, defaults to dev/<appKey>
|
|
155
|
+
* @returns {Promise<string|null>} Remote path for -v mount or null if not remote/reload or already on server (localhost)
|
|
156
|
+
* @throws {Error} If --reload but remote not configured, or Mutagen install fails
|
|
157
|
+
*/
|
|
158
|
+
async function ensureReloadSync(appName, developerId, debug, codePath, remoteSyncPath) {
|
|
159
|
+
const endpoint = await config.getDockerEndpoint();
|
|
160
|
+
const serverUrl = await config.getRemoteServer();
|
|
161
|
+
if (!endpoint && !serverUrl) return null;
|
|
162
|
+
const syncSshHost = await config.getSyncSshHost();
|
|
163
|
+
if (isLocalhostEndpoint(endpoint) || isLocalhostEndpoint(serverUrl) || isLocalhostHost(syncSshHost || '')) {
|
|
164
|
+
if (debug) logger.log(chalk.gray('[DEBUG] Docker/remote/sync host is localhost; skipping Mutagen, using local path'));
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const [userMutagenFolder, syncSshUser] = await Promise.all([
|
|
168
|
+
config.getUserMutagenFolder(),
|
|
169
|
+
config.getSyncSshUser()
|
|
170
|
+
]);
|
|
171
|
+
if (!userMutagenFolder || !syncSshUser || !syncSshHost) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'run --reload requires remote server sync settings. Run "aifabrix dev init" or set user-mutagen-folder, sync-ssh-user, sync-ssh-host in config.'
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
const mutagenPath = await mutagen.ensureMutagenPath(logger.log);
|
|
177
|
+
const remotePath = mutagen.getRemotePath(userMutagenFolder, appName, remoteSyncPath);
|
|
178
|
+
const sshUrl = mutagen.getSyncSshUrl(syncSshUser, syncSshHost, remotePath);
|
|
179
|
+
const sessionName = mutagen.getSessionName(developerId, appName);
|
|
180
|
+
const localPath = (codePath && typeof codePath === 'string') ? codePath : pathsUtil.getBuilderPath(appName);
|
|
181
|
+
if (debug) logger.log(chalk.gray(`[DEBUG] Mutagen sync: ${sessionName} ${localPath} <-> ${sshUrl}`));
|
|
182
|
+
logger.log(chalk.blue('Ensuring Mutagen sync session...'));
|
|
183
|
+
await mutagen.ensureSyncSession(mutagenPath, sessionName, localPath, sshUrl);
|
|
184
|
+
return remotePath;
|
|
185
|
+
}
|
|
186
|
+
|
|
109
187
|
/**
|
|
110
188
|
* Load and configure application
|
|
111
189
|
* @async
|
|
@@ -132,16 +210,20 @@ async function loadAndConfigureApp(appName, debug) {
|
|
|
132
210
|
* @param {string} tempComposePath - Path to compose file
|
|
133
211
|
* @param {number} hostPort - Host port
|
|
134
212
|
* @param {Object} appConfig - Application configuration
|
|
135
|
-
* @param {
|
|
213
|
+
* @param {Object} opts - Options (debug, runEnvPath, runEnvAdminPath)
|
|
136
214
|
* @throws {Error} If container start fails
|
|
137
215
|
*/
|
|
138
|
-
async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
|
|
216
|
+
async function startAppContainer(appName, tempComposePath, hostPort, appConfig, opts) {
|
|
217
|
+
const { debug, runEnvPath = null, runEnvAdminPath = null } = opts;
|
|
139
218
|
try {
|
|
140
|
-
await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, debug);
|
|
219
|
+
await helpers.startContainer(appName, tempComposePath, hostPort, appConfig, { debug, runEnvPath, runEnvAdminPath });
|
|
141
220
|
await helpers.displayRunStatus(appName, hostPort, appConfig);
|
|
142
221
|
} catch (error) {
|
|
143
222
|
logger.log(chalk.yellow(`\n⚠️ Compose file preserved at: ${tempComposePath}`));
|
|
144
223
|
logger.log(chalk.yellow(' Review the file to debug issues'));
|
|
224
|
+
if (runEnvPath || runEnvAdminPath) {
|
|
225
|
+
logger.log(chalk.yellow(' Run .env file(s) (contain secrets) were not deleted; remove them manually if desired.'));
|
|
226
|
+
}
|
|
145
227
|
if (debug) {
|
|
146
228
|
logger.log(chalk.gray(`[DEBUG] Error during container start: ${error.message}`));
|
|
147
229
|
}
|
|
@@ -149,6 +231,54 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
|
|
|
149
231
|
}
|
|
150
232
|
}
|
|
151
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Resolve run options (paths and optional reload sync) for prepareAppRun.
|
|
236
|
+
* @param {string} appName - Application name
|
|
237
|
+
* @param {Object} appConfig - Application configuration
|
|
238
|
+
* @param {Object} options - Run options
|
|
239
|
+
* @param {string} envKey - Environment key (dev/tst/pro)
|
|
240
|
+
* @param {boolean} debug - Debug flag
|
|
241
|
+
* @returns {Promise<Object>} Run options with optional devMountPath
|
|
242
|
+
*/
|
|
243
|
+
async function resolveRunOptions(appName, appConfig, options, envKey, debug) {
|
|
244
|
+
const runOptions = { ...options, env: envKey };
|
|
245
|
+
const builderPath = pathsUtil.getBuilderPath(appName);
|
|
246
|
+
const codePath = pathsUtil.resolveBuildContext(builderPath, appConfig.build?.context || '.');
|
|
247
|
+
if (options.reload && envKey === 'dev') {
|
|
248
|
+
const remoteSyncPath = appConfig.build?.remoteSyncPath;
|
|
249
|
+
const remotePath = await ensureReloadSync(appName, appConfig.developerId, debug, codePath, remoteSyncPath);
|
|
250
|
+
runOptions.devMountPath = remotePath || codePath;
|
|
251
|
+
}
|
|
252
|
+
return runOptions;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Prepare run: validate app, load config, check prereqs, stop existing container, resolve port and reload, prepare env.
|
|
257
|
+
* @param {string} appName - Application name
|
|
258
|
+
* @param {Object} options - Run options
|
|
259
|
+
* @param {boolean} debug - Debug flag
|
|
260
|
+
* @returns {Promise<{ appConfig: Object, tempComposePath: string, hostPort: number }|null>} Prepared run context or null if should not continue
|
|
261
|
+
*/
|
|
262
|
+
async function prepareAppRun(appName, options, debug) {
|
|
263
|
+
const envKey = (options.env || 'dev').toLowerCase();
|
|
264
|
+
if (envKey !== 'dev' && envKey !== 'tst' && envKey !== 'pro') {
|
|
265
|
+
throw new Error('--env must be dev, tst, or pro');
|
|
266
|
+
}
|
|
267
|
+
const shouldContinue = await validateAppForRun(appName, debug);
|
|
268
|
+
if (!shouldContinue) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const appConfig = await loadAndConfigureApp(appName, debug);
|
|
272
|
+
await helpers.checkPrerequisites(appName, appConfig, debug, options.skipInfraCheck === true, options);
|
|
273
|
+
await checkAndStopContainer(appName, appConfig.developerId, debug);
|
|
274
|
+
const hostPort = await calculateHostPort(appConfig, options, debug);
|
|
275
|
+
const runOptions = await resolveRunOptions(appName, appConfig, options, envKey, debug);
|
|
276
|
+
const { composePath: tempComposePath, runEnvPath, runEnvAdminPath } = await helpers.prepareEnvironment(appName, appConfig, runOptions);
|
|
277
|
+
const result = { appConfig, tempComposePath, hostPort, runEnvPath, runEnvAdminPath };
|
|
278
|
+
if (runOptions.devMountPath) result.devMountPath = runOptions.devMountPath;
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
|
|
152
282
|
/**
|
|
153
283
|
* Runs the application locally using Docker
|
|
154
284
|
* Starts container with proper port mapping and environment
|
|
@@ -168,40 +298,27 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
|
|
|
168
298
|
*/
|
|
169
299
|
async function runApp(appName, options = {}) {
|
|
170
300
|
const debug = options.debug || false;
|
|
171
|
-
|
|
172
301
|
if (debug) {
|
|
173
302
|
logger.log(chalk.gray(`[DEBUG] Starting run process for: ${appName}`));
|
|
174
303
|
logger.log(chalk.gray(`[DEBUG] Options: ${JSON.stringify(options, null, 2)}`));
|
|
175
304
|
}
|
|
176
|
-
|
|
177
305
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!shouldContinue) {
|
|
306
|
+
const prepared = await prepareAppRun(appName, options, debug);
|
|
307
|
+
if (!prepared) {
|
|
181
308
|
return;
|
|
182
309
|
}
|
|
183
|
-
|
|
184
|
-
// Load and configure application
|
|
185
|
-
const appConfig = await loadAndConfigureApp(appName, debug);
|
|
186
|
-
|
|
187
|
-
// Check prerequisites: image and (unless skipped) infrastructure
|
|
188
|
-
await helpers.checkPrerequisites(appName, appConfig, debug, options.skipInfraCheck === true);
|
|
189
|
-
|
|
190
|
-
// Check if container is already running and stop it if needed
|
|
191
|
-
await checkAndStopContainer(appName, appConfig.developerId, debug);
|
|
192
|
-
|
|
193
|
-
// Calculate host port and validate it's available
|
|
194
|
-
const hostPort = await calculateHostPort(appConfig, options, debug);
|
|
195
|
-
|
|
196
|
-
// Prepare environment: ensure .env file and generate Docker Compose
|
|
197
|
-
const tempComposePath = await helpers.prepareEnvironment(appName, appConfig, options);
|
|
198
310
|
if (debug) {
|
|
199
|
-
logger.log(chalk.gray(`[DEBUG] Compose file generated: ${tempComposePath}`));
|
|
311
|
+
logger.log(chalk.gray(`[DEBUG] Compose file generated: ${prepared.tempComposePath}`));
|
|
200
312
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
313
|
+
if (options.reload && prepared.devMountPath) {
|
|
314
|
+
logger.log(chalk.gray('With --reload: workspace mounted from host at /app (container runs as your user for write access).'));
|
|
315
|
+
logger.log(chalk.gray(` Host path: ${prepared.devMountPath}`));
|
|
316
|
+
}
|
|
317
|
+
await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
|
|
318
|
+
debug,
|
|
319
|
+
runEnvPath: prepared.runEnvPath,
|
|
320
|
+
runEnvAdminPath: prepared.runEnvAdminPath
|
|
321
|
+
});
|
|
205
322
|
} catch (error) {
|
|
206
323
|
if (debug) {
|
|
207
324
|
logger.log(chalk.gray(`[DEBUG] Run failed: ${error.message}`));
|
|
@@ -243,6 +360,9 @@ async function restartApp(appName) {
|
|
|
243
360
|
module.exports = {
|
|
244
361
|
runApp,
|
|
245
362
|
restartApp,
|
|
363
|
+
ensureReloadSync,
|
|
364
|
+
isLocalhostHost,
|
|
365
|
+
isLocalhostEndpoint,
|
|
246
366
|
checkImageExists: helpers.checkImageExists,
|
|
247
367
|
checkContainerRunning: helpers.checkContainerRunning,
|
|
248
368
|
stopAndRemoveContainer: helpers.stopAndRemoveContainer,
|
package/lib/app/show-display.js
CHANGED
|
@@ -161,7 +161,7 @@ function logExternalSystemMain(ext) {
|
|
|
161
161
|
logger.log(` Credential: ${ext.credentialId ?? '—'}`);
|
|
162
162
|
logger.log(` Status: ${ext.status ?? '—'}`);
|
|
163
163
|
logger.log(` API docs: ${ext.openApiDocsPageUrl ?? ext.apiDocumentUrl ?? '—'}`);
|
|
164
|
-
logger.log(` MCP server:
|
|
164
|
+
logger.log(` MCP server: ${ext.mcpServerUrl ?? '—'}`);
|
|
165
165
|
logger.log(` OpenAPI spec: ${ext.apiDocumentUrl ?? '—'}`);
|
|
166
166
|
}
|
|
167
167
|
|
package/lib/app/show.js
CHANGED
|
@@ -171,7 +171,9 @@ function healthStrFromVariables(variables) {
|
|
|
171
171
|
function buildStrFromVariables(variables) {
|
|
172
172
|
const build = variables.build;
|
|
173
173
|
if (!build) return '—';
|
|
174
|
-
|
|
174
|
+
const port = variables.port ?? build.containerPort;
|
|
175
|
+
const portStr = (port !== undefined && port !== null) ? `port ${port}` : '';
|
|
176
|
+
return [build.language || '—', portStr].filter(Boolean).join(', ') || '—';
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
function buildApplicationFromVariables(variables) {
|
|
@@ -438,7 +440,8 @@ function formatBuildForDisplay(build) {
|
|
|
438
440
|
if (!build) return '—';
|
|
439
441
|
const parts = [];
|
|
440
442
|
if (build.language) parts.push(build.language);
|
|
441
|
-
|
|
443
|
+
const port = build.port ?? build.localPort;
|
|
444
|
+
if (port !== undefined && port !== null) parts.push(`port ${port}`);
|
|
442
445
|
if (build.dockerfile) parts.push('dockerfile');
|
|
443
446
|
if (build.envOutputPath) parts.push(`envOutputPath: ${build.envOutputPath}`);
|
|
444
447
|
return parts.length ? parts.join(', ') : '—';
|
package/lib/build/index.js
CHANGED
|
@@ -26,6 +26,7 @@ const dockerBuild = require('../utils/docker-build');
|
|
|
26
26
|
const buildCopy = require('../utils/build-copy');
|
|
27
27
|
const { buildDevImageName } = require('../utils/image-name');
|
|
28
28
|
const buildHelpers = require('../utils/build-helpers');
|
|
29
|
+
const secretsEnvWrite = require('../core/secrets-env-write');
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Loads application config for an application
|
|
@@ -410,11 +411,18 @@ async function buildApp(appName, options = {}) {
|
|
|
410
411
|
appConfig
|
|
411
412
|
}, options, buildHelpers);
|
|
412
413
|
|
|
413
|
-
// 5.
|
|
414
|
+
// 5. Resolve NPM_TOKEN/PYPI_TOKEN for private registries and pass as build-args
|
|
415
|
+
const envMap = await secretsEnvWrite.resolveAndGetEnvMap(appName, { environment: 'docker' });
|
|
416
|
+
const buildArgs = {};
|
|
417
|
+
if (envMap.NPM_TOKEN) buildArgs.NPM_TOKEN = envMap.NPM_TOKEN;
|
|
418
|
+
if (envMap.PYPI_TOKEN) buildArgs.PYPI_TOKEN = envMap.PYPI_TOKEN;
|
|
419
|
+
const buildOptions = { ...options, buildArgs };
|
|
420
|
+
|
|
421
|
+
// 6. Execute Docker build
|
|
414
422
|
const tag = options.tag || 'latest';
|
|
415
|
-
await dockerBuild.executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag,
|
|
423
|
+
await dockerBuild.executeDockerBuildWithTag(effectiveImageName, imageName, dockerfilePath, contextPath, tag, buildOptions);
|
|
416
424
|
|
|
417
|
-
//
|
|
425
|
+
// 7. Post-build tasks
|
|
418
426
|
await postBuildTasks(appName, buildConfig);
|
|
419
427
|
|
|
420
428
|
logger.log(chalk.green('\n✅ Build completed successfully!'));
|
package/lib/cli/setup-app.js
CHANGED
|
@@ -17,12 +17,20 @@ const { handleCommandError } = require('../utils/cli-utils');
|
|
|
17
17
|
* @param {Object} options - Raw CLI options
|
|
18
18
|
* @returns {Object} Normalized options
|
|
19
19
|
*/
|
|
20
|
+
const VALID_ENTITY_TYPES = ['recordStorage', 'documentStorage', 'vectorStore', 'messageService', 'none'];
|
|
21
|
+
|
|
20
22
|
function normalizeExternalOptions(options) {
|
|
21
23
|
const normalized = { ...options };
|
|
22
24
|
if (options.displayName) normalized.systemDisplayName = options.displayName;
|
|
23
25
|
if (options.description) normalized.systemDescription = options.description;
|
|
24
26
|
if (options.systemType) normalized.systemType = options.systemType;
|
|
25
27
|
if (options.authType) normalized.authType = options.authType;
|
|
28
|
+
if (options.entityType) {
|
|
29
|
+
if (!VALID_ENTITY_TYPES.includes(options.entityType)) {
|
|
30
|
+
throw new Error(`Invalid --entity-type. Must be one of: ${VALID_ENTITY_TYPES.join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
normalized.entityType = options.entityType;
|
|
33
|
+
}
|
|
26
34
|
if (options.datasources !== undefined) {
|
|
27
35
|
const parsedCount = parseInt(options.datasources, 10);
|
|
28
36
|
if (Number.isNaN(parsedCount) || parsedCount < 1 || parsedCount > 10) {
|
|
@@ -48,6 +56,7 @@ function validateNonInteractiveExternalOptions(normalizedOptions) {
|
|
|
48
56
|
if (!normalizedOptions.systemDescription) missing.push('--description');
|
|
49
57
|
if (!normalizedOptions.systemType) missing.push('--system-type');
|
|
50
58
|
if (!normalizedOptions.authType) missing.push('--auth-type');
|
|
59
|
+
if (!normalizedOptions.entityType) missing.push('--entity-type');
|
|
51
60
|
if (!normalizedOptions.datasourceCount) missing.push('--datasources');
|
|
52
61
|
if (missing.length > 0) {
|
|
53
62
|
throw new Error(`Missing required options for non-interactive external create: ${missing.join(', ')}`);
|
|
@@ -110,7 +119,8 @@ function setupCreateCommand(program) {
|
|
|
110
119
|
.option('--display-name <name>', 'External system display name')
|
|
111
120
|
.option('--description <desc>', 'External system description')
|
|
112
121
|
.option('--system-type <type>', 'External system type (openapi, mcp, custom)')
|
|
113
|
-
.option('--auth-type <type>', 'External system auth type (oauth2, apikey, basic)')
|
|
122
|
+
.option('--auth-type <type>', 'External system auth type (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none)')
|
|
123
|
+
.option('--entity-type <type>', 'Entity type for datasources (recordStorage, documentStorage, vectorStore, messageService, none)')
|
|
114
124
|
.option('--datasources <count>', 'Number of datasources to create')
|
|
115
125
|
.action(async(appName, options) => {
|
|
116
126
|
try {
|
|
@@ -130,6 +140,7 @@ Examples:
|
|
|
130
140
|
$ aifabrix wizard my-integration --silent Run headless with integration/my-integration/wizard.yaml (no prompts)
|
|
131
141
|
$ aifabrix wizard -a my-integration Same as above (app name set)
|
|
132
142
|
$ aifabrix wizard --config wizard.yaml Run headless from a wizard config file
|
|
143
|
+
$ aifabrix wizard hubspot-test-v2 --debug Enable debug output and save debug manifests on validation failure
|
|
133
144
|
|
|
134
145
|
Config path: When appName is provided, integration/<appName>/wizard.yaml is used for load/save and error.log.
|
|
135
146
|
To change settings after a run, edit that file and run "aifabrix wizard <app>" again.
|
|
@@ -140,6 +151,7 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`;
|
|
|
140
151
|
.option('-a, --app <app>', 'Application name (synonym for positional appName)')
|
|
141
152
|
.option('--config <file>', 'Run headless using a wizard.yaml file (appName, mode, source, credential, preferences)')
|
|
142
153
|
.option('--silent', 'Run with saved integration/<app>/wizard.yaml only; no prompts (requires app name and existing wizard.yaml)')
|
|
154
|
+
.option('--debug', 'Enable debug output and save debug manifests on validation failure')
|
|
143
155
|
.addHelpText('after', wizardHelp)
|
|
144
156
|
.action(async(positionalAppName, options) => {
|
|
145
157
|
try {
|
|
@@ -154,6 +166,31 @@ See integration/hubspot/wizard-hubspot-e2e.yaml for an example.`;
|
|
|
154
166
|
});
|
|
155
167
|
}
|
|
156
168
|
|
|
169
|
+
function registerRunCommand(program) {
|
|
170
|
+
const runHelp = `
|
|
171
|
+
In dev: use --reload for sync and mount (requires remote server with Mutagen, or local Docker).
|
|
172
|
+
Examples:
|
|
173
|
+
$ aifabrix run myapp
|
|
174
|
+
$ aifabrix run myapp --env tst
|
|
175
|
+
$ aifabrix run myapp --reload`;
|
|
176
|
+
program.command('run <app>')
|
|
177
|
+
.description('Run application locally (or remotely on your Docker host)')
|
|
178
|
+
.option('-p, --port <port>', 'Override local port')
|
|
179
|
+
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
180
|
+
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
181
|
+
.option('-e, --env <env>', 'Environment: dev (default), tst, or pro', 'dev')
|
|
182
|
+
.option('--reload', 'In dev: use sync and mount (requires remote server; Mutagen or local Docker)')
|
|
183
|
+
.addHelpText('after', runHelp)
|
|
184
|
+
.action(async(appName, options) => {
|
|
185
|
+
try {
|
|
186
|
+
await app.runApp(appName, options);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
handleCommandError(error, 'run');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
157
194
|
function setupBuildRunLogsDownCommands(program) {
|
|
158
195
|
program.command('build <app>')
|
|
159
196
|
.description('Build container image (auto-detects runtime)')
|
|
@@ -170,19 +207,7 @@ function setupBuildRunLogsDownCommands(program) {
|
|
|
170
207
|
}
|
|
171
208
|
});
|
|
172
209
|
|
|
173
|
-
program
|
|
174
|
-
.description('Run application locally')
|
|
175
|
-
.option('-p, --port <port>', 'Override local port')
|
|
176
|
-
.option('-d, --debug', 'Enable debug output with detailed container information')
|
|
177
|
-
.option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
|
|
178
|
-
.action(async(appName, options) => {
|
|
179
|
-
try {
|
|
180
|
-
await app.runApp(appName, options);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
handleCommandError(error, 'run');
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
210
|
+
registerRunCommand(program);
|
|
186
211
|
|
|
187
212
|
program.command('logs <app>')
|
|
188
213
|
.description('Show application container logs (and optional env summary with secrets masked)')
|
|
@@ -215,6 +240,136 @@ function setupBuildRunLogsDownCommands(program) {
|
|
|
215
240
|
});
|
|
216
241
|
}
|
|
217
242
|
|
|
243
|
+
function setupShellTestStopCommands(program) {
|
|
244
|
+
program.command('stop <app>')
|
|
245
|
+
.description('Stop and remove application container (alias for down-app)')
|
|
246
|
+
.option('--volumes', 'Remove application Docker volume')
|
|
247
|
+
.action(async(appName, options) => {
|
|
248
|
+
try {
|
|
249
|
+
const { runDownAppWithImageRemoval } = require('../commands/app-down');
|
|
250
|
+
await runDownAppWithImageRemoval(appName, { volumes: options.volumes });
|
|
251
|
+
} catch (error) {
|
|
252
|
+
handleCommandError(error, 'stop');
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
program.command('shell <app>')
|
|
258
|
+
.description('Open interactive shell in the application container')
|
|
259
|
+
.option('--env <env>', 'Environment (dev|tst); dev uses running container', 'dev')
|
|
260
|
+
.action(async(appName, options) => {
|
|
261
|
+
try {
|
|
262
|
+
const { runAppShell } = require('../commands/app-shell');
|
|
263
|
+
await runAppShell(appName, { env: options.env });
|
|
264
|
+
} catch (error) {
|
|
265
|
+
handleCommandError(error, 'shell');
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
program.command('test <app>')
|
|
271
|
+
.description('Run tests (builder app: in container; external system: local validation)')
|
|
272
|
+
.option('--env <env>', 'For builder app: dev (running container) or tst (ephemeral)', 'dev')
|
|
273
|
+
.option('-d, --datasource <key>', 'For external system: test specific datasource only')
|
|
274
|
+
.option('-v, --verbose', 'Verbose output')
|
|
275
|
+
.action(async(appName, options) => {
|
|
276
|
+
try {
|
|
277
|
+
const pathsUtil = require('../utils/paths');
|
|
278
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
279
|
+
if (appType && appType.baseDir === 'integration') {
|
|
280
|
+
const test = require('../external-system/test');
|
|
281
|
+
const results = await test.testExternalSystem(appName, options);
|
|
282
|
+
test.displayTestResults(results, options.verbose);
|
|
283
|
+
if (!results.valid) process.exit(1);
|
|
284
|
+
} else {
|
|
285
|
+
const { runAppTest } = require('../commands/app-test');
|
|
286
|
+
await runAppTest(appName, { env: options.env });
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
handleCommandError(error, 'test');
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function runTestE2ECommand(appName, options) {
|
|
296
|
+
const pathsUtil = require('../utils/paths');
|
|
297
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
298
|
+
if (appType && appType.baseDir === 'integration') {
|
|
299
|
+
const { runTestE2EForExternalSystem } = require('../commands/test-e2e-external');
|
|
300
|
+
const { success, results } = await runTestE2EForExternalSystem(appName, {
|
|
301
|
+
env: options.env,
|
|
302
|
+
debug: options.debug,
|
|
303
|
+
verbose: options.verbose,
|
|
304
|
+
async: options.async !== false
|
|
305
|
+
});
|
|
306
|
+
results.forEach(r => {
|
|
307
|
+
const icon = r.success ? chalk.green('✓') : chalk.red('✗');
|
|
308
|
+
const msg = r.error ? `${r.key}: ${r.error}` : r.key;
|
|
309
|
+
logger.log(` ${icon} ${msg}`);
|
|
310
|
+
});
|
|
311
|
+
if (!success) process.exit(1);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const { runAppTestE2e } = require('../commands/app-test');
|
|
315
|
+
await runAppTestE2e(appName, { env: options.env });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function setupInstallTestE2eLintCommands(program) {
|
|
319
|
+
program.command('install <app>')
|
|
320
|
+
.description('Install dependencies in container (builder apps only)')
|
|
321
|
+
.option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
|
|
322
|
+
.action(async(appName, options) => {
|
|
323
|
+
try {
|
|
324
|
+
const pathsUtil = require('../utils/paths');
|
|
325
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
326
|
+
if (appType && appType.baseDir === 'integration') {
|
|
327
|
+
logger.log(chalk.gray('Install is for builder applications only. Use aifabrix shell <app> to run commands in external setups.'));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const { runAppInstall } = require('../commands/app-install');
|
|
331
|
+
await runAppInstall(appName, { env: options.env });
|
|
332
|
+
} catch (error) {
|
|
333
|
+
handleCommandError(error, 'install');
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
program.command('test-e2e <app>')
|
|
339
|
+
.description('Run e2e tests (builder: in container; external system: all datasources via dataplane)')
|
|
340
|
+
.option('-e, --env <env>', 'Environment: dev, tst, or pro (builder: dev/tst for container)')
|
|
341
|
+
.option('-v, --verbose', 'Show detailed step output and poll progress')
|
|
342
|
+
.option('--debug', 'Include debug output and write log to integration/<app>/logs/')
|
|
343
|
+
.option('--no-async', 'Use sync mode (no polling); single POST per datasource')
|
|
344
|
+
.action(async(appName, options) => {
|
|
345
|
+
try {
|
|
346
|
+
await runTestE2ECommand(appName, options);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
handleCommandError(error, 'test-e2e');
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
program.command('lint <app>')
|
|
354
|
+
.description('Run lint in container (builder apps only)')
|
|
355
|
+
.option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
|
|
356
|
+
.action(async(appName, options) => {
|
|
357
|
+
try {
|
|
358
|
+
const pathsUtil = require('../utils/paths');
|
|
359
|
+
const appType = await pathsUtil.detectAppType(appName).catch(() => null);
|
|
360
|
+
if (appType && appType.baseDir === 'integration') {
|
|
361
|
+
logger.log(chalk.gray('lint is for builder applications only. Use aifabrix shell <app> then make lint or pnpm lint.'));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const { runAppLint } = require('../commands/app-test');
|
|
365
|
+
await runAppLint(appName, { env: options.env });
|
|
366
|
+
} catch (error) {
|
|
367
|
+
handleCommandError(error, 'lint');
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
218
373
|
function setupPushDeployDockerfileCommands(program) {
|
|
219
374
|
program.command('push <app>')
|
|
220
375
|
.description('Push image to Azure Container Registry')
|
|
@@ -230,7 +385,7 @@ function setupPushDeployDockerfileCommands(program) {
|
|
|
230
385
|
});
|
|
231
386
|
|
|
232
387
|
program.command('deploy <app>')
|
|
233
|
-
.description('Deploy to Azure via Miso Controller')
|
|
388
|
+
.description('Deploy to Azure or locally via Miso Controller')
|
|
234
389
|
.option('--local', 'Send manifest to controller then run app locally (app: same as aifabrix run <app>; external: restart dataplane)')
|
|
235
390
|
.option('--client-id <id>', 'Client ID (overrides config)')
|
|
236
391
|
.option('--client-secret <secret>', 'Client Secret (overrides config)')
|
|
@@ -274,6 +429,8 @@ function setupAppCommands(program) {
|
|
|
274
429
|
setupCreateCommand(program);
|
|
275
430
|
setupWizardCommand(program);
|
|
276
431
|
setupBuildRunLogsDownCommands(program);
|
|
432
|
+
setupShellTestStopCommands(program);
|
|
433
|
+
setupInstallTestE2eLintCommands(program);
|
|
277
434
|
setupPushDeployDockerfileCommands(program);
|
|
278
435
|
}
|
|
279
436
|
|
|
@@ -11,8 +11,37 @@ const chalk = require('chalk');
|
|
|
11
11
|
const logger = require('../utils/logger');
|
|
12
12
|
const { handleCommandError } = require('../utils/cli-utils');
|
|
13
13
|
const { runCredentialList } = require('../commands/credential-list');
|
|
14
|
+
const { runCredentialEnv } = require('../commands/credential-env');
|
|
15
|
+
const { runCredentialPush } = require('../commands/credential-push');
|
|
14
16
|
const { runDeploymentList } = require('../commands/deployment-list');
|
|
15
17
|
|
|
18
|
+
function setupCredentialEnvAndPush(credential) {
|
|
19
|
+
credential
|
|
20
|
+
.command('env <system-key>')
|
|
21
|
+
.description('Prompt for KV_* credential values and write integration/<system-key>/.env')
|
|
22
|
+
.action(async(systemKey, _options) => {
|
|
23
|
+
try {
|
|
24
|
+
await runCredentialEnv(systemKey);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
logger.error(chalk.red(`Error: ${error.message}`));
|
|
27
|
+
handleCommandError(error, 'credential env');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
credential
|
|
32
|
+
.command('push <system-key>')
|
|
33
|
+
.description('Push credential secrets from .env to Dataplane (KV_* vars)')
|
|
34
|
+
.action(async(systemKey, _options) => {
|
|
35
|
+
try {
|
|
36
|
+
await runCredentialPush(systemKey);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error(chalk.red(`Error: ${error.message}`));
|
|
39
|
+
handleCommandError(error, 'credential push');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
16
45
|
/**
|
|
17
46
|
* Sets up credential and deployment list commands
|
|
18
47
|
* @param {Command} program - Commander program instance
|
|
@@ -21,19 +50,15 @@ function setupCredentialDeploymentCommands(program) {
|
|
|
21
50
|
const credential = program
|
|
22
51
|
.command('credential')
|
|
23
52
|
.description('Manage credentials');
|
|
24
|
-
|
|
53
|
+
setupCredentialEnvAndPush(credential);
|
|
25
54
|
credential
|
|
26
55
|
.command('list')
|
|
27
|
-
.description('
|
|
28
|
-
.option('--controller <url>', 'Controller URL (default: from config)')
|
|
29
|
-
.option('--dataplane <url>', 'Dataplane URL (default: resolved from controller + environment)')
|
|
56
|
+
.description('Get credentials from Dataplane')
|
|
30
57
|
.option('--active-only', 'List only active credentials')
|
|
31
58
|
.option('--page-size <n>', 'Items per page', '50')
|
|
32
59
|
.action(async(options) => {
|
|
33
60
|
try {
|
|
34
61
|
const opts = {
|
|
35
|
-
controller: options.controller,
|
|
36
|
-
dataplane: options.dataplane,
|
|
37
62
|
activeOnly: options.activeOnly,
|
|
38
63
|
pageSize: parseInt(options.pageSize, 10) || 50
|
|
39
64
|
};
|