@aifabrix/builder 2.51.0 → 2.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-container-start.js +4 -0
  14. package/lib/app/run-deploy-job.js +216 -0
  15. package/lib/app/run-env-compose.js +32 -0
  16. package/lib/app/run-env-recovery.js +35 -0
  17. package/lib/app/run-helpers.js +15 -9
  18. package/lib/app/run.js +18 -2
  19. package/lib/channels/add-channel.js +54 -23
  20. package/lib/channels/approval-guides/chatgpt.js +13 -9
  21. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  22. package/lib/channels/channel-artifact-generator.js +14 -2
  23. package/lib/channels/channel-credential.js +3 -1
  24. package/lib/channels/channel-probe-evidence.js +79 -0
  25. package/lib/channels/channel-probe.js +265 -13
  26. package/lib/channels/channel-runtime-resolver.js +120 -29
  27. package/lib/channels/channel-status-display.js +44 -0
  28. package/lib/channels/channel-targets.js +25 -1
  29. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  30. package/lib/channels/microsoft-copilot-package.js +280 -0
  31. package/lib/channels/runtime-tool-catalog.js +165 -0
  32. package/lib/cli/index.js +2 -0
  33. package/lib/cli/infra-guided-footers.js +1 -1
  34. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  35. package/lib/cli/infra-guided.js +65 -56
  36. package/lib/cli/setup-app.help.js +4 -0
  37. package/lib/cli/setup-app.js +4 -0
  38. package/lib/cli/setup-app.test-commands.js +59 -28
  39. package/lib/cli/setup-external-system-help.js +8 -2
  40. package/lib/cli/setup-external-system.js +62 -12
  41. package/lib/commands/app-test.js +2 -14
  42. package/lib/commands/channel-add-setup.js +8 -2
  43. package/lib/commands/channel.js +16 -5
  44. package/lib/commands/datasource-load-export-cli.js +6 -1
  45. package/lib/commands/deploy-gate-failure-details.js +19 -17
  46. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  47. package/lib/commands/dev-down.js +2 -6
  48. package/lib/commands/evidence-kind.js +65 -15
  49. package/lib/commands/governance-generate-external.js +56 -21
  50. package/lib/commands/governance-verify-external.js +2 -5
  51. package/lib/commands/lifecycle-external.js +21 -8
  52. package/lib/commands/repair-datasource-apply.js +10 -1
  53. package/lib/commands/repair-datasource-run.js +4 -2
  54. package/lib/commands/repair-datasource-test-payload.js +80 -19
  55. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  56. package/lib/commands/repair-internal.js +9 -1
  57. package/lib/commands/repair-openapi-sync.js +34 -17
  58. package/lib/commands/role-assistant.js +371 -0
  59. package/lib/commands/test-governance-external.js +2 -5
  60. package/lib/commands/up-miso.js +61 -24
  61. package/lib/commands/upload-data-sync-finalize.js +25 -7
  62. package/lib/commands/upload-sidecar-followups.js +144 -47
  63. package/lib/commands/upload.js +43 -6
  64. package/lib/commands/verify-governance-combined-external.js +73 -9
  65. package/lib/commands/verify-governance-db.js +80 -25
  66. package/lib/commands/verify-governance-external.js +31 -10
  67. package/lib/commands/verify-operations-external.js +2 -5
  68. package/lib/commands/verify-operations-step-plan.js +31 -0
  69. package/lib/commands/verify-trust-command-action.js +2 -5
  70. package/lib/commands/wizard-core-helpers.js +7 -3
  71. package/lib/commands/wizard-core.js +78 -9
  72. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  73. package/lib/datasource/bulk-loader-service.js +5 -1
  74. package/lib/datasource/datasource-identifier.js +4 -3
  75. package/lib/datasource/datasource-validate-summary.js +2 -2
  76. package/lib/datasource/integration-context.js +3 -3
  77. package/lib/datasource/resolve-app.js +90 -8
  78. package/lib/datasource/test-e2e.js +2 -2
  79. package/lib/datasource/unified-validation-run-body.js +15 -1
  80. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  81. package/lib/datasource/validate.js +13 -6
  82. package/lib/external-system/deploy-helpers.js +39 -1
  83. package/lib/external-system/deploy.js +25 -11
  84. package/lib/external-system/download-helpers.js +111 -2
  85. package/lib/external-system/download-role-assistant.js +55 -0
  86. package/lib/external-system/download.js +45 -26
  87. package/lib/external-system/test-helpers.js +1 -1
  88. package/lib/external-system/test-system-level.js +21 -3
  89. package/lib/external-system/test.js +43 -7
  90. package/lib/generator/builders.js +12 -15
  91. package/lib/generator/wizard.js +51 -4
  92. package/lib/lifecycle/access-control-abac.js +121 -0
  93. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  94. package/lib/lifecycle/product-model.js +3 -2
  95. package/lib/lifecycle/report-display.js +14 -2
  96. package/lib/programmatic/capability-registry.js +20 -1
  97. package/lib/programmatic/command-options.js +1 -0
  98. package/lib/programmatic/index.js +4 -0
  99. package/lib/programmatic/manifest-patch-section.js +48 -9
  100. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  101. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  102. package/lib/programmatic/openapi-component-schemas.js +16 -1
  103. package/lib/programmatic/openapi-description-enricher.js +3 -1
  104. package/lib/programmatic/openapi-descriptions.js +55 -5
  105. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  106. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  107. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  108. package/lib/programmatic/openapi-request-schemas.js +36 -1
  109. package/lib/programmatic/openapi-result-schemas.js +1 -1
  110. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  111. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  112. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  113. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  114. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  115. package/lib/programmatic/openapi-spec.js +47 -28
  116. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  117. package/lib/programmatic/request-timeout.js +3 -0
  118. package/lib/programmatic/route-handler-map.js +89 -2
  119. package/lib/programmatic/run-certify.js +15 -2
  120. package/lib/programmatic/run-deploy.js +74 -0
  121. package/lib/programmatic/run-evidence-authoring.js +80 -0
  122. package/lib/programmatic/run-protection.js +124 -0
  123. package/lib/programmatic/run-template-workspace.js +28 -1
  124. package/lib/programmatic/workspace-context.js +6 -0
  125. package/lib/protection/auth-context.js +48 -6
  126. package/lib/protection/paths.js +22 -0
  127. package/lib/protection/run-protection-create-helpers.js +22 -1
  128. package/lib/protection/run-protection-create.js +1 -1
  129. package/lib/protection/sync-after-upload.js +1 -1
  130. package/lib/role-assistant/evidence-sync.js +450 -0
  131. package/lib/role-assistant/github-workflow.js +96 -0
  132. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  133. package/lib/role-assistant/knowledge-sync.js +485 -0
  134. package/lib/role-assistant/settings.js +126 -0
  135. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  136. package/lib/role-assistant/test-cases-honesty.js +184 -0
  137. package/lib/role-assistant/test-cases.js +364 -0
  138. package/lib/role-assistant/test-runner-workhub-answers.js +221 -0
  139. package/lib/role-assistant/test-runner-workhub.js +399 -0
  140. package/lib/role-assistant/test-runner.js +500 -0
  141. package/lib/schema/application-schema.json +24 -28
  142. package/lib/schema/deployment-rules.yaml +1 -0
  143. package/lib/schema/external-datasource.schema.json +38 -4
  144. package/lib/schema/external-system.schema.json +4 -4
  145. package/lib/schema/role-assistant-settings.schema.json +108 -0
  146. package/lib/utils/api.js +52 -27
  147. package/lib/utils/app-path-resolve.js +72 -24
  148. package/lib/utils/authentication-shape.js +3 -1
  149. package/lib/utils/certify-external-workspace.js +40 -0
  150. package/lib/utils/deployment-validation.js +28 -3
  151. package/lib/utils/docker-run-ephemeral.js +115 -0
  152. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  153. package/lib/utils/env-copy-keycloak.js +196 -0
  154. package/lib/utils/env-copy.js +34 -1
  155. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  156. package/lib/utils/health-check-public-warn.js +31 -13
  157. package/lib/utils/health-check.js +11 -1
  158. package/lib/utils/help-builder.js +1 -0
  159. package/lib/utils/paths.js +80 -38
  160. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  161. package/lib/utils/role-assistant-paths.js +160 -0
  162. package/lib/utils/schema-loader.js +41 -0
  163. package/lib/utils/schema-resolver.js +36 -4
  164. package/lib/utils/token-manager.js +38 -29
  165. package/lib/utils/upload-sync-options.js +7 -4
  166. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  167. package/lib/validation/external-manifest-validator.js +31 -8
  168. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  169. package/lib/validation/validate-external-file.js +8 -1
  170. package/lib/validation/validate.js +54 -19
  171. package/package.json +2 -1
  172. package/scripts/export-builder-api-evidence.js +1 -1
  173. package/templates/applications/builder-api/application.yaml +3 -3
  174. package/templates/applications/dataplane/application.yaml +24 -17
  175. package/templates/applications/dataplane/env.template +2 -2
  176. package/templates/applications/dataplane/rbac.yaml +24 -10
  177. package/templates/applications/keycloak/application.yaml +2 -2
  178. package/templates/applications/keycloak/env.template +5 -2
  179. package/templates/applications/miso-controller/application.yaml +3 -3
  180. package/templates/applications/miso-controller/env.template +17 -2
  181. package/templates/channels/chatgpt/install.template.md.hbs +82 -26
  182. package/templates/channels/chatgpt/integration/README.md +11 -7
  183. package/templates/channels/chatgpt/integration/application.json +2 -2
  184. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  185. package/templates/channels/chatgpt/integration/env.template +1 -1
  186. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  187. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  188. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  189. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  190. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  191. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  192. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  193. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  194. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  195. package/templates/channels/slack/install.template.md.hbs +6 -1
  196. package/templates/python/docker-compose.hbs +10 -0
  197. package/templates/typescript/docker-compose.hbs +10 -0
  198. /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
@@ -66,6 +66,17 @@ function buildE2eStep(systemKey, options) {
66
66
  ok: () => true
67
67
  };
68
68
  }
69
+ if (shouldSkipE2eForRoleAssistant(systemKey, options)) {
70
+ return {
71
+ key: 'e2e',
72
+ run: async() => ({
73
+ success: true,
74
+ skipped: true,
75
+ reason: 'role-assistant-auth-none'
76
+ }),
77
+ ok: () => true
78
+ };
79
+ }
69
80
  return {
70
81
  key: 'e2e',
71
82
  run: (progress) =>
@@ -88,6 +99,26 @@ function buildE2eStep(systemKey, options) {
88
99
  };
89
100
  }
90
101
 
102
+ /**
103
+ * Role Assistant packages use authType none + nested builder-api CIP.
104
+ * Live capacity E2E expects a system baseUrl credential and is not the RA certify path.
105
+ * @param {string} systemKey
106
+ * @param {Object} options
107
+ * @returns {boolean}
108
+ */
109
+ function shouldSkipE2eForRoleAssistant(systemKey, options) {
110
+ if (options.forceE2e === true) {
111
+ return false;
112
+ }
113
+ try {
114
+ const { resolveAppWorkspace } = require('../utils/app-path-resolve');
115
+ const { baseDir } = resolveAppWorkspace(systemKey, {});
116
+ return baseDir === 'role-assistant';
117
+ } catch {
118
+ return false;
119
+ }
120
+ }
121
+
91
122
  /**
92
123
  * @param {string} systemKey
93
124
  * @param {Object} options
@@ -17,12 +17,9 @@ const { buildJsonEnvelope } = require('../lifecycle/json-envelope');
17
17
  async function runVerifyTrustCommandAction(appName, options, cmd) {
18
18
  const opts = normalizeTestTrustCliOptions(options, cmd);
19
19
  const pathsUtil = require('../utils/paths');
20
+ const { assertCertifyExternalWorkspace } = require('../utils/certify-external-workspace');
20
21
  const appType = await pathsUtil.detectAppType(appName).catch(() => null);
21
- if (!appType || appType.baseDir !== 'integration') {
22
- throw new Error(
23
- 'verify-trust applies to external integration folders only (integration/<systemKey>/).'
24
- );
25
- }
22
+ assertCertifyExternalWorkspace(appType && appType.baseDir, 'verify-trust');
26
23
 
27
24
  const result = await runVerifyTrustForExternalSystem(appName, {
28
25
  env: opts.env,
@@ -232,15 +232,19 @@ function buildPlatformConfigPayload({ credentialIdOrKey, datasourceKeys, configu
232
232
  }
233
233
 
234
234
  /**
235
- * Extract configuration from API response
235
+ * Extract configuration from API response.
236
+ * add-datasource generation returns datasourceConfig(s) only (systemConfig is null by design).
237
+ * Callers must load the existing workspace system when systemConfig is missing.
236
238
  * @param {Object} generateResponse - API response
237
239
  * @returns {Object} Extracted configuration
238
240
  */
239
241
  function extractConfigurationFromResponse(generateResponse) {
240
- const systemConfig = generateResponse.data?.systemConfig;
242
+ const systemConfig = generateResponse.data?.systemConfig || null;
241
243
  const datasourceConfigs = generateResponse.data?.datasourceConfigs ||
242
244
  (generateResponse.data?.datasourceConfig ? [generateResponse.data.datasourceConfig] : []);
243
- if (!systemConfig) throw new Error('System configuration not found');
245
+ if (!systemConfig && (!Array.isArray(datasourceConfigs) || datasourceConfigs.length === 0)) {
246
+ throw new Error('System configuration not found');
247
+ }
244
248
  return { systemConfig, datasourceConfigs, systemKey: generateResponse.data?.systemKey };
245
249
  }
246
250
 
@@ -312,6 +312,72 @@ async function callGenerateApi(dataplaneUrl, authConfig, options, prefs) {
312
312
  return await generateConfig(dataplaneUrl, authConfig, configPayload);
313
313
  }
314
314
 
315
+ /**
316
+ * Load existing system JSON/YAML from the integration workspace (add-datasource).
317
+ * @param {string} appName - Integration app / system key
318
+ * @returns {Object|null}
319
+ */
320
+ function loadExistingWorkspaceSystemConfig(appName) {
321
+ if (!appName || typeof appName !== 'string') {
322
+ return null;
323
+ }
324
+ const { getIntegrationPath } = require('../utils/paths');
325
+ const { loadConfigFile } = require('../utils/config-format');
326
+ const fsSync = require('fs');
327
+ const appPath = getIntegrationPath(appName);
328
+ for (const ext of ['.json', '.yaml', '.yml']) {
329
+ const candidate = path.join(appPath, `${appName}-system${ext}`);
330
+ if (fsSync.existsSync(candidate)) {
331
+ try {
332
+ return loadConfigFile(candidate);
333
+ } catch {
334
+ return null;
335
+ }
336
+ }
337
+ }
338
+ return null;
339
+ }
340
+
341
+ /**
342
+ * @param {Object|null|undefined} systemConfig
343
+ * @param {Object} options
344
+ * @returns {Object}
345
+ */
346
+ function resolveSystemConfigForWizardMode(systemConfig, options) {
347
+ if (systemConfig) {
348
+ return systemConfig;
349
+ }
350
+ if (options.mode === 'add-datasource') {
351
+ const existing = loadExistingWorkspaceSystemConfig(
352
+ options.appName || options.systemIdOrKey
353
+ );
354
+ if (!existing) {
355
+ throw new Error(
356
+ 'System configuration not found for add-datasource. ' +
357
+ 'Run create-system wizard first so the workspace has a system file.'
358
+ );
359
+ }
360
+ return existing;
361
+ }
362
+ throw new Error('System configuration not found');
363
+ }
364
+
365
+ /**
366
+ * @param {Object} generateResponse
367
+ * @param {Object} prefs
368
+ * @param {Object} options
369
+ * @returns {Promise<void>}
370
+ */
371
+ async function maybeWriteWizardDebugLog(generateResponse, prefs, options) {
372
+ if (!prefs.debug || !options.appName) {
373
+ return;
374
+ }
375
+ const debugLog = generateResponse.data?.debugLog;
376
+ if (debugLog && typeof debugLog === 'string') {
377
+ await writeDebugLog(options.appName, debugLog);
378
+ }
379
+ }
380
+
315
381
  async function handleConfigurationGeneration(dataplaneUrl, authConfig, options) {
316
382
  logger.log(chalk.blue('\n\uD83D\uDCCB Step 5: Generate Configuration'));
317
383
  const prefs = buildConfigPreferences(options.configPrefs);
@@ -320,18 +386,21 @@ async function handleConfigurationGeneration(dataplaneUrl, authConfig, options)
320
386
  const generateResponse = await callGenerateApi(dataplaneUrl, authConfig, options, prefs);
321
387
  spinner.stop();
322
388
  if (!generateResponse.success) {
323
- await saveDebugManifestOnErrorAndThrow(generateResponse, { enableDebug: prefs.debug, appName: options.appName });
389
+ await saveDebugManifestOnErrorAndThrow(generateResponse, {
390
+ enableDebug: prefs.debug,
391
+ appName: options.appName
392
+ });
324
393
  }
325
394
  const result = extractConfigurationFromResponse(generateResponse);
326
- const normalized = normalizeWizardConfigs(result.systemConfig, result.datasourceConfigs);
327
- if (prefs.debug && options.appName) {
328
- const debugLog = generateResponse.data?.debugLog;
329
- if (debugLog && typeof debugLog === 'string') {
330
- await writeDebugLog(options.appName, debugLog);
331
- }
332
- }
395
+ const systemConfig = resolveSystemConfigForWizardMode(result.systemConfig, options);
396
+ const normalized = normalizeWizardConfigs(systemConfig, result.datasourceConfigs);
397
+ await maybeWriteWizardDebugLog(generateResponse, prefs, options);
333
398
  logger.log(formatSuccessLine('Configuration generated successfully'));
334
- return { systemConfig: normalized.systemConfig, datasourceConfigs: normalized.datasourceConfigs, systemKey: result.systemKey };
399
+ return {
400
+ systemConfig: normalized.systemConfig,
401
+ datasourceConfigs: normalized.datasourceConfigs,
402
+ systemKey: result.systemKey || options.systemIdOrKey || options.appName
403
+ };
335
404
  } catch (error) {
336
405
  spinner.stop();
337
406
  throw error;
@@ -70,19 +70,21 @@ function recordBatchSuccess(state, index, response, batchRecords) {
70
70
  * @returns {Promise<{ batches: Object[], totals: Object }>}
71
71
  */
72
72
  async function uploadRecordBatches(params) {
73
- const { chunks, syncType, dataplaneUrl, datasourceKey, authConfig } = params;
73
+ const { chunks, syncType, dataplaneUrl, datasourceKey, authConfig, timeoutMs } = params;
74
74
  const state = {
75
75
  batches: [],
76
76
  totals: { insertedCount: 0, updatedCount: 0, failedCount: 0 },
77
77
  chunkCount: chunks.length
78
78
  };
79
+ const transportOpts =
80
+ timeoutMs !== undefined && timeoutMs !== null ? { timeoutMs } : {};
79
81
 
80
82
  for (let i = 0; i < chunks.length; i += 1) {
81
83
  const batchRecords = chunks[i];
82
84
  const body = { syncType, sync: false, records: batchRecords };
83
85
  try {
84
86
  const response = await withTransientRetry(() =>
85
- bulkSyncRecords(dataplaneUrl, datasourceKey, authConfig, body)
87
+ bulkSyncRecords(dataplaneUrl, datasourceKey, authConfig, body, transportOpts)
86
88
  );
87
89
  recordBatchSuccess(state, i, response, batchRecords);
88
90
  } catch (err) {
@@ -81,12 +81,16 @@ async function executeDatasourceLoadWork(params) {
81
81
  return { ...result, context: ctx };
82
82
  }
83
83
 
84
+ const timeoutMs = options.timeoutMs
85
+ ? parseInt(String(options.timeoutMs), 10)
86
+ : undefined;
84
87
  const { batches, totals } = await uploadRecordBatches({
85
88
  chunks: chunkRecords(records, batchSize),
86
89
  syncType,
87
90
  dataplaneUrl: ctx.dataplaneUrl,
88
91
  datasourceKey: ctx.datasourceKey,
89
- authConfig: ctx.authConfig
92
+ authConfig: ctx.authConfig,
93
+ timeoutMs: Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : undefined
90
94
  });
91
95
 
92
96
  return { ...result, batches, totals, context: ctx };
@@ -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);
@@ -167,10 +167,10 @@ function assembleSummary(parsed) {
167
167
  foreignKeys: fks,
168
168
  dimensionKeys,
169
169
  dimensions,
170
- exposedProfileNames: summarizeExposedProfileNames(parsed),
171
- openapiLine: oa.openapiLine,
172
170
  hasOpenapi: oa.hasOpenapi,
171
+ openapiLine: oa.openapiLine,
173
172
  capabilityKeys: oa.capabilityKeys,
173
+ exposedProfileNames: summarizeExposedProfileNames(parsed),
174
174
  testPayloadLine: summarizeTestPayloadLine(parsed),
175
175
  hasTestPayload: !!parsed.testPayload && typeof parsed.testPayload === 'object',
176
176
  syncLine: summarizeSyncLine(sync)
@@ -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 || [];
@@ -242,9 +242,7 @@ async function validateDatasourceFile(filePathOrKey) {
242
242
  };
243
243
  }
244
244
 
245
- const fieldRefErrors = validateFieldReferences(parsed);
246
- const abacErrors = validateAbac(parsed);
247
- const postSchemaErrors = [...fieldRefErrors, ...abacErrors];
245
+ const postSchemaErrors = collectPostSchemaErrors(parsed);
248
246
  if (postSchemaErrors.length > 0) {
249
247
  return {
250
248
  valid: false,
@@ -264,6 +262,17 @@ async function validateDatasourceFile(filePathOrKey) {
264
262
  };
265
263
  }
266
264
 
265
+ /**
266
+ * @param {object} parsed
267
+ * @returns {string[]}
268
+ */
269
+ function collectPostSchemaErrors(parsed) {
270
+ return [
271
+ ...validateFieldReferences(parsed),
272
+ ...validateAbac(parsed)
273
+ ];
274
+ }
275
+
267
276
  /**
268
277
  * Validates parsed datasource JSON (same rules as {@link validateDatasourceFile}, without reading a file).
269
278
  *
@@ -294,9 +303,7 @@ function validateDatasourceParsed(parsed) {
294
303
  };
295
304
  }
296
305
 
297
- const fieldRefErrors = validateFieldReferences(parsed);
298
- const abacErrors = validateAbac(parsed);
299
- const postSchemaErrors = [...fieldRefErrors, ...abacErrors];
306
+ const postSchemaErrors = collectPostSchemaErrors(parsed);
300
307
  if (postSchemaErrors.length > 0) {
301
308
  return {
302
309
  valid: false,
@@ -133,12 +133,50 @@ function extractSystemKey(systemFilePath) {
133
133
  return systemFileName.replace(/-deploy$/, '');
134
134
  }
135
135
 
136
+ /**
137
+ * Role Assistant (digitalWorker) deploy uses upload, not controller deploy.
138
+ * @async
139
+ * @param {string} appName - Application name
140
+ * @param {Object} options - Deploy options
141
+ * @param {boolean} compact - Quiet UI when true
142
+ * @param {Object} manifest - Controller manifest
143
+ * @returns {Promise<Object|null>} Upload result, or null when not a digital worker
144
+ */
145
+ async function uploadDigitalWorkerIfNeeded(appName, options, _compact, manifest) {
146
+ const systemKind =
147
+ manifest && manifest.system && typeof manifest.system.systemKind === 'string'
148
+ ? manifest.system.systemKind.trim()
149
+ : '';
150
+ if (systemKind !== 'digitalWorker') {
151
+ return null;
152
+ }
153
+ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
154
+ const logger = require('../utils/logger');
155
+ if (options.json !== true) {
156
+ logger.log(
157
+ formatSuccessParagraph(
158
+ 'Role Assistant deployment uses upload pipeline (controller deploy is skipped).'
159
+ )
160
+ );
161
+ }
162
+ const { uploadExternalSystem } = require('../commands/upload');
163
+ return uploadExternalSystem(appName, {
164
+ ...options,
165
+ uploadStatus: 'published',
166
+ // Match standalone `af upload` progress for the final publish.
167
+ silentResolve: false,
168
+ suppressPipelineSpinner: false,
169
+ syncMode: false
170
+ });
171
+ }
172
+
136
173
  module.exports = {
137
174
  loadVariablesYaml,
138
175
  validateSingleSystemFile,
139
176
  validateSystemFiles,
140
177
  validateSingleDatasourceFile,
141
178
  validateDatasourceFiles,
142
- extractSystemKey
179
+ extractSystemKey,
180
+ uploadDigitalWorkerIfNeeded
143
181
  };
144
182
 
@@ -30,6 +30,7 @@ const { displayValidationResults } = require('../validation/validate-display');
30
30
  const { syncDeployJsonFromSources } = require('./sync-deploy-manifest');
31
31
  const { runDeployGateOrchestrator } = require('../commands/deploy-gate-orchestrator');
32
32
  const { runPostPublishDataSync } = require('../commands/run-post-publish-data-sync');
33
+ const { uploadDigitalWorkerIfNeeded } = require('./deploy-helpers');
33
34
 
34
35
  /**
35
36
  * Validates manifest on dataplane (dry-run) before controller deploy.
@@ -342,8 +343,20 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
342
343
  * @param {Object} options - Deployment options
343
344
  * @returns {Promise<Object>} Deployment configuration
344
345
  */
345
- async function prepareDeploymentConfig(appName, _options) {
346
+ async function prepareDeploymentConfig(appName, options = {}) {
346
347
  const { resolveEnvironment } = require('../core/config');
348
+ if (options.authConfig && (options.authConfig.token || options.authConfig.clientId)) {
349
+ const environment = options.environment || (await resolveEnvironment());
350
+ let controllerUrl = options.controllerUrl || '';
351
+ if (!controllerUrl) {
352
+ controllerUrl = await resolveControllerUrl();
353
+ }
354
+ if (!controllerUrl) {
355
+ throw new Error('Controller URL is required. Run "aifabrix login" first.');
356
+ }
357
+ return { environment, controllerUrl, authConfig: options.authConfig };
358
+ }
359
+
347
360
  const environment = await resolveEnvironment();
348
361
  const controllerUrl = await resolveControllerUrl();
349
362
  const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
@@ -413,24 +426,27 @@ async function runExternalSystemDeployFlow(appName, options) {
413
426
  const compact = options.verbose !== true;
414
427
  const { appPath } = await detectAppType(appName);
415
428
  logOfflinePathWhenType(appPath);
416
-
417
429
  if (!compact) {
418
430
  logger.log(chalk.blue(`\nšŸš€ Deploying external system: ${appName}`));
419
431
  }
420
-
421
432
  await validateExternalSystemForDeploy(appName, options, compact);
422
-
423
433
  const gateOutcomes = await runDeployGateOrchestrator(appName, {
424
434
  ...options,
425
- quietDeployUi: compact
435
+ // Compact deploy still shows step progress; only JSON mode stays fully quiet.
436
+ quietDeployUi: options.json === true
426
437
  });
427
-
428
438
  await syncDeployJsonFromSources(appName);
429
-
430
439
  const manifest = await generateControllerManifest(appName, options);
431
-
440
+ const digitalWorkerResult = await uploadDigitalWorkerIfNeeded(
441
+ appName,
442
+ options,
443
+ compact,
444
+ manifest
445
+ );
446
+ if (digitalWorkerResult) {
447
+ return digitalWorkerResult;
448
+ }
432
449
  const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
433
-
434
450
  let dataplaneUrl;
435
451
  try {
436
452
  dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
@@ -439,9 +455,7 @@ async function runExternalSystemDeployFlow(appName, options) {
439
455
  } catch (err) {
440
456
  throw new Error(`Failed to resolve dataplane URL before deploy: ${err.message}`);
441
457
  }
442
-
443
458
  await validateDataplaneManifestForDeploy(manifest, authConfig, dataplaneUrl, compact);
444
-
445
459
  return executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, {
446
460
  ...options,
447
461
  gateOutcomes,