@aifabrix/builder 2.51.0 → 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/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +43 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/work.api.js +283 -0
- package/lib/app/run-container-start.js +4 -0
- package/lib/app/run-deploy-job.js +216 -0
- package/lib/app/run-env-compose.js +32 -0
- package/lib/app/run-env-recovery.js +35 -0
- package/lib/app/run-helpers.js +15 -9
- package/lib/app/run.js +18 -2
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +13 -9
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +14 -2
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +25 -1
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +2 -0
- 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.js +4 -0
- package/lib/cli/setup-app.test-commands.js +59 -28
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +62 -12
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/channel-add-setup.js +8 -2
- package/lib/commands/channel.js +16 -5
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- package/lib/commands/deploy-gate-orchestrator.js +3 -2
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/evidence-kind.js +65 -15
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/governance-verify-external.js +2 -5
- package/lib/commands/lifecycle-external.js +21 -8
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-sync.js +34 -17
- package/lib/commands/role-assistant.js +371 -0
- 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 +25 -7
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +31 -10
- 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/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/datasource-identifier.js +4 -3
- package/lib/datasource/datasource-validate-summary.js +2 -2
- 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/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +39 -1
- package/lib/external-system/deploy.js +25 -11
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test-system-level.js +21 -3
- package/lib/external-system/test.js +43 -7
- package/lib/generator/builders.js +12 -15
- package/lib/generator/wizard.js +51 -4
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-spec.js +47 -28
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-template-workspace.js +28 -1
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-markdown-frontmatter.js +75 -0
- package/lib/role-assistant/knowledge-sync.js +485 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +221 -0
- package/lib/role-assistant/test-runner-workhub.js +399 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +24 -28
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +38 -4
- package/lib/schema/external-system.schema.json +4 -4
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/utils/api.js +52 -27
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/certify-external-workspace.js +40 -0
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +115 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +34 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-public-warn.js +31 -13
- package/lib/utils/health-check.js +11 -1
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/paths.js +80 -38
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +160 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +38 -29
- package/lib/utils/upload-sync-options.js +7 -4
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/reserved-system-key-prefixes.js +56 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validate.js +54 -19
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/builder-api/application.yaml +3 -3
- package/templates/applications/dataplane/application.yaml +24 -17
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +24 -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 +17 -2
- package/templates/channels/chatgpt/install.template.md.hbs +82 -26
- package/templates/channels/chatgpt/integration/README.md +11 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- 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
- /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host .env.local Keycloak loopback URL helpers for env-copy.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve developer-scoped Keycloak ports for host-side .env.local
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const yaml = require('js-yaml');
|
|
14
|
+
const config = require('../core/config');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read unquoted env value from content.
|
|
18
|
+
* @param {string} content
|
|
19
|
+
* @param {string} key
|
|
20
|
+
* @returns {string}
|
|
21
|
+
*/
|
|
22
|
+
function readEnvLineValue(content, key) {
|
|
23
|
+
const re = new RegExp(`^${key}\\s*=\\s*(.*)$`, 'm');
|
|
24
|
+
const m = content.match(re);
|
|
25
|
+
if (!m) return '';
|
|
26
|
+
return m[1].trim().replace(/^["']|["']$/g, '');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Object} cfgModule - Config module with CONFIG_FILE
|
|
31
|
+
* @returns {number|null}
|
|
32
|
+
*/
|
|
33
|
+
function readDeveloperIdFromConfig(cfgModule) {
|
|
34
|
+
const configPath = cfgModule && cfgModule.CONFIG_FILE ? cfgModule.CONFIG_FILE : null;
|
|
35
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const cfgContent = fs.readFileSync(configPath, 'utf8');
|
|
40
|
+
const cfg = yaml.load(cfgContent) || {};
|
|
41
|
+
const raw = cfg['developer-id'];
|
|
42
|
+
if (typeof raw === 'number') {
|
|
43
|
+
return raw;
|
|
44
|
+
}
|
|
45
|
+
if (typeof raw === 'string' && /^[0-9]+$/.test(raw)) {
|
|
46
|
+
return parseInt(raw, 10);
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
// ignore
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Base Keycloak published port from urls.local.yaml (before developer offset).
|
|
56
|
+
* @returns {number|null}
|
|
57
|
+
*/
|
|
58
|
+
function readKeycloakBasePortFromUrlsLocal() {
|
|
59
|
+
try {
|
|
60
|
+
const urlsPath = path.join(path.dirname(config.CONFIG_FILE || ''), 'urls.local.yaml');
|
|
61
|
+
if (!fs.existsSync(urlsPath)) return null;
|
|
62
|
+
const doc = yaml.load(fs.readFileSync(urlsPath, 'utf8')) || {};
|
|
63
|
+
const raw = doc['keycloak-port'];
|
|
64
|
+
const n = typeof raw === 'number' ? raw : parseInt(String(raw || ''), 10);
|
|
65
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} host
|
|
73
|
+
* @returns {string|null} Normalized loopback host or null when not loopback
|
|
74
|
+
*/
|
|
75
|
+
function normalizeKeycloakLoopbackHost(host) {
|
|
76
|
+
let next = host;
|
|
77
|
+
if (next === 'keycloak') {
|
|
78
|
+
next = 'localhost';
|
|
79
|
+
}
|
|
80
|
+
const isLoopback =
|
|
81
|
+
next === 'localhost' || next === '127.0.0.1' || next === '::1' || next === '[::1]';
|
|
82
|
+
if (!isLoopback) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return next === '::1' ? '[::1]' : next;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} content
|
|
90
|
+
* @returns {number|null}
|
|
91
|
+
*/
|
|
92
|
+
function resolveExplicitKeycloakPort(content) {
|
|
93
|
+
const publicPortRaw = readEnvLineValue(content, 'KEYCLOAK_PUBLIC_PORT');
|
|
94
|
+
if (/^\d+$/.test(publicPortRaw)) {
|
|
95
|
+
return parseInt(publicPortRaw, 10);
|
|
96
|
+
}
|
|
97
|
+
const portRaw = readEnvLineValue(content, 'KEYCLOAK_PORT');
|
|
98
|
+
if (!/^\d+$/.test(portRaw)) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const portNum = parseInt(portRaw, 10);
|
|
102
|
+
// 8080 is the in-container listen port — not the host publish port
|
|
103
|
+
return portNum !== 8080 ? portNum : null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @returns {number|null}
|
|
108
|
+
*/
|
|
109
|
+
function resolveDeveloperIdForKeycloakPort() {
|
|
110
|
+
try {
|
|
111
|
+
const raw = process.env.AIFABRIX_DEVELOPERID;
|
|
112
|
+
if (raw && /^[0-9]+$/.test(String(raw).trim())) {
|
|
113
|
+
return parseInt(String(raw).trim(), 10);
|
|
114
|
+
}
|
|
115
|
+
return readDeveloperIdFromConfig(config);
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Published Keycloak host port for this developer (never a fixed 8182).
|
|
123
|
+
* Prefer KEYCLOAK_PUBLIC_PORT, then loopback KEYCLOAK_PORT (if not container 8080),
|
|
124
|
+
* else base from urls.local.yaml / 8082 + developer-id × 100.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} content - Env file content
|
|
127
|
+
* @returns {{ host: string, port: number }|null}
|
|
128
|
+
*/
|
|
129
|
+
function resolveHostKeycloakLoopbackEndpoint(content) {
|
|
130
|
+
const { publishedHostPort, parseDeveloperIdNum } = require('./declarative-url-ports');
|
|
131
|
+
const host = normalizeKeycloakLoopbackHost(
|
|
132
|
+
readEnvLineValue(content, 'KEYCLOAK_HOST') || 'localhost'
|
|
133
|
+
);
|
|
134
|
+
if (!host) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const explicit = resolveExplicitKeycloakPort(content);
|
|
138
|
+
if (explicit !== null) {
|
|
139
|
+
return { host, port: explicit };
|
|
140
|
+
}
|
|
141
|
+
const base = readKeycloakBasePortFromUrlsLocal() || 8082;
|
|
142
|
+
const port = publishedHostPort(base, parseDeveloperIdNum(resolveDeveloperIdForKeycloakPort()));
|
|
143
|
+
return { host, port };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Upsert KEYCLOAK_PORT and KEYCLOAK_INTERNAL_SERVER_URL for host .env.local.
|
|
148
|
+
* @param {string} content
|
|
149
|
+
* @param {string} host
|
|
150
|
+
* @param {number} port
|
|
151
|
+
* @returns {string}
|
|
152
|
+
*/
|
|
153
|
+
function patchKeycloakHostInternalLines(content, host, port) {
|
|
154
|
+
const internalUrl = `http://${host}:${port}/auth`;
|
|
155
|
+
let next = content;
|
|
156
|
+
if (readEnvLineValue(content, 'KEYCLOAK_HOST') === 'keycloak') {
|
|
157
|
+
next = next.replace(/^KEYCLOAK_HOST\s*=.*$/m, 'KEYCLOAK_HOST=localhost');
|
|
158
|
+
}
|
|
159
|
+
if (/^KEYCLOAK_PORT\s*=/m.test(next)) {
|
|
160
|
+
next = next.replace(/^KEYCLOAK_PORT\s*=.*$/m, `KEYCLOAK_PORT=${port}`);
|
|
161
|
+
} else {
|
|
162
|
+
next = `${next.trimEnd()}\nKEYCLOAK_PORT=${port}\n`;
|
|
163
|
+
}
|
|
164
|
+
if (/^KEYCLOAK_INTERNAL_SERVER_URL\s*=/m.test(next)) {
|
|
165
|
+
return next.replace(
|
|
166
|
+
/^KEYCLOAK_INTERNAL_SERVER_URL\s*=.*$/m,
|
|
167
|
+
`KEYCLOAK_INTERNAL_SERVER_URL=${internalUrl}`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return `${next.trimEnd()}\nKEYCLOAK_INTERNAL_SERVER_URL=${internalUrl}\n`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* For host `.env.local`, force Keycloak admin/internal base to this developer's
|
|
175
|
+
* published loopback port (`base + developerId × 100`). Never hardcodes a single
|
|
176
|
+
* developer’s port (e.g. 8182).
|
|
177
|
+
*
|
|
178
|
+
* @param {string} content - Env file content
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function applyHostLocalKeycloakInternalUrl(content) {
|
|
182
|
+
if (typeof content !== 'string' || content.length === 0) {
|
|
183
|
+
return content;
|
|
184
|
+
}
|
|
185
|
+
const endpoint = resolveHostKeycloakLoopbackEndpoint(content);
|
|
186
|
+
if (!endpoint) {
|
|
187
|
+
return content;
|
|
188
|
+
}
|
|
189
|
+
return patchKeycloakHostInternalLines(content, endpoint.host, endpoint.port);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
applyHostLocalKeycloakInternalUrl,
|
|
194
|
+
readEnvLineValue,
|
|
195
|
+
resolveHostKeycloakLoopbackEndpoint
|
|
196
|
+
};
|
package/lib/utils/env-copy.js
CHANGED
|
@@ -21,6 +21,7 @@ const { rewriteInfraEndpoints } = require('./env-endpoints');
|
|
|
21
21
|
const { buildEnvVarMap } = require('./env-map');
|
|
22
22
|
const { interpolateEnvVars } = require('./secrets-helpers');
|
|
23
23
|
const { getLocalPort } = require('./port-resolver');
|
|
24
|
+
const { applyHostLocalKeycloakInternalUrl } = require('./env-copy-keycloak');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Read developer ID from config file synchronously
|
|
@@ -98,6 +99,26 @@ function resolveEnvOutputPath(rawOutputPath, variablesPath) {
|
|
|
98
99
|
return outputPath;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Sibling host-only env path next to `build.envOutputPath` (`.env` → `.env.local`).
|
|
104
|
+
* Used for host CLI / manual tests; must not be mounted into the container.
|
|
105
|
+
*
|
|
106
|
+
* @param {string} envOutputPath - Resolved path ending in `.env`
|
|
107
|
+
* @returns {string} Path ending in `.env.local`
|
|
108
|
+
*/
|
|
109
|
+
function resolveEnvLocalOutputPath(envOutputPath) {
|
|
110
|
+
if (typeof envOutputPath !== 'string' || envOutputPath.trim() === '') {
|
|
111
|
+
throw new Error('envOutputPath is required to resolve .env.local');
|
|
112
|
+
}
|
|
113
|
+
if (envOutputPath.endsWith('.env.local')) {
|
|
114
|
+
return envOutputPath;
|
|
115
|
+
}
|
|
116
|
+
if (envOutputPath.endsWith('.env')) {
|
|
117
|
+
return `${envOutputPath}.local`;
|
|
118
|
+
}
|
|
119
|
+
return path.join(path.dirname(envOutputPath), '.env.local');
|
|
120
|
+
}
|
|
121
|
+
|
|
101
122
|
/**
|
|
102
123
|
* Writes .env to envOutputPath for `--reload`: merge container `.env.run` into the host file.
|
|
103
124
|
* Preserves comments when a file already exists (e.g. after `aifabrix resolve`). Keys present only
|
|
@@ -149,8 +170,18 @@ async function writeEnvOutputForLocal(appName, outputPath) {
|
|
|
149
170
|
const localMap = parseEnvContentToMap(localContent);
|
|
150
171
|
toWrite = mergeEnvMapIntoContent(existingContent, localMap);
|
|
151
172
|
}
|
|
173
|
+
const isLocalSibling = path.basename(outputPath) === '.env.local';
|
|
174
|
+
if (isLocalSibling) {
|
|
175
|
+
toWrite = applyHostLocalKeycloakInternalUrl(toWrite);
|
|
176
|
+
}
|
|
152
177
|
await fsp.writeFile(outputPath, toWrite, { mode: 0o600 });
|
|
153
|
-
logger.log(
|
|
178
|
+
logger.log(
|
|
179
|
+
formatSuccessLine(
|
|
180
|
+
isLocalSibling
|
|
181
|
+
? `Wrote host .env.local (localhost ports for CLI/tests): ${outputPath}`
|
|
182
|
+
: `Wrote .env to envOutputPath (localPort): ${outputPath}`
|
|
183
|
+
)
|
|
184
|
+
);
|
|
154
185
|
}
|
|
155
186
|
|
|
156
187
|
/**
|
|
@@ -408,6 +439,8 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath,
|
|
|
408
439
|
module.exports = {
|
|
409
440
|
processEnvVariables,
|
|
410
441
|
resolveEnvOutputPath,
|
|
442
|
+
resolveEnvLocalOutputPath,
|
|
443
|
+
applyHostLocalKeycloakInternalUrl,
|
|
411
444
|
substituteMntDataForLocal,
|
|
412
445
|
syncWrittenBuilderEnvToOutputPath,
|
|
413
446
|
writeEnvOutputForReload,
|
|
@@ -248,6 +248,9 @@ function formatGatePillarLine(opts = {}) {
|
|
|
248
248
|
if (verdict === VERDICT.FAILED) {
|
|
249
249
|
return formatBlockingError(`${label} · FAILED${suffix}`);
|
|
250
250
|
}
|
|
251
|
+
if (verdict === VERDICT.NOT_APPLICABLE) {
|
|
252
|
+
return formatSuccessLine(`${label} · NOT APPLICABLE${suffix}`);
|
|
253
|
+
}
|
|
251
254
|
return formatWarningLine(`${label} · NOT VERIFIED${suffix}`);
|
|
252
255
|
}
|
|
253
256
|
|
|
@@ -38,32 +38,50 @@ async function filterTraefikUrlByDns(traefikUrl, debug, isHostnameResolvableFn)
|
|
|
38
38
|
* @param {string} [p.skippedPublicHealthUrl]
|
|
39
39
|
* @param {string[]} [p.urlsToTry]
|
|
40
40
|
* @param {number} p.resolvedIndex
|
|
41
|
+
* @returns {string|null} Warning message without leading glyph, or null
|
|
41
42
|
*/
|
|
42
|
-
function
|
|
43
|
+
function buildPublicHealthUrlWarningMessage(p) {
|
|
43
44
|
const { skippedPublicHealthUrl, urlsToTry, resolvedIndex } = p;
|
|
44
|
-
if (typeof resolvedIndex !== 'number' || resolvedIndex < 0) return;
|
|
45
|
+
if (typeof resolvedIndex !== 'number' || resolvedIndex < 0) return null;
|
|
45
46
|
|
|
46
47
|
if (skippedPublicHealthUrl) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
'The application reported healthy via localhost only. Validate DNS names and Traefik routing for this host.'
|
|
51
|
-
)
|
|
48
|
+
return (
|
|
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.'
|
|
52
51
|
);
|
|
53
|
-
return;
|
|
54
52
|
}
|
|
55
53
|
if (Array.isArray(urlsToTry) && urlsToTry.length > 1 && resolvedIndex > 0) {
|
|
56
54
|
const pub = urlsToTry[0];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'Health checks succeeded via localhost only. Validate Traefik routing, TLS, and DNS.'
|
|
61
|
-
)
|
|
55
|
+
return (
|
|
56
|
+
`Public URL was not verified: ${pub}. ` +
|
|
57
|
+
'Health checks succeeded via localhost only. Validate Traefik routing, TLS, and DNS.'
|
|
62
58
|
);
|
|
63
59
|
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {Object} p
|
|
65
|
+
* @param {string} [p.skippedPublicHealthUrl]
|
|
66
|
+
* @param {string[]} [p.urlsToTry]
|
|
67
|
+
* @param {number} p.resolvedIndex
|
|
68
|
+
* @param {boolean} [p.defer]
|
|
69
|
+
* @param {(message: string) => void} [p.onDeferred]
|
|
70
|
+
*/
|
|
71
|
+
function logPublicHealthUrlWarningIfNeeded(p) {
|
|
72
|
+
const message = buildPublicHealthUrlWarningMessage(p);
|
|
73
|
+
if (!message) return;
|
|
74
|
+
if (p && p.defer === true) {
|
|
75
|
+
if (typeof p.onDeferred === 'function') {
|
|
76
|
+
p.onDeferred(message);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
logger.log(chalk.yellow(`⚠ ${message}`));
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
module.exports = {
|
|
67
84
|
filterTraefikUrlByDns,
|
|
85
|
+
buildPublicHealthUrlWarningMessage,
|
|
68
86
|
logPublicHealthUrlWarningIfNeeded
|
|
69
87
|
};
|
|
@@ -339,6 +339,14 @@ function resolveHealthCheckPollOptions(runOptions = {}) {
|
|
|
339
339
|
if (typeof runOptions.onPollText === 'function') {
|
|
340
340
|
pollOptions.onPollText = runOptions.onPollText;
|
|
341
341
|
}
|
|
342
|
+
const defer =
|
|
343
|
+
runOptions.deferPublicUrlWarning === true || typeof runOptions.onPollText === 'function';
|
|
344
|
+
if (defer) {
|
|
345
|
+
pollOptions.deferPublicUrlWarning = true;
|
|
346
|
+
if (typeof runOptions.onDeferredPublicUrlWarning === 'function') {
|
|
347
|
+
pollOptions.onDeferredPublicUrlWarning = runOptions.onDeferredPublicUrlWarning;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
342
350
|
return pollOptions;
|
|
343
351
|
}
|
|
344
352
|
|
|
@@ -367,7 +375,9 @@ async function pollUntilHealthCheckPasses({
|
|
|
367
375
|
logPublicHealthUrlWarningIfNeeded({
|
|
368
376
|
skippedPublicHealthUrl,
|
|
369
377
|
urlsToTry,
|
|
370
|
-
resolvedIndex: attemptResult.resolvedIndex
|
|
378
|
+
resolvedIndex: attemptResult.resolvedIndex,
|
|
379
|
+
defer: pollOptions.deferPublicUrlWarning === true,
|
|
380
|
+
onDeferred: pollOptions.onDeferredPublicUrlWarning
|
|
371
381
|
});
|
|
372
382
|
return;
|
|
373
383
|
}
|
package/lib/utils/paths.js
CHANGED
|
@@ -382,15 +382,41 @@ function getIntegrationBuilderBaseDir() {
|
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
/**
|
|
385
|
-
* Returns the default integration root
|
|
386
|
-
*
|
|
385
|
+
* Returns the default integration root: {@link getCwdIntegrationRoot} when present, else
|
|
386
|
+
* under {@link getAppsMaterializationParent}. Listing may still scan both trees
|
|
387
|
+
* (see {@link listIntegrationAppNames}).
|
|
387
388
|
*
|
|
388
389
|
* @returns {string} Absolute path to integration/ directory
|
|
389
390
|
*/
|
|
390
391
|
function getIntegrationRoot() {
|
|
392
|
+
const cwdRoot = getCwdIntegrationRoot();
|
|
393
|
+
if (cwdRoot) {
|
|
394
|
+
return cwdRoot;
|
|
395
|
+
}
|
|
391
396
|
return path.join(getAppsMaterializationParent(), 'integration');
|
|
392
397
|
}
|
|
393
398
|
|
|
399
|
+
/**
|
|
400
|
+
* True when path exists and is a directory (best-effort; false on error).
|
|
401
|
+
* @param {string} dirPath
|
|
402
|
+
* @returns {boolean}
|
|
403
|
+
*/
|
|
404
|
+
function isExistingDirectoryPath(dirPath) {
|
|
405
|
+
if (!dirPath || typeof dirPath !== 'string') {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
const disk = nodeFs();
|
|
410
|
+
if (!disk.existsSync(dirPath)) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
const st = disk.statSync(dirPath);
|
|
414
|
+
return Boolean(st && typeof st.isDirectory === 'function' && st.isDirectory());
|
|
415
|
+
} catch {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
394
420
|
/**
|
|
395
421
|
* Absolute `integration/` next to {@link process.cwd} when that directory exists.
|
|
396
422
|
*
|
|
@@ -574,8 +600,10 @@ function listIntegrationAppNames() {
|
|
|
574
600
|
if (cwdRoot) {
|
|
575
601
|
addBuilderSubdirNamesToSet(disk, cwdRoot, names, withAppConfig(cwdRoot));
|
|
576
602
|
}
|
|
577
|
-
const matInt =
|
|
578
|
-
|
|
603
|
+
const matInt = path.join(getAppsMaterializationParent(), 'integration');
|
|
604
|
+
if (!cwdRoot || path.resolve(matInt) !== path.resolve(cwdRoot)) {
|
|
605
|
+
addBuilderSubdirNamesToSet(disk, matInt, names, withAppConfig(matInt));
|
|
606
|
+
}
|
|
579
607
|
return [...names].sort();
|
|
580
608
|
}
|
|
581
609
|
|
|
@@ -629,8 +657,11 @@ function listBuilderAppNames() {
|
|
|
629
657
|
}
|
|
630
658
|
|
|
631
659
|
/**
|
|
632
|
-
* Gets the integration folder path
|
|
633
|
-
*
|
|
660
|
+
* Gets the integration folder path.
|
|
661
|
+
* Resolution: existing `cwd/integration/<app>` → existing materialization copy →
|
|
662
|
+
* `cwd/integration/<app>` when `cwd/integration/` exists (new download/create) →
|
|
663
|
+
* materialization parent. Download should use {@link getAppCreatePath} when it must
|
|
664
|
+
* always land under cwd even if a materialization orphan exists.
|
|
634
665
|
*
|
|
635
666
|
* @param {string} appName - Application name
|
|
636
667
|
* @returns {string} Absolute path to integration directory
|
|
@@ -648,18 +679,21 @@ function getIntegrationPath(appName) {
|
|
|
648
679
|
} catch {
|
|
649
680
|
// programmatic module optional for CLI-only usage
|
|
650
681
|
}
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
682
|
+
const cwdRoot = getCwdIntegrationRoot();
|
|
683
|
+
const matApp = path.join(getAppsMaterializationParent(), 'integration', appName);
|
|
684
|
+
if (cwdRoot) {
|
|
685
|
+
const cwdApp = path.join(cwdRoot, appName);
|
|
686
|
+
if (isExistingDirectoryPath(cwdApp)) {
|
|
687
|
+
return cwdApp;
|
|
658
688
|
}
|
|
659
|
-
} catch {
|
|
660
|
-
// ignore
|
|
661
689
|
}
|
|
662
|
-
|
|
690
|
+
if (isExistingDirectoryPath(matApp)) {
|
|
691
|
+
return matApp;
|
|
692
|
+
}
|
|
693
|
+
if (cwdRoot) {
|
|
694
|
+
return path.join(cwdRoot, appName);
|
|
695
|
+
}
|
|
696
|
+
return matApp;
|
|
663
697
|
}
|
|
664
698
|
|
|
665
699
|
/**
|
|
@@ -797,13 +831,15 @@ function resolveExistingWorkspacePath(appName, options) {
|
|
|
797
831
|
if (isExistingAppDirectory(appPath)) {
|
|
798
832
|
return { appPath, baseDir };
|
|
799
833
|
}
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
834
|
+
const alternates = ['integration', 'enterprise-knowledge', 'role-assistant']
|
|
835
|
+
.filter((workspaceName) => workspaceName !== baseDir);
|
|
836
|
+
for (const workspaceName of alternates) {
|
|
837
|
+
const alt = resolveAppWorkspace(appName, { ...options, workspace: workspaceName });
|
|
838
|
+
if (isExistingAppDirectory(alt.appPath)) {
|
|
839
|
+
return { appPath: alt.appPath, baseDir: alt.baseDir };
|
|
840
|
+
}
|
|
805
841
|
}
|
|
806
|
-
return
|
|
842
|
+
return null;
|
|
807
843
|
}
|
|
808
844
|
|
|
809
845
|
function externalWorkspaceFolderFromConfig(appPath, baseDir, scopedWorkspace) {
|
|
@@ -926,7 +962,7 @@ async function detectAppType(appName, options = {}) {
|
|
|
926
962
|
const builderAfterTemplate = checkBuilderFolder(appName);
|
|
927
963
|
if (builderAfterTemplate) return builderAfterTemplate;
|
|
928
964
|
throw new Error(
|
|
929
|
-
`App '${appName}' not found in integration/${appName}, enterprise-knowledge/${appName}, or builder/${appName}`
|
|
965
|
+
`App '${appName}' not found in integration/${appName}, enterprise-knowledge/${appName}, role-assistant/${appName}, or builder/${appName}`
|
|
930
966
|
);
|
|
931
967
|
}
|
|
932
968
|
|
|
@@ -964,6 +1000,11 @@ async function getResolveAppPath(appName, options = {}) {
|
|
|
964
1000
|
if (workspaceHit) {
|
|
965
1001
|
return workspaceHit;
|
|
966
1002
|
}
|
|
1003
|
+
const { getRoleAssistantPath } = require('./role-assistant-paths');
|
|
1004
|
+
const roleAssistantHit = tryResolveEnvOnlyPath(getRoleAssistantPath(appName));
|
|
1005
|
+
if (roleAssistantHit) {
|
|
1006
|
+
return roleAssistantHit;
|
|
1007
|
+
}
|
|
967
1008
|
const integrationHit = tryResolveEnvOnlyPath(getIntegrationPath(appName));
|
|
968
1009
|
if (integrationHit) {
|
|
969
1010
|
return integrationHit;
|
|
@@ -981,23 +1022,17 @@ async function getResolveAppPath(appName, options = {}) {
|
|
|
981
1022
|
return { appPath: result.appPath, envOnly: false };
|
|
982
1023
|
}
|
|
983
1024
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
* @param {string} cwd - Resolved process.cwd()
|
|
987
|
-
* @param {ReturnType<typeof nodeFs>} disk
|
|
988
|
-
* @returns {string|null}
|
|
989
|
-
*/
|
|
990
|
-
function tryIntegrationAppKeyAtWalkStep(walkDir, cwd, disk) {
|
|
991
|
-
const integrationDir = path.join(walkDir, 'integration');
|
|
1025
|
+
function tryAuthoringAppKeyAtWalkStep(walkDir, cwd, disk, folderName) {
|
|
1026
|
+
const authoringDir = path.join(walkDir, folderName);
|
|
992
1027
|
try {
|
|
993
|
-
if (!disk.existsSync(
|
|
1028
|
+
if (!disk.existsSync(authoringDir)) {
|
|
994
1029
|
return null;
|
|
995
1030
|
}
|
|
996
|
-
const
|
|
997
|
-
if (cwd !==
|
|
1031
|
+
const rootNorm = path.resolve(authoringDir);
|
|
1032
|
+
if (cwd !== rootNorm && !cwd.startsWith(rootNorm + path.sep)) {
|
|
998
1033
|
return null;
|
|
999
1034
|
}
|
|
1000
|
-
const rel = path.relative(
|
|
1035
|
+
const rel = path.relative(rootNorm, cwd);
|
|
1001
1036
|
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
1002
1037
|
return null;
|
|
1003
1038
|
}
|
|
@@ -1008,16 +1043,20 @@ function tryIntegrationAppKeyAtWalkStep(walkDir, cwd, disk) {
|
|
|
1008
1043
|
}
|
|
1009
1044
|
|
|
1010
1045
|
/**
|
|
1011
|
-
* Resolve app folder name when cwd is under
|
|
1046
|
+
* Resolve app folder name when cwd is under integration/, role-assistant/, or
|
|
1047
|
+
* enterprise-knowledge/<systemKey>/.
|
|
1012
1048
|
* Walks up from cwd (does not use {@link getIntegrationBuilderBaseDir}) so detection still works
|
|
1013
|
-
* when the canonical base is config/home but the shell is inside a checkout's
|
|
1049
|
+
* when the canonical base is config/home but the shell is inside a checkout's tree.
|
|
1014
1050
|
*/
|
|
1015
1051
|
function resolveIntegrationAppKeyFromCwd() {
|
|
1016
1052
|
const cwd = path.resolve(process.cwd());
|
|
1017
1053
|
const disk = nodeFs();
|
|
1018
1054
|
let p = cwd;
|
|
1019
1055
|
for (let i = 0; i < 64; i += 1) {
|
|
1020
|
-
const key =
|
|
1056
|
+
const key =
|
|
1057
|
+
tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'integration') ||
|
|
1058
|
+
tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'role-assistant') ||
|
|
1059
|
+
tryAuthoringAppKeyAtWalkStep(p, cwd, disk, 'enterprise-knowledge');
|
|
1021
1060
|
if (key) {
|
|
1022
1061
|
return key;
|
|
1023
1062
|
}
|
|
@@ -1073,6 +1112,9 @@ module.exports = {
|
|
|
1073
1112
|
getEnterpriseKnowledgePath: require('./enterprise-knowledge-paths').getEnterpriseKnowledgePath,
|
|
1074
1113
|
getEnterpriseKnowledgeRoot: require('./enterprise-knowledge-paths').getEnterpriseKnowledgeRoot,
|
|
1075
1114
|
listEnterpriseKnowledgeAnchors: require('./enterprise-knowledge-paths').listEnterpriseKnowledgeAnchors,
|
|
1115
|
+
getRoleAssistantPath: require('./role-assistant-paths').getRoleAssistantPath,
|
|
1116
|
+
getRoleAssistantRoot: require('./role-assistant-paths').getRoleAssistantRoot,
|
|
1117
|
+
getCwdRoleAssistantRoot: require('./role-assistant-paths').getCwdRoleAssistantRoot,
|
|
1076
1118
|
resolveAppWorkspace: require('./app-path-resolve').resolveAppWorkspace,
|
|
1077
1119
|
resolveExternalSystemPath: require('./app-path-resolve').resolveExternalSystemPath
|
|
1078
1120
|
};
|
|
@@ -57,10 +57,22 @@ async function resolveClientTokenAuthForPipelineRetry(systemKey) {
|
|
|
57
57
|
* @param {Object} authConfig
|
|
58
58
|
* @param {Object} payload
|
|
59
59
|
* @param {string} systemKey - External system key for credential lookup on retry
|
|
60
|
+
* @param {Object} [options] - Forwarded to uploadApplicationViaPipeline (e.g. timeoutMs)
|
|
60
61
|
* @returns {Promise<Object>}
|
|
61
62
|
*/
|
|
62
|
-
async function uploadApplicationViaPipelineWithAuthRetry(
|
|
63
|
-
|
|
63
|
+
async function uploadApplicationViaPipelineWithAuthRetry(
|
|
64
|
+
dataplaneUrl,
|
|
65
|
+
authConfig,
|
|
66
|
+
payload,
|
|
67
|
+
systemKey,
|
|
68
|
+
options = {}
|
|
69
|
+
) {
|
|
70
|
+
const first = await uploadApplicationViaPipeline(
|
|
71
|
+
dataplaneUrl,
|
|
72
|
+
authConfig,
|
|
73
|
+
payload,
|
|
74
|
+
options
|
|
75
|
+
);
|
|
64
76
|
if (!isPipelineAuthFailure(first) || authConfig.type !== 'bearer') {
|
|
65
77
|
return first;
|
|
66
78
|
}
|
|
@@ -77,7 +89,7 @@ async function uploadApplicationViaPipelineWithAuthRetry(dataplaneUrl, authConfi
|
|
|
77
89
|
'Device login token was rejected for pipeline publish; retrying with application client token…'
|
|
78
90
|
)
|
|
79
91
|
);
|
|
80
|
-
return uploadApplicationViaPipeline(dataplaneUrl, clientAuth, payload);
|
|
92
|
+
return uploadApplicationViaPipeline(dataplaneUrl, clientAuth, payload, options);
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
module.exports = {
|