@aifabrix/builder 2.45.5 → 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 (355) hide show
  1. package/.cursor/rules/cli-layout.mdc +2 -0
  2. package/.cursor/rules/cli-product-neutral.mdc +43 -0
  3. package/.cursor/rules/project-rules.mdc +1 -0
  4. package/.npmrc.token +1 -0
  5. package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
  6. package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
  7. package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
  8. package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
  9. package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
  10. package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
  11. package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
  12. package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
  13. package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
  14. package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
  15. package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
  16. package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
  17. package/.temp/validation/summary.txt +5 -0
  18. package/README.md +43 -17
  19. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  20. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  21. package/jest.config.default.js +2 -1
  22. package/jest.config.isolated.js +2 -1
  23. package/jest.global-live-fabrix-hooks.js +36 -0
  24. package/jest.isolated-projects.js +146 -0
  25. package/jest.projects.js +65 -136
  26. package/lib/api/api-response-errors.js +124 -0
  27. package/lib/api/dataplane-health.api.js +19 -7
  28. package/lib/api/governance-scenario-pack.api.js +159 -0
  29. package/lib/api/index.js +43 -3
  30. package/lib/api/lifecycle.api.js +98 -0
  31. package/lib/api/pipeline.api.js +3 -1
  32. package/lib/api/records-bulk.api.js +14 -1
  33. package/lib/api/resource-types.api.js +107 -0
  34. package/lib/api/runtime-configuration.api.js +72 -0
  35. package/lib/api/types/resource-types.types.js +45 -0
  36. package/lib/api/user-groups.api.js +9 -9
  37. package/lib/api/users.api.js +8 -8
  38. package/lib/api/validation-runner.js +3 -1
  39. package/lib/api/wizard.api.js +10 -2
  40. package/lib/app/deploy-config.js +4 -1
  41. package/lib/app/down.js +78 -27
  42. package/lib/app/index.js +2 -2
  43. package/lib/app/push.js +4 -5
  44. package/lib/app/register.js +7 -4
  45. package/lib/app/run-env-compose.js +84 -30
  46. package/lib/app/run-resolve-image.js +2 -1
  47. package/lib/app/run-stop-containers.js +113 -0
  48. package/lib/app/run.js +1 -31
  49. package/lib/app/show-display.js +35 -0
  50. package/lib/app/show.js +32 -1
  51. package/lib/build/index.js +22 -5
  52. package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
  53. package/lib/certification/sync-system-certification.js +13 -6
  54. package/lib/channels/add-channel.js +348 -0
  55. package/lib/channels/channel-artifact-generator.js +141 -0
  56. package/lib/channels/channel-audit.js +101 -0
  57. package/lib/channels/channel-auth-context.js +37 -0
  58. package/lib/channels/channel-credential.js +200 -0
  59. package/lib/channels/channel-display-name.js +64 -0
  60. package/lib/channels/channel-external-system.js +106 -0
  61. package/lib/channels/channel-integration-generator.js +274 -0
  62. package/lib/channels/channel-probe.js +89 -0
  63. package/lib/channels/channel-runtime-resolver.js +152 -0
  64. package/lib/channels/channel-status-display.js +141 -0
  65. package/lib/channels/channel-targets.js +115 -0
  66. package/lib/cli/index.js +20 -4
  67. package/lib/cli/infra-guided-auth.js +109 -0
  68. package/lib/cli/infra-guided.js +84 -81
  69. package/lib/cli/setup-app.help.js +0 -31
  70. package/lib/cli/setup-app.js +82 -5
  71. package/lib/cli/setup-app.test-commands.js +5 -57
  72. package/lib/cli/setup-auth.js +25 -0
  73. package/lib/cli/setup-certification-commands.js +147 -0
  74. package/lib/cli/setup-certification.help.js +141 -0
  75. package/lib/cli/setup-environment.js +19 -2
  76. package/lib/cli/setup-external-system-help.js +62 -0
  77. package/lib/cli/setup-external-system.js +25 -3
  78. package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
  79. package/lib/cli/setup-infra-up-platform-action.js +2 -1
  80. package/lib/cli/setup-infra.js +9 -1
  81. package/lib/cli/setup-platform.js +6 -1
  82. package/lib/cli/setup-secrets.js +33 -1
  83. package/lib/cli/setup-utility-help-after.js +25 -2
  84. package/lib/cli/setup-utility-repair.js +198 -0
  85. package/lib/cli/setup-utility-resolve.js +44 -5
  86. package/lib/cli/setup-utility-validate.js +197 -0
  87. package/lib/cli/setup-utility.js +4 -239
  88. package/lib/commands/app-logs.js +54 -26
  89. package/lib/commands/app.js +41 -0
  90. package/lib/commands/auth-status-client-tokens.js +214 -0
  91. package/lib/commands/auth-status-dataplane-version.js +142 -68
  92. package/lib/commands/auth-status.js +43 -54
  93. package/lib/commands/channel.js +58 -0
  94. package/lib/commands/datasource-test-trust-cli.js +27 -23
  95. package/lib/commands/datasource-unified-test-cli.js +6 -3
  96. package/lib/commands/datasource-unified-test-cli.options.js +5 -1
  97. package/lib/commands/datasource.js +8 -8
  98. package/lib/commands/dev-show-display.js +8 -1
  99. package/lib/commands/governance-cli-shared.js +94 -0
  100. package/lib/commands/governance-command-action.js +132 -0
  101. package/lib/commands/governance-download-external.js +34 -0
  102. package/lib/commands/governance-generate-external.js +71 -0
  103. package/lib/commands/governance-upload-external.js +36 -0
  104. package/lib/commands/governance-verify-external.js +64 -0
  105. package/lib/commands/governance.js +144 -0
  106. package/lib/commands/identity-apply-sync.js +49 -8
  107. package/lib/commands/identity-group-cli.js +66 -26
  108. package/lib/commands/identity-membership-role-cache.js +58 -37
  109. package/lib/commands/identity-shared.js +22 -1
  110. package/lib/commands/identity-user-cli.js +80 -34
  111. package/lib/commands/lifecycle-command-action.js +123 -0
  112. package/lib/commands/lifecycle-external.js +268 -0
  113. package/lib/commands/login.js +11 -2
  114. package/lib/commands/repair-auth-apply.js +153 -0
  115. package/lib/commands/repair-auth-config.js +8 -13
  116. package/lib/commands/repair-changed-files.js +41 -0
  117. package/lib/commands/repair-datasource-com-v2.js +15 -0
  118. package/lib/commands/repair-datasource-keys.js +4 -1
  119. package/lib/commands/repair-datasource-run.js +59 -0
  120. package/lib/commands/repair-datasource-service-contract.js +206 -0
  121. package/lib/commands/repair-datasource-test-payload.js +259 -0
  122. package/lib/commands/repair-datasource.js +49 -64
  123. package/lib/commands/repair-env-template.js +76 -25
  124. package/lib/commands/repair-external-run.js +216 -0
  125. package/lib/commands/repair-openapi-sync.js +49 -4
  126. package/lib/commands/repair-persist.js +4 -0
  127. package/lib/commands/repair-rbac.js +52 -1
  128. package/lib/commands/repair-steps.js +160 -0
  129. package/lib/commands/repair-system-alignment.js +7 -4
  130. package/lib/commands/repair.js +29 -296
  131. package/lib/commands/resource-type.js +371 -0
  132. package/lib/commands/secrets-get.js +184 -0
  133. package/lib/commands/secrets-remove-all.js +3 -1
  134. package/lib/commands/setup-image-refresh.js +146 -0
  135. package/lib/commands/setup-modes.js +158 -162
  136. package/lib/commands/setup-platform-auth.js +76 -0
  137. package/lib/commands/setup-prompts-admin.js +244 -0
  138. package/lib/commands/setup-prompts-platform-mode.js +70 -0
  139. package/lib/commands/setup-prompts-validators.js +36 -0
  140. package/lib/commands/setup-prompts.js +24 -76
  141. package/lib/commands/setup-run-context.js +45 -0
  142. package/lib/commands/setup-spinners.js +39 -0
  143. package/lib/commands/setup.js +27 -5
  144. package/lib/commands/teardown.js +39 -0
  145. package/lib/commands/test-e2e-external.js +24 -8
  146. package/lib/commands/test-governance-external.js +3 -24
  147. package/lib/commands/test-trust-external.js +48 -100
  148. package/lib/commands/up-common.js +57 -13
  149. package/lib/commands/up-dataplane.js +56 -15
  150. package/lib/commands/upload-publish-context.js +75 -0
  151. package/lib/commands/upload-sidecar-followups.js +89 -0
  152. package/lib/commands/upload.js +113 -110
  153. package/lib/commands/verify-governance-combined-external.js +61 -0
  154. package/lib/commands/verify-governance-command-action.js +64 -0
  155. package/lib/commands/verify-governance-db.js +102 -0
  156. package/lib/commands/verify-governance-external.js +77 -0
  157. package/lib/commands/verify-governance-result-builders.js +71 -0
  158. package/lib/commands/verify-operations-command-action.js +68 -0
  159. package/lib/commands/verify-operations-external.js +268 -0
  160. package/lib/commands/verify-operations-persist.js +75 -0
  161. package/lib/commands/verify-operations-step-plan.js +68 -0
  162. package/lib/commands/verify-operations-steps.js +111 -0
  163. package/lib/commands/verify-trust-command-action.js +73 -0
  164. package/lib/commands/verify-trust-external.js +59 -0
  165. package/lib/commands/wizard-config-normalizer.js +43 -23
  166. package/lib/commands/wizard-core.js +2 -0
  167. package/lib/core/admin-password-generator.js +38 -0
  168. package/lib/core/admin-secrets-bundle.js +111 -0
  169. package/lib/core/admin-secrets-env-overlay.js +62 -0
  170. package/lib/core/admin-secrets.js +32 -4
  171. package/lib/core/config-attach-extensions.js +3 -0
  172. package/lib/core/config-platform-controller.js +44 -0
  173. package/lib/core/config-setup-installation-profile.js +42 -0
  174. package/lib/core/config-setup-platform-mode.js +46 -0
  175. package/lib/core/config.js +46 -36
  176. package/lib/core/secrets-admin-env.js +6 -2
  177. package/lib/core/secrets-ensure.js +4 -0
  178. package/lib/core/secrets-env-content.js +54 -48
  179. package/lib/core/secrets-env-write.js +10 -1
  180. package/lib/core/secrets-infra-placeholder-sync.js +2 -0
  181. package/lib/core/setup-installation-profile.js +43 -0
  182. package/lib/core/setup-platform-mode.js +90 -0
  183. package/lib/datasource/agent-trust-debug-log.js +1 -1
  184. package/lib/datasource/agent-trust-run.js +5 -3
  185. package/lib/datasource/audit-evidence-extract-debug.js +30 -18
  186. package/lib/datasource/audit-evidence-extract.js +7 -4
  187. package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
  188. package/lib/datasource/bulk-loader-run-batches.js +15 -3
  189. package/lib/datasource/capability/capability-diff-slice.js +2 -2
  190. package/lib/datasource/capability/capability-manifest.js +254 -0
  191. package/lib/datasource/capability/capability-resolve.js +3 -4
  192. package/lib/datasource/capability/copy-operations.js +5 -3
  193. package/lib/datasource/capability/remove-operations.js +2 -1
  194. package/lib/datasource/capability/run-capability-copy.js +32 -4
  195. package/lib/datasource/capability/run-capability-diff.js +3 -3
  196. package/lib/datasource/capability/run-capability-dimension.js +2 -4
  197. package/lib/datasource/capability/run-capability-edit.js +4 -7
  198. package/lib/datasource/capability/run-capability-relate.js +2 -4
  199. package/lib/datasource/capability/run-capability-remove.js +2 -4
  200. package/lib/datasource/capability/validate-capability-slice.js +2 -1
  201. package/lib/datasource/datasource-identifier.js +2 -1
  202. package/lib/datasource/datasource-load-export-context.js +3 -0
  203. package/lib/datasource/datasource-validate-display.js +1 -1
  204. package/lib/datasource/field-reference-validator.js +24 -3
  205. package/lib/datasource/log-cleaner.js +2 -2
  206. package/lib/datasource/log-viewer.js +8 -8
  207. package/lib/datasource/orphan-profiles-validator.js +98 -0
  208. package/lib/datasource/test-e2e.js +4 -1
  209. package/lib/datasource/unified-validation-run-body.js +3 -1
  210. package/lib/datasource/unified-validation-run.js +5 -2
  211. package/lib/datasource/validate.js +41 -23
  212. package/lib/deployment/environment-poll-status.js +138 -0
  213. package/lib/deployment/environment.js +42 -34
  214. package/lib/external-system/download-helpers.js +87 -0
  215. package/lib/external-system/download.js +78 -138
  216. package/lib/external-system/generator.js +1 -55
  217. package/lib/external-system/integration-auth-context.js +45 -0
  218. package/lib/external-system/test-auth.js +12 -5
  219. package/lib/external-system/test.js +16 -21
  220. package/lib/generator/builders.js +11 -1
  221. package/lib/generator/external-controller-manifest.js +18 -0
  222. package/lib/generator/split.js +3 -1
  223. package/lib/generator/wizard.js +1 -28
  224. package/lib/governance/governance-cli-errors.js +96 -0
  225. package/lib/governance/governance-download-display.js +38 -0
  226. package/lib/governance/governance-run-display.js +215 -25
  227. package/lib/identity/fk-reference.js +65 -0
  228. package/lib/identity/identity-apply-core.js +13 -58
  229. package/lib/identity/identity-apply-phases.js +5 -1
  230. package/lib/identity/identity-display.js +243 -0
  231. package/lib/identity/identity-response-helpers.js +52 -0
  232. package/lib/identity/identity-sync-errors.js +62 -0
  233. package/lib/identity/resolve-user-id.js +129 -0
  234. package/lib/identity/username-default.js +31 -0
  235. package/lib/infrastructure/admin-secrets-apply.js +84 -0
  236. package/lib/infrastructure/admin-secrets-ensure.js +130 -0
  237. package/lib/infrastructure/admin-secrets-resolve.js +95 -0
  238. package/lib/infrastructure/admin-secrets-sync.js +91 -0
  239. package/lib/infrastructure/helpers.js +23 -129
  240. package/lib/infrastructure/index.js +5 -6
  241. package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
  242. package/lib/lifecycle/certification-progress.js +231 -0
  243. package/lib/lifecycle/dataplane-ui-url.js +145 -0
  244. package/lib/lifecycle/exit-codes.js +24 -0
  245. package/lib/lifecycle/json-envelope.js +25 -0
  246. package/lib/lifecycle/lifecycle-governance-options.js +51 -0
  247. package/lib/lifecycle/logger-quiet.js +29 -0
  248. package/lib/lifecycle/open-api-published.js +59 -0
  249. package/lib/lifecycle/operations-readiness-metrics.js +101 -0
  250. package/lib/lifecycle/product-model.js +196 -0
  251. package/lib/lifecycle/report-display-trust.js +70 -0
  252. package/lib/lifecycle/report-display.js +472 -0
  253. package/lib/lifecycle/scenario-upload.js +191 -0
  254. package/lib/lifecycle/verify-step-progress.js +87 -0
  255. package/lib/parameters/infra-kv-discovery.js +27 -2
  256. package/lib/protection/auth-context.js +10 -4
  257. package/lib/resolvers/datasource-resolver.js +2 -3
  258. package/lib/resolvers/resource-type-file.js +159 -0
  259. package/lib/schema/application-schema.json +17 -1
  260. package/lib/schema/datasource-test-run.schema.json +89 -6
  261. package/lib/schema/deployment-rules.yaml +2 -1
  262. package/lib/schema/external-datasource.schema.json +2947 -2619
  263. package/lib/schema/external-system.schema.json +1418 -761
  264. package/lib/schema/infra.parameter.yaml +45 -0
  265. package/lib/schema/type/credential-field-registry.json +103 -97
  266. package/lib/schema/type/document-storage.json +9 -5
  267. package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
  268. package/lib/utils/api.js +55 -31
  269. package/lib/utils/app-relative-path-env.js +112 -0
  270. package/lib/utils/app-run-containers.js +25 -2
  271. package/lib/utils/authentication-shape.js +224 -0
  272. package/lib/utils/build-copy.js +36 -5
  273. package/lib/utils/build-resolve-image.js +66 -12
  274. package/lib/utils/celery-companion-apps.js +53 -0
  275. package/lib/utils/cli-secrets-error-format.js +83 -21
  276. package/lib/utils/cli-utils.js +20 -4
  277. package/lib/utils/compose-celery-worker.js +23 -0
  278. package/lib/utils/compose-generate-docker-compose.js +13 -1
  279. package/lib/utils/compose-traefik-ingress-base.js +16 -1
  280. package/lib/utils/config-secrets-path.js +46 -0
  281. package/lib/utils/configuration-env-resolver.js +36 -0
  282. package/lib/utils/controller-url.js +88 -1
  283. package/lib/utils/credential-secrets-env-kv.js +216 -0
  284. package/lib/utils/credential-secrets-env.js +175 -257
  285. package/lib/utils/dataplane-resolver.js +18 -2
  286. package/lib/utils/declarative-url-ports.js +27 -4
  287. package/lib/utils/deployment-auth-mode.js +41 -0
  288. package/lib/utils/developer-volume-id.js +33 -0
  289. package/lib/utils/docker-build.js +1 -4
  290. package/lib/utils/env-copy.js +14 -9
  291. package/lib/utils/env-kv-orphan-lines.js +43 -0
  292. package/lib/utils/error-formatters/network-errors.js +13 -4
  293. package/lib/utils/external-env-template.js +11 -3
  294. package/lib/utils/external-system-readiness-core.js +10 -0
  295. package/lib/utils/external-system-readiness-display.js +80 -23
  296. package/lib/utils/external-system-validators.js +8 -2
  297. package/lib/utils/health-check.js +4 -1
  298. package/lib/utils/help-builder.js +13 -8
  299. package/lib/utils/keycloak-realm-ready.js +101 -0
  300. package/lib/utils/load-cip-capacity-display-config.js +61 -15
  301. package/lib/utils/manifest-source-emit.js +2 -0
  302. package/lib/utils/paths.js +25 -0
  303. package/lib/utils/pipeline-upload-auth-retry.js +82 -0
  304. package/lib/utils/platform-controller-url.js +157 -0
  305. package/lib/utils/platform-kv-ref.js +57 -0
  306. package/lib/utils/postgres-platform-bootstrap.js +199 -0
  307. package/lib/utils/postgres-wipe.js +9 -2
  308. package/lib/utils/resolve-env-mode.js +47 -0
  309. package/lib/utils/runtime-configuration-download.js +189 -0
  310. package/lib/utils/runtime-configuration-values.js +119 -0
  311. package/lib/utils/schema-loader.js +19 -4
  312. package/lib/utils/secrets-generator.js +31 -25
  313. package/lib/utils/secrets-missing-error.js +164 -0
  314. package/lib/utils/service-url-label.js +81 -0
  315. package/lib/utils/setup-quiet-output.js +25 -0
  316. package/lib/utils/template-helpers.js +15 -0
  317. package/lib/utils/token-manager-client-credentials.js +201 -0
  318. package/lib/utils/token-manager-messages.js +8 -0
  319. package/lib/utils/token-manager-refresh.js +12 -2
  320. package/lib/utils/token-manager.js +86 -105
  321. package/lib/utils/upload-pipeline-ui.js +12 -5
  322. package/lib/utils/upload-sync-options.js +42 -0
  323. package/lib/utils/url-declarative-public-base.js +21 -3
  324. package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
  325. package/lib/utils/validation-poll-ui.js +8 -0
  326. package/lib/utils/validation-run-stage.js +45 -0
  327. package/lib/utils/worker-sidecars.js +142 -0
  328. package/lib/validation/external-manifest-validator.js +2 -0
  329. package/lib/validation/runtime-configuration-rules.js +98 -0
  330. package/lib/validation/validate-display-dimensions.js +154 -0
  331. package/lib/validation/validate-display-sections.js +276 -0
  332. package/lib/validation/validate-display-steps.js +67 -0
  333. package/lib/validation/validate-display.js +24 -402
  334. package/package.json +1 -1
  335. package/templates/applications/dataplane/application.yaml +7 -1
  336. package/templates/applications/dataplane/env.template +50 -14
  337. package/templates/applications/dataplane/rbac.yaml +108 -1
  338. package/templates/applications/miso-controller/application.yaml +2 -2
  339. package/templates/applications/miso-controller/env.template +53 -122
  340. package/templates/applications/miso-controller/rbac.yaml +28 -13
  341. package/templates/channels/chatgpt/install.template.md.hbs +49 -0
  342. package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
  343. package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
  344. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
  345. package/templates/channels/slack/install.template.md.hbs +49 -0
  346. package/templates/channels/slack/manifest.template.json.hbs +66 -0
  347. package/templates/channels/teams-bot/install.template.md.hbs +49 -0
  348. package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
  349. package/templates/channels/web-embed/install.template.md.hbs +49 -0
  350. package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
  351. package/templates/external-system/README.md.hbs +74 -17
  352. package/templates/python/docker-compose.hbs +75 -18
  353. package/templates/typescript/docker-compose.hbs +70 -17
  354. package/lib/commands/test-governance-command-action.js +0 -55
  355. package/lib/commands/test-trust-command-action.js +0 -118
@@ -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,305 +9,245 @@
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;
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');
17
23
 
18
24
  /**
19
- * Converts systemKey to KV_* prefix (e.g. hubspot -> HUBSPOT, my-hubspot -> MY_HUBSPOT).
20
- * @param {string} systemKey - System key
21
- * @returns {string}
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>}
22
28
  */
23
- function systemKeyToKvPrefix(systemKey) {
24
- if (!systemKey || typeof systemKey !== 'string') return '';
25
- return systemKey.replace(/-/g, '_').toUpperCase();
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;
41
+ }
42
+ }
43
+ return map;
26
44
  }
27
45
 
28
46
  /**
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}
47
+ * @param {string} envFilePath
48
+ * @param {Object} secrets
49
+ * @param {Map<string, string>} itemsByKey
50
+ * @param {string} [systemKey]
33
51
  */
34
- function securityKeyToVar(securityKey) {
35
- if (!securityKey || typeof securityKey !== 'string') return '';
36
- return securityKey.replace(/_/g, '').toUpperCase();
52
+ function buildItemsFromEnv(envFilePath, secrets, itemsByKey, systemKey) {
53
+ if (!envFilePath || typeof envFilePath !== 'string' || !fs.existsSync(envFilePath)) return;
54
+ try {
55
+ const content = fs.readFileSync(envFilePath, 'utf8');
56
+ const envMap = parseEnvToMap(content);
57
+ const fromEnv = collectKvEnvVarsAsSecretItems(envMap, systemKey);
58
+ for (const { key, value } of fromEnv) {
59
+ const resolved = resolveKvValue(secrets, value);
60
+ if (
61
+ isNonEmptySecretValue(resolved) &&
62
+ isValidKvPath(key) &&
63
+ String(resolved).trim() !== key.trim()
64
+ ) {
65
+ itemsByKey.set(key, resolved);
66
+ }
67
+ }
68
+ } catch {
69
+ // Best-effort: continue without .env items
70
+ }
37
71
  }
38
72
 
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
73
  /**
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}
74
+ * @param {Object} payload
75
+ * @param {Object} secrets
76
+ * @param {Map<string, string>} itemsByKey
46
77
  */
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;
78
+ function buildItemsFromPayload(payload, secrets, itemsByKey) {
79
+ if (!payload || typeof payload !== 'object') return;
80
+ const refs = collectKvRefsFromPayload(payload);
81
+ const existingKeys = new Set(itemsByKey.keys());
82
+ for (const ref of refs) {
83
+ if (existingKeys.has(ref)) continue;
84
+ const pathKey = ref.replace(/^kv:\/\//, '');
85
+ const resolved = secrets[pathKey];
86
+ if (isNonEmptySecretValue(resolved) && isValidKvPath(ref)) {
87
+ itemsByKey.set(ref, typeof resolved === 'string' ? resolved : String(resolved));
88
+ }
57
89
  }
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
90
  }
63
91
 
64
92
  /**
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}
93
+ * @param {Object} res
94
+ * @param {number} fallback
95
+ * @returns {number}
69
96
  */
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;
97
+ function storedCountFromResponse(res, fallback) {
98
+ if (res && typeof res.stored === 'number') return res.stored;
99
+ if (res && res.data && res.data.stored !== undefined && res.data.stored !== null) return res.data.stored;
100
+ return fallback;
79
101
  }
80
102
 
81
103
  /**
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}
104
+ * @param {Map<string, string>} itemsByKey
105
+ * @param {string} [systemKey]
106
+ * @param {Object} [authConfig]
85
107
  */
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;
108
+ function reconcileBearerTokenSecretPaths(itemsByKey, systemKey, authConfig) {
109
+ const method = (
110
+ authConfig?.method ||
111
+ authConfig?.type ||
112
+ ''
113
+ )
114
+ .toString()
115
+ .toLowerCase()
116
+ .replace(/_/g, '');
117
+ if (method !== 'bearertoken' || !systemKey) return;
118
+ const tokenPath = `kv://${systemKey}/token`;
119
+ const apiKeyPath = `kv://${systemKey}/apiKey`;
120
+ if (!itemsByKey.has(apiKeyPath)) return;
121
+ if (!itemsByKey.has(tokenPath)) {
122
+ itemsByKey.set(tokenPath, itemsByKey.get(apiKeyPath));
123
+ }
124
+ itemsByKey.delete(apiKeyPath);
93
125
  }
94
126
 
95
127
  /**
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')
128
+ * @returns {string|null}
100
129
  */
101
- function getKvPathSegmentForSecurityKey(securityKey) {
102
- if (!securityKey || typeof securityKey !== 'string') return '';
103
- return varSegmentsToCamelCase([securityKeyToVar(securityKey)]);
130
+ function resolveLabDataplaneApiKey() {
131
+ const candidates = [
132
+ process.env.AIFABRIX_DATAPLANE_API_KEY,
133
+ process.env.DATAPLANE_API_KEY,
134
+ process.env.API_KEY
135
+ ];
136
+ for (const raw of candidates) {
137
+ const trimmed = typeof raw === 'string' ? raw.trim() : '';
138
+ if (trimmed) return trimmed;
139
+ }
140
+ return null;
104
141
  }
105
142
 
106
143
  /**
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
144
+ * @param {string} token
145
+ * @returns {{ type: string, token: string }}
114
146
  */
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);
147
+ function apiKeyAuthConfig(token) {
148
+ return { type: 'bearer', token };
125
149
  }
126
150
 
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
- }
151
+ const PERMISSION_DENIED_WARNING =
152
+ 'Could not push credential secrets (permission denied or unauthenticated). Ensure dataplane role has credential:create if you use KV_* in .env, or set AIFABRIX_DATAPLANE_API_KEY for local lab bypass.';
153
153
 
154
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
155
+ * @param {Object} res
156
+ * @returns {boolean}
161
157
  */
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);
158
+ function isStoreResponseFailed(res) {
159
+ return Boolean(res && (res.success === false || res.data?.success === false));
174
160
  }
175
161
 
176
162
  /**
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"])
163
+ * @param {Object} res
164
+ * @returns {number|undefined}
182
165
  */
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);
204
- }
205
- return Array.from(acc);
206
- }
207
- return Array.from(acc);
166
+ function storeResponseStatus(res) {
167
+ return res?.status ?? res?.statusCode;
208
168
  }
209
169
 
210
170
  /**
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}
171
+ * @param {Object} res
172
+ * @returns {{ pushed: number, warning: string }}
215
173
  */
216
- function isValidKvPath(key) {
217
- return typeof key === 'string' && /^kv:\/\/[a-z0-9][a-z0-9\-/]*$/i.test(key.trim());
174
+ function storeErrorWarning(res) {
175
+ const errMsg =
176
+ res.formattedError ||
177
+ res.data?.formattedError ||
178
+ res.error ||
179
+ res.data?.error ||
180
+ 'Failed to push credential secrets to dataplane.';
181
+ return { pushed: 0, warning: errMsg };
218
182
  }
219
183
 
220
184
  /**
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
185
+ * @param {string} dataplaneUrl
186
+ * @param {Object} authConfig
187
+ * @param {Array<{ key: string, value: string }>} items
188
+ * @returns {Promise<{ pushed: number }|null>}
225
189
  */
226
- function buildItemsFromEnv(envFilePath, secrets, itemsByKey, systemKey) {
227
- if (!envFilePath || typeof envFilePath !== 'string' || !fs.existsSync(envFilePath)) return;
228
- try {
229
- const content = fs.readFileSync(envFilePath, 'utf8');
230
- const envMap = parseEnvToMap(content);
231
- const fromEnv = collectKvEnvVarsAsSecretItems(envMap, systemKey);
232
- for (const { key, value } of fromEnv) {
233
- 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()) {
236
- itemsByKey.set(key, resolved);
237
- }
238
- }
239
- } catch {
240
- // Best-effort: continue without .env items
190
+ async function tryLabApiKeyRetry(dataplaneUrl, authConfig, items) {
191
+ const labKey = resolveLabDataplaneApiKey();
192
+ if (!labKey || authConfig?.token === labKey) {
193
+ return null;
241
194
  }
195
+ const retry = await storeCredentialSecrets(
196
+ dataplaneUrl,
197
+ apiKeyAuthConfig(labKey),
198
+ items
199
+ );
200
+ if (isStoreResponseFailed(retry)) {
201
+ return null;
202
+ }
203
+ return { pushed: storedCountFromResponse(retry, items.length) };
242
204
  }
243
205
 
244
206
  /**
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
207
+ * @param {string} dataplaneUrl
208
+ * @param {Object} authConfig
209
+ * @param {Array<{ key: string, value: string }>} items
210
+ * @param {Object} res
211
+ * @returns {Promise<{ pushed: number, warning?: string }>}
249
212
  */
250
- function buildItemsFromPayload(payload, secrets, itemsByKey) {
251
- if (!payload || typeof payload !== 'object') return;
252
- const refs = collectKvRefsFromPayload(payload);
253
- const existingKeys = new Set(itemsByKey.keys());
254
- for (const ref of refs) {
255
- if (existingKeys.has(ref)) continue;
256
- const pathKey = ref.replace(/^kv:\/\//, '');
257
- const resolved = secrets[pathKey];
258
- if (resolved !== null && resolved !== undefined && isValidKvPath(ref)) {
259
- itemsByKey.set(ref, typeof resolved === 'string' ? resolved : String(resolved));
213
+ async function handleFailedStoreResponse(dataplaneUrl, authConfig, items, res) {
214
+ const status = storeResponseStatus(res);
215
+ if (status === 403 || status === 401) {
216
+ const retryResult = await tryLabApiKeyRetry(dataplaneUrl, authConfig, items);
217
+ if (retryResult) {
218
+ return retryResult;
260
219
  }
220
+ return { pushed: 0, warning: PERMISSION_DENIED_WARNING };
261
221
  }
222
+ return storeErrorWarning(res);
262
223
  }
263
224
 
264
225
  /**
265
- * Derives stored count from API response.
266
- * @param {Object} res - Response from storeCredentialSecrets
267
- * @param {number} fallback - Fallback when stored not in response
268
- * @returns {number}
269
- */
270
- function storedCountFromResponse(res, fallback) {
271
- if (res && typeof res.stored === 'number') return res.stored;
272
- if (res && res.data && res.data.stored !== undefined && res.data.stored !== null) return res.data.stored;
273
- return fallback;
274
- }
275
-
276
- /**
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
226
+ * @param {string} dataplaneUrl
227
+ * @param {Object} authConfig
228
+ * @param {Array<{ key: string, value: string }>} items
281
229
  * @returns {Promise<{ pushed: number, warning?: string }>}
282
230
  */
283
231
  async function sendCredentialSecrets(dataplaneUrl, authConfig, items) {
284
232
  try {
285
233
  const res = await storeCredentialSecrets(dataplaneUrl, authConfig, items);
286
- const failed = res && (res.success === false || res.data?.success === false);
287
- if (failed) {
288
- const status = res.status ?? res.statusCode;
289
- if (status === 403 || status === 401) {
290
- return { pushed: 0, warning: 'Could not push credential secrets (permission denied or unauthenticated). Ensure dataplane role has credential:create if you use KV_* in .env.' };
291
- }
292
- const errMsg = res.formattedError || res.data?.formattedError || res.error || res.data?.error || 'Failed to push credential secrets to dataplane.';
293
- return { pushed: 0, warning: errMsg };
234
+ if (isStoreResponseFailed(res)) {
235
+ return handleFailedStoreResponse(dataplaneUrl, authConfig, items, res);
294
236
  }
295
237
  return { pushed: storedCountFromResponse(res, items.length) };
296
238
  } catch (err) {
297
- return { pushed: 0, warning: err.message || 'Failed to push credential secrets to dataplane.' };
239
+ return {
240
+ pushed: 0,
241
+ warning: err.message || 'Failed to push credential secrets to dataplane.'
242
+ };
298
243
  }
299
244
  }
300
245
 
301
246
  /**
302
- * Pushes credential secrets to dataplane: from .env (KV_*) and from payload kv:// refs.
303
- * Resolves all kv:// in values via loadSecrets; sends only plain values. Best-effort:
304
- * on 403/401 logs warning and returns; never logs secret values.
305
- *
306
- * @param {string} dataplaneUrl - Dataplane base URL
307
- * @param {Object} authConfig - Auth config (Bearer)
308
- * @param {Object} options - Options
309
- * @param {string} [options.envFilePath] - Path to .env (integration/<systemKey>/.env)
310
- * @param {string} [options.appName] - App/system name for loadSecrets context
311
- * @param {Object} [options.payload] - Upload payload { application, dataSources } for kv scan
312
- * @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 }>}
313
251
  */
314
252
  async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
315
253
  const { envFilePath, appName, payload } = options;
@@ -322,6 +260,7 @@ async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
322
260
  const itemsByKey = new Map();
323
261
  buildItemsFromEnv(envFilePath, secrets, itemsByKey, appName);
324
262
  buildItemsFromPayload(payload, secrets, itemsByKey);
263
+ reconcileBearerTokenSecretPaths(itemsByKey, appName, authConfig);
325
264
 
326
265
  const items = Array.from(itemsByKey.entries())
327
266
  .filter(([k]) => isValidKvPath(k))
@@ -335,33 +274,12 @@ async function pushCredentialSecrets(dataplaneUrl, authConfig, options = {}) {
335
274
  return sendResult;
336
275
  }
337
276
 
338
- /**
339
- * Parses .env-style content into key-value map (first = separates key and value).
340
- *
341
- * @param {string} content - Raw .env content
342
- * @returns {Object.<string, string>}
343
- */
344
- function parseEnvToMap(content) {
345
- if (!content || typeof content !== 'string') return {};
346
- const map = {};
347
- const lines = content.split(/\r?\n/);
348
- for (const line of lines) {
349
- const trimmed = line.trim();
350
- if (!trimmed || trimmed.startsWith('#')) continue;
351
- const eq = trimmed.indexOf('=');
352
- if (eq > 0) {
353
- const key = trimmed.substring(0, eq).trim();
354
- const value = trimmed.substring(eq + 1);
355
- map[key] = value;
356
- }
357
- }
358
- return map;
359
- }
360
-
361
277
  module.exports = {
362
278
  collectKvEnvVarsAsSecretItems,
363
279
  collectKvRefsFromPayload,
364
280
  pushCredentialSecrets,
281
+ reconcileBearerTokenSecretPaths,
282
+ resolveLabDataplaneApiKey,
365
283
  kvEnvKeyToPath,
366
284
  getKvPathSegmentForSecurityKey,
367
285
  systemKeyToKvPrefix,
@@ -1,7 +1,8 @@
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
@@ -10,6 +11,17 @@
10
11
 
11
12
  const { discoverDataplaneUrl } = require('../commands/wizard-dataplane');
12
13
 
14
+ /**
15
+ * @returns {string|null}
16
+ */
17
+ function dataplaneUrlFromProcessEnv() {
18
+ const raw = process.env.DP || process.env.DATAPLANE_URL;
19
+ if (!raw || typeof raw !== 'string') {
20
+ return null;
21
+ }
22
+ return raw.trim().replace(/\/$/, '');
23
+ }
24
+
13
25
  /**
14
26
  * Resolve dataplane URL by discovering from controller
15
27
  * @async
@@ -22,7 +34,11 @@ const { discoverDataplaneUrl } = require('../commands/wizard-dataplane');
22
34
  * @throws {Error} If dataplane URL cannot be resolved
23
35
  */
24
36
  async function resolveDataplaneUrl(controllerUrl, environment, authConfig, opts = {}) {
25
- return await discoverDataplaneUrl(controllerUrl, environment, authConfig, opts);
37
+ const envUrl = dataplaneUrlFromProcessEnv();
38
+ if (envUrl) {
39
+ return envUrl;
40
+ }
41
+ return discoverDataplaneUrl(controllerUrl, environment, authConfig, opts);
26
42
  }
27
43
 
28
44
  module.exports = {
@@ -21,7 +21,7 @@ function parseDeveloperIdNum(raw) {
21
21
  }
22
22
 
23
23
  /**
24
- * Docker published host port: port + devId*100 (dev 0 → base port).
24
+ * Host port for compose publish, localhost URLs, and health checks: port + devId×100 (dev 0 → base port).
25
25
  * @param {number} appPort - Manifest listen port
26
26
  * @param {number} developerIdNum
27
27
  * @returns {number}
@@ -31,17 +31,40 @@ function publishedHostPort(appPort, developerIdNum) {
31
31
  }
32
32
 
33
33
  /**
34
- * Local workstation host port: port + 10 + devId*100 (dev 0 → port+10).
34
+ * Same as {@link publishedHostPort} (legacy name kept for call sites).
35
35
  * @param {number} appPort
36
36
  * @param {number} developerIdNum
37
37
  * @returns {number}
38
38
  */
39
39
  function localHostPort(appPort, developerIdNum) {
40
- return appPort + 10 + developerIdNum * 100;
40
+ return publishedHostPort(appPort, developerIdNum);
41
+ }
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;
41
63
  }
42
64
 
43
65
  module.exports = {
44
66
  parseDeveloperIdNum,
45
67
  publishedHostPort,
46
- localHostPort
68
+ localHostPort,
69
+ dataplaneDockerHostPort
47
70
  };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Deployment auth mode for pipeline publish / external integration CLI.
3
+ * @fileoverview AIFABRIX_DEPLOYMENT_AUTH and options.deploymentAuth parsing
4
+ * @author AI Fabrix Team
5
+ * @version 1.0.0
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ /**
11
+ * @typedef {'auto'|'client-credentials'|'device'} DeploymentAuthMode
12
+ */
13
+
14
+ /**
15
+ * @param {Object} [options] - CLI or caller options
16
+ * @returns {DeploymentAuthMode}
17
+ */
18
+ function getDeploymentAuthMode(options = {}) {
19
+ const raw = options.deploymentAuth || process.env.AIFABRIX_DEPLOYMENT_AUTH || 'auto';
20
+ const mode = String(raw).trim().toLowerCase().replace(/_/g, '-');
21
+ if (mode === 'client-credentials' || mode === 'client') {
22
+ return 'client-credentials';
23
+ }
24
+ if (mode === 'device' || mode === 'device-token' || mode === 'bearer') {
25
+ return 'device';
26
+ }
27
+ return 'auto';
28
+ }
29
+
30
+ /**
31
+ * @param {DeploymentAuthMode} mode
32
+ * @returns {boolean}
33
+ */
34
+ function shouldUseDeviceTokenForDeployment(mode) {
35
+ return mode !== 'client-credentials';
36
+ }
37
+
38
+ module.exports = {
39
+ getDeploymentAuthMode,
40
+ shouldUseDeviceTokenForDeployment
41
+ };