@aifabrix/builder 2.45.5 → 2.46.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/.cursor/rules/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-credential-real.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-hubspot-env-vars.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +130 -0
- package/jest.projects.js +41 -136
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/index.js +43 -3
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +3 -1
- package/lib/api/records-bulk.api.js +14 -1
- package/lib/api/validation-runner.js +3 -1
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/down.js +20 -1
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +4 -0
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +84 -81
- package/lib/cli/setup-app.help.js +0 -31
- package/lib/cli/setup-app.js +28 -2
- package/lib/cli/setup-app.test-commands.js +5 -57
- package/lib/cli/setup-auth.js +25 -0
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +25 -3
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +22 -2
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +40 -4
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +4 -239
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +58 -20
- package/lib/commands/auth-status.js +41 -47
- package/lib/commands/datasource-test-trust-cli.js +27 -23
- package/lib/commands/datasource-unified-test-cli.js +6 -3
- package/lib/commands/datasource-unified-test-cli.options.js +5 -1
- package/lib/commands/datasource.js +7 -7
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +11 -2
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -296
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +67 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +24 -8
- package/lib/commands/test-governance-external.js +3 -24
- package/lib/commands/test-trust-external.js +20 -21
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +90 -108
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +2 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +55 -0
- package/lib/datasource/agent-trust-debug-log.js +1 -1
- package/lib/datasource/agent-trust-run.js +5 -3
- package/lib/datasource/audit-evidence-extract-debug.js +30 -18
- package/lib/datasource/audit-evidence-extract.js +7 -4
- package/lib/datasource/audit-evidence-verify-matrix.js +54 -26
- package/lib/datasource/bulk-loader-run-batches.js +15 -3
- package/lib/datasource/datasource-load-export-context.js +3 -0
- package/lib/datasource/log-cleaner.js +2 -2
- package/lib/datasource/log-viewer.js +8 -8
- package/lib/datasource/test-e2e.js +4 -1
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run.js +5 -2
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test.js +6 -6
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-run-display.js +215 -25
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display.js +471 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +10 -4
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2622
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/type/credential-field-registry.json +103 -97
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-resolver.js +129 -2
- package/lib/utils/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +11 -8
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +2 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +12 -5
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +8 -0
- package/lib/utils/validation-run-stage.js +45 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +24 -1
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +74 -17
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
- package/lib/commands/test-governance-command-action.js +0 -55
- package/lib/commands/test-trust-command-action.js +0 -118
package/lib/commands/repair.js
CHANGED
|
@@ -12,207 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
-
const path = require('path');
|
|
16
15
|
const fs = require('fs');
|
|
17
|
-
const chalk = require('chalk');
|
|
18
16
|
const { detectAppType } = require('../utils/paths');
|
|
19
|
-
const { resolveApplicationConfigPath
|
|
20
|
-
const { loadConfigFile,
|
|
21
|
-
const logger = require('../utils/logger');
|
|
22
|
-
const { repairEnvTemplate, normalizeSystemFileAuthAndConfig } = require('./repair-env-template');
|
|
23
|
-
const { repairDatasourceFile } = require('./repair-datasource');
|
|
24
|
-
const { mergeRbacFromDatasources, extractRbacFromSystem, migrateSystemRbacIntoRbacFile } = require('./repair-rbac');
|
|
17
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
18
|
+
const { loadConfigFile, isYamlPath } = require('../utils/config-format');
|
|
25
19
|
const { discoverIntegrationFiles, buildEffectiveDatasourceFiles } = require('./repair-internal');
|
|
26
|
-
const {
|
|
27
|
-
const { backupIntegrationFile } = require('../utils/integration-file-backup');
|
|
28
|
-
const { maybeSyncOpenApiFilesForMcp } = require('./repair-openapi-sync');
|
|
29
|
-
const {
|
|
30
|
-
alignAppKeyWithSystem,
|
|
31
|
-
alignDatasourceSystemKeys,
|
|
32
|
-
alignSystemFileDataSources,
|
|
33
|
-
ensureExternalIntegrationBlock,
|
|
34
|
-
removeAuthVarsFromConfiguration,
|
|
35
|
-
resolveSystemContext
|
|
36
|
-
} = require('./repair-system-alignment');
|
|
37
|
-
const { persistChangesAndRegenerate, regenerateReadmeIfRequested } = require('./repair-persist');
|
|
38
|
-
|
|
39
|
-
/** Allowed authentication methods for repair --auth (matches external-system schema) */
|
|
40
|
-
const ALLOWED_AUTH = ['oauth2', 'aad', 'apikey', 'basic', 'queryParam', 'oidc', 'hmac', 'none'];
|
|
41
|
-
|
|
42
|
-
function createRbacFromSystemIfNeeded(opts) {
|
|
43
|
-
const { appPath, systemFilePath, systemParsed, dryRun, changes, format, backupCtx } = opts;
|
|
44
|
-
if (resolveRbacPath(appPath)) return false;
|
|
45
|
-
const rbacFromSystem = extractRbacFromSystem(systemParsed);
|
|
46
|
-
if (!rbacFromSystem) return false;
|
|
47
|
-
if (!dryRun) {
|
|
48
|
-
const rbacFormat = format === 'json' ? 'json' : 'yaml';
|
|
49
|
-
const defaultRbacPath = path.join(appPath, rbacFormat === 'json' ? 'rbac.json' : 'rbac.yaml');
|
|
50
|
-
writeConfigFile(defaultRbacPath, rbacFromSystem, rbacFormat);
|
|
51
|
-
delete systemParsed.roles;
|
|
52
|
-
delete systemParsed.permissions;
|
|
53
|
-
backupIntegrationFile(systemFilePath, backupCtx);
|
|
54
|
-
writeConfigFile(systemFilePath, systemParsed);
|
|
55
|
-
}
|
|
56
|
-
changes.push('Created rbac.yaml from system roles/permissions');
|
|
57
|
-
changes.push('Removed roles/permissions from system file (now in rbac file)');
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Runs datasource repair for each file (v2.4 root dimensions, metadataSchema; optional expose/sync/test).
|
|
63
|
-
* @param {string} appPath - Application path
|
|
64
|
-
* @param {string[]} datasourceFiles - Datasource file names
|
|
65
|
-
* @param {Object} options - { expose?: boolean, sync?: boolean, test?: boolean }
|
|
66
|
-
* @param {boolean} dryRun - If true, do not write
|
|
67
|
-
* @param {string[]} changes - Array to append change descriptions to
|
|
68
|
-
* @returns {boolean} True if any datasource was updated
|
|
69
|
-
*/
|
|
70
|
-
function runDatasourceRepairs(appPath, datasourceFiles, options, dryRun, changes) {
|
|
71
|
-
if (!datasourceFiles || datasourceFiles.length === 0) return false;
|
|
72
|
-
let updated = false;
|
|
73
|
-
for (const fileName of datasourceFiles) {
|
|
74
|
-
const filePath = path.join(appPath, fileName);
|
|
75
|
-
if (!fs.existsSync(filePath)) continue;
|
|
76
|
-
try {
|
|
77
|
-
const parsed = loadConfigFile(filePath);
|
|
78
|
-
const { updated: fileUpdated, changes: fileChanges } = repairDatasourceFile(parsed, {
|
|
79
|
-
expose: options.expose,
|
|
80
|
-
sync: options.sync,
|
|
81
|
-
test: options.test
|
|
82
|
-
});
|
|
83
|
-
if (fileUpdated) {
|
|
84
|
-
updated = true;
|
|
85
|
-
fileChanges.forEach(c => changes.push(`${fileName}: ${c}`));
|
|
86
|
-
if (!dryRun) {
|
|
87
|
-
backupIntegrationFile(filePath, options.backupCtx);
|
|
88
|
-
writeConfigFile(filePath, parsed);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} catch (err) {
|
|
92
|
-
logger.log(chalk.yellow(`⚠ Could not repair datasource ${fileName}: ${err.message}`));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return updated;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function maybeRepairRbac(appPath, systemFilePath, systemParsed, datasourceFiles, options) {
|
|
99
|
-
if (!options.rbac) return { rbacMigratedFromSystem: false, rbacMergeUpdated: false };
|
|
100
|
-
const { dryRun, changes, backupCtx, rbacFmt } = options;
|
|
101
|
-
const rbacMigratedFromSystem = migrateSystemRbacIntoRbacFile(appPath, systemFilePath, systemParsed, { dryRun, changes, backupCtx });
|
|
102
|
-
const rbacMergeUpdated = mergeRbacFromDatasources(appPath, systemParsed, datasourceFiles, extractRbacFromSystem, {
|
|
103
|
-
format: rbacFmt,
|
|
104
|
-
dryRun,
|
|
105
|
-
changes,
|
|
106
|
-
backupCtx
|
|
107
|
-
});
|
|
108
|
-
return { rbacMigratedFromSystem, rbacMergeUpdated };
|
|
109
|
-
}
|
|
20
|
+
const { runRepairExternalIntegration } = require('./repair-external-run');
|
|
110
21
|
|
|
111
|
-
/**
|
|
112
|
-
* Apply --auth: replace system file authentication with canonical block for the given method.
|
|
113
|
-
* Preserves existing authentication.variables (e.g. baseUrl, tokenUrl) from the current system file.
|
|
114
|
-
* @param {Object} ctx - Context with systemParsed, systemKey, auth, dryRun, changes
|
|
115
|
-
* @returns {boolean} True if auth was replaced
|
|
116
|
-
*/
|
|
117
|
-
function applyAuthMethod(ctx) {
|
|
118
|
-
if (!ctx.auth || typeof ctx.auth !== 'string') return false;
|
|
119
|
-
const method = ctx.auth.trim().toLowerCase();
|
|
120
|
-
if (!ALLOWED_AUTH.includes(method)) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
`Invalid --auth "${ctx.auth}". Allowed methods: ${ALLOWED_AUTH.join(', ')}`
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
const existingAuth = ctx.systemParsed.authentication || ctx.systemParsed.auth || {};
|
|
126
|
-
const { buildAuthenticationFromMethod } = require('../external-system/generator');
|
|
127
|
-
const newAuth = buildAuthenticationFromMethod(ctx.systemKey, method);
|
|
128
|
-
const existingVars =
|
|
129
|
-
existingAuth.variables && typeof existingAuth.variables === 'object'
|
|
130
|
-
? existingAuth.variables
|
|
131
|
-
: {};
|
|
132
|
-
// When switching auth method, keep only connectivity fields from the wizard draft.
|
|
133
|
-
// Do not let OAuth-era variables (tokenUrl, scope, headerName, …) override the new method.
|
|
134
|
-
const mergedVariables = { ...newAuth.variables };
|
|
135
|
-
for (const key of ['baseUrl', 'testEndpoint']) {
|
|
136
|
-
if (existingVars[key] !== undefined && existingVars[key] !== null) {
|
|
137
|
-
mergedVariables[key] = existingVars[key];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
ctx.systemParsed.authentication = {
|
|
141
|
-
...newAuth,
|
|
142
|
-
variables: mergedVariables
|
|
143
|
-
};
|
|
144
|
-
if (existingAuth.displayName !== undefined) {
|
|
145
|
-
ctx.systemParsed.authentication.displayName = existingAuth.displayName;
|
|
146
|
-
}
|
|
147
|
-
ctx.changes.push(`Set authentication method to ${method}`);
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Runs all repair steps (integration block, system dataSources, auth/config, app key, datasource keys, rbac, env.template).
|
|
153
|
-
* @param {Object} ctx - Context with appPath, configPath, variables, systemFilePath, systemParsed, systemKey, systemFiles, datasourceFiles, dryRun, changes, auth?, backupCtx?
|
|
154
|
-
* @returns {{ updated: boolean, appKeyFixed: boolean, datasourceKeysFixed: boolean, rbacFileCreated: boolean, envTemplateRepaired: boolean }}
|
|
155
|
-
*/
|
|
156
|
-
function runRepairSteps(ctx) {
|
|
157
|
-
const bc = ctx.backupCtx;
|
|
158
|
-
let updated = ensureExternalIntegrationBlock(
|
|
159
|
-
ctx.variables, ctx.systemFiles, ctx.datasourceFiles, ctx.changes
|
|
160
|
-
);
|
|
161
|
-
const authReplaced = applyAuthMethod(ctx);
|
|
162
|
-
updated = updated || authReplaced;
|
|
163
|
-
const systemAuthConfigNormalized = normalizeSystemFileAuthAndConfig(
|
|
164
|
-
ctx.systemParsed, ctx.systemKey, ctx.changes
|
|
165
|
-
);
|
|
166
|
-
const systemDataSourcesAligned = alignSystemFileDataSources(
|
|
167
|
-
ctx.appPath, ctx.systemParsed, ctx.datasourceFiles, ctx.systemKey, ctx.dryRun, ctx.changes
|
|
168
|
-
);
|
|
169
|
-
const authVarsRemoved = removeAuthVarsFromConfiguration(
|
|
170
|
-
ctx.systemParsed, ctx.systemKey, ctx.dryRun, ctx.changes
|
|
171
|
-
);
|
|
172
|
-
const systemFileUpdated =
|
|
173
|
-
authReplaced || systemAuthConfigNormalized || systemDataSourcesAligned || authVarsRemoved;
|
|
174
|
-
if (systemFileUpdated && !ctx.dryRun) {
|
|
175
|
-
backupIntegrationFile(ctx.systemFilePath, bc);
|
|
176
|
-
writeConfigFile(ctx.systemFilePath, ctx.systemParsed);
|
|
177
|
-
}
|
|
178
|
-
updated = updated || systemFileUpdated;
|
|
179
|
-
const appKeyFixed = alignAppKeyWithSystem(
|
|
180
|
-
ctx.variables, ctx.systemKey, ctx.systemParsed, ctx.changes
|
|
181
|
-
);
|
|
182
|
-
const datasourceKeysFixed = alignDatasourceSystemKeys(
|
|
183
|
-
ctx.appPath, ctx.datasourceFiles, ctx.systemKey, ctx.dryRun, ctx.changes, bc
|
|
184
|
-
);
|
|
185
|
-
const rbacFileCreated = createRbacFromSystemIfNeeded({
|
|
186
|
-
appPath: ctx.appPath,
|
|
187
|
-
systemFilePath: ctx.systemFilePath,
|
|
188
|
-
systemParsed: ctx.systemParsed,
|
|
189
|
-
dryRun: ctx.dryRun,
|
|
190
|
-
changes: ctx.changes,
|
|
191
|
-
format: ctx.format,
|
|
192
|
-
backupCtx: bc
|
|
193
|
-
});
|
|
194
|
-
const envTemplateRepaired = repairEnvTemplate(
|
|
195
|
-
ctx.appPath, ctx.systemParsed, ctx.systemKey, ctx.dryRun, ctx.changes, bc
|
|
196
|
-
);
|
|
197
|
-
updated = updated || appKeyFixed || datasourceKeysFixed || rbacFileCreated || envTemplateRepaired;
|
|
198
|
-
return {
|
|
199
|
-
updated,
|
|
200
|
-
appKeyFixed,
|
|
201
|
-
datasourceKeysFixed,
|
|
202
|
-
rbacFileCreated,
|
|
203
|
-
envTemplateRepaired
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Repairs external integration config: syncs files, aligns keys, creates rbac, regenerates manifest
|
|
209
|
-
* @async
|
|
210
|
-
* @param {string} appName - Application/integration name
|
|
211
|
-
* @param {Object} [options] - Options
|
|
212
|
-
* @param {boolean} [options.dryRun] - If true, only report changes; do not write
|
|
213
|
-
* @param {boolean} [options.doc] - If true, regenerate README.md from deployment manifest
|
|
214
|
-
* @returns {Promise<{ updated: boolean, changes: string[], systemFiles: string[], datasourceFiles: string[], appKeyFixed?: boolean, datasourceKeysFixed?: boolean, rbacFileCreated?: boolean, envTemplateRepaired?: boolean, manifestRegenerated?: boolean, readmeRegenerated?: boolean }>}
|
|
215
|
-
*/
|
|
216
22
|
/**
|
|
217
23
|
* Loads application config and discovers integration files; validates at least one system file exists.
|
|
218
24
|
* @param {string} appPath - Application path
|
|
@@ -226,43 +32,13 @@ function loadConfigAndDiscover(appPath, configPath) {
|
|
|
226
32
|
originalYamlContent = fs.readFileSync(configPath, 'utf8');
|
|
227
33
|
}
|
|
228
34
|
const { systemFiles, datasourceFiles: discoveredDatasourceFiles } = discoverIntegrationFiles(appPath);
|
|
229
|
-
if (systemFiles.length === 0) {
|
|
35
|
+
if (!systemFiles || systemFiles.length === 0) {
|
|
230
36
|
throw new Error(`No system file found in ${appPath}. Expected *-system.yaml or *-system.json`);
|
|
231
37
|
}
|
|
232
38
|
const datasourceFiles = buildEffectiveDatasourceFiles(appPath, discoveredDatasourceFiles, variables.externalIntegration?.dataSources);
|
|
233
39
|
return { variables, originalYamlContent, systemFiles, datasourceFiles };
|
|
234
40
|
}
|
|
235
41
|
|
|
236
|
-
/**
|
|
237
|
-
* Builds the repair result object from steps and flags.
|
|
238
|
-
* @param {Object} steps - Result of runRepairSteps
|
|
239
|
-
* @param {boolean} anyUpdated - Whether any repair made changes
|
|
240
|
-
* @param {boolean} manifestRegenerated - Whether manifest was regenerated
|
|
241
|
-
* @param {boolean} readmeRegenerated - Whether README was regenerated
|
|
242
|
-
* @param {{ changes: string[], systemFiles: string[], datasourceFiles: string[] }} ctx - changes and file lists
|
|
243
|
-
* @returns {Object} Combined result object
|
|
244
|
-
*/
|
|
245
|
-
function buildRepairResult(steps, anyUpdated, manifestRegenerated, readmeRegenerated, ctx) {
|
|
246
|
-
return Object.assign(
|
|
247
|
-
{
|
|
248
|
-
updated: anyUpdated,
|
|
249
|
-
actionsPerformed: ctx.actionsPerformed === true,
|
|
250
|
-
changes: ctx.changes,
|
|
251
|
-
systemFiles: ctx.systemFiles,
|
|
252
|
-
datasourceFiles: ctx.datasourceFiles,
|
|
253
|
-
backupPaths: ctx.backupPaths || []
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
appKeyFixed: steps.appKeyFixed,
|
|
257
|
-
datasourceKeysFixed: steps.datasourceKeysFixed,
|
|
258
|
-
rbacFileCreated: steps.rbacFileCreated,
|
|
259
|
-
envTemplateRepaired: steps.envTemplateRepaired,
|
|
260
|
-
manifestRegenerated,
|
|
261
|
-
readmeRegenerated
|
|
262
|
-
}
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
42
|
/**
|
|
267
43
|
* Validates repair inputs and resolves app path and config path.
|
|
268
44
|
* @param {string} appName - Application name
|
|
@@ -291,83 +67,40 @@ function createRepairBackupContext(options, dryRun) {
|
|
|
291
67
|
const noBackup = options.noBackup === true || options.backup === false;
|
|
292
68
|
const backupPaths = [];
|
|
293
69
|
const backedUpFiles = new Set();
|
|
294
|
-
const backupCtx = {
|
|
70
|
+
const backupCtx = {
|
|
71
|
+
dryRun,
|
|
72
|
+
noBackup,
|
|
73
|
+
backupPaths,
|
|
74
|
+
backedUpFiles,
|
|
75
|
+
changedFiles: [],
|
|
76
|
+
wouldChangeFiles: []
|
|
77
|
+
};
|
|
295
78
|
return { backupPaths, backupCtx };
|
|
296
79
|
}
|
|
297
80
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
});
|
|
308
|
-
openapiLines.forEach((l) => changes.push(l));
|
|
309
|
-
return openapiLines.length > 0;
|
|
310
|
-
} catch (err) {
|
|
311
|
-
changes.push(`OpenAPI upload for MCP failed: ${err.message}`);
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Repairs external integration config: syncs files, aligns keys, creates rbac, regenerates manifest
|
|
83
|
+
* @async
|
|
84
|
+
* @param {string} appName - Application/integration name
|
|
85
|
+
* @param {Object} [options] - Options
|
|
86
|
+
* @param {boolean} [options.dryRun] - If true, only report changes; do not write
|
|
87
|
+
* @param {boolean} [options.doc] - If true, regenerate README.md from deployment manifest
|
|
88
|
+
* @returns {Promise<Object>}
|
|
89
|
+
*/
|
|
316
90
|
async function repairExternalIntegration(appName, options = {}) {
|
|
317
91
|
const { appPath, configPath, dryRun, authOption } = await validateAndResolveRepairPaths(appName, options);
|
|
318
92
|
const { backupPaths, backupCtx } = createRepairBackupContext(options, dryRun);
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const { updated: keysNormalized, datasourceFiles } = normalizeDatasourceKeysAndFilenames(appPath, initialDatasourceFiles, systemKey, { variables, dryRun, changes, backupCtx });
|
|
324
|
-
const steps = runRepairSteps({
|
|
93
|
+
const loaded = loadConfigAndDiscover(appPath, configPath);
|
|
94
|
+
return runRepairExternalIntegration({
|
|
95
|
+
appName,
|
|
96
|
+
options,
|
|
325
97
|
appPath,
|
|
326
98
|
configPath,
|
|
327
|
-
variables,
|
|
328
|
-
systemFilePath,
|
|
329
|
-
systemParsed,
|
|
330
|
-
systemKey,
|
|
331
|
-
systemFiles,
|
|
332
|
-
datasourceFiles,
|
|
333
|
-
dryRun,
|
|
334
|
-
changes,
|
|
335
|
-
auth: authOption,
|
|
336
|
-
format: rbacFmt,
|
|
337
|
-
backupCtx
|
|
338
|
-
});
|
|
339
|
-
const datasourceRepairUpdated = runDatasourceRepairs(appPath, datasourceFiles, {
|
|
340
|
-
expose: Boolean(options.expose),
|
|
341
|
-
sync: Boolean(options.sync),
|
|
342
|
-
test: Boolean(options.test),
|
|
343
|
-
backupCtx
|
|
344
|
-
}, dryRun, changes);
|
|
345
|
-
const openapiActionsPerformed = await maybeRunOpenApiSyncForMcp({
|
|
346
|
-
options,
|
|
347
99
|
dryRun,
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
});
|
|
353
|
-
const { rbacMigratedFromSystem, rbacMergeUpdated } = maybeRepairRbac(
|
|
354
|
-
appPath,
|
|
355
|
-
systemFilePath,
|
|
356
|
-
systemParsed,
|
|
357
|
-
datasourceFiles,
|
|
358
|
-
{ rbac: Boolean(options.rbac), rbacFmt, dryRun, changes, backupCtx }
|
|
359
|
-
);
|
|
360
|
-
const anyLocalUpdated = keysNormalized || steps.updated || datasourceRepairUpdated || rbacMigratedFromSystem || rbacMergeUpdated;
|
|
361
|
-
const manifestRegenerated = (anyLocalUpdated && !dryRun) ? await persistChangesAndRegenerate(
|
|
362
|
-
{ configPath, variables, appName, appPath, changes, originalYamlContent, backupCtx }
|
|
363
|
-
) : false;
|
|
364
|
-
const readmeRegenerated = await regenerateReadmeIfRequested(appName, appPath, { ...options, backupCtx }, changes);
|
|
365
|
-
return buildRepairResult(steps, anyLocalUpdated, manifestRegenerated, readmeRegenerated, {
|
|
366
|
-
actionsPerformed: openapiActionsPerformed,
|
|
367
|
-
changes,
|
|
368
|
-
systemFiles,
|
|
369
|
-
datasourceFiles,
|
|
370
|
-
backupPaths
|
|
100
|
+
authOption,
|
|
101
|
+
backupPaths,
|
|
102
|
+
backupCtx,
|
|
103
|
+
...loaded
|
|
371
104
|
});
|
|
372
105
|
}
|
|
373
106
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview aifabrix secret get – read one secret (local user file or shared-only)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { getAifabrixSecretsPath } = require('../core/config');
|
|
9
|
+
const { decryptSecretsObject } = require('../core/secrets-load');
|
|
10
|
+
const { loadPrimaryUserSecrets } = require('../utils/secrets-utils');
|
|
11
|
+
const { loadConfiguredSharedSecretsStore } = require('../utils/remote-secrets-loader');
|
|
12
|
+
const remoteDevAuth = require('../utils/remote-dev-auth');
|
|
13
|
+
const devApi = require('../api/dev.api');
|
|
14
|
+
const logger = require('../utils/logger');
|
|
15
|
+
|
|
16
|
+
const REMOTE_NOT_CONFIGURED_MSG =
|
|
17
|
+
'Remote server is not configured. Set remote-server and run "aifabrix dev init" first.';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Human-readable label for which store(s) were queried (paths included for file stores).
|
|
21
|
+
* @param {{ shared?: boolean }} options
|
|
22
|
+
* @returns {Promise<string>}
|
|
23
|
+
*/
|
|
24
|
+
async function resolveSecretsScopeLabel(options) {
|
|
25
|
+
const useSharedOnly = Boolean(options.shared || options['shared']);
|
|
26
|
+
const generalSecretsPath = await getAifabrixSecretsPath();
|
|
27
|
+
|
|
28
|
+
if (useSharedOnly) {
|
|
29
|
+
if (!generalSecretsPath) {
|
|
30
|
+
return 'shared secrets (aifabrix-secrets not configured; run aifabrix secret set-secrets-file <path>)';
|
|
31
|
+
}
|
|
32
|
+
const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
|
|
33
|
+
if (remoteDevAuth.isRemoteSecretsUrl(target)) {
|
|
34
|
+
const host = remoteDevAuth.getSharedSecretsRemoteHostname(target) || target;
|
|
35
|
+
return `shared secrets API (${host})`;
|
|
36
|
+
}
|
|
37
|
+
const resolved = path.isAbsolute(target) ? target : path.resolve(process.cwd(), target);
|
|
38
|
+
return `shared secrets file ${resolved}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const pathsUtil = require('../utils/paths');
|
|
42
|
+
const userPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
43
|
+
return `local secrets file ${userPath}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {unknown} value
|
|
48
|
+
* @returns {boolean}
|
|
49
|
+
*/
|
|
50
|
+
function isSecretPresent(value) {
|
|
51
|
+
if (value === undefined || value === null) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return String(value).trim() !== '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {Array<{ name?: string, key?: string, value?: unknown }>} items
|
|
59
|
+
* @returns {Record<string, string>}
|
|
60
|
+
*/
|
|
61
|
+
function remoteSecretItemsToMap(items) {
|
|
62
|
+
const raw = {};
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
const name = item?.name || item?.key;
|
|
65
|
+
if (typeof name === 'string' && item.value !== undefined) {
|
|
66
|
+
raw[name] = String(item.value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return raw;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} target
|
|
74
|
+
* @returns {Promise<Record<string, string>>}
|
|
75
|
+
*/
|
|
76
|
+
async function loadRemoteSharedSecretsDecrypted(target) {
|
|
77
|
+
const auth = await remoteDevAuth.getRemoteDevAuth();
|
|
78
|
+
if (!auth) {
|
|
79
|
+
throw new Error(REMOTE_NOT_CONFIGURED_MSG);
|
|
80
|
+
}
|
|
81
|
+
const items = await devApi.listSecrets(
|
|
82
|
+
auth.serverUrl,
|
|
83
|
+
auth.clientCertPem,
|
|
84
|
+
auth.serverCaPem || undefined,
|
|
85
|
+
target
|
|
86
|
+
);
|
|
87
|
+
const label = remoteDevAuth.getSharedSecretsRemoteHostname(target) || 'shared secrets API';
|
|
88
|
+
return decryptSecretsObject(remoteSecretItemsToMap(items), { defaultSourceLabel: label });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Load shared secrets (file or remote), decrypted.
|
|
93
|
+
* @returns {Promise<Record<string, string>>}
|
|
94
|
+
*/
|
|
95
|
+
async function loadSharedSecretsDecrypted() {
|
|
96
|
+
const generalSecretsPath = await getAifabrixSecretsPath();
|
|
97
|
+
if (!generalSecretsPath) {
|
|
98
|
+
throw new Error('Shared secrets not configured. Set aifabrix-secrets in config.yaml.');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const target = await remoteDevAuth.resolveSharedSecretsEndpoint(generalSecretsPath);
|
|
102
|
+
if (remoteDevAuth.isRemoteSecretsUrl(target)) {
|
|
103
|
+
return loadRemoteSharedSecretsDecrypted(target);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const raw = await loadConfiguredSharedSecretsStore();
|
|
107
|
+
if (!raw || typeof raw !== 'object') {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
const resolvedPath = path.isAbsolute(target) ? target : path.resolve(process.cwd(), target);
|
|
111
|
+
return decryptSecretsObject(raw, { defaultSourceLabel: resolvedPath });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Load and decrypt the primary user secrets file only (not aifabrix-secrets / shared).
|
|
116
|
+
* @returns {Promise<Record<string, string>>}
|
|
117
|
+
*/
|
|
118
|
+
async function loadLocalUserSecretsDecrypted() {
|
|
119
|
+
const pathsUtil = require('../utils/paths');
|
|
120
|
+
const userPath = pathsUtil.getPrimaryUserSecretsLocalPath();
|
|
121
|
+
const raw = loadPrimaryUserSecrets();
|
|
122
|
+
return decryptSecretsObject(raw || {}, { defaultSourceLabel: userPath });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Load secrets for lookup: local user file (default) or shared store only (--shared).
|
|
127
|
+
* @param {{ shared?: boolean }} options
|
|
128
|
+
* @returns {Promise<Record<string, string>>}
|
|
129
|
+
*/
|
|
130
|
+
async function loadSecretsForGet(options) {
|
|
131
|
+
if (options.shared || options['shared']) {
|
|
132
|
+
return loadSharedSecretsDecrypted();
|
|
133
|
+
}
|
|
134
|
+
return loadLocalUserSecretsDecrypted();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Handle secret get command.
|
|
139
|
+
* @param {string} key - Secret key (no kv:// prefix)
|
|
140
|
+
* @param {Object} options
|
|
141
|
+
* @param {boolean} [options.shared] - Shared store only (aifabrix-secrets); default is local user file only
|
|
142
|
+
* @param {boolean} [options.exists] - Exit 0 when present; no stdout (script-friendly)
|
|
143
|
+
* @param {boolean} [options.json] - JSON { key, exists, value }
|
|
144
|
+
* @returns {Promise<{ key: string, exists: boolean, value?: string }>}
|
|
145
|
+
*/
|
|
146
|
+
async function handleSecretsGet(key, options = {}) {
|
|
147
|
+
if (!key || typeof key !== 'string') {
|
|
148
|
+
throw new Error('Secret key is required and must be a string');
|
|
149
|
+
}
|
|
150
|
+
if (key.startsWith('kv://')) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
'Secret key must not start with kv://. Use the key path without the prefix (e.g. hubspot-demo/token).'
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const secrets = await loadSecretsForGet(options);
|
|
157
|
+
const value = secrets[key];
|
|
158
|
+
const present = isSecretPresent(value);
|
|
159
|
+
|
|
160
|
+
if (!present) {
|
|
161
|
+
const scope = await resolveSecretsScopeLabel(options);
|
|
162
|
+
throw new Error(`Secret '${key}' is missing or empty in ${scope}.`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (options.exists || options['exists']) {
|
|
166
|
+
return { key, exists: true };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (options.json || options['json']) {
|
|
170
|
+
logger.log(JSON.stringify({ key, exists: true, value: String(value) }));
|
|
171
|
+
return { key, exists: true, value: String(value) };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
logger.log(String(value));
|
|
175
|
+
return { key, exists: true, value: String(value) };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
handleSecretsGet,
|
|
180
|
+
isSecretPresent,
|
|
181
|
+
loadSecretsForGet,
|
|
182
|
+
loadLocalUserSecretsDecrypted,
|
|
183
|
+
resolveSecretsScopeLabel
|
|
184
|
+
};
|
|
@@ -56,6 +56,8 @@ function listKeysFromYamlFile(filePath) {
|
|
|
56
56
|
* @param {string} filePath - Absolute path
|
|
57
57
|
*/
|
|
58
58
|
function writeEmptySecretsFile(filePath) {
|
|
59
|
+
const { assertWritableSecretsPathForTests } = require('../utils/aifabrix-test-runtime-guard');
|
|
60
|
+
assertWritableSecretsPathForTests(filePath);
|
|
59
61
|
const yamlContent = yaml.dump({}, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
|
|
60
62
|
fs.writeFileSync(filePath, yamlContent, { mode: 0o600 });
|
|
61
63
|
}
|
|
@@ -217,4 +219,4 @@ async function handleSecretsRemoveAll(options) {
|
|
|
217
219
|
await removeAllSharedSecrets(generalSecretsPath, options);
|
|
218
220
|
}
|
|
219
221
|
|
|
220
|
-
module.exports = { handleSecretsRemoveAll };
|
|
222
|
+
module.exports = { handleSecretsRemoveAll, writeEmptySecretsFile };
|