@aifabrix/builder 2.44.6 → 2.45.5

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 (257) hide show
  1. package/.cursor/rules/cli-layout.mdc +7 -3
  2. package/.cursor/rules/docs-rules.mdc +3 -1
  3. package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
  4. package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
  5. package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
  6. package/jest.projects.js +100 -1
  7. package/lib/api/agent-metadata-validation.api.js +93 -0
  8. package/lib/api/audit-api-helpers.js +68 -0
  9. package/lib/api/audit.api.js +119 -0
  10. package/lib/api/auth-cache.api.js +44 -0
  11. package/lib/api/certificates.api.js +4 -4
  12. package/lib/api/credential.api.js +2 -2
  13. package/lib/api/dataplane-health.api.js +137 -0
  14. package/lib/api/dataplane-sync.api.js +27 -0
  15. package/lib/api/datasources-core.api.js +21 -18
  16. package/lib/api/datasources-extended.api.js +22 -22
  17. package/lib/api/dimensions.api.js +3 -0
  18. package/lib/api/external-systems.api.js +13 -13
  19. package/lib/api/governance-scenarios.api.js +39 -0
  20. package/lib/api/groups.api.js +79 -0
  21. package/lib/api/index.js +45 -2
  22. package/lib/api/pipeline.api.js +4 -4
  23. package/lib/api/protection.api.js +157 -0
  24. package/lib/api/records-bulk.api.js +30 -0
  25. package/lib/api/records-search-parse.js +49 -0
  26. package/lib/api/records-search.api.js +29 -0
  27. package/lib/api/types/agent-metadata-validation.types.js +67 -0
  28. package/lib/api/types/audit.types.js +72 -0
  29. package/lib/api/types/dataplane-health.types.js +27 -0
  30. package/lib/api/types/dimensions.types.js +2 -0
  31. package/lib/api/types/governance-scenarios.types.js +39 -0
  32. package/lib/api/types/protection.types.js +69 -0
  33. package/lib/api/types/records-bulk.types.js +35 -0
  34. package/lib/api/types/records-search.types.js +28 -0
  35. package/lib/api/user-groups.api.js +50 -0
  36. package/lib/api/users.api.js +95 -0
  37. package/lib/api/validation-run.api.js +3 -3
  38. package/lib/api/validation-runner.js +3 -3
  39. package/lib/api/wizard-platform.api.js +3 -3
  40. package/lib/api/wizard.api.js +21 -21
  41. package/lib/app/deploy.js +34 -19
  42. package/lib/app/helpers.js +3 -3
  43. package/lib/app/index.js +3 -3
  44. package/lib/app/register.js +7 -6
  45. package/lib/app/restart-display.js +52 -21
  46. package/lib/app/rotate-secret.js +7 -6
  47. package/lib/app/run-helpers.js +15 -8
  48. package/lib/app/run.js +57 -9
  49. package/lib/app/show-display.js +7 -0
  50. package/lib/app/show.js +87 -5
  51. package/lib/build/index.js +9 -5
  52. package/lib/cli/index.js +4 -2
  53. package/lib/cli/infra-guided.js +62 -28
  54. package/lib/cli/installation-log-command.js +73 -0
  55. package/lib/cli/setup-app.help.js +50 -2
  56. package/lib/cli/setup-app.js +44 -25
  57. package/lib/cli/setup-app.test-commands.js +80 -21
  58. package/lib/cli/setup-auth.js +124 -52
  59. package/lib/cli/setup-external-system.js +37 -3
  60. package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
  61. package/lib/cli/setup-infra-up-platform-action.js +131 -0
  62. package/lib/cli/setup-infra.js +60 -119
  63. package/lib/cli/setup-platform.js +1 -1
  64. package/lib/cli/setup-utility-help-after.js +45 -0
  65. package/lib/cli/setup-utility-resolve.js +132 -0
  66. package/lib/cli/setup-utility.js +148 -116
  67. package/lib/commands/app-logs.js +81 -33
  68. package/lib/commands/auth-config.js +116 -18
  69. package/lib/commands/auth-status-dataplane-version.js +231 -0
  70. package/lib/commands/auth-status-display.js +94 -0
  71. package/lib/commands/auth-status.js +70 -6
  72. package/lib/commands/credential-push.js +10 -5
  73. package/lib/commands/datasource-load-export-cli.js +178 -0
  74. package/lib/commands/datasource-test-trust-cli.js +114 -0
  75. package/lib/commands/datasource-unified-test-cli.js +25 -17
  76. package/lib/commands/datasource-unified-test-cli.options.js +18 -3
  77. package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
  78. package/lib/commands/datasource-verify-audit-cli.js +96 -0
  79. package/lib/commands/datasource.js +68 -1
  80. package/lib/commands/dimension-value.js +43 -13
  81. package/lib/commands/dimension.js +38 -7
  82. package/lib/commands/identity-apply-sync.js +131 -0
  83. package/lib/commands/identity-group-cli.js +146 -0
  84. package/lib/commands/identity-membership-role-cache.js +175 -0
  85. package/lib/commands/identity-shared.js +94 -0
  86. package/lib/commands/identity-user-cli.js +158 -0
  87. package/lib/commands/identity.js +43 -0
  88. package/lib/commands/login.js +46 -16
  89. package/lib/commands/protection-cli-leaves.js +286 -0
  90. package/lib/commands/protection.js +50 -0
  91. package/lib/commands/repair.js +13 -3
  92. package/lib/commands/setup-modes.js +19 -6
  93. package/lib/commands/setup-prompts.js +41 -8
  94. package/lib/commands/setup.js +114 -9
  95. package/lib/commands/teardown.js +54 -5
  96. package/lib/commands/test-e2e-external.js +40 -9
  97. package/lib/commands/test-governance-cli-options.js +47 -0
  98. package/lib/commands/test-governance-command-action.js +55 -0
  99. package/lib/commands/test-governance-external.js +88 -0
  100. package/lib/commands/test-trust-cli-options.js +32 -0
  101. package/lib/commands/test-trust-command-action.js +118 -0
  102. package/lib/commands/test-trust-external.js +127 -0
  103. package/lib/commands/up-common.js +48 -14
  104. package/lib/commands/up-dataplane.js +21 -18
  105. package/lib/commands/up-miso.js +12 -8
  106. package/lib/commands/upload.js +38 -15
  107. package/lib/commands/wizard-core.js +3 -0
  108. package/lib/core/audit-logger.js +1 -34
  109. package/lib/core/config-admin-email.js +56 -0
  110. package/lib/core/config-device-dataplane.js +185 -0
  111. package/lib/core/config-normalize.js +60 -0
  112. package/lib/core/config-registered-controller-urls.js +54 -0
  113. package/lib/core/config.js +33 -50
  114. package/lib/core/secrets-ensure-infra.js +1 -1
  115. package/lib/core/secrets-env-content.js +86 -90
  116. package/lib/core/secrets-env-declarative-expand.js +170 -0
  117. package/lib/core/secrets-env-write.js +2 -0
  118. package/lib/core/secrets-load.js +106 -102
  119. package/lib/datasource/agent-trust-debug-log.js +45 -0
  120. package/lib/datasource/agent-trust-fetch.js +115 -0
  121. package/lib/datasource/agent-trust-map.js +101 -0
  122. package/lib/datasource/agent-trust-mock.js +44 -0
  123. package/lib/datasource/agent-trust-run.js +93 -0
  124. package/lib/datasource/agent-trust-timeout.js +25 -0
  125. package/lib/datasource/audit-evidence-display.js +80 -0
  126. package/lib/datasource/audit-evidence-extract-debug.js +103 -0
  127. package/lib/datasource/audit-evidence-extract.js +177 -0
  128. package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
  129. package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
  130. package/lib/datasource/audit-evidence-poll.js +109 -0
  131. package/lib/datasource/audit-evidence-run.js +177 -0
  132. package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
  133. package/lib/datasource/audit-evidence-verify-matrix.js +177 -0
  134. package/lib/datasource/audit-evidence-verify.js +108 -0
  135. package/lib/datasource/bulk-loader-run-batches.js +88 -0
  136. package/lib/datasource/bulk-loader-service-retry.js +52 -0
  137. package/lib/datasource/bulk-loader-service.js +127 -0
  138. package/lib/datasource/capability/run-capability-dimension.js +2 -1
  139. package/lib/datasource/datasource-exporter-resolve.js +82 -0
  140. package/lib/datasource/datasource-exporter-service.js +144 -0
  141. package/lib/datasource/datasource-identifier.js +148 -0
  142. package/lib/datasource/datasource-load-export-context.js +92 -0
  143. package/lib/datasource/integration-context.js +6 -4
  144. package/lib/datasource/local-data-paths.js +145 -0
  145. package/lib/datasource/log-cleaner.js +241 -0
  146. package/lib/datasource/log-viewer.js +111 -6
  147. package/lib/datasource/record-file-parser.js +85 -0
  148. package/lib/datasource/record-mapper.js +129 -0
  149. package/lib/datasource/resolve-app.js +74 -55
  150. package/lib/datasource/test-e2e.js +26 -2
  151. package/lib/datasource/unified-validation-run-resolve.js +45 -12
  152. package/lib/datasource/unified-validation-run.js +43 -17
  153. package/lib/datasource/validate.js +37 -20
  154. package/lib/external-system/deploy.js +5 -1
  155. package/lib/external-system/test-system-level.js +25 -4
  156. package/lib/external-system/test.js +3 -1
  157. package/lib/governance/governance-pack-loader.js +70 -0
  158. package/lib/governance/governance-run-display.js +119 -0
  159. package/lib/identity/identity-apply-core.js +108 -0
  160. package/lib/identity/identity-apply-phases.js +244 -0
  161. package/lib/identity/identity-apply-service.js +77 -0
  162. package/lib/identity/identity-csv-parser.js +114 -0
  163. package/lib/internal/fs-real-sync.js +29 -1
  164. package/lib/internal/node-fs.js +2 -0
  165. package/lib/protection/auth-context.js +45 -0
  166. package/lib/protection/convert-batch.js +90 -0
  167. package/lib/protection/load.js +41 -0
  168. package/lib/protection/paths.js +158 -0
  169. package/lib/protection/preflight-datasource-ready.js +46 -0
  170. package/lib/protection/protection-create-probe-lines.js +142 -0
  171. package/lib/protection/protection-create-probes.js +95 -0
  172. package/lib/protection/protection-create-scaffold.js +281 -0
  173. package/lib/protection/protection-display-helpers.js +445 -0
  174. package/lib/protection/protection-display.js +215 -0
  175. package/lib/protection/protection-preset-registry.js +97 -0
  176. package/lib/protection/report-exit.js +62 -0
  177. package/lib/protection/resolve.js +202 -0
  178. package/lib/protection/run-commands-validate.js +130 -0
  179. package/lib/protection/run-commands.js +216 -0
  180. package/lib/protection/run-protection-create-helpers.js +134 -0
  181. package/lib/protection/run-protection-create-probes-flow.js +43 -0
  182. package/lib/protection/run-protection-create.js +191 -0
  183. package/lib/protection/scope.js +22 -0
  184. package/lib/protection/sync-after-upload.js +64 -0
  185. package/lib/protection/upload-batch.js +127 -0
  186. package/lib/protection/validate-batch.js +113 -0
  187. package/lib/protection/validate-local.js +65 -0
  188. package/lib/resolvers/dimension-file.js +29 -4
  189. package/lib/schema/application-schema.json +4 -0
  190. package/lib/schema/external-datasource.schema.json +47 -13
  191. package/lib/schema/flag-map-validation-run.json +2 -2
  192. package/lib/schema/infra.parameter.yaml +10 -0
  193. package/lib/schema/protection.schema.json +374 -0
  194. package/lib/schema/type/credential-field-registry.json +636 -0
  195. package/lib/schema/wizard-config.schema.json +1 -1
  196. package/lib/utils/agent-trust-run-display.js +231 -0
  197. package/lib/utils/agent-trust-run-exit.js +59 -0
  198. package/lib/utils/agent-trust-wait-ui.js +94 -0
  199. package/lib/utils/app-config-resolver.js +24 -1
  200. package/lib/utils/applications-config-defaults.js +206 -0
  201. package/lib/utils/auth-config-validator.js +2 -12
  202. package/lib/utils/bash-secret-env.js +1 -1
  203. package/lib/utils/cli-exit-codes.js +30 -0
  204. package/lib/utils/cli-sync-options.js +33 -0
  205. package/lib/utils/compose-generate-docker-compose.js +111 -6
  206. package/lib/utils/compose-generator.js +17 -8
  207. package/lib/utils/controller-url.js +50 -7
  208. package/lib/utils/credential-push-ui.js +90 -0
  209. package/lib/utils/dataplane-cli-version-gate.js +202 -0
  210. package/lib/utils/dataplane-cli-version-help.js +74 -0
  211. package/lib/utils/datasource-export-display.js +137 -0
  212. package/lib/utils/datasource-load-display.js +174 -0
  213. package/lib/utils/datasource-load-export-ui.js +120 -0
  214. package/lib/utils/datasource-test-run-capability-scope.js +3 -3
  215. package/lib/utils/datasource-test-run-display.js +3 -3
  216. package/lib/utils/datasource-test-run-tty-log.js +5 -1
  217. package/lib/utils/env-copy.js +99 -14
  218. package/lib/utils/env-template.js +5 -1
  219. package/lib/utils/health-check-url.js +18 -15
  220. package/lib/utils/health-check.js +7 -5
  221. package/lib/utils/help-builder.js +30 -24
  222. package/lib/utils/infra-optional-service-flags.js +69 -0
  223. package/lib/utils/installation-log-core.js +282 -0
  224. package/lib/utils/installation-log-record.js +237 -0
  225. package/lib/utils/installation-log.js +123 -0
  226. package/lib/utils/log-redaction.js +105 -0
  227. package/lib/utils/manifest-location.js +164 -0
  228. package/lib/utils/manifest-source-emit.js +184 -0
  229. package/lib/utils/paths.js +238 -89
  230. package/lib/utils/remote-secrets-loader.js +7 -1
  231. package/lib/utils/run-cli-flags.js +29 -0
  232. package/lib/utils/secrets-canonical.js +10 -3
  233. package/lib/utils/secrets-path.js +3 -4
  234. package/lib/utils/secrets-utils.js +20 -10
  235. package/lib/utils/semver-compare.js +127 -0
  236. package/lib/utils/system-builder-root.js +10 -2
  237. package/lib/utils/upload-pipeline-ui.js +91 -0
  238. package/lib/utils/url-declarative-public-base.js +80 -12
  239. package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
  240. package/lib/utils/url-declarative-resolve-build.js +24 -393
  241. package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
  242. package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
  243. package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
  244. package/lib/utils/url-declarative-resolve.js +47 -7
  245. package/lib/utils/url-declarative-runtime-base-path.js +21 -1
  246. package/lib/utils/urls-local-registry-scan.js +103 -0
  247. package/lib/utils/urls-local-registry.js +161 -90
  248. package/lib/utils/validation-poll-ui.js +14 -7
  249. package/lib/validation/wizard-config-validator.js +58 -19
  250. package/package.json +3 -1
  251. package/templates/applications/dataplane/application.yaml +4 -0
  252. package/templates/applications/dataplane/env.template +13 -1
  253. package/templates/applications/dataplane/rbac.yaml +4 -0
  254. package/templates/applications/miso-controller/application.yaml +2 -0
  255. package/templates/applications/miso-controller/env.template +27 -29
  256. package/templates/external-system/README.md.hbs +56 -8
  257. package/.npmrc.token +0 -1
@@ -23,6 +23,9 @@ const { handleLogin } = require('../commands/login');
23
23
  const healthCheck = require('../utils/health-check');
24
24
  const { prepareUrlsLocalRegistryForUpPlatform } = require('../commands/up-common');
25
25
  const { assertDevInfraUp } = require('../commands/dev-infra-gate');
26
+ const {
27
+ emitSystemBuilderAppManifestLineIfTTY
28
+ } = require('../utils/manifest-source-emit');
26
29
 
27
30
  const SEPARATOR = '────────────────────────────────────────';
28
31
 
@@ -67,22 +70,23 @@ async function hasErrorLogs(appName, opts = {}) {
67
70
 
68
71
  async function validateAppErrorLogs(appNames) {
69
72
  const names = Array.isArray(appNames) ? appNames : [];
70
- const results = await Promise.all(
71
- names.map(async(name) => {
72
- try {
73
- const hasErrors = await hasErrorLogs(name, { tailLines: 300 });
74
- return { name, hasErrors };
75
- } catch {
76
- return { name, hasErrors: false };
73
+ const bad = [];
74
+ for (const name of names) {
75
+ try {
76
+ const hasErrors = await hasErrorLogs(name, { tailLines: 300 });
77
+ if (hasErrors) {
78
+ bad.push(name);
77
79
  }
78
- })
79
- );
80
- const bad = results.filter(r => r && r.hasErrors);
81
- if (bad.length === 0) return;
82
-
80
+ } catch {
81
+ // ignore per-app log failures
82
+ }
83
+ }
84
+ if (bad.length === 0) {
85
+ return;
86
+ }
83
87
  logger.log(chalk.yellow('⚠ Some services reported error logs'));
84
- for (const r of bad) {
85
- logger.log(chalk.gray(` Run: aifabrix logs ${r.name} -l error`));
88
+ for (const name of bad) {
89
+ logger.log(chalk.gray(` Run: aifabrix logs ${name} -l error`));
86
90
  }
87
91
  logger.log('');
88
92
  }
@@ -158,7 +162,7 @@ async function computeAppBaseUrl(appName) {
158
162
  return joinUrlPath(publicBase, mount);
159
163
  }
160
164
 
161
- // No Traefik front-door routing: publicBase already includes correct scheme/host/port (localhost or remote-server).
165
+ // No Traefik front-door routing: publicBase is localhost + published port unless Traefik/proxy opts into remote.
162
166
  return String(publicBase).replace(/\/+$/, '');
163
167
  }
164
168
 
@@ -226,7 +230,9 @@ async function getInfraHostAndPorts() {
226
230
  const developerId = await config.getDeveloperId();
227
231
  const idNum = typeof developerId === 'string' ? parseInt(developerId, 10) : developerId;
228
232
  const ports = devConfig.getDevPorts(idNum);
229
- const remoteServer = await config.getRemoteServer();
233
+ const cfg = await config.getConfig();
234
+ const traefikOn = Boolean(cfg && cfg.traefik === true);
235
+ const remoteServer = traefikOn ? await config.getRemoteServer() : null;
230
236
  const host = resolveInfraHost(remoteServer);
231
237
  return { idNum, ports, host };
232
238
  }
@@ -308,11 +314,13 @@ async function runGuidedUpDataplane(options, handleUpDataplane) {
308
314
  await runGuidedAuthStep(handleLogin);
309
315
 
310
316
  logger.log('');
317
+ emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
311
318
  const dpSpin = startSpinner('Starting Dataplane...');
312
319
  await withMutedLogger(() =>
313
320
  handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
314
321
  );
315
322
  stopSpinnerSuccess(dpSpin, 'Dataplane ready');
323
+ await refreshDataplaneVersionAfterPlatformInstall();
316
324
  await validateAppErrorLogs(['dataplane']);
317
325
  await logDataplaneReadyFooter();
318
326
  }
@@ -346,6 +354,7 @@ function logGuidedPlatformSetupHeader() {
346
354
  logger.log('');
347
355
  logger.log(title('AI Fabrix Platform Setup'));
348
356
  logger.log(SEPARATOR);
357
+ logger.log(chalk.gray('This may take a few minutes...'));
349
358
  logger.log('');
350
359
  }
351
360
 
@@ -353,43 +362,68 @@ async function runGuidedAuthStep(handleLogin) {
353
362
  const spin = startSpinner('Authenticating...');
354
363
  try {
355
364
  const controllerUrl = await resolveControllerUrl();
356
- await handleLogin({ method: 'device', controller: controllerUrl, environment: 'dev', compact: true });
365
+ await handleLogin({
366
+ method: 'device',
367
+ controller: controllerUrl,
368
+ environment: 'dev',
369
+ compact: true,
370
+ // Dataplane is not up yet during guided platform/dataplane install; refresh after it starts.
371
+ skipDataplaneVersionRefresh: true
372
+ });
357
373
  } catch (authErr) {
358
374
  if (spin) spin.fail('Authentication failed');
359
375
  logger.error(formatBlockingError('Authentication failed'));
360
376
  logger.log(chalk.gray('\nRun:\n af login\n\nThen retry:\n af up-platform\n'));
361
377
  throw authErr;
362
378
  }
363
- // Compact device-login already prints a success line; don't duplicate it.
364
- if (spin) spin.stop();
379
+ // Compact device-login prints "Authentication successful"; close the step spinner cleanly.
380
+ stopSpinnerSuccess(spin, 'Authenticated');
365
381
  }
366
382
 
367
- async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, handleLogin, forceCleanSummary = null) {
368
- logGuidedPlatformSetupHeader();
369
- if (forceCleanSummary && forceCleanSummary.forceSummary) {
370
- logUpPlatformForceCleanSummary(forceCleanSummary.forceSummary, forceCleanSummary.cleanedApps);
383
+ async function refreshDataplaneVersionAfterPlatformInstall() {
384
+ try {
385
+ const { tryRefreshDataplaneVersionAfterLogin } = require('../commands/auth-status-dataplane-version');
386
+ const controllerUrl = await resolveControllerUrl();
387
+ await tryRefreshDataplaneVersionAfterLogin(controllerUrl, {});
388
+ } catch {
389
+ // best-effort only
371
390
  }
391
+ }
372
392
 
373
- await withMutedLogger(() => prepareUrlsLocalRegistryForUpPlatform());
374
-
393
+ async function runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso) {
394
+ emitSystemBuilderAppManifestLineIfTTY(logger, 'keycloak');
375
395
  const kcSpin = startSpinner('Starting Keycloak...');
376
396
  await withMutedLogger(() => handleUpMiso({ ...options, platformInstall: true }));
377
397
  stopSpinnerSuccess(kcSpin, 'Keycloak ready');
378
398
  logger.log('');
379
-
399
+ emitSystemBuilderAppManifestLineIfTTY(logger, 'miso-controller');
380
400
  const mcSpin = startSpinner('Starting Miso Controller...');
381
401
  await waitForAppReady('miso-controller', { timeoutSeconds: 120 });
382
402
  stopSpinnerSuccess(mcSpin, 'Miso Controller ready');
383
403
  logger.log('');
404
+ }
384
405
 
385
- await runGuidedAuthStep(handleLogin);
386
-
406
+ async function runGuidedDataplaneInstallPhase(options, handleUpDataplane) {
387
407
  logger.log('');
408
+ emitSystemBuilderAppManifestLineIfTTY(logger, 'dataplane');
388
409
  const dpSpin = startSpinner('Starting Dataplane...');
389
410
  await withMutedLogger(() =>
390
411
  handleUpDataplane({ ...options, platformInstall: true, skipInfraCheck: true })
391
412
  );
392
413
  stopSpinnerSuccess(dpSpin, 'Dataplane ready');
414
+ await refreshDataplaneVersionAfterPlatformInstall();
415
+ }
416
+
417
+ async function runGuidedUpPlatform(options, handleUpMiso, handleUpDataplane, handleLogin, forceCleanSummary = null) {
418
+ logGuidedPlatformSetupHeader();
419
+ if (forceCleanSummary && forceCleanSummary.forceSummary) {
420
+ logUpPlatformForceCleanSummary(forceCleanSummary.forceSummary, forceCleanSummary.cleanedApps);
421
+ }
422
+
423
+ await withMutedLogger(() => prepareUrlsLocalRegistryForUpPlatform());
424
+ await runGuidedKeycloakAndMisoControllerPhase(options, handleUpMiso);
425
+ await runGuidedAuthStep(handleLogin);
426
+ await runGuidedDataplaneInstallPhase(options, handleUpDataplane);
393
427
  await validateAppErrorLogs(['miso-controller', 'dataplane']);
394
428
  await logPlatformReadyFooter();
395
429
  }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Shared completion hook for infra/platform CLI commands (installation.log).
3
+ *
4
+ * @fileoverview installation.log CLI helper
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const config = require('../core/config');
12
+ const installationLog = require('../utils/installation-log');
13
+
14
+ /**
15
+ * @param {Object} params
16
+ * @param {string} params.command
17
+ * @param {Object} params.options
18
+ * @param {Date} params.startedAt
19
+ * @param {'success'|'failure'} params.outcome
20
+ * @param {Error} [params.error]
21
+ * @param {string[]} [params.platformAppList]
22
+ * @param {Object} [params.cleanup]
23
+ * @param {boolean} [params.upPlatformForce]
24
+ * @param {boolean} [params.omitInfraSection]
25
+ * @returns {Promise<void>}
26
+ */
27
+ async function recordInfraInstallationCommand(params) {
28
+ const {
29
+ command,
30
+ options,
31
+ startedAt,
32
+ outcome,
33
+ error,
34
+ platformAppList,
35
+ cleanup,
36
+ upPlatformForce,
37
+ omitInfraSection
38
+ } = params;
39
+
40
+ const completedAt = new Date();
41
+ let cfg = {};
42
+ try {
43
+ cfg = await config.getConfig();
44
+ } catch {
45
+ cfg = {};
46
+ }
47
+
48
+ try {
49
+ await installationLog.appendInstallationRecord({
50
+ command,
51
+ outcome,
52
+ startedAt,
53
+ completedAt,
54
+ options,
55
+ infra: !omitInfraSection && cfg ? { cfg, options } : undefined,
56
+ platformAppList,
57
+ cleanup,
58
+ upPlatformForce: upPlatformForce === true,
59
+ configExtra: {
60
+ controllerUrl: await installationLog.resolveControllerUrlForLog(),
61
+ adminEmail: await installationLog.resolveAdminEmailPresence()
62
+ },
63
+ error,
64
+ errorCode: error && error.code ? String(error.code) : undefined
65
+ });
66
+ } catch {
67
+ // never block CLI on log failure
68
+ }
69
+ }
70
+
71
+ module.exports = {
72
+ recordInfraInstallationCommand
73
+ };
@@ -56,12 +56,60 @@ Examples:
56
56
  Notes:
57
57
  - To run E2E for one datasource, use:
58
58
  aifabrix datasource test-e2e <datasourceKey>
59
- - External integration: local system and datasource files are published to the dataplane before E2E (same as upload). Use --no-sync to exercise only config already deployed. The old --sync flag is a no-op (kept for scripts).
59
+ - External integration: local system and datasource files are published to the dataplane before E2E (same as upload). Use --no-sync to exercise only config already deployed.
60
+ `;
61
+
62
+ const IDENTITY_HELP_AFTER = `
63
+ Examples:
64
+ # Bulk seed from demo users.csv (test-protection prefix) + sync + cache purge
65
+ $ aifabrix identity apply --file ./users.csv --filter-prefix test-protection -e dev --sync --purge-cache
66
+ $ aifabrix identity sync -e dev --purge-cache
67
+ $ aifabrix identity cache clear
68
+
69
+ # Single user / group
70
+ $ aifabrix identity user create --email alice@example.com --first-name Alice --last-name HR
71
+ $ aifabrix identity group create --name test-protection-sales-fi --description "Sales Finland"
72
+
73
+ Notes:
74
+ - Requires aifabrix login (controller token). See docs/commands/identity-management.md.
75
+ - Prefer identity apply over SQL in tests/fixtures/protection/optional-dev-identity-dataplane.sql.
76
+ `;
77
+
78
+ const TEST_GOVERNANCE_HELP_AFTER = `
79
+ Examples:
80
+ # External system — ABAC visibility acceptance (governance scenario pack)
81
+ $ aifabrix test-governance test-protection
82
+ $ aifabrix test-governance test-protection --pack scenarios/test-protection-v1.yaml
83
+ $ aifabrix test-governance test-protection -v --no-sync
84
+ $ aifabrix test-governance test-protection --scenario country-isolation-maria-fi --json
85
+
86
+ Notes:
87
+ - Separate from test-e2e (vendor connectivity). Does not call external vendor APIs.
88
+ - Load baseline fixtures first (datasource load, protection upload) — not auto-run by this command.
89
+ - Default pack: integration/<systemKey>/scenarios/default.yaml or <systemKey>-v1.yaml
90
+ - Requires governance:evaluate on the operator token (see permissions.md).
91
+ `;
92
+
93
+ const TEST_TRUST_HELP_AFTER = `
94
+ Examples:
95
+ # External system — semantic trust for every datasource (dataplane 404.5)
96
+ $ aifabrix test-trust hubspot
97
+ $ aifabrix test-trust hubspot -v --revalidate
98
+ $ aifabrix test-trust hubspot --no-sync
99
+ $ aifabrix test-trust hubspot --summary --timeout 120000
100
+
101
+ Notes:
102
+ - For one datasource: aifabrix datasource test-trust <datasourceKey>
103
+ - Does not call external APIs (use test-e2e for live proof).
104
+ - Local integration files are published before run unless --no-sync.
60
105
  `;
61
106
 
62
107
  module.exports = {
63
108
  TEST_HELP_AFTER,
64
109
  TEST_INTEGRATION_HELP_AFTER,
65
- TEST_E2E_HELP_AFTER
110
+ TEST_E2E_HELP_AFTER,
111
+ TEST_TRUST_HELP_AFTER,
112
+ TEST_GOVERNANCE_HELP_AFTER,
113
+ IDENTITY_HELP_AFTER
66
114
  };
67
115
 
@@ -16,14 +16,23 @@ const { setupInstallTestE2eLintCommands } = require('./setup-app.test-commands')
16
16
 
17
17
  const CREATE_HELP_AFTER = `
18
18
  Examples:
19
+ # Builder app → builder/<appKey>/
19
20
  $ aifabrix create myapi --type webapp -l typescript
20
- $ aifabrix create mycrm --wizard
21
+ $ aifabrix create mysvc --type api -l python
22
+
23
+ # External system → integration/<systemKey>/ (default --type external)
24
+ $ aifabrix create hubspot --wizard
25
+ $ aifabrix create mycrm --type external --display-name "My CRM" --description "..." \\
26
+ --system-type openapi --auth-type oauth2 --entity-type recordStorage --datasources 1
27
+
28
+ Positional: appKey for builder types; systemKey for --type external (folder under integration/).
29
+ Prefer aifabrix wizard [systemKey] for guided external setup.
21
30
  `;
22
31
 
23
32
  const DEPLOY_HELP_AFTER = `
24
33
  Examples:
25
- $ aifabrix deploy myapp
26
- $ aifabrix deploy myext --local
34
+ $ aifabrix deploy myapp # builder appKey → builder/myapp/
35
+ $ aifabrix deploy hubspot --local # systemKey → integration/hubspot/
27
36
  `;
28
37
 
29
38
  const PUSH_HELP_AFTER = `
@@ -133,8 +142,8 @@ async function handleCreateCommand(appName, options) {
133
142
  }
134
143
 
135
144
  function setupCreateCommand(program) {
136
- program.command('create <app>')
137
- .description('Scaffold builder or external app (flags or --wizard)')
145
+ program.command('create <appKey|systemKey>')
146
+ .description('Create builder app (builder/<appKey>/) or external system (integration/<systemKey>/; --type, flags, or --wizard)')
138
147
  .addHelpText('after', CREATE_HELP_AFTER)
139
148
  .option('-p, --port <port>', 'Application port', '3000')
140
149
  .option('-d, --database', 'Requires database')
@@ -148,7 +157,7 @@ function setupCreateCommand(program) {
148
157
  .option('-g, --github', 'Generate GitHub Actions workflows')
149
158
  .option('--github-steps <steps>', 'Extra GitHub workflow steps (comma-separated, e.g., npm,test)')
150
159
  .option('--main-branch <branch>', 'Main branch name for workflows', 'main')
151
- .option('--wizard', 'Use interactive wizard for external system creation')
160
+ .option('--wizard', 'Run wizard for external system (systemKey → integration/<systemKey>/; same as aifabrix wizard)')
152
161
  .option('--display-name <name>', 'External system display name')
153
162
  .option('--description <desc>', 'External system description')
154
163
  .option('--system-type <type>', 'External system type (openapi, mcp, custom)')
@@ -207,15 +216,25 @@ Examples:
207
216
  $ aifabrix run myapp --env tst
208
217
  $ aifabrix run myapp --tag v1.0.0
209
218
  $ aifabrix run myapp --base
210
- $ aifabrix run myapp --reload`;
211
- program.command('run <app>')
212
- .description('Run app locally or on remote Docker host')
219
+ $ aifabrix run myapp --reload
220
+ $ aifabrix run myapp --no-proxy # same as --proxy false: localhost for Docker declarative public URLs; saves applications.<app>.proxy: false`;
221
+ program.command('run <appKey>')
222
+ .description('Run builder app by appKey locally or on remote Docker host')
213
223
  .option('-p, --port <port>', 'Override local port')
214
224
  .option('-d, --debug', 'Enable debug output with detailed container information')
215
225
  .option('-t, --tag <tag>', 'Image tag to run (e.g. v1.0.0); overrides application.yaml image.tag')
216
226
  .option('-e, --env <env>', 'Environment: dev (default), tst, or pro', 'dev')
217
227
  .option('--base', 'Use manifest base image only (skip local developer-scoped tag preference)')
218
228
  .option('--reload', 'In dev: mount workspace into container (Mutagen only if docker-endpoint is a remote host)')
229
+ .option(
230
+ '--proxy',
231
+ 'Use Traefik/front-door public URL hints when infra has Traefik and application.yaml enables frontDoorRouting (default: on)',
232
+ true
233
+ )
234
+ .option(
235
+ '--no-proxy',
236
+ 'Docker declarative public url://* use localhost + published port (saves applications.<app>.proxy: false); overrides --proxy'
237
+ )
219
238
  .addHelpText('after', runHelp)
220
239
  .action(async(appName, options) => {
221
240
  try {
@@ -228,8 +247,8 @@ Examples:
228
247
  }
229
248
 
230
249
  function setupBuildRunLogsDownCommands(program) {
231
- program.command('build <app>')
232
- .description('Build Docker image (auto-detect runtime)')
250
+ program.command('build <appKey>')
251
+ .description('Build Docker image for builder appKey (auto-detect runtime)')
233
252
  .option('-l, --language <lang>', 'Override language detection')
234
253
  .option('-f, --force-template', 'Force rebuild from template')
235
254
  .option('--no-cache', 'Full Docker rebuild (disable layer cache); use after Dockerfile or context fixes')
@@ -247,8 +266,8 @@ function setupBuildRunLogsDownCommands(program) {
247
266
 
248
267
  registerRunCommand(program);
249
268
 
250
- program.command('logs <app>')
251
- .description('Tail app container logs (optional env summary; secrets masked)')
269
+ program.command('logs <appKey>')
270
+ .description('Tail container logs for appKey (optional env summary; secrets masked)')
252
271
  .option('-f', 'Follow log stream')
253
272
  .option('-t, --tail <lines>', 'Number of lines (default: 100); 0 = full list', '100')
254
273
  .option('-l, --level <level>', 'Show only logs at this level or above (debug|info|warn|error)')
@@ -265,8 +284,8 @@ function setupBuildRunLogsDownCommands(program) {
265
284
  }
266
285
  });
267
286
 
268
- program.command('down-app <app>')
269
- .description('Stop and remove app container (--volumes removes data volume)')
287
+ program.command('down-app <appKey>')
288
+ .description('Stop and remove container for appKey (--volumes removes data volume)')
270
289
  .option('--volumes', 'Remove application Docker volume')
271
290
  .action(async(appName, options) => {
272
291
  try {
@@ -280,8 +299,8 @@ function setupBuildRunLogsDownCommands(program) {
280
299
  }
281
300
 
282
301
  function setupShellTestStopCommands(program) {
283
- program.command('stop <app>')
284
- .description('Alias for down-app: stop and remove container')
302
+ program.command('stop <appKey>')
303
+ .description('Alias for down-app: stop and remove container for appKey')
285
304
  .option('--volumes', 'Remove application Docker volume')
286
305
  .action(async(appName, options) => {
287
306
  try {
@@ -293,8 +312,8 @@ function setupShellTestStopCommands(program) {
293
312
  }
294
313
  });
295
314
 
296
- program.command('shell <app>')
297
- .description('Interactive shell in running or ephemeral container')
315
+ program.command('shell <appKey>')
316
+ .description('Interactive shell in container for builder appKey')
298
317
  .option('--env <env>', 'Environment (dev|tst); dev uses running container', 'dev')
299
318
  .action(async(appName, options) => {
300
319
  try {
@@ -308,8 +327,8 @@ function setupShellTestStopCommands(program) {
308
327
  }
309
328
 
310
329
  function setupDockerfileGenerateCommand(program) {
311
- program.command('dockerfile <app>')
312
- .description('Generate Dockerfile from detected runtime')
330
+ program.command('dockerfile <appKey>')
331
+ .description('Generate Dockerfile for builder appKey from detected runtime')
313
332
  .option('-l, --language <lang>', 'Override language detection')
314
333
  .option('-f, --force', 'Overwrite existing Dockerfile')
315
334
  .action(async(appName, options) => {
@@ -325,8 +344,8 @@ function setupDockerfileGenerateCommand(program) {
325
344
  }
326
345
 
327
346
  function setupPushDeployDockerfileCommands(program) {
328
- program.command('push <app>')
329
- .description('Push image to Azure Container Registry')
347
+ program.command('push <appKey>')
348
+ .description('Push builder appKey image to Azure Container Registry')
330
349
  .addHelpText('after', PUSH_HELP_AFTER)
331
350
  .option('-r, --registry <registry>', 'ACR registry URL (overrides application.yaml)')
332
351
  .option('-t, --tag <tag>', 'Image tag(s) - comma-separated for multiple (default: latest)')
@@ -339,8 +358,8 @@ function setupPushDeployDockerfileCommands(program) {
339
358
  }
340
359
  });
341
360
 
342
- program.command('deploy <app>')
343
- .description('Deploy via Miso Controller (Azure or --local)')
361
+ program.command('deploy <appKey|systemKey>')
362
+ .description('Deploy builder app or external system via Miso Controller (Azure or --local)')
344
363
  .addHelpText('after', DEPLOY_HELP_AFTER)
345
364
  .option('--local', 'Send manifest to controller then run app locally (app: same as aifabrix run <app>; external: restart dataplane)')
346
365
  .option('--client-id <id>', 'Client ID (overrides config)')
@@ -8,11 +8,16 @@ const chalk = require('chalk');
8
8
  const logger = require('../utils/logger');
9
9
  const { handleCommandError } = require('../utils/cli-utils');
10
10
  const { cliOptsSkipCertSync } = require('../certification/cli-cert-sync-skip');
11
- const { TEST_HELP_AFTER, TEST_E2E_HELP_AFTER } = require('./setup-app.help');
11
+ const {
12
+ TEST_HELP_AFTER,
13
+ TEST_E2E_HELP_AFTER,
14
+ TEST_TRUST_HELP_AFTER,
15
+ TEST_GOVERNANCE_HELP_AFTER
16
+ } = require('./setup-app.help');
12
17
 
13
18
  function setupTestCommand(program) {
14
- program.command('test <app>')
15
- .description('Tests: builder in container; external = local validation')
19
+ program.command('test <appKey|systemKey>')
20
+ .description('Tests: builder appKey in container; external systemKey = local validation')
16
21
  .option('-e, --env <env>', 'For builder app: dev (running container) or tst (ephemeral)', 'dev')
17
22
  .option('-v, --verbose', 'Verbose output')
18
23
  .option('-d, --debug', 'Write debug log to integration/<systemKey>/logs/ (external only)')
@@ -84,14 +89,16 @@ function firstIssuanceFailureHintFromE2eResults(results) {
84
89
  * @param {Object} options
85
90
  * @returns {Promise<void>}
86
91
  */
87
- async function runExternalIntegrationE2EAndCertSync(appName, options) {
92
+ async function runExternalIntegrationE2EAndCertSync(appName, options, cmd) {
93
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
88
94
  const { runTestE2EForExternalSystem } = require('../commands/test-e2e-external');
95
+ const noSync = cliOptsSkipSync(options, cmd);
89
96
  const { success, results } = await runTestE2EForExternalSystem(appName, {
90
97
  env: options.env,
91
98
  debug: options.debug,
92
99
  verbose: options.verbose,
93
100
  async: options.async !== false,
94
- noSync: options.noSync === true
101
+ noSync
95
102
  });
96
103
  const { displayIntegrationTestResults } = require('../utils/external-system-display');
97
104
  const datasourceResults = results.map(r => ({
@@ -118,17 +125,19 @@ async function runExternalIntegrationE2EAndCertSync(appName, options) {
118
125
  await trySyncCertificationFromDataplaneForExternalApp(appName, 'test-e2e', { issuanceFailureHint });
119
126
  }
120
127
 
121
- async function runTestE2ECommand(appName, options) {
128
+ async function runTestE2ECommand(appName, options, cmd) {
129
+ const { cliOptsSkipSync } = require('../utils/cli-sync-options');
130
+ const noSync = cliOptsSkipSync(options, cmd);
122
131
  const pathsUtil = require('../utils/paths');
123
132
  const appType = await pathsUtil.detectAppType(appName).catch(() => null);
124
- if (options.noSync === true && appType && appType.baseDir === 'builder') {
133
+ if (noSync && appType && appType.baseDir === 'builder') {
125
134
  throw new Error(
126
135
  'Option --no-sync applies only to external integration E2E (integration/<systemKey>/). ' +
127
136
  'Remove --no-sync for builder app E2E.'
128
137
  );
129
138
  }
130
139
  if (appType && appType.baseDir === 'integration') {
131
- await runExternalIntegrationE2EAndCertSync(appName, options);
140
+ await runExternalIntegrationE2EAndCertSync(appName, options, cmd);
132
141
  return;
133
142
  }
134
143
  const { runAppTestE2e } = require('../commands/app-test');
@@ -136,8 +145,8 @@ async function runTestE2ECommand(appName, options) {
136
145
  }
137
146
 
138
147
  function setupInstallCommand(program) {
139
- program.command('install <app>')
140
- .description('Install deps in container (builder apps only)')
148
+ program.command('install <appKey>')
149
+ .description('Install deps in container (builder appKey only)')
141
150
  .option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
142
151
  .action(async(appName, options) => {
143
152
  try {
@@ -145,7 +154,7 @@ function setupInstallCommand(program) {
145
154
  const appType = await pathsUtil.detectAppType(appName).catch(() => null);
146
155
  if (appType && appType.baseDir === 'integration') {
147
156
  logger.log(
148
- chalk.gray('Install is for builder applications only. Use aifabrix shell <app> to run commands in external setups.')
157
+ chalk.gray('Install is for builder appKey only. Use aifabrix shell <appKey> for builder apps or External Systems commands for integrations.')
149
158
  );
150
159
  return;
151
160
  }
@@ -159,15 +168,11 @@ function setupInstallCommand(program) {
159
168
  }
160
169
 
161
170
  function setupTestE2eCommand(program) {
162
- program.command('test-e2e <app>')
163
- .description('E2E: builder in container; external = all datasources via dataplane')
171
+ program.command('test-e2e <appKey|systemKey>')
172
+ .description('E2E: builder appKey in container; external systemKey = all datasources via dataplane')
164
173
  .option('-e, --env <env>', 'Environment: dev, tst, or pro (builder: dev/tst for container)', 'dev')
165
174
  .option('-v, --verbose', 'Show detailed step output and poll progress')
166
175
  .option('-d, --debug', 'Include debug output and write log to integration/<systemKey>/logs/')
167
- .option(
168
- '--sync',
169
- '(Deprecated; no-op.) Local integration files are published before E2E by default; use --no-sync to skip.'
170
- )
171
176
  .option(
172
177
  '--no-sync',
173
178
  'Skip publishing local integration files; E2E uses the system config already on the dataplane (external integration only)'
@@ -188,7 +193,7 @@ function setupTestE2eCommand(program) {
188
193
  env: envExplicit ? options.env : undefined,
189
194
  async: true // system-level command surface omits --no-async; always poll for completeness
190
195
  };
191
- await runTestE2ECommand(appName, externalOpts);
196
+ await runTestE2ECommand(appName, externalOpts, cmd);
192
197
  } catch (error) {
193
198
  handleCommandError(error, 'test-e2e');
194
199
  process.exit(1);
@@ -197,15 +202,15 @@ function setupTestE2eCommand(program) {
197
202
  }
198
203
 
199
204
  function setupLintCommand(program) {
200
- program.command('lint <app>')
201
- .description('Lint in container (builder apps only)')
205
+ program.command('lint <appKey>')
206
+ .description('Lint in container (builder appKey only)')
202
207
  .option('--env <env>', 'dev (running container) or tst (ephemeral with .env)', 'dev')
203
208
  .action(async(appName, options) => {
204
209
  try {
205
210
  const pathsUtil = require('../utils/paths');
206
211
  const appType = await pathsUtil.detectAppType(appName).catch(() => null);
207
212
  if (appType && appType.baseDir === 'integration') {
208
- logger.log(chalk.gray('lint is for builder applications only. Use aifabrix shell <app> then make lint or pnpm lint.'));
213
+ logger.log(chalk.gray('lint is for builder appKey only. Use aifabrix shell <appKey> then make lint or pnpm lint.'));
209
214
  return;
210
215
  }
211
216
  const { runAppLint } = require('../commands/app-test');
@@ -217,10 +222,64 @@ function setupLintCommand(program) {
217
222
  });
218
223
  }
219
224
 
225
+ function collectScenarioId(value, previous) {
226
+ return previous.concat([value]);
227
+ }
228
+
229
+ function setupTestGovernanceCommand(program) {
230
+ const { testGovernanceCommandHandler } = require('../commands/test-governance-command-action');
231
+ program
232
+ .command('test-governance <appKey|systemKey>')
233
+ .description(
234
+ 'Governance scenario acceptance: ABAC visibility via governed search (key sets only)'
235
+ )
236
+ .option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
237
+ .option('--pack <path>', 'Scenario pack YAML (default: scenarios/default.yaml or <systemKey>-v1.yaml)')
238
+ .option('-a, --app <path>', 'Integration folder override')
239
+ .option(
240
+ '--scenario <id>',
241
+ 'Run subset of scenario ids (repeatable)',
242
+ collectScenarioId,
243
+ []
244
+ )
245
+ .option('--no-sync', 'Skip publishing local integration files before run')
246
+ .option('-v, --verbose', 'Per-scenario verdict lines with subject and counts')
247
+ .option('--json', 'Machine-readable redacted result (same allowlist as API)')
248
+ .addHelpText('after', TEST_GOVERNANCE_HELP_AFTER)
249
+ .action((appName, options, cmd) => testGovernanceCommandHandler(appName, options, cmd));
250
+ }
251
+
252
+ function setupTestTrustCommand(program) {
253
+ const { testTrustCommandHandler } = require('../commands/test-trust-command-action');
254
+ program
255
+ .command('test-trust <appKey|systemKey>')
256
+ .description(
257
+ 'Semantic agent metadata validation for external system (all datasources via dataplane 404.5)'
258
+ )
259
+ .option('-e, --env <env>', 'Environment: dev, tst, or pro', 'dev')
260
+ .option('-v, --verbose', 'Show findings and observed business model')
261
+ .option('-d, --debug', 'Write debug log under integration/<systemKey>/logs/')
262
+ .option('--no-sync', 'Skip publishing local integration files before validate')
263
+ .option('--revalidate', 'Force new validation for each datasource')
264
+ .option(
265
+ '--timeout <ms>',
266
+ 'HTTP timeout for validate/latest requests (ms)',
267
+ '120000'
268
+ )
269
+ .option('--strict', 'Exit 1 unless every datasource is trusted')
270
+ .option('--warnings-as-errors', 'Exit 1 when any datasource is usableWithWarnings')
271
+ .option('--json', 'Print JSON array of trust runs')
272
+ .option('--summary', 'One line per datasource')
273
+ .addHelpText('after', TEST_TRUST_HELP_AFTER)
274
+ .action((appName, options, cmd) => testTrustCommandHandler(appName, options, cmd));
275
+ }
276
+
220
277
  function setupInstallTestE2eLintCommands(program) {
221
278
  setupInstallCommand(program);
222
279
  setupTestCommand(program);
223
280
  setupTestE2eCommand(program);
281
+ setupTestGovernanceCommand(program);
282
+ setupTestTrustCommand(program);
224
283
  setupLintCommand(program);
225
284
  }
226
285