@aifabrix/builder 2.44.4 → 2.44.6

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 (214) hide show
  1. package/.cursor/rules/cli-layout.mdc +1 -1
  2. package/.cursor/rules/project-rules.mdc +1 -1
  3. package/.npmrc.token +1 -1
  4. package/README.md +15 -23
  5. package/integration/hubspot-test/README.md +2 -0
  6. package/integration/hubspot-test/test.js +5 -3
  7. package/jest.projects.js +68 -17
  8. package/lib/api/controller-health.api.js +49 -0
  9. package/lib/api/dimension-values.api.js +82 -0
  10. package/lib/api/dimensions.api.js +114 -0
  11. package/lib/api/external-systems.api.js +1 -0
  12. package/lib/api/integration-clients.api.js +168 -0
  13. package/lib/api/types/dimension-values.types.js +28 -0
  14. package/lib/api/types/dimensions.types.js +31 -0
  15. package/lib/api/types/integration-clients.types.js +45 -0
  16. package/lib/api/types/wizard.types.js +2 -1
  17. package/lib/api/validation-runner.js +46 -25
  18. package/lib/app/deploy-config.js +11 -1
  19. package/lib/app/deploy-status-display.js +3 -3
  20. package/lib/app/deploy.js +36 -14
  21. package/lib/app/display.js +15 -11
  22. package/lib/app/push.js +46 -23
  23. package/lib/app/register.js +1 -1
  24. package/lib/app/restart-display.js +95 -0
  25. package/lib/app/rotate-secret.js +1 -1
  26. package/lib/app/run-container-start.js +12 -6
  27. package/lib/app/run-env-compose.js +30 -1
  28. package/lib/app/run-helpers.js +44 -12
  29. package/lib/app/run-reload-sync.js +148 -0
  30. package/lib/app/run-resolve-image.js +51 -1
  31. package/lib/app/run.js +99 -73
  32. package/lib/build/index.js +75 -45
  33. package/lib/cli/doctor-check.js +117 -0
  34. package/lib/cli/index.js +8 -2
  35. package/lib/cli/infra-guided.js +445 -0
  36. package/lib/cli/setup-app.help.js +1 -1
  37. package/lib/cli/setup-app.js +20 -2
  38. package/lib/cli/setup-app.test-commands.js +9 -5
  39. package/lib/cli/setup-auth.js +26 -0
  40. package/lib/cli/setup-dev-path-commands.js +50 -3
  41. package/lib/cli/setup-infra.js +138 -61
  42. package/lib/cli/setup-integration-client.js +182 -0
  43. package/lib/cli/setup-parameters.js +21 -2
  44. package/lib/cli/setup-platform.js +102 -0
  45. package/lib/cli/setup-secrets.js +18 -6
  46. package/lib/cli/setup-utility.js +97 -33
  47. package/lib/commands/datasource-capability-dimension-cli.js +128 -0
  48. package/lib/commands/datasource-capability-output.js +29 -0
  49. package/lib/commands/datasource-capability-relate-cli.js +140 -0
  50. package/lib/commands/datasource-capability.js +411 -0
  51. package/lib/commands/datasource-unified-test-cli.options.js +1 -1
  52. package/lib/commands/datasource.js +53 -13
  53. package/lib/commands/dev-down.js +3 -3
  54. package/lib/commands/dev-infra-gate.js +32 -0
  55. package/lib/commands/dev-init.js +13 -7
  56. package/lib/commands/dimension-value.js +179 -0
  57. package/lib/commands/dimension.js +330 -0
  58. package/lib/commands/integration-client.js +430 -0
  59. package/lib/commands/login-device.js +65 -30
  60. package/lib/commands/login.js +21 -10
  61. package/lib/commands/parameters-validate.js +78 -13
  62. package/lib/commands/repair-datasource-auto-rbac.js +166 -0
  63. package/lib/commands/repair-datasource-keys.js +10 -5
  64. package/lib/commands/repair-datasource.js +19 -7
  65. package/lib/commands/repair-env-template.js +4 -1
  66. package/lib/commands/repair-openapi-sync.js +172 -0
  67. package/lib/commands/repair-persist.js +102 -0
  68. package/lib/commands/repair-rbac-extract.js +27 -0
  69. package/lib/commands/repair-rbac-migrate.js +186 -0
  70. package/lib/commands/repair-rbac.js +225 -19
  71. package/lib/commands/repair-system-alignment.js +246 -0
  72. package/lib/commands/repair-system-permissions.js +168 -0
  73. package/lib/commands/repair.js +120 -354
  74. package/lib/commands/secure.js +1 -1
  75. package/lib/commands/setup-modes.js +455 -0
  76. package/lib/commands/setup-prompts.js +388 -0
  77. package/lib/commands/setup.js +149 -0
  78. package/lib/commands/teardown.js +228 -0
  79. package/lib/commands/test-e2e-external.js +4 -3
  80. package/lib/commands/up-common.js +97 -12
  81. package/lib/commands/up-dataplane.js +33 -11
  82. package/lib/commands/up-miso.js +7 -11
  83. package/lib/commands/upload.js +109 -23
  84. package/lib/commands/wizard-core-helpers.js +14 -11
  85. package/lib/commands/wizard-core.js +58 -15
  86. package/lib/commands/wizard-dataplane.js +2 -2
  87. package/lib/commands/wizard-entity-selection.js +72 -14
  88. package/lib/commands/wizard-headless.js +7 -3
  89. package/lib/commands/wizard-helpers.js +13 -1
  90. package/lib/commands/wizard.js +210 -61
  91. package/lib/constants/infra-compose-service-names.js +40 -0
  92. package/lib/core/env-reader.js +16 -3
  93. package/lib/core/secrets-admin-env.js +101 -0
  94. package/lib/core/secrets-ensure-infra.js +34 -1
  95. package/lib/core/secrets-ensure.js +88 -66
  96. package/lib/core/secrets-env-content.js +432 -0
  97. package/lib/core/secrets-env-write.js +27 -1
  98. package/lib/core/secrets-load.js +248 -0
  99. package/lib/core/secrets-names.js +32 -0
  100. package/lib/core/secrets.js +17 -757
  101. package/lib/datasource/capability/basic-exposure.js +76 -0
  102. package/lib/datasource/capability/capability-diff-slice.js +41 -0
  103. package/lib/datasource/capability/capability-key.js +34 -0
  104. package/lib/datasource/capability/capability-resolve.js +172 -0
  105. package/lib/datasource/capability/capability-storage-keys.js +22 -0
  106. package/lib/datasource/capability/copy-operations.js +348 -0
  107. package/lib/datasource/capability/copy-test-payload.js +139 -0
  108. package/lib/datasource/capability/create-operations.js +235 -0
  109. package/lib/datasource/capability/dimension-operations.js +151 -0
  110. package/lib/datasource/capability/dimension-validate.js +219 -0
  111. package/lib/datasource/capability/json-pointer.js +31 -0
  112. package/lib/datasource/capability/reference-rewrite.js +51 -0
  113. package/lib/datasource/capability/relate-operations.js +325 -0
  114. package/lib/datasource/capability/relate-validate.js +219 -0
  115. package/lib/datasource/capability/remove-operations.js +275 -0
  116. package/lib/datasource/capability/run-capability-copy.js +152 -0
  117. package/lib/datasource/capability/run-capability-diff.js +135 -0
  118. package/lib/datasource/capability/run-capability-dimension.js +291 -0
  119. package/lib/datasource/capability/run-capability-edit.js +377 -0
  120. package/lib/datasource/capability/run-capability-relate.js +193 -0
  121. package/lib/datasource/capability/run-capability-remove.js +105 -0
  122. package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
  123. package/lib/datasource/capability/validate-capability-slice.js +35 -0
  124. package/lib/datasource/list.js +136 -23
  125. package/lib/datasource/log-viewer.js +2 -4
  126. package/lib/datasource/unified-validation-run.js +51 -16
  127. package/lib/datasource/validate.js +53 -1
  128. package/lib/deployment/deploy-poll-ui.js +60 -0
  129. package/lib/deployment/deployer-status.js +29 -3
  130. package/lib/deployment/deployer.js +48 -30
  131. package/lib/deployment/environment.js +7 -2
  132. package/lib/deployment/poll-interval.js +72 -0
  133. package/lib/deployment/push.js +11 -9
  134. package/lib/external-system/deploy.js +4 -1
  135. package/lib/external-system/download.js +61 -32
  136. package/lib/external-system/sync-deploy-manifest.js +33 -0
  137. package/lib/generator/wizard-prompts.js +7 -1
  138. package/lib/generator/wizard.js +34 -0
  139. package/lib/infrastructure/index.js +49 -19
  140. package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
  141. package/lib/parameters/infra-kv-discovery.js +29 -4
  142. package/lib/parameters/infra-parameter-catalog.js +6 -3
  143. package/lib/parameters/infra-parameter-validate.js +67 -19
  144. package/lib/resolvers/datasource-resolver.js +53 -0
  145. package/lib/resolvers/dimension-file.js +52 -0
  146. package/lib/resolvers/manifest-resolver.js +133 -0
  147. package/lib/schema/external-datasource.schema.json +183 -53
  148. package/lib/schema/external-system.schema.json +23 -10
  149. package/lib/schema/infra.parameter.yaml +26 -11
  150. package/lib/schema/wizard-config.schema.json +2 -2
  151. package/lib/utils/aifabrix-config-dir-walk.js +40 -0
  152. package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
  153. package/lib/utils/app-run-containers.js +2 -2
  154. package/lib/utils/bash-secret-env.js +59 -0
  155. package/lib/utils/cli-secrets-error-format.js +78 -0
  156. package/lib/utils/cli-test-layout-chalk.js +31 -9
  157. package/lib/utils/cli-utils.js +4 -36
  158. package/lib/utils/datasource-test-run-display.js +8 -0
  159. package/lib/utils/dev-hosts-helper.js +3 -2
  160. package/lib/utils/dev-init-ssh-merge.js +2 -1
  161. package/lib/utils/docker-build.js +17 -9
  162. package/lib/utils/docker-reload-mount.js +127 -0
  163. package/lib/utils/external-readme.js +117 -4
  164. package/lib/utils/external-system-local-test-tty.js +3 -2
  165. package/lib/utils/external-system-readiness-core.js +45 -12
  166. package/lib/utils/external-system-readiness-deploy-display.js +3 -3
  167. package/lib/utils/external-system-readiness-display-internals.js +33 -3
  168. package/lib/utils/external-system-readiness-display.js +10 -1
  169. package/lib/utils/file-upload.js +40 -3
  170. package/lib/utils/health-check-db-init.js +107 -0
  171. package/lib/utils/health-check-public-warn.js +69 -0
  172. package/lib/utils/health-check-url.js +19 -4
  173. package/lib/utils/health-check.js +135 -105
  174. package/lib/utils/help-builder.js +5 -1
  175. package/lib/utils/image-name.js +34 -7
  176. package/lib/utils/integration-file-backup.js +74 -0
  177. package/lib/utils/mutagen-install.js +30 -3
  178. package/lib/utils/paths.js +108 -25
  179. package/lib/utils/postgres-wipe.js +212 -0
  180. package/lib/utils/register-aifabrix-shell-env.js +15 -0
  181. package/lib/utils/remote-dev-auth.js +21 -5
  182. package/lib/utils/remote-docker-env.js +9 -1
  183. package/lib/utils/remote-secrets-loader.js +42 -3
  184. package/lib/utils/resolve-docker-image-ref.js +9 -3
  185. package/lib/utils/secrets-ancestor-paths.js +47 -0
  186. package/lib/utils/secrets-helpers.js +17 -10
  187. package/lib/utils/secrets-kv-refs.js +42 -0
  188. package/lib/utils/secrets-kv-scope.js +19 -2
  189. package/lib/utils/secrets-materialize-local.js +134 -0
  190. package/lib/utils/secrets-path.js +24 -10
  191. package/lib/utils/secrets-utils.js +2 -2
  192. package/lib/utils/system-builder-root.js +34 -0
  193. package/lib/utils/url-declarative-resolve-build.js +6 -1
  194. package/lib/utils/url-declarative-runtime-base-path.js +32 -0
  195. package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
  196. package/lib/utils/urls-local-registry.js +73 -20
  197. package/lib/utils/validation-poll-ui.js +81 -0
  198. package/lib/utils/validation-run-poll.js +29 -5
  199. package/lib/utils/with-muted-logger.js +53 -0
  200. package/package.json +1 -1
  201. package/templates/applications/dataplane/application.yaml +1 -1
  202. package/templates/applications/dataplane/rbac.yaml +10 -10
  203. package/templates/applications/keycloak/env.template +8 -6
  204. package/templates/applications/miso-controller/application.yaml +7 -0
  205. package/templates/applications/miso-controller/env.template +7 -7
  206. package/templates/applications/miso-controller/rbac.yaml +9 -9
  207. package/templates/external-system/README.md.hbs +89 -102
  208. package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  209. package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
  210. package/.nyc_output/processinfo/index.json +0 -1
  211. package/lib/api/service-users.api.js +0 -150
  212. package/lib/api/types/service-users.types.js +0 -65
  213. package/lib/cli/setup-service-user.js +0 -187
  214. package/lib/commands/service-user.js +0 -429
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @fileoverview Integration clients API (Controller /api/v1/integration-clients)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { ApiClient } = require('./index');
8
+
9
+ const BASE = '/api/v1/integration-clients';
10
+
11
+ /**
12
+ * Create integration client; returns one-time clientSecret
13
+ * @requiresPermission {Controller} integration-client:create
14
+ * @async
15
+ * @function createIntegrationClient
16
+ * @param {string} controllerUrl - Controller base URL
17
+ * @param {Object} authConfig - Authentication configuration (bearer or client-credentials)
18
+ * @param {Object} body - Request body
19
+ * @param {string} body.key - Key (required)
20
+ * @param {string} body.displayName - Display name (required)
21
+ * @param {string[]} body.redirectUris - Redirect URIs (required, min 1)
22
+ * @param {string[]} [body.groupNames] - Group names (optional)
23
+ * @param {string} [body.description] - Optional description
24
+ * @param {string} [body.keycloakClientId] - Optional Keycloak client id
25
+ * @returns {Promise<Object>} API response
26
+ * @throws {Error} If request fails
27
+ */
28
+ async function createIntegrationClient(controllerUrl, authConfig, body) {
29
+ const client = new ApiClient(controllerUrl, authConfig);
30
+ const payload = {
31
+ key: body.key,
32
+ displayName: body.displayName,
33
+ redirectUris: body.redirectUris,
34
+ groupNames: Array.isArray(body.groupNames) ? body.groupNames : []
35
+ };
36
+ if (body.description !== undefined && body.description !== null && body.description !== '') {
37
+ payload.description = body.description;
38
+ }
39
+ if (body.keycloakClientId) {
40
+ payload.keycloakClientId = body.keycloakClientId;
41
+ }
42
+ return await client.post(BASE, { body: payload });
43
+ }
44
+
45
+ /**
46
+ * List integration clients
47
+ * @requiresPermission {Controller} integration-client:read
48
+ * @async
49
+ * @function listIntegrationClients
50
+ * @param {string} controllerUrl - Controller base URL
51
+ * @param {Object} authConfig - Authentication configuration
52
+ * @param {Object} [options] - Query options
53
+ * @param {number} [options.page] - Page number
54
+ * @param {number} [options.pageSize] - Page size
55
+ * @param {string} [options.sort] - Sort
56
+ * @param {string} [options.filter] - Filter
57
+ * @param {string} [options.search] - Search
58
+ * @returns {Promise<Object>} API response
59
+ * @throws {Error} If request fails
60
+ */
61
+ async function listIntegrationClients(controllerUrl, authConfig, options = {}) {
62
+ const client = new ApiClient(controllerUrl, authConfig);
63
+ const params = {};
64
+ if (options.page !== undefined && options.page !== null) params.page = options.page;
65
+ if (options.pageSize !== undefined && options.pageSize !== null) params.pageSize = options.pageSize;
66
+ if (options.sort) params.sort = options.sort;
67
+ if (options.filter) params.filter = options.filter;
68
+ if (options.search) params.search = options.search;
69
+ return await client.get(BASE, { params });
70
+ }
71
+
72
+ /**
73
+ * Get integration client by id
74
+ * @requiresPermission {Controller} integration-client:read
75
+ * @async
76
+ * @function getIntegrationClient
77
+ * @param {string} controllerUrl - Controller base URL
78
+ * @param {Object} authConfig - Authentication configuration
79
+ * @param {string} id - Integration client id
80
+ * @returns {Promise<Object>} API response
81
+ * @throws {Error} If request fails
82
+ */
83
+ async function getIntegrationClient(controllerUrl, authConfig, id) {
84
+ const client = new ApiClient(controllerUrl, authConfig);
85
+ return await client.get(`${BASE}/${encodeURIComponent(id)}`);
86
+ }
87
+
88
+ /**
89
+ * Regenerate client secret (shown once)
90
+ * @requiresPermission {Controller} integration-client:update
91
+ * @async
92
+ * @function regenerateIntegrationClientSecret
93
+ * @param {string} controllerUrl - Controller base URL
94
+ * @param {Object} authConfig - Authentication configuration
95
+ * @param {string} id - Integration client id
96
+ * @returns {Promise<Object>} API response
97
+ * @throws {Error} If request fails
98
+ */
99
+ async function regenerateIntegrationClientSecret(controllerUrl, authConfig, id) {
100
+ const client = new ApiClient(controllerUrl, authConfig);
101
+ return await client.post(`${BASE}/${encodeURIComponent(id)}/regenerate-secret`);
102
+ }
103
+
104
+ /**
105
+ * Deactivate integration client
106
+ * @requiresPermission {Controller} integration-client:delete
107
+ * @async
108
+ * @function deleteIntegrationClient
109
+ * @param {string} controllerUrl - Controller base URL
110
+ * @param {Object} authConfig - Authentication configuration
111
+ * @param {string} id - Integration client id
112
+ * @returns {Promise<Object>} API response
113
+ * @throws {Error} If request fails
114
+ */
115
+ async function deleteIntegrationClient(controllerUrl, authConfig, id) {
116
+ const client = new ApiClient(controllerUrl, authConfig);
117
+ return await client.delete(`${BASE}/${encodeURIComponent(id)}`);
118
+ }
119
+
120
+ /**
121
+ * Replace group memberships
122
+ * @requiresPermission {Controller} integration-client:update
123
+ * @async
124
+ * @function updateIntegrationClientGroups
125
+ * @param {string} controllerUrl - Controller base URL
126
+ * @param {Object} authConfig - Authentication configuration
127
+ * @param {string} id - Integration client id
128
+ * @param {Object} body - Body with groupNames
129
+ * @param {string[]} body.groupNames - Group names
130
+ * @returns {Promise<Object>} API response
131
+ * @throws {Error} If request fails
132
+ */
133
+ async function updateIntegrationClientGroups(controllerUrl, authConfig, id, body) {
134
+ const client = new ApiClient(controllerUrl, authConfig);
135
+ return await client.put(`${BASE}/${encodeURIComponent(id)}/groups`, {
136
+ body: { groupNames: body.groupNames }
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Replace redirect URIs
142
+ * @requiresPermission {Controller} integration-client:update
143
+ * @async
144
+ * @function updateIntegrationClientRedirectUris
145
+ * @param {string} controllerUrl - Controller base URL
146
+ * @param {Object} authConfig - Authentication configuration
147
+ * @param {string} id - Integration client id
148
+ * @param {Object} body - Body with redirectUris
149
+ * @param {string[]} body.redirectUris - Redirect URIs (min 1)
150
+ * @returns {Promise<Object>} API response
151
+ * @throws {Error} If request fails
152
+ */
153
+ async function updateIntegrationClientRedirectUris(controllerUrl, authConfig, id, body) {
154
+ const client = new ApiClient(controllerUrl, authConfig);
155
+ return await client.put(`${BASE}/${encodeURIComponent(id)}/redirect-uris`, {
156
+ body: { redirectUris: body.redirectUris }
157
+ });
158
+ }
159
+
160
+ module.exports = {
161
+ createIntegrationClient,
162
+ listIntegrationClients,
163
+ getIntegrationClient,
164
+ regenerateIntegrationClientSecret,
165
+ deleteIntegrationClient,
166
+ updateIntegrationClientGroups,
167
+ updateIntegrationClientRedirectUris
168
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Dimension values API type definitions (Controller)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Create dimension value payload
9
+ * @typedef {Object} DimensionValueCreateRequest
10
+ * @property {string} value
11
+ * @property {string} [displayName]
12
+ * @property {string} [description]
13
+ */
14
+
15
+ /**
16
+ * Dimension value entity
17
+ * @typedef {Object} DimensionValue
18
+ * @property {string} id
19
+ * @property {string} dimensionId
20
+ * @property {string} value
21
+ * @property {string|null} [displayName]
22
+ * @property {string|null} [description]
23
+ * @property {string|null} [createdBy]
24
+ * @property {string|null} [updatedBy]
25
+ * @property {string} [createdAt]
26
+ * @property {string} [updatedAt]
27
+ */
28
+
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview Dimensions API type definitions (Controller)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Dimension create/update payload (Controller catalog)
9
+ * @typedef {Object} DimensionCreateRequest
10
+ * @property {string} key
11
+ * @property {string} displayName
12
+ * @property {string} [description]
13
+ * @property {'string'|'number'|'boolean'} dataType
14
+ * @property {boolean} [isRequired]
15
+ */
16
+
17
+ /**
18
+ * Dimension entity
19
+ * @typedef {Object} Dimension
20
+ * @property {string} id
21
+ * @property {string} key
22
+ * @property {string} displayName
23
+ * @property {string|null} [description]
24
+ * @property {'string'|'number'|'boolean'} dataType
25
+ * @property {boolean} isRequired
26
+ * @property {string|null} [createdBy]
27
+ * @property {string|null} [updatedBy]
28
+ * @property {string} [createdAt]
29
+ * @property {string} [updatedAt]
30
+ */
31
+
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Integration clients API type definitions (Controller camelCase)
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ /**
8
+ * Integration client create request body (builder → controller)
9
+ * @typedef {Object} IntegrationClientCreateRequest
10
+ * @property {string} key - Stable key (lowercase alphanumeric + hyphens)
11
+ * @property {string} displayName - Human-readable name
12
+ * @property {string[]} redirectUris - Allowed redirect URIs for OAuth2 (min 1)
13
+ * @property {string[]} [groupNames] - Optional group names (RBAC); omit or empty for OAuth-only clients
14
+ * @property {string} [description] - Optional description
15
+ * @property {string} [keycloakClientId] - Optional fixed Keycloak client id
16
+ */
17
+
18
+ /**
19
+ * Create response (clientSecret is one-time-only)
20
+ * @typedef {Object} IntegrationClientCreateResponseData
21
+ * @property {Object} [integrationClient] - Created record
22
+ * @property {string} [integrationClient.id] - Integration client id
23
+ * @property {string} [integrationClient.key] - Key
24
+ * @property {string} [integrationClient.displayName] - Display name
25
+ * @property {string} [integrationClient.keycloakClientId] - OAuth client id in Keycloak
26
+ * @property {string} [integrationClient.status] - Status
27
+ * @property {string} [clientSecret] - One-time secret
28
+ */
29
+
30
+ /**
31
+ * Single integration client in list response
32
+ * @typedef {Object} ListIntegrationClientItem
33
+ * @property {string} id - Integration client id
34
+ * @property {string} [key] - Key
35
+ * @property {string} [displayName] - Display name
36
+ * @property {string} [keycloakClientId] - OAuth client id
37
+ * @property {string} [status] - Status (e.g. active)
38
+ */
39
+
40
+ /**
41
+ * Regenerate secret response
42
+ * @typedef {Object} RegenerateIntegrationClientSecretResponse
43
+ * @property {Object} [data] - Nested data
44
+ * @property {string} [data.clientSecret] - New secret (one-time)
45
+ */
@@ -260,12 +260,13 @@
260
260
  * @property {string} [source.serverUrl] - MCP server URL (for mcp-server)
261
261
  * @property {string} [source.token] - MCP token (for mcp-server, supports ${ENV_VAR})
262
262
  * @property {string} [source.platform] - Known platform (for known-platform)
263
+ * @property {string} [source.entityName] - Entity from discover-entities (openapi-file / openapi-url); skips interactive Step 4.5 when valid
263
264
  * @property {Object} [credential] - Credential configuration
264
265
  * @property {string} credential.action - Action ('create' | 'select' | 'skip')
265
266
  * @property {string} [credential.credentialIdOrKey] - Credential ID/key (for select)
266
267
  * @property {Object} [credential.config] - Credential config (for create)
267
268
  * @property {Object} [preferences] - Generation preferences
268
- * @property {string} [preferences.intent] - User intent (any descriptive text)
269
+ * @property {string} [preferences.intent] - User intent (any descriptive text, max 1000 chars)
269
270
  * @property {string} [preferences.fieldOnboardingLevel] - Field level ('full' | 'standard' | 'minimal')
270
271
  * @property {boolean} [preferences.enableOpenAPIGeneration] - Enable OpenAPI generation
271
272
  * @property {boolean} [preferences.enableMCP] - Enable MCP
@@ -9,6 +9,8 @@
9
9
  const { extractTestRunId } = require('./validation-run.api');
10
10
  const { postValidationRunWithTransportRetry } = require('../utils/validation-run-post-retry');
11
11
  const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll');
12
+ const { createValidationPollHandlers } = require('../utils/validation-poll-ui');
13
+ const logger = require('../utils/logger');
12
14
 
13
15
  /**
14
16
  * POST /api/v1/validation/run and (when async) poll GET until reportCompleteness is full.
@@ -20,6 +22,8 @@ const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll
20
22
  * @param {number} opts.timeoutMs
21
23
  * @param {boolean} opts.useAsync
22
24
  * @param {boolean} opts.noAsync
25
+ * @param {boolean} [opts.verbosePoll] - Throttled poll lines when no TTY poll UI (see validation-run-poll)
26
+ * @param {Function} [opts.onPollProgress] - Extra hook: `(envelope, attemptIndex, meta)` during poll
23
27
  * @returns {Promise<{ envelope: Object|null, apiError: Object|null, pollTimedOut: boolean, incompleteNoAsync: boolean }>}
24
28
  */
25
29
  /* eslint-disable max-lines-per-function, max-statements, complexity -- POST + poll orchestration */
@@ -54,31 +58,48 @@ async function postValidationRunAndOptionalPoll(opts) {
54
58
  if (needsPoll && testRunId) {
55
59
  const elapsed = Date.now() - started;
56
60
  const remaining = Math.max(0, timeoutMs - elapsed);
57
- const pollResult = await pollValidationRunUntilComplete({
58
- dataplaneUrl,
59
- authConfig,
60
- testRunId,
61
- budgetMs: remaining,
62
- verbosePoll: verbosePoll === true,
63
- pollRequestTimeoutMs:
64
- Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : undefined
65
- });
66
- if (!pollResult.lastApiResult || !pollResult.lastApiResult.success) {
67
- return {
68
- envelope: pollResult.envelope,
69
- apiError: pollResult.lastApiResult,
70
- pollTimedOut: pollResult.timedOut,
71
- incompleteNoAsync: false
72
- };
73
- }
74
- envelope = pollResult.envelope;
75
- if (pollResult.timedOut) {
76
- return {
77
- envelope,
78
- apiError: null,
79
- pollTimedOut: true,
80
- incompleteNoAsync: false
81
- };
61
+ const deadlineMs = Date.now() + remaining;
62
+
63
+ logger.log('');
64
+ const pollUi = createValidationPollHandlers(deadlineMs);
65
+ const mergeOnPollProgress =
66
+ typeof opts.onPollProgress === 'function'
67
+ ? (envelope, attemptIndex, meta) => {
68
+ pollUi.onPollProgress(envelope, attemptIndex, meta);
69
+ opts.onPollProgress(envelope, attemptIndex, meta);
70
+ }
71
+ : pollUi.onPollProgress;
72
+
73
+ try {
74
+ const pollResult = await pollValidationRunUntilComplete({
75
+ dataplaneUrl,
76
+ authConfig,
77
+ testRunId,
78
+ budgetMs: remaining,
79
+ verbosePoll: verbosePoll === true,
80
+ pollRequestTimeoutMs:
81
+ Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : undefined,
82
+ onPollProgress: mergeOnPollProgress
83
+ });
84
+ if (!pollResult.lastApiResult || !pollResult.lastApiResult.success) {
85
+ return {
86
+ envelope: pollResult.envelope,
87
+ apiError: pollResult.lastApiResult,
88
+ pollTimedOut: pollResult.timedOut,
89
+ incompleteNoAsync: false
90
+ };
91
+ }
92
+ envelope = pollResult.envelope;
93
+ if (pollResult.timedOut) {
94
+ return {
95
+ envelope,
96
+ apiError: null,
97
+ pollTimedOut: true,
98
+ incompleteNoAsync: false
99
+ };
100
+ }
101
+ } finally {
102
+ pollUi.finish();
82
103
  }
83
104
  }
84
105
 
@@ -62,11 +62,21 @@ async function extractDeploymentConfig(options, _variables) {
62
62
  const controllerUrl = await resolveControllerUrl();
63
63
  const envKey = await resolveEnvironment();
64
64
 
65
+ const pollIntervalExplicit =
66
+ options.pollInterval !== undefined && options.pollInterval !== null
67
+ ? Number(options.pollInterval)
68
+ : undefined;
69
+
65
70
  return {
66
71
  controllerUrl,
67
72
  envKey,
68
73
  poll: options.poll !== false,
69
- pollInterval: options.pollInterval || 5000,
74
+ // When true, polling still happens but is not rendered (no nested ora spinner).
75
+ // Used by guided installers which already show a top-level spinner.
76
+ silentPoll: options.silentPoll === true,
77
+ repositoryUrl: options.repositoryUrl,
78
+ // Omit default here so deployer can resolve 2s vs 5s from controller deploymentType (GET /api/v1/health)
79
+ pollInterval: Number.isFinite(pollIntervalExplicit) ? pollIntervalExplicit : undefined,
70
80
  pollMaxAttempts: options.pollMaxAttempts || 60
71
81
  };
72
82
  }
@@ -5,8 +5,8 @@
5
5
  * @version 2.0.0
6
6
  */
7
7
 
8
- const chalk = require('chalk');
9
8
  const logger = require('../utils/logger');
9
+ const { formatSuccessLine, metadata } = require('../utils/cli-test-layout-chalk');
10
10
  const { getApplicationStatus } = require('../api/applications.api');
11
11
 
12
12
  /**
@@ -69,9 +69,9 @@ async function displayAppUrlFromController(controllerUrl, envKey, appKey, authCo
69
69
  url = buildAppUrlFromControllerAndPort(controllerUrl, port);
70
70
  }
71
71
  if (url) {
72
- logger.log(chalk.green(`App running at ${url}`));
72
+ logger.log(formatSuccessLine(`App running at ${url}`));
73
73
  } else {
74
- logger.log(chalk.blue('App deployed. Get URL from controller dashboard.'));
74
+ logger.log(metadata('App deployed. Resolve URL from the controller dashboard.'));
75
75
  }
76
76
  }
77
77
 
package/lib/app/deploy.js CHANGED
@@ -1,4 +1,10 @@
1
- const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
+ const {
2
+ formatSuccessLine,
3
+ formatSuccessParagraph,
4
+ sectionTitle,
5
+ headerKeyValue,
6
+ metadata
7
+ } = require('../utils/cli-test-layout-chalk');
2
8
  /**
3
9
  * AI Fabrix Builder Application Deployment Module
4
10
  *
@@ -13,6 +19,8 @@ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test
13
19
  const fs = require('fs').promises;
14
20
  const chalk = require('chalk');
15
21
  const pushUtils = require('../deployment/push');
22
+
23
+ const SEP = chalk.gray('────────────────────────────────────────');
16
24
  const logger = require('../utils/logger');
17
25
  const { detectAppType, getBuilderPath, getIntegrationPath } = require('../utils/paths');
18
26
  const { checkApplicationExists } = require('../utils/app-existence');
@@ -156,7 +164,10 @@ async function pushApp(appName, options = {}) {
156
164
  * @throws {Error} If generation or validation fails
157
165
  */
158
166
  async function generateAndValidateManifest(appName, options = {}) {
159
- logger.log(chalk.blue(`\n📋 Generating deployment manifest for ${appName}...`));
167
+ logger.log('');
168
+ logger.log(sectionTitle('Deployment manifest'));
169
+ logger.log(SEP);
170
+ logger.log(metadata(`Generating manifest for ${appName}...`));
160
171
  const generator = require('../generator');
161
172
 
162
173
  // generateDeployJson already validates against schema and throws on error
@@ -175,10 +186,10 @@ async function generateAndValidateManifest(appName, options = {}) {
175
186
  */
176
187
  function displayDeploymentInfo(manifest, manifestPath) {
177
188
  logger.log(formatSuccessLine(`Manifest generated: ${manifestPath}`));
178
- logger.log(chalk.blue(` Key: ${manifest.key}`));
179
- logger.log(chalk.blue(` Display Name: ${manifest.displayName}`));
180
- logger.log(chalk.blue(` Image: ${manifest.image}`));
181
- logger.log(chalk.blue(` Port: ${manifest.port}`));
189
+ logger.log(headerKeyValue('Key:', manifest.key));
190
+ logger.log(headerKeyValue('Display Name:', manifest.displayName));
191
+ logger.log(headerKeyValue('Image:', manifest.image));
192
+ logger.log(headerKeyValue('Port:', String(manifest.port)));
182
193
  }
183
194
 
184
195
  /**
@@ -189,7 +200,11 @@ function displayDeploymentInfo(manifest, manifestPath) {
189
200
  * @returns {Promise<Object>} Deployment result
190
201
  */
191
202
  async function executeDeployment(manifest, deploymentConfig) {
192
- logger.log(chalk.blue(`\n🚀 Deploying to ${deploymentConfig.controllerUrl} (environment: ${deploymentConfig.envKey})...`));
203
+ logger.log('');
204
+ logger.log(sectionTitle('Deploy to controller'));
205
+ logger.log(SEP);
206
+ logger.log(headerKeyValue('URL:', deploymentConfig.controllerUrl));
207
+ logger.log(headerKeyValue('Environment:', deploymentConfig.envKey));
193
208
  const deployer = require('../deployment/deployer');
194
209
  return await deployer.deployToController(
195
210
  manifest,
@@ -199,7 +214,9 @@ async function executeDeployment(manifest, deploymentConfig) {
199
214
  {
200
215
  poll: deploymentConfig.poll,
201
216
  pollInterval: deploymentConfig.pollInterval,
202
- pollMaxAttempts: deploymentConfig.pollMaxAttempts
217
+ pollMaxAttempts: deploymentConfig.pollMaxAttempts,
218
+ repositoryUrl: deploymentConfig.repositoryUrl,
219
+ silentPoll: deploymentConfig.silentPoll === true
203
220
  }
204
221
  );
205
222
  }
@@ -209,17 +226,22 @@ async function executeDeployment(manifest, deploymentConfig) {
209
226
  * @param {Object} result - Deployment result
210
227
  */
211
228
  function displayDeploymentResults(result) {
212
- logger.log(chalk.green('\n✅ Deployment initiated successfully'));
229
+ logger.log(formatSuccessParagraph('Deployment initiated successfully'));
213
230
  if (result.deploymentUrl) {
214
- logger.log(chalk.blue(` URL: ${result.deploymentUrl}`));
231
+ logger.log(headerKeyValue('URL:', result.deploymentUrl));
215
232
  }
216
233
  if (result.deploymentId) {
217
- logger.log(chalk.blue(` Deployment ID: ${result.deploymentId}`));
234
+ logger.log(headerKeyValue('Deployment ID:', result.deploymentId));
218
235
  }
219
236
  if (result.status) {
220
- const statusIcon = result.status.status === 'completed' ? '✔' :
221
- result.status.status === 'failed' ? '✖' : '⏳';
222
- logger.log(chalk.blue(` Status: ${statusIcon} ${result.status.status}`));
237
+ const st = result.status.status;
238
+ const statusText =
239
+ st === 'completed'
240
+ ? formatSuccessLine(st)
241
+ : st === 'failed' || st === 'cancelled'
242
+ ? chalk.red(`✖ ${st}`)
243
+ : chalk.yellow(`⏳ ${st}`);
244
+ logger.log(`${chalk.gray('Status:')} ${statusText}`);
223
245
  }
224
246
  }
225
247
 
@@ -1,4 +1,4 @@
1
- const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
+ const { formatNextActions, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
2
2
  /**
3
3
  * Application Display Utilities
4
4
  *
@@ -24,11 +24,13 @@ const logger = require('../utils/logger');
24
24
  function displayExternalSystemSuccess(appName, config, location) {
25
25
  logger.log(chalk.blue('Type: External System'));
26
26
  logger.log(chalk.blue(`System Key: ${config.systemKey || appName}`));
27
- logger.log(chalk.green('\nNext steps:'));
28
- logger.log(chalk.white('1. Edit external system JSON files in ' + location));
29
- logger.log(chalk.white('2. Run: aifabrix validate ' + appName));
30
- logger.log(chalk.white('3. Run: aifabrix login'));
31
- logger.log(chalk.white('4. Run: aifabrix deploy ' + appName));
27
+ logger.log('');
28
+ logger.log(formatNextActions([
29
+ `Edit external system JSON files in ${location}`,
30
+ `Run: aifabrix validate ${appName}`,
31
+ 'Run: aifabrix login',
32
+ `Run: aifabrix deploy ${appName}`
33
+ ]));
32
34
  }
33
35
 
34
36
  /**
@@ -49,11 +51,13 @@ function displayWebappSuccess(appName, config, envConversionMessage) {
49
51
 
50
52
  logger.log(chalk.gray(envConversionMessage));
51
53
 
52
- logger.log(chalk.green('\nNext steps:'));
53
- logger.log(chalk.white('1. Copy env.template to .env and fill in your values'));
54
- logger.log(chalk.white('2. Run: aifabrix up-infra'));
55
- logger.log(chalk.white('3. Run: aifabrix build ' + appName));
56
- logger.log(chalk.white('4. Run: aifabrix run ' + appName));
54
+ logger.log('');
55
+ logger.log(formatNextActions([
56
+ 'Copy env.template to .env and fill in your values',
57
+ 'Run: aifabrix up-infra',
58
+ `Run: aifabrix build ${appName}`,
59
+ `Run: aifabrix run ${appName}`
60
+ ]));
57
61
  }
58
62
 
59
63
  /**