@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.
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +135 -27
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +5 -6
- package/lib/app/run.js +11 -3
- package/lib/channels/approval-guides/chatgpt.js +12 -9
- package/lib/channels/channel-targets.js +18 -1
- package/lib/channels/chatgpt-runtime-contract.js +1 -1
- package/lib/cli/infra-guided-footers.js +1 -1
- package/lib/cli/infra-guided-public-url-warn.js +55 -0
- package/lib/cli/infra-guided.js +65 -56
- package/lib/cli/setup-app.help.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system.js +50 -12
- package/lib/commands/channel-add-setup.js +8 -2
- package/lib/commands/channel.js +16 -5
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/lifecycle-external.js +14 -7
- package/lib/commands/role-assistant.js +66 -8
- package/lib/commands/test-governance-external.js +2 -5
- package/lib/commands/up-miso.js +61 -24
- package/lib/commands/upload-data-sync-finalize.js +20 -3
- package/lib/commands/verify-governance-external.js +2 -5
- package/lib/commands/verify-operations-external.js +2 -5
- package/lib/commands/verify-operations-step-plan.js +31 -0
- package/lib/commands/verify-trust-command-action.js +2 -5
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/integration-context.js +3 -3
- package/lib/datasource/resolve-app.js +90 -8
- package/lib/datasource/test-e2e.js +2 -2
- package/lib/datasource/unified-validation-run-body.js +15 -1
- package/lib/datasource/unified-validation-run-resolve.js +2 -2
- package/lib/external-system/deploy-helpers.js +9 -5
- package/lib/external-system/deploy.js +2 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +4 -4
- package/lib/generator/builders.js +9 -15
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync.js +25 -5
- package/lib/role-assistant/test-runner-workhub-answers.js +44 -26
- package/lib/role-assistant/test-runner-workhub.js +21 -7
- package/lib/schema/application-schema.json +10 -28
- package/lib/schema/infra.parameter.yaml +0 -20
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/docker-run-ephemeral.js +77 -17
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/paths.js +13 -15
- package/lib/utils/role-assistant-paths.js +76 -14
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate.js +54 -19
- package/package.json +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +3 -4
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/env.template +5 -2
- package/templates/applications/miso-controller/application.yaml +3 -3
- package/templates/applications/miso-controller/env.template +16 -3
- package/templates/channels/chatgpt/install.template.md.hbs +75 -37
- package/templates/channels/chatgpt/integration/README.md +3 -2
- package/templates/channels/chatgpt/manifest.template.json.hbs +3 -3
- package/templates/channels/slack/install.template.md.hbs +6 -1
- package/templates/python/docker-compose.hbs +10 -0
- 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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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 };
|
package/lib/app/run-helpers.js
CHANGED
|
@@ -253,18 +253,17 @@ async function checkInfraHealthOrThrow(debug, appConfig) {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
|
-
*
|
|
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
|
-
|
|
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://
|
|
19
|
+
portalUrl: 'https://chatgpt.com/',
|
|
20
20
|
steps: [
|
|
21
|
-
'Sign in to
|
|
22
|
-
'Enable Developer mode
|
|
23
|
-
'Create a custom MCP app
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
formatCredentialPortalStep(
|
|
27
|
-
|
|
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
|
|
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 = '
|
|
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
|
|
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
|
+
};
|