@aifabrix/builder 2.51.1 → 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 (69) hide show
  1. package/lib/app/run-container-start.js +4 -0
  2. package/lib/app/run-deploy-job.js +135 -27
  3. package/lib/app/run-env-compose.js +32 -0
  4. package/lib/app/run-env-recovery.js +35 -0
  5. package/lib/app/run-helpers.js +5 -6
  6. package/lib/app/run.js +11 -3
  7. package/lib/channels/approval-guides/chatgpt.js +12 -9
  8. package/lib/channels/channel-targets.js +18 -1
  9. package/lib/channels/chatgpt-runtime-contract.js +1 -1
  10. package/lib/cli/infra-guided-footers.js +1 -1
  11. package/lib/cli/infra-guided-public-url-warn.js +55 -0
  12. package/lib/cli/infra-guided.js +65 -56
  13. package/lib/cli/setup-app.help.js +4 -0
  14. package/lib/cli/setup-app.test-commands.js +59 -28
  15. package/lib/cli/setup-external-system.js +50 -12
  16. package/lib/commands/channel-add-setup.js +8 -2
  17. package/lib/commands/channel.js +16 -5
  18. package/lib/commands/deploy-gate-orchestrator.js +3 -2
  19. package/lib/commands/governance-verify-external.js +2 -5
  20. package/lib/commands/lifecycle-external.js +14 -7
  21. package/lib/commands/role-assistant.js +66 -8
  22. package/lib/commands/test-governance-external.js +2 -5
  23. package/lib/commands/up-miso.js +61 -24
  24. package/lib/commands/upload-data-sync-finalize.js +20 -3
  25. package/lib/commands/verify-governance-external.js +2 -5
  26. package/lib/commands/verify-operations-external.js +2 -5
  27. package/lib/commands/verify-operations-step-plan.js +31 -0
  28. package/lib/commands/verify-trust-command-action.js +2 -5
  29. package/lib/datasource/datasource-identifier.js +4 -3
  30. package/lib/datasource/integration-context.js +3 -3
  31. package/lib/datasource/resolve-app.js +90 -8
  32. package/lib/datasource/test-e2e.js +2 -2
  33. package/lib/datasource/unified-validation-run-body.js +15 -1
  34. package/lib/datasource/unified-validation-run-resolve.js +2 -2
  35. package/lib/external-system/deploy-helpers.js +9 -5
  36. package/lib/external-system/deploy.js +2 -1
  37. package/lib/external-system/test-system-level.js +21 -3
  38. package/lib/external-system/test.js +4 -4
  39. package/lib/generator/builders.js +9 -15
  40. package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
  41. package/lib/role-assistant/knowledge-sync.js +25 -5
  42. package/lib/role-assistant/test-runner-workhub-answers.js +44 -26
  43. package/lib/role-assistant/test-runner-workhub.js +21 -7
  44. package/lib/schema/application-schema.json +10 -28
  45. package/lib/schema/infra.parameter.yaml +0 -20
  46. package/lib/utils/certify-external-workspace.js +40 -0
  47. package/lib/utils/docker-run-ephemeral.js +77 -17
  48. package/lib/utils/health-check-public-warn.js +31 -13
  49. package/lib/utils/health-check.js +11 -1
  50. package/lib/utils/paths.js +13 -15
  51. package/lib/utils/role-assistant-paths.js +76 -14
  52. package/lib/utils/upload-sync-options.js +7 -4
  53. package/lib/validation/reserved-system-key-prefixes.js +56 -0
  54. package/lib/validation/validate.js +54 -19
  55. package/package.json +1 -1
  56. package/templates/applications/builder-api/application.yaml +3 -3
  57. package/templates/applications/dataplane/application.yaml +3 -4
  58. package/templates/applications/dataplane/env.template +2 -2
  59. package/templates/applications/dataplane/rbac.yaml +10 -10
  60. package/templates/applications/keycloak/application.yaml +2 -2
  61. package/templates/applications/keycloak/env.template +5 -2
  62. package/templates/applications/miso-controller/application.yaml +3 -3
  63. package/templates/applications/miso-controller/env.template +16 -3
  64. package/templates/channels/chatgpt/install.template.md.hbs +75 -37
  65. package/templates/channels/chatgpt/integration/README.md +3 -2
  66. package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
  67. package/templates/channels/slack/install.template.md.hbs +6 -1
  68. package/templates/python/docker-compose.hbs +10 -0
  69. package/templates/typescript/docker-compose.hbs +10 -0
@@ -141,6 +141,7 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
141
141
  debug = false,
142
142
  runEnvPath = null,
143
143
  runEnvAdminPath = null,
144
+ ensureRunEnvFiles = null,
144
145
  runOptions = {},
145
146
  devMountPath = null,
146
147
  misoEnvironment = 'dev'
@@ -166,6 +167,9 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
166
167
  }
167
168
 
168
169
  if (!usedDockerRunFallback) {
170
+ if (typeof ensureRunEnvFiles === 'function') {
171
+ await ensureRunEnvFiles();
172
+ }
169
173
  const env = await prepareContainerEnv(debug);
170
174
  await executeComposeUp(composeCmdBase, composePath, env, debug);
171
175
  }
@@ -11,11 +11,16 @@ const logger = require('../utils/logger');
11
11
  const {
12
12
  infoLine,
13
13
  formatSuccessLine,
14
- formatBlockingError
14
+ formatBlockingError,
15
+ metadata
15
16
  } = require('../utils/cli-test-layout-chalk');
16
17
  const { runDockerRunEphemeral } = require('../utils/docker-run-ephemeral');
17
18
  const { getInfraBridgeNetworkName } = require('../infrastructure/orphan-infra-docker-teardown');
18
19
 
20
+ /** Migrate status lines: `[system] head=… applied=3 pending=[]` */
21
+ const DEPLOY_JOB_STATUS_RE =
22
+ /^\[([^\]]+)\]\s+head=\S+\s+applied=(\d+)\s+pending=(\[[^\]]*\])/i;
23
+
19
24
  /**
20
25
  * Resolve trimmed deployJob from app config, or null if unset.
21
26
  * @param {Object} appConfig - Application configuration
@@ -39,6 +44,115 @@ async function resolveDeployJobNetwork() {
39
44
  return getInfraBridgeNetworkName(developerId);
40
45
  }
41
46
 
47
+ /**
48
+ * Quiet docker stdio by default (hide app bootstrap warnings). Full inherit only with --debug.
49
+ * @param {{ quiet?: boolean, debug?: boolean }} params
50
+ * @returns {boolean}
51
+ */
52
+ function shouldQuietDeployJob(params = {}) {
53
+ if (params.debug === true) {
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+
59
+ /**
60
+ * Parse migrate CLI status lines into totals (no per-chain head/pending dump).
61
+ * @param {string} output
62
+ * @returns {{ chains: number, applied: number, pending: number }|null}
63
+ */
64
+ function summarizeDeployJobMigrateOutput(output) {
65
+ if (typeof output !== 'string' || !output.trim()) {
66
+ return null;
67
+ }
68
+ let chains = 0;
69
+ let applied = 0;
70
+ let pending = 0;
71
+ for (const line of output.split(/\r?\n/)) {
72
+ const match = DEPLOY_JOB_STATUS_RE.exec(line.trim());
73
+ if (!match) {
74
+ continue;
75
+ }
76
+ chains += 1;
77
+ applied += Number.parseInt(match[2], 10) || 0;
78
+ const inner = match[3].slice(1, -1).trim();
79
+ if (inner) {
80
+ pending += inner.split(',').filter((part) => part.trim()).length;
81
+ }
82
+ }
83
+ if (chains === 0) {
84
+ return null;
85
+ }
86
+ return { chains, applied, pending };
87
+ }
88
+
89
+ /**
90
+ * One-line migrate totals from quiet deployJob output (replaces per-chain dump).
91
+ * @param {string} output
92
+ * @returns {string|null} Summary text for the completed line, or null
93
+ */
94
+ function formatDeployJobMigrateSummary(output) {
95
+ const summary = summarizeDeployJobMigrateOutput(output);
96
+ if (!summary) {
97
+ return null;
98
+ }
99
+ return `${summary.applied} applied, ${summary.pending} pending (${summary.chains} chains)`;
100
+ }
101
+
102
+ /**
103
+ * @deprecated Prefer formatDeployJobMigrateSummary — kept for tests.
104
+ * @param {string} output
105
+ */
106
+ function emitDeployJobSummaryLines(output) {
107
+ const text = formatDeployJobMigrateSummary(output);
108
+ if (text) {
109
+ logger.log(metadata(`DB migrate: ${text}`));
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @param {string|null|undefined} devMountPath
115
+ * @returns {Array<{ host: string, container: string }>}
116
+ */
117
+ function _deployJobVolumeMounts(devMountPath) {
118
+ if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
119
+ return [];
120
+ }
121
+ return [{ host: devMountPath.trim(), container: '/app' }];
122
+ }
123
+
124
+ /**
125
+ * Always announce the job; debug adds command/network/mount detail.
126
+ * @param {{ fullImage: string, cmd: string, network: string, volumeMounts: Array, debug: boolean }} params
127
+ */
128
+ function _logDeployJobStart(params) {
129
+ logger.log(infoLine(`Running build.deployJob once before start (${params.fullImage})…`));
130
+ if (!params.debug) {
131
+ return;
132
+ }
133
+ logger.log(infoLine(`deployJob: ${params.cmd}`));
134
+ logger.log(infoLine(`deployJob network: ${params.network}`));
135
+ if (params.volumeMounts.length) {
136
+ logger.log(infoLine(`deployJob mount: ${params.volumeMounts[0].host} -> /app`));
137
+ }
138
+ }
139
+
140
+ /**
141
+ * @param {{ result: any, quietJob: boolean }} params
142
+ * @throws {Error}
143
+ */
144
+ function _assertDeployJobExit(params) {
145
+ const code = params.quietJob ? params.result.code : params.result;
146
+ if (code === 0) {
147
+ return;
148
+ }
149
+ if (params.quietJob && params.result.output) {
150
+ logger.log(params.result.output.trimEnd());
151
+ }
152
+ logger.log(formatBlockingError(`build.deployJob failed with exit code ${code}`));
153
+ throw new Error(`build.deployJob failed with exit code ${code}`);
154
+ }
155
+
42
156
  /**
43
157
  * If build.deployJob is set, run it once in an ephemeral container (same image + env).
44
158
  * Fail-closed on non-zero exit. No-op when unset.
@@ -53,6 +167,7 @@ async function resolveDeployJobNetwork() {
53
167
  * @param {string|null} [params.runEnvPath] - Ephemeral .env.run path for --env-file
54
168
  * @param {string|null} [params.devMountPath] - Host path mounted at /app for --reload
55
169
  * @param {boolean} [params.debug]
170
+ * @param {boolean} [params.quiet] - Unused legacy flag; quiet is default unless debug
56
171
  * @returns {Promise<void>}
57
172
  * @throws {Error} If deployJob exits non-zero
58
173
  */
@@ -61,7 +176,8 @@ async function runDeployJobIfConfigured({
61
176
  fullImage,
62
177
  runEnvPath = null,
63
178
  devMountPath = null,
64
- debug = false
179
+ debug = false,
180
+ quiet = false
65
181
  }) {
66
182
  const cmd = resolveDeployJobCommand(appConfig);
67
183
  if (!cmd) {
@@ -72,37 +188,29 @@ async function runDeployJobIfConfigured({
72
188
  }
73
189
  const network = await resolveDeployJobNetwork();
74
190
  const volumeMounts = _deployJobVolumeMounts(devMountPath);
75
- logger.log(infoLine(`Running build.deployJob once before start (${fullImage})…`));
76
- if (debug) {
77
- logger.log(infoLine(`deployJob: ${cmd}`));
78
- logger.log(infoLine(`deployJob network: ${network}`));
79
- if (volumeMounts.length) {
80
- logger.log(infoLine(`deployJob mount: ${volumeMounts[0].host} -> /app`));
81
- }
82
- }
83
- const code = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
191
+ const quietJob = shouldQuietDeployJob({ quiet, debug });
192
+ _logDeployJobStart({ fullImage, cmd, network, volumeMounts, debug });
193
+ const result = await runDockerRunEphemeral(fullImage, cmd, runEnvPath, {
84
194
  network,
85
- volumeMounts
195
+ volumeMounts,
196
+ quiet: quietJob
86
197
  });
87
- if (code !== 0) {
88
- logger.log(formatBlockingError(`build.deployJob failed with exit code ${code}`));
89
- throw new Error(`build.deployJob failed with exit code ${code}`);
198
+ _assertDeployJobExit({ result, quietJob });
199
+ if (quietJob && result.output) {
200
+ const migrateSummary = formatDeployJobMigrateSummary(result.output);
201
+ if (migrateSummary) {
202
+ logger.log(formatSuccessLine(`build.deployJob completed — ${migrateSummary}`));
203
+ return;
204
+ }
90
205
  }
91
206
  logger.log(formatSuccessLine('build.deployJob completed'));
92
207
  }
93
208
 
94
- /**
95
- * @param {string|null|undefined} devMountPath
96
- * @returns {Array<{ host: string, container: string }>}
97
- */
98
- function _deployJobVolumeMounts(devMountPath) {
99
- if (typeof devMountPath !== 'string' || !devMountPath.trim()) {
100
- return [];
101
- }
102
- return [{ host: devMountPath.trim(), container: '/app' }];
103
- }
104
-
105
209
  module.exports = {
106
210
  resolveDeployJobCommand,
107
- runDeployJobIfConfigured
211
+ runDeployJobIfConfigured,
212
+ shouldQuietDeployJob,
213
+ emitDeployJobSummaryLines,
214
+ formatDeployJobMigrateSummary,
215
+ summarizeDeployJobMigrateOutput
108
216
  };
@@ -315,6 +315,37 @@ async function buildMergedRunEnvAndWrite(appName, appConfig, devDir, developerId
315
315
  return { runEnvPath, runEnvAdminPath };
316
316
  }
317
317
 
318
+ /**
319
+ * Rebuild run env files when another use of the shared applications directory removed them.
320
+ * @async
321
+ * @param {string} appName - Application name
322
+ * @param {Object} appConfig - Application configuration
323
+ * @param {string} runEnvPath - Expected .env.run path
324
+ * @param {string} runEnvAdminPath - Expected .env.run.admin path
325
+ * @param {number|string} developerId - Developer ID
326
+ * @param {Object|null} scopeOpts - Environment-scoped run options
327
+ * @returns {Promise<void>}
328
+ */
329
+ async function ensureRunEnvFilesPresent(
330
+ appName,
331
+ appConfig,
332
+ runEnvPath,
333
+ runEnvAdminPath,
334
+ developerId,
335
+ scopeOpts = null
336
+ ) {
337
+ if (fsSync.existsSync(runEnvPath) && fsSync.existsSync(runEnvAdminPath)) {
338
+ return;
339
+ }
340
+ await buildMergedRunEnvAndWrite(
341
+ appName,
342
+ appConfig,
343
+ path.dirname(runEnvPath),
344
+ developerId,
345
+ scopeOpts
346
+ );
347
+ }
348
+
318
349
  /**
319
350
  * Assert generated compose does not contain password literals in environment (ISO 27K).
320
351
  * @param {string} composeContent - Generated docker-compose content
@@ -330,6 +361,7 @@ function assertNoPasswordLiteralsInCompose(composeContent) {
330
361
  module.exports = {
331
362
  cleanApplicationsDir,
332
363
  buildMergedRunEnvAndWrite,
364
+ ensureRunEnvFilesPresent,
333
365
  assertNoPasswordLiteralsInCompose,
334
366
  ensureRunSecretsForApp
335
367
  };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Last-moment run env recovery for the shared applications workspace.
3
+ *
4
+ * @fileoverview Builds the callback used immediately before Docker Compose starts
5
+ * @author AI Fabrix Team
6
+ * @version 2.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const runEnvCompose = require('./run-env-compose');
12
+
13
+ /**
14
+ * Build a recovery hook for run env files in the shared workspace.
15
+ * @param {string} appName - Application name
16
+ * @param {Object} prepared - Prepared run context
17
+ * @returns {Function} Async recovery hook
18
+ */
19
+ function buildRunEnvRecovery(appName, prepared) {
20
+ return () =>
21
+ runEnvCompose.ensureRunEnvFilesPresent(
22
+ appName,
23
+ prepared.appConfig,
24
+ prepared.runEnvPath,
25
+ prepared.runEnvAdminPath,
26
+ prepared.appConfig.developerId,
27
+ {
28
+ runEnvKey: prepared.mergedRunOptions.env,
29
+ effectiveEnvironmentScopedResources:
30
+ prepared.mergedRunOptions.effectiveEnvironmentScopedResources
31
+ }
32
+ );
33
+ }
34
+
35
+ module.exports = { buildRunEnvRecovery };
@@ -253,18 +253,17 @@ async function checkInfraHealthOrThrow(debug, appConfig) {
253
253
  }
254
254
 
255
255
  /**
256
- * Ensure dev directory exists
256
+ * Refresh the shared developer run directory for the current application.
257
+ *
258
+ * The applications-dev-* directory is shared by all applications. Always copy
259
+ * the selected app so files from a previous run cannot leak into this run.
257
260
  * @async
258
261
  * @param {string} appName - Application name
259
262
  * @param {string} developerId - Developer ID
260
263
  * @returns {Promise<string>} Dev directory path
261
264
  */
262
265
  async function ensureDevDirectory(appName, developerId) {
263
- const devDir = buildCopy.getDevDirectory(appName, developerId);
264
- if (!fsSync.existsSync(devDir)) {
265
- await buildCopy.copyBuilderToDevDirectory(appName, developerId);
266
- }
267
- return devDir;
266
+ return buildCopy.copyBuilderToDevDirectory(appName, developerId);
268
267
  }
269
268
 
270
269
  /**
package/lib/app/run.js CHANGED
@@ -28,6 +28,7 @@ const helpers = require('./run-helpers');
28
28
  const { ensureReloadSync, logReloadDevSummary } = require('./run-reload-sync');
29
29
  const { logRestartDevMountSummary } = require('./restart-display');
30
30
  const { checkAndStopContainer } = require('./run-stop-containers');
31
+ const { buildRunEnvRecovery } = require('./run-env-recovery');
31
32
  const { execWithDockerEnv } = require('../utils/docker-exec');
32
33
  const { resolveRunProxyPersistEnabled, resolveRunEffectiveNoProxy } = require('../utils/run-cli-flags');
33
34
 
@@ -178,7 +179,8 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
178
179
  runEnvPath = null,
179
180
  runEnvAdminPath = null,
180
181
  runOptions = {},
181
- devMountPath = null
182
+ devMountPath = null,
183
+ ensureRunEnvFiles = null
182
184
  } = opts;
183
185
  const misoEnvironment = opts.misoEnvironment ?? composeGenerator.resolveMisoEnvironment(runOptions);
184
186
  try {
@@ -188,6 +190,7 @@ async function startAppContainer(appName, tempComposePath, hostPort, appConfig,
188
190
  runEnvAdminPath,
189
191
  runOptions,
190
192
  devMountPath,
193
+ ensureRunEnvFiles,
191
194
  misoEnvironment
192
195
  });
193
196
  await helpers.displayRunStatus(
@@ -410,15 +413,20 @@ async function runApp(appName, options = {}) {
410
413
  fullImage: prepared.mergedRunOptions.image,
411
414
  runEnvPath: prepared.runEnvPath,
412
415
  devMountPath: prepared.devMountPath || null,
413
- debug
416
+ debug,
417
+ // Guided installers already show a top-level spinner; keep migrate/deployJob logs out of TTY.
418
+ quiet:
419
+ options.silentPoll === true || typeof options.onPollText === 'function'
414
420
  });
421
+ const ensureRunEnvFiles = buildRunEnvRecovery(appName, prepared);
415
422
  await startAppContainer(appName, prepared.tempComposePath, prepared.hostPort, prepared.appConfig, {
416
423
  debug,
417
424
  runEnvPath: prepared.runEnvPath,
418
425
  runEnvAdminPath: prepared.runEnvAdminPath,
419
426
  runOptions: prepared.mergedRunOptions,
420
427
  devMountPath: prepared.devMountPath,
421
- runScopeOpts: prepared.runScopeOpts
428
+ runScopeOpts: prepared.runScopeOpts,
429
+ ensureRunEnvFiles
422
430
  });
423
431
  } catch (error) {
424
432
  if (debug) {
@@ -16,17 +16,20 @@ const { formatCredentialPortalStep } = require('./credential-portal');
16
16
  */
17
17
  function buildChatGptApprovalGuide(ctx) {
18
18
  return {
19
- portalUrl: 'https://platform.openai.com/',
19
+ portalUrl: 'https://chatgpt.com/',
20
20
  steps: [
21
- 'Sign in to OpenAI Platform (https://platform.openai.com/) or your ChatGPT Enterprise workspace admin console.',
22
- 'Enable Developer mode / custom MCP app creation for the workspace (if policy requires admin allow-listing).',
23
- 'Create a custom MCP app using the generated channel-chatgpt.json reference artifact (path shown below).',
24
- 'Configure the Runtime MCP endpoint from INSTALL.md and verify Runtime Layer B tool discovery.',
25
- 'Complete workspace authentication setup for the custom MCP app.',
26
- formatCredentialPortalStep(ctx, 'workspace credential references used by the ChatGPT channel'),
27
- 'Publish the app to your workspace and configure end-user access controls.',
21
+ 'Sign in to the ChatGPT web app as a workspace admin or owner.',
22
+ 'Enable Developer mode for custom MCP apps (Business: Workspace settings → Apps, or Settings → Apps → Advanced settings; Enterprise/Edu: grant Developer mode under Permissions & roles → Connected data first).',
23
+ 'Create a custom MCP app with the Remote MCP URL from INSTALL.md (channel-chatgpt.json is a deployment reference, not a plugin archive).',
24
+ 'Select OAuth (authorization-code + PKCE S256). Do not configure a custom API key for a remote custom MCP app.',
25
+ 'Scan tools, complete OAuth if prompted, review discovered Runtime Layer B tools, then save the app as a draft.',
26
+ formatCredentialPortalStep(
27
+ ctx,
28
+ `${ctx.credentialKey || 'channel'} credential references (not secrets to paste into ChatGPT)`
29
+ ),
30
+ 'Test representative workflows, then publish the app and configure workspace access and action controls.',
28
31
  `After ChatGPT lists the app as active, re-run: ${ctx.resumeCommand}`,
29
- 'Optional: use --probe for AI Fabrix-side runtime checks; OpenAI-side publication remains a manual admin responsibility.'
32
+ 'Optional: use --probe for AI Fabrix-side Runtime checks; ChatGPT Scan tools / OAuth / publication remain workspace-admin responsibilities.'
30
33
  ]
31
34
  };
32
35
  }
@@ -113,10 +113,27 @@ function getDefaultChannelDescription() {
113
113
  );
114
114
  }
115
115
 
116
+ /**
117
+ * Human-readable supported channel list for CLI help and error hints.
118
+ * @returns {string}
119
+ */
120
+ function formatSupportedChannelTargetsHelp() {
121
+ const stable = CHANNEL_TARGET_KEYS.filter((key) => !CHANNEL_TARGETS[key].experimental);
122
+ const experimental = CHANNEL_TARGET_KEYS.filter((key) => CHANNEL_TARGETS[key].experimental);
123
+ const lines = ['Supported channels:', ` ${stable.join(', ')}`];
124
+ if (experimental.length > 0) {
125
+ lines.push(
126
+ ` ${experimental.join(', ')} (experimental — scaffold with --allow-experimental)`
127
+ );
128
+ }
129
+ return lines.join('\n');
130
+ }
131
+
116
132
  module.exports = {
117
133
  CHANNEL_TARGETS,
118
134
  CHANNEL_TARGET_KEYS,
119
135
  isChannelTargetKey,
120
136
  getChannelTarget,
121
- getDefaultChannelDescription
137
+ getDefaultChannelDescription,
138
+ formatSupportedChannelTargetsHelp
122
139
  };
@@ -12,7 +12,7 @@ const RUNTIME_ORCHESTRATION_SYSTEM_KEY = 'runtime-orchestration';
12
12
 
13
13
  const CHATGPT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
14
14
  const CHATGPT_RUNTIME_TRANSPORT = 'streamable-http';
15
- const CHATGPT_RUNTIME_AUTH_MODE = 'workspace-managed-oauth-or-api-key';
15
+ const CHATGPT_RUNTIME_AUTH_MODE = 'oauth21-authorization-code-pkce-s256';
16
16
 
17
17
  const COPILOT_RUNTIME_SYSTEM_KEY = RUNTIME_ORCHESTRATION_SYSTEM_KEY;
18
18
  const COPILOT_RUNTIME_TRANSPORT = 'streamable-http';
@@ -95,7 +95,7 @@ function logPlatformSetupDurationHint(scope = 'platform') {
95
95
  const line =
96
96
  scope === 'dataplane'
97
97
  ? 'Installation may take a few minutes. Authenticating and starting Dataplane — please wait.'
98
- : 'Installation may take a few minutes. Keycloak, Miso Controller, and Dataplane are starting — please wait.';
98
+ : 'Installation may take a few minutes. Keycloak, Miso Controller, Dataplane, and Builder API are starting — please wait.';
99
99
  logger.log(chalk.yellow(`⚠ ${line}`));
100
100
  }
101
101
 
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Consolidate deferred public-URL health warnings for guided install UX.
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ const logger = require('../utils/logger');
8
+ const { formatWarningLine } = require('../utils/cli-test-layout-chalk');
9
+
10
+ /**
11
+ * @param {string} message
12
+ * @returns {{ kind: 'dns'|'localhost', url: string }|null}
13
+ */
14
+ function extractPublicUrlFromWarning(message) {
15
+ const text = String(message || '');
16
+ const dnsMatch = text.match(/Public URL was not verified \(DNS\):\s*(\S+)/);
17
+ if (dnsMatch) {
18
+ return { kind: 'dns', url: dnsMatch[1].replace(/\.$/, '') };
19
+ }
20
+ const match = text.match(/Public URL was not verified:\s*(\S+)/);
21
+ if (match) {
22
+ return { kind: 'localhost', url: match[1].replace(/\.$/, '') };
23
+ }
24
+ return null;
25
+ }
26
+
27
+ /**
28
+ * Print one consolidated public-URL warning (avoid N near-duplicate lines).
29
+ * @param {string[]} messages
30
+ */
31
+ function flushDeferredPublicUrlWarnings(messages) {
32
+ const list = (messages || []).filter(Boolean);
33
+ if (list.length === 0) {
34
+ return;
35
+ }
36
+ const parsed = list.map(extractPublicUrlFromWarning).filter(Boolean);
37
+ if (parsed.length === 0) {
38
+ for (const message of list) {
39
+ logger.log(formatWarningLine(message));
40
+ }
41
+ return;
42
+ }
43
+ const urls = [...new Set(parsed.map((row) => row.url))];
44
+ const hasDns = parsed.some((row) => row.kind === 'dns');
45
+ const urlPart = urls.length === 1 ? urls[0] : urls.join(', ');
46
+ const detail = hasDns
47
+ ? 'Hostname DNS and/or Traefik routing/TLS could not be verified; health succeeded via localhost only.'
48
+ : 'Health checks succeeded via localhost only. Validate Traefik routing, TLS, and DNS.';
49
+ logger.log(formatWarningLine(`Public URL was not verified: ${urlPart}. ${detail}`));
50
+ }
51
+
52
+ module.exports = {
53
+ extractPublicUrlFromWarning,
54
+ flushDeferredPublicUrlWarnings
55
+ };