@aifabrix/builder 2.51.0 → 2.51.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/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-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- 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 +7 -0
- 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/setup-app.js +4 -0
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +19 -17
- 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/lifecycle-external.js +10 -4
- 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 +313 -0
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- 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 +29 -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-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- 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.js +39 -3
- package/lib/generator/builders.js +3 -0
- 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-sync.js +465 -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 +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +16 -2
- 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/infra.parameter.yaml +20 -0
- 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/deployment-validation.js +28 -3
- package/lib/utils/docker-run-ephemeral.js +55 -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/help-builder.js +1 -0
- package/lib/utils/paths.js +67 -23
- package/lib/utils/pipeline-upload-auth-retry.js +15 -3
- package/lib/utils/role-assistant-paths.js +98 -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/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/validate-external-file.js +8 -1
- package/package.json +2 -1
- package/scripts/export-builder-api-evidence.js +1 -1
- package/templates/applications/dataplane/application.yaml +23 -15
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +14 -0
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +3 -1
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -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/.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
|
|
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;
|
|
@@ -1073,6 +1114,9 @@ module.exports = {
|
|
|
1073
1114
|
getEnterpriseKnowledgePath: require('./enterprise-knowledge-paths').getEnterpriseKnowledgePath,
|
|
1074
1115
|
getEnterpriseKnowledgeRoot: require('./enterprise-knowledge-paths').getEnterpriseKnowledgeRoot,
|
|
1075
1116
|
listEnterpriseKnowledgeAnchors: require('./enterprise-knowledge-paths').listEnterpriseKnowledgeAnchors,
|
|
1117
|
+
getRoleAssistantPath: require('./role-assistant-paths').getRoleAssistantPath,
|
|
1118
|
+
getRoleAssistantRoot: require('./role-assistant-paths').getRoleAssistantRoot,
|
|
1119
|
+
getCwdRoleAssistantRoot: require('./role-assistant-paths').getCwdRoleAssistantRoot,
|
|
1076
1120
|
resolveAppWorkspace: require('./app-path-resolve').resolveAppWorkspace,
|
|
1077
1121
|
resolveExternalSystemPath: require('./app-path-resolve').resolveExternalSystemPath
|
|
1078
1122
|
};
|
|
@@ -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 = {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant workspace path helpers.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Paths under role-assistant/ for Role Assistant packages
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const os = require('os');
|
|
13
|
+
const { nodeFs } = require('../internal/node-fs');
|
|
14
|
+
|
|
15
|
+
const WORKSPACE_ROLE_ASSISTANT = 'role-assistant';
|
|
16
|
+
const SYSTEM_KIND_DIGITAL_WORKER = 'digitalWorker';
|
|
17
|
+
|
|
18
|
+
function getMaterializationParent() {
|
|
19
|
+
const work = process.env.AIFABRIX_WORK;
|
|
20
|
+
if (work && typeof work === 'string' && work.trim()) {
|
|
21
|
+
return path.resolve(work.trim());
|
|
22
|
+
}
|
|
23
|
+
if (process.env.AIFABRIX_HOME && typeof process.env.AIFABRIX_HOME === 'string') {
|
|
24
|
+
return path.resolve(process.env.AIFABRIX_HOME.trim());
|
|
25
|
+
}
|
|
26
|
+
return path.join(os.homedir(), '.aifabrix');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getCwdRoleAssistantRoot() {
|
|
30
|
+
const p = path.join(path.resolve(process.cwd()), WORKSPACE_ROLE_ASSISTANT);
|
|
31
|
+
try {
|
|
32
|
+
const disk = nodeFs();
|
|
33
|
+
if (disk.existsSync(p)) {
|
|
34
|
+
const st = disk.statSync(p);
|
|
35
|
+
if (st && typeof st.isDirectory === 'function' && st.isDirectory()) {
|
|
36
|
+
return p;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// ignore
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getRoleAssistantRoot() {
|
|
46
|
+
const cwdRoot = getCwdRoleAssistantRoot();
|
|
47
|
+
if (cwdRoot) {
|
|
48
|
+
return cwdRoot;
|
|
49
|
+
}
|
|
50
|
+
return path.join(getMaterializationParent(), WORKSPACE_ROLE_ASSISTANT);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isExistingDirectory(disk, dirPath) {
|
|
54
|
+
try {
|
|
55
|
+
if (!dirPath || !disk.existsSync(dirPath)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const st = disk.statSync(dirPath);
|
|
59
|
+
return Boolean(st && typeof st.isDirectory === 'function' && st.isDirectory());
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolves role-assistant/<roleKey> with cwd-first behavior.
|
|
67
|
+
* @param {string} roleKey
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
function getRoleAssistantPath(roleKey) {
|
|
71
|
+
if (!roleKey || typeof roleKey !== 'string') {
|
|
72
|
+
throw new Error('Role key is required and must be a string');
|
|
73
|
+
}
|
|
74
|
+
const disk = nodeFs();
|
|
75
|
+
const cwdRoot = getCwdRoleAssistantRoot();
|
|
76
|
+
const matPath = path.join(getMaterializationParent(), WORKSPACE_ROLE_ASSISTANT, roleKey);
|
|
77
|
+
if (cwdRoot) {
|
|
78
|
+
const cwdPath = path.join(cwdRoot, roleKey);
|
|
79
|
+
if (isExistingDirectory(disk, cwdPath)) {
|
|
80
|
+
return cwdPath;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (isExistingDirectory(disk, matPath)) {
|
|
84
|
+
return matPath;
|
|
85
|
+
}
|
|
86
|
+
if (cwdRoot) {
|
|
87
|
+
return path.join(cwdRoot, roleKey);
|
|
88
|
+
}
|
|
89
|
+
return matPath;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
WORKSPACE_ROLE_ASSISTANT,
|
|
94
|
+
SYSTEM_KIND_DIGITAL_WORKER,
|
|
95
|
+
getCwdRoleAssistantRoot,
|
|
96
|
+
getRoleAssistantRoot,
|
|
97
|
+
getRoleAssistantPath
|
|
98
|
+
};
|
|
@@ -18,6 +18,7 @@ const addFormats = require('ajv-formats');
|
|
|
18
18
|
// These are reset when module is reloaded (for testing)
|
|
19
19
|
let externalSystemValidator = null;
|
|
20
20
|
let externalDataSourceValidator = null;
|
|
21
|
+
let roleAssistantSettingsValidator = null;
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Reset validators cache (for testing)
|
|
@@ -26,6 +27,7 @@ let externalDataSourceValidator = null;
|
|
|
26
27
|
function resetValidators() {
|
|
27
28
|
externalSystemValidator = null;
|
|
28
29
|
externalDataSourceValidator = null;
|
|
30
|
+
roleAssistantSettingsValidator = null;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -138,6 +140,38 @@ function loadExternalDataSourceSchema() {
|
|
|
138
140
|
return externalDataSourceValidator;
|
|
139
141
|
}
|
|
140
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Loads and compiles role-assistant settings schema.
|
|
145
|
+
* @function loadRoleAssistantSettingsSchema
|
|
146
|
+
* @returns {Function}
|
|
147
|
+
*/
|
|
148
|
+
function loadRoleAssistantSettingsSchema() {
|
|
149
|
+
if (roleAssistantSettingsValidator) {
|
|
150
|
+
return roleAssistantSettingsValidator;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const schemaPath = path.join(__dirname, '..', 'schema', 'role-assistant-settings.schema.json');
|
|
154
|
+
let schema;
|
|
155
|
+
try {
|
|
156
|
+
const schemaContent = fsRealSync.readFileSync(schemaPath, 'utf8');
|
|
157
|
+
schema = JSON.parse(schemaContent);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
try {
|
|
160
|
+
schema = require('../schema/role-assistant-settings.schema.json');
|
|
161
|
+
} catch {
|
|
162
|
+
if (error && error.code === 'ENOENT') {
|
|
163
|
+
throw new Error(`Role assistant settings schema not found: ${schemaPath}`);
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`Invalid JSON in role-assistant-settings.schema.json: ${error.message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const ajv = new Ajv({ allErrors: true, strict: false, strictSchema: false });
|
|
170
|
+
addFormats(ajv);
|
|
171
|
+
roleAssistantSettingsValidator = ajv.compile(schema);
|
|
172
|
+
return roleAssistantSettingsValidator;
|
|
173
|
+
}
|
|
174
|
+
|
|
141
175
|
/**
|
|
142
176
|
* Detect schema type from $id field
|
|
143
177
|
* @param {Object} parsed - Parsed JSON object
|
|
@@ -157,6 +191,9 @@ function detectFromId(parsed) {
|
|
|
157
191
|
if (parsed.$id.includes('application-schema')) {
|
|
158
192
|
return 'application';
|
|
159
193
|
}
|
|
194
|
+
if (parsed.$id.includes('role-assistant-settings')) {
|
|
195
|
+
return 'role-assistant-settings';
|
|
196
|
+
}
|
|
160
197
|
|
|
161
198
|
return null;
|
|
162
199
|
}
|
|
@@ -239,6 +276,9 @@ function detectFromApplicationFields(parsed) {
|
|
|
239
276
|
*/
|
|
240
277
|
function detectFromFilename(filePath) {
|
|
241
278
|
const fileName = path.basename(filePath).toLowerCase();
|
|
279
|
+
if (fileName === 'role-assistant.yaml' || fileName === 'role-assistant.yml') {
|
|
280
|
+
return 'role-assistant-settings';
|
|
281
|
+
}
|
|
242
282
|
if (fileName.includes('external-system') || fileName.includes('external_system')) {
|
|
243
283
|
return 'external-system';
|
|
244
284
|
}
|
|
@@ -347,6 +387,7 @@ function detectSchemaType(filePath, content) {
|
|
|
347
387
|
module.exports = {
|
|
348
388
|
loadExternalSystemSchema,
|
|
349
389
|
loadExternalDataSourceSchema,
|
|
390
|
+
loadRoleAssistantSettingsSchema,
|
|
350
391
|
registerExternalDatasourceAjvDependencies,
|
|
351
392
|
detectSchemaType,
|
|
352
393
|
detectSchemaTypeFromParsed,
|