@aifabrix/builder 2.46.0 → 2.47.1
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/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/jest.isolated-projects.js +18 -2
- package/jest.projects.js +24 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/lifecycle.api.js +22 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/down.js +58 -26
- package/lib/app/index.js +2 -2
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +17 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +18 -4
- package/lib/cli/infra-guided-auth.js +36 -12
- package/lib/cli/setup-app.js +54 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-npm.js +59 -0
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-secrets.js +1 -1
- package/lib/cli/setup-utility-help-after.js +4 -1
- package/lib/cli/setup-utility-resolve.js +8 -5
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-dataplane-version.js +97 -61
- package/lib/commands/auth-status.js +2 -7
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource.js +1 -1
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/login-device.js +11 -3
- package/lib/commands/login.js +8 -3
- package/lib/commands/npm-config.js +103 -0
- package/lib/commands/repair-auth-apply.js +30 -4
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource.js +53 -18
- package/lib/commands/repair-env-template.js +12 -12
- package/lib/commands/repair-openapi-sync.js +26 -4
- package/lib/commands/repair-steps.js +28 -13
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/setup-modes.js +1 -1
- package/lib/commands/setup-prompts-platform-mode.js +24 -21
- package/lib/commands/test-trust-external.js +39 -90
- package/lib/commands/up-common.js +33 -1
- package/lib/commands/up-dataplane.js +8 -4
- package/lib/commands/upload.js +24 -3
- package/lib/commands/verify-trust-command-action.js +4 -1
- package/lib/commands/verify-trust-external.js +12 -20
- package/lib/core/config-setup-platform-mode.js +3 -3
- package/lib/core/setup-platform-mode.js +41 -6
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/validate.js +41 -23
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +61 -122
- package/lib/external-system/generator.js +1 -59
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +5 -2
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/lifecycle/certification-progress.js +2 -2
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +14 -13
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +16 -0
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +372 -58
- package/lib/schema/external-system.schema.json +331 -2
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/api.js +2 -1
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-utils.js +4 -0
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +9 -1
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/controller-url.js +4 -1
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +52 -262
- package/lib/utils/dataplane-resolver.js +4 -115
- package/lib/utils/declarative-url-ports.js +24 -1
- package/lib/utils/dev-platform-tls.js +148 -0
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/external-env-template.js +10 -3
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/health-check-request.js +114 -0
- package/lib/utils/health-check-url.js +3 -1
- package/lib/utils/health-check.js +1 -113
- package/lib/utils/help-builder.js +3 -0
- package/lib/utils/keycloak-realm-ready.js +5 -1
- package/lib/utils/localhost-public-host.js +31 -0
- package/lib/utils/npm-user-config.js +145 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +58 -4
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/url-declarative-public-base.js +33 -25
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +5 -0
- package/templates/applications/dataplane/env.template +26 -13
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +37 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/python/docker-compose.hbs +56 -0
- package/templates/typescript/docker-compose.hbs +52 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev platform HTTPS: builder-server nginx front door uses TLS while infra `tlsEnabled` may be off.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Self-signed TLS allowance for remote dev platform URLs
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const https = require('https');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const { isLocalhostPublicSchemeHostname } = require('./localhost-public-host');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} url
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function isHttpsNonLocalhostUrl(url) {
|
|
20
|
+
try {
|
|
21
|
+
const u = new URL(url);
|
|
22
|
+
return u.protocol === 'https:' && !isLocalhostPublicSchemeHostname(u.hostname);
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} requestHostname
|
|
30
|
+
* @param {string} remoteServerUrl
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
function hostnameMatchesRemoteDevPlatform(requestHostname, remoteServerUrl) {
|
|
34
|
+
let remoteHost = '';
|
|
35
|
+
try {
|
|
36
|
+
remoteHost = new URL(remoteServerUrl).hostname.toLowerCase();
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const host = String(requestHostname || '').trim().toLowerCase();
|
|
41
|
+
if (!host || !remoteHost) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return host === remoteHost || host.endsWith(`.${remoteHost}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* True when URL is a remote dev platform HTTPS endpoint (builder-server nginx / Traefik front door).
|
|
49
|
+
*
|
|
50
|
+
* @async
|
|
51
|
+
* @param {string} url
|
|
52
|
+
* @returns {Promise<boolean>}
|
|
53
|
+
*/
|
|
54
|
+
async function shouldAllowInsecureDevPlatformTls(url) {
|
|
55
|
+
if (!isHttpsNonLocalhostUrl(url)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const remote = await config.getRemoteServer();
|
|
60
|
+
const remoteTrimmed = remote && String(remote).trim();
|
|
61
|
+
if (!remoteTrimmed) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const hostname = new URL(url).hostname;
|
|
65
|
+
return hostnameMatchesRemoteDevPlatform(hostname, remoteTrimmed);
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Minimal fetch for self-signed dev platform HTTPS (Node global fetch cannot set rejectUnauthorized).
|
|
73
|
+
*
|
|
74
|
+
* @param {string} url
|
|
75
|
+
* @param {Object} options
|
|
76
|
+
* @returns {Promise<{ status: number, statusText: string, headers: { get: Function }, text: Function, json: Function }>}
|
|
77
|
+
*/
|
|
78
|
+
function nodeHttpsFetchInsecure(url, options = {}) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const u = new URL(url);
|
|
81
|
+
const method = String(options.method || 'GET').toUpperCase();
|
|
82
|
+
const headers = { ...(options.headers || {}) };
|
|
83
|
+
const body = options.body;
|
|
84
|
+
const reqOpts = {
|
|
85
|
+
hostname: u.hostname,
|
|
86
|
+
port: u.port || 443,
|
|
87
|
+
path: `${u.pathname}${u.search}`,
|
|
88
|
+
method,
|
|
89
|
+
headers,
|
|
90
|
+
rejectUnauthorized: false
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const req = https.request(reqOpts, (res) => {
|
|
94
|
+
const chunks = [];
|
|
95
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
96
|
+
res.on('end', () => {
|
|
97
|
+
const raw = Buffer.concat(chunks).toString('utf8');
|
|
98
|
+
const status = res.statusCode || 0;
|
|
99
|
+
resolve({
|
|
100
|
+
ok: status >= 200 && status < 300,
|
|
101
|
+
status,
|
|
102
|
+
statusText: res.statusMessage || '',
|
|
103
|
+
headers: {
|
|
104
|
+
get: (name) => res.headers[String(name || '').toLowerCase()] || null
|
|
105
|
+
},
|
|
106
|
+
text: async() => raw,
|
|
107
|
+
json: async() => JSON.parse(raw)
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
req.on('error', reject);
|
|
113
|
+
if (options.signal) {
|
|
114
|
+
const onAbort = () => {
|
|
115
|
+
req.destroy(new Error('The operation was aborted'));
|
|
116
|
+
};
|
|
117
|
+
if (options.signal.aborted) {
|
|
118
|
+
onAbort();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
options.signal.addEventListener('abort', onAbort, { once: true });
|
|
122
|
+
}
|
|
123
|
+
if (body !== undefined && body !== null) {
|
|
124
|
+
req.write(typeof body === 'string' ? body : String(body));
|
|
125
|
+
}
|
|
126
|
+
req.end();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @async
|
|
132
|
+
* @param {string} url
|
|
133
|
+
* @param {Object} options
|
|
134
|
+
* @returns {Promise<Response>}
|
|
135
|
+
*/
|
|
136
|
+
async function fetchWithOptionalDevPlatformTls(url, options = {}) {
|
|
137
|
+
if (await shouldAllowInsecureDevPlatformTls(url)) {
|
|
138
|
+
return nodeHttpsFetchInsecure(url, options);
|
|
139
|
+
}
|
|
140
|
+
return fetch(url, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
isHttpsNonLocalhostUrl,
|
|
145
|
+
hostnameMatchesRemoteDevPlatform,
|
|
146
|
+
shouldAllowInsecureDevPlatformTls,
|
|
147
|
+
fetchWithOptionalDevPlatformTls
|
|
148
|
+
};
|
|
@@ -264,10 +264,7 @@ async function executeDockerBuildWithTag(effectiveImageName, imageName, dockerfi
|
|
|
264
264
|
|
|
265
265
|
await executeBuild(effectiveImageName, dockerfilePath, contextPath, tag, options);
|
|
266
266
|
|
|
267
|
-
const wantCompatTag =
|
|
268
|
-
effectiveImageName !== imageName &&
|
|
269
|
-
options &&
|
|
270
|
-
options.base === true;
|
|
267
|
+
const wantCompatTag = effectiveImageName !== imageName && imageName;
|
|
271
268
|
if (!wantCompatTag) {
|
|
272
269
|
return;
|
|
273
270
|
}
|
|
@@ -13,6 +13,7 @@ const path = require('path');
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const Handlebars = require('handlebars');
|
|
15
15
|
const { systemKeyToKvPrefix, kvEnvKeyToPath, securityKeyToVar } = require('./credential-secrets-env');
|
|
16
|
+
const { getAuthCredentialTemplate } = require('./authentication-shape');
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Builds hint string from portalInput (options → enum, validation → min-max or pattern).
|
|
@@ -63,9 +64,15 @@ function buildAuthSecureVarsFromSystem(system) {
|
|
|
63
64
|
const systemKey = system?.key || 'external-system';
|
|
64
65
|
const prefix = systemKeyToKvPrefix(systemKey);
|
|
65
66
|
if (!prefix) return authSecureVars;
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const authBlock = system?.authentication || system?.auth;
|
|
68
|
+
const credential = getAuthCredentialTemplate(authBlock);
|
|
69
|
+
const security = credential?.security || authBlock?.security;
|
|
70
|
+
const authMethod = (
|
|
71
|
+
credential?.method ||
|
|
72
|
+
authBlock?.method ||
|
|
73
|
+
authBlock?.type ||
|
|
74
|
+
'apikey'
|
|
75
|
+
).toLowerCase();
|
|
69
76
|
if (security && typeof security === 'object' && Object.keys(security).length > 0) {
|
|
70
77
|
for (const key of Object.keys(security)) {
|
|
71
78
|
const envName = `KV_${prefix}_${securityKeyToVar(key)}`;
|
|
@@ -392,8 +392,7 @@ function validateAgainstSchema(data, schema) {
|
|
|
392
392
|
validateSchema: false
|
|
393
393
|
});
|
|
394
394
|
addFormats(ajv);
|
|
395
|
-
//
|
|
396
|
-
// Adding them up-front keeps validation offline-safe and avoids unresolved $ref failures.
|
|
395
|
+
// Type schemas are referenced by $id from external-system and external-datasource manifests.
|
|
397
396
|
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
398
397
|
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
399
398
|
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
@@ -404,6 +403,13 @@ function validateAgainstSchema(data, schema) {
|
|
|
404
403
|
}
|
|
405
404
|
// Normalize schema to handle nullable without type
|
|
406
405
|
const normalizedSchema = normalizeSchema(schemaCopy);
|
|
406
|
+
const schemaId = normalizedSchema.$id || '';
|
|
407
|
+
if (schemaId.includes('external-datasource')) {
|
|
408
|
+
const externalSystemSchema = require('../schema/external-system.schema.json');
|
|
409
|
+
if (!ajv.getSchema(externalSystemSchema.$id)) {
|
|
410
|
+
ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
407
413
|
const validate = ajv.compile(normalizedSchema);
|
|
408
414
|
const valid = validate(data);
|
|
409
415
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP(S) health probe request helpers.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Low-level health endpoint HTTP client
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const http = require('http');
|
|
12
|
+
const https = require('https');
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
const logger = require('./logger');
|
|
15
|
+
const { shouldAllowInsecureDevPlatformTls } = require('./dev-platform-tls');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} data
|
|
19
|
+
* @param {number} statusCode
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function parseHealthResponse(data, statusCode) {
|
|
23
|
+
try {
|
|
24
|
+
const health = JSON.parse(data);
|
|
25
|
+
if (health.status === 'UP') {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (health.status === 'ok') {
|
|
29
|
+
return health.database === 'connected' || !health.database;
|
|
30
|
+
}
|
|
31
|
+
if (health.status === 'healthy') {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (health.success === true) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
} catch {
|
|
39
|
+
return statusCode === 200;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleHealthResponse(res, data, debug, resolve) {
|
|
44
|
+
const isHealthy = parseHealthResponse(data, res.statusCode);
|
|
45
|
+
if (debug) {
|
|
46
|
+
const truncatedData = data.length > 200 ? data.substring(0, 200) + '...' : data;
|
|
47
|
+
logger.log(chalk.gray(`[DEBUG] Response body: ${truncatedData}`));
|
|
48
|
+
logger.log(chalk.gray(`[DEBUG] Health check result: ${isHealthy ? 'healthy' : 'unhealthy'}`));
|
|
49
|
+
}
|
|
50
|
+
resolve(isHealthy);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function doHealthCheckRequest(healthCheckUrl, debug, resolve, reject, allowInsecureTls = false) {
|
|
54
|
+
try {
|
|
55
|
+
const urlObj = new URL(healthCheckUrl);
|
|
56
|
+
const transport = urlObj.protocol === 'https:' ? https : http;
|
|
57
|
+
const options = {
|
|
58
|
+
hostname: urlObj.hostname,
|
|
59
|
+
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
60
|
+
path: urlObj.pathname + urlObj.search,
|
|
61
|
+
method: 'GET',
|
|
62
|
+
...(allowInsecureTls ? { rejectUnauthorized: false } : {})
|
|
63
|
+
};
|
|
64
|
+
if (debug) {
|
|
65
|
+
logger.log(chalk.gray(`[DEBUG] Health check request: ${healthCheckUrl}`));
|
|
66
|
+
logger.log(chalk.gray(`[DEBUG] Request options: ${JSON.stringify(options, null, 2)}`));
|
|
67
|
+
}
|
|
68
|
+
const timer = { id: null };
|
|
69
|
+
const req = transport.request(options, (res) => {
|
|
70
|
+
clearTimeout(timer.id);
|
|
71
|
+
let data = '';
|
|
72
|
+
if (debug) {
|
|
73
|
+
logger.log(chalk.gray(`[DEBUG] Response status code: ${res.statusCode}`));
|
|
74
|
+
logger.log(chalk.gray(`[DEBUG] Response headers: ${JSON.stringify(res.headers, null, 2)}`));
|
|
75
|
+
}
|
|
76
|
+
res.on('data', (chunk) => {
|
|
77
|
+
data += chunk;
|
|
78
|
+
});
|
|
79
|
+
res.on('end', () => handleHealthResponse(res, data, debug, resolve));
|
|
80
|
+
});
|
|
81
|
+
timer.id = setTimeout(() => {
|
|
82
|
+
if (debug) logger.log(chalk.gray('[DEBUG] Health check request timeout after 5 seconds'));
|
|
83
|
+
req.destroy();
|
|
84
|
+
resolve(false);
|
|
85
|
+
}, 5000);
|
|
86
|
+
req.on('error', (error) => {
|
|
87
|
+
clearTimeout(timer.id);
|
|
88
|
+
if (debug) logger.log(chalk.gray(`[DEBUG] Health check request error: ${error.message}`));
|
|
89
|
+
resolve(false);
|
|
90
|
+
});
|
|
91
|
+
req.end();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (debug) logger.log(chalk.gray(`[DEBUG] Health check exception: ${error.message}`));
|
|
94
|
+
reject(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @async
|
|
100
|
+
* @param {string} healthCheckUrl
|
|
101
|
+
* @param {boolean} [debug=false]
|
|
102
|
+
* @returns {Promise<boolean>}
|
|
103
|
+
*/
|
|
104
|
+
async function checkHealthEndpoint(healthCheckUrl, debug = false) {
|
|
105
|
+
const allowInsecureTls = await shouldAllowInsecureDevPlatformTls(healthCheckUrl);
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
doHealthCheckRequest(healthCheckUrl, debug, resolve, reject, allowInsecureTls);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
parseHealthResponse,
|
|
113
|
+
checkHealthEndpoint
|
|
114
|
+
};
|
|
@@ -92,6 +92,7 @@ async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig)
|
|
|
92
92
|
const remoteServer = await coreConfig.getRemoteServer();
|
|
93
93
|
const developerIdRaw = await coreConfig.getDeveloperId();
|
|
94
94
|
const developerIdNum = parseDeveloperIdNum(developerIdRaw);
|
|
95
|
+
const setupPlatformMode = await coreConfig.getSetupPlatformMode();
|
|
95
96
|
|
|
96
97
|
// URLs are resolved for the CLI on the host, not from inside a container.
|
|
97
98
|
const profile = 'local';
|
|
@@ -107,7 +108,8 @@ async function computeTraefikPublicAppUrl(_appName, _healthCheckPort, appConfig)
|
|
|
107
108
|
profile,
|
|
108
109
|
listenPort,
|
|
109
110
|
developerIdNum,
|
|
110
|
-
infraTlsEnabled
|
|
111
|
+
infraTlsEnabled,
|
|
112
|
+
setupPlatformMode
|
|
111
113
|
});
|
|
112
114
|
|
|
113
115
|
return joinUrlPath(publicBase, mountPath);
|
|
@@ -9,9 +9,8 @@ const { formatSuccessLine } = require('./cli-test-layout-chalk');
|
|
|
9
9
|
* @version 2.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const http = require('http');
|
|
13
|
-
const https = require('https');
|
|
14
12
|
const net = require('net');
|
|
13
|
+
const { checkHealthEndpoint } = require('./health-check-request');
|
|
15
14
|
const dns = require('dns');
|
|
16
15
|
const chalk = require('chalk');
|
|
17
16
|
const logger = require('./logger');
|
|
@@ -192,117 +191,6 @@ async function getContainerPort(appName, debug = false) {
|
|
|
192
191
|
return 3000;
|
|
193
192
|
}
|
|
194
193
|
|
|
195
|
-
/**
|
|
196
|
-
* Parses health check response
|
|
197
|
-
* @function parseHealthResponse
|
|
198
|
-
* @param {string} data - Response data
|
|
199
|
-
* @param {number} statusCode - HTTP status code
|
|
200
|
-
* @returns {boolean} True if healthy
|
|
201
|
-
*/
|
|
202
|
-
function parseHealthResponse(data, statusCode) {
|
|
203
|
-
try {
|
|
204
|
-
const health = JSON.parse(data);
|
|
205
|
-
if (health.status === 'UP') {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
if (health.status === 'ok') {
|
|
209
|
-
return health.database === 'connected' || !health.database;
|
|
210
|
-
}
|
|
211
|
-
if (health.status === 'healthy') {
|
|
212
|
-
return true;
|
|
213
|
-
}
|
|
214
|
-
if (health.success === true) {
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
return false;
|
|
218
|
-
} catch (error) {
|
|
219
|
-
return statusCode === 200;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function handleHealthResponse(res, data, debug, resolve) {
|
|
224
|
-
const isHealthy = parseHealthResponse(data, res.statusCode);
|
|
225
|
-
if (debug) {
|
|
226
|
-
const truncatedData = data.length > 200 ? data.substring(0, 200) + '...' : data;
|
|
227
|
-
logger.log(chalk.gray(`[DEBUG] Response body: ${truncatedData}`));
|
|
228
|
-
logger.log(chalk.gray(`[DEBUG] Health check result: ${isHealthy ? 'healthy' : 'unhealthy'}`));
|
|
229
|
-
}
|
|
230
|
-
resolve(isHealthy);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function doHealthCheckRequest(healthCheckUrl, debug, resolve, reject) {
|
|
234
|
-
try {
|
|
235
|
-
const urlObj = new URL(healthCheckUrl);
|
|
236
|
-
const transport = urlObj.protocol === 'https:' ? https : http;
|
|
237
|
-
const options = {
|
|
238
|
-
hostname: urlObj.hostname,
|
|
239
|
-
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
|
|
240
|
-
path: urlObj.pathname + urlObj.search,
|
|
241
|
-
method: 'GET'
|
|
242
|
-
};
|
|
243
|
-
if (debug) {
|
|
244
|
-
logger.log(chalk.gray(`[DEBUG] Health check request: ${healthCheckUrl}`));
|
|
245
|
-
logger.log(chalk.gray(`[DEBUG] Request options: ${JSON.stringify(options, null, 2)}`));
|
|
246
|
-
}
|
|
247
|
-
const timer = { id: null };
|
|
248
|
-
const req = transport.request(options, (res) => {
|
|
249
|
-
clearTimeout(timer.id);
|
|
250
|
-
let data = '';
|
|
251
|
-
if (debug) {
|
|
252
|
-
logger.log(chalk.gray(`[DEBUG] Response status code: ${res.statusCode}`));
|
|
253
|
-
logger.log(chalk.gray(`[DEBUG] Response headers: ${JSON.stringify(res.headers, null, 2)}`));
|
|
254
|
-
}
|
|
255
|
-
res.on('data', (chunk) => {
|
|
256
|
-
data += chunk;
|
|
257
|
-
});
|
|
258
|
-
res.on('end', () => handleHealthResponse(res, data, debug, resolve));
|
|
259
|
-
});
|
|
260
|
-
timer.id = setTimeout(() => {
|
|
261
|
-
if (debug) logger.log(chalk.gray('[DEBUG] Health check request timeout after 5 seconds'));
|
|
262
|
-
req.destroy();
|
|
263
|
-
resolve(false);
|
|
264
|
-
}, 5000);
|
|
265
|
-
req.on('error', (error) => {
|
|
266
|
-
clearTimeout(timer.id);
|
|
267
|
-
if (debug) logger.log(chalk.gray(`[DEBUG] Health check request error: ${error.message}`));
|
|
268
|
-
resolve(false);
|
|
269
|
-
});
|
|
270
|
-
req.end();
|
|
271
|
-
} catch (error) {
|
|
272
|
-
if (debug) logger.log(chalk.gray(`[DEBUG] Health check exception: ${error.message}`));
|
|
273
|
-
reject(error);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Checks health endpoint
|
|
279
|
-
* @async
|
|
280
|
-
* @function checkHealthEndpoint
|
|
281
|
-
* @param {string} healthCheckUrl - Health check URL
|
|
282
|
-
* @param {boolean} [debug=false] - Enable debug logging
|
|
283
|
-
* @returns {Promise<boolean>} True if healthy
|
|
284
|
-
* @throws {Error} If request fails with exception
|
|
285
|
-
*/
|
|
286
|
-
async function checkHealthEndpoint(healthCheckUrl, debug = false) {
|
|
287
|
-
return new Promise((resolve, reject) => {
|
|
288
|
-
doHealthCheckRequest(healthCheckUrl, debug, resolve, reject);
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Waits for application health check to pass
|
|
294
|
-
* Checks HTTP endpoint and waits for healthy response
|
|
295
|
-
*
|
|
296
|
-
* @async
|
|
297
|
-
* @function waitForHealthCheck
|
|
298
|
-
* @param {string} appName - Application name
|
|
299
|
-
* @param {number} timeout - Timeout in seconds (default: 90)
|
|
300
|
-
* @param {number} [port] - Application port (auto-detected if not provided)
|
|
301
|
-
* @param {Object} [config] - Application configuration
|
|
302
|
-
* @param {boolean} [debug=false] - Enable debug logging
|
|
303
|
-
* @returns {Promise<void>} Resolves when health check passes
|
|
304
|
-
* @throws {Error} If health check times out
|
|
305
|
-
*/
|
|
306
194
|
/**
|
|
307
195
|
* Determines health check port
|
|
308
196
|
* @async
|
|
@@ -76,6 +76,7 @@ const CATEGORIES = [
|
|
|
76
76
|
{ name: 'integration-client' },
|
|
77
77
|
{ name: 'dimension' },
|
|
78
78
|
{ name: 'dimension value', term: 'dimension value <command>' },
|
|
79
|
+
{ name: 'resource-type' },
|
|
79
80
|
{ name: 'identity' },
|
|
80
81
|
{ name: 'protection' }
|
|
81
82
|
]
|
|
@@ -101,6 +102,7 @@ const CATEGORIES = [
|
|
|
101
102
|
{ name: 'upload', term: 'upload <systemKey>' },
|
|
102
103
|
{ name: 'delete', term: 'delete <systemKey>' },
|
|
103
104
|
{ name: 'repair', term: 'repair <systemKey>' },
|
|
105
|
+
{ name: 'channel', term: 'channel scaffold <channelTarget>' },
|
|
104
106
|
{ name: 'datasource' },
|
|
105
107
|
{ name: 'datasource load', term: 'datasource load <datasourceKey>' },
|
|
106
108
|
{ name: 'datasource export', term: 'datasource export <datasourceKey>' },
|
|
@@ -119,6 +121,7 @@ const CATEGORIES = [
|
|
|
119
121
|
commands: [
|
|
120
122
|
{ name: 'dev' },
|
|
121
123
|
{ name: 'secret' },
|
|
124
|
+
{ name: 'npm', term: 'npm config' },
|
|
122
125
|
{ name: 'secure' }
|
|
123
126
|
]
|
|
124
127
|
}
|
|
@@ -31,7 +31,11 @@ async function probeRealmWellKnown(url) {
|
|
|
31
31
|
const controller = new AbortController();
|
|
32
32
|
const timer = setTimeout(() => controller.abort(), 8000);
|
|
33
33
|
try {
|
|
34
|
-
const
|
|
34
|
+
const { fetchWithOptionalDevPlatformTls } = require('./dev-platform-tls');
|
|
35
|
+
const res = await fetchWithOptionalDevPlatformTls(url, {
|
|
36
|
+
method: 'GET',
|
|
37
|
+
signal: controller.signal
|
|
38
|
+
});
|
|
35
39
|
return res.ok;
|
|
36
40
|
} catch {
|
|
37
41
|
return false;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback hostnames always use http for declarative public URLs.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Localhost hostname detection for public URL scheme
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} hostname
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
15
|
+
function isLocalhostPublicSchemeHostname(hostname) {
|
|
16
|
+
const h = String(hostname || '').trim().toLowerCase();
|
|
17
|
+
if (!h) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (h === 'localhost') {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
if (h === '127.0.0.1') {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return h === '::1';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = {
|
|
30
|
+
isLocalhostPublicSchemeHostname
|
|
31
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge npm user-global auth lines from BASH_NPM_TOKEN / NPM_TOKEN.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview User-level ~/.npmrc auth for @aifabrix scope (not repo .npmrc)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Keys managed by aifabrix npm config (replaced or removed on each run).
|
|
17
|
+
* `always-auth` is legacy — npm 11+ warns at userconfig; auth uses scoped registry + _authToken.
|
|
18
|
+
*/
|
|
19
|
+
const MANAGED_KEYS = new Set([
|
|
20
|
+
'@aifabrix:registry',
|
|
21
|
+
'always-auth',
|
|
22
|
+
'//registry.npmjs.org/:_authToken'
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve npm userconfig file path (~/.npmrc by default).
|
|
29
|
+
*
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
function getUserNpmrcPath() {
|
|
33
|
+
const fromEnv = process.env.npm_config_userconfig || process.env.NPM_CONFIG_USERCONFIG;
|
|
34
|
+
if (fromEnv && typeof fromEnv === 'string' && fromEnv.trim()) {
|
|
35
|
+
return path.resolve(fromEnv.trim());
|
|
36
|
+
}
|
|
37
|
+
return path.join(os.homedir(), '.npmrc');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse a single .npmrc line into key/value (ignores comments and blanks).
|
|
42
|
+
*
|
|
43
|
+
* @param {string} line
|
|
44
|
+
* @returns {{ key: string, value: string }|null}
|
|
45
|
+
*/
|
|
46
|
+
function parseNpmrcLine(line) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith(';')) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const eq = trimmed.indexOf('=');
|
|
52
|
+
if (eq === -1) {
|
|
53
|
+
return { key: trimmed, value: '' };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
key: trimmed.slice(0, eq).trim(),
|
|
57
|
+
value: trimmed.slice(eq + 1).trim()
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Build managed auth lines for @aifabrix private registry.
|
|
63
|
+
*
|
|
64
|
+
* @param {string} token - Bare npm automation token (never logged by callers)
|
|
65
|
+
* @returns {string[]}
|
|
66
|
+
*/
|
|
67
|
+
function buildManagedLines(token) {
|
|
68
|
+
return [
|
|
69
|
+
`@aifabrix:registry=${REGISTRY_URL}`,
|
|
70
|
+
`//registry.npmjs.org/:_authToken=${token}`
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Merge managed lines into existing .npmrc content (idempotent).
|
|
76
|
+
*
|
|
77
|
+
* @param {string} existingContent
|
|
78
|
+
* @param {string} token
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
function mergeNpmrcContent(existingContent, token) {
|
|
82
|
+
const kept = [];
|
|
83
|
+
const lines = (existingContent || '').split(/\r?\n/);
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
const parsed = parseNpmrcLine(line);
|
|
86
|
+
if (parsed && MANAGED_KEYS.has(parsed.key)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
kept.push(line);
|
|
90
|
+
}
|
|
91
|
+
while (kept.length > 0 && kept[kept.length - 1] === '') {
|
|
92
|
+
kept.pop();
|
|
93
|
+
}
|
|
94
|
+
const managed = buildManagedLines(token);
|
|
95
|
+
if (kept.length > 0) {
|
|
96
|
+
return `${kept.join('\n')}\n${managed.join('\n')}\n`;
|
|
97
|
+
}
|
|
98
|
+
return `${managed.join('\n')}\n`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Write merged user-global npm auth from token.
|
|
103
|
+
*
|
|
104
|
+
* @param {string} token
|
|
105
|
+
* @param {{ dryRun?: boolean, npmrcPath?: string }} [options]
|
|
106
|
+
* @returns {{ npmrcPath: string, managedKeys: string[], dryRun: boolean, wrote: boolean }}
|
|
107
|
+
*/
|
|
108
|
+
function writeUserNpmrcFromToken(token, options = {}) {
|
|
109
|
+
const npmrcPath = options.npmrcPath || getUserNpmrcPath();
|
|
110
|
+
const dryRun = Boolean(options.dryRun);
|
|
111
|
+
const managedKeys = [...MANAGED_KEYS];
|
|
112
|
+
if (!token || !String(token).trim()) {
|
|
113
|
+
throw new Error('NPM token is required to update user npm config');
|
|
114
|
+
}
|
|
115
|
+
const existing = fs.existsSync(npmrcPath) ? fs.readFileSync(npmrcPath, 'utf8') : '';
|
|
116
|
+
const merged = mergeNpmrcContent(existing, String(token).trim());
|
|
117
|
+
if (!dryRun) {
|
|
118
|
+
const dir = path.dirname(npmrcPath);
|
|
119
|
+
if (!fs.existsSync(dir)) {
|
|
120
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
fs.writeFileSync(npmrcPath, merged, { mode: 0o600 });
|
|
123
|
+
try {
|
|
124
|
+
fs.chmodSync(npmrcPath, 0o600);
|
|
125
|
+
} catch {
|
|
126
|
+
// best-effort on platforms that restrict chmod
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
npmrcPath,
|
|
131
|
+
managedKeys,
|
|
132
|
+
dryRun,
|
|
133
|
+
wrote: !dryRun
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
MANAGED_KEYS,
|
|
139
|
+
REGISTRY_URL,
|
|
140
|
+
getUserNpmrcPath,
|
|
141
|
+
parseNpmrcLine,
|
|
142
|
+
buildManagedLines,
|
|
143
|
+
mergeNpmrcContent,
|
|
144
|
+
writeUserNpmrcFromToken
|
|
145
|
+
};
|