@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.
Files changed (191) 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 +18 -4
  47. package/lib/cli/infra-guided-auth.js +36 -12
  48. package/lib/cli/setup-app.js +54 -3
  49. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  50. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  51. package/lib/cli/setup-npm.js +59 -0
  52. package/lib/cli/setup-platform.js +1 -1
  53. package/lib/cli/setup-secrets.js +1 -1
  54. package/lib/cli/setup-utility-help-after.js +4 -1
  55. package/lib/cli/setup-utility-resolve.js +8 -5
  56. package/lib/commands/app-logs.js +54 -26
  57. package/lib/commands/app.js +41 -0
  58. package/lib/commands/auth-status-dataplane-version.js +97 -61
  59. package/lib/commands/auth-status.js +2 -7
  60. package/lib/commands/channel.js +58 -0
  61. package/lib/commands/datasource.js +1 -1
  62. package/lib/commands/identity-apply-sync.js +49 -8
  63. package/lib/commands/identity-group-cli.js +66 -26
  64. package/lib/commands/identity-membership-role-cache.js +58 -37
  65. package/lib/commands/identity-shared.js +22 -1
  66. package/lib/commands/identity-user-cli.js +80 -34
  67. package/lib/commands/login-device.js +11 -3
  68. package/lib/commands/login.js +8 -3
  69. package/lib/commands/npm-config.js +103 -0
  70. package/lib/commands/repair-auth-apply.js +30 -4
  71. package/lib/commands/repair-auth-config.js +8 -13
  72. package/lib/commands/repair-datasource-com-v2.js +15 -0
  73. package/lib/commands/repair-datasource-service-contract.js +206 -0
  74. package/lib/commands/repair-datasource.js +53 -18
  75. package/lib/commands/repair-env-template.js +12 -12
  76. package/lib/commands/repair-openapi-sync.js +26 -4
  77. package/lib/commands/repair-steps.js +28 -13
  78. package/lib/commands/repair-system-alignment.js +7 -4
  79. package/lib/commands/resource-type.js +371 -0
  80. package/lib/commands/setup-modes.js +1 -1
  81. package/lib/commands/setup-prompts-platform-mode.js +24 -21
  82. package/lib/commands/test-trust-external.js +39 -90
  83. package/lib/commands/up-common.js +33 -1
  84. package/lib/commands/up-dataplane.js +8 -4
  85. package/lib/commands/upload.js +24 -3
  86. package/lib/commands/verify-trust-command-action.js +4 -1
  87. package/lib/commands/verify-trust-external.js +12 -20
  88. package/lib/core/config-setup-platform-mode.js +3 -3
  89. package/lib/core/setup-platform-mode.js +41 -6
  90. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  91. package/lib/datasource/capability/capability-manifest.js +254 -0
  92. package/lib/datasource/capability/capability-resolve.js +3 -4
  93. package/lib/datasource/capability/copy-operations.js +5 -3
  94. package/lib/datasource/capability/remove-operations.js +2 -1
  95. package/lib/datasource/capability/run-capability-copy.js +32 -4
  96. package/lib/datasource/capability/run-capability-diff.js +3 -3
  97. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  98. package/lib/datasource/capability/run-capability-edit.js +4 -7
  99. package/lib/datasource/capability/run-capability-relate.js +2 -4
  100. package/lib/datasource/capability/run-capability-remove.js +2 -4
  101. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  102. package/lib/datasource/datasource-identifier.js +2 -1
  103. package/lib/datasource/datasource-validate-display.js +1 -1
  104. package/lib/datasource/field-reference-validator.js +24 -3
  105. package/lib/datasource/orphan-profiles-validator.js +98 -0
  106. package/lib/datasource/validate.js +41 -23
  107. package/lib/external-system/download-helpers.js +87 -0
  108. package/lib/external-system/download.js +61 -122
  109. package/lib/external-system/generator.js +1 -59
  110. package/lib/external-system/test.js +10 -15
  111. package/lib/generator/builders.js +11 -1
  112. package/lib/generator/external-controller-manifest.js +5 -2
  113. package/lib/generator/split.js +3 -1
  114. package/lib/generator/wizard.js +1 -28
  115. package/lib/identity/fk-reference.js +65 -0
  116. package/lib/identity/identity-apply-core.js +13 -58
  117. package/lib/identity/identity-apply-phases.js +5 -1
  118. package/lib/identity/identity-display.js +243 -0
  119. package/lib/identity/identity-response-helpers.js +52 -0
  120. package/lib/identity/identity-sync-errors.js +62 -0
  121. package/lib/identity/resolve-user-id.js +129 -0
  122. package/lib/identity/username-default.js +31 -0
  123. package/lib/lifecycle/certification-progress.js +2 -2
  124. package/lib/lifecycle/report-display-trust.js +70 -0
  125. package/lib/lifecycle/report-display.js +14 -13
  126. package/lib/resolvers/datasource-resolver.js +2 -3
  127. package/lib/resolvers/resource-type-file.js +159 -0
  128. package/lib/schema/application-schema.json +16 -0
  129. package/lib/schema/deployment-rules.yaml +2 -1
  130. package/lib/schema/external-datasource.schema.json +372 -58
  131. package/lib/schema/external-system.schema.json +331 -2
  132. package/lib/schema/type/document-storage.json +9 -5
  133. package/lib/utils/api.js +2 -1
  134. package/lib/utils/app-run-containers.js +25 -2
  135. package/lib/utils/authentication-shape.js +224 -0
  136. package/lib/utils/build-copy.js +36 -5
  137. package/lib/utils/build-resolve-image.js +66 -12
  138. package/lib/utils/celery-companion-apps.js +53 -0
  139. package/lib/utils/cli-utils.js +4 -0
  140. package/lib/utils/compose-celery-worker.js +23 -0
  141. package/lib/utils/compose-generate-docker-compose.js +9 -1
  142. package/lib/utils/configuration-env-resolver.js +36 -0
  143. package/lib/utils/controller-url.js +4 -1
  144. package/lib/utils/credential-secrets-env-kv.js +216 -0
  145. package/lib/utils/credential-secrets-env.js +52 -262
  146. package/lib/utils/dataplane-resolver.js +4 -115
  147. package/lib/utils/declarative-url-ports.js +24 -1
  148. package/lib/utils/dev-platform-tls.js +148 -0
  149. package/lib/utils/docker-build.js +1 -4
  150. package/lib/utils/external-env-template.js +10 -3
  151. package/lib/utils/external-system-validators.js +8 -2
  152. package/lib/utils/health-check-request.js +114 -0
  153. package/lib/utils/health-check-url.js +3 -1
  154. package/lib/utils/health-check.js +1 -113
  155. package/lib/utils/help-builder.js +3 -0
  156. package/lib/utils/keycloak-realm-ready.js +5 -1
  157. package/lib/utils/localhost-public-host.js +31 -0
  158. package/lib/utils/npm-user-config.js +145 -0
  159. package/lib/utils/paths.js +25 -0
  160. package/lib/utils/platform-controller-url.js +58 -4
  161. package/lib/utils/platform-kv-ref.js +57 -0
  162. package/lib/utils/runtime-configuration-download.js +189 -0
  163. package/lib/utils/runtime-configuration-values.js +119 -0
  164. package/lib/utils/schema-loader.js +19 -4
  165. package/lib/utils/url-declarative-public-base.js +33 -25
  166. package/lib/utils/worker-sidecars.js +142 -0
  167. package/lib/validation/external-manifest-validator.js +2 -0
  168. package/lib/validation/runtime-configuration-rules.js +98 -0
  169. package/lib/validation/validate-display-dimensions.js +154 -0
  170. package/lib/validation/validate-display-sections.js +276 -0
  171. package/lib/validation/validate-display-steps.js +67 -0
  172. package/lib/validation/validate-display.js +24 -402
  173. package/package.json +1 -1
  174. package/templates/applications/dataplane/application.yaml +5 -0
  175. package/templates/applications/dataplane/env.template +26 -13
  176. package/templates/applications/dataplane/rbac.yaml +108 -1
  177. package/templates/applications/miso-controller/application.yaml +2 -2
  178. package/templates/applications/miso-controller/env.template +37 -122
  179. package/templates/applications/miso-controller/rbac.yaml +28 -13
  180. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  181. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  182. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  183. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  184. package/templates/channels/slack/install.template.md.hbs +49 -0
  185. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  186. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  187. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  188. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  189. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  190. package/templates/python/docker-compose.hbs +56 -0
  191. package/templates/typescript/docker-compose.hbs +52 -0
@@ -1,7 +1,5 @@
1
1
  /**
2
2
  * Collect KV_* env vars as secret store items and push to dataplane.
3
- * Reads integration .env, resolves kv:// in values, scans payload for kv:// refs,
4
- * and pushes plain values to POST /api/v1/credential/secret.
5
3
  *
6
4
  * @fileoverview Credential secrets push from .env and payload (Dataplane)
7
5
  * @author AI Fabrix Team
@@ -11,217 +9,45 @@
11
9
  const fs = require('fs');
12
10
  const { loadSecrets } = require('../core/secrets');
13
11
  const { storeCredentialSecrets } = require('../api/credential.api');
14
-
15
- const KV_PREFIX = 'KV_';
16
- const KV_REF_PATTERN = /kv:\/\/([a-zA-Z0-9_\-/]+)/g;
17
-
18
- /**
19
- * Converts systemKey to KV_* prefix (e.g. hubspot -> HUBSPOT, my-hubspot -> MY_HUBSPOT).
20
- * @param {string} systemKey - System key
21
- * @returns {string}
22
- */
23
- function systemKeyToKvPrefix(systemKey) {
24
- if (!systemKey || typeof systemKey !== 'string') return '';
25
- return systemKey.replace(/-/g, '_').toUpperCase();
26
- }
27
-
28
- /**
29
- * Maps authentication security key (camelCase) to env VAR (UPPERCASE, no underscores).
30
- * Used for canonical KV_<APPKEY>_<VAR> names (e.g. clientId → CLIENTID).
31
- * @param {string} securityKey - Security key (e.g. 'clientId', 'clientSecret')
32
- * @returns {string}
33
- */
34
- function securityKeyToVar(securityKey) {
35
- if (!securityKey || typeof securityKey !== 'string') return '';
36
- return securityKey.replace(/_/g, '').toUpperCase();
37
- }
38
-
39
- /** Known single-segment variable suffixes (uppercase) for inferring var vs namespace in env keys */
40
- const VAR_SUFFIXES = new Set(['ID', 'SECRET', 'KEY', 'TOKEN', 'URL', 'USERNAME', 'PASSWORD']);
41
-
42
- /**
43
- * Converts var segment(s) from env key to path-style camelCase (e.g. CLIENT_ID → clientId, CLIENTID → clientId).
44
- * @param {string[]} varSegments - One or two segments (e.g. ['CLIENT', 'ID'], ['CLIENTID'])
45
- * @returns {string}
46
- */
47
- function varSegmentsToCamelCase(varSegments) {
48
- if (!varSegments || varSegments.length === 0) return '';
49
- if (varSegments.length === 1) {
50
- const s = varSegments[0].toLowerCase();
51
- if (s.endsWith('id') && s.length > 2) return s.slice(0, -2) + 'Id';
52
- if (s.endsWith('secret') && s.length > 6) return s.slice(0, -6) + 'Secret';
53
- if (s.endsWith('key') && s.length > 3) return s.slice(0, -3) + 'Key';
54
- if (s.endsWith('token') && s.length > 5) return s.slice(0, -5) + 'Token';
55
- if (s.endsWith('url') && s.length > 3) return s.slice(0, -3) + 'Url';
56
- return s;
57
- }
58
- return varSegments.map((seg, i) => {
59
- const lower = seg.toLowerCase();
60
- return i === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1);
61
- }).join('');
62
- }
63
-
64
- /**
65
- * Builds kv path from segments when systemKey is provided (path = kv://systemKey/variable).
66
- * @param {string[]} segments - Parsed segments after KV_ prefix
67
- * @param {string} systemKey - System key (e.g. 'microsoft-teams')
68
- * @returns {string|null}
69
- */
70
- function kvPathWithSystemKey(segments, systemKey) {
71
- const prefixInKey = systemKey.replace(/-/g, '_').toUpperCase();
72
- const prefixSegs = prefixInKey.split('_').filter(Boolean);
73
- if (segments.length <= prefixSegs.length) return null;
74
- const prefixMatch = prefixSegs.every((p, i) => segments[i] === p);
75
- if (!prefixMatch) return null;
76
- const varSegments = segments.slice(prefixSegs.length);
77
- const pathVar = varSegmentsToCamelCase(varSegments);
78
- return pathVar ? `kv://${systemKey}/${pathVar}` : null;
79
- }
80
-
81
- /**
82
- * Builds kv path from segments when systemKey is not provided (infers namespace and variable).
83
- * @param {string[]} segments - Parsed segments after KV_ prefix
84
- * @returns {string|null}
85
- */
86
- function kvPathInferred(segments) {
87
- if (segments.length === 1) return `kv://${segments[0].toLowerCase()}`;
88
- const varSegmentCount = (segments.length >= 2 && VAR_SUFFIXES.has(segments[segments.length - 1])) ? 2 : 1;
89
- const namespace = segments.slice(0, -varSegmentCount).map(s => s.toLowerCase()).join('-');
90
- const varSegments = segments.slice(-varSegmentCount);
91
- const pathVar = varSegmentsToCamelCase(varSegments);
92
- return (namespace && pathVar) ? `kv://${namespace}/${pathVar}` : null;
93
- }
94
-
95
- /**
96
- * Returns the path segment used in kv://&lt;systemKey&gt;/&lt;segment&gt; for a given security key.
97
- * Uses the same derivation as env key → path (securityKeyToVar + varSegmentsToCamelCase).
98
- * @param {string} securityKey - Security key (e.g. 'apiKey', 'clientId', 'clientSecret')
99
- * @returns {string} Canonical path segment (e.g. 'apiKey', 'clientId')
100
- */
101
- function getKvPathSegmentForSecurityKey(securityKey) {
102
- if (!securityKey || typeof securityKey !== 'string') return '';
103
- return varSegmentsToCamelCase([securityKeyToVar(securityKey)]);
104
- }
105
-
106
- /**
107
- * Converts KV_* env key to kv:// path in format kv://&lt;system-key&gt;/&lt;variable&gt;.
108
- * System-key uses hyphens (e.g. microsoft-teams); variable is camelCase (e.g. clientId).
109
- * When systemKey is provided, uses it as the path namespace; otherwise infers from segments.
110
- *
111
- * @param {string} envKey - Env var name (e.g. KV_MICROSOFT_TEAMS_CLIENT_ID)
112
- * @param {string} [systemKey] - Optional system key (e.g. 'microsoft-teams'); when provided, path is kv://systemKey/variable
113
- * @returns {string|null} kv:// path or null if invalid
114
- */
115
- function kvEnvKeyToPath(envKey, systemKey) {
116
- if (!envKey || typeof envKey !== 'string' || !envKey.toUpperCase().startsWith(KV_PREFIX)) return null;
117
- const rest = envKey.slice(KV_PREFIX.length).trim();
118
- if (!rest) return null;
119
- const segments = rest.split('_').filter(Boolean);
120
- if (segments.length === 0) return null;
121
-
122
- const hasSystemKey = typeof systemKey === 'string' && systemKey.length > 0;
123
- if (hasSystemKey) return kvPathWithSystemKey(segments, systemKey);
124
- return kvPathInferred(segments);
125
- }
126
-
127
- /**
128
- * Collects KV_* entries from env map as secret items (key = kv path, value = raw).
129
- * When value is a kv:// path, uses it as the key so it matches payload paths (e.g. kv://microsoft-teams/clientId).
130
- * Otherwise derives path from env key via kvEnvKeyToPath(envKey).
131
- *
132
- * @param {Object.<string, string>} envMap - Key-value map from .env
133
- * @returns {Array<{ key: string, value: string }>} Items (key = kv://..., value = raw)
134
- */
135
- function collectKvEnvVarsAsSecretItems(envMap, systemKey) {
136
- if (!envMap || typeof envMap !== 'object') {
137
- return [];
138
- }
139
- const items = [];
140
- for (const [envKey, rawValue] of Object.entries(envMap)) {
141
- const value = typeof rawValue === 'string' ? rawValue.trim() : '';
142
- if (value === '') continue;
143
- let kvPath = null;
144
- if (value.startsWith('kv://') && isValidKvPath(value)) {
145
- kvPath = value;
146
- }
147
- if (!kvPath) kvPath = kvEnvKeyToPath(envKey, systemKey) || kvEnvKeyToPath(envKey);
148
- if (!kvPath) continue;
149
- items.push({ key: kvPath, value });
150
- }
151
- return items;
152
- }
153
-
154
- /**
155
- * Resolves a single value if it is a kv:// reference using secrets map.
156
- * Supports path-style keys (e.g. secrets/foo) and hyphen-style (secrets-foo).
157
- *
158
- * @param {Object} secrets - Loaded secrets object
159
- * @param {string} value - Value (may be "kv://..." or plain)
160
- * @returns {string|null} Resolved plain value or null if unresolved
161
- */
162
- function resolveKvValue(secrets, value) {
163
- if (typeof value !== 'string') return null;
164
- const trimmed = value.trim();
165
- if (!trimmed.startsWith('kv://')) {
166
- return trimmed;
167
- }
168
- const pathMatch = trimmed.match(/^kv:\/\/([a-zA-Z0-9_\-/]+)$/);
169
- if (!pathMatch) return null;
170
- const pathKey = pathMatch[1];
171
- const resolved = secrets[pathKey];
172
- if (resolved === undefined) return null;
173
- return typeof resolved === 'string' ? resolved : String(resolved);
174
- }
12
+ const {
13
+ collectKvEnvVarsAsSecretItems,
14
+ collectKvRefsFromPayload,
15
+ getKvPathSegmentForSecurityKey,
16
+ isNonEmptySecretValue,
17
+ isValidKvPath,
18
+ kvEnvKeyToPath,
19
+ resolveKvValue,
20
+ securityKeyToVar,
21
+ systemKeyToKvPrefix
22
+ } = require('./credential-secrets-env-kv');
175
23
 
176
24
  /**
177
- * Recursively collects all kv:// references from a JSON-serializable payload.
178
- *
179
- * @param {Object|Array|string|number|boolean|null} obj - Upload payload (application + dataSources)
180
- * @param {Set<string>} [acc] - Accumulator set (internal)
181
- * @returns {string[]} Unique kv:// refs (e.g. ["kv://secrets/foo"])
25
+ * Parses .env-style content into key-value map (first = separates key and value).
26
+ * @param {string} content - Raw .env content
27
+ * @returns {Object.<string, string>}
182
28
  */
183
- function collectKvRefsFromPayload(obj, acc = new Set()) {
184
- if (obj === null || obj === undefined) {
185
- return Array.from(acc);
186
- }
187
- if (typeof obj === 'string') {
188
- let m;
189
- KV_REF_PATTERN.lastIndex = 0;
190
- while ((m = KV_REF_PATTERN.exec(obj)) !== null) {
191
- acc.add(`kv://${m[1]}`);
192
- }
193
- return Array.from(acc);
194
- }
195
- if (Array.isArray(obj)) {
196
- for (const item of obj) {
197
- collectKvRefsFromPayload(item, acc);
198
- }
199
- return Array.from(acc);
200
- }
201
- if (typeof obj === 'object') {
202
- for (const v of Object.values(obj)) {
203
- collectKvRefsFromPayload(v, acc);
29
+ function parseEnvToMap(content) {
30
+ if (!content || typeof content !== 'string') return {};
31
+ const map = {};
32
+ const lines = content.split(/\r?\n/);
33
+ for (const line of lines) {
34
+ const trimmed = line.trim();
35
+ if (!trimmed || trimmed.startsWith('#')) continue;
36
+ const eq = trimmed.indexOf('=');
37
+ if (eq > 0) {
38
+ const key = trimmed.substring(0, eq).trim();
39
+ const value = trimmed.substring(eq + 1);
40
+ map[key] = value;
204
41
  }
205
- return Array.from(acc);
206
42
  }
207
- return Array.from(acc);
208
- }
209
-
210
- /**
211
- * Validates that key is a well-formed kv path (kv:// with alphanumeric/hyphen/slash segments).
212
- *
213
- * @param {string} key - kv:// path
214
- * @returns {boolean}
215
- */
216
- function isValidKvPath(key) {
217
- return typeof key === 'string' && /^kv:\/\/[a-z0-9][a-z0-9\-/]*$/i.test(key.trim());
43
+ return map;
218
44
  }
219
45
 
220
46
  /**
221
- * Builds secret items from .env file (KV_* vars, values resolved).
222
- * @param {string} envFilePath - Path to .env
223
- * @param {Object} secrets - Loaded secrets
224
- * @param {Map<string, string>} itemsByKey - Mutable map to add items to
47
+ * @param {string} envFilePath
48
+ * @param {Object} secrets
49
+ * @param {Map<string, string>} itemsByKey
50
+ * @param {string} [systemKey]
225
51
  */
226
52
  function buildItemsFromEnv(envFilePath, secrets, itemsByKey, systemKey) {
227
53
  if (!envFilePath || typeof envFilePath !== 'string' || !fs.existsSync(envFilePath)) return;
@@ -231,8 +57,11 @@ function buildItemsFromEnv(envFilePath, secrets, itemsByKey, systemKey) {
231
57
  const fromEnv = collectKvEnvVarsAsSecretItems(envMap, systemKey);
232
58
  for (const { key, value } of fromEnv) {
233
59
  const resolved = resolveKvValue(secrets, value);
234
- // Skip placeholder: value that equals the kv path (e.g. from env.template) must not be pushed as the secret
235
- if (resolved !== null && resolved !== undefined && isValidKvPath(key) && resolved.trim() !== key.trim()) {
60
+ if (
61
+ isNonEmptySecretValue(resolved) &&
62
+ isValidKvPath(key) &&
63
+ String(resolved).trim() !== key.trim()
64
+ ) {
236
65
  itemsByKey.set(key, resolved);
237
66
  }
238
67
  }
@@ -242,10 +71,9 @@ function buildItemsFromEnv(envFilePath, secrets, itemsByKey, systemKey) {
242
71
  }
243
72
 
244
73
  /**
245
- * Builds secret items from payload kv:// refs not already in itemsByKey.
246
- * @param {Object} payload - Upload payload
247
- * @param {Object} secrets - Loaded secrets
248
- * @param {Map<string, string>} itemsByKey - Mutable map to add items to
74
+ * @param {Object} payload
75
+ * @param {Object} secrets
76
+ * @param {Map<string, string>} itemsByKey
249
77
  */
250
78
  function buildItemsFromPayload(payload, secrets, itemsByKey) {
251
79
  if (!payload || typeof payload !== 'object') return;
@@ -255,16 +83,15 @@ function buildItemsFromPayload(payload, secrets, itemsByKey) {
255
83
  if (existingKeys.has(ref)) continue;
256
84
  const pathKey = ref.replace(/^kv:\/\//, '');
257
85
  const resolved = secrets[pathKey];
258
- if (resolved !== null && resolved !== undefined && isValidKvPath(ref)) {
86
+ if (isNonEmptySecretValue(resolved) && isValidKvPath(ref)) {
259
87
  itemsByKey.set(ref, typeof resolved === 'string' ? resolved : String(resolved));
260
88
  }
261
89
  }
262
90
  }
263
91
 
264
92
  /**
265
- * Derives stored count from API response.
266
- * @param {Object} res - Response from storeCredentialSecrets
267
- * @param {number} fallback - Fallback when stored not in response
93
+ * @param {Object} res
94
+ * @param {number} fallback
268
95
  * @returns {number}
269
96
  */
270
97
  function storedCountFromResponse(res, fallback) {
@@ -274,17 +101,6 @@ function storedCountFromResponse(res, fallback) {
274
101
  }
275
102
 
276
103
  /**
277
- * Sends items to dataplane credential API; returns pushed count or warning.
278
- * @param {string} dataplaneUrl - Dataplane URL
279
- * @param {Object} authConfig - Auth config
280
- * @param {Array<{ key: string, value: string }>} items - Items to send
281
- * @returns {Promise<{ pushed: number, warning?: string }>}
282
- */
283
- /**
284
- * For bearerToken auth, credential template resolves security.token only.
285
- * Legacy .env keys KV_*_APIKEY with a plain token wrongly map to kv://.../apiKey.
286
- * Prefer token path; drop duplicate apiKey when both would be pushed.
287
- *
288
104
  * @param {Map<string, string>} itemsByKey
289
105
  * @param {string} [systemKey]
290
106
  * @param {Object} [authConfig]
@@ -309,8 +125,6 @@ function reconcileBearerTokenSecretPaths(itemsByKey, systemKey, authConfig) {
309
125
  }
310
126
 
311
127
  /**
312
- * Optional dev/lab dataplane API key (Bearer bypass) for credential secret push only.
313
- * Does not alter pipeline publish auth when AIFABRIX_DEPLOYMENT_AUTH=client-credentials.
314
128
  * @returns {string|null}
315
129
  */
316
130
  function resolveLabDataplaneApiKey() {
@@ -408,6 +222,12 @@ async function handleFailedStoreResponse(dataplaneUrl, authConfig, items, res) {
408
222
  return storeErrorWarning(res);
409
223
  }
410
224
 
225
+ /**
226
+ * @param {string} dataplaneUrl
227
+ * @param {Object} authConfig
228
+ * @param {Array<{ key: string, value: string }>} items
229
+ * @returns {Promise<{ pushed: number, warning?: string }>}
230
+ */
411
231
  async function sendCredentialSecrets(dataplaneUrl, authConfig, items) {
412
232
  try {
413
233
  const res = await storeCredentialSecrets(dataplaneUrl, authConfig, items);
@@ -424,17 +244,10 @@ async function sendCredentialSecrets(dataplaneUrl, authConfig, items) {
424
244
  }
425
245
 
426
246
  /**
427
- * Pushes credential secrets to dataplane: from .env (KV_*) and from payload kv:// refs.
428
- * Resolves all kv:// in values via loadSecrets; sends only plain values. Best-effort:
429
- * on 403/401 logs warning and returns; never logs secret values.
430
- *
431
- * @param {string} dataplaneUrl - Dataplane base URL
432
- * @param {Object} authConfig - Auth config (Bearer)
433
- * @param {Object} options - Options
434
- * @param {string} [options.envFilePath] - Path to .env (integration/<systemKey>/.env)
435
- * @param {string} [options.appName] - App/system name for loadSecrets context
436
- * @param {Object} [options.payload] - Upload payload { application, dataSources } for kv scan
437
- * @returns {Promise<{ pushed: number, keys?: string[], skipped?: boolean, warning?: string }>} Count pushed, keys (on success), skipped (when nothing to push), optional warning
247
+ * @param {string} dataplaneUrl
248
+ * @param {Object} authConfig
249
+ * @param {Object} options
250
+ * @returns {Promise<{ pushed: number, keys?: string[], skipped?: boolean, warning?: string }>}
438
251
  */
439
252
  async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
440
253
  const { envFilePath, appName, payload } = options;
@@ -461,29 +274,6 @@ async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
461
274
  return sendResult;
462
275
  }
463
276
 
464
- /**
465
- * Parses .env-style content into key-value map (first = separates key and value).
466
- *
467
- * @param {string} content - Raw .env content
468
- * @returns {Object.<string, string>}
469
- */
470
- function parseEnvToMap(content) {
471
- if (!content || typeof content !== 'string') return {};
472
- const map = {};
473
- const lines = content.split(/\r?\n/);
474
- for (const line of lines) {
475
- const trimmed = line.trim();
476
- if (!trimmed || trimmed.startsWith('#')) continue;
477
- const eq = trimmed.indexOf('=');
478
- if (eq > 0) {
479
- const key = trimmed.substring(0, eq).trim();
480
- const value = trimmed.substring(eq + 1);
481
- map[key] = value;
482
- }
483
- }
484
- return map;
485
- }
486
-
487
277
  module.exports = {
488
278
  collectKvEnvVarsAsSecretItems,
489
279
  collectKvRefsFromPayload,
@@ -1,123 +1,15 @@
1
1
  /**
2
2
  * Dataplane URL Resolver
3
3
  *
4
- * Resolves dataplane URL by discovering from controller
4
+ * Resolves dataplane URL by discovering from controller (application.url in Miso).
5
+ * The controller registration is the source of truth — the CLI does not rewrite ports.
5
6
  *
6
7
  * @fileoverview Dataplane URL resolution utilities
7
8
  * @author AI Fabrix Team
8
9
  * @version 2.0.0
9
10
  */
10
11
 
11
- const fs = require('fs');
12
- const path = require('path');
13
-
14
- const config = require('../core/config');
15
12
  const { discoverDataplaneUrl } = require('../commands/wizard-dataplane');
16
- const pathsUtil = require('./paths');
17
- const { loadConfigFile } = require('./config-format');
18
-
19
- /** Matches `templates/applications/dataplane/application.yaml` and `calculateComposePort`. */
20
- const DEFAULT_DATAPLANE_MANIFEST_PORT = 3001;
21
-
22
- /**
23
- * @param {string} url
24
- * @returns {number|null}
25
- */
26
- function localhostPortFromUrl(url) {
27
- if (!url || typeof url !== 'string') return null;
28
- const normalized = url.replace(/127\.0\.0\.1/g, 'localhost');
29
- const match = normalized.match(/localhost:(\d+)/);
30
- if (!match) return null;
31
- const port = parseInt(match[1], 10);
32
- return Number.isFinite(port) ? port : null;
33
- }
34
-
35
- /**
36
- * Read dataplane `port` from materialized builder config or bundled template.
37
- *
38
- * @returns {number}
39
- */
40
- function loadDataplaneManifestListenPort() {
41
- const tryLoad = (configPath) => {
42
- if (!fs.existsSync(configPath)) {
43
- return null;
44
- }
45
- const variables = loadConfigFile(configPath);
46
- const port = variables && variables.port !== undefined ? Number(variables.port) : NaN;
47
- return Number.isFinite(port) && port > 0 ? port : null;
48
- };
49
-
50
- try {
51
- const builderPath = pathsUtil.getBuilderPath('dataplane');
52
- if (fs.existsSync(builderPath)) {
53
- try {
54
- const fromBuilder = tryLoad(pathsUtil.resolveApplicationConfigPath(builderPath));
55
- if (fromBuilder) {
56
- return fromBuilder;
57
- }
58
- } catch {
59
- // Fall through to bundled template when builder dir exists but config is missing.
60
- }
61
- }
62
- } catch {
63
- // ignore
64
- }
65
-
66
- const templatePath = path.join(
67
- pathsUtil.getProjectRoot(),
68
- 'templates',
69
- 'applications',
70
- 'dataplane',
71
- 'application.yaml'
72
- );
73
- const fromTemplate = tryLoad(templatePath);
74
- return fromTemplate || DEFAULT_DATAPLANE_MANIFEST_PORT;
75
- }
76
-
77
- /**
78
- * Local Docker host port: `application.yaml` `port` (default 3001) + developerId×100.
79
- * Same rule as {@link calculateComposePort} / `aifabrix up-platform` — not
80
- * `APP_SERVICE_ENV_DEFAULTS_LOCAL.DATAPLANE_PUBLIC_PORT` (3011), which is for other local profiles.
81
- *
82
- * @returns {Promise<string>}
83
- */
84
- async function expectedLocalDataplaneBaseUrl() {
85
- const developerIdRaw = await config.getDeveloperId();
86
- const idNum =
87
- typeof developerIdRaw === 'string'
88
- ? parseInt(developerIdRaw, 10)
89
- : Number(developerIdRaw);
90
- const devId = Number.isFinite(idNum) && idNum >= 0 ? idNum : 0;
91
- const basePort = loadDataplaneManifestListenPort();
92
- const hostPort = devId === 0 ? basePort : basePort + devId * 100;
93
- return `http://localhost:${hostPort}`;
94
- }
95
-
96
- /**
97
- * Controller application.url can lag after dev-id changes; prefer builder host port formula for localhost.
98
- * @param {string} discoveredUrl
99
- * @returns {Promise<string>}
100
- */
101
- async function alignDiscoveredDataplaneUrl(discoveredUrl) {
102
- if (!discoveredUrl || !/localhost|127\.0\.0\.1/i.test(discoveredUrl)) {
103
- return discoveredUrl;
104
- }
105
- try {
106
- const expectedUrl = await expectedLocalDataplaneBaseUrl();
107
- const discoveredPort = localhostPortFromUrl(discoveredUrl);
108
- const expectedPort = localhostPortFromUrl(expectedUrl);
109
- if (
110
- discoveredPort !== null &&
111
- expectedPort !== null &&
112
- discoveredPort !== expectedPort
113
- ) {
114
- return expectedUrl;
115
- }
116
- } catch {
117
- // Keep controller-discovered URL when developer config is unavailable.
118
- }
119
- return discoveredUrl;
120
- }
121
13
 
122
14
  /**
123
15
  * @returns {string|null}
@@ -146,12 +38,9 @@ async function resolveDataplaneUrl(controllerUrl, environment, authConfig, opts
146
38
  if (envUrl) {
147
39
  return envUrl;
148
40
  }
149
- const discovered = await discoverDataplaneUrl(controllerUrl, environment, authConfig, opts);
150
- return alignDiscoveredDataplaneUrl(discovered);
41
+ return discoverDataplaneUrl(controllerUrl, environment, authConfig, opts);
151
42
  }
152
43
 
153
44
  module.exports = {
154
- resolveDataplaneUrl,
155
- expectedLocalDataplaneBaseUrl,
156
- alignDiscoveredDataplaneUrl
45
+ resolveDataplaneUrl
157
46
  };
@@ -40,8 +40,31 @@ function localHostPort(appPort, developerIdNum) {
40
40
  return publishedHostPort(appPort, developerIdNum);
41
41
  }
42
42
 
43
+ /**
44
+ * Host port for local dataplane Docker (`aifabrix up-dataplane` / `run --env dev|tst`).
45
+ * Matches {@link computeDataplanePortOverride} in up-dataplane.js (+10 dev, +20 tst).
46
+ *
47
+ * @param {number} appPort - Manifest listen port (dataplane default 3001)
48
+ * @param {number} developerIdNum
49
+ * @param {string} [environmentKey] - dev | tst | other
50
+ * @returns {number}
51
+ */
52
+ function dataplaneDockerHostPort(appPort, developerIdNum, environmentKey = 'dev') {
53
+ const id = Number.isFinite(developerIdNum) && developerIdNum >= 0 ? developerIdNum : 0;
54
+ const baseHostPort = appPort + id * 100;
55
+ const env = String(environmentKey || '').trim().toLowerCase();
56
+ if (env === 'dev') {
57
+ return baseHostPort + 10;
58
+ }
59
+ if (env === 'tst') {
60
+ return baseHostPort + 20;
61
+ }
62
+ return id === 0 ? appPort : baseHostPort;
63
+ }
64
+
43
65
  module.exports = {
44
66
  parseDeveloperIdNum,
45
67
  publishedHostPort,
46
- localHostPort
68
+ localHostPort,
69
+ dataplaneDockerHostPort
47
70
  };