@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.
Files changed (175) hide show
  1. package/.npmrc.token +1 -0
  2. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  3. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  4. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  5. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  7. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  8. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  9. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  10. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  14. package/.temp/validation/summary.txt +5 -0
  15. package/jest.isolated-projects.js +18 -2
  16. package/jest.projects.js +24 -0
  17. package/lib/api/dataplane-health.api.js +19 -7
  18. package/lib/api/lifecycle.api.js +22 -1
  19. package/lib/api/resource-types.api.js +107 -0
  20. package/lib/api/runtime-configuration.api.js +72 -0
  21. package/lib/api/types/resource-types.types.js +45 -0
  22. package/lib/api/user-groups.api.js +9 -9
  23. package/lib/api/users.api.js +8 -8
  24. package/lib/api/wizard.api.js +10 -2
  25. package/lib/app/down.js +58 -26
  26. package/lib/app/index.js +2 -2
  27. package/lib/app/run-resolve-image.js +2 -1
  28. package/lib/app/run-stop-containers.js +113 -0
  29. package/lib/app/run.js +1 -31
  30. package/lib/app/show-display.js +35 -0
  31. package/lib/app/show.js +32 -1
  32. package/lib/build/index.js +17 -5
  33. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  34. package/lib/channels/add-channel.js +348 -0
  35. package/lib/channels/channel-artifact-generator.js +141 -0
  36. package/lib/channels/channel-audit.js +101 -0
  37. package/lib/channels/channel-auth-context.js +37 -0
  38. package/lib/channels/channel-credential.js +200 -0
  39. package/lib/channels/channel-display-name.js +64 -0
  40. package/lib/channels/channel-external-system.js +106 -0
  41. package/lib/channels/channel-integration-generator.js +274 -0
  42. package/lib/channels/channel-probe.js +89 -0
  43. package/lib/channels/channel-runtime-resolver.js +152 -0
  44. package/lib/channels/channel-status-display.js +141 -0
  45. package/lib/channels/channel-targets.js +115 -0
  46. package/lib/cli/index.js +16 -4
  47. package/lib/cli/setup-app.js +54 -3
  48. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  49. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  50. package/lib/cli/setup-platform.js +1 -1
  51. package/lib/cli/setup-utility-help-after.js +4 -1
  52. package/lib/cli/setup-utility-resolve.js +8 -5
  53. package/lib/commands/app-logs.js +54 -26
  54. package/lib/commands/app.js +41 -0
  55. package/lib/commands/auth-status-dataplane-version.js +97 -61
  56. package/lib/commands/auth-status.js +2 -7
  57. package/lib/commands/channel.js +58 -0
  58. package/lib/commands/datasource.js +1 -1
  59. package/lib/commands/identity-apply-sync.js +49 -8
  60. package/lib/commands/identity-group-cli.js +66 -26
  61. package/lib/commands/identity-membership-role-cache.js +58 -37
  62. package/lib/commands/identity-shared.js +22 -1
  63. package/lib/commands/identity-user-cli.js +80 -34
  64. package/lib/commands/repair-auth-apply.js +30 -4
  65. package/lib/commands/repair-auth-config.js +8 -13
  66. package/lib/commands/repair-datasource-com-v2.js +15 -0
  67. package/lib/commands/repair-datasource-service-contract.js +206 -0
  68. package/lib/commands/repair-datasource.js +53 -18
  69. package/lib/commands/repair-env-template.js +12 -12
  70. package/lib/commands/repair-openapi-sync.js +26 -4
  71. package/lib/commands/repair-steps.js +28 -13
  72. package/lib/commands/repair-system-alignment.js +7 -4
  73. package/lib/commands/resource-type.js +371 -0
  74. package/lib/commands/setup-modes.js +1 -1
  75. package/lib/commands/setup-prompts-platform-mode.js +24 -21
  76. package/lib/commands/test-trust-external.js +39 -90
  77. package/lib/commands/up-common.js +33 -1
  78. package/lib/commands/up-dataplane.js +8 -4
  79. package/lib/commands/upload.js +24 -3
  80. package/lib/commands/verify-trust-command-action.js +4 -1
  81. package/lib/commands/verify-trust-external.js +12 -20
  82. package/lib/core/config-setup-platform-mode.js +3 -3
  83. package/lib/core/setup-platform-mode.js +41 -6
  84. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  85. package/lib/datasource/capability/capability-manifest.js +254 -0
  86. package/lib/datasource/capability/capability-resolve.js +3 -4
  87. package/lib/datasource/capability/copy-operations.js +5 -3
  88. package/lib/datasource/capability/remove-operations.js +2 -1
  89. package/lib/datasource/capability/run-capability-copy.js +32 -4
  90. package/lib/datasource/capability/run-capability-diff.js +3 -3
  91. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  92. package/lib/datasource/capability/run-capability-edit.js +4 -7
  93. package/lib/datasource/capability/run-capability-relate.js +2 -4
  94. package/lib/datasource/capability/run-capability-remove.js +2 -4
  95. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  96. package/lib/datasource/datasource-identifier.js +2 -1
  97. package/lib/datasource/datasource-validate-display.js +1 -1
  98. package/lib/datasource/field-reference-validator.js +24 -3
  99. package/lib/datasource/orphan-profiles-validator.js +98 -0
  100. package/lib/datasource/validate.js +41 -23
  101. package/lib/external-system/download-helpers.js +87 -0
  102. package/lib/external-system/download.js +61 -122
  103. package/lib/external-system/generator.js +1 -59
  104. package/lib/external-system/test.js +10 -15
  105. package/lib/generator/builders.js +11 -1
  106. package/lib/generator/external-controller-manifest.js +5 -2
  107. package/lib/generator/split.js +3 -1
  108. package/lib/generator/wizard.js +1 -28
  109. package/lib/identity/fk-reference.js +65 -0
  110. package/lib/identity/identity-apply-core.js +13 -58
  111. package/lib/identity/identity-apply-phases.js +5 -1
  112. package/lib/identity/identity-display.js +243 -0
  113. package/lib/identity/identity-response-helpers.js +52 -0
  114. package/lib/identity/identity-sync-errors.js +62 -0
  115. package/lib/identity/resolve-user-id.js +129 -0
  116. package/lib/identity/username-default.js +31 -0
  117. package/lib/lifecycle/certification-progress.js +2 -2
  118. package/lib/lifecycle/report-display-trust.js +70 -0
  119. package/lib/lifecycle/report-display.js +14 -13
  120. package/lib/resolvers/datasource-resolver.js +2 -3
  121. package/lib/resolvers/resource-type-file.js +159 -0
  122. package/lib/schema/application-schema.json +16 -0
  123. package/lib/schema/deployment-rules.yaml +2 -1
  124. package/lib/schema/external-datasource.schema.json +372 -58
  125. package/lib/schema/external-system.schema.json +331 -2
  126. package/lib/schema/type/document-storage.json +9 -5
  127. package/lib/utils/app-run-containers.js +25 -2
  128. package/lib/utils/authentication-shape.js +224 -0
  129. package/lib/utils/build-copy.js +36 -5
  130. package/lib/utils/build-resolve-image.js +66 -12
  131. package/lib/utils/celery-companion-apps.js +53 -0
  132. package/lib/utils/cli-utils.js +4 -0
  133. package/lib/utils/compose-celery-worker.js +23 -0
  134. package/lib/utils/compose-generate-docker-compose.js +9 -1
  135. package/lib/utils/configuration-env-resolver.js +36 -0
  136. package/lib/utils/credential-secrets-env-kv.js +216 -0
  137. package/lib/utils/credential-secrets-env.js +52 -262
  138. package/lib/utils/dataplane-resolver.js +4 -115
  139. package/lib/utils/declarative-url-ports.js +24 -1
  140. package/lib/utils/docker-build.js +1 -4
  141. package/lib/utils/external-env-template.js +10 -3
  142. package/lib/utils/external-system-validators.js +8 -2
  143. package/lib/utils/help-builder.js +2 -0
  144. package/lib/utils/paths.js +25 -0
  145. package/lib/utils/platform-controller-url.js +2 -1
  146. package/lib/utils/platform-kv-ref.js +57 -0
  147. package/lib/utils/runtime-configuration-download.js +189 -0
  148. package/lib/utils/runtime-configuration-values.js +119 -0
  149. package/lib/utils/schema-loader.js +19 -4
  150. package/lib/utils/worker-sidecars.js +142 -0
  151. package/lib/validation/external-manifest-validator.js +2 -0
  152. package/lib/validation/runtime-configuration-rules.js +98 -0
  153. package/lib/validation/validate-display-dimensions.js +154 -0
  154. package/lib/validation/validate-display-sections.js +276 -0
  155. package/lib/validation/validate-display-steps.js +67 -0
  156. package/lib/validation/validate-display.js +24 -402
  157. package/package.json +1 -1
  158. package/templates/applications/dataplane/application.yaml +5 -0
  159. package/templates/applications/dataplane/env.template +26 -13
  160. package/templates/applications/dataplane/rbac.yaml +108 -1
  161. package/templates/applications/miso-controller/application.yaml +2 -2
  162. package/templates/applications/miso-controller/env.template +37 -122
  163. package/templates/applications/miso-controller/rbac.yaml +28 -13
  164. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  165. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  166. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  167. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  168. package/templates/channels/slack/install.template.md.hbs +49 -0
  169. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  170. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  171. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  172. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  173. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  174. package/templates/python/docker-compose.hbs +56 -0
  175. package/templates/typescript/docker-compose.hbs +52 -0
@@ -315,6 +315,30 @@ function getDevDirectory(appName, developerId) {
315
315
  return baseDir;
316
316
  }
317
317
 
318
+ /**
319
+ * Target directory for `aifabrix create` — always under {@link process.cwd} (`builder/` or
320
+ * `integration/`) even when those folders do not exist yet. Platform system apps still
321
+ * materialize under {@link getAppsMaterializationParent}.
322
+ *
323
+ * Post-create lookup uses {@link getAppPath} / {@link getBuilderPath} / {@link getIntegrationPath}.
324
+ *
325
+ * @param {string} appName - Application name
326
+ * @param {string} [appType] - Application type (`external` or other)
327
+ * @returns {string} Absolute path to the new application directory
328
+ */
329
+ function getAppCreatePath(appName, appType) {
330
+ if (!appName || typeof appName !== 'string') {
331
+ throw new Error('App name is required and must be a string');
332
+ }
333
+ if (appType === 'external') {
334
+ return path.join(path.resolve(process.cwd()), 'integration', appName);
335
+ }
336
+ if (isSystemBuilderAppName(appName)) {
337
+ return path.join(getAppsMaterializationParent(), 'builder', appName);
338
+ }
339
+ return path.join(path.resolve(process.cwd()), 'builder', appName);
340
+ }
341
+
318
342
  /**
319
343
  * Gets the application path (builder or integration folder).
320
344
  * Matches {@link getBuilderPath} / {@link getIntegrationPath} (cwd `integration/` / `builder/`, then
@@ -881,6 +905,7 @@ module.exports = {
881
905
  getApplicationsBaseDir,
882
906
  getDevDirectory,
883
907
  getAppPath,
908
+ getAppCreatePath,
884
909
  getAppsMaterializationParent,
885
910
  getCwdIntegrationRoot,
886
911
  getCwdBuilderRoot,
@@ -18,6 +18,7 @@ const { loadConfigFile } = require('./config-format');
18
18
  const { computePublicUrlBaseString } = require('./url-declarative-public-base');
19
19
  const { joinUrlPath, normalizeFrontDoorPatternForHealth } = require('./health-check-url');
20
20
  const { getDevPorts } = require('./dev-config');
21
+ const { flagsForSetupPlatformMode } = require('../core/setup-platform-mode');
21
22
 
22
23
  const MISO_CONTROLLER_APP = 'miso-controller';
23
24
 
@@ -74,7 +75,7 @@ function loadAppVariables(appName) {
74
75
 
75
76
  function computeLocalFullPlatformFrontDoorUrl(params) {
76
77
  const { setupPlatformMode, remoteServer, developerIdNum, frontDoorRouting } = params;
77
- if (setupPlatformMode !== 'full') {
78
+ if (!flagsForSetupPlatformMode(setupPlatformMode).traefik) {
78
79
  return null;
79
80
  }
80
81
  if (remoteServer && String(remoteServer).trim()) {
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Platform installation kv:// refs from infra.parameter.yaml (flat keys, no path segments).
3
+ *
4
+ * @fileoverview Distinguish platform catalog secrets from per-integration path-style kv refs
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
12
+
13
+ /**
14
+ * Returns the single-segment path after kv:// when the ref has no slashes.
15
+ *
16
+ * @param {string} val - kv:// reference
17
+ * @returns {string|null}
18
+ */
19
+ function kvRefSegment(val) {
20
+ if (typeof val !== 'string' || !val.trim().toLowerCase().startsWith('kv://')) return null;
21
+ const after = val.trim().slice(5);
22
+ if (!after || after.includes('/')) return null;
23
+ return after;
24
+ }
25
+
26
+ /**
27
+ * True when val is kv://<key> and key exists in shipped infra.parameter.yaml.
28
+ *
29
+ * @param {string} val - kv:// reference
30
+ * @returns {boolean}
31
+ */
32
+ function isPlatformCatalogKvRef(val) {
33
+ const segment = kvRefSegment(val);
34
+ if (!segment) return false;
35
+ try {
36
+ const catalog = getInfraParameterCatalog();
37
+ return Boolean(catalog.findEntryForKey(segment));
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * True when repair should normalize a kv value to path-style kv://systemKey/variable.
45
+ * Platform catalog refs (e.g. kv://secrets-openaiApiKeyVault) are never legacy.
46
+ *
47
+ * @param {string} val - Value from authentication.security or configuration
48
+ * @returns {boolean}
49
+ */
50
+ function isLegacyKvValue(val) {
51
+ if (typeof val !== 'string' || !val.trim().toLowerCase().startsWith('kv://')) return false;
52
+ if (isPlatformCatalogKvRef(val)) return false;
53
+ const after = val.trim().slice(5);
54
+ return after.includes('KeyVault') || !after.includes('/');
55
+ }
56
+
57
+ module.exports = { kvRefSegment, isPlatformCatalogKvRef, isLegacyKvValue };
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Sync runtime configuration_values from dataplane on download (values → env.template; manifest → placeholders).
3
+ *
4
+ * @fileoverview Download-time runtime configuration sync
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const fsPromises = require('fs').promises;
11
+ const path = require('path');
12
+ const yaml = require('js-yaml');
13
+
14
+ const {
15
+ getExternalSystemConfigurationResolved,
16
+ getExternalDataSourceConfigurationResolved
17
+ } = require('../api/runtime-configuration.api');
18
+ const {
19
+ retemplateAllVariableDefinitions,
20
+ retemplateDatasourceConfigurationOverrides
21
+ } = require('./configuration-env-resolver');
22
+ const { mergeEnvTemplateWithExisting } = require('../generator/split');
23
+
24
+ /**
25
+ * @param {Object} response - Dataplane API client response
26
+ * @returns {Record<string, string>}
27
+ */
28
+ function extractResolvedVariableValues(response) {
29
+ if (!response || !response.success) {
30
+ return {};
31
+ }
32
+ const body = response.data && typeof response.data === 'object' ? response.data : {};
33
+ const variables = body.variables && typeof body.variables === 'object' ? body.variables : {};
34
+ const out = {};
35
+ for (const [name, entry] of Object.entries(variables)) {
36
+ if (!entry || typeof entry.value !== 'string') {
37
+ continue;
38
+ }
39
+ const value = entry.value.trim();
40
+ if (!value || value.startsWith('kv://')) {
41
+ continue;
42
+ }
43
+ out[name] = value;
44
+ }
45
+ return out;
46
+ }
47
+
48
+ /**
49
+ * @param {string} dataplaneUrl
50
+ * @param {Object} authConfig
51
+ * @param {string} systemKey
52
+ * @param {Array<{ key?: string }>} dataSources
53
+ * @returns {Promise<Record<string, string>>}
54
+ */
55
+ async function collectResolvedConfigurationEnvUpdates(
56
+ dataplaneUrl,
57
+ authConfig,
58
+ systemKey,
59
+ dataSources
60
+ ) {
61
+ const updates = {};
62
+ const systemResponse = await getExternalSystemConfigurationResolved(
63
+ dataplaneUrl,
64
+ systemKey,
65
+ authConfig
66
+ );
67
+ Object.assign(updates, extractResolvedVariableValues(systemResponse));
68
+
69
+ for (const ds of dataSources || []) {
70
+ if (!ds?.key) {
71
+ continue;
72
+ }
73
+ const dsResponse = await getExternalDataSourceConfigurationResolved(
74
+ dataplaneUrl,
75
+ ds.key,
76
+ authConfig
77
+ );
78
+ Object.assign(updates, extractResolvedVariableValues(dsResponse));
79
+ }
80
+ return updates;
81
+ }
82
+
83
+ /**
84
+ * @param {string} integrationPath
85
+ * @param {Record<string, string>} updates
86
+ * @returns {Promise<boolean>}
87
+ */
88
+ async function mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates) {
89
+ const keys = Object.keys(updates || {});
90
+ if (!keys.length) {
91
+ return false;
92
+ }
93
+ const envTemplatePath = path.join(integrationPath, 'env.template');
94
+ const expectedByKey = new Map();
95
+ for (const key of keys) {
96
+ expectedByKey.set(key, `${key}=${updates[key]}`);
97
+ }
98
+ const existingContent = fs.existsSync(envTemplatePath)
99
+ ? await fsPromises.readFile(envTemplatePath, 'utf8')
100
+ : '';
101
+ const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
102
+ await fsPromises.writeFile(envTemplatePath, merged, { mode: 0o644, encoding: 'utf8' });
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * @param {string} filePath
108
+ * @returns {Promise<boolean>}
109
+ */
110
+ async function retemplateConfigurationFile(filePath, { system = false } = {}) {
111
+ if (!filePath || !fs.existsSync(filePath)) {
112
+ return false;
113
+ }
114
+ const raw = await fsPromises.readFile(filePath, 'utf8');
115
+ const isJson = filePath.endsWith('.json');
116
+ const parsed = isJson ? JSON.parse(raw) : yaml.load(raw);
117
+ if (!parsed || typeof parsed !== 'object') {
118
+ return false;
119
+ }
120
+ if (system) {
121
+ retemplateAllVariableDefinitions(parsed.configuration);
122
+ } else {
123
+ retemplateDatasourceConfigurationOverrides(parsed.configuration);
124
+ }
125
+ const next = isJson ? `${JSON.stringify(parsed, null, 2)}\n` : yaml.dump(parsed, { indent: 2, lineWidth: -1 });
126
+ await fsPromises.writeFile(filePath, next, { mode: 0o644, encoding: 'utf8' });
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * @param {string} integrationPath
132
+ * @param {string} systemKey
133
+ * @returns {Promise<void>}
134
+ */
135
+ async function retemplateDownloadedConfigurationFiles(integrationPath, systemKey) {
136
+ for (const ext of ['yaml', 'json']) {
137
+ const systemPath = path.join(integrationPath, `${systemKey}-system.${ext}`);
138
+ if (fs.existsSync(systemPath)) {
139
+ await retemplateConfigurationFile(systemPath, { system: true });
140
+ }
141
+ }
142
+ const entries = await fsPromises.readdir(integrationPath);
143
+ for (const name of entries) {
144
+ if (!name.includes('-datasource-') || (!name.endsWith('.yaml') && !name.endsWith('.json'))) {
145
+ continue;
146
+ }
147
+ await retemplateConfigurationFile(path.join(integrationPath, name), { system: false });
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Pulls configuration_values from dataplane, merges env.template, and re-templates manifests.
153
+ *
154
+ * @param {Object} params
155
+ * @param {string} params.dataplaneUrl
156
+ * @param {Object} params.authConfig
157
+ * @param {string} params.systemKey
158
+ * @param {string} params.integrationPath
159
+ * @param {Array<{ key?: string }>} params.dataSources
160
+ * @returns {Promise<{ merged: number, names: string[] }>}
161
+ */
162
+ async function syncRuntimeConfigurationFromDataplane({
163
+ dataplaneUrl,
164
+ authConfig,
165
+ systemKey,
166
+ integrationPath,
167
+ dataSources
168
+ }) {
169
+ const updates = await collectResolvedConfigurationEnvUpdates(
170
+ dataplaneUrl,
171
+ authConfig,
172
+ systemKey,
173
+ dataSources
174
+ );
175
+ const names = Object.keys(updates);
176
+ if (names.length) {
177
+ await mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates);
178
+ }
179
+ await retemplateDownloadedConfigurationFiles(integrationPath, systemKey);
180
+ return { merged: names.length, names };
181
+ }
182
+
183
+ module.exports = {
184
+ extractResolvedVariableValues,
185
+ collectResolvedConfigurationEnvUpdates,
186
+ mergeConfigurationUpdatesIntoEnvTemplate,
187
+ retemplateDownloadedConfigurationFiles,
188
+ syncRuntimeConfigurationFromDataplane
189
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Push runtime configuration_values to dataplane after publish (never logs values).
3
+ *
4
+ * @fileoverview Post-publish configuration PUT helpers
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ const {
10
+ putExternalSystemConfiguration,
11
+ putExternalDataSourceConfiguration
12
+ } = require('../api/runtime-configuration.api');
13
+ const { buildResolvedEnvMapForIntegration } = require('./configuration-env-resolver');
14
+
15
+ /**
16
+ * @param {Array<{ name?: string, location?: string }>|undefined} systemConfiguration
17
+ * @param {Object.<string, string>} envMap
18
+ * @returns {Array<{ name: string, value: string }>}
19
+ */
20
+ function collectSystemVariablePutRequests(systemConfiguration, envMap) {
21
+ if (!Array.isArray(systemConfiguration)) {
22
+ return [];
23
+ }
24
+ const requests = [];
25
+ for (const item of systemConfiguration) {
26
+ if (!item || !item.name) {
27
+ continue;
28
+ }
29
+ const location = String(item.location || 'variable').toLowerCase();
30
+ if (location !== 'variable') {
31
+ continue;
32
+ }
33
+ const name = String(item.name).trim();
34
+ const raw = envMap[name];
35
+ if (raw === undefined || raw === null) {
36
+ continue;
37
+ }
38
+ const value = String(raw).trim();
39
+ if (!value || value.startsWith('kv://') || value.includes('{{')) {
40
+ continue;
41
+ }
42
+ requests.push({ name, value });
43
+ }
44
+ return requests;
45
+ }
46
+
47
+ /**
48
+ * Datasource overrides are name+value only; values may already be resolved in upload payload.
49
+ *
50
+ * @param {Array<{ name?: string, value?: string }>|undefined} datasourceConfiguration
51
+ * @returns {Array<{ name: string, value: string }>}
52
+ */
53
+ function collectDatasourceOverridePutRequests(datasourceConfiguration) {
54
+ if (!Array.isArray(datasourceConfiguration)) {
55
+ return [];
56
+ }
57
+ const requests = [];
58
+ for (const item of datasourceConfiguration) {
59
+ if (!item || !item.name) {
60
+ continue;
61
+ }
62
+ const name = String(item.name).trim();
63
+ const raw = item.value;
64
+ if (typeof raw !== 'string') {
65
+ continue;
66
+ }
67
+ const value = raw.trim();
68
+ if (!value || value.startsWith('kv://') || value.includes('{{')) {
69
+ continue;
70
+ }
71
+ requests.push({ name, value });
72
+ }
73
+ return requests;
74
+ }
75
+
76
+ /**
77
+ * Upserts system-scoped and datasource-scoped configuration values after publish.
78
+ *
79
+ * @param {string} dataplaneUrl
80
+ * @param {Object} authConfig
81
+ * @param {string} systemKey
82
+ * @param {{ application?: { configuration?: unknown[] }, dataSources?: Array<{ key?: string, configuration?: unknown[] }> }} payload
83
+ * @returns {Promise<{ pushed: number, names: string[] }>}
84
+ */
85
+ async function pushRuntimeConfigurationValues(dataplaneUrl, authConfig, systemKey, payload) {
86
+ const { envMap } = await buildResolvedEnvMapForIntegration(systemKey);
87
+ const names = [];
88
+ let pushed = 0;
89
+
90
+ const systemRequests = collectSystemVariablePutRequests(
91
+ payload?.application?.configuration,
92
+ envMap
93
+ );
94
+ for (const body of systemRequests) {
95
+ await putExternalSystemConfiguration(dataplaneUrl, systemKey, authConfig, body);
96
+ names.push(body.name);
97
+ pushed += 1;
98
+ }
99
+
100
+ for (const ds of payload?.dataSources || []) {
101
+ if (!ds?.key) {
102
+ continue;
103
+ }
104
+ const dsRequests = collectDatasourceOverridePutRequests(ds.configuration);
105
+ for (const body of dsRequests) {
106
+ await putExternalDataSourceConfiguration(dataplaneUrl, ds.key, authConfig, body);
107
+ names.push(`${ds.key}:${body.name}`);
108
+ pushed += 1;
109
+ }
110
+ }
111
+
112
+ return { pushed, names };
113
+ }
114
+
115
+ module.exports = {
116
+ collectSystemVariablePutRequests,
117
+ collectDatasourceOverridePutRequests,
118
+ pushRuntimeConfigurationValues
119
+ };
@@ -28,6 +28,23 @@ function resetValidators() {
28
28
  externalDataSourceValidator = null;
29
29
  }
30
30
 
31
+ /**
32
+ * Register AJV schemas required to compile or validate external-datasource manifests.
33
+ * Includes type schemas and external-system (for $defs/subscriptionFilter cross-ref).
34
+ *
35
+ * @function registerExternalDatasourceAjvDependencies
36
+ * @param {import('ajv').default} ajv - AJV instance
37
+ */
38
+ function registerExternalDatasourceAjvDependencies(ajv) {
39
+ ajv.addSchema(require('../schema/type/document-storage.json'));
40
+ ajv.addSchema(require('../schema/type/message-service.json'));
41
+ ajv.addSchema(require('../schema/type/vector-store.json'));
42
+ const externalSystemSchema = require('../schema/external-system.schema.json');
43
+ if (!ajv.getSchema(externalSystemSchema.$id)) {
44
+ ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
45
+ }
46
+ }
47
+
31
48
  /**
32
49
  * Loads and compiles external-system schema
33
50
  * Caches the compiled validator for performance
@@ -115,10 +132,7 @@ function loadExternalDataSourceSchema() {
115
132
 
116
133
  const ajv = new Ajv({ allErrors: true, strict: false, strictSchema: false });
117
134
  addFormats(ajv);
118
- // external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
119
- ajv.addSchema(require('../schema/type/document-storage.json'));
120
- ajv.addSchema(require('../schema/type/message-service.json'));
121
- ajv.addSchema(require('../schema/type/vector-store.json'));
135
+ registerExternalDatasourceAjvDependencies(ajv);
122
136
  externalDataSourceValidator = ajv.compile(schemaToCompile);
123
137
 
124
138
  return externalDataSourceValidator;
@@ -333,6 +347,7 @@ function detectSchemaType(filePath, content) {
333
347
  module.exports = {
334
348
  loadExternalSystemSchema,
335
349
  loadExternalDataSourceSchema,
350
+ registerExternalDatasourceAjvDependencies,
336
351
  detectSchemaType,
337
352
  detectSchemaTypeFromParsed,
338
353
  resetValidators
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Worker sidecar resolution from application.yaml build settings.
3
+ *
4
+ * @fileoverview Generic worker/beat companions driven by build.worker and imageRun* fields
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { buildDefaultLocalContainerName } = require('./environment-scoped-resources');
10
+
11
+ /**
12
+ * @param {object|null|undefined} build
13
+ * @param {string} field
14
+ * @returns {string|null}
15
+ */
16
+ function readImageRunCommand(build, field) {
17
+ if (!build || typeof build !== 'object') {
18
+ return null;
19
+ }
20
+ const raw = build[field];
21
+ if (typeof raw !== 'string') {
22
+ return null;
23
+ }
24
+ const trimmed = raw.trim();
25
+ return trimmed ? trimmed : null;
26
+ }
27
+
28
+ /**
29
+ * @param {object|null|undefined} appConfig
30
+ * @returns {boolean}
31
+ */
32
+ function isWorkerStackEnabled(appConfig) {
33
+ if (!appConfig || typeof appConfig !== 'object') {
34
+ return false;
35
+ }
36
+ return appConfig.build?.worker === true;
37
+ }
38
+
39
+ /**
40
+ * @param {string} appKey
41
+ * @param {object|null|undefined} appConfig
42
+ * @returns {{
43
+ * enabled: boolean,
44
+ * worker: { key: string, command: string }|null,
45
+ * beat: { key: string, command: string }|null,
46
+ * companions: Array<{ role: string, key: string, command: string }>
47
+ * }}
48
+ */
49
+ function resolveWorkerSidecars(appKey, appConfig) {
50
+ const empty = { enabled: false, worker: null, beat: null, companions: [] };
51
+ if (!isWorkerStackEnabled(appConfig)) {
52
+ return empty;
53
+ }
54
+
55
+ const build = appConfig.build && typeof appConfig.build === 'object' ? appConfig.build : {};
56
+ const workerCommand = readImageRunCommand(build, 'imageRunWorker');
57
+ const beatCommand = readImageRunCommand(build, 'imageRunBeat');
58
+ const companions = [];
59
+
60
+ if (workerCommand) {
61
+ companions.push({ role: 'worker', key: `${appKey}-worker`, command: workerCommand });
62
+ }
63
+ if (beatCommand) {
64
+ companions.push({ role: 'beat', key: `${appKey}-beat`, command: beatCommand });
65
+ }
66
+
67
+ if (companions.length === 0) {
68
+ return empty;
69
+ }
70
+
71
+ const worker = companions.find((entry) => entry.role === 'worker') || null;
72
+ const beat = companions.find((entry) => entry.role === 'beat') || null;
73
+
74
+ return {
75
+ enabled: true,
76
+ worker,
77
+ beat,
78
+ companions
79
+ };
80
+ }
81
+
82
+ /**
83
+ * @param {string} appKey
84
+ * @param {object|null|undefined} appConfig
85
+ * @returns {string[]}
86
+ */
87
+ function getWorkerCompanionAppKeys(appKey, appConfig) {
88
+ return resolveWorkerSidecars(appKey, appConfig).companions.map((entry) => entry.key);
89
+ }
90
+
91
+ /**
92
+ * @param {string} appKey
93
+ * @param {object} appConfig
94
+ * @param {{ devId: string|number, idNum: number }} ids
95
+ * @returns {{
96
+ * enabled: boolean,
97
+ * worker?: { key: string, containerName: string, command: string },
98
+ * beat?: { key: string, containerName: string, command: string }
99
+ * }}
100
+ */
101
+ function buildWorkerSidecarComposeContext(appKey, appConfig, ids) {
102
+ const sidecars = resolveWorkerSidecars(appKey, appConfig);
103
+ if (!sidecars.enabled) {
104
+ return { enabled: false };
105
+ }
106
+
107
+ const context = { enabled: true };
108
+ if (sidecars.worker) {
109
+ context.worker = {
110
+ key: sidecars.worker.key,
111
+ containerName: buildDefaultLocalContainerName(sidecars.worker.key, ids.devId, ids.idNum),
112
+ command: sidecars.worker.command
113
+ };
114
+ }
115
+ if (sidecars.beat) {
116
+ context.beat = {
117
+ key: sidecars.beat.key,
118
+ containerName: buildDefaultLocalContainerName(sidecars.beat.key, ids.devId, ids.idNum),
119
+ command: sidecars.beat.command
120
+ };
121
+ }
122
+ return context;
123
+ }
124
+
125
+ /**
126
+ * Azure startupCommand from compose imageRun (drops leading exec).
127
+ *
128
+ * @param {string} imageRun
129
+ * @returns {string}
130
+ */
131
+ function imageRunToStartupCommand(imageRun) {
132
+ return imageRun.replace(/^exec\s+/i, '').trim();
133
+ }
134
+
135
+ module.exports = {
136
+ readImageRunCommand,
137
+ isWorkerStackEnabled,
138
+ resolveWorkerSidecars,
139
+ getWorkerCompanionAppKeys,
140
+ buildWorkerSidecarComposeContext,
141
+ imageRunToStartupCommand
142
+ };
@@ -16,6 +16,7 @@ const path = require('path');
16
16
  const { formatValidationErrors } = require('../utils/error-formatter');
17
17
  const { validateFieldReferences } = require('../datasource/field-reference-validator');
18
18
  const { validateAbac } = require('../datasource/abac-validator');
19
+ const { validateManifestRuntimeConfiguration } = require('./runtime-configuration-rules');
19
20
 
20
21
  /**
21
22
  * Load and normalize external-system + external-datasource schema objects for manifest AJV.
@@ -247,6 +248,7 @@ async function validateControllerManifest(manifest) {
247
248
  validateInlineSystem(manifest, ajv, externalSystemSchema, errors);
248
249
  validateDatasources(manifest, ajv, externalDatasourceSchema, errors, warnings);
249
250
  validateDatasourceSystemKeyAlignment(manifest, errors);
251
+ errors.push(...validateManifestRuntimeConfiguration(manifest));
250
252
  validateConditionalRequirements(manifest, errors, warnings);
251
253
  validateRequiredFields(manifest, errors);
252
254