@aifabrix/builder 2.46.0 → 2.47.1
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 +18 -4
- package/lib/cli/infra-guided-auth.js +36 -12
- 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-npm.js +59 -0
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-secrets.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/login-device.js +11 -3
- package/lib/commands/login.js +8 -3
- package/lib/commands/npm-config.js +103 -0
- 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/api.js +2 -1
- 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/controller-url.js +4 -1
- 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/dev-platform-tls.js +148 -0
- 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/health-check-request.js +114 -0
- package/lib/utils/health-check-url.js +3 -1
- package/lib/utils/health-check.js +1 -113
- package/lib/utils/help-builder.js +3 -0
- package/lib/utils/keycloak-realm-ready.js +5 -1
- package/lib/utils/localhost-public-host.js +31 -0
- package/lib/utils/npm-user-config.js +145 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +58 -4
- 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/url-declarative-public-base.js +33 -25
- 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/utils/build-copy.js
CHANGED
|
@@ -14,6 +14,11 @@ const fs = require('fs').promises;
|
|
|
14
14
|
const fsSync = require('fs');
|
|
15
15
|
const path = require('path');
|
|
16
16
|
const paths = require('./paths');
|
|
17
|
+
const { formatWarningLine } = require('./cli-test-layout-chalk');
|
|
18
|
+
const logger = require('./logger');
|
|
19
|
+
|
|
20
|
+
/** Builder `.env` is not copied into the dev workspace (plan 139: resolve in-memory; run uses `.env.run`). */
|
|
21
|
+
const BUILD_WORKSPACE_SKIP_FILES = new Set(['.env']);
|
|
17
22
|
|
|
18
23
|
/**
|
|
19
24
|
* Copies all files from builder directory to developer-specific directory
|
|
@@ -81,8 +86,11 @@ async function copyDirectory(sourceDir, targetDir) {
|
|
|
81
86
|
const entries = await fs.readdir(sourceDir);
|
|
82
87
|
|
|
83
88
|
for (const entry of entries) {
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
if (BUILD_WORKSPACE_SKIP_FILES.has(entry)) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// Skip hidden files and directories (allow .gitignore)
|
|
93
|
+
if (entry.startsWith('.') && entry !== '.gitignore') {
|
|
86
94
|
continue;
|
|
87
95
|
}
|
|
88
96
|
|
|
@@ -92,12 +100,35 @@ async function copyDirectory(sourceDir, targetDir) {
|
|
|
92
100
|
const stats = await fs.stat(sourcePath);
|
|
93
101
|
|
|
94
102
|
if (stats.isDirectory()) {
|
|
95
|
-
// Recursively copy subdirectories
|
|
96
103
|
await copyDirectory(sourcePath, targetPath);
|
|
97
104
|
} else if (stats.isFile()) {
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
await copyFileToWorkspace(sourcePath, targetPath, entry);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Copy a single file into the dev workspace; skip unreadable files instead of failing the build.
|
|
112
|
+
* @async
|
|
113
|
+
* @param {string} sourcePath
|
|
114
|
+
* @param {string} targetPath
|
|
115
|
+
* @param {string} entryName
|
|
116
|
+
*/
|
|
117
|
+
async function copyFileToWorkspace(sourcePath, targetPath, entryName) {
|
|
118
|
+
try {
|
|
119
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const code = err && err.code ? String(err.code) : '';
|
|
122
|
+
if (code === 'EACCES' || code === 'EPERM') {
|
|
123
|
+
logger.log(
|
|
124
|
+
formatWarningLine(
|
|
125
|
+
`Skipped ${entryName} during workspace copy (permission denied). ` +
|
|
126
|
+
'Use "aifabrix resolve <app>" for a persistent .env when needed.'
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
return;
|
|
100
130
|
}
|
|
131
|
+
throw err;
|
|
101
132
|
}
|
|
102
133
|
}
|
|
103
134
|
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
const chalk = require('chalk');
|
|
12
12
|
const logger = require('./logger');
|
|
13
13
|
const { execWithDockerEnv } = require('./docker-exec');
|
|
14
|
+
const { buildDevImageName } = require('./image-name');
|
|
14
15
|
const {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
getRepositoryPathFromConfig,
|
|
17
|
+
resolveDockerImageRef
|
|
17
18
|
} = require('./resolve-docker-image-ref');
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -47,20 +48,44 @@ function repositoryTail(repoPath) {
|
|
|
47
48
|
return parts[parts.length - 1] || repoPath;
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Remove registry host prefix from a Docker repository path (local build only).
|
|
53
|
+
* @param {string} repository
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
function stripRegistryHostFromRepository(repository) {
|
|
57
|
+
if (!repository || typeof repository !== 'string') {
|
|
58
|
+
return repository;
|
|
59
|
+
}
|
|
60
|
+
const parts = repository.split('/');
|
|
61
|
+
if (parts.length <= 1) {
|
|
62
|
+
return repository;
|
|
63
|
+
}
|
|
64
|
+
const host = parts[0];
|
|
65
|
+
if (host.includes('.') || host.includes(':') || host === 'localhost') {
|
|
66
|
+
return parts.slice(1).join('/');
|
|
67
|
+
}
|
|
68
|
+
return repository;
|
|
69
|
+
}
|
|
70
|
+
|
|
50
71
|
/**
|
|
51
72
|
* Strip dev-scoped or -extra suffix from a Docker repository path.
|
|
52
73
|
* @param {string} repo
|
|
53
74
|
* @returns {string}
|
|
54
75
|
*/
|
|
55
76
|
function baseRepositoryFromDevScoped(repo) {
|
|
56
|
-
const
|
|
77
|
+
const localRepo = stripRegistryHostFromRepository(repo);
|
|
78
|
+
const tail = repositoryTail(localRepo);
|
|
79
|
+
const devMatch = tail.match(/^(.*)-dev\d+$/);
|
|
57
80
|
if (devMatch) {
|
|
58
|
-
|
|
81
|
+
const prefix = localRepo.slice(0, localRepo.length - tail.length);
|
|
82
|
+
return `${prefix}${devMatch[1]}`;
|
|
59
83
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
84
|
+
if (tail.endsWith('-extra')) {
|
|
85
|
+
const prefix = localRepo.slice(0, localRepo.length - tail.length);
|
|
86
|
+
return `${prefix}${tail.slice(0, -'-extra'.length)}`;
|
|
62
87
|
}
|
|
63
|
-
return
|
|
88
|
+
return localRepo;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
/**
|
|
@@ -140,7 +165,33 @@ async function discoverLocalBaseImageRepository(appName, developerId) {
|
|
|
140
165
|
}
|
|
141
166
|
|
|
142
167
|
/**
|
|
143
|
-
*
|
|
168
|
+
* Resolve local `docker build -t` repository path(s) from the manifest or discovery.
|
|
169
|
+
* When `application.yaml` declares `image`, the manifest ref is used exactly (registry + name).
|
|
170
|
+
* Developer-scoped `-devN` suffixes apply only when the manifest omits `image` and Docker is scanned.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} appName
|
|
173
|
+
* @param {Object} variables - application manifest
|
|
174
|
+
* @param {string|number} developerId
|
|
175
|
+
* @param {Object} [options] - CLI build options (`base`, `tag`, `registry`)
|
|
176
|
+
* @returns {Promise<{ buildImageName: string, compatBaseName: string|null }>}
|
|
177
|
+
*/
|
|
178
|
+
async function resolveBuildDockerTagNames(appName, variables, developerId, options = {}) {
|
|
179
|
+
if (!wantsLocalDockerImageDiscovery(variables)) {
|
|
180
|
+
const { imageName } = resolveDockerImageRef(appName, variables, options);
|
|
181
|
+
return { buildImageName: imageName, compatBaseName: null };
|
|
182
|
+
}
|
|
183
|
+
const baseRepo = await resolveBuildImageRepositoryName(appName, variables, developerId);
|
|
184
|
+
const buildImageName = buildDevImageName(baseRepo, developerId);
|
|
185
|
+
const wantCompat =
|
|
186
|
+
buildImageName !== baseRepo && options && options.base === true;
|
|
187
|
+
return {
|
|
188
|
+
buildImageName,
|
|
189
|
+
compatBaseName: wantCompat ? baseRepo : null
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Repository path for local `docker build -t` when manifest omits `image` (discovery / app key).
|
|
144
195
|
* @param {string} appName
|
|
145
196
|
* @param {Object} variables - application manifest
|
|
146
197
|
* @param {string|number} developerId
|
|
@@ -148,18 +199,21 @@ async function discoverLocalBaseImageRepository(appName, developerId) {
|
|
|
148
199
|
*/
|
|
149
200
|
async function resolveBuildImageRepositoryName(appName, variables, developerId) {
|
|
150
201
|
if (!wantsLocalDockerImageDiscovery(variables)) {
|
|
151
|
-
return
|
|
202
|
+
return getRepositoryPathFromConfig(variables, appName);
|
|
152
203
|
}
|
|
153
204
|
const discovered = await discoverLocalBaseImageRepository(appName, developerId);
|
|
154
205
|
if (discovered) {
|
|
155
|
-
|
|
156
|
-
|
|
206
|
+
const localRepo = stripRegistryHostFromRepository(discovered);
|
|
207
|
+
logger.log(chalk.blue(`Using image repository from local Docker: ${localRepo}`));
|
|
208
|
+
return localRepo;
|
|
157
209
|
}
|
|
158
210
|
return getRepositoryPathFromConfig(variables, appName);
|
|
159
211
|
}
|
|
160
212
|
|
|
161
213
|
module.exports = {
|
|
214
|
+
resolveBuildDockerTagNames,
|
|
162
215
|
resolveBuildImageRepositoryName,
|
|
163
216
|
wantsLocalDockerImageDiscovery,
|
|
164
|
-
pickBaseFromDockerLines
|
|
217
|
+
pickBaseFromDockerLines,
|
|
218
|
+
stripRegistryHostFromRepository
|
|
165
219
|
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker companion helpers for run/logs/down lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Loads application.yaml and resolves worker sidecar app keys
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const yaml = require('js-yaml');
|
|
12
|
+
const paths = require('./paths');
|
|
13
|
+
const {
|
|
14
|
+
getWorkerCompanionAppKeys,
|
|
15
|
+
resolveWorkerSidecars
|
|
16
|
+
} = require('./worker-sidecars');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} appName
|
|
20
|
+
* @param {object|null|undefined} appConfig
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
function isCeleryWorkerStackEnabled(appName, appConfig) {
|
|
24
|
+
return resolveWorkerSidecars(appName, appConfig).enabled;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} appName
|
|
29
|
+
* @param {object|null|undefined} appConfig
|
|
30
|
+
* @returns {string[]}
|
|
31
|
+
*/
|
|
32
|
+
function getCeleryCompanionAppKeys(appName, appConfig) {
|
|
33
|
+
return getWorkerCompanionAppKeys(appName, appConfig);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} appName
|
|
38
|
+
* @returns {object|null}
|
|
39
|
+
*/
|
|
40
|
+
function loadBuilderApplicationConfig(appName) {
|
|
41
|
+
const configPath = path.join(paths.getBuilderPath(appName), 'application.yaml');
|
|
42
|
+
if (!fs.existsSync(configPath)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
isCeleryWorkerStackEnabled,
|
|
50
|
+
getCeleryCompanionAppKeys,
|
|
51
|
+
loadBuilderApplicationConfig,
|
|
52
|
+
resolveWorkerSidecars
|
|
53
|
+
};
|
package/lib/utils/cli-utils.js
CHANGED
|
@@ -215,6 +215,10 @@ function formatAzureError(errorMsg) {
|
|
|
215
215
|
' Use format: *.azurecr.io (e.g., myacr.azurecr.io)'
|
|
216
216
|
];
|
|
217
217
|
}
|
|
218
|
+
// Docker build failures may mention azurecr.io in the local tag; show the real build error instead.
|
|
219
|
+
if (errorMsg.includes('Docker build failed') || errorMsg.includes('Build failed: Docker')) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
218
222
|
// Only match ACR-specific authentication errors, not general authentication failures
|
|
219
223
|
if (errorMsg.includes('ACR') || errorMsg.includes('azurecr.io') ||
|
|
220
224
|
(errorMsg.includes('authenticate') && (errorMsg.includes('registry') || errorMsg.includes('container')))) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker/beat compose fragment for local runs.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Compose context for optional worker and beat services from application.yaml
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { buildWorkerSidecarComposeContext } = require('./worker-sidecars');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} appKey
|
|
13
|
+
* @param {object} appConfig
|
|
14
|
+
* @param {{ devId: string|number, idNum: number }} ids
|
|
15
|
+
* @returns {ReturnType<typeof buildWorkerSidecarComposeContext>}
|
|
16
|
+
*/
|
|
17
|
+
function buildCeleryWorkerComposeContext(appKey, appConfig, ids) {
|
|
18
|
+
return buildWorkerSidecarComposeContext(appKey, appConfig, ids);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
buildCeleryWorkerComposeContext
|
|
23
|
+
};
|
|
@@ -14,6 +14,7 @@ const buildCopy = require('./build-copy');
|
|
|
14
14
|
const paths = require('./paths');
|
|
15
15
|
const { getInfraDirName } = require('../infrastructure/helpers');
|
|
16
16
|
const { resolveMisoEnvironment } = require('./compose-miso-env');
|
|
17
|
+
const { buildCeleryWorkerComposeContext } = require('./compose-celery-worker');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* When running a pulled Python image (no bind-mounted source), the image CMD may invoke the
|
|
@@ -149,7 +150,11 @@ function buildComposeTemplateContext(ctx) {
|
|
|
149
150
|
devMountPath: ctx.devMountPath,
|
|
150
151
|
reloadStartCommand: ctx.reloadStartCommand,
|
|
151
152
|
infraPgpassPath: ctx.useInfraPgpass ? ctx.infraPgpassPath : null,
|
|
152
|
-
useInfraPgpass: !!ctx.useInfraPgpass
|
|
153
|
+
useInfraPgpass: !!ctx.useInfraPgpass,
|
|
154
|
+
workerSidecars: buildCeleryWorkerComposeContext(ctx.appName, ctx.appConfig, {
|
|
155
|
+
devId: ctx.devId,
|
|
156
|
+
idNum: ctx.idNum
|
|
157
|
+
})
|
|
153
158
|
};
|
|
154
159
|
}
|
|
155
160
|
|
|
@@ -303,6 +308,9 @@ async function generateDockerComposeImpl(deps, appName, appConfig, options) {
|
|
|
303
308
|
...layouts,
|
|
304
309
|
...side,
|
|
305
310
|
idNum: head.idNum,
|
|
311
|
+
appName,
|
|
312
|
+
appConfig,
|
|
313
|
+
devId: head.devId,
|
|
306
314
|
options
|
|
307
315
|
})
|
|
308
316
|
);
|
|
@@ -190,6 +190,40 @@ function retemplateConfigurationFromEnvTemplate(configArray, envTemplateVariable
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Re-templates every system variable definition to {{NAME}} (download/export safe).
|
|
195
|
+
*
|
|
196
|
+
* @param {Array<{ name?: string, location?: string }>} configArray - Configuration array (mutated)
|
|
197
|
+
*/
|
|
198
|
+
function retemplateAllVariableDefinitions(configArray) {
|
|
199
|
+
if (!Array.isArray(configArray)) return;
|
|
200
|
+
for (const item of configArray) {
|
|
201
|
+
if (!item || String(item.location || 'variable').toLowerCase() !== 'variable') continue;
|
|
202
|
+
const name = item.name && String(item.name).trim();
|
|
203
|
+
if (name) {
|
|
204
|
+
item.value = `{{${name}}}`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Re-templates datasource override literals to {{NAME}} placeholders.
|
|
211
|
+
*
|
|
212
|
+
* @param {Array<{ name?: string, value?: string }>} configArray - Datasource configuration (mutated)
|
|
213
|
+
*/
|
|
214
|
+
function retemplateDatasourceConfigurationOverrides(configArray) {
|
|
215
|
+
if (!Array.isArray(configArray)) return;
|
|
216
|
+
for (const item of configArray) {
|
|
217
|
+
if (!item || !item.name) continue;
|
|
218
|
+
const name = String(item.name).trim();
|
|
219
|
+
const raw = item.value;
|
|
220
|
+
if (typeof raw !== 'string') continue;
|
|
221
|
+
const value = raw.trim();
|
|
222
|
+
if (!value || value.startsWith('kv://') || value.includes('{{')) continue;
|
|
223
|
+
item.value = `{{${name}}}`;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
193
227
|
/**
|
|
194
228
|
* Reads env.template at integration path, re-templates the given configuration array,
|
|
195
229
|
* and returns the updated array (mutates in place). If env.template is missing, does nothing.
|
|
@@ -215,6 +249,8 @@ module.exports = {
|
|
|
215
249
|
collectResolvedVariableConfigurationNames,
|
|
216
250
|
getEnvTemplateVariableNames,
|
|
217
251
|
retemplateConfigurationFromEnvTemplate,
|
|
252
|
+
retemplateAllVariableDefinitions,
|
|
253
|
+
retemplateDatasourceConfigurationOverrides,
|
|
218
254
|
retemplateConfigurationForDownload,
|
|
219
255
|
substituteVarPlaceholders
|
|
220
256
|
};
|
|
@@ -145,7 +145,10 @@ async function isControllerHealthReachable(controllerUrl) {
|
|
|
145
145
|
return false;
|
|
146
146
|
}
|
|
147
147
|
try {
|
|
148
|
-
const
|
|
148
|
+
const { fetchWithOptionalDevPlatformTls } = require('./dev-platform-tls');
|
|
149
|
+
const res = await fetchWithOptionalDevPlatformTls(`${base}/health`, {
|
|
150
|
+
signal: AbortSignal.timeout(2500)
|
|
151
|
+
});
|
|
149
152
|
return res.ok;
|
|
150
153
|
} catch {
|
|
151
154
|
return false;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KV_* env key ↔ kv:// path mapping and payload reference scanning.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview KV path helpers for credential-secrets-env
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const KV_PREFIX = 'KV_';
|
|
10
|
+
const KV_REF_PATTERN = /kv:\/\/([a-zA-Z0-9_\-/]+)/g;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Converts systemKey to KV_* prefix (e.g. hubspot -> HUBSPOT, my-hubspot -> MY_HUBSPOT).
|
|
14
|
+
* @param {string} systemKey - System key
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function systemKeyToKvPrefix(systemKey) {
|
|
18
|
+
if (!systemKey || typeof systemKey !== 'string') return '';
|
|
19
|
+
return systemKey.replace(/-/g, '_').toUpperCase();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Maps authentication security key (camelCase) to env VAR (UPPERCASE, no underscores).
|
|
24
|
+
* @param {string} securityKey - Security key (e.g. 'clientId', 'clientSecret')
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
function securityKeyToVar(securityKey) {
|
|
28
|
+
if (!securityKey || typeof securityKey !== 'string') return '';
|
|
29
|
+
return securityKey.replace(/_/g, '').toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Known single-segment variable suffixes (uppercase) for inferring var vs namespace in env keys */
|
|
33
|
+
const VAR_SUFFIXES = new Set(['ID', 'SECRET', 'KEY', 'TOKEN', 'URL', 'USERNAME', 'PASSWORD']);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Converts var segment(s) from env key to path-style camelCase.
|
|
37
|
+
* @param {string[]} varSegments - One or two segments (e.g. ['CLIENT', 'ID'], ['CLIENTID'])
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
function varSegmentsToCamelCase(varSegments) {
|
|
41
|
+
if (!varSegments || varSegments.length === 0) return '';
|
|
42
|
+
if (varSegments.length === 1) {
|
|
43
|
+
const s = varSegments[0].toLowerCase();
|
|
44
|
+
if (s.endsWith('id') && s.length > 2) return s.slice(0, -2) + 'Id';
|
|
45
|
+
if (s.endsWith('secret') && s.length > 6) return s.slice(0, -6) + 'Secret';
|
|
46
|
+
if (s.endsWith('key') && s.length > 3) return s.slice(0, -3) + 'Key';
|
|
47
|
+
if (s.endsWith('token') && s.length > 5) return s.slice(0, -5) + 'Token';
|
|
48
|
+
if (s.endsWith('url') && s.length > 3) return s.slice(0, -3) + 'Url';
|
|
49
|
+
return s;
|
|
50
|
+
}
|
|
51
|
+
return varSegments.map((seg, i) => {
|
|
52
|
+
const lower = seg.toLowerCase();
|
|
53
|
+
return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
|
|
54
|
+
}).join('');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {string[]} segments - Parsed segments after KV_ prefix
|
|
59
|
+
* @param {string} systemKey - System key (e.g. 'microsoft-teams')
|
|
60
|
+
* @returns {string|null}
|
|
61
|
+
*/
|
|
62
|
+
function kvPathWithSystemKey(segments, systemKey) {
|
|
63
|
+
const prefixInKey = systemKey.replace(/-/g, '_').toUpperCase();
|
|
64
|
+
const prefixSegs = prefixInKey.split('_').filter(Boolean);
|
|
65
|
+
if (segments.length <= prefixSegs.length) return null;
|
|
66
|
+
const prefixMatch = prefixSegs.every((p, i) => segments[i] === p);
|
|
67
|
+
if (!prefixMatch) return null;
|
|
68
|
+
const varSegments = segments.slice(prefixSegs.length);
|
|
69
|
+
const pathVar = varSegmentsToCamelCase(varSegments);
|
|
70
|
+
return pathVar ? `kv://${systemKey}/${pathVar}` : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string[]} segments - Parsed segments after KV_ prefix
|
|
75
|
+
* @returns {string|null}
|
|
76
|
+
*/
|
|
77
|
+
function kvPathInferred(segments) {
|
|
78
|
+
if (segments.length === 1) return `kv://${segments[0].toLowerCase()}`;
|
|
79
|
+
const varSegmentCount = (segments.length >= 2 && VAR_SUFFIXES.has(segments[segments.length - 1])) ? 2 : 1;
|
|
80
|
+
const namespace = segments.slice(0, -varSegmentCount).map(s => s.toLowerCase()).join('-');
|
|
81
|
+
const varSegments = segments.slice(-varSegmentCount);
|
|
82
|
+
const pathVar = varSegmentsToCamelCase(varSegments);
|
|
83
|
+
return (namespace && pathVar) ? `kv://${namespace}/${pathVar}` : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {string} securityKey - Security key (e.g. 'apiKey', 'clientId')
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
function getKvPathSegmentForSecurityKey(securityKey) {
|
|
91
|
+
if (!securityKey || typeof securityKey !== 'string') return '';
|
|
92
|
+
return varSegmentsToCamelCase([securityKeyToVar(securityKey)]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} envKey - Env var name (e.g. KV_MICROSOFT_TEAMS_CLIENT_ID)
|
|
97
|
+
* @param {string} [systemKey] - Optional system key
|
|
98
|
+
* @returns {string|null}
|
|
99
|
+
*/
|
|
100
|
+
function kvEnvKeyToPath(envKey, systemKey) {
|
|
101
|
+
if (!envKey || typeof envKey !== 'string' || !envKey.toUpperCase().startsWith(KV_PREFIX)) return null;
|
|
102
|
+
const rest = envKey.slice(KV_PREFIX.length).trim();
|
|
103
|
+
if (!rest) return null;
|
|
104
|
+
const segments = rest.split('_').filter(Boolean);
|
|
105
|
+
if (segments.length === 0) return null;
|
|
106
|
+
|
|
107
|
+
const hasSystemKey = typeof systemKey === 'string' && systemKey.length > 0;
|
|
108
|
+
if (hasSystemKey) return kvPathWithSystemKey(segments, systemKey);
|
|
109
|
+
return kvPathInferred(segments);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {Object.<string, string>} envMap - Key-value map from .env
|
|
114
|
+
* @param {string} [systemKey]
|
|
115
|
+
* @returns {Array<{ key: string, value: string }>}
|
|
116
|
+
*/
|
|
117
|
+
function collectKvEnvVarsAsSecretItems(envMap, systemKey) {
|
|
118
|
+
if (!envMap || typeof envMap !== 'object') {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const items = [];
|
|
122
|
+
for (const [envKey, rawValue] of Object.entries(envMap)) {
|
|
123
|
+
const value = typeof rawValue === 'string' ? rawValue.trim() : '';
|
|
124
|
+
if (value === '') continue;
|
|
125
|
+
let kvPath = null;
|
|
126
|
+
if (value.startsWith('kv://') && isValidKvPath(value)) {
|
|
127
|
+
kvPath = value;
|
|
128
|
+
}
|
|
129
|
+
if (!kvPath) kvPath = kvEnvKeyToPath(envKey, systemKey) || kvEnvKeyToPath(envKey);
|
|
130
|
+
if (!kvPath) continue;
|
|
131
|
+
items.push({ key: kvPath, value });
|
|
132
|
+
}
|
|
133
|
+
return items;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {Object} secrets - Loaded secrets object
|
|
138
|
+
* @param {string} value - Value (may be "kv://..." or plain)
|
|
139
|
+
* @returns {string|null}
|
|
140
|
+
*/
|
|
141
|
+
function resolveKvValue(secrets, value) {
|
|
142
|
+
if (typeof value !== 'string') return null;
|
|
143
|
+
const trimmed = value.trim();
|
|
144
|
+
if (!trimmed.startsWith('kv://')) {
|
|
145
|
+
return trimmed;
|
|
146
|
+
}
|
|
147
|
+
const pathMatch = trimmed.match(/^kv:\/\/([a-zA-Z0-9_\-/]+)$/);
|
|
148
|
+
if (!pathMatch) return null;
|
|
149
|
+
const pathKey = pathMatch[1];
|
|
150
|
+
const resolved = secrets[pathKey];
|
|
151
|
+
if (resolved === undefined) return null;
|
|
152
|
+
return typeof resolved === 'string' ? resolved : String(resolved);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {Object|Array|string|number|boolean|null} obj
|
|
157
|
+
* @param {Set<string>} [acc]
|
|
158
|
+
* @returns {string[]}
|
|
159
|
+
*/
|
|
160
|
+
function collectKvRefsFromPayload(obj, acc = new Set()) {
|
|
161
|
+
if (obj === null || obj === undefined) {
|
|
162
|
+
return Array.from(acc);
|
|
163
|
+
}
|
|
164
|
+
if (typeof obj === 'string') {
|
|
165
|
+
let m;
|
|
166
|
+
KV_REF_PATTERN.lastIndex = 0;
|
|
167
|
+
while ((m = KV_REF_PATTERN.exec(obj)) !== null) {
|
|
168
|
+
acc.add(`kv://${m[1]}`);
|
|
169
|
+
}
|
|
170
|
+
return Array.from(acc);
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(obj)) {
|
|
173
|
+
for (const item of obj) {
|
|
174
|
+
collectKvRefsFromPayload(item, acc);
|
|
175
|
+
}
|
|
176
|
+
return Array.from(acc);
|
|
177
|
+
}
|
|
178
|
+
if (typeof obj === 'object') {
|
|
179
|
+
for (const v of Object.values(obj)) {
|
|
180
|
+
collectKvRefsFromPayload(v, acc);
|
|
181
|
+
}
|
|
182
|
+
return Array.from(acc);
|
|
183
|
+
}
|
|
184
|
+
return Array.from(acc);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {string} key - kv:// path
|
|
189
|
+
* @returns {boolean}
|
|
190
|
+
*/
|
|
191
|
+
function isValidKvPath(key) {
|
|
192
|
+
return typeof key === 'string' && /^kv:\/\/[a-z0-9][a-z0-9\-/]*$/i.test(key.trim());
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {unknown} resolved
|
|
197
|
+
* @returns {boolean}
|
|
198
|
+
*/
|
|
199
|
+
function isNonEmptySecretValue(resolved) {
|
|
200
|
+
if (resolved === null || resolved === undefined) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return String(resolved).trim() !== '';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = {
|
|
207
|
+
collectKvEnvVarsAsSecretItems,
|
|
208
|
+
collectKvRefsFromPayload,
|
|
209
|
+
getKvPathSegmentForSecurityKey,
|
|
210
|
+
isNonEmptySecretValue,
|
|
211
|
+
isValidKvPath,
|
|
212
|
+
kvEnvKeyToPath,
|
|
213
|
+
resolveKvValue,
|
|
214
|
+
securityKeyToVar,
|
|
215
|
+
systemKeyToKvPrefix
|
|
216
|
+
};
|