@aifabrix/builder 2.55.0 → 2.56.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 (101) hide show
  1. package/lib/agent-kit/host-approve.js +48 -6
  2. package/lib/agent-kit/init.js +2 -2
  3. package/lib/agent-kit/remote-prompts.js +46 -13
  4. package/lib/agent-kit/run.js +1 -1
  5. package/lib/agent-kit/start.js +310 -0
  6. package/lib/api/pipeline.api.js +32 -3
  7. package/lib/app/deploy.js +13 -9
  8. package/lib/app/offer-create-from-template.js +184 -0
  9. package/lib/cli/setup-app.js +15 -7
  10. package/lib/cli/setup-auth.js +11 -2
  11. package/lib/cli/setup-external-system-help.js +15 -3
  12. package/lib/cli/setup-external-system.js +21 -3
  13. package/lib/commands/agent-kit-help.js +74 -4
  14. package/lib/commands/agent-kit.js +83 -12
  15. package/lib/commands/auth-status-user.js +99 -0
  16. package/lib/commands/auth-status.js +14 -4
  17. package/lib/commands/datasource-load-export-cli.js +7 -2
  18. package/lib/commands/datasource-test-trust-cli.js +5 -1
  19. package/lib/commands/governance-command-action.js +5 -4
  20. package/lib/commands/identity-membership-role-cache.js +6 -1
  21. package/lib/commands/lifecycle-command-action.js +16 -15
  22. package/lib/commands/login-credentials.js +8 -3
  23. package/lib/commands/login-device.js +14 -5
  24. package/lib/commands/login.js +14 -1
  25. package/lib/commands/logout.js +50 -15
  26. package/lib/commands/repair-datasource-auto-rbac.js +4 -9
  27. package/lib/commands/role-assistant.js +24 -12
  28. package/lib/commands/upload-openapi-sync-followup.js +59 -0
  29. package/lib/commands/upload-qualification-client.js +131 -0
  30. package/lib/commands/upload-qualification-followups.js +104 -0
  31. package/lib/commands/upload-qualification-poll.js +143 -0
  32. package/lib/commands/upload.js +37 -156
  33. package/lib/commands/verify-governance-command-action.js +9 -7
  34. package/lib/commands/verify-operations-command-action.js +9 -8
  35. package/lib/commands/verify-trust-command-action.js +6 -5
  36. package/lib/deployment/deploy-poll-ui.js +14 -10
  37. package/lib/deployment/deployer-status.js +4 -4
  38. package/lib/external-system/deploy.js +46 -51
  39. package/lib/external-system/download.js +1 -2
  40. package/lib/generator/helpers.js +14 -46
  41. package/lib/generator/index.js +0 -4
  42. package/lib/integration-definition/apply-static-enterprise.js +5 -2
  43. package/lib/programmatic/builder-api-request.js +8 -4
  44. package/lib/programmatic/capability-registry.js +2 -0
  45. package/lib/programmatic/get-deploy-status.js +45 -0
  46. package/lib/programmatic/get-upload-status.js +45 -0
  47. package/lib/programmatic/http-accepted-response.js +31 -0
  48. package/lib/programmatic/openapi-descriptions.js +14 -4
  49. package/lib/programmatic/request-timeout.js +21 -15
  50. package/lib/programmatic/route-handler-map.js +6 -0
  51. package/lib/programmatic/run-deploy.js +7 -3
  52. package/lib/programmatic/run-upload.js +11 -7
  53. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  54. package/lib/role-assistant/knowledge-sync.js +9 -126
  55. package/lib/role-assistant/test-cases-evaluate.js +12 -1
  56. package/lib/role-assistant/test-cases-evidence.js +44 -5
  57. package/lib/role-assistant/test-cases-lifecycle.js +9 -3
  58. package/lib/role-assistant/test-runner.js +3 -0
  59. package/lib/schema/infra-parameter.schema.json +51 -3
  60. package/lib/schema/infra.parameter.yaml +482 -47
  61. package/lib/schema/type/document-storage.json +1 -1
  62. package/lib/utils/api-required-permissions.js +271 -0
  63. package/lib/utils/api.js +16 -13
  64. package/lib/utils/cli-json-exit.js +41 -0
  65. package/lib/utils/cli-utils.js +26 -7
  66. package/lib/utils/controller-url.js +78 -4
  67. package/lib/utils/deployment-errors.js +15 -0
  68. package/lib/utils/deployment-validation-helpers.js +4 -1
  69. package/lib/utils/device-code.js +31 -10
  70. package/lib/utils/error-formatters/error-parser.js +16 -1
  71. package/lib/utils/error-formatters/permission-errors.js +19 -5
  72. package/lib/utils/keycloak-realm-ready.js +5 -2
  73. package/lib/utils/logger.js +1 -0
  74. package/lib/utils/paths.js +26 -5
  75. package/lib/utils/qualification-poll-ui.js +136 -0
  76. package/lib/utils/secrets-helpers.js +2 -2
  77. package/lib/utils/write-stream-and-wait.js +63 -0
  78. package/package.json +22 -7
  79. package/templates/agent-kit/agent-kit.yaml +1 -1
  80. package/templates/agent-kit/instructions/AGENTKIT.md +25 -4
  81. package/templates/agent-kit/instructions/root.AGENTS.md +1 -1
  82. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +2 -1
  83. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +1 -1
  84. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +2 -1
  85. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +1 -0
  86. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +1 -0
  87. package/templates/agent-kit/skills/shared/hosts.md +13 -26
  88. package/templates/agent-kit/skills/shared/login.md +16 -0
  89. package/templates/agent-kit/workspace/README.md +1 -1
  90. package/templates/applications/builder-api/env.template +4 -0
  91. package/templates/applications/dataplane/application.yaml +1 -2
  92. package/templates/applications/dataplane/env.template +5 -5
  93. package/templates/applications/keycloak/Dockerfile +12 -5
  94. package/templates/applications/keycloak/application.yaml +2 -2
  95. package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +3 -3
  96. package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +6 -6
  97. package/templates/applications/keycloak/env.template +13 -1
  98. package/templates/applications/miso-controller/application.yaml +21 -2
  99. package/templates/applications/miso-controller/env.template +97 -28
  100. package/templates/applications/miso-controller/rbac.yaml +5 -0
  101. package/lib/generator/deploy-manifest-azure-kv.js +0 -81
@@ -1,6 +1,5 @@
1
1
  const {
2
2
  formatSuccessLine,
3
- formatWarningLine,
4
3
  sectionTitle,
5
4
  headerKeyValue,
6
5
  metadata
@@ -21,8 +20,6 @@ const SEP = chalk.gray('──────────────────
21
20
  const { resolveControllerUrl } = require('../utils/controller-url');
22
21
  const { getDeploymentAuth } = require('../utils/token-manager');
23
22
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
24
- const { resolveExternalSystemPath } = require('../utils/app-path-resolve');
25
- const { runWithUploadPipelineSpinner } = require('../utils/upload-pipeline-ui');
26
23
  const {
27
24
  buildResolvedEnvMapForIntegration,
28
25
  resolveConfigurationValues
@@ -33,21 +30,16 @@ const { generateControllerManifest } = require('../generator/external-controller
33
30
  const {
34
31
  validatePipelineConfig
35
32
  } = require('../api/pipeline.api');
36
- const { uploadApplicationViaPipelineWithAuthRetry } = require('../utils/pipeline-upload-auth-retry');
37
33
  const { formatApiError } = require('../utils/api-error-handler');
38
- const { unwrapPublicationResult, unwrapApiData, isPublicationUnchanged } = require('../utils/external-system-readiness-core');
34
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
39
35
  const {
40
36
  logServerValidationWarnings
41
37
  } = require('../utils/external-system-readiness-display');
42
- const { maybeRunUploadProbe } = require('./upload-probe');
43
- const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
44
- const { exportProgrammaticOpenApiForRepair } = require('./repair-openapi-export');
45
38
  const { resolveUploadPublishContext } = require('./upload-publish-context');
39
+ const { runQualificationUpload, resumeQualificationUpload } = require('./upload-qualification-client');
40
+ const { completeQualifiedUpload } = require('./upload-qualification-followups');
46
41
  const { syncDeployJsonFromSources } = require('../external-system/sync-deploy-manifest');
47
42
  const { assertUploadPackageIdentity } = require('../external-system/upload-identity');
48
- const { handlePublicationAndFollowups } = require('./upload-sidecar-followups');
49
- const { finalizeUploadPublishResult, finishUnchangedUploadPublication } = require('./upload-data-sync-finalize');
50
- const { pushAndLogCredentialSecrets } = require('./upload-credential-push-log');
51
43
  const {
52
44
  SYSTEM_KIND_DIGITAL_WORKER,
53
45
  readRoleAssistantSettings,
@@ -129,39 +121,6 @@ async function resolveDataplaneAndAuth(systemKey, opts = {}) {
129
121
  return { dataplaneUrl, authConfig, environment };
130
122
  }
131
123
 
132
- /**
133
- * Runs single pipeline upload (upload → validate → publish) on the dataplane.
134
- * @param {string} dataplaneUrl - Dataplane base URL
135
- * @param {Object} authConfig - Auth config
136
- * @param {Object} payload - { version, application, dataSources, status: "draft" }
137
- * @param {string} systemKey
138
- * @param {Object} [options]
139
- * @param {number|string} [options.timeoutMs]
140
- * @returns {Promise<Object>} Raw API response envelope
141
- */
142
- async function runUploadValidatePublish(
143
- dataplaneUrl,
144
- authConfig,
145
- payload,
146
- systemKey,
147
- options = {}
148
- ) {
149
- const res = await uploadApplicationViaPipelineWithAuthRetry(
150
- dataplaneUrl,
151
- authConfig,
152
- payload,
153
- systemKey,
154
- { timeoutMs: options.timeoutMs }
155
- );
156
- if (res?.success === false) {
157
- const err = new Error(formatApiError(res, dataplaneUrl));
158
- const status = Number(res.status || res.statusCode);
159
- err.statusCode = status >= 400 && status < 600 ? status : 400;
160
- throw err;
161
- }
162
- return res;
163
- }
164
-
165
124
  /**
166
125
  * @param {Object} validationResult - Result from validateExternalSystemComplete
167
126
  * @returns {string}
@@ -269,97 +228,30 @@ async function buildValidatedUploadManifestPayload(systemKey, opts = {}) {
269
228
  }
270
229
 
271
230
  /**
272
- * Upload local `integration/<systemKey>/openapi/*.json` specs when present so dataplane can store
273
- * vendor OpenAPI keyed by each datasource `openapi.documentKey` (same behavior as repair --api OpenAPI sync).
274
- * Call this **after** a successful pipeline publish: list/upload endpoints require the external system row.
275
- * First publish still succeeds because pipeline materializes internal specs from `openapi.operations` when needed.
276
- *
231
+ * Accept (unless --resume) then poll a Dataplane qualification job to terminal state.
232
+ * @param {string} dataplaneUrl
233
+ * @param {Object} authConfig
234
+ * @param {Object} payload
277
235
  * @param {string} systemKey
278
- * @param {Object} manifest
279
- * @returns {Promise<void>}
236
+ * @param {Object} options - { resume?: string, wait?: boolean, json?: boolean, timeoutMs? }
237
+ * @returns {Promise<{ accepted: Object, terminal: Object|null }>}
280
238
  */
281
- async function logAndSyncLocalOpenApiForUpload(systemKey, manifest, uploadContext = {}) {
282
- const pathOptions = uploadContext.pathOptions || {};
283
- const appPath = resolveExternalSystemPath(systemKey, pathOptions);
284
- const ei = manifest && manifest.externalIntegration;
285
- const datasourceFiles = ei && Array.isArray(ei.dataSources) ? ei.dataSources : [];
286
- await exportProgrammaticOpenApiForRepair({
287
- appPath,
288
- systemKey,
289
- datasourceFiles,
290
- dryRun: false,
291
- backupCtx: { dryRun: false, changedFiles: [] }
292
- });
293
- const openapiSyncLines = await maybeSyncOpenApiFilesForMcp({
294
- enabled: true,
295
- dryRun: false,
296
- appPath,
297
- systemKey,
298
- datasourceFiles,
299
- dataplaneUrl: uploadContext.dataplaneUrl,
300
- authConfig: uploadContext.authConfig,
301
- // Always replace SpecFile rows keyed by openapi.documentKey so vendor/full
302
- // specs are not left missing while only thin internal:* slices remain.
303
- force: true
304
- });
305
- if (!uploadContext.quiet) {
306
- for (const line of openapiSyncLines) {
307
- logger.log(line.startsWith('Skipped') ? formatWarningLine(line) : formatSuccessLine(line));
308
- }
239
+ async function acceptOrResumeQualification(dataplaneUrl, authConfig, payload, systemKey, options) {
240
+ if (options.resume) {
241
+ const terminal = await resumeQualificationUpload(dataplaneUrl, authConfig, options.resume, systemKey, options);
242
+ return {
243
+ accepted: { jobId: options.resume, candidateFingerprint: terminal.result?.candidateFingerprint },
244
+ terminal
245
+ };
309
246
  }
247
+ const result = await runQualificationUpload(dataplaneUrl, authConfig, payload, systemKey, options);
248
+ return { accepted: result, terminal: result.terminal };
310
249
  }
311
250
 
312
251
  /**
313
- * @param {string} systemKey
314
- * @param {Object} options
315
- * @param {Object} manifest
316
- * @param {Object} payload
317
- * @param {{ dataplaneUrl: string, authConfig: Object, environment: string, rawRes: Object }} ctx
318
- */
319
- async function completeUploadAfterPublish(systemKey, options, manifest, payload, ctx) {
320
- const quietCompact = options.minimal === true && options.verbose !== true;
321
- const pushMeta = await pushAndLogCredentialSecrets(ctx.dataplaneUrl, ctx.authConfig, systemKey, payload, {
322
- json: options.json === true,
323
- pathOptions: options.pathOptions,
324
- suppressCredentialLog: quietCompact,
325
- excludeSecretKeys: options.excludeSecretKeys
326
- });
327
- await logAndSyncLocalOpenApiForUpload(systemKey, manifest, {
328
- dataplaneUrl: ctx.dataplaneUrl,
329
- authConfig: ctx.authConfig,
330
- pathOptions: options.pathOptions,
331
- quiet: quietCompact
332
- });
333
- const followupResult = await handlePublicationAndFollowups(
334
- {
335
- systemKey,
336
- options,
337
- manifest,
338
- payload,
339
- environment: ctx.environment,
340
- dataplaneUrl: ctx.dataplaneUrl,
341
- authConfig: ctx.authConfig,
342
- rawRes: ctx.rawRes,
343
- credentialPush: pushMeta.credentialPush,
344
- configPush: pushMeta.configPush
345
- },
346
- maybeRunUploadProbe
347
- );
348
- return finalizeUploadPublishResult(
349
- {
350
- dataplaneUrl: ctx.dataplaneUrl,
351
- authConfig: ctx.authConfig,
352
- environment: ctx.environment,
353
- uploadSkipped: false,
354
- sidecar: followupResult?.sidecar || null
355
- },
356
- payload,
357
- options
358
- );
359
- }
360
-
361
- /**
362
- * Upload path after dry-run check.
252
+ * Upload path after dry-run check: submit for durable Dataplane qualification (async, no
253
+ * request-lifetime failure), then — unless `--no-wait` — poll to terminal and run the same
254
+ * downstream follow-ups (credential push, OpenAPI sync, sidecar, probe) as before.
363
255
  * @param {string} systemKey
364
256
  * @param {Object} options
365
257
  * @param {Object} manifest
@@ -373,40 +265,28 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
373
265
  options,
374
266
  payload
375
267
  );
376
- const rawRes = await runWithUploadPipelineSpinner(
377
- async() =>
378
- runUploadValidatePublish(dataplaneUrl, authConfig, payload, systemKey, {
379
- timeoutMs: options.timeoutMs
380
- }),
381
- {
382
- systemKey,
383
- json: options.json === true,
384
- syncMode: options.syncMode === true,
385
- suppressPipelineSpinner: options.suppressPipelineSpinner === true
386
- }
268
+ const { accepted, terminal } = await acceptOrResumeQualification(
269
+ dataplaneUrl,
270
+ authConfig,
271
+ payload,
272
+ systemKey,
273
+ options
387
274
  );
388
- const publication = unwrapPublicationResult(rawRes);
389
- if (isPublicationUnchanged(publication)) {
390
- return finishUnchangedUploadPublication({
391
- systemKey,
392
- options,
393
- payload,
394
- dataplaneUrl,
395
- authConfig,
396
- environment
397
- });
275
+ if (!terminal) {
276
+ return accepted;
398
277
  }
399
-
400
- return completeUploadAfterPublish(systemKey, options, manifest, payload, {
278
+ return completeQualifiedUpload(systemKey, options, manifest, payload, {
401
279
  dataplaneUrl,
402
280
  authConfig,
403
281
  environment,
404
- rawRes
282
+ accepted,
283
+ job: terminal
405
284
  });
406
285
  }
407
286
 
408
287
  /**
409
- * Uploads external system: publishes to dataplane and registers with controller (draft).
288
+ * Uploads external system: durably qualifies on dataplane (async ladder, no request-lifetime
289
+ * failure) then registers with controller (draft).
410
290
  * @param {string} systemKey - External system key (integration/<systemKey>/)
411
291
  * @param {Object} [options] - Options
412
292
  * @param {boolean} [options.dryRun] - Validate and build payload only; no API calls
@@ -415,7 +295,9 @@ async function runUploadPublishAndSummary(systemKey, options, manifest, payload)
415
295
  * @param {number} [options.probeTimeout] - Probe timeout ms (default 120000)
416
296
  * @param {number} [options.timeoutMs] - Pipeline publish timeout ms (HTTP + dataplane; default 300000)
417
297
  * @param {boolean} [options.minimal] - Short summary (default: compact unless --verbose)
418
- * @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }|void>}
298
+ * @param {boolean} [options.wait=true] - Poll the qualification job to terminal state; --no-wait returns the job id immediately
299
+ * @param {string} [options.resume] - Poll an existing qualification job id instead of submitting a new candidate
300
+ * @returns {Promise<Object|void>} Terminal result when waited, `{jobId, candidateFingerprint}` when `--no-wait`, or `undefined` on dry-run
419
301
  * @throws {Error} If validation or API calls fail
420
302
  */
421
303
  async function uploadExternalSystem(systemKey, options = {}) {
@@ -461,6 +343,5 @@ module.exports = {
461
343
  buildValidatedUploadManifestPayload,
462
344
  buildUploadPayload,
463
345
  resolveDataplaneAndAuth,
464
- runUploadValidatePublish,
465
346
  validateSystemKeyFormat
466
347
  };
@@ -6,6 +6,7 @@
6
6
 
7
7
  const logger = require('../utils/logger');
8
8
  const { handleCommandError } = require('../utils/cli-utils');
9
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
9
10
  const { runVerifyGovernanceCombined } = require('./verify-governance-combined-external');
10
11
  const { normalizeTestGovernanceCliOptions } = require('./test-governance-cli-options');
11
12
  const { displayVerifyGovernanceTTY } = require('../lifecycle/report-display');
@@ -33,16 +34,17 @@ async function runVerifyGovernanceCommandAction(appName, options, cmd) {
33
34
  const opts = normalizeVerifyGovernanceCliOptions(options, cmd);
34
35
  const result = await runVerifyGovernanceCombined(appName, opts);
35
36
 
37
+ const exitCode = exitCodeFromVerdict(result.verdict);
38
+
36
39
  if (opts.json) {
37
- logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
38
- } else {
39
- if (opts.verbose && !opts.pack && typeof result.generated === 'number' && result.generated > 0) {
40
- logger.log(formatSuccessLine(`Generated packs for ${result.generated} datasource(s)`));
41
- }
42
- displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
40
+ await writeJsonEnvelopeAndExit(buildJsonEnvelope(result, opts.verbose), exitCode);
41
+ return;
43
42
  }
44
43
 
45
- const exitCode = exitCodeFromVerdict(result.verdict);
44
+ if (opts.verbose && !opts.pack && typeof result.generated === 'number' && result.generated > 0) {
45
+ logger.log(formatSuccessLine(`Generated packs for ${result.generated} datasource(s)`));
46
+ }
47
+ displayVerifyGovernanceTTY(result.systemKey, result, { details: opts.verbose });
46
48
  if (exitCode !== 0) {
47
49
  process.exit(exitCode);
48
50
  }
@@ -6,8 +6,8 @@
6
6
 
7
7
  'use strict';
8
8
 
9
- const logger = require('../utils/logger');
10
9
  const { handleCommandError } = require('../utils/cli-utils');
10
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
11
11
  const { cliOptsSkipPublish } = require('../utils/cli-sync-options');
12
12
  const { runVerifyOperationsForExternalSystem } = require('./verify-operations-external');
13
13
  const { displayVerifyOperationsTTY } = require('../lifecycle/report-display');
@@ -37,16 +37,17 @@ async function runVerifyOperationsCommandAction(appName, options, cmd) {
37
37
  const opts = normalizeVerifyOperationsCliOptions(options, cmd);
38
38
  const result = await runVerifyOperationsForExternalSystem(appName, opts);
39
39
 
40
+ const exitCode = exitCodeFromVerdict(result.verdict);
41
+
40
42
  if (opts.json) {
41
- logger.log(JSON.stringify(buildJsonEnvelope(result, opts.verbose), null, 2));
42
- } else {
43
- displayVerifyOperationsTTY(appName, result, {
44
- details: opts.verbose,
45
- warnings: result.warnings
46
- });
43
+ await writeJsonEnvelopeAndExit(buildJsonEnvelope(result, opts.verbose), exitCode);
44
+ return;
47
45
  }
48
46
 
49
- const exitCode = exitCodeFromVerdict(result.verdict);
47
+ displayVerifyOperationsTTY(appName, result, {
48
+ details: opts.verbose,
49
+ warnings: result.warnings
50
+ });
50
51
  if (exitCode !== 0) {
51
52
  process.exit(exitCode);
52
53
  }
@@ -6,8 +6,8 @@
6
6
 
7
7
  'use strict';
8
8
 
9
- const logger = require('../utils/logger');
10
9
  const { handleCommandError } = require('../utils/cli-utils');
10
+ const { writeJsonEnvelopeAndExit } = require('../utils/cli-json-exit');
11
11
  const { runVerifyTrustForExternalSystem } = require('./verify-trust-external');
12
12
  const { normalizeTestTrustCliOptions } = require('./test-trust-cli-options');
13
13
  const { displayVerifyTrustTTY } = require('../lifecycle/report-display');
@@ -30,6 +30,8 @@ async function runVerifyTrustCommandAction(appName, options, cmd) {
30
30
  timeout: opts.timeout
31
31
  });
32
32
 
33
+ const exitCode = exitCodeFromVerdict(result.verdict);
34
+
33
35
  if (opts.json) {
34
36
  const envelope = buildJsonEnvelope(
35
37
  {
@@ -44,12 +46,11 @@ async function runVerifyTrustCommandAction(appName, options, cmd) {
44
46
  },
45
47
  opts.details || opts.verbose
46
48
  );
47
- logger.log(JSON.stringify(envelope, null, 2));
48
- } else {
49
- displayVerifyTrustTTY(result.systemKey, result, { details: opts.verbose || opts.details });
49
+ await writeJsonEnvelopeAndExit(envelope, exitCode);
50
+ return;
50
51
  }
51
52
 
52
- const exitCode = exitCodeFromVerdict(result.verdict);
53
+ displayVerifyTrustTTY(result.systemKey, result, { details: opts.verbose || opts.details });
53
54
  if (exitCode !== 0) {
54
55
  process.exit(exitCode);
55
56
  }
@@ -23,7 +23,10 @@ function shouldUseDeployPollSpinner() {
23
23
  * @returns {{ onPollProgress: Function, finish: () => void }}
24
24
  */
25
25
  function createDeployPollHandlers(maxAttempts, options = {}) {
26
+ void maxAttempts;
26
27
  const silent = options && options.silent === true;
28
+ const startedAt = Date.now();
29
+ const elapsedMs = () => Date.now() - startedAt;
27
30
  const noop = () => {};
28
31
  if (silent) {
29
32
  return {
@@ -49,11 +52,11 @@ function createDeployPollHandlers(maxAttempts, options = {}) {
49
52
  setText(text) {
50
53
  ensureSpinner(text);
51
54
  },
52
- onPollProgress: (deploymentData, attempt, maxA) => {
53
- ensureSpinner(buildDeployPollSpinnerText(deploymentData, attempt, maxA));
55
+ onPollProgress: (deploymentData) => {
56
+ ensureSpinner(buildDeployPollSpinnerText(deploymentData, elapsedMs()));
54
57
  },
55
- onPollWait: (attempt, maxA) => {
56
- ensureSpinner(buildDeployPollWaitText(attempt, maxA));
58
+ onPollWait: () => {
59
+ ensureSpinner(buildDeployPollWaitText(elapsedMs()));
57
60
  },
58
61
  finish: () => {
59
62
  if (spinner) {
@@ -64,13 +67,14 @@ function createDeployPollHandlers(maxAttempts, options = {}) {
64
67
  }
65
68
  return {
66
69
  setText: noop,
67
- onPollProgress: (deploymentData, attempt, maxA) => {
70
+ onPollProgress: (deploymentData) => {
68
71
  const status = deploymentData?.status ?? 'pending';
69
72
  const progress =
70
73
  deploymentData && Number.isFinite(Number(deploymentData.progress))
71
74
  ? Number(deploymentData.progress)
72
75
  : 0;
73
- logger.log(chalk.gray(`Status: ${status} (${progress}%) (attempt ${attempt + 1}/${maxA})`));
76
+ const elapsedSeconds = Math.max(0, Math.floor(elapsedMs() / 1000));
77
+ logger.log(chalk.gray(`Status: ${status} (${progress}%) — ${elapsedSeconds}s elapsed`));
74
78
  },
75
79
  onPollWait: noop,
76
80
  finish: noop
@@ -79,12 +83,12 @@ function createDeployPollHandlers(maxAttempts, options = {}) {
79
83
 
80
84
  /**
81
85
  * Plain progress while awaiting the next status poll (HTTP in flight).
82
- * @param {number} attempt
83
- * @param {number} maxAttempts
86
+ * @param {number} elapsedMs
84
87
  * @returns {string}
85
88
  */
86
- function buildDeployPollWaitText(attempt, maxAttempts) {
87
- return `Deploying application... Checking status (attempt ${attempt + 1}/${maxAttempts})`;
89
+ function buildDeployPollWaitText(elapsedMs) {
90
+ const elapsedSeconds = Math.max(0, Math.floor(Number(elapsedMs || 0) / 1000));
91
+ return `Deploying application — ${elapsedSeconds}s elapsed`;
88
92
  }
89
93
 
90
94
  /**
@@ -89,14 +89,14 @@ function logDeploymentProgress(deploymentData, attempt, maxAttempts) {
89
89
  /**
90
90
  * Single-line ora text for deploy polling (updates in place; same style as guided infra spinners).
91
91
  * @param {Object|null|undefined} deploymentData - Latest deployment payload (optional before first response)
92
- * @param {number} attempt - Zero-based attempt index
93
- * @param {number} maxAttempts - Max polling attempts
92
+ * @param {number} elapsedMs - Time elapsed since deployment polling began
94
93
  * @returns {string}
95
94
  */
96
- function buildDeployPollSpinnerText(deploymentData, attempt, maxAttempts) {
95
+ function buildDeployPollSpinnerText(deploymentData, elapsedMs) {
97
96
  const status = deploymentData?.status ?? 'pending';
98
97
  const progress = deploymentData && Number.isFinite(Number(deploymentData.progress)) ? Number(deploymentData.progress) : 0;
99
- return `Deploying application... Status: ${status} (${progress}%) (attempt ${attempt + 1}/${maxAttempts})`;
98
+ const elapsedSeconds = Math.max(0, Math.floor(Number(elapsedMs || 0) / 1000));
99
+ return `Deploying application — ${elapsedSeconds}s elapsed · ${status} (${progress}%)`;
100
100
  }
101
101
 
102
102
  /**
@@ -28,7 +28,6 @@ const { generateControllerManifest } = require('../generator/external-controller
28
28
  const { validateExternalSystemComplete } = require('../validation/validate');
29
29
  const { displayValidationResults } = require('../validation/validate-display');
30
30
  const { syncDeployJsonFromSources } = require('./sync-deploy-manifest');
31
- const { runDeployGateOrchestrator } = require('../commands/deploy-gate-orchestrator');
32
31
  const { runPostPublishDataSync } = require('../commands/run-post-publish-data-sync');
33
32
  const { uploadDigitalWorkerIfNeeded, deployRoleAssistantShellIfNeeded } = require('./deploy-helpers');
34
33
  const { createDataplanePublishedCheck } = require('./deploy-dataplane-published');
@@ -199,7 +198,9 @@ function buildExternalDeployPollOpts(options, extra = {}) {
199
198
  return {
200
199
  ...options,
201
200
  ...extra,
202
- poll: true,
201
+ // Default true (CLI blocks and shows a spinner); explicit false (builder-api web path,
202
+ // plan 191.0 task 3) accepts and returns immediately — the caller polls its own status route.
203
+ poll: options.poll !== false,
203
204
  pollInterval:
204
205
  options.pollInterval !== undefined ? options.pollInterval : EXTERNAL_DEPLOY_POLL_INTERVAL_MS,
205
206
  pollMaxAttempts:
@@ -210,28 +211,15 @@ function buildExternalDeployPollOpts(options, extra = {}) {
210
211
  };
211
212
  }
212
213
 
213
- async function promoteExternalSystemLifecycleAfterDeploy(manifest, dataplaneUrl, authConfig) {
214
- const { buildUploadPayload } = require('../commands/upload');
215
- const { uploadApplicationViaPipelineWithAuthRetry } = require('../utils/pipeline-upload-auth-retry');
216
- const payload = buildUploadPayload(manifest, { status: 'published' });
217
- const res = await uploadApplicationViaPipelineWithAuthRetry(
218
- dataplaneUrl,
219
- authConfig,
220
- payload,
221
- manifest.key
222
- );
223
- if (res?.success === false) {
224
- throw new Error(
225
- res.formattedError || res.error || 'Failed to promote external system lifecycle after deploy'
226
- );
227
- }
228
- return res;
229
- }
230
-
214
+ /**
215
+ * Reads back dataplane readiness after Miso's deploy call. Miso (226.0/191.0 addendum) already
216
+ * decided and executed fast-qualified promotion or the legacy full ladder server-side — Builder
217
+ * never re-publishes here; that was the duplicate work this task removes.
218
+ */
231
219
  async function finalizeExternalDeployDataplaneWork(manifest, controllerUrl, environment, authConfig, options) {
232
220
  const compact = options.verbose !== true;
233
221
  const silent = compact;
234
- const ctx = await fetchDataplaneDeployReadiness(
222
+ const refreshedCtx = await fetchDataplaneDeployReadiness(
235
223
  controllerUrl,
236
224
  environment,
237
225
  authConfig,
@@ -239,21 +227,6 @@ async function finalizeExternalDeployDataplaneWork(manifest, controllerUrl, envi
239
227
  { dataplaneUrl: options.knownDataplaneUrl, silent }
240
228
  );
241
229
 
242
- if (ctx.dataplaneUrl && !ctx.error) {
243
- await promoteExternalSystemLifecycleAfterDeploy(manifest, ctx.dataplaneUrl, authConfig);
244
- }
245
-
246
- const refreshedCtx =
247
- ctx.dataplaneUrl && !ctx.error
248
- ? await fetchDataplaneDeployReadiness(
249
- controllerUrl,
250
- environment,
251
- authConfig,
252
- manifest.key,
253
- { dataplaneUrl: ctx.dataplaneUrl, silent: true }
254
- )
255
- : ctx;
256
-
257
230
  const probeData = await runOptionalExternalDeployProbe(refreshedCtx, manifest.key, authConfig, options);
258
231
  return { refreshedCtx, probeData };
259
232
  }
@@ -294,6 +267,21 @@ async function runPostDeployExternalSystemSteps(manifest, controllerUrl, environ
294
267
  });
295
268
  }
296
269
 
270
+ // Builder-api web path (plan 191.0 task 3): accept-only, raw controller accept response
271
+ // (deploymentId, not-yet-terminal status) — caller polls getDeployStatus for terminal truth.
272
+ function logCompactDeployFailure(manifest, environment, deploymentOutcome, options, result) {
273
+ logDeployReadinessSummary({
274
+ environment,
275
+ dataplaneUrl: null,
276
+ manifest,
277
+ deploymentOk: false,
278
+ deploymentDetail: deploymentOutcome,
279
+ verbose: false,
280
+ gateOutcomes: options.gateOutcomes || null,
281
+ draftDeploymentId: result?.deploymentId || null
282
+ });
283
+ }
284
+
297
285
  async function executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, options) {
298
286
  const compact = options.verbose !== true;
299
287
  const deployer = require('../deployment/deployer');
@@ -313,6 +301,10 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
313
301
  })
314
302
  );
315
303
 
304
+ if (options.poll === false) {
305
+ return result;
306
+ }
307
+
316
308
  const deploymentOutcome = parseControllerDeploymentOutcome(result);
317
309
  if (!compact) {
318
310
  logImmediateControllerDeploymentOutcome(deploymentOutcome);
@@ -320,16 +312,7 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
320
312
 
321
313
  if (!deploymentOutcome.ok) {
322
314
  if (compact) {
323
- logDeployReadinessSummary({
324
- environment,
325
- dataplaneUrl: null,
326
- manifest,
327
- deploymentOk: false,
328
- deploymentDetail: deploymentOutcome,
329
- verbose: false,
330
- gateOutcomes: options.gateOutcomes || null,
331
- draftDeploymentId: result?.deploymentId || null
332
- });
315
+ logCompactDeployFailure(manifest, environment, deploymentOutcome, options, result);
333
316
  }
334
317
  return result;
335
318
  }
@@ -422,15 +405,28 @@ async function validateDataplaneManifestForDeploy(manifest, authConfig, dataplan
422
405
  }
423
406
  }
424
407
 
408
+ /**
409
+ * Deploy no longer runs local unit/integration tests or optional --gate certification steps
410
+ * (plan 191.0 task 2): that ladder now runs once, durably, during `aifabrix upload`. A caller
411
+ * still passing --gate gets an actionable notice instead of a silently-ignored flag.
412
+ */
413
+ function warnIfDeployGateFlagIsNowNoop(systemKey, options) {
414
+ if (!options.gate) {
415
+ return;
416
+ }
417
+ logger.log(
418
+ chalk.yellow(
419
+ `⚠ --gate is no longer run during deploy; it runs once during "aifabrix upload ${systemKey}".`
420
+ )
421
+ );
422
+ }
423
+
425
424
  async function runConnectedSystemDeployAfterDetect(appName, options, compact) {
426
425
  if (!compact) {
427
426
  logger.log(chalk.blue(`\n🚀 Deploying external system: ${appName}`));
428
427
  }
429
428
  await validateExternalSystemForDeploy(appName, options, compact);
430
- const gateOutcomes = await runDeployGateOrchestrator(appName, {
431
- ...options,
432
- quietDeployUi: options.json === true
433
- });
429
+ warnIfDeployGateFlagIsNowNoop(appName, options);
434
430
  await syncDeployJsonFromSources(appName);
435
431
  const manifest = await generateControllerManifest(appName, options);
436
432
  const digitalWorkerResult = await uploadDigitalWorkerIfNeeded(
@@ -454,7 +450,6 @@ async function runConnectedSystemDeployAfterDetect(appName, options, compact) {
454
450
  await validateDataplaneManifestForDeploy(manifest, authConfig, dataplaneUrl, compact);
455
451
  return executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, {
456
452
  ...options,
457
- gateOutcomes,
458
453
  knownDataplaneUrl: dataplaneUrl
459
454
  });
460
455
  }
@@ -74,9 +74,8 @@ async function setupAuthenticationAndDataplane(systemKey, _options, _config) {
74
74
  }
75
75
 
76
76
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
77
- logger.log(formatProgress('Resolving dataplane URL…'));
77
+ // discoverDataplaneUrl already logs progress and success
78
78
  const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
79
- logger.log(formatSuccessLine(`Dataplane URL: ${dataplaneUrl}`));
80
79
 
81
80
  return { authConfig, dataplaneUrl };
82
81
  }