@aifabrix/builder 2.46.0 → 2.47.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/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/jest.isolated-projects.js +18 -2
- package/jest.projects.js +24 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/lifecycle.api.js +22 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/down.js +58 -26
- package/lib/app/index.js +2 -2
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +17 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +16 -4
- package/lib/cli/setup-app.js +54 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-utility-help-after.js +4 -1
- package/lib/cli/setup-utility-resolve.js +8 -5
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-dataplane-version.js +97 -61
- package/lib/commands/auth-status.js +2 -7
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource.js +1 -1
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/repair-auth-apply.js +30 -4
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource.js +53 -18
- package/lib/commands/repair-env-template.js +12 -12
- package/lib/commands/repair-openapi-sync.js +26 -4
- package/lib/commands/repair-steps.js +28 -13
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/setup-modes.js +1 -1
- package/lib/commands/setup-prompts-platform-mode.js +24 -21
- package/lib/commands/test-trust-external.js +39 -90
- package/lib/commands/up-common.js +33 -1
- package/lib/commands/up-dataplane.js +8 -4
- package/lib/commands/upload.js +24 -3
- package/lib/commands/verify-trust-command-action.js +4 -1
- package/lib/commands/verify-trust-external.js +12 -20
- package/lib/core/config-setup-platform-mode.js +3 -3
- package/lib/core/setup-platform-mode.js +41 -6
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/validate.js +41 -23
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +61 -122
- package/lib/external-system/generator.js +1 -59
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +5 -2
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/lifecycle/certification-progress.js +2 -2
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +14 -13
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +16 -0
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +372 -58
- package/lib/schema/external-system.schema.json +331 -2
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-utils.js +4 -0
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +9 -1
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +52 -262
- package/lib/utils/dataplane-resolver.js +4 -115
- package/lib/utils/declarative-url-ports.js +24 -1
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/external-env-template.js +10 -3
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/help-builder.js +2 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +2 -1
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +5 -0
- package/templates/applications/dataplane/env.template +26 -13
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +37 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/python/docker-compose.hbs +56 -0
- package/templates/typescript/docker-compose.hbs +52 -0
package/lib/api/wizard.api.js
CHANGED
|
@@ -205,6 +205,12 @@ async function getPlatformConfig(dataplaneUrl, authConfig, platformKey, config)
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Wizard AI generation can exceed the default 30s HTTP timeout (OpenAPI + LLM).
|
|
210
|
+
* @constant {number}
|
|
211
|
+
*/
|
|
212
|
+
const WIZARD_GENERATE_CONFIG_TIMEOUT_MS = 120000;
|
|
213
|
+
|
|
208
214
|
/**
|
|
209
215
|
* Generate configuration via AI (OpenAPI-based)
|
|
210
216
|
* POST /api/v1/wizard/generate-config
|
|
@@ -235,7 +241,8 @@ async function getPlatformConfig(dataplaneUrl, authConfig, platformKey, config)
|
|
|
235
241
|
async function generateConfig(dataplaneUrl, authConfig, config) {
|
|
236
242
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
237
243
|
return await client.post('/api/v1/wizard/generate-config', {
|
|
238
|
-
body: config
|
|
244
|
+
body: config,
|
|
245
|
+
timeoutMs: WIZARD_GENERATE_CONFIG_TIMEOUT_MS
|
|
239
246
|
});
|
|
240
247
|
}
|
|
241
248
|
|
|
@@ -254,7 +261,8 @@ async function generateConfig(dataplaneUrl, authConfig, config) {
|
|
|
254
261
|
async function generateConfigStream(dataplaneUrl, authConfig, config) {
|
|
255
262
|
const client = createDataplaneApiClient(dataplaneUrl, authConfig);
|
|
256
263
|
return await client.post('/api/v1/wizard/generate-config-stream', {
|
|
257
|
-
body: config
|
|
264
|
+
body: config,
|
|
265
|
+
timeoutMs: WIZARD_GENERATE_CONFIG_TIMEOUT_MS
|
|
258
266
|
});
|
|
259
267
|
}
|
|
260
268
|
|
package/lib/app/down.js
CHANGED
|
@@ -18,6 +18,13 @@ const logger = require('../utils/logger');
|
|
|
18
18
|
const config = require('../core/config');
|
|
19
19
|
const helpers = require('./run-helpers');
|
|
20
20
|
const { validateAppName } = require('./push');
|
|
21
|
+
const buildCopy = require('../utils/build-copy');
|
|
22
|
+
const {
|
|
23
|
+
getCeleryCompanionAppKeys,
|
|
24
|
+
loadBuilderApplicationConfig
|
|
25
|
+
} = require('../utils/celery-companion-apps');
|
|
26
|
+
const fsSync = require('fs');
|
|
27
|
+
const path = require('path');
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
30
|
* Executes a shell command asynchronously.
|
|
@@ -74,6 +81,53 @@ function getAppVolumeNameCandidates(appName, developerId) {
|
|
|
74
81
|
return suffixes.map((suffix) => `aifabrix_dev${suffix}_${appName}_data`);
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
function buildVolumeRemovalCandidates(appName, developerId) {
|
|
85
|
+
const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
|
|
86
|
+
const primaryName = getAppVolumeName(appName, developerId);
|
|
87
|
+
const legacyDev0Name = idNum === 0 ? `aifabrix_dev0_${appName}_data` : null;
|
|
88
|
+
return Array.from(new Set([primaryName, legacyDev0Name].filter(Boolean)));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function removeVolumeIfPresent(volumeName) {
|
|
92
|
+
logger.log(chalk.yellow(`Removing volume ${volumeName}...`));
|
|
93
|
+
try {
|
|
94
|
+
await execAsync(`docker volume rm -f ${volumeName}`);
|
|
95
|
+
logger.log(formatSuccessLine(`Volume ${volumeName} removed`));
|
|
96
|
+
} catch (volErr) {
|
|
97
|
+
logger.log(chalk.gray(`Volume ${volumeName} not found or already removed`));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function removeAppVolumes(appName, developerId) {
|
|
102
|
+
for (const volumeName of buildVolumeRemovalCandidates(appName, developerId)) {
|
|
103
|
+
await removeVolumeIfPresent(volumeName);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function stopAppContainers(appName, companionKeys, developerId) {
|
|
108
|
+
const devDir = buildCopy.getDevDirectory(appName, developerId);
|
|
109
|
+
const composePath = path.join(devDir, 'docker-compose.yaml');
|
|
110
|
+
|
|
111
|
+
if (companionKeys.length > 0 && fsSync.existsSync(composePath)) {
|
|
112
|
+
try {
|
|
113
|
+
await execAsync(`docker compose -f "${composePath}" down`);
|
|
114
|
+
return;
|
|
115
|
+
} catch (composeErr) {
|
|
116
|
+
logger.log(chalk.gray(`Compose down: ${composeErr.message}; stopping containers individually`));
|
|
117
|
+
for (const companionKey of companionKeys) {
|
|
118
|
+
await helpers.stopAndRemoveContainer(companionKey, developerId, false);
|
|
119
|
+
}
|
|
120
|
+
await helpers.stopAndRemoveContainer(appName, developerId, false);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await helpers.stopAndRemoveContainer(appName, developerId, false);
|
|
126
|
+
for (const companionKey of companionKeys) {
|
|
127
|
+
await helpers.stopAndRemoveContainer(companionKey, developerId, false);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
77
131
|
/**
|
|
78
132
|
* Stops and removes a specific application's container.
|
|
79
133
|
* If --volumes is passed, attempts to remove the app's named Docker volume.
|
|
@@ -93,42 +147,20 @@ function getAppVolumeNameCandidates(appName, developerId) {
|
|
|
93
147
|
*/
|
|
94
148
|
async function downApp(appName, options = {}) {
|
|
95
149
|
try {
|
|
96
|
-
// Input validation
|
|
97
150
|
if (!appName || typeof appName !== 'string') {
|
|
98
151
|
throw new Error('Application name is required and must be a string');
|
|
99
152
|
}
|
|
100
153
|
validateAppName(appName);
|
|
101
154
|
|
|
102
155
|
const volumes = !!options.volumes;
|
|
103
|
-
|
|
104
|
-
// Load developer ID
|
|
105
156
|
const developerId = await config.getDeveloperId();
|
|
157
|
+
const appConfig = loadBuilderApplicationConfig(appName);
|
|
158
|
+
const companionKeys = getCeleryCompanionAppKeys(appName, appConfig);
|
|
106
159
|
|
|
107
|
-
|
|
108
|
-
await helpers.stopAndRemoveContainer(appName, developerId, false);
|
|
160
|
+
await stopAppContainers(appName, companionKeys, developerId);
|
|
109
161
|
|
|
110
|
-
// Optionally remove named Docker volume (ignore if it doesn't exist)
|
|
111
162
|
if (volumes) {
|
|
112
|
-
|
|
113
|
-
// Primary expected name
|
|
114
|
-
const primaryName = getAppVolumeName(appName, developerId);
|
|
115
|
-
// Legacy name used when devId \"0\" was treated as string in templates
|
|
116
|
-
const legacyDev0Name = idNum === 0 ? `aifabrix_dev0_${appName}_data` : null;
|
|
117
|
-
// Build list of candidates, unique and non-empty
|
|
118
|
-
const candidates = Array.from(
|
|
119
|
-
new Set([primaryName, legacyDev0Name].filter(Boolean))
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
for (const volumeName of candidates) {
|
|
123
|
-
logger.log(chalk.yellow(`Removing volume ${volumeName}...`));
|
|
124
|
-
try {
|
|
125
|
-
await execAsync(`docker volume rm -f ${volumeName}`);
|
|
126
|
-
logger.log(formatSuccessLine(`Volume ${volumeName} removed`));
|
|
127
|
-
} catch (volErr) {
|
|
128
|
-
// Swallow errors for missing volume; provide neutral message
|
|
129
|
-
logger.log(chalk.gray(`Volume ${volumeName} not found or already removed`));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
163
|
+
await removeAppVolumes(appName, developerId);
|
|
132
164
|
}
|
|
133
165
|
} catch (error) {
|
|
134
166
|
// Provide meaningful error while avoiding sensitive info
|
package/lib/app/index.js
CHANGED
|
@@ -93,7 +93,7 @@ function validateAppNameAndSetup(appName, options) {
|
|
|
93
93
|
|
|
94
94
|
const initialType = options.type || 'external';
|
|
95
95
|
const baseDir = getBaseDirForAppType(initialType);
|
|
96
|
-
const appPath = paths.
|
|
96
|
+
const appPath = paths.getAppCreatePath(appName, initialType);
|
|
97
97
|
|
|
98
98
|
return { initialType, baseDir, appPath };
|
|
99
99
|
}
|
|
@@ -124,7 +124,7 @@ async function handleTemplateSetup(options) {
|
|
|
124
124
|
*/
|
|
125
125
|
async function prepareFinalAppPath(appName, config, initialAppPath) {
|
|
126
126
|
const finalBaseDir = getBaseDirForAppType(config.type);
|
|
127
|
-
const finalAppPath = paths.
|
|
127
|
+
const finalAppPath = paths.getAppCreatePath(appName, config.type);
|
|
128
128
|
|
|
129
129
|
// If path changed, validate the new path
|
|
130
130
|
if (finalAppPath !== initialAppPath) {
|
|
@@ -10,6 +10,7 @@ const config = require('../core/config');
|
|
|
10
10
|
const { resolveDockerImageRef } = require('../utils/resolve-docker-image-ref');
|
|
11
11
|
const { checkImageExists } = require('../utils/app-run-containers');
|
|
12
12
|
const { buildDevImageRepositoryPath } = require('../utils/image-name');
|
|
13
|
+
const { wantsLocalDockerImageDiscovery } = require('../utils/build-resolve-image');
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* @param {string} appName
|
|
@@ -41,7 +42,7 @@ async function resolveRunImageWithLocalFallback(appName, appConfig, runOptions =
|
|
|
41
42
|
|
|
42
43
|
const baseRef = resolveDockerImageRef(appName, appConfig, opts);
|
|
43
44
|
|
|
44
|
-
if (opts.base === true) {
|
|
45
|
+
if (opts.base === true || !wantsLocalDockerImageDiscovery(appConfig)) {
|
|
45
46
|
return baseRef;
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop running app containers (and celery companions) before `aifabrix run`.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Pre-run container teardown for port reuse
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { formatWarningLine } = require('../utils/cli-test-layout-chalk');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const containerHelpers = require('../utils/app-run-containers');
|
|
15
|
+
const helpers = require('./run-helpers');
|
|
16
|
+
const { computeEffectiveEnvironmentScopedResources } = require('../utils/environment-scoped-resources');
|
|
17
|
+
const { getCeleryCompanionAppKeys } = require('../utils/celery-companion-apps');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {Object} appConfig
|
|
21
|
+
* @param {Object} options
|
|
22
|
+
* @returns {Promise<{ effectiveEnvironmentScopedResources: true, env: string }|null>}
|
|
23
|
+
*/
|
|
24
|
+
async function resolveRunScopeOpts(appConfig, options) {
|
|
25
|
+
const runEnvKey = (options.env || 'dev').toLowerCase();
|
|
26
|
+
const userCfg = await config.getConfig();
|
|
27
|
+
const effectiveEnvironmentScopedResources = computeEffectiveEnvironmentScopedResources(
|
|
28
|
+
Boolean(userCfg.useEnvironmentScopedResources),
|
|
29
|
+
appConfig.environmentScopedResources === true,
|
|
30
|
+
runEnvKey
|
|
31
|
+
);
|
|
32
|
+
if (!effectiveEnvironmentScopedResources) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return { effectiveEnvironmentScopedResources: true, env: runEnvKey };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string[]} companionKeys
|
|
40
|
+
* @param {string|number} developerId
|
|
41
|
+
* @param {boolean} debug
|
|
42
|
+
* @param {Object|null} scopeOpts
|
|
43
|
+
* @returns {Promise<string[]>}
|
|
44
|
+
*/
|
|
45
|
+
async function findRunningCompanionKeys(companionKeys, developerId, debug, scopeOpts) {
|
|
46
|
+
const running = [];
|
|
47
|
+
for (const companionKey of companionKeys) {
|
|
48
|
+
if (await helpers.checkContainerRunning(companionKey, developerId, debug, scopeOpts)) {
|
|
49
|
+
running.push(companionKey);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return running;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string[]} companionKeys
|
|
57
|
+
* @param {string|number} developerId
|
|
58
|
+
* @param {boolean} debug
|
|
59
|
+
* @param {Object|null} scopeOpts
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
async function stopCompanionContainers(companionKeys, developerId, debug, scopeOpts) {
|
|
63
|
+
for (const companionKey of companionKeys) {
|
|
64
|
+
const companionName = containerHelpers.getContainerName(companionKey, developerId, scopeOpts);
|
|
65
|
+
logger.log(formatWarningLine(`Stopping companion container ${companionName}`));
|
|
66
|
+
await helpers.stopAndRemoveContainer(companionKey, developerId, debug, scopeOpts);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if container is running and stop it (and celery companions) if needed.
|
|
72
|
+
*
|
|
73
|
+
* @async
|
|
74
|
+
* @param {string} appName
|
|
75
|
+
* @param {Object} appConfig
|
|
76
|
+
* @param {Object} options
|
|
77
|
+
* @param {boolean} debug
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
async function checkAndStopContainer(appName, appConfig, options, debug) {
|
|
81
|
+
const developerId = appConfig.developerId;
|
|
82
|
+
const scopeOpts = await resolveRunScopeOpts(appConfig, options);
|
|
83
|
+
const companionKeys = getCeleryCompanionAppKeys(appName, appConfig);
|
|
84
|
+
const companionsToStop = await findRunningCompanionKeys(
|
|
85
|
+
companionKeys,
|
|
86
|
+
developerId,
|
|
87
|
+
debug,
|
|
88
|
+
scopeOpts
|
|
89
|
+
);
|
|
90
|
+
const containerRunning = await helpers.checkContainerRunning(
|
|
91
|
+
appName,
|
|
92
|
+
developerId,
|
|
93
|
+
debug,
|
|
94
|
+
scopeOpts
|
|
95
|
+
);
|
|
96
|
+
if (!containerRunning && companionsToStop.length === 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
await stopCompanionContainers(companionsToStop, developerId, debug, scopeOpts);
|
|
100
|
+
if (!containerRunning) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const containerName = containerHelpers.getContainerName(appName, developerId, scopeOpts);
|
|
104
|
+
logger.log(formatWarningLine(`Container ${containerName} is already running`));
|
|
105
|
+
await helpers.stopAndRemoveContainer(appName, developerId, debug, scopeOpts);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
checkAndStopContainer,
|
|
110
|
+
resolveRunScopeOpts,
|
|
111
|
+
findRunningCompanionKeys,
|
|
112
|
+
stopCompanionContainers
|
|
113
|
+
};
|
package/lib/app/run.js
CHANGED
|
@@ -27,6 +27,7 @@ const containerHelpers = require('../utils/app-run-containers');
|
|
|
27
27
|
const helpers = require('./run-helpers');
|
|
28
28
|
const { ensureReloadSync, logReloadDevSummary } = require('./run-reload-sync');
|
|
29
29
|
const { logRestartDevMountSummary } = require('./restart-display');
|
|
30
|
+
const { checkAndStopContainer } = require('./run-stop-containers');
|
|
30
31
|
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
31
32
|
const { isRunCliNoProxy } = require('../utils/run-cli-flags');
|
|
32
33
|
|
|
@@ -111,37 +112,6 @@ async function validateAppForRun(appName, _debug) {
|
|
|
111
112
|
return true;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
/**
|
|
115
|
-
* Check if container is running and stop it if needed
|
|
116
|
-
* @async
|
|
117
|
-
* @param {string} appName - Application name
|
|
118
|
-
* @param {number|string} developerId - Developer ID
|
|
119
|
-
* @param {boolean} debug - Debug flag
|
|
120
|
-
* @returns {Promise<void>}
|
|
121
|
-
*/
|
|
122
|
-
async function checkAndStopContainer(appName, appConfig, options, debug) {
|
|
123
|
-
const developerId = appConfig.developerId;
|
|
124
|
-
const runEnvKey = (options.env || 'dev').toLowerCase();
|
|
125
|
-
const userCfg = await config.getConfig();
|
|
126
|
-
const { computeEffectiveEnvironmentScopedResources } = require('../utils/environment-scoped-resources');
|
|
127
|
-
const effectiveEnvironmentScopedResources = computeEffectiveEnvironmentScopedResources(
|
|
128
|
-
Boolean(userCfg.useEnvironmentScopedResources),
|
|
129
|
-
appConfig.environmentScopedResources === true,
|
|
130
|
-
runEnvKey
|
|
131
|
-
);
|
|
132
|
-
const scopeOpts = effectiveEnvironmentScopedResources
|
|
133
|
-
? { effectiveEnvironmentScopedResources: true, env: runEnvKey }
|
|
134
|
-
: null;
|
|
135
|
-
const containerRunning = await helpers.checkContainerRunning(appName, developerId, debug, scopeOpts);
|
|
136
|
-
if (!containerRunning) {
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const containerName = containerHelpers.getContainerName(appName, developerId, scopeOpts);
|
|
141
|
-
logger.log(formatWarningLine(`Container ${containerName} is already running`));
|
|
142
|
-
await helpers.stopAndRemoveContainer(appName, developerId, debug, scopeOpts);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
115
|
/**
|
|
146
116
|
* Calculate host port and validate it's available
|
|
147
117
|
* @async
|
package/lib/app/show-display.js
CHANGED
|
@@ -49,6 +49,40 @@ function logApplicationOptional(a) {
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function localRunProfileDiffers(application, localRunProfile) {
|
|
53
|
+
if (!localRunProfile || !application) return false;
|
|
54
|
+
if (localRunProfile.url && localRunProfile.url !== application.url) return true;
|
|
55
|
+
if (localRunProfile.internalUrl && localRunProfile.internalUrl !== application.internalUrl) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return (
|
|
59
|
+
localRunProfile.port !== undefined &&
|
|
60
|
+
localRunProfile.port !== null &&
|
|
61
|
+
localRunProfile.port !== application.port
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function logLocalRunProfileSection(summary) {
|
|
66
|
+
const local = summary.localRunProfile;
|
|
67
|
+
if (!local || summary.source !== 'online' || !localRunProfileDiffers(summary.application, local)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
logger.log('');
|
|
71
|
+
logger.log(chalk.gray(' Local run profile (docker manifest url://):'));
|
|
72
|
+
if (local.url && local.url !== summary.application.url) {
|
|
73
|
+
logger.log(chalk.gray(` URL: ${local.url}`));
|
|
74
|
+
}
|
|
75
|
+
if (local.internalUrl && local.internalUrl !== summary.application.internalUrl) {
|
|
76
|
+
logger.log(chalk.gray(` Internal URL: ${local.internalUrl}`));
|
|
77
|
+
}
|
|
78
|
+
if (local.port !== undefined && local.port !== summary.application.port) {
|
|
79
|
+
logger.log(chalk.gray(` Port: ${local.port}`));
|
|
80
|
+
}
|
|
81
|
+
logger.log(
|
|
82
|
+
chalk.gray(' Controller registry values above are what identity sync and pipeline use.')
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
52
86
|
function logApplicationFields(a) {
|
|
53
87
|
logApplicationRequired(a);
|
|
54
88
|
logApplicationOptional(a);
|
|
@@ -96,6 +130,7 @@ function logApplicationSection(a, summary) {
|
|
|
96
130
|
} else {
|
|
97
131
|
logApplicationFields(a);
|
|
98
132
|
}
|
|
133
|
+
logLocalRunProfileSection(summary);
|
|
99
134
|
if (summary.runReloadDefault && !summary.isExternal) {
|
|
100
135
|
logger.log(` ${'Run default:'.padEnd(16)} reload on (config)`);
|
|
101
136
|
}
|
package/lib/app/show.js
CHANGED
|
@@ -54,10 +54,22 @@ function attachRunDefaultsFromUserConfig(summary, appKey, userCfg) {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Overlay `application.url` / `application.internalUrl` from declarative url:// rules (same profile as run `.env`, default `docker`).
|
|
57
|
-
*
|
|
57
|
+
* Offline: replaces URL fields for local run hints. Online: keeps controller registry on `application` and adds `localRunProfile` when it differs.
|
|
58
58
|
* @param {Object} summary
|
|
59
59
|
* @param {string} appKey
|
|
60
60
|
*/
|
|
61
|
+
function portFromShowUrl(url) {
|
|
62
|
+
if (!url || typeof url !== 'string') return undefined;
|
|
63
|
+
try {
|
|
64
|
+
const parsed = new URL(url);
|
|
65
|
+
if (!parsed.port) return undefined;
|
|
66
|
+
const port = Number(parsed.port);
|
|
67
|
+
return Number.isFinite(port) ? port : undefined;
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
async function attachDeclarativeUrlsToShowApplication(summary, appKey) {
|
|
62
74
|
if (!summary || summary.isExternal) return;
|
|
63
75
|
const t = summary.application && summary.application.type;
|
|
@@ -68,8 +80,24 @@ async function attachDeclarativeUrlsToShowApplication(summary, appKey) {
|
|
|
68
80
|
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
69
81
|
const urls = await resolveDeclarativeShowUrlsForApp(appKey, appPath, variablesPath, 'docker');
|
|
70
82
|
if (!urls) return;
|
|
83
|
+
|
|
84
|
+
const localPort = portFromShowUrl(urls.publicUrl);
|
|
85
|
+
const localRunProfile = {
|
|
86
|
+
url: urls.publicUrl,
|
|
87
|
+
internalUrl: urls.internalUrl,
|
|
88
|
+
port: localPort ?? summary.application.port
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (summary.source === 'online') {
|
|
92
|
+
summary.localRunProfile = localRunProfile;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
71
96
|
summary.application.url = urls.publicUrl;
|
|
72
97
|
summary.application.internalUrl = urls.internalUrl;
|
|
98
|
+
if (localPort !== undefined) {
|
|
99
|
+
summary.application.port = localPort;
|
|
100
|
+
}
|
|
73
101
|
} catch {
|
|
74
102
|
/* display-only */
|
|
75
103
|
}
|
|
@@ -830,6 +858,9 @@ function outputOnlineJson(summary, permissionsOnly, manifestSource) {
|
|
|
830
858
|
out.runReloadDefault = true;
|
|
831
859
|
}
|
|
832
860
|
out.runProxyDefault = Boolean(summary.runProxyDefault);
|
|
861
|
+
if (summary.localRunProfile) {
|
|
862
|
+
out.localRunProfile = summary.localRunProfile;
|
|
863
|
+
}
|
|
833
864
|
if (summary.externalSystem !== undefined && summary.externalSystem !== null) {
|
|
834
865
|
out.externalSystem = summary.externalSystem && summary.externalSystem.error
|
|
835
866
|
? { error: summary.externalSystem.error }
|
package/lib/build/index.js
CHANGED
|
@@ -33,10 +33,11 @@ const logger = require('../utils/logger');
|
|
|
33
33
|
const dockerfileUtils = require('../utils/dockerfile-utils');
|
|
34
34
|
const dockerBuild = require('../utils/docker-build');
|
|
35
35
|
const buildCopy = require('../utils/build-copy');
|
|
36
|
-
const {
|
|
36
|
+
const { resolveBuildDockerTagNames } = require('../utils/build-resolve-image');
|
|
37
37
|
const buildHelpers = require('../utils/build-helpers');
|
|
38
38
|
const secretsEnvWrite = require('../core/secrets-env-write');
|
|
39
39
|
const { ensureRunSecretsForApp } = require('../app/run-env-compose');
|
|
40
|
+
const { assertMisoControllerNpmSdkDeps } = require('./miso-controller-npm-sdk-guard');
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Loads application config for an application
|
|
@@ -286,8 +287,15 @@ async function prepareDevDirectory(appName, buildConfig, options) {
|
|
|
286
287
|
const devDir = await buildCopy.copyBuilderToDevDirectory(appName, developerId);
|
|
287
288
|
logger.log(formatSuccessLine(`Workspace ready: ${devDir}`));
|
|
288
289
|
|
|
289
|
-
const { config: appConfig
|
|
290
|
-
const
|
|
290
|
+
const { config: appConfig } = await buildHelpers.loadAndValidateConfig(appName);
|
|
291
|
+
const { buildImageName, compatBaseName } = await resolveBuildDockerTagNames(
|
|
292
|
+
appName,
|
|
293
|
+
appConfig,
|
|
294
|
+
developerId,
|
|
295
|
+
options
|
|
296
|
+
);
|
|
297
|
+
const effectiveImageName = buildImageName;
|
|
298
|
+
const imageName = compatBaseName || buildImageName;
|
|
291
299
|
|
|
292
300
|
// Check if application source files exist, if not copy from templates
|
|
293
301
|
const filesCopied = await copyApplicationSourceFiles(appName, devDir);
|
|
@@ -389,9 +397,10 @@ async function handleDockerfileGeneration(appName, params, options, buildHelpers
|
|
|
389
397
|
/**
|
|
390
398
|
* Resolve Docker build-args from resolved env map and merged BASH_* secrets.
|
|
391
399
|
* @param {string} appName
|
|
400
|
+
* @param {string} [contextPath] Docker build context (repo root for miso-controller)
|
|
392
401
|
* @returns {Promise<Object.<string, string>>}
|
|
393
402
|
*/
|
|
394
|
-
async function resolveDockerBuildArgsForApp(appName) {
|
|
403
|
+
async function resolveDockerBuildArgsForApp(appName, contextPath) {
|
|
395
404
|
const envMap = await secretsEnvWrite.resolveAndGetEnvMap(appName, { environment: 'docker' });
|
|
396
405
|
const { getBashPrefixedProcessEnvOverlay } = require('../utils/bash-secret-env');
|
|
397
406
|
const bashOverlay = await getBashPrefixedProcessEnvOverlay(null, appName);
|
|
@@ -406,6 +415,9 @@ async function resolveDockerBuildArgsForApp(appName) {
|
|
|
406
415
|
const shellPypi = typeof process.env.PYPI_TOKEN === 'string' ? process.env.PYPI_TOKEN.trim() : '';
|
|
407
416
|
if (shellNpm) buildArgs.NPM_TOKEN = shellNpm;
|
|
408
417
|
if (shellPypi) buildArgs.PYPI_TOKEN = shellPypi;
|
|
418
|
+
if (appName === 'miso-controller' && contextPath) {
|
|
419
|
+
assertMisoControllerNpmSdkDeps(path.resolve(contextPath));
|
|
420
|
+
}
|
|
409
421
|
return buildArgs;
|
|
410
422
|
}
|
|
411
423
|
|
|
@@ -429,7 +441,7 @@ async function runStandardDockerBuild(appName, options) {
|
|
|
429
441
|
appConfig
|
|
430
442
|
}, options, buildHelpers);
|
|
431
443
|
await ensureRunSecretsForApp(appName);
|
|
432
|
-
const buildArgs = await resolveDockerBuildArgsForApp(appName);
|
|
444
|
+
const buildArgs = await resolveDockerBuildArgsForApp(appName, contextPath);
|
|
433
445
|
const buildOptions = { ...options, buildArgs };
|
|
434
446
|
const tag = options.tag || 'latest';
|
|
435
447
|
if (typeof tag === 'string' && tag.includes(',')) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure miso-controller SDK deps are npm semver before Docker build (no file: vendor hacks).
|
|
3
|
+
* @fileoverview miso-controller npm SDK guard for builder
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execFileSync } = require('child_process');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} repoRoot - aifabrix-miso repo root (Docker build context)
|
|
12
|
+
* @throws {Error} When guard script is missing or deps use file:
|
|
13
|
+
*/
|
|
14
|
+
function assertMisoControllerNpmSdkDeps(repoRoot) {
|
|
15
|
+
const script = path.join(
|
|
16
|
+
repoRoot,
|
|
17
|
+
'packages/miso-controller/scripts/assert-npm-sdk-deps.js'
|
|
18
|
+
);
|
|
19
|
+
if (!fs.existsSync(script)) {
|
|
20
|
+
throw new Error(`miso-controller SDK guard not found: ${script}`);
|
|
21
|
+
}
|
|
22
|
+
execFileSync('node', [script], { cwd: repoRoot, stdio: 'inherit' });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { assertMisoControllerNpmSdkDeps };
|