@aifabrix/builder 2.44.5 → 2.44.6
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/.cursor/rules/cli-layout.mdc +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +48 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +134 -61
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility.js +78 -33
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/up-common.js +79 -19
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +23 -12
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
headerKeyValue,
|
|
20
20
|
formatStatusKeyValue,
|
|
21
21
|
colorRollupPrefixedLine,
|
|
22
|
+
formatSuccessLine,
|
|
22
23
|
metadata: metaGray
|
|
23
24
|
} = require('./cli-test-layout-chalk');
|
|
24
25
|
|
|
@@ -338,7 +339,7 @@ function displayLocalExternalTestPlanLayout(results, verbose, appName) {
|
|
|
338
339
|
function logVerboseSystemRows(systemResults) {
|
|
339
340
|
for (const s of systemResults || []) {
|
|
340
341
|
const ok = s.valid;
|
|
341
|
-
logger.log(ok ? chalk.green(
|
|
342
|
+
logger.log(ok ? (chalk.green(' ') + formatSuccessLine(s.file)) : chalk.red(` ✖ ${s.file}`));
|
|
342
343
|
(s.errors || []).forEach(e => logger.log(chalk.red(` - ${e}`)));
|
|
343
344
|
}
|
|
344
345
|
}
|
|
@@ -346,7 +347,7 @@ function logVerboseSystemRows(systemResults) {
|
|
|
346
347
|
function logVerboseDatasourceRows(datasourceResults) {
|
|
347
348
|
for (const d of datasourceResults || []) {
|
|
348
349
|
const ok = d.valid;
|
|
349
|
-
logger.log(ok ? chalk.green(
|
|
350
|
+
logger.log(ok ? (chalk.green(' ') + formatSuccessLine(`${d.key} (${d.file})`)) : chalk.red(` ✖ ${d.key} (${d.file})`));
|
|
350
351
|
(d.errors || []).forEach(e => logger.log(chalk.red(` - ${e}`)));
|
|
351
352
|
(d.warnings || []).forEach(w => logger.log(chalk.yellow(` ⚠ ${w}`)));
|
|
352
353
|
if (d.fieldMappingResults && d.fieldMappingResults.mappedFields) {
|
|
@@ -43,30 +43,57 @@ function unwrapPublicationResult(res) {
|
|
|
43
43
|
* Rules: inactive/archived → Failed; MCP expected but missing → Partial; draft → Partial; published/deployed + active → Ready.
|
|
44
44
|
* @param {Object} ds - ExternalDataSourceResponse-like
|
|
45
45
|
* @param {boolean} generateMcpContract - From application config
|
|
46
|
-
* @returns {'ready'|'partial'|'failed'}
|
|
46
|
+
* @returns {{ tier: 'ready'|'partial'|'failed', partialReason: string|null }}
|
|
47
47
|
*/
|
|
48
|
-
function
|
|
48
|
+
function classifyDatasourceTierADetail(ds, generateMcpContract) {
|
|
49
49
|
const active = ds.isActive !== false;
|
|
50
50
|
const status = String(ds.status || '').toLowerCase();
|
|
51
51
|
if (!active || status === 'archived') {
|
|
52
|
-
return 'failed';
|
|
52
|
+
return { tier: 'failed', partialReason: null };
|
|
53
53
|
}
|
|
54
54
|
if (generateMcpContract === true && !ds.mcpContract) {
|
|
55
|
-
return 'partial';
|
|
55
|
+
return { tier: 'partial', partialReason: 'mcp_missing' };
|
|
56
56
|
}
|
|
57
57
|
if (status === 'draft') {
|
|
58
|
-
return 'partial';
|
|
58
|
+
return { tier: 'partial', partialReason: 'draft' };
|
|
59
59
|
}
|
|
60
60
|
if (status === 'published' || status === 'deployed') {
|
|
61
|
-
return 'ready';
|
|
61
|
+
return { tier: 'ready', partialReason: null };
|
|
62
|
+
}
|
|
63
|
+
return { tier: 'partial', partialReason: 'unknown_status' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {Object} ds - ExternalDataSourceResponse-like
|
|
68
|
+
* @param {boolean} generateMcpContract - From application config
|
|
69
|
+
* @returns {'ready'|'partial'|'failed'}
|
|
70
|
+
*/
|
|
71
|
+
function classifyDatasourceTierA(ds, generateMcpContract) {
|
|
72
|
+
return classifyDatasourceTierADetail(ds, generateMcpContract).tier;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Short hint for CLI when Tier A is partial (machine codes from classifyDatasourceTierADetail).
|
|
77
|
+
* @param {string|null} partialReason
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
function formatTierAPartialHint(partialReason) {
|
|
81
|
+
if (partialReason === 'mcp_missing') {
|
|
82
|
+
return 'no MCP contract stored (OpenAPI link or generation)';
|
|
83
|
+
}
|
|
84
|
+
if (partialReason === 'draft') {
|
|
85
|
+
return 'status draft (trigger paths / certification gate)';
|
|
62
86
|
}
|
|
63
|
-
|
|
87
|
+
if (partialReason === 'unknown_status') {
|
|
88
|
+
return 'unexpected lifecycle status';
|
|
89
|
+
}
|
|
90
|
+
return '';
|
|
64
91
|
}
|
|
65
92
|
|
|
66
93
|
/**
|
|
67
94
|
* @param {Array<Object>} datasources - Datasource list
|
|
68
95
|
* @param {boolean} generateMcpContract
|
|
69
|
-
* @returns {{ rows: Array<{ key: string, tier: string }>, ready: number, partial: number, failed: number }}
|
|
96
|
+
* @returns {{ rows: Array<{ key: string, tier: string, partialReason?: string|null }>, ready: number, partial: number, failed: number }}
|
|
70
97
|
*/
|
|
71
98
|
function summarizeDatasourceTiersA(datasources, generateMcpContract) {
|
|
72
99
|
const rows = [];
|
|
@@ -75,10 +102,14 @@ function summarizeDatasourceTiersA(datasources, generateMcpContract) {
|
|
|
75
102
|
let failed = 0;
|
|
76
103
|
for (const ds of datasources || []) {
|
|
77
104
|
const key = ds.key || ds.sourceKey || 'unknown';
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (tier === '
|
|
81
|
-
|
|
105
|
+
const detail = classifyDatasourceTierADetail(ds, generateMcpContract);
|
|
106
|
+
const row = { key, tier: detail.tier };
|
|
107
|
+
if (detail.tier === 'partial' && detail.partialReason) {
|
|
108
|
+
row.partialReason = detail.partialReason;
|
|
109
|
+
}
|
|
110
|
+
rows.push(row);
|
|
111
|
+
if (detail.tier === 'ready') ready += 1;
|
|
112
|
+
else if (detail.tier === 'partial') partial += 1;
|
|
82
113
|
else failed += 1;
|
|
83
114
|
}
|
|
84
115
|
return { rows, ready, partial, failed };
|
|
@@ -401,7 +432,9 @@ module.exports = {
|
|
|
401
432
|
unwrapApiData,
|
|
402
433
|
unwrapPublicationResult,
|
|
403
434
|
isPublicationResultShape,
|
|
435
|
+
classifyDatasourceTierADetail,
|
|
404
436
|
classifyDatasourceTierA,
|
|
437
|
+
formatTierAPartialHint,
|
|
405
438
|
summarizeDatasourceTiersA,
|
|
406
439
|
aggregateVerdictFromCounts,
|
|
407
440
|
classifyDatasourceTierB,
|
|
@@ -83,7 +83,7 @@ function logDeployProbeDatasourceSection(probeData) {
|
|
|
83
83
|
* @param {Object|null} systemFromDataplane
|
|
84
84
|
* @param {boolean} genMcp
|
|
85
85
|
*/
|
|
86
|
-
function logDeployContractsSection(systemFromDataplane, genMcp) {
|
|
86
|
+
function logDeployContractsSection(systemFromDataplane, genMcp, dataplaneUrl, systemKey) {
|
|
87
87
|
if (!systemFromDataplane) return;
|
|
88
88
|
logSeparator();
|
|
89
89
|
logSectionTitle('Contracts:');
|
|
@@ -94,7 +94,7 @@ function logDeployContractsSection(systemFromDataplane, genMcp) {
|
|
|
94
94
|
} else {
|
|
95
95
|
logger.log(chalk.gray('○ OpenAPI docs URL not available'));
|
|
96
96
|
}
|
|
97
|
-
logDocsBlock(systemFromDataplane);
|
|
97
|
+
logDocsBlock(systemFromDataplane, { dataplaneUrl, systemKey, genMcp: mcpOk });
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
@@ -260,7 +260,7 @@ function logDeployReadinessSummary(ctx) {
|
|
|
260
260
|
|
|
261
261
|
logDeployIdentityAndCredentialBlocks(systemCfg, !!probeData);
|
|
262
262
|
|
|
263
|
-
logDeployContractsSection(systemFromDataplane, genMcp);
|
|
263
|
+
logDeployContractsSection(systemFromDataplane, genMcp, dataplaneUrl, systemKey);
|
|
264
264
|
logDeployNextActionsSection(systemKey, probeData, summary, genMcp);
|
|
265
265
|
}
|
|
266
266
|
|
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
const chalk = require('chalk');
|
|
10
10
|
const { failureGlyph, successGlyph } = require('./cli-test-layout-chalk');
|
|
11
11
|
const logger = require('./logger');
|
|
12
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
extractIdentitySummary,
|
|
14
|
+
resolveCredentialTestEndpointDisplay,
|
|
15
|
+
formatTierAPartialHint
|
|
16
|
+
} = require('./external-system-readiness-core');
|
|
13
17
|
|
|
14
18
|
const SEP = chalk.gray('────────────────────────────────');
|
|
15
19
|
|
|
@@ -56,8 +60,12 @@ function logDatasourceTable(rows, counts, title) {
|
|
|
56
60
|
logSectionTitle(title && String(title).trim() ? String(title).trim() : 'Datasources:');
|
|
57
61
|
for (const r of rows) {
|
|
58
62
|
const statusLabel = r.tier === 'ready' ? 'Ready' : r.tier === 'failed' ? 'Failed' : 'Partial';
|
|
63
|
+
const hint =
|
|
64
|
+
r.tier === 'partial' && r.partialReason
|
|
65
|
+
? chalk.gray(` — ${formatTierAPartialHint(r.partialReason)}`)
|
|
66
|
+
: '';
|
|
59
67
|
logger.log(
|
|
60
|
-
`${tierGlyph(r.tier)} ${r.key.padEnd(14, ' ')} ${chalk.gray('(' + statusLabel + ')')}`
|
|
68
|
+
`${tierGlyph(r.tier)} ${r.key.padEnd(14, ' ')} ${chalk.gray('(' + statusLabel + ')')}${hint}`
|
|
61
69
|
);
|
|
62
70
|
}
|
|
63
71
|
logger.log('');
|
|
@@ -120,14 +128,35 @@ function logNextActions(actions, extraLine) {
|
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Dataplane serves MCP OpenAPI docs at /api/v1/mcp/{systemKey}/docs.
|
|
133
|
+
*
|
|
134
|
+
* @param {Object} sys - ExternalSystemResponse
|
|
135
|
+
* @param {{ dataplaneUrl?: string, systemKey?: string, genMcp?: boolean }} [opts]
|
|
136
|
+
* @returns {string|null}
|
|
137
|
+
*/
|
|
138
|
+
function deriveMcpDocsPageUrl(sys, opts) {
|
|
139
|
+
if (!sys || !opts) return null;
|
|
140
|
+
if (sys.mcpDocsPageUrl) return String(sys.mcpDocsPageUrl);
|
|
141
|
+
const { dataplaneUrl, systemKey, genMcp } = opts;
|
|
142
|
+
if (!genMcp || !dataplaneUrl || !systemKey) return null;
|
|
143
|
+
if (!sys.mcpServerUrl) return null;
|
|
144
|
+
if (!sys.openApiDocsPageUrl && !sys.apiDocumentUrl) return null;
|
|
145
|
+
const base = String(dataplaneUrl).replace(/\/+$/, '');
|
|
146
|
+
return `${base}/api/v1/mcp/${encodeURIComponent(systemKey)}/docs`;
|
|
147
|
+
}
|
|
148
|
+
|
|
123
149
|
/**
|
|
124
150
|
* @param {Object} sys - ExternalSystemResponse
|
|
151
|
+
* @param {{ dataplaneUrl?: string, systemKey?: string, genMcp?: boolean }} [opts]
|
|
125
152
|
*/
|
|
126
|
-
function logDocsBlock(sys) {
|
|
153
|
+
function logDocsBlock(sys, opts) {
|
|
127
154
|
if (!sys) return;
|
|
128
155
|
const urls = [];
|
|
129
156
|
if (sys.openApiDocsPageUrl) urls.push({ label: 'OpenAPI Docs Page', url: sys.openApiDocsPageUrl });
|
|
130
157
|
if (sys.apiDocumentUrl) urls.push({ label: 'API Docs', url: sys.apiDocumentUrl });
|
|
158
|
+
const mcpDocs = deriveMcpDocsPageUrl(sys, opts);
|
|
159
|
+
if (mcpDocs) urls.push({ label: 'MCP Docs Page', url: mcpDocs });
|
|
131
160
|
if (sys.mcpServerUrl) urls.push({ label: 'MCP Server', url: sys.mcpServerUrl });
|
|
132
161
|
if (urls.length === 0) return;
|
|
133
162
|
logSeparator();
|
|
@@ -147,5 +176,6 @@ module.exports = {
|
|
|
147
176
|
logIdentityBlock,
|
|
148
177
|
logCredentialIntentBlock,
|
|
149
178
|
logNextActions,
|
|
179
|
+
deriveMcpDocsPageUrl,
|
|
150
180
|
logDocsBlock
|
|
151
181
|
};
|
|
@@ -44,7 +44,16 @@ function logPublishResultBlock(publication) {
|
|
|
44
44
|
}
|
|
45
45
|
logSeparator();
|
|
46
46
|
logSectionTitle('MCP Contract:');
|
|
47
|
-
|
|
47
|
+
if (genMcp) {
|
|
48
|
+
logger.log(formatSuccessLine('Generation requested (manifest generateMcpContract)'));
|
|
49
|
+
logger.log(
|
|
50
|
+
chalk.gray(
|
|
51
|
+
' Per-datasource MCP appears when dataplane stores mcpContract (OpenAPI resolves + generation succeeds).'
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
logger.log(chalk.gray('○ Not requested (generateMcpContract false)'));
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
/**
|
package/lib/utils/file-upload.js
CHANGED
|
@@ -33,10 +33,10 @@ async function validateFileExists(filePath) {
|
|
|
33
33
|
* @param {Object} additionalFields - Additional fields
|
|
34
34
|
* @returns {Promise<FormData>} FormData object
|
|
35
35
|
*/
|
|
36
|
-
async function buildFormData(filePath, fieldName, additionalFields) {
|
|
36
|
+
async function buildFormData(filePath, fieldName, additionalFields, opts = {}) {
|
|
37
37
|
const formData = new FormData();
|
|
38
38
|
const fileContent = await fs.readFile(filePath);
|
|
39
|
-
const fileName = path.basename(filePath);
|
|
39
|
+
const fileName = opts.filenameOverride ? String(opts.filenameOverride) : path.basename(filePath);
|
|
40
40
|
const fileBlob = new Blob([fileContent], { type: 'application/octet-stream' });
|
|
41
41
|
formData.append(fieldName, fileBlob, fileName);
|
|
42
42
|
|
|
@@ -74,6 +74,43 @@ async function uploadFile(url, filePath, fieldName = 'file', authConfig = {}, ad
|
|
|
74
74
|
return await client.postFormData(endpointPath, formData);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Upload a file using multipart/form-data but override the filename sent to the server.
|
|
79
|
+
* Useful when the server derives a key from the upload filename.
|
|
80
|
+
*
|
|
81
|
+
* @async
|
|
82
|
+
* @function uploadFileAs
|
|
83
|
+
* @param {string} url - Full API endpoint URL
|
|
84
|
+
* @param {string} filePath - Path to file to upload
|
|
85
|
+
* @param {string} filenameOverride - Filename to present to server (e.g. 'my-key.json')
|
|
86
|
+
* @param {string} fieldName - Form field name for the file (default: 'file')
|
|
87
|
+
* @param {Object} [authConfig] - Authentication configuration
|
|
88
|
+
* @param {Object} [additionalFields] - Additional form fields to include
|
|
89
|
+
* @returns {Promise<Object>} API response
|
|
90
|
+
*/
|
|
91
|
+
async function uploadFileAs(
|
|
92
|
+
url,
|
|
93
|
+
filePath,
|
|
94
|
+
filenameOverride,
|
|
95
|
+
fieldName = 'file',
|
|
96
|
+
authConfig = {},
|
|
97
|
+
additionalFields = {}
|
|
98
|
+
) {
|
|
99
|
+
await validateFileExists(filePath);
|
|
100
|
+
if (!filenameOverride || typeof filenameOverride !== 'string') {
|
|
101
|
+
throw new Error('filenameOverride is required and must be a string');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const parsed = new URL(url);
|
|
105
|
+
const baseUrl = parsed.origin;
|
|
106
|
+
const endpointPath = parsed.pathname + parsed.search;
|
|
107
|
+
|
|
108
|
+
const formData = await buildFormData(filePath, fieldName, additionalFields, { filenameOverride });
|
|
109
|
+
const client = new ApiClient(baseUrl, authConfig);
|
|
110
|
+
return await client.postFormData(endpointPath, formData);
|
|
111
|
+
}
|
|
112
|
+
|
|
77
113
|
module.exports = {
|
|
78
|
-
uploadFile
|
|
114
|
+
uploadFile,
|
|
115
|
+
uploadFileAs
|
|
79
116
|
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
3
|
+
const logger = require('./logger');
|
|
4
|
+
const { execWithDockerEnv } = require('./docker-exec');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Checks if db-init container exists.
|
|
8
|
+
* @async
|
|
9
|
+
* @param {string} dbInitContainer
|
|
10
|
+
* @returns {Promise<boolean>}
|
|
11
|
+
*/
|
|
12
|
+
async function checkDbInitContainerExists(dbInitContainer) {
|
|
13
|
+
try {
|
|
14
|
+
const { stdout } = await execWithDockerEnv(
|
|
15
|
+
`docker ps -a --filter "name=${dbInitContainer}" --format "{{.Names}}"`
|
|
16
|
+
);
|
|
17
|
+
return stdout.trim() === dbInitContainer;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets container exit code.
|
|
25
|
+
* @async
|
|
26
|
+
* @param {string} dbInitContainer
|
|
27
|
+
* @returns {Promise<string>}
|
|
28
|
+
*/
|
|
29
|
+
async function getContainerExitCode(dbInitContainer) {
|
|
30
|
+
const { stdout: exitCode } = await execWithDockerEnv(
|
|
31
|
+
`docker inspect --format='{{.State.ExitCode}}' ${dbInitContainer}`
|
|
32
|
+
);
|
|
33
|
+
return exitCode.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handles exited container status.
|
|
38
|
+
* @async
|
|
39
|
+
* @param {string} dbInitContainer
|
|
40
|
+
* @returns {Promise<boolean>}
|
|
41
|
+
*/
|
|
42
|
+
async function handleExitedContainer(dbInitContainer) {
|
|
43
|
+
const { stdout: status } = await execWithDockerEnv(
|
|
44
|
+
`docker inspect --format='{{.State.Status}}' ${dbInitContainer}`
|
|
45
|
+
);
|
|
46
|
+
if (status.trim() === 'exited') {
|
|
47
|
+
const exitCode = await getContainerExitCode(dbInitContainer);
|
|
48
|
+
if (exitCode === '0') {
|
|
49
|
+
logger.log(formatSuccessLine('Database initialization already completed'));
|
|
50
|
+
} else {
|
|
51
|
+
logger.log(chalk.yellow(`⚠ Database initialization exited with code ${exitCode}`));
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Waits for container to exit (best-effort).
|
|
60
|
+
* @async
|
|
61
|
+
* @param {string} dbInitContainer
|
|
62
|
+
* @param {number} maxAttempts
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
async function waitForContainerExit(dbInitContainer, maxAttempts) {
|
|
66
|
+
for (let attempts = 0; attempts < maxAttempts; attempts++) {
|
|
67
|
+
const { stdout: currentStatus } = await execWithDockerEnv(
|
|
68
|
+
`docker inspect --format='{{.State.Status}}' ${dbInitContainer}`
|
|
69
|
+
);
|
|
70
|
+
if (currentStatus.trim() === 'exited') {
|
|
71
|
+
const exitCode = await getContainerExitCode(dbInitContainer);
|
|
72
|
+
if (exitCode === '0') {
|
|
73
|
+
logger.log(formatSuccessLine('Database initialization completed'));
|
|
74
|
+
} else {
|
|
75
|
+
logger.log(chalk.yellow(`⚠ Database initialization exited with code ${exitCode}`));
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Checks if db-init container exists and waits for it to complete.
|
|
85
|
+
* @async
|
|
86
|
+
* @param {string} appName
|
|
87
|
+
* @returns {Promise<void>}
|
|
88
|
+
*/
|
|
89
|
+
async function waitForDbInit(appName) {
|
|
90
|
+
const dbInitContainer = `aifabrix-${appName}-db-init`;
|
|
91
|
+
try {
|
|
92
|
+
if (!(await checkDbInitContainerExists(dbInitContainer))) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (await handleExitedContainer(dbInitContainer)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger.log(chalk.blue('Waiting for database initialization to complete...'));
|
|
101
|
+
await waitForContainerExit(dbInitContainer, 30);
|
|
102
|
+
} catch {
|
|
103
|
+
// db-init container might not exist, which is fine
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { waitForDbInit };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-success warnings when public/Traefik health URL was skipped or not verified.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Split from health-check.js for file size limits
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const logger = require('./logger');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* When Traefik URL is set, drop it if the hostname does not resolve; retain full URL for UX.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} traefikUrl
|
|
16
|
+
* @param {boolean} debug
|
|
17
|
+
* @param {Function} isHostnameResolvableFn - (hostname, debug) => Promise<boolean>
|
|
18
|
+
* @returns {Promise<{ traefikUrl: string, skippedPublicHealthUrl: string }>}
|
|
19
|
+
*/
|
|
20
|
+
async function filterTraefikUrlByDns(traefikUrl, debug, isHostnameResolvableFn) {
|
|
21
|
+
if (!traefikUrl) {
|
|
22
|
+
return { traefikUrl: '', skippedPublicHealthUrl: '' };
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const hn = new URL(traefikUrl).hostname;
|
|
26
|
+
const ok = await isHostnameResolvableFn(hn, debug);
|
|
27
|
+
if (!ok) {
|
|
28
|
+
return { traefikUrl: '', skippedPublicHealthUrl: traefikUrl };
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
return { traefikUrl: '', skippedPublicHealthUrl: '' };
|
|
32
|
+
}
|
|
33
|
+
return { traefikUrl, skippedPublicHealthUrl: '' };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Object} p
|
|
38
|
+
* @param {string} [p.skippedPublicHealthUrl]
|
|
39
|
+
* @param {string[]} [p.urlsToTry]
|
|
40
|
+
* @param {number} p.resolvedIndex
|
|
41
|
+
*/
|
|
42
|
+
function logPublicHealthUrlWarningIfNeeded(p) {
|
|
43
|
+
const { skippedPublicHealthUrl, urlsToTry, resolvedIndex } = p;
|
|
44
|
+
if (typeof resolvedIndex !== 'number' || resolvedIndex < 0) return;
|
|
45
|
+
|
|
46
|
+
if (skippedPublicHealthUrl) {
|
|
47
|
+
logger.log(
|
|
48
|
+
chalk.yellow(
|
|
49
|
+
`⚠ Public URL was not verified (DNS): ${skippedPublicHealthUrl}. ` +
|
|
50
|
+
'The application reported healthy via localhost only. Validate DNS names and Traefik routing for this host.'
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(urlsToTry) && urlsToTry.length > 1 && resolvedIndex > 0) {
|
|
56
|
+
const pub = urlsToTry[0];
|
|
57
|
+
logger.log(
|
|
58
|
+
chalk.yellow(
|
|
59
|
+
`⚠ Public URL was not verified: ${pub}. ` +
|
|
60
|
+
'Health checks succeeded via localhost only. Validate Traefik routing, TLS, and DNS.'
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
filterTraefikUrlByDns,
|
|
68
|
+
logPublicHealthUrlWarningIfNeeded
|
|
69
|
+
};
|
|
@@ -73,7 +73,16 @@ function frontDoorPattern(appConfig) {
|
|
|
73
73
|
* @param {Object|null} appConfig
|
|
74
74
|
* @returns {Promise<string|null>}
|
|
75
75
|
*/
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Public app URL (Traefik + frontDoor mount path), without the health path — for CLI summaries.
|
|
78
|
+
*
|
|
79
|
+
* @async
|
|
80
|
+
* @param {string} appName
|
|
81
|
+
* @param {number} healthCheckPort
|
|
82
|
+
* @param {Object|null} appConfig
|
|
83
|
+
* @returns {Promise<string|null>}
|
|
84
|
+
*/
|
|
85
|
+
async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig) {
|
|
77
86
|
if (!frontDoorEnabled(appConfig)) return null;
|
|
78
87
|
const pattern = frontDoorPattern(appConfig);
|
|
79
88
|
if (!pattern) return null;
|
|
@@ -87,7 +96,7 @@ async function computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig)
|
|
|
87
96
|
const developerIdRaw = await coreConfig.getDeveloperId();
|
|
88
97
|
const developerIdNum = parseDeveloperIdNum(developerIdRaw);
|
|
89
98
|
|
|
90
|
-
//
|
|
99
|
+
// URLs are resolved for the CLI on the host, not from inside a container.
|
|
91
100
|
const profile = 'local';
|
|
92
101
|
const fd = appConfig.frontDoorRouting;
|
|
93
102
|
const listenPort = Number(appConfig?.port || 3000);
|
|
@@ -105,15 +114,21 @@ async function computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig)
|
|
|
105
114
|
infraTlsEnabled
|
|
106
115
|
});
|
|
107
116
|
|
|
108
|
-
const healthCheckPath = appConfig?.healthCheck?.path || '/health';
|
|
109
117
|
const mountPath = normalizeFrontDoorPatternForHealth(pattern);
|
|
110
|
-
|
|
118
|
+
return joinUrlPath(publicBase, mountPath);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function computeTraefikHealthCheckUrl(appName, healthCheckPort, appConfig) {
|
|
122
|
+
const baseWithFrontDoor = await computeTraefikPublicAppUrl(appName, healthCheckPort, appConfig);
|
|
123
|
+
if (!baseWithFrontDoor) return null;
|
|
124
|
+
const healthCheckPath = appConfig?.healthCheck?.path || '/health';
|
|
111
125
|
return joinUrlPath(baseWithFrontDoor, healthCheckPath);
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
module.exports = {
|
|
115
129
|
joinUrlPath,
|
|
116
130
|
normalizeFrontDoorPatternForHealth,
|
|
131
|
+
computeTraefikPublicAppUrl,
|
|
117
132
|
computeTraefikHealthCheckUrl
|
|
118
133
|
};
|
|
119
134
|
|