@aifabrix/builder 2.50.0 → 2.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
  2. package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
  3. package/integration/hubspot-test/create-hubspot.js +1 -16
  4. package/jest.isolated-projects.js +77 -1
  5. package/jest.projects.js +5 -426
  6. package/lib/api/assistants.api.js +67 -0
  7. package/lib/api/datasources-extended.api.js +6 -6
  8. package/lib/api/evidence-authoring-helpers.js +267 -0
  9. package/lib/api/evidence-authoring.api.js +337 -0
  10. package/lib/api/evidence-kinds.api.js +128 -0
  11. package/lib/api/evidence.api.js +57 -1
  12. package/lib/api/index.js +24 -7
  13. package/lib/api/pipeline.api.js +47 -3
  14. package/lib/api/records-bulk.api.js +28 -3
  15. package/lib/api/resource-types.api.js +4 -4
  16. package/lib/api/roles.api.js +121 -0
  17. package/lib/api/types/evidence-kinds.types.js +42 -0
  18. package/lib/api/types/evidence.types.js +156 -8
  19. package/lib/api/types/roles.types.js +36 -0
  20. package/lib/api/work.api.js +283 -0
  21. package/lib/app/certification-verify-rows.js +1 -1
  22. package/lib/app/run-deploy-job.js +108 -0
  23. package/lib/app/run-helpers.js +10 -3
  24. package/lib/app/run.js +8 -0
  25. package/lib/app/show.js +2 -2
  26. package/lib/certification/merge-certification-from-artifact.js +1 -1
  27. package/lib/channels/add-channel.js +54 -23
  28. package/lib/channels/approval-guides/chatgpt.js +8 -7
  29. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  30. package/lib/channels/channel-artifact-generator.js +16 -3
  31. package/lib/channels/channel-credential.js +3 -1
  32. package/lib/channels/channel-probe-evidence.js +79 -0
  33. package/lib/channels/channel-probe.js +265 -13
  34. package/lib/channels/channel-runtime-resolver.js +120 -29
  35. package/lib/channels/channel-status-display.js +44 -0
  36. package/lib/channels/channel-targets.js +7 -0
  37. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  38. package/lib/channels/microsoft-copilot-package.js +280 -0
  39. package/lib/channels/runtime-tool-catalog.js +165 -0
  40. package/lib/cli/index.js +6 -0
  41. package/lib/cli/setup-app.js +26 -5
  42. package/lib/cli/setup-dev.js +23 -7
  43. package/lib/cli/setup-external-system-help.js +8 -2
  44. package/lib/cli/setup-external-system.js +12 -0
  45. package/lib/cli/setup-utility-help-after.js +12 -0
  46. package/lib/cli/setup-utility-validate.js +41 -2
  47. package/lib/commands/app-test.js +2 -14
  48. package/lib/commands/datasource-load-export-cli.js +6 -1
  49. package/lib/commands/deploy-gate-failure-details.js +23 -0
  50. package/lib/commands/deploy-gate-orchestrator.js +64 -18
  51. package/lib/commands/dev-cli-handlers.js +6 -3
  52. package/lib/commands/dev-down.js +2 -6
  53. package/lib/commands/dev-init.js +7 -109
  54. package/lib/commands/dev-refresh.js +197 -0
  55. package/lib/commands/evidence-kind.js +478 -0
  56. package/lib/commands/governance-command-action.js +4 -2
  57. package/lib/commands/governance-generate-external.js +56 -21
  58. package/lib/commands/lifecycle-external.js +10 -4
  59. package/lib/commands/repair-datasource-apply.js +10 -1
  60. package/lib/commands/repair-datasource-run.js +4 -2
  61. package/lib/commands/repair-datasource-test-payload.js +80 -19
  62. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  63. package/lib/commands/repair-internal.js +9 -1
  64. package/lib/commands/repair-openapi-export.js +1 -1
  65. package/lib/commands/repair-openapi-sync.js +43 -18
  66. package/lib/commands/resource-type.js +101 -18
  67. package/lib/commands/role-assistant.js +313 -0
  68. package/lib/commands/role.js +452 -0
  69. package/lib/commands/setup.js +1 -1
  70. package/lib/commands/upload-data-sync-finalize.js +5 -4
  71. package/lib/commands/upload-sidecar-followups.js +144 -47
  72. package/lib/commands/upload.js +43 -6
  73. package/lib/commands/validate-json.js +230 -0
  74. package/lib/commands/verify-governance-combined-external.js +73 -9
  75. package/lib/commands/verify-governance-db.js +80 -25
  76. package/lib/commands/verify-governance-external.js +29 -5
  77. package/lib/commands/verify-operations-step-plan.js +78 -40
  78. package/lib/commands/wizard-core-helpers.js +7 -3
  79. package/lib/commands/wizard-core.js +78 -9
  80. package/lib/core/audit-logger.js +1 -1
  81. package/lib/core/secrets-load.js +1 -3
  82. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  83. package/lib/datasource/bulk-loader-service.js +5 -1
  84. package/lib/datasource/capability/relate-operations.js +1 -1
  85. package/lib/datasource/datasource-validate-summary.js +2 -2
  86. package/lib/datasource/validate.js +13 -6
  87. package/lib/external-system/deploy-helpers.js +35 -1
  88. package/lib/external-system/deploy.js +23 -10
  89. package/lib/external-system/download-helpers.js +111 -2
  90. package/lib/external-system/download-role-assistant.js +55 -0
  91. package/lib/external-system/download.js +45 -26
  92. package/lib/external-system/test-helpers.js +1 -1
  93. package/lib/external-system/test.js +39 -3
  94. package/lib/generator/builders.js +3 -0
  95. package/lib/generator/helpers.js +13 -5
  96. package/lib/generator/split-variables.js +9 -3
  97. package/lib/generator/wizard.js +51 -4
  98. package/lib/infrastructure/helpers-docker-check.js +6 -0
  99. package/lib/infrastructure/helpers.js +1 -1
  100. package/lib/lifecycle/access-control-abac.js +121 -0
  101. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  102. package/lib/lifecycle/product-model.js +3 -2
  103. package/lib/lifecycle/report-display.js +14 -2
  104. package/lib/programmatic/auth-config.js +3 -2
  105. package/lib/programmatic/bearer-auth.js +1 -1
  106. package/lib/programmatic/capability-registry.js +20 -1
  107. package/lib/programmatic/command-options.js +1 -0
  108. package/lib/programmatic/enterprise-mcp-client.js +18 -2
  109. package/lib/programmatic/index.js +4 -0
  110. package/lib/programmatic/manifest-patch-section.js +48 -9
  111. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  112. package/lib/programmatic/manifest-section-registry.js +18 -1
  113. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  114. package/lib/programmatic/openapi-component-schemas.js +16 -1
  115. package/lib/programmatic/openapi-description-enricher.js +3 -1
  116. package/lib/programmatic/openapi-descriptions.js +55 -5
  117. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  118. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  119. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  120. package/lib/programmatic/openapi-request-schemas.js +36 -1
  121. package/lib/programmatic/openapi-result-schemas.js +29 -12
  122. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  123. package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
  124. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  125. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  126. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  127. package/lib/programmatic/openapi-schema-helpers.js +16 -1
  128. package/lib/programmatic/openapi-spec.js +48 -29
  129. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  130. package/lib/programmatic/request-timeout.js +3 -0
  131. package/lib/programmatic/route-handler-map.js +89 -2
  132. package/lib/programmatic/run-certify.js +15 -2
  133. package/lib/programmatic/run-deploy.js +74 -0
  134. package/lib/programmatic/run-evidence-authoring.js +80 -0
  135. package/lib/programmatic/run-protection.js +124 -0
  136. package/lib/programmatic/run-repository-emcp.js +11 -3
  137. package/lib/programmatic/run-template-workspace.js +42 -2
  138. package/lib/programmatic/workspace-context.js +6 -0
  139. package/lib/protection/auth-context.js +48 -6
  140. package/lib/protection/paths.js +22 -0
  141. package/lib/protection/run-protection-create-helpers.js +22 -1
  142. package/lib/protection/run-protection-create.js +1 -1
  143. package/lib/protection/sync-after-upload.js +1 -1
  144. package/lib/resolvers/evidence-kind-file.js +230 -0
  145. package/lib/resolvers/resource-type-file.js +36 -1
  146. package/lib/resolvers/role-file.js +227 -0
  147. package/lib/role-assistant/evidence-sync.js +450 -0
  148. package/lib/role-assistant/github-workflow.js +96 -0
  149. package/lib/role-assistant/knowledge-sync.js +465 -0
  150. package/lib/role-assistant/settings.js +126 -0
  151. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  152. package/lib/role-assistant/test-cases-honesty.js +184 -0
  153. package/lib/role-assistant/test-cases.js +364 -0
  154. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  155. package/lib/role-assistant/test-runner-workhub.js +385 -0
  156. package/lib/role-assistant/test-runner.js +500 -0
  157. package/lib/schema/application-schema.json +36 -2
  158. package/lib/schema/deployment-rules.yaml +1 -0
  159. package/lib/schema/external-datasource.schema.json +121 -4
  160. package/lib/schema/external-system.schema.json +20 -6
  161. package/lib/schema/infra.parameter.yaml +45 -1
  162. package/lib/schema/role-assistant-settings.schema.json +108 -0
  163. package/lib/schema/type/credential-field-registry.json +19 -3
  164. package/lib/utils/api.js +52 -21
  165. package/lib/utils/app-path-resolve.js +72 -24
  166. package/lib/utils/app-register-config.js +4 -3
  167. package/lib/utils/authentication-shape.js +3 -1
  168. package/lib/utils/cli-sync-options.js +59 -3
  169. package/lib/utils/cli-utils.js +11 -3
  170. package/lib/utils/deployment-validation.js +28 -3
  171. package/lib/utils/dev-ca-install.js +5 -6
  172. package/lib/utils/dev-config.js +2 -2
  173. package/lib/utils/dev-list-sort.js +145 -0
  174. package/lib/utils/dev-platform-tls.js +0 -63
  175. package/lib/utils/docker-build.js +46 -27
  176. package/lib/utils/docker-daemon-tls-ca.js +1 -1
  177. package/lib/utils/docker-exec.js +91 -6
  178. package/lib/utils/docker-run-ephemeral.js +55 -0
  179. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  180. package/lib/utils/env-copy-keycloak.js +196 -0
  181. package/lib/utils/env-copy.js +35 -4
  182. package/lib/utils/error-formatters/error-parser.js +0 -3
  183. package/lib/utils/external-env-template.js +1 -1
  184. package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
  185. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  186. package/lib/utils/health-check-request.js +2 -3
  187. package/lib/utils/help-builder.js +4 -0
  188. package/lib/utils/image-name.js +22 -4
  189. package/lib/utils/logger.js +37 -4
  190. package/lib/utils/manifest-source-emit.js +1 -1
  191. package/lib/utils/paths.js +98 -26
  192. package/lib/utils/pipeline-upload-auth-retry.js +19 -3
  193. package/lib/utils/postgres-platform-bootstrap.js +24 -17
  194. package/lib/utils/postgres-wipe.js +5 -2
  195. package/lib/utils/remote-dev-auth.js +39 -4
  196. package/lib/utils/resolve-docker-image-ref.js +36 -6
  197. package/lib/utils/role-assistant-paths.js +98 -0
  198. package/lib/utils/schema-loader.js +41 -0
  199. package/lib/utils/schema-resolver.js +36 -4
  200. package/lib/utils/token-manager.js +47 -22
  201. package/lib/utils/upload-sync-options.js +17 -3
  202. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  203. package/lib/validation/external-manifest-validator.js +31 -8
  204. package/lib/validation/openapi-contract-surface-validator.js +4 -0
  205. package/lib/validation/validate-external-file.js +8 -1
  206. package/lib/validation/validator.js +6 -5
  207. package/package.json +35 -12
  208. package/scripts/export-builder-api-evidence.js +3 -3
  209. package/scripts/lib/plan-actor.js +2 -2
  210. package/scripts/lib/plan-meta.js +1 -1
  211. package/scripts/lib/plan-parse.js +1 -1
  212. package/scripts/lib/plan-prevalidate-gates.js +12 -1
  213. package/scripts/lib/plan-run-report.js +13 -2
  214. package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
  215. package/templates/applications/dataplane/application.yaml +47 -17
  216. package/templates/applications/dataplane/env.template +28 -9
  217. package/templates/applications/dataplane/rbac.yaml +78 -2
  218. package/templates/applications/miso-controller/application.yaml +10 -1
  219. package/templates/applications/miso-controller/env.template +13 -8
  220. package/templates/applications/miso-controller/rbac.yaml +72 -0
  221. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  222. package/templates/channels/chatgpt/integration/README.md +10 -7
  223. package/templates/channels/chatgpt/integration/application.json +2 -2
  224. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  225. package/templates/channels/chatgpt/integration/env.template +1 -1
  226. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  227. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  228. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  229. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  230. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  231. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  232. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  233. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  234. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  235. package/templates/channels/slack/integration/slack-system.json +60 -2
  236. package/templates/python/requirements.txt +1 -1
@@ -15,6 +15,11 @@ const {
15
15
  SYSTEM_KIND_ENTERPRISE_KNOWLEDGE,
16
16
  getEnterpriseKnowledgePath
17
17
  } = require('./enterprise-knowledge-paths');
18
+ const {
19
+ WORKSPACE_ROLE_ASSISTANT,
20
+ SYSTEM_KIND_DIGITAL_WORKER,
21
+ getRoleAssistantPath
22
+ } = require('./role-assistant-paths');
18
23
 
19
24
  const WORKSPACE_INTEGRATION = 'integration';
20
25
 
@@ -62,10 +67,14 @@ function normalizeWorkspaceOverride(raw) {
62
67
  return null;
63
68
  }
64
69
  const v = String(raw).trim().toLowerCase();
65
- if (v === WORKSPACE_INTEGRATION || v === WORKSPACE_ENTERPRISE_KNOWLEDGE) {
70
+ if (
71
+ v === WORKSPACE_INTEGRATION ||
72
+ v === WORKSPACE_ENTERPRISE_KNOWLEDGE ||
73
+ v === WORKSPACE_ROLE_ASSISTANT
74
+ ) {
66
75
  return v;
67
76
  }
68
- throw new Error(`Unknown workspace "${raw}". Use integration or enterprise-knowledge.`);
77
+ throw new Error(`Unknown workspace "${raw}". Use integration, enterprise-knowledge, or role-assistant.`);
69
78
  }
70
79
 
71
80
  function workspacePair(appPath, baseDir) {
@@ -76,47 +85,84 @@ function resolveFromWorkspaceOverride(workspaceOverride, appName, getIntegration
76
85
  if (workspaceOverride === WORKSPACE_ENTERPRISE_KNOWLEDGE) {
77
86
  return workspacePair(getEnterpriseKnowledgePath(appName), WORKSPACE_ENTERPRISE_KNOWLEDGE);
78
87
  }
88
+ if (workspaceOverride === WORKSPACE_ROLE_ASSISTANT) {
89
+ return workspacePair(getRoleAssistantPath(appName), WORKSPACE_ROLE_ASSISTANT);
90
+ }
79
91
  if (workspaceOverride === WORKSPACE_INTEGRATION) {
80
92
  return workspacePair(getIntegrationPath(appName), WORKSPACE_INTEGRATION);
81
93
  }
82
94
  return null;
83
95
  }
84
96
 
85
- function resolveAmbiguousWorkspace(ekPath, intPath, explicitKind, appName) {
86
- const kind =
87
- explicitKind ||
88
- readSystemKindFromAppDir(ekPath, appName) ||
89
- readSystemKindFromAppDir(intPath, appName);
97
+ function resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath) {
90
98
  if (kind === SYSTEM_KIND_ENTERPRISE_KNOWLEDGE) {
91
99
  return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
92
100
  }
101
+ if (kind === SYSTEM_KIND_DIGITAL_WORKER) {
102
+ return workspacePair(roleAssistantPath, WORKSPACE_ROLE_ASSISTANT);
103
+ }
93
104
  return workspacePair(intPath, WORKSPACE_INTEGRATION);
94
105
  }
95
106
 
96
- function resolveFromExistingPaths(ekPath, intPath, ekExists, intExists, explicitKind, appName) {
97
- if (ekExists && !intExists) {
98
- return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
99
- }
100
- if (intExists && !ekExists) {
107
+ function resolveAmbiguousWorkspace(ekPath, intPath, roleAssistantPath, explicitKind, appName) {
108
+ const kind =
109
+ explicitKind ||
110
+ readSystemKindFromAppDir(roleAssistantPath, appName) ||
111
+ readSystemKindFromAppDir(ekPath, appName) ||
112
+ readSystemKindFromAppDir(intPath, appName);
113
+ return resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath);
114
+ }
115
+
116
+ function resolveFromExistingPaths(paths, existence, explicitKind, appName) {
117
+ const { ekPath, intPath, roleAssistantPath } = paths;
118
+ const { ekExists, intExists, roleAssistantExists } = existence;
119
+ const existingCount = [ekExists, intExists, roleAssistantExists].filter(Boolean).length;
120
+ if (existingCount === 1) {
121
+ if (ekExists) {
122
+ return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
123
+ }
124
+ if (roleAssistantExists) {
125
+ return workspacePair(roleAssistantPath, WORKSPACE_ROLE_ASSISTANT);
126
+ }
101
127
  return workspacePair(intPath, WORKSPACE_INTEGRATION);
102
128
  }
103
- if (ekExists && intExists) {
104
- return resolveAmbiguousWorkspace(ekPath, intPath, explicitKind, appName);
129
+ if (existingCount > 1) {
130
+ return resolveAmbiguousWorkspace(ekPath, intPath, roleAssistantPath, explicitKind, appName);
105
131
  }
106
- const kind = explicitKind || readSystemKindFromAppDir(intPath, appName);
107
- if (kind === SYSTEM_KIND_ENTERPRISE_KNOWLEDGE) {
108
- return workspacePair(ekPath, WORKSPACE_ENTERPRISE_KNOWLEDGE);
109
- }
110
- return workspacePair(intPath, WORKSPACE_INTEGRATION);
132
+ const kind =
133
+ explicitKind ||
134
+ readSystemKindFromAppDir(roleAssistantPath, appName) ||
135
+ readSystemKindFromAppDir(ekPath, appName) ||
136
+ readSystemKindFromAppDir(intPath, appName);
137
+ return resolveWorkspaceFromSystemKind(kind, ekPath, intPath, roleAssistantPath);
138
+ }
139
+
140
+ function resolveFromExistingPathsBackwardCompatible(
141
+ ekPath,
142
+ intPath,
143
+ roleAssistantPath,
144
+ explicitKind,
145
+ appName
146
+ ) {
147
+ return resolveFromExistingPaths(
148
+ { ekPath, intPath, roleAssistantPath },
149
+ {
150
+ ekExists: isExistingDirectory(ekPath),
151
+ intExists: isExistingDirectory(intPath),
152
+ roleAssistantExists: isExistingDirectory(roleAssistantPath)
153
+ },
154
+ explicitKind,
155
+ appName
156
+ );
111
157
  }
112
158
 
113
159
  /**
114
160
  * Resolves authoring tree for an external system or Enterprise Knowledge anchor.
115
161
  * @param {string} appName - App or anchor key
116
162
  * @param {Object} [options]
117
- * @param {string} [options.workspace] - integration | enterprise-knowledge
163
+ * @param {string} [options.workspace] - integration | enterprise-knowledge | role-assistant
118
164
  * @param {string} [options.systemKind] - Explicit systemKind override
119
- * @returns {{ appPath: string, baseDir: 'integration' | 'enterprise-knowledge' }}
165
+ * @returns {{ appPath: string, baseDir: 'integration' | 'enterprise-knowledge' | 'role-assistant' }}
120
166
  */
121
167
  function resolveAppWorkspace(appName, options = {}) {
122
168
  if (!appName || typeof appName !== 'string') {
@@ -137,11 +183,11 @@ function resolveAppWorkspace(appName, options = {}) {
137
183
 
138
184
  const ekPath = getEnterpriseKnowledgePath(appName);
139
185
  const intPath = getIntegrationPath(appName);
140
- return resolveFromExistingPaths(
186
+ const roleAssistantPath = getRoleAssistantPath(appName);
187
+ return resolveFromExistingPathsBackwardCompatible(
141
188
  ekPath,
142
189
  intPath,
143
- isExistingDirectory(ekPath),
144
- isExistingDirectory(intPath),
190
+ roleAssistantPath,
145
191
  explicitKind,
146
192
  appName
147
193
  );
@@ -160,7 +206,9 @@ function resolveExternalSystemPath(appName, options = {}) {
160
206
  module.exports = {
161
207
  WORKSPACE_INTEGRATION,
162
208
  WORKSPACE_ENTERPRISE_KNOWLEDGE,
209
+ WORKSPACE_ROLE_ASSISTANT,
163
210
  SYSTEM_KIND_ENTERPRISE_KNOWLEDGE,
211
+ SYSTEM_KIND_DIGITAL_WORKER,
164
212
  readSystemKindFromAppDir,
165
213
  normalizeWorkspaceOverride,
166
214
  resolveAppWorkspace,
@@ -228,11 +228,11 @@ async function extractExternalAppConfiguration(appKey, variables, appKeyFromFile
228
228
  * @returns {Object} Webapp configuration
229
229
  */
230
230
  function extractWebappConfiguration(variables, appKeyFromFile, displayName, description, options) {
231
- const appType = variables.build?.language === 'typescript' ? 'webapp' : 'service';
231
+ const appType = variables.app?.type || 'webapp';
232
232
  const registryMode = variables.image?.registryMode || 'external';
233
233
  const port = options.port ?? getContainerPort(variables);
234
234
  const localPort = port;
235
- const language = variables.build?.language || 'typescript';
235
+ const language = variables.build?.language || variables.app?.language || 'typescript';
236
236
  const image = buildImageReference(variables, appKeyFromFile);
237
237
  const url = variables.app?.url || variables.deployment?.dataplaneUrl || variables.deployment?.appUrl || null;
238
238
 
@@ -252,7 +252,8 @@ function extractWebappConfiguration(variables, appKeyFromFile, displayName, desc
252
252
 
253
253
  async function extractAppConfiguration(variables, appKey, options) {
254
254
  const appKeyFromFile = variables.app?.key || appKey;
255
- const displayName = variables.app?.name || options.name || appKey;
255
+ const displayName =
256
+ variables.app?.displayName || variables.app?.name || options.name || appKey;
256
257
  const description = variables.app?.description || '';
257
258
 
258
259
  if (variables.app?.type === 'external') {
@@ -64,7 +64,9 @@ function buildFlatCredentialTemplate(systemKey, authType) {
64
64
  security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
65
65
  },
66
66
  apikey: {
67
- variables: { baseUrl: base, headerName: 'X-API-Key' },
67
+ // Match credential-field-registry placeholders (Authorization + Bearer).
68
+ // Integrations that need a literal custom header set headerName after repair.
69
+ variables: { baseUrl: base, headerName: 'Authorization', prefix: 'Bearer' },
68
70
  security: { apiKey: kvPath('apiKey') }
69
71
  },
70
72
  bearerToken: {
@@ -6,9 +6,60 @@
6
6
 
7
7
  'use strict';
8
8
 
9
+ /**
10
+ * True when `node` looks like a Commander command (or a test double), not a plain opts bag.
11
+ *
12
+ * @param {*} node
13
+ * @returns {boolean}
14
+ */
15
+ function isCommanderLike(node) {
16
+ if (!node || typeof node !== 'object') {
17
+ return false;
18
+ }
19
+ return (
20
+ Array.isArray(node.rawArgs) ||
21
+ typeof node.opts === 'function' ||
22
+ (Object.prototype.hasOwnProperty.call(node, 'parent') &&
23
+ (node.parent === null ||
24
+ node.parent === undefined ||
25
+ typeof node.parent === 'object'))
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Collect argv-like arrays from a Commander command and its parents.
31
+ * Subcommand `rawArgs` is often empty; the flag lives on the root command.
32
+ * Safely no-ops for null/undefined or plain options objects (not Command-like).
33
+ *
34
+ * @param {import('commander').Command|null|undefined} cmd
35
+ * @returns {string[]}
36
+ */
37
+ function collectCommanderRawArgs(cmd) {
38
+ if (!isCommanderLike(cmd)) {
39
+ return [];
40
+ }
41
+ const chunks = [];
42
+ const seen = new Set();
43
+ let current = cmd;
44
+ while (isCommanderLike(current)) {
45
+ if (seen.has(current)) {
46
+ break;
47
+ }
48
+ seen.add(current);
49
+ if (Array.isArray(current.rawArgs) && current.rawArgs.length > 0) {
50
+ chunks.push(...current.rawArgs);
51
+ }
52
+ current = current.parent;
53
+ }
54
+ return chunks;
55
+ }
56
+
9
57
  /**
10
58
  * True when the user passed `--no-publish` or set `noPublish: true` (programmatic).
11
59
  *
60
+ * Commander maps `.option('--no-publish', …)` to `publish: false` (default `true`),
61
+ * not `noPublish: true`. Detect that shape as well as programmatic `noPublish`.
62
+ *
12
63
  * @param {Object|null|undefined} options - Commander-parsed options
13
64
  * @param {import('commander').Command} [cmd] - Commander command (for rawArgs fallback)
14
65
  * @returns {boolean}
@@ -20,10 +71,15 @@ function cliOptsSkipPublish(options, cmd) {
20
71
  if (options.noPublish === true) {
21
72
  return true;
22
73
  }
23
- const raw = cmd && Array.isArray(cmd.rawArgs) ? cmd.rawArgs : [];
24
- return raw.includes('--no-publish');
74
+ // Commander `--no-X` → property `X` defaults true; flag sets it false
75
+ if (options.publish === false) {
76
+ return true;
77
+ }
78
+ return collectCommanderRawArgs(cmd).includes('--no-publish');
25
79
  }
26
80
 
27
81
  module.exports = {
28
- cliOptsSkipPublish
82
+ cliOptsSkipPublish,
83
+ collectCommanderRawArgs,
84
+ isCommanderLike
29
85
  };
@@ -219,9 +219,14 @@ function formatAzureError(errorMsg) {
219
219
  if (errorMsg.includes('Docker build failed') || errorMsg.includes('Build failed: Docker')) {
220
220
  return null;
221
221
  }
222
- // Only match ACR-specific authentication errors, not general authentication failures
223
- if (errorMsg.includes('ACR') || errorMsg.includes('azurecr.io') ||
224
- (errorMsg.includes('authenticate') && (errorMsg.includes('registry') || errorMsg.includes('container')))) {
222
+ const lowerMsg = String(errorMsg || '').toLowerCase();
223
+ const hasAcrDomain = /\b[a-z0-9.-]+\.azurecr\.io\b/i.test(errorMsg);
224
+ const hasAcrKeyword = /\bacr\b/.test(lowerMsg);
225
+ const hasAuthAndRegistryContext =
226
+ /\bauthenticate\b/.test(lowerMsg) &&
227
+ (/\bregistry\b/.test(lowerMsg) || /\bcontainer\b/.test(lowerMsg));
228
+ // Only match ACR-specific authentication errors, not general authentication failures.
229
+ if (hasAcrKeyword || hasAcrDomain || hasAuthAndRegistryContext) {
225
230
  return [
226
231
  ' Azure Container Registry authentication failed.',
227
232
  ' Run: az acr login --name <registry-name>',
@@ -414,6 +419,9 @@ function isAuthenticationError(error) {
414
419
  * @param {string} command - Command that failed
415
420
  */
416
421
  function handleCommandError(error, command) {
422
+ if (error && error.compactGateAbortLogged === true) {
423
+ return;
424
+ }
417
425
  const errorMessages = formatError(error);
418
426
  const skipDoctor = isMissingSecretsErrorMessage(error && error.message);
419
427
  logError(command, errorMessages, { skipDoctor });
@@ -8,6 +8,27 @@
8
8
  * @version 2.0.0
9
9
  */
10
10
 
11
+ /**
12
+ * True when URL is http to a Docker Compose service DNS name (single hostname label).
13
+ * Example: http://miso-controller:3000/miso — used by builder-api in compose, not public HTTP.
14
+ *
15
+ * @param {string} url
16
+ * @returns {boolean}
17
+ */
18
+ function isDockerComposeServiceHttpUrl(url) {
19
+ try {
20
+ const parsed = new URL(url);
21
+ if (parsed.protocol !== 'http:') {
22
+ return false;
23
+ }
24
+ const host = parsed.hostname || '';
25
+ // Compose DNS: one label, no dots (miso-controller, dataplane, …)
26
+ return Boolean(host) && !host.includes('.') && host !== 'localhost';
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
11
32
  /**
12
33
  * Validates and sanitizes controller URL
13
34
  * Enforces HTTPS-only communication for security
@@ -20,9 +41,13 @@ function validateControllerUrl(url) {
20
41
  throw new Error('Controller URL is required and must be a string');
21
42
  }
22
43
 
23
- // Must use HTTPS for security (allow http://localhost for local development)
24
- if (!url.startsWith('https://') && !url.startsWith('http://localhost')) {
25
- throw new Error('Controller URL must use HTTPS (https://) or http://localhost');
44
+ // HTTPS required in public networks; allow localhost and compose-service HTTP for local Docker
45
+ const allowedHttp =
46
+ url.startsWith('http://localhost') || isDockerComposeServiceHttpUrl(url);
47
+ if (!url.startsWith('https://') && !allowedHttp) {
48
+ throw new Error(
49
+ 'Controller URL must use HTTPS (https://), http://localhost, or a Docker Compose service HTTP URL'
50
+ );
26
51
  }
27
52
 
28
53
  // Basic URL format validation
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Dev CA Install – SSL untrusted detection, fetch CA from Builder Server, install into OS trust store.
3
- * Used by `aifabrix dev init` when the server certificate is self-signed. Only /install-ca uses
4
- * rejectUnauthorized: false; all other requests use default TLS verification.
3
+ * Used by `aifabrix dev init` when the server certificate is self-signed.
4
+ * Requests keep TLS verification enabled by default.
5
5
  *
6
6
  * @fileoverview CA install utilities for development Builder Server
7
7
  * @author AI Fabrix Team
@@ -163,7 +163,7 @@ function finalizeInstallCaResponse(res, absoluteUrl, chunks, resolve, reject) {
163
163
  }
164
164
 
165
165
  /**
166
- * GET absolute install-ca URL with insecure TLS; follow redirects (resolve relative Location).
166
+ * GET absolute install-ca URL with TLS verification enabled; follow redirects.
167
167
  * @param {string} absoluteUrl - Full https URL
168
168
  * @param {number} redirectCount - Redirect depth
169
169
  * @returns {Promise<Buffer>} CA PEM
@@ -179,10 +179,9 @@ function fetchInstallCaAbsolute(absoluteUrl, redirectCount) {
179
179
  reject(new Error('install-ca requires https URL'));
180
180
  return;
181
181
  }
182
- const agent = new https.Agent({ rejectUnauthorized: false });
183
182
  const req = https.get(
184
183
  absoluteUrl,
185
- { agent },
184
+ {},
186
185
  (res) => {
187
186
  if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
188
187
  res.destroy();
@@ -208,7 +207,7 @@ function fetchInstallCaAbsolute(absoluteUrl, redirectCount) {
208
207
 
209
208
  /**
210
209
  * Fetch CA PEM from Builder Server via GET {baseUrl}/install-ca.
211
- * Uses rejectUnauthorized: false only for this endpoint (dev setup).
210
+ * Uses default TLS verification for this endpoint.
212
211
  * @param {string} baseUrl - Builder Server base URL (no trailing slash)
213
212
  * @returns {Promise<Buffer>} CA certificate PEM
214
213
  */
@@ -44,8 +44,8 @@ function getDevPorts(developerId) {
44
44
  throw new Error('Developer ID must be a positive number');
45
45
  }
46
46
 
47
- // Handle invalids
48
- if (developerId === undefined || developerId === null || Number.isNaN(developerId)) {
47
+ // Handle invalid numeric values
48
+ if (Number.isNaN(developerId)) {
49
49
  throw new Error('Developer ID must be a positive number');
50
50
  }
51
51
  if (developerId < 0 || !Number.isInteger(developerId)) {
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @fileoverview Client-side sort for aifabrix dev list (--sort field / -field).
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const ALLOWED_SORT_FIELDS = Object.freeze(['id', 'name', 'email', 'until']);
8
+
9
+ /**
10
+ * @param {unknown} id
11
+ * @returns {string}
12
+ */
13
+ function normalizeIdKey(id) {
14
+ const trimmed = String(id ?? '').trim();
15
+ if (!/^\d+$/.test(trimmed)) {
16
+ return trimmed;
17
+ }
18
+ return String(parseInt(trimmed, 10)).padStart(2, '0');
19
+ }
20
+
21
+ /**
22
+ * @param {unknown} a
23
+ * @param {unknown} b
24
+ * @returns {number}
25
+ */
26
+ function compareIds(a, b) {
27
+ const sa = String(a ?? '').trim();
28
+ const sb = String(b ?? '').trim();
29
+ if (/^\d+$/.test(sa) && /^\d+$/.test(sb)) {
30
+ return parseInt(sa, 10) - parseInt(sb, 10);
31
+ }
32
+ return normalizeIdKey(sa).localeCompare(normalizeIdKey(sb), undefined, { sensitivity: 'base' });
33
+ }
34
+
35
+ /**
36
+ * @param {string} a
37
+ * @param {string} b
38
+ * @returns {number}
39
+ */
40
+ function compareText(a, b) {
41
+ return a.localeCompare(b, undefined, { sensitivity: 'base' });
42
+ }
43
+
44
+ /**
45
+ * @param {Object} user
46
+ * @returns {string}
47
+ */
48
+ function untilValue(user) {
49
+ const raw = user && user.certificateValidNotAfter;
50
+ return typeof raw === 'string' ? raw.trim() : '';
51
+ }
52
+
53
+ /**
54
+ * Parse --sort option. Default: name ascending.
55
+ * @param {string|undefined|null} sortOption
56
+ * @returns {{ field: string, descending: boolean }}
57
+ * @throws {Error} When field is empty or not allowed
58
+ */
59
+ function parseSortSpec(sortOption) {
60
+ if (sortOption === undefined || sortOption === null) {
61
+ return { field: 'name', descending: false };
62
+ }
63
+ const trimmed = String(sortOption).trim();
64
+ if (!trimmed) {
65
+ throw new Error(
66
+ 'Invalid --sort value "". Allowed: id, name, email, until (prefix - for descending).'
67
+ );
68
+ }
69
+ let descending = false;
70
+ let fieldPart = trimmed;
71
+ if (fieldPart.startsWith('-')) {
72
+ descending = true;
73
+ fieldPart = fieldPart.slice(1).trim();
74
+ }
75
+ if (!fieldPart) {
76
+ throw new Error(
77
+ 'Invalid --sort value "-". Allowed: id, name, email, until (prefix - for descending).'
78
+ );
79
+ }
80
+ const field = fieldPart.toLowerCase();
81
+ if (!ALLOWED_SORT_FIELDS.includes(field)) {
82
+ throw new Error(
83
+ `Invalid --sort value "${trimmed}". Allowed: id, name, email, until (prefix - for descending).`
84
+ );
85
+ }
86
+ return { field, descending };
87
+ }
88
+
89
+ /**
90
+ * @param {Object} a
91
+ * @param {Object} b
92
+ * @param {string} field
93
+ * @returns {number}
94
+ */
95
+ function compareByField(a, b, field) {
96
+ if (field === 'id') {
97
+ return compareIds(a.id, b.id);
98
+ }
99
+ if (field === 'name') {
100
+ return compareText(String(a.name || ''), String(b.name || ''));
101
+ }
102
+ if (field === 'email') {
103
+ return compareText(String(a.email || ''), String(b.email || ''));
104
+ }
105
+ const ua = untilValue(a);
106
+ const ub = untilValue(b);
107
+ if (!ua && !ub) {
108
+ return 0;
109
+ }
110
+ if (!ua) {
111
+ return 1;
112
+ }
113
+ if (!ub) {
114
+ return -1;
115
+ }
116
+ return compareText(ua, ub);
117
+ }
118
+
119
+ /**
120
+ * Sort developer users without mutating the input array.
121
+ * @param {Object[]} users
122
+ * @param {string|undefined|null} sortOption
123
+ * @returns {Object[]}
124
+ */
125
+ function sortDevUsers(users, sortOption) {
126
+ const { field, descending } = parseSortSpec(sortOption);
127
+ const list = Array.isArray(users) ? [...users] : [];
128
+ list.sort((a, b) => {
129
+ let cmp = compareByField(a, b, field);
130
+ if (descending) {
131
+ cmp = -cmp;
132
+ }
133
+ if (cmp !== 0) {
134
+ return cmp;
135
+ }
136
+ return compareIds(a.id, b.id);
137
+ });
138
+ return list;
139
+ }
140
+
141
+ module.exports = {
142
+ ALLOWED_SORT_FIELDS,
143
+ parseSortSpec,
144
+ sortDevUsers
145
+ };
@@ -8,7 +8,6 @@
8
8
 
9
9
  'use strict';
10
10
 
11
- const https = require('https');
12
11
  const config = require('../core/config');
13
12
  const { isLocalhostPublicSchemeHostname } = require('./localhost-public-host');
14
13
 
@@ -68,65 +67,6 @@ async function shouldAllowInsecureDevPlatformTls(url) {
68
67
  }
69
68
  }
70
69
 
71
- /**
72
- * Minimal fetch for self-signed dev platform HTTPS (Node global fetch cannot set rejectUnauthorized).
73
- *
74
- * @param {string} url
75
- * @param {Object} options
76
- * @returns {Promise<{ status: number, statusText: string, headers: { get: Function }, text: Function, json: Function }>}
77
- */
78
- function nodeHttpsFetchInsecure(url, options = {}) {
79
- return new Promise((resolve, reject) => {
80
- const u = new URL(url);
81
- const method = String(options.method || 'GET').toUpperCase();
82
- const headers = { ...(options.headers || {}) };
83
- const body = options.body;
84
- const reqOpts = {
85
- hostname: u.hostname,
86
- port: u.port || 443,
87
- path: `${u.pathname}${u.search}`,
88
- method,
89
- headers,
90
- rejectUnauthorized: false
91
- };
92
-
93
- const req = https.request(reqOpts, (res) => {
94
- const chunks = [];
95
- res.on('data', (chunk) => chunks.push(chunk));
96
- res.on('end', () => {
97
- const raw = Buffer.concat(chunks).toString('utf8');
98
- const status = res.statusCode || 0;
99
- resolve({
100
- ok: status >= 200 && status < 300,
101
- status,
102
- statusText: res.statusMessage || '',
103
- headers: {
104
- get: (name) => res.headers[String(name || '').toLowerCase()] || null
105
- },
106
- text: async() => raw,
107
- json: async() => JSON.parse(raw)
108
- });
109
- });
110
- });
111
-
112
- req.on('error', reject);
113
- if (options.signal) {
114
- const onAbort = () => {
115
- req.destroy(new Error('The operation was aborted'));
116
- };
117
- if (options.signal.aborted) {
118
- onAbort();
119
- return;
120
- }
121
- options.signal.addEventListener('abort', onAbort, { once: true });
122
- }
123
- if (body !== undefined && body !== null) {
124
- req.write(typeof body === 'string' ? body : String(body));
125
- }
126
- req.end();
127
- });
128
- }
129
-
130
70
  /**
131
71
  * @async
132
72
  * @param {string} url
@@ -134,9 +74,6 @@ function nodeHttpsFetchInsecure(url, options = {}) {
134
74
  * @returns {Promise<Response>}
135
75
  */
136
76
  async function fetchWithOptionalDevPlatformTls(url, options = {}) {
137
- if (await shouldAllowInsecureDevPlatformTls(url)) {
138
- return nodeHttpsFetchInsecure(url, options);
139
- }
140
77
  return fetch(url, options);
141
78
  }
142
79