@aifabrix/builder 2.54.1 → 2.55.2

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 (253) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +172 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +272 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/start.js +310 -0
  23. package/lib/agent-kit/state-file.js +83 -0
  24. package/lib/agent-kit/status.js +50 -0
  25. package/lib/agent-kit/v1-detect.js +81 -0
  26. package/lib/agent-kit/validate-kit.js +87 -0
  27. package/lib/agent-kit/workspace-name.js +40 -0
  28. package/lib/agent-kit/workspace-skeleton.js +103 -0
  29. package/lib/api/role-assistant-test-pack.api.js +86 -0
  30. package/lib/api/work-search.api.js +31 -4
  31. package/lib/api/work.api.js +19 -12
  32. package/lib/app/deploy.js +7 -4
  33. package/lib/app/list-external-systems.js +2 -1
  34. package/lib/app/offer-create-from-template.js +184 -0
  35. package/lib/app/show-display.js +33 -6
  36. package/lib/app/show.js +25 -2
  37. package/lib/channels/add-channel.js +1 -0
  38. package/lib/channels/approval-guides/claude.js +2 -2
  39. package/lib/channels/channel-add-failure.js +1 -0
  40. package/lib/channels/channel-add-remaining.js +15 -9
  41. package/lib/channels/channel-definition-status.js +115 -0
  42. package/lib/channels/channel-description.js +2 -1
  43. package/lib/channels/channel-install-envelope.js +4 -2
  44. package/lib/channels/channel-integration-generator.js +2 -0
  45. package/lib/channels/channel-runtime-resolver.js +14 -0
  46. package/lib/channels/channel-targets.js +31 -5
  47. package/lib/channels/claude-setup-values.js +4 -1
  48. package/lib/channels/slack-channel-add.js +59 -6
  49. package/lib/channels/slack-mcp-callback.js +51 -0
  50. package/lib/channels/slack-mcp-runtime.js +156 -0
  51. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  52. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  53. package/lib/cli/index.js +4 -0
  54. package/lib/cli/setup-app.js +8 -1
  55. package/lib/cli/setup-auth.js +11 -2
  56. package/lib/cli/setup-certification-commands.js +2 -2
  57. package/lib/cli/setup-certification.help.js +3 -1
  58. package/lib/commands/agent-kit-help.js +149 -0
  59. package/lib/commands/agent-kit.js +417 -0
  60. package/lib/commands/auth-status-user.js +99 -0
  61. package/lib/commands/auth-status.js +14 -4
  62. package/lib/commands/channel-add-help.js +5 -5
  63. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  64. package/lib/commands/datasource-load-export-cli.js +7 -2
  65. package/lib/commands/datasource-test-trust-cli.js +5 -1
  66. package/lib/commands/governance-command-action.js +5 -4
  67. package/lib/commands/identity-membership-role-cache.js +6 -1
  68. package/lib/commands/lifecycle-command-action.js +16 -15
  69. package/lib/commands/login-credentials.js +8 -3
  70. package/lib/commands/login-device.js +14 -5
  71. package/lib/commands/login.js +14 -1
  72. package/lib/commands/logout.js +50 -15
  73. package/lib/commands/repair-auth-apply.js +1 -1
  74. package/lib/commands/repair-datasource-key-skip.js +39 -0
  75. package/lib/commands/repair-datasource-keys.js +24 -12
  76. package/lib/commands/repair-rbac.js +16 -10
  77. package/lib/commands/role-assistant-copy-output.js +61 -0
  78. package/lib/commands/role-assistant-copy.js +155 -0
  79. package/lib/commands/role-assistant-test-output.js +8 -9
  80. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  81. package/lib/commands/role-assistant.js +140 -59
  82. package/lib/commands/search.js +143 -0
  83. package/lib/commands/test-trust-external.js +7 -5
  84. package/lib/commands/upload-sidecar-followups.js +4 -1
  85. package/lib/commands/verify-governance-command-action.js +9 -7
  86. package/lib/commands/verify-operations-command-action.js +9 -8
  87. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  88. package/lib/commands/verify-operations-step-plan.js +6 -33
  89. package/lib/commands/verify-operations-steps.js +3 -1
  90. package/lib/commands/verify-trust-command-action.js +6 -5
  91. package/lib/datasource/capability/relate-operations.js +28 -10
  92. package/lib/datasource/capability/run-capability-relate.js +48 -25
  93. package/lib/datasource/datasource-identifier.js +13 -5
  94. package/lib/deployment/deployer-poll-run.js +117 -35
  95. package/lib/external-system/deploy-dataplane-published.js +70 -0
  96. package/lib/external-system/deploy.js +12 -2
  97. package/lib/external-system/download-role-assistant.js +41 -15
  98. package/lib/external-system/download.js +8 -3
  99. package/lib/generator/helpers.js +12 -1
  100. package/lib/generator/wizard-datasource-key.js +54 -0
  101. package/lib/generator/wizard.js +55 -54
  102. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  103. package/lib/integration-definition/apply-relationships.js +195 -0
  104. package/lib/integration-definition/apply-static-enterprise.js +232 -0
  105. package/lib/integration-definition/apply-system-file.js +71 -0
  106. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  107. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  108. package/lib/integration-definition/apply.js +78 -59
  109. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  110. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  111. package/lib/lifecycle/role-assistant-governance.js +56 -21
  112. package/lib/programmatic/capability-registry.js +1 -0
  113. package/lib/programmatic/index.js +2 -0
  114. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  115. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  116. package/lib/programmatic/openapi-component-schemas.js +1 -0
  117. package/lib/programmatic/openapi-descriptions.js +9 -3
  118. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  119. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  120. package/lib/programmatic/openapi-request-schemas.js +5 -0
  121. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  122. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  123. package/lib/programmatic/openapi-spec.js +1 -0
  124. package/lib/programmatic/request-timeout.js +3 -0
  125. package/lib/programmatic/route-handler-map.js +7 -0
  126. package/lib/programmatic/run-channel-catalog.js +50 -8
  127. package/lib/programmatic/run-channel-install-options.js +5 -1
  128. package/lib/programmatic/run-download.js +41 -0
  129. package/lib/protection/auth-context.js +3 -1
  130. package/lib/protection/protection-display.js +3 -2
  131. package/lib/protection/report-exit.js +50 -0
  132. package/lib/protection/run-commands.js +2 -0
  133. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  134. package/lib/role-assistant/knowledge-sync.js +9 -126
  135. package/lib/role-assistant/package-validate.js +175 -0
  136. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  137. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  138. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  139. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  140. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  141. package/lib/role-assistant/test-case-copy.js +119 -0
  142. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  143. package/lib/role-assistant/test-case-validate.js +88 -0
  144. package/lib/role-assistant/test-cases-bid.js +117 -4
  145. package/lib/role-assistant/test-cases-evaluate.js +149 -31
  146. package/lib/role-assistant/test-cases-evidence.js +44 -5
  147. package/lib/role-assistant/test-cases-honesty.js +155 -6
  148. package/lib/role-assistant/test-cases-lifecycle.js +297 -0
  149. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  150. package/lib/role-assistant/test-cases-search.js +20 -2
  151. package/lib/role-assistant/test-cases.js +34 -168
  152. package/lib/role-assistant/test-pack-map.js +180 -0
  153. package/lib/role-assistant/test-pack-sync.js +187 -0
  154. package/lib/role-assistant/test-runner-debug.js +54 -0
  155. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  156. package/lib/role-assistant/test-runner-search.js +20 -5
  157. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  158. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  159. package/lib/role-assistant/test-runner-workhub.js +45 -21
  160. package/lib/role-assistant/test-runner.js +53 -10
  161. package/lib/schema/external-datasource.schema.json +36 -5
  162. package/lib/schema/external-system.schema.json +20 -2
  163. package/lib/schema/infra-parameter.schema.json +51 -3
  164. package/lib/schema/infra.parameter.yaml +476 -40
  165. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  166. package/lib/schema/type/document-storage.json +1 -1
  167. package/lib/search/dataplane-context.js +23 -0
  168. package/lib/search/search-args.js +211 -0
  169. package/lib/search/search-display.js +238 -0
  170. package/lib/search/search-errors.js +50 -0
  171. package/lib/search/search-prompts.js +119 -0
  172. package/lib/search/search-roles.js +85 -0
  173. package/lib/search/search-run.js +241 -0
  174. package/lib/search/search-skills.js +198 -0
  175. package/lib/utils/api-required-permissions.js +271 -0
  176. package/lib/utils/api.js +16 -13
  177. package/lib/utils/authentication-slot-show.js +153 -0
  178. package/lib/utils/cli-json-exit.js +41 -0
  179. package/lib/utils/cli-utils.js +23 -5
  180. package/lib/utils/controller-url.js +78 -4
  181. package/lib/utils/credential-display.js +43 -1
  182. package/lib/utils/deployment-errors.js +15 -0
  183. package/lib/utils/deployment-validation-helpers.js +4 -1
  184. package/lib/utils/device-code.js +31 -10
  185. package/lib/utils/error-formatters/error-parser.js +16 -1
  186. package/lib/utils/error-formatters/permission-errors.js +19 -5
  187. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  188. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  189. package/lib/utils/external-system-validators.js +2 -1
  190. package/lib/utils/help-builder.js +3 -1
  191. package/lib/utils/keycloak-realm-ready.js +5 -2
  192. package/lib/utils/logger.js +1 -0
  193. package/lib/utils/paths.js +26 -5
  194. package/lib/utils/schema-loader.js +37 -0
  195. package/lib/utils/secrets-helpers.js +2 -2
  196. package/lib/utils/write-stream-and-wait.js +63 -0
  197. package/lib/validation/validate-external-file.js +5 -1
  198. package/lib/validation/validate.js +3 -2
  199. package/package.json +20 -6
  200. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  201. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  202. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  203. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  204. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  205. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  206. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  207. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  208. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  209. package/templates/agent-kit/agent-kit.yaml +45 -0
  210. package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
  211. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  212. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  213. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  214. package/templates/agent-kit/parity-inventory.yaml +188 -0
  215. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
  216. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  217. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  218. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  219. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  220. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
  221. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  222. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  223. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  224. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
  225. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  226. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  227. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  228. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
  229. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  230. package/templates/agent-kit/skills/shared/hosts.md +65 -0
  231. package/templates/agent-kit/skills/shared/login.md +16 -0
  232. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  233. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  234. package/templates/agent-kit/workspace/README.md +89 -0
  235. package/templates/applications/builder-api/Dockerfile +3 -1
  236. package/templates/applications/dataplane/application.yaml +2 -3
  237. package/templates/applications/dataplane/env.template +5 -5
  238. package/templates/applications/dataplane/rbac.yaml +28 -0
  239. package/templates/applications/keycloak/env.template +3 -3
  240. package/templates/applications/miso-controller/application.yaml +21 -2
  241. package/templates/applications/miso-controller/env.template +96 -25
  242. package/templates/applications/miso-controller/rbac.yaml +5 -0
  243. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  244. package/templates/channels/claude/channel.json +1 -1
  245. package/templates/channels/claude/install.template.md.hbs +1 -0
  246. package/templates/channels/claude/integration/claude-system.json +1 -1
  247. package/templates/channels/slack/channel.json +11 -0
  248. package/templates/channels/slack/install.template.md.hbs +9 -0
  249. package/templates/channels/slack/integration/slack-system.json +11 -0
  250. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  251. package/templates/channels/teams-bot/channel.json +1 -0
  252. package/templates/python/docker-compose.hbs +14 -0
  253. package/templates/typescript/docker-compose.hbs +14 -0
@@ -18,52 +18,133 @@ const {
18
18
  const { resolvePollIntervalFromController } = require('./poll-interval');
19
19
  const { createDeployPollHandlers } = require('./deploy-poll-ui');
20
20
 
21
+ /** Same progress for this many polls + dataplane published → treat deploy as complete. */
22
+ const STUCK_PROGRESS_POLLS = 10;
23
+
24
+ function recordStuckProgress(state, snapshot) {
25
+ const progress = Number(snapshot && snapshot.progress);
26
+ const value = Number.isFinite(progress) ? progress : null;
27
+ if (value === state.lastProgress) {
28
+ state.stuckCount += 1;
29
+ } else {
30
+ state.lastProgress = value;
31
+ state.stuckCount = 1;
32
+ }
33
+ return state;
34
+ }
35
+
36
+ async function completeIfDataplanePublished(lastSeen, options) {
37
+ if (!lastSeen || typeof options.isDataplanePublished !== 'function') {
38
+ return null;
39
+ }
40
+ try {
41
+ if (await options.isDataplanePublished()) {
42
+ return {
43
+ ...lastSeen,
44
+ status: 'completed',
45
+ progress: 100,
46
+ completedViaDataplane: true
47
+ };
48
+ }
49
+ } catch {
50
+ return null;
51
+ }
52
+ return null;
53
+ }
54
+
21
55
  /**
22
- * Polls deployment status from controller.
23
- *
24
- * @async
25
- * @param {string} deploymentId
26
- * @param {string} controllerUrl
27
- * @param {string} envKey
28
- * @param {Object} authConfig
29
- * @param {Object} [options]
30
- * @returns {Promise<Object>}
56
+ * Poll until the controller job is terminal, or dataplane is already published.
57
+ * @param {Object} ctx
58
+ * @returns {Promise<Object|null>}
31
59
  */
60
+ async function pollUntilTerminal(ctx) {
61
+ const {
62
+ controllerUrl,
63
+ validatedEnvKey,
64
+ deploymentId,
65
+ pipelineAuthConfig,
66
+ interval,
67
+ maxAttempts,
68
+ onPollWait,
69
+ wrappedProgress,
70
+ stuckState,
71
+ options,
72
+ getLastSeen
73
+ } = ctx;
74
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
75
+ if (onPollWait) {
76
+ onPollWait(attempt, maxAttempts);
77
+ }
78
+ const response = await getPipelineDeployment(
79
+ controllerUrl,
80
+ validatedEnvKey,
81
+ deploymentId,
82
+ pipelineAuthConfig
83
+ );
84
+ const deploymentData = await processDeploymentStatusResponse(
85
+ response,
86
+ attempt,
87
+ maxAttempts,
88
+ interval,
89
+ deploymentId,
90
+ wrappedProgress
91
+ );
92
+ if (deploymentData) {
93
+ return deploymentData;
94
+ }
95
+ if (stuckState.stuckCount >= STUCK_PROGRESS_POLLS) {
96
+ const viaDataplane = await completeIfDataplanePublished(getLastSeen(), options);
97
+ if (viaDataplane) {
98
+ return viaDataplane;
99
+ }
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+
32
105
  async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authConfig, options = {}) {
33
106
  const interval = options.interval || 5000;
34
107
  const maxAttempts = options.maxAttempts || 300;
35
-
36
108
  const validatedEnvKey = validateEnvironmentKey(envKey);
37
109
  const pipelineAuthConfig = convertToPipelineAuthConfig(authConfig);
38
-
39
110
  const onPollProgress = typeof options.onPollProgress === 'function' ? options.onPollProgress : null;
40
111
  const onPollWait = typeof options.onPollWait === 'function' ? options.onPollWait : null;
41
-
42
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
43
- try {
44
- if (onPollWait) {
45
- onPollWait(attempt, maxAttempts);
46
- }
47
- const response = await getPipelineDeployment(controllerUrl, validatedEnvKey, deploymentId, pipelineAuthConfig);
48
- const deploymentData = await processDeploymentStatusResponse(
49
- response,
50
- attempt,
51
- maxAttempts,
52
- interval,
53
- deploymentId,
54
- onPollProgress
55
- );
56
- if (deploymentData) {
57
- return deploymentData;
58
- }
59
- } catch (error) {
60
- if (error.message && error.message.includes('not found')) {
61
- throw error;
62
- }
112
+ const stuckState = { lastProgress: undefined, stuckCount: 0 };
113
+ let lastSeen = null;
114
+ const wrappedProgress = (data, attempt, max) => {
115
+ lastSeen = data;
116
+ recordStuckProgress(stuckState, data);
117
+ if (onPollProgress) {
118
+ onPollProgress(data, attempt, max);
119
+ }
120
+ };
121
+ try {
122
+ const terminal = await pollUntilTerminal({
123
+ controllerUrl,
124
+ validatedEnvKey,
125
+ deploymentId,
126
+ pipelineAuthConfig,
127
+ interval,
128
+ maxAttempts,
129
+ onPollWait,
130
+ wrappedProgress,
131
+ stuckState,
132
+ options,
133
+ getLastSeen: () => lastSeen
134
+ });
135
+ if (terminal) {
136
+ return terminal;
137
+ }
138
+ } catch (error) {
139
+ if (error.message && error.message.includes('not found')) {
63
140
  throw error;
64
141
  }
142
+ throw error;
143
+ }
144
+ const timedOutComplete = await completeIfDataplanePublished(lastSeen, options);
145
+ if (timedOutComplete) {
146
+ return timedOutComplete;
65
147
  }
66
-
67
148
  throw new Error('Deployment timeout: Maximum polling attempts reached');
68
149
  }
69
150
 
@@ -94,7 +175,8 @@ async function pollDeployment(result, url, validatedEnvKey, authConfig, options)
94
175
  interval: resolvedInterval,
95
176
  maxAttempts,
96
177
  onPollProgress: pollUi.onPollProgress,
97
- onPollWait: pollUi.onPollWait
178
+ onPollWait: pollUi.onPollWait,
179
+ isDataplanePublished: options.isDataplanePublished
98
180
  }
99
181
  );
100
182
 
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Treat a stuck controller deploy poll as complete when dataplane is already published.
3
+ *
4
+ * @fileoverview Dataplane published check for external deploy poll
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const { getExternalSystem } = require('../api/external-systems.api');
10
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
11
+ const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
12
+
13
+ /**
14
+ * @param {Object|null|undefined} system
15
+ * @returns {boolean}
16
+ */
17
+ function isDataplaneSystemPublished(system) {
18
+ const status = String((system && system.status) || '').toLowerCase();
19
+ return status === 'published' || status === 'deployed';
20
+ }
21
+
22
+ /**
23
+ * @param {string} controllerUrl
24
+ * @param {string} environment
25
+ * @param {Object} authConfig
26
+ * @param {string} systemKey
27
+ * @param {string} [dataplaneUrl]
28
+ * @returns {Promise<boolean>}
29
+ */
30
+ async function checkDataplaneSystemPublished(
31
+ controllerUrl,
32
+ environment,
33
+ authConfig,
34
+ systemKey,
35
+ dataplaneUrl
36
+ ) {
37
+ let url = dataplaneUrl;
38
+ if (!url) {
39
+ url = await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent: true });
40
+ }
41
+ const getRes = await getExternalSystem(url, systemKey, authConfig);
42
+ return isDataplaneSystemPublished(unwrapApiData(getRes));
43
+ }
44
+
45
+ /**
46
+ * @param {Object} params
47
+ * @returns {() => Promise<boolean>}
48
+ */
49
+ function createDataplanePublishedCheck(params) {
50
+ const { controllerUrl, environment, authConfig, systemKey, dataplaneUrl } = params;
51
+ return async function isDataplanePublished() {
52
+ try {
53
+ return await checkDataplaneSystemPublished(
54
+ controllerUrl,
55
+ environment,
56
+ authConfig,
57
+ systemKey,
58
+ dataplaneUrl
59
+ );
60
+ } catch {
61
+ return false;
62
+ }
63
+ };
64
+ }
65
+
66
+ module.exports = {
67
+ isDataplaneSystemPublished,
68
+ checkDataplaneSystemPublished,
69
+ createDataplanePublishedCheck
70
+ };
@@ -31,6 +31,7 @@ const { syncDeployJsonFromSources } = require('./sync-deploy-manifest');
31
31
  const { runDeployGateOrchestrator } = require('../commands/deploy-gate-orchestrator');
32
32
  const { runPostPublishDataSync } = require('../commands/run-post-publish-data-sync');
33
33
  const { uploadDigitalWorkerIfNeeded, deployRoleAssistantShellIfNeeded } = require('./deploy-helpers');
34
+ const { createDataplanePublishedCheck } = require('./deploy-dataplane-published');
34
35
 
35
36
  /**
36
37
  * Validates manifest on dataplane (dry-run) before controller deploy.
@@ -194,9 +195,10 @@ const EXTERNAL_DEPLOY_POLL_INTERVAL_MS = 2000;
194
195
  /** 300 × 2s ≈ 10 minutes — must cover controller publish-job max wait with gates. */
195
196
  const EXTERNAL_DEPLOY_POLL_MAX_ATTEMPTS = 300;
196
197
 
197
- function buildExternalDeployPollOpts(options) {
198
+ function buildExternalDeployPollOpts(options, extra = {}) {
198
199
  return {
199
200
  ...options,
201
+ ...extra,
200
202
  poll: true,
201
203
  pollInterval:
202
204
  options.pollInterval !== undefined ? options.pollInterval : EXTERNAL_DEPLOY_POLL_INTERVAL_MS,
@@ -300,7 +302,15 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
300
302
  controllerUrl,
301
303
  environment,
302
304
  authConfig,
303
- buildExternalDeployPollOpts(options)
305
+ buildExternalDeployPollOpts(options, {
306
+ isDataplanePublished: createDataplanePublishedCheck({
307
+ controllerUrl,
308
+ environment,
309
+ authConfig,
310
+ systemKey: manifest.key,
311
+ dataplaneUrl: options.knownDataplaneUrl
312
+ })
313
+ })
304
314
  );
305
315
 
306
316
  const deploymentOutcome = parseControllerDeploymentOutcome(result);
@@ -24,13 +24,23 @@ const {
24
24
  isPlatformShellKind
25
25
  } = require('../utils/platform-shell-package');
26
26
 
27
- const CONNECTED_SYSTEM_DRY_RUN_FILES = [
28
- 'deploy.json',
29
- 'application.yaml',
30
- 'system.yaml',
31
- 'env.template',
32
- 'README.md'
33
- ];
27
+ function connectedSystemDryRunFileNames(systemKey, dataSources, format) {
28
+ const { getExternalDatasourceFileName } = require('../generator/split-variables');
29
+ const ext = format === 'json' ? 'json' : 'yaml';
30
+ const names = [
31
+ `${systemKey}-deploy.json`,
32
+ `application.${ext}`,
33
+ `${systemKey}-system.${ext}`,
34
+ 'env.template',
35
+ 'README.md'
36
+ ];
37
+ const list = Array.isArray(dataSources) ? dataSources : [];
38
+ for (let i = 0; i < list.length; i += 1) {
39
+ const yamlName = getExternalDatasourceFileName(systemKey, list[i], i);
40
+ names.push(ext === 'json' ? yamlName.replace(/\.yaml$/i, '.json') : yamlName);
41
+ }
42
+ return names;
43
+ }
34
44
 
35
45
  async function maybeExportRoleAssistantEvidenceArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options) {
36
46
  try {
@@ -47,6 +57,23 @@ async function maybeExportRoleAssistantEvidenceArtifacts(systemKey, manifest, da
47
57
  }
48
58
  }
49
59
 
60
+ async function maybeExportRoleAssistantTestsArtifacts(systemKey, dataplaneUrl, authConfig, options) {
61
+ try {
62
+ const { exportRoleAssistantTests } = require('../role-assistant/test-pack-sync');
63
+ const testsResult = await exportRoleAssistantTests({
64
+ systemKey,
65
+ dataplaneUrl,
66
+ authConfig,
67
+ options
68
+ });
69
+ if (testsResult && testsResult.skipped) {
70
+ logger.log(formatWarningLine(`Role assistant tests export skipped: ${testsResult.reason}`));
71
+ }
72
+ } catch (testsErr) {
73
+ logger.log(formatWarningLine(`Role assistant tests export skipped: ${testsErr.message}`));
74
+ }
75
+ }
76
+
50
77
  async function maybeExportRoleAssistantKnowledgeArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options) {
51
78
  try {
52
79
  const knowledgeResult = await exportRoleAssistantKnowledge({
@@ -67,19 +94,17 @@ async function runRoleAssistantDownloadSidecars(params) {
67
94
  const { systemKey, manifest, dataplaneUrl, authConfig, options = {} } = params;
68
95
  await maybeExportRoleAssistantEvidenceArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options);
69
96
  await maybeExportRoleAssistantKnowledgeArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options);
97
+ await maybeExportRoleAssistantTestsArtifacts(systemKey, dataplaneUrl, authConfig, options);
70
98
  }
71
99
 
72
- function logConnectedSystemDryRunFiles(systemKey) {
100
+ function logConnectedSystemDryRunFiles(systemKey, dataSources, format) {
73
101
  logger.log(metadata('Would create:'));
74
- CONNECTED_SYSTEM_DRY_RUN_FILES.forEach((name) => {
75
- const fileName = name === 'deploy.json' || name === 'system.yaml'
76
- ? `${systemKey}-${name}`
77
- : name;
102
+ connectedSystemDryRunFileNames(systemKey, dataSources, format).forEach((fileName) => {
78
103
  logger.log(metadata(` - integration/${systemKey}/${fileName}`));
79
104
  });
80
105
  }
81
106
 
82
- function handleDownloadDryRun(systemKey, dataplaneUrl, application) {
107
+ function handleDownloadDryRun(systemKey, dataplaneUrl, application, dataSources, format) {
83
108
  logger.log(formatWarningLine('Dry run mode: no files written.'));
84
109
  logger.log(metadata(`Would fetch: ${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`));
85
110
  if (isEnterpriseKnowledgeKind(application)) {
@@ -91,7 +116,7 @@ function handleDownloadDryRun(systemKey, dataplaneUrl, application) {
91
116
  logger.log(metadata(` - role-assistant/${systemKey}/role-assistant.yaml`));
92
117
  return;
93
118
  }
94
- logConnectedSystemDryRunFiles(systemKey);
119
+ logConnectedSystemDryRunFiles(systemKey, dataSources, format);
95
120
  }
96
121
 
97
122
  async function downloadDigitalWorkerPackage(params) {
@@ -130,5 +155,6 @@ async function downloadPlatformShellIfNeeded(params) {
130
155
  module.exports = {
131
156
  runRoleAssistantDownloadSidecars,
132
157
  downloadPlatformShellIfNeeded,
133
- handleDownloadDryRun
158
+ handleDownloadDryRun,
159
+ connectedSystemDryRunFileNames
134
160
  };
@@ -74,9 +74,8 @@ async function setupAuthenticationAndDataplane(systemKey, _options, _config) {
74
74
  }
75
75
 
76
76
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
77
- logger.log(formatProgress('Resolving dataplane URL…'));
77
+ // discoverDataplaneUrl already logs progress and success
78
78
  const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
79
- logger.log(formatSuccessLine(`Dataplane URL: ${dataplaneUrl}`));
80
79
 
81
80
  return { authConfig, dataplaneUrl };
82
81
  }
@@ -421,7 +420,13 @@ async function downloadExternalSystem(systemKey, options = {}) {
421
420
  );
422
421
  const manifest = await downloadFullManifest(dataplaneUrl, systemKey, authConfig, options);
423
422
  if (options.dryRun) {
424
- handleDownloadDryRun(systemKey, dataplaneUrl, manifest.application);
423
+ handleDownloadDryRun(
424
+ systemKey,
425
+ dataplaneUrl,
426
+ manifest.application,
427
+ manifest.dataSources,
428
+ format
429
+ );
425
430
  return;
426
431
  }
427
432
  const platformHandled = await downloadPlatformShellIfNeeded({
@@ -234,6 +234,18 @@ function determineVariableLocation(value, key) {
234
234
  required = true;
235
235
  }
236
236
 
237
+ if (location === 'keyvault' && value.startsWith('kv://')) {
238
+ try {
239
+ const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
240
+ const entry = getInfraParameterCatalog().findEntryForKey(value.slice('kv://'.length));
241
+ if (entry?.scope === 'infra' && entry.generator?.type === 'emptyAllowed') {
242
+ required = false;
243
+ }
244
+ } catch {
245
+ // Keep the conservative required=true default when the catalog is unavailable.
246
+ }
247
+ }
248
+
237
249
  return { location, required };
238
250
  }
239
251
 
@@ -344,4 +356,3 @@ module.exports = {
344
356
  validatePortalInput,
345
357
  parseEnvironmentVariables
346
358
  };
347
-
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Canonical kebab datasource keys for wizard generate-config.
3
+ *
4
+ * @fileoverview Wizard datasource key segments
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ /**
10
+ * @param {string} str
11
+ * @returns {string}
12
+ */
13
+ function toKeySegment(str) {
14
+ if (!str || typeof str !== 'string') {
15
+ return 'default';
16
+ }
17
+ const withHyphens = str.replace(/([A-Z])/g, '-$1').toLowerCase();
18
+ const sanitized = withHyphens.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
19
+ return sanitized || 'default';
20
+ }
21
+
22
+ /**
23
+ * @param {string} finalSystemKey
24
+ * @param {{ key?: string, entityName?: string, entityType?: string, entityKey?: string }} ds
25
+ * @param {string} originalPrefix
26
+ * @param {Set<string>} [usedKeys]
27
+ * @returns {string}
28
+ */
29
+ function wizardCanonicalDatasourceKey(finalSystemKey, ds, originalPrefix, usedKeys) {
30
+ const dsKey = String(ds.key || '').trim();
31
+ let segment;
32
+ if (dsKey && originalPrefix && dsKey.startsWith(originalPrefix)) {
33
+ segment = toKeySegment(dsKey.substring(originalPrefix.length));
34
+ } else {
35
+ const raw = ds.entityName || dsKey || ds.entityType || ds.entityKey || 'default';
36
+ segment = toKeySegment(raw);
37
+ }
38
+ let candidate = segment.includes('-') && !segment.startsWith(`${finalSystemKey}-`)
39
+ ? segment
40
+ : `${finalSystemKey}-${segment}`;
41
+ if (usedKeys && usedKeys.has(candidate)) {
42
+ let suffix = 2;
43
+ while (usedKeys.has(`${candidate}-${suffix}`)) {
44
+ suffix += 1;
45
+ }
46
+ candidate = `${candidate}-${suffix}`;
47
+ }
48
+ return candidate;
49
+ }
50
+
51
+ module.exports = {
52
+ toKeySegment,
53
+ wizardCanonicalDatasourceKey
54
+ };
@@ -17,19 +17,7 @@ const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
17
17
  const { systemKeyToKvPrefix, securityKeyToVar, isValidKvPath } = require('../utils/credential-secrets-env');
18
18
  const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
19
19
  const { generateReadme } = require('./wizard-readme');
20
-
21
- /**
22
- * Converts a string to a schema-valid key segment (lowercase letters, numbers, hyphens only).
23
- * e.g. "recordStorage" -> "record-storage", "documentStorage" -> "document-storage"
24
- * @param {string} str - Raw entity type or key segment (may be camelCase)
25
- * @returns {string} Segment matching ^[a-z0-9-]+$
26
- */
27
- function toKeySegment(str) {
28
- if (!str || typeof str !== 'string') return 'default';
29
- const withHyphens = str.replace(/([A-Z])/g, '-$1').toLowerCase();
30
- const sanitized = withHyphens.replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
31
- return sanitized || 'default';
32
- }
20
+ const { toKeySegment, wizardCanonicalDatasourceKey } = require('./wizard-datasource-key');
33
21
 
34
22
  const { normalizeAuthenticationToSystemKey } = require('../utils/authentication-shape');
35
23
 
@@ -118,10 +106,46 @@ async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfi
118
106
  * @param {string} [params.aiGeneratedReadme] - Optional AI-generated README content
119
107
  * @returns {Promise<Object>} Object with file paths
120
108
  */
121
- async function generateConfigFilesForWizard(params) {
122
- const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme, format } = params;
109
+ async function writeWizardEnvTemplate(appPath, systemConfig) {
110
+ const envTemplatePath = path.join(appPath, 'env.template');
111
+ const envTemplateContent = generateExternalEnvTemplateContent(systemConfig);
112
+ await fs.writeFile(envTemplatePath, envTemplateContent, 'utf8');
113
+ logger.log(formatSuccessLine('Generated env.template'));
114
+ try {
115
+ const secretsEnsure = require('../core/secrets-ensure');
116
+ await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, {
117
+ emptyValuesForCredentials: true
118
+ });
119
+ } catch (err) {
120
+ if (err.code !== 'ENOENT') {
121
+ logger.warn(`Could not ensure integration placeholder secrets: ${err.message}`);
122
+ }
123
+ }
124
+ return envTemplatePath;
125
+ }
123
126
 
124
- // Generate or update application config with externalIntegration block
127
+ async function writeWizardDeployManifest(appPath, appName, finalSystemKey) {
128
+ const { generateControllerManifest, toDeployJsonShape } = require('./external-controller-manifest');
129
+ const manifest = await generateControllerManifest(appName, { appPath });
130
+ const deployJson = toDeployJsonShape(manifest);
131
+ const deployManifestPath = path.join(appPath, `${finalSystemKey}-deploy.json`);
132
+ await fs.writeFile(deployManifestPath, JSON.stringify(deployJson, null, 2), 'utf8');
133
+ logger.log(formatSuccessLine(`Generated deployment manifest: ${finalSystemKey}-deploy.json`));
134
+ return deployManifestPath;
135
+ }
136
+
137
+ async function generateConfigFilesForWizard(params) {
138
+ const {
139
+ appPath,
140
+ appName,
141
+ finalSystemKey,
142
+ systemFileName,
143
+ datasourceFileNames,
144
+ systemConfig,
145
+ datasourceConfigs,
146
+ aiGeneratedReadme,
147
+ format
148
+ } = params;
125
149
  const configPath = await generateOrUpdateVariablesYaml({
126
150
  appPath,
127
151
  appName,
@@ -131,21 +155,7 @@ async function generateConfigFilesForWizard(params) {
131
155
  systemConfig,
132
156
  format: format || 'yaml'
133
157
  });
134
-
135
- // Generate env.template with Authentication and Configuration sections (Handlebars)
136
- const envTemplatePath = path.join(appPath, 'env.template');
137
- const envTemplateContent = generateExternalEnvTemplateContent(systemConfig);
138
- await fs.writeFile(envTemplatePath, envTemplateContent, 'utf8');
139
- logger.log(formatSuccessLine('Generated env.template'));
140
-
141
- try {
142
- const secretsEnsure = require('../core/secrets-ensure');
143
- await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, { emptyValuesForCredentials: true });
144
- } catch (err) {
145
- if (err.code !== 'ENOENT') logger.warn(`Could not ensure integration placeholder secrets: ${err.message}`);
146
- }
147
-
148
- // Generate README.md (use AI-generated content if available)
158
+ const envTemplatePath = await writeWizardEnvTemplate(appPath, systemConfig);
149
159
  await generateReadme({
150
160
  appPath,
151
161
  appName,
@@ -155,21 +165,16 @@ async function generateConfigFilesForWizard(params) {
155
165
  aiGeneratedContent: aiGeneratedReadme,
156
166
  format
157
167
  });
158
-
159
- // Generate deployment scripts
160
- const deployScripts = await generateDeployScripts(appPath, finalSystemKey, systemFileName, datasourceFileNames);
161
-
162
- // Generate deployment manifest (<systemKey>-deploy.json) using controller format
163
- const { generateControllerManifest, toDeployJsonShape } = require('./external-controller-manifest');
164
- const manifest = await generateControllerManifest(appName, { appPath });
165
- const deployJson = toDeployJsonShape(manifest);
166
- const deployManifestPath = path.join(appPath, `${finalSystemKey}-deploy.json`);
167
- await fs.writeFile(deployManifestPath, JSON.stringify(deployJson, null, 2), 'utf8');
168
- logger.log(formatSuccessLine(`Generated deployment manifest: ${finalSystemKey}-deploy.json`));
169
-
168
+ const deployScripts = await generateDeployScripts(
169
+ appPath,
170
+ finalSystemKey,
171
+ systemFileName,
172
+ datasourceFileNames
173
+ );
174
+ const deployManifestPath = await writeWizardDeployManifest(appPath, appName, finalSystemKey);
170
175
  return {
171
176
  variablesPath: configPath,
172
- envTemplatePath: path.join(appPath, 'env.template'),
177
+ envTemplatePath,
173
178
  readmePath: path.join(appPath, 'README.md'),
174
179
  applicationSchemaPath: deployManifestPath,
175
180
  ...deployScripts
@@ -190,16 +195,10 @@ async function prepareWizardContext(appName, systemConfig, datasourceConfigs) {
190
195
  appDisplayName
191
196
  );
192
197
  const originalPrefix = `${originalSystemKey}-`;
198
+ const usedKeys = new Set();
193
199
  const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
194
- let newKey;
195
- const dsKey = ds.key || '';
196
- if (dsKey && dsKey.startsWith(originalPrefix)) {
197
- newKey = `${finalSystemKey}-${dsKey.substring(originalPrefix.length)}`;
198
- } else {
199
- const entityType = ds.entityType || ds.entityKey || dsKey.split('-').pop() || 'default';
200
- const keySegment = toKeySegment(entityType);
201
- newKey = `${finalSystemKey}-${keySegment}`;
202
- }
200
+ const newKey = wizardCanonicalDatasourceKey(finalSystemKey, ds, originalPrefix, usedKeys);
201
+ usedKeys.add(newKey);
203
202
  const entityType = ds.entityType || ds.entityKey || newKey.split('-').pop() || 'default';
204
203
  const entityDisplayName = String(entityType).charAt(0).toUpperCase() + String(entityType).slice(1).replace(/-/g, ' ');
205
204
  return { ...ds, key: newKey, systemKey: finalSystemKey, displayName: ds.displayName || `${appDisplayName} ${entityDisplayName}` };
@@ -481,6 +480,8 @@ const { generateDeployScripts } = require('./deploy-script');
481
480
 
482
481
  module.exports = {
483
482
  generateWizardFiles,
484
- generateDeployScripts
483
+ generateDeployScripts,
484
+ wizardCanonicalDatasourceKey,
485
+ toKeySegment
485
486
  };
486
487