@aifabrix/builder 2.51.1 → 2.52.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 (187) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/app/run-container-start.js +4 -0
  10. package/lib/app/run-deploy-job.js +135 -27
  11. package/lib/app/run-env-compose.js +32 -0
  12. package/lib/app/run-env-recovery.js +35 -0
  13. package/lib/app/run-helpers.js +5 -6
  14. package/lib/app/run.js +11 -3
  15. package/lib/channels/add-channel.js +126 -19
  16. package/lib/channels/approval-guides/chatgpt.js +12 -9
  17. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  18. package/lib/channels/channel-add-ensure.js +128 -0
  19. package/lib/channels/channel-artifact-generator.js +11 -1
  20. package/lib/channels/channel-branding.js +94 -0
  21. package/lib/channels/channel-credential.js +8 -0
  22. package/lib/channels/channel-discovery.js +330 -0
  23. package/lib/channels/channel-external-system.js +1 -1
  24. package/lib/channels/channel-runtime-resolver.js +103 -14
  25. package/lib/channels/channel-status-display.js +7 -1
  26. package/lib/channels/channel-targets.js +18 -1
  27. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  28. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  29. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  30. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  31. package/lib/channels/microsoft-copilot-icons.js +74 -0
  32. package/lib/channels/microsoft-copilot-limits.js +84 -0
  33. package/lib/channels/microsoft-copilot-package.js +130 -49
  34. package/lib/channels/microsoft-copilot-validate.js +197 -0
  35. package/lib/cli/index.js +2 -0
  36. package/lib/cli/infra-guided-footers.js +1 -1
  37. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  38. package/lib/cli/infra-guided.js +78 -62
  39. package/lib/cli/setup-app.help.js +4 -0
  40. package/lib/cli/setup-app.test-commands.js +59 -28
  41. package/lib/cli/setup-external-system.js +50 -12
  42. package/lib/cli/setup-infra.js +31 -4
  43. package/lib/commands/channel-add-setup.js +35 -10
  44. package/lib/commands/channel.js +27 -12
  45. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  46. package/lib/commands/governance-verify-external.js +2 -5
  47. package/lib/commands/integration-definition.js +158 -0
  48. package/lib/commands/lifecycle-external.js +14 -7
  49. package/lib/commands/repair-auth-apply.js +19 -5
  50. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  51. package/lib/commands/role-assistant-test-output.js +200 -0
  52. package/lib/commands/role-assistant.js +131 -114
  53. package/lib/commands/test-governance-external.js +2 -5
  54. package/lib/commands/up-miso.js +61 -24
  55. package/lib/commands/upload-data-sync-finalize.js +20 -3
  56. package/lib/commands/verify-governance-external.js +2 -5
  57. package/lib/commands/verify-operations-external.js +2 -5
  58. package/lib/commands/verify-operations-step-plan.js +31 -0
  59. package/lib/commands/verify-trust-command-action.js +2 -5
  60. package/lib/datasource/capability/capability-resolve.js +15 -5
  61. package/lib/datasource/capability/copy-operations.js +14 -4
  62. package/lib/datasource/datasource-identifier.js +4 -3
  63. package/lib/datasource/integration-context.js +3 -3
  64. package/lib/datasource/resolve-app.js +90 -8
  65. package/lib/datasource/test-e2e.js +2 -2
  66. package/lib/datasource/unified-validation-run-body.js +15 -1
  67. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  68. package/lib/external-system/deploy-helpers.js +9 -5
  69. package/lib/external-system/deploy.js +2 -1
  70. package/lib/external-system/test-system-level.js +21 -3
  71. package/lib/external-system/test.js +4 -4
  72. package/lib/generator/builders.js +9 -15
  73. package/lib/infrastructure/index.js +19 -4
  74. package/lib/integration-definition/apply.js +478 -0
  75. package/lib/integration-definition/canonical-json.js +51 -0
  76. package/lib/integration-definition/emit-suite-bid.js +94 -0
  77. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  78. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  79. package/lib/integration-definition/from-discovery.js +339 -0
  80. package/lib/integration-definition/hash.js +37 -0
  81. package/lib/integration-definition/index.js +37 -0
  82. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  83. package/lib/integration-definition/resource-type-catalog.js +59 -0
  84. package/lib/integration-definition/system-kind-enum.js +40 -0
  85. package/lib/integration-definition/validate.js +148 -0
  86. package/lib/programmatic/builder-api-request.js +5 -1
  87. package/lib/programmatic/builder-help-governance.js +3 -2
  88. package/lib/programmatic/capability-registry.js +19 -0
  89. package/lib/programmatic/openapi-component-schemas.js +6 -1
  90. package/lib/programmatic/openapi-descriptions.js +40 -0
  91. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  92. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  93. package/lib/programmatic/openapi-request-schemas.js +14 -0
  94. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  95. package/lib/programmatic/openapi-result-schemas.js +46 -99
  96. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  97. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  98. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  99. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  100. package/lib/programmatic/openapi-spec.js +11 -1
  101. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  102. package/lib/programmatic/route-handler-map.js +29 -1
  103. package/lib/programmatic/run-channel-package.js +236 -0
  104. package/lib/programmatic/run-integration-definition.js +69 -0
  105. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  106. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  107. package/lib/role-assistant/knowledge-sync.js +104 -89
  108. package/lib/role-assistant/test-cases-bid.js +155 -0
  109. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  110. package/lib/role-assistant/test-cases-honesty.js +24 -10
  111. package/lib/role-assistant/test-cases.js +28 -0
  112. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  113. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  114. package/lib/role-assistant/test-runner-workhub-answers.js +289 -46
  115. package/lib/role-assistant/test-runner-workhub.js +121 -25
  116. package/lib/role-assistant/test-suite.js +119 -0
  117. package/lib/schema/application-schema.json +64 -32
  118. package/lib/schema/external-datasource.schema.json +103 -17
  119. package/lib/schema/external-system.schema.json +54 -3
  120. package/lib/schema/infra.parameter.yaml +0 -20
  121. package/lib/schema/integration-definition.schema.json +195 -0
  122. package/lib/utils/authentication-shape.js +6 -0
  123. package/lib/utils/certify-external-workspace.js +40 -0
  124. package/lib/utils/docker-build.js +3 -3
  125. package/lib/utils/docker-run-ephemeral.js +77 -17
  126. package/lib/utils/health-check-public-warn.js +31 -13
  127. package/lib/utils/health-check.js +11 -1
  128. package/lib/utils/help-builder.js +1 -0
  129. package/lib/utils/paths.js +13 -15
  130. package/lib/utils/promote-dev-docker-image.js +97 -18
  131. package/lib/utils/role-assistant-paths.js +76 -14
  132. package/lib/utils/transient-developer-id.js +28 -0
  133. package/lib/utils/upload-sync-options.js +7 -4
  134. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  135. package/lib/validation/validate.js +54 -19
  136. package/package.json +1 -1
  137. package/scripts/pnpm/af.mjs +24 -9
  138. package/scripts/pnpm/help.mjs +1 -1
  139. package/templates/applications/builder-api/application.yaml +3 -3
  140. package/templates/applications/dataplane/application.yaml +5 -5
  141. package/templates/applications/dataplane/env.template +8 -7
  142. package/templates/applications/dataplane/rbac.yaml +10 -10
  143. package/templates/applications/keycloak/application.yaml +2 -2
  144. package/templates/applications/keycloak/env.template +5 -2
  145. package/templates/applications/miso-controller/application.yaml +25 -11
  146. package/templates/applications/miso-controller/env.template +24 -5
  147. package/templates/channels/README.md +11 -1
  148. package/templates/channels/brand/color-192.png +0 -0
  149. package/templates/channels/brand/dark/128.png +0 -0
  150. package/templates/channels/brand/dark/192.png +0 -0
  151. package/templates/channels/brand/dark/256.png +0 -0
  152. package/templates/channels/brand/dark/32.png +0 -0
  153. package/templates/channels/brand/dark/512.png +0 -0
  154. package/templates/channels/brand/dark/64.png +0 -0
  155. package/templates/channels/brand/light/128.png +0 -0
  156. package/templates/channels/brand/light/192.png +0 -0
  157. package/templates/channels/brand/light/256.png +0 -0
  158. package/templates/channels/brand/light/32.png +0 -0
  159. package/templates/channels/brand/light/512.png +0 -0
  160. package/templates/channels/brand/light/64.png +0 -0
  161. package/templates/channels/brand/mono-white/128.png +0 -0
  162. package/templates/channels/brand/mono-white/192.png +0 -0
  163. package/templates/channels/brand/mono-white/256.png +0 -0
  164. package/templates/channels/brand/mono-white/32.png +0 -0
  165. package/templates/channels/brand/mono-white/512.png +0 -0
  166. package/templates/channels/brand/mono-white/64.png +0 -0
  167. package/templates/channels/brand/outline-32.png +0 -0
  168. package/templates/channels/chatgpt/install.template.md.hbs +76 -36
  169. package/templates/channels/chatgpt/integration/README.md +4 -4
  170. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  171. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  172. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  173. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  174. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  175. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  176. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  177. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  178. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  179. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  180. package/templates/channels/slack/install.template.md.hbs +6 -1
  181. package/templates/channels/slack/integration/README.md +2 -4
  182. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  183. package/templates/channels/teams-bot/integration/README.md +3 -5
  184. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  185. package/templates/python/docker-compose.hbs +10 -0
  186. package/templates/typescript/docker-compose.hbs +10 -0
  187. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -7,7 +7,11 @@
7
7
  */
8
8
 
9
9
  const { rewriteCapabilityReferences } = require('./reference-rewrite');
10
- const { capabilityKeyFromEntry } = require('./capability-manifest');
10
+ const {
11
+ capabilityKeyFromEntry,
12
+ capabilitiesIncludes,
13
+ buildCapabilityObject
14
+ } = require('./capability-manifest');
11
15
  const { jsonPointerPath } = require('./json-pointer');
12
16
  const { buildBasicExposureProfileArray } = require('./basic-exposure');
13
17
  const {
@@ -256,14 +260,20 @@ function appendCapabilityList(doc, resolvedAs, patchOperations, updatedSections)
256
260
  if (!Array.isArray(doc.capabilities)) {
257
261
  doc.capabilities = [];
258
262
  }
259
- if (doc.capabilities.includes(resolvedAs)) {
263
+ if (capabilitiesIncludes(doc.capabilities, resolvedAs)) {
260
264
  return;
261
265
  }
262
- doc.capabilities.push(resolvedAs);
266
+ const usesObjects = doc.capabilities.some(
267
+ (entry) => entry && typeof entry === 'object' && !Array.isArray(entry)
268
+ );
269
+ const value = usesObjects
270
+ ? buildCapabilityObject(resolvedAs, doc.displayName || doc.key || 'records')
271
+ : resolvedAs;
272
+ doc.capabilities.push(value);
263
273
  patchOperations.push({
264
274
  op: 'add',
265
275
  path: '/capabilities/-',
266
- value: resolvedAs
276
+ value
267
277
  });
268
278
  updatedSections.push(`capabilities[]: ${resolvedAs}`);
269
279
  }
@@ -7,7 +7,7 @@
7
7
  'use strict';
8
8
 
9
9
  const path = require('path');
10
- const { getIntegrationPath } = require('../utils/paths');
10
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
11
11
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
12
12
  const { loadConfigFile } = require('../utils/config-format');
13
13
 
@@ -65,12 +65,13 @@ function pickIntegrationAppForDatasourceIdentifier(identifier, appNames) {
65
65
  }
66
66
 
67
67
  /**
68
- * List manifest datasource files for an integration app.
68
+ * List manifest datasource files for an external authoring app
69
+ * (integration/, role-assistant/, or enterprise-knowledge/).
69
70
  * @param {string} appKey
70
71
  * @returns {{ appPath: string, schemaBasePath: string, datasourceFiles: string[] }|null}
71
72
  */
72
73
  function listDatasourceFilesForApp(appKey) {
73
- const appPath = getIntegrationPath(appKey);
74
+ const appPath = resolveExternalSystemPath(appKey);
74
75
  let config;
75
76
  try {
76
77
  const configPath = resolveApplicationConfigPath(appPath);
@@ -6,7 +6,7 @@
6
6
 
7
7
  const path = require('path');
8
8
  const fs = require('fs').promises;
9
- const { getIntegrationPath } = require('../utils/paths');
9
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
10
10
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
11
11
  const { loadConfigFile } = require('../utils/config-format');
12
12
  const {
@@ -15,11 +15,11 @@ const {
15
15
  } = require('./datasource-identifier');
16
16
 
17
17
  /**
18
- * @param {string} appKey - Integration app key
18
+ * @param {string} appKey - Integration / role-assistant / EK app key
19
19
  * @returns {Promise<string>} systemKey
20
20
  */
21
21
  async function getSystemKeyFromAppKey(appKey) {
22
- const appPath = getIntegrationPath(appKey);
22
+ const appPath = resolveExternalSystemPath(appKey);
23
23
  const configPath = resolveApplicationConfigPath(appPath);
24
24
  const config = loadConfigFile(configPath);
25
25
  if (!config.externalIntegration || !config.externalIntegration.systems || config.externalIntegration.systems.length === 0) {
@@ -2,15 +2,20 @@
2
2
  * Resolve app key for datasource commands from explicit --app, cwd, scan, or key parse.
3
3
  * @fileoverview App resolution for datasource test-e2e, test-integration, log-e2e, log-integration
4
4
  * @author AI Fabrix Team
5
- * @version 2.0.0
5
+ * @version 2.1.0
6
6
  */
7
7
 
8
+ 'use strict';
9
+
8
10
  const fs = require('fs');
9
11
  const {
10
- getIntegrationPath,
11
12
  listIntegrationAppNames,
13
+ listEnterpriseKnowledgeAnchors,
14
+ getRoleAssistantRoot,
15
+ getCwdRoleAssistantRoot,
12
16
  resolveIntegrationAppKeyFromCwd
13
17
  } = require('../utils/paths');
18
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
14
19
  const {
15
20
  appHasDatasourceIdentifier,
16
21
  pickIntegrationAppForDatasourceIdentifier
@@ -21,6 +26,80 @@ function appHasDatasourceKey(appKey, datasourceKey) {
21
26
  return appHasDatasourceIdentifier(appKey, datasourceKey);
22
27
  }
23
28
 
29
+ /**
30
+ * @param {string} appKey
31
+ * @returns {boolean}
32
+ */
33
+ function externalAppPathExists(appKey) {
34
+ try {
35
+ const appPath = resolveExternalSystemPath(appKey);
36
+ return Boolean(appPath && fs.existsSync(appPath));
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @param {string} root
44
+ * @param {Set<string>} diskNames
45
+ */
46
+ function addRoleAssistantAppDirs(root, diskNames) {
47
+ if (!root || !fs.existsSync(root)) {
48
+ return;
49
+ }
50
+ let entries;
51
+ try {
52
+ entries = fs.readdirSync(root);
53
+ } catch {
54
+ return;
55
+ }
56
+ const pathJoin = require('path').join;
57
+ for (const name of entries) {
58
+ if (!name || name.startsWith('.')) {
59
+ continue;
60
+ }
61
+ try {
62
+ if (fs.statSync(pathJoin(root, name)).isDirectory()) {
63
+ diskNames.add(name);
64
+ }
65
+ } catch {
66
+ // skip
67
+ }
68
+ }
69
+ }
70
+
71
+ /**
72
+ * List app folders under role-assistant/ (cwd + materialization).
73
+ * @returns {string[]}
74
+ */
75
+ function listRoleAssistantAppNames() {
76
+ const diskNames = new Set();
77
+ const roots = new Set();
78
+ const cwdRoot = getCwdRoleAssistantRoot();
79
+ if (cwdRoot) {
80
+ roots.add(cwdRoot);
81
+ }
82
+ roots.add(getRoleAssistantRoot());
83
+ for (const root of roots) {
84
+ addRoleAssistantAppDirs(root, diskNames);
85
+ }
86
+ return [...diskNames].sort();
87
+ }
88
+
89
+ /**
90
+ * External authoring apps: integration + role-assistant + enterprise-knowledge.
91
+ * @returns {string[]}
92
+ */
93
+ function listExternalAuthoringAppNames() {
94
+ return [
95
+ ...new Set([
96
+ ...listIntegrationAppNames(),
97
+ ...listRoleAssistantAppNames(),
98
+ ...listEnterpriseKnowledgeAnchors()
99
+ ])
100
+ ].sort();
101
+ }
102
+
24
103
  /**
25
104
  * @param {string} key
26
105
  * @param {string} [explicitApp]
@@ -31,7 +110,7 @@ function resolveFromExplicitApp(key, explicitApp) {
31
110
  return null;
32
111
  }
33
112
  const appKey = explicitApp.trim();
34
- if (fs.existsSync(getIntegrationPath(appKey))) {
113
+ if (externalAppPathExists(appKey)) {
35
114
  return { appKey };
36
115
  }
37
116
  return null;
@@ -71,15 +150,16 @@ function resolveFromPrefixOrSegments(key, appNames) {
71
150
  return null;
72
151
  }
73
152
  const candidate = segments.slice(0, -1).join('-');
74
- const candidatePath = getIntegrationPath(candidate);
75
- if (fs.existsSync(candidatePath) && appHasDatasourceIdentifier(candidate, key)) {
153
+ if (externalAppPathExists(candidate) && appHasDatasourceIdentifier(candidate, key)) {
76
154
  return { appKey: candidate };
77
155
  }
78
156
  return null;
79
157
  }
80
158
 
81
159
  const RESOLVE_APP_HINT =
82
- 'Could not determine app context. Use --app <app>, a datasource key, a datasource JSON filename (e.g. myapp-datasource-contacts), or run from integration/<systemKey>/ directory.';
160
+ 'Could not determine app context. Use --app <app>, a datasource key, a datasource JSON filename ' +
161
+ '(e.g. myapp-datasource-contacts), or run from integration/<systemKey>/, ' +
162
+ 'role-assistant/<systemKey>/, or enterprise-knowledge/<systemKey>/.';
83
163
 
84
164
  /**
85
165
  * Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
@@ -108,7 +188,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
108
188
  return { appKey: fromCwd };
109
189
  }
110
190
 
111
- const appNames = listIntegrationAppNames();
191
+ const appNames = listExternalAuthoringAppNames();
112
192
  const scanned = resolveFromAppScan(key, appNames);
113
193
  if (scanned) {
114
194
  return scanned;
@@ -124,5 +204,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
124
204
 
125
205
  module.exports = {
126
206
  resolveAppKeyForDatasource,
127
- appHasDatasourceKey
207
+ appHasDatasourceKey,
208
+ listExternalAuthoringAppNames,
209
+ listRoleAssistantAppNames
128
210
  };
@@ -9,7 +9,6 @@ const path = require('path');
9
9
  const fs = require('fs').promises;
10
10
  const chalk = require('chalk');
11
11
  const logger = require('../utils/logger');
12
- const { getIntegrationPath } = require('../utils/paths');
13
12
  const { resolveAppKeyForDatasource } = require('./resolve-app');
14
13
  const { formatProgress, formatSuccessLine, infoLine } = require('../utils/cli-test-layout-chalk');
15
14
  const { runUnifiedDatasourceValidation } = require('./unified-validation-run');
@@ -94,7 +93,8 @@ async function resolvePrimaryKeyValue(value) {
94
93
  }
95
94
 
96
95
  function e2eIntegrationLogDir(appKey) {
97
- return path.dirname(getIntegrationPath(appKey));
96
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
97
+ return path.dirname(resolveExternalSystemPath(appKey));
98
98
  }
99
99
 
100
100
  /**
@@ -12,6 +12,16 @@ const {
12
12
  const testHelpers = require('../utils/external-system-test-helpers');
13
13
  const { withStageOnValidationRunBody } = require('../utils/validation-run-stage');
14
14
 
15
+ /**
16
+ * @param {Object} datasource
17
+ * @returns {boolean}
18
+ */
19
+ function isVoidCipInput(datasource) {
20
+ const execution = datasource && typeof datasource === 'object' ? datasource.execution : null;
21
+ const cip = execution && typeof execution === 'object' ? execution.cip : null;
22
+ return String((cip && cip.inputType) || '').trim().toLowerCase() === 'void';
23
+ }
24
+
15
25
  /**
16
26
  * @param {Object} params
17
27
  * @param {string} params.systemKey
@@ -31,7 +41,11 @@ async function buildUnifiedValidationBody(params) {
31
41
  const customPayload = await testHelpers.loadCustomPayload(payloadPath);
32
42
  payloadTemplate = testHelpers.determinePayloadTemplate(datasource, datasourceKey, customPayload);
33
43
  if (runType === 'integration' && !payloadTemplate) {
34
- throw new Error(`No test payload found for datasource '${datasourceKey}'`);
44
+ if (isVoidCipInput(datasource)) {
45
+ payloadTemplate = {};
46
+ } else {
47
+ throw new Error(`No test payload found for datasource '${datasourceKey}'`);
48
+ }
35
49
  }
36
50
  }
37
51
 
@@ -4,7 +4,7 @@
4
4
  * @version 2.0.0
5
5
  */
6
6
 
7
- const { getIntegrationPath } = require('../utils/paths');
7
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
8
8
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
9
9
  const { loadConfigFile } = require('../utils/config-format');
10
10
  const {
@@ -48,7 +48,7 @@ function findDatasourceFileByIdentifier(appPath, schemaBasePath, datasourceFiles
48
48
  * @returns {{ datasource: Object, datasourcePath: string, datasourceKey: string }}
49
49
  */
50
50
  function loadDatasourceForApp(appKey, datasourceKey, findDatasourceFileByKey) {
51
- const appPath = getIntegrationPath(appKey);
51
+ const appPath = resolveExternalSystemPath(appKey);
52
52
  const config = loadConfigFile(resolveApplicationConfigPath(appPath));
53
53
  const schemaBasePath = config.externalIntegration?.schemaBasePath || './';
54
54
  const datasourceFiles = config.externalIntegration?.dataSources || [];
@@ -142,7 +142,7 @@ function extractSystemKey(systemFilePath) {
142
142
  * @param {Object} manifest - Controller manifest
143
143
  * @returns {Promise<Object|null>} Upload result, or null when not a digital worker
144
144
  */
145
- async function uploadDigitalWorkerIfNeeded(appName, options, compact, manifest) {
145
+ async function uploadDigitalWorkerIfNeeded(appName, options, _compact, manifest) {
146
146
  const systemKind =
147
147
  manifest && manifest.system && typeof manifest.system.systemKind === 'string'
148
148
  ? manifest.system.systemKind.trim()
@@ -150,9 +150,9 @@ async function uploadDigitalWorkerIfNeeded(appName, options, compact, manifest)
150
150
  if (systemKind !== 'digitalWorker') {
151
151
  return null;
152
152
  }
153
- if (!compact) {
154
- const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
155
- const logger = require('../utils/logger');
153
+ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
154
+ const logger = require('../utils/logger');
155
+ if (options.json !== true) {
156
156
  logger.log(
157
157
  formatSuccessParagraph(
158
158
  'Role Assistant deployment uses upload pipeline (controller deploy is skipped).'
@@ -162,7 +162,11 @@ async function uploadDigitalWorkerIfNeeded(appName, options, compact, manifest)
162
162
  const { uploadExternalSystem } = require('../commands/upload');
163
163
  return uploadExternalSystem(appName, {
164
164
  ...options,
165
- uploadStatus: 'published'
165
+ uploadStatus: 'published',
166
+ // Match standalone `af upload` progress for the final publish.
167
+ silentResolve: false,
168
+ suppressPipelineSpinner: false,
169
+ syncMode: false
166
170
  });
167
171
  }
168
172
 
@@ -432,7 +432,8 @@ async function runExternalSystemDeployFlow(appName, options) {
432
432
  await validateExternalSystemForDeploy(appName, options, compact);
433
433
  const gateOutcomes = await runDeployGateOrchestrator(appName, {
434
434
  ...options,
435
- quietDeployUi: compact
435
+ // Compact deploy still shows step progress; only JSON mode stays fully quiet.
436
+ quietDeployUi: options.json === true
436
437
  });
437
438
  await syncDeployJsonFromSources(appName);
438
439
  const manifest = await generateControllerManifest(appName, options);
@@ -12,7 +12,7 @@ const path = require('path');
12
12
  const chalk = require('chalk');
13
13
  const logger = require('../utils/logger');
14
14
  const { writeTestLog } = require('../utils/test-log-writer');
15
- const { getIntegrationPath } = require('../utils/paths');
15
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
16
16
  const { runUnifiedDatasourceValidation } = require('../datasource/unified-validation-run');
17
17
 
18
18
  async function runOneDatasourceKey(appName, datasourceKey, opts) {
@@ -61,16 +61,34 @@ async function runOneDatasourceKey(appName, datasourceKey, opts) {
61
61
  };
62
62
  }
63
63
  const env = uni.envelope;
64
- const ok = env && typeof env.status === 'string' ? env.status !== 'fail' : false;
64
+ const ok = isDatasourceRunSuccessful(env);
65
65
  return { key: datasourceKey, success: ok, skipped: false, datasourceTestRun: env };
66
66
  } catch (err) {
67
67
  return { key: datasourceKey, success: false, skipped: false, error: err.message };
68
68
  }
69
69
  }
70
70
 
71
+ /**
72
+ * Prefer explicit envelope status; soft-pass when validation+certificate succeeded
73
+ * (documentStorage packs may report overall fail only for missing live evidence).
74
+ * @param {Object|null|undefined} env
75
+ * @returns {boolean}
76
+ */
77
+ function isDatasourceRunSuccessful(env) {
78
+ if (!env || typeof env.status !== 'string') {
79
+ return false;
80
+ }
81
+ if (env.status !== 'fail') {
82
+ return true;
83
+ }
84
+ const validationOk = env.validation && env.validation.status === 'ok';
85
+ const certPassed = env.certificate && env.certificate.status === 'passed';
86
+ return Boolean(validationOk && certPassed);
87
+ }
88
+
71
89
  async function maybeWriteSystemRunLog(appName, systemKey, datasourceKeys, runType, debug, payload) {
72
90
  if (!debug) return;
73
- const appPath = getIntegrationPath(appName);
91
+ const appPath = resolveExternalSystemPath(appName);
74
92
  const integrationDir = path.dirname(appPath);
75
93
  const logPath = await writeTestLog(
76
94
  appName,
@@ -533,8 +533,8 @@ async function testExternalSystemIntegration(appName, options = {}) {
533
533
  if (options.debug) {
534
534
  try {
535
535
  const { writeTestLog } = require('../utils/test-log-writer');
536
- const { getIntegrationPath } = require('../utils/paths');
537
- const appPath = getIntegrationPath(appName);
536
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
537
+ const appPath = resolveExternalSystemPath(appName);
538
538
  await writeTestLog(appName, { request: { systemKey }, error: err.message }, 'test-integration', path.dirname(appPath));
539
539
  } catch (_) { /* ignore */ }
540
540
  }
@@ -561,8 +561,8 @@ async function testExternalSystemIntegration(appName, options = {}) {
561
561
  if (options.debug && results.datasourceResults.length > 0) {
562
562
  try {
563
563
  const { writeTestLog } = require('../utils/test-log-writer');
564
- const { getIntegrationPath } = require('../utils/paths');
565
- const appPath = getIntegrationPath(appName);
564
+ const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
565
+ const appPath = resolveExternalSystemPath(appName);
566
566
  await writeTestLog(appName, {
567
567
  request: { systemKey, datasource: options.datasource, includeDebug: true },
568
568
  response: results
@@ -27,24 +27,18 @@ function sanitizeAuthType(authType) {
27
27
  }
28
28
 
29
29
  /**
30
- * Filters configuration based on registry mode
31
- * When registryMode is "external", only DOCKER_REGISTRY_SERVER_* variables are allowed
30
+ * Historically (incorrectly) stripped all non-DOCKER_REGISTRY_* vars when
31
+ * registryMode was "external", which wiped platform app env/portal config on
32
+ * `aifabrix json`. External only means image pull source — keep full configuration.
33
+ * DOCKER_REGISTRY_SERVER_* may still appear when present in env.template.
34
+ *
32
35
  * @function filterConfigurationByRegistryMode
33
36
  * @param {Array} configuration - Environment configuration
34
- * @param {string} registryMode - Registry mode ('external' or 'internal')
35
- * @returns {Array} Filtered configuration
37
+ * @param {string} _registryMode - Registry mode ('external' or 'acr'); unused
38
+ * @returns {Array} Configuration unchanged
36
39
  */
37
- function filterConfigurationByRegistryMode(configuration, registryMode) {
38
- if (registryMode !== 'external') {
39
- return configuration;
40
- }
41
-
42
- const allowedDockerRegistryVars = [
43
- 'DOCKER_REGISTRY_SERVER_URL',
44
- 'DOCKER_REGISTRY_SERVER_USERNAME',
45
- 'DOCKER_REGISTRY_SERVER_PASSWORD'
46
- ];
47
- return configuration.filter(config => allowedDockerRegistryVars.includes(config.name));
40
+ function filterConfigurationByRegistryMode(configuration, _registryMode) {
41
+ return Array.isArray(configuration) ? configuration : [];
48
42
  }
49
43
 
50
44
  function buildImageReference(variables) {
@@ -203,12 +203,26 @@ async function removeAppVolumes(appNames, devId) {
203
203
  }
204
204
  }
205
205
 
206
+ /**
207
+ * Resolve developer id for stop paths. Optional override is transient (not config).
208
+ * @param {string|number|null|undefined} developerId
209
+ * @returns {Promise<string>}
210
+ */
211
+ async function resolveStopDeveloperId(developerId) {
212
+ if (developerId === undefined || developerId === null || developerId === '') {
213
+ return config.getDeveloperId();
214
+ }
215
+ const { validateAndNormalizeDeveloperId } = require('../core/config-normalize');
216
+ return validateAndNormalizeDeveloperId(developerId);
217
+ }
218
+
206
219
  /**
207
220
  * Stops and removes local infrastructure services and all application containers
208
221
  * on the same network. Cleanly shuts down infra and app containers.
209
222
  *
210
223
  * @async
211
224
  * @function stopInfra
225
+ * @param {string|number|null|undefined} [developerId] - Optional target developer (does not change config)
212
226
  * @returns {Promise<void>} Resolves when infrastructure is stopped
213
227
  * @throws {Error} If Docker compose fails
214
228
  *
@@ -216,8 +230,8 @@ async function removeAppVolumes(appNames, devId) {
216
230
  * await stopInfra();
217
231
  * // All infrastructure and app containers on the same network are stopped and removed
218
232
  */
219
- async function stopInfra() {
220
- const devId = await config.getDeveloperId();
233
+ async function stopInfra(developerId) {
234
+ const devId = await resolveStopDeveloperId(developerId);
221
235
  const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
222
236
  const composePath = path.join(infraDir, 'compose.yaml');
223
237
  const hasCompose = fs.existsSync(composePath);
@@ -279,6 +293,7 @@ async function stopAllAppContainersAndVolumes(devId) {
279
293
  *
280
294
  * @async
281
295
  * @function stopInfraWithVolumes
296
+ * @param {string|number|null|undefined} [developerId] - Optional target developer (does not change config)
282
297
  * @returns {Promise<void>} Resolves when infrastructure is stopped and volumes removed
283
298
  * @throws {Error} If Docker compose fails
284
299
  *
@@ -286,8 +301,8 @@ async function stopAllAppContainersAndVolumes(devId) {
286
301
  * await stopInfraWithVolumes();
287
302
  * // All infrastructure and app containers and volumes are removed
288
303
  */
289
- async function stopInfraWithVolumes() {
290
- const devId = await config.getDeveloperId();
304
+ async function stopInfraWithVolumes(developerId) {
305
+ const devId = await resolveStopDeveloperId(developerId);
291
306
  const { infraDir, adminSecretsPath } = resolveInfraStatePaths(devId);
292
307
  const composePath = path.join(infraDir, 'compose.yaml');
293
308
  const hasCompose = fs.existsSync(composePath);