@aifabrix/builder 2.44.5 → 2.45.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 +8 -4
- package/.cursor/rules/project-rules.mdc +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +104 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +132 -118
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +9 -2
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/internal/node-fs.js +2 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/application-schema.json +4 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -1
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -388
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI registration for `aifabrix setup` and `aifabrix teardown`.
|
|
3
|
+
*
|
|
4
|
+
* `setup` is the one-shot platform installer. When no infrastructure is
|
|
5
|
+
* detected it runs a small wizard (admin email/password, AI tool keys) and
|
|
6
|
+
* then `up-infra` + `up-platform`. When infra is already up, it offers a
|
|
7
|
+
* mode menu (re-install, wipe data, clean install files, update images).
|
|
8
|
+
*
|
|
9
|
+
* `teardown` is the symmetrical inverse of `setup`: `down-infra -v` plus a
|
|
10
|
+
* full clean of the AI Fabrix system directory (everything except
|
|
11
|
+
* `config.yaml`).
|
|
12
|
+
*
|
|
13
|
+
* @fileoverview CLI command setup for setup/teardown
|
|
14
|
+
* @author AI Fabrix Team
|
|
15
|
+
* @version 2.0.0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const { handleCommandError } = require('../utils/cli-utils');
|
|
21
|
+
const { handleSetup } = require('../commands/setup');
|
|
22
|
+
const { handleTeardown } = require('../commands/teardown');
|
|
23
|
+
|
|
24
|
+
const SETUP_HELP_AFTER = `
|
|
25
|
+
What this command does:
|
|
26
|
+
- With no infra running: prompts for admin email/password (used for Postgres, pgAdmin, Keycloak). The email is saved as adminEmail in config.yaml so the next setup can show it as the default (you can still change it). Prompts for an AI tool (OpenAI or Azure OpenAI) only when keys are not already set, then runs up-infra and up-platform.
|
|
27
|
+
- With infra running: shows a mode menu:
|
|
28
|
+
1) Re-install — stop infra and remove all volumes, then up-infra + up-platform --force.
|
|
29
|
+
2) Wipe data — drop every database and DB user, then up-infra + up-platform --force.
|
|
30
|
+
3) Clean installation files — remove user-local secrets, then up-infra + up-platform --force.
|
|
31
|
+
4) Update images — pull the latest infra and platform images, then up-infra + up-platform.
|
|
32
|
+
|
|
33
|
+
AI tool keys are read from the user-local file (~/.aifabrix/secrets.local.yaml) merged with the shared aifabrix-secrets file. If either source already provides keys, the AI tool prompt is skipped.
|
|
34
|
+
|
|
35
|
+
Use 'aifabrix teardown' to fully remove the local installation.
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const TEARDOWN_HELP_AFTER = `
|
|
39
|
+
What this command does:
|
|
40
|
+
- Runs down-infra -v (stops all infra + apps, removes every Docker volume).
|
|
41
|
+
- Removes every file and subfolder inside ~/.aifabrix/ EXCEPT config.yaml.
|
|
42
|
+
This includes secrets.local.yaml, admin-secrets.env, auth/token files,
|
|
43
|
+
and infra-dev*/ directories.
|
|
44
|
+
|
|
45
|
+
This is the symmetrical inverse of 'aifabrix setup'. Use --yes / -y to skip the confirmation prompt in CI.
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Register the setup command on the Commander program.
|
|
50
|
+
* @param {import('commander').Command} program
|
|
51
|
+
*/
|
|
52
|
+
function registerSetup(program) {
|
|
53
|
+
program
|
|
54
|
+
.command('setup')
|
|
55
|
+
.description('Install or refresh the full AI Fabrix platform (infra + miso + dataplane)')
|
|
56
|
+
.option('-d, --developer <id>', 'Pin developer ID before fresh install (fresh path only; ignored when infra is already up)')
|
|
57
|
+
.option('-y, --yes', 'Skip destructive confirmation prompts (re-install, wipe data, teardown)')
|
|
58
|
+
.addHelpText('after', SETUP_HELP_AFTER)
|
|
59
|
+
.action(async(options) => {
|
|
60
|
+
try {
|
|
61
|
+
await handleSetup(options || {});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
handleCommandError(error, 'setup');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Register the teardown command on the Commander program.
|
|
71
|
+
* @param {import('commander').Command} program
|
|
72
|
+
*/
|
|
73
|
+
function registerTeardown(program) {
|
|
74
|
+
program
|
|
75
|
+
.command('teardown')
|
|
76
|
+
.description('Tear down local AI Fabrix infra and clean ~/.aifabrix/ except config.yaml')
|
|
77
|
+
.option('-y, --yes', 'Skip the confirmation prompt')
|
|
78
|
+
.addHelpText('after', TEARDOWN_HELP_AFTER)
|
|
79
|
+
.action(async(options) => {
|
|
80
|
+
try {
|
|
81
|
+
await handleTeardown(options || {});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
handleCommandError(error, 'teardown');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Register all setup-platform commands on the Commander program.
|
|
91
|
+
* @param {import('commander').Command} program
|
|
92
|
+
*/
|
|
93
|
+
function setupPlatformCommands(program) {
|
|
94
|
+
registerSetup(program);
|
|
95
|
+
registerTeardown(program);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
setupPlatformCommands,
|
|
100
|
+
registerSetup,
|
|
101
|
+
registerTeardown
|
|
102
|
+
};
|
package/lib/cli/setup-secrets.js
CHANGED
|
@@ -24,8 +24,8 @@ Subcommands:
|
|
|
24
24
|
|
|
25
25
|
Also: aifabrix secure Encrypt secrets.local.yaml (ISO 27001)
|
|
26
26
|
|
|
27
|
-
Default "secret set" (no --shared): writes only to your user secrets.local.yaml
|
|
28
|
-
|
|
27
|
+
Default "secret set" (no --shared): writes only to your user secrets.local.yaml under the
|
|
28
|
+
resolved AI Fabrix home (config key aifabrix-home, else ~/.aifabrix). Use --shared to write the
|
|
29
29
|
shared store from config aifabrix-secrets (another file or https), not that user file.
|
|
30
30
|
|
|
31
31
|
Examples:
|
|
@@ -46,8 +46,8 @@ Examples:
|
|
|
46
46
|
|
|
47
47
|
const SECRET_SET_HELP_AFTER = `
|
|
48
48
|
Where the value is stored:
|
|
49
|
-
No --shared User file secrets.local.yaml
|
|
50
|
-
|
|
49
|
+
No --shared User file secrets.local.yaml under the resolved AI Fabrix home
|
|
50
|
+
(config key aifabrix-home, else ~/.aifabrix/). Used for app/integration
|
|
51
51
|
secrets and kv:// resolution on your machine.
|
|
52
52
|
--shared The store set in config.yaml as aifabrix-secrets: a YAML file path or
|
|
53
53
|
an https secrets API (never the user-only file above unless you point
|
|
@@ -70,6 +70,13 @@ Examples:
|
|
|
70
70
|
`;
|
|
71
71
|
|
|
72
72
|
const SECRET_REMOVE_ALL_HELP_AFTER = `
|
|
73
|
+
Where keys are removed:
|
|
74
|
+
Default All keys in your user secrets.local.yaml (under resolved aifabrix home,
|
|
75
|
+
often ~/.aifabrix/). This is NOT the shared store unless you deliberately
|
|
76
|
+
point aifabrix-secrets there.
|
|
77
|
+
--shared Every key in the shared store from config aifabrix-secrets (YAML file
|
|
78
|
+
path or https secrets API). Requires that setting; use secret set-secrets-file if needed.
|
|
79
|
+
|
|
73
80
|
You will be asked to type "yes" to confirm unless you pass --yes.
|
|
74
81
|
|
|
75
82
|
Examples:
|
|
@@ -103,9 +110,14 @@ function setupSecretRemoveAllCommand(secretCmd) {
|
|
|
103
110
|
const { handleSecretsRemoveAll } = require('../commands/secrets-remove-all');
|
|
104
111
|
secretCmd
|
|
105
112
|
.command('remove-all')
|
|
106
|
-
.description(
|
|
113
|
+
.description(
|
|
114
|
+
'Remove every key from user secrets.local.yaml (add --shared for aifabrix-secrets store; confirm unless --yes)'
|
|
115
|
+
)
|
|
107
116
|
.addHelpText('after', SECRET_REMOVE_ALL_HELP_AFTER)
|
|
108
|
-
.option(
|
|
117
|
+
.option(
|
|
118
|
+
'--shared',
|
|
119
|
+
'Remove every key from shared secrets (config aifabrix-secrets: YAML file or https API)'
|
|
120
|
+
)
|
|
109
121
|
.option('-y, --yes', 'Skip confirmation prompt (non-interactive / scripts)')
|
|
110
122
|
.action(async options => {
|
|
111
123
|
try {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `aifabrix resolve <app>` command wiring (split from setup-utility for file/function size limits).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve CLI command
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const secrets = require('../core/secrets');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
14
|
+
const { handleCommandError } = require('../utils/cli-utils');
|
|
15
|
+
const coreConfig = require('../core/config');
|
|
16
|
+
const { resolvePreferLocalEnvOutputPathFlag } = require('../utils/applications-config-defaults');
|
|
17
|
+
const { getResolveAppPath } = require('../utils/paths');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} appName
|
|
21
|
+
* @param {string} appPath
|
|
22
|
+
* @param {boolean} envOnly
|
|
23
|
+
* @param {string} envPath
|
|
24
|
+
* @param {{ skipValidation?: boolean }} options
|
|
25
|
+
* @param {{ getManifestSourcePayload: function }} manifestEmit
|
|
26
|
+
* @returns {Promise<void>}
|
|
27
|
+
*/
|
|
28
|
+
async function runResolveJsonOutput(appName, appPath, envOnly, envPath, options, manifestEmit) {
|
|
29
|
+
/** @type {Record<string, unknown>} */
|
|
30
|
+
const payload = {
|
|
31
|
+
app: appName,
|
|
32
|
+
appPath,
|
|
33
|
+
envOnly,
|
|
34
|
+
envPath,
|
|
35
|
+
manifestSource: envOnly ? null : manifestEmit.getManifestSourcePayload(appName, appPath)
|
|
36
|
+
};
|
|
37
|
+
if (!envOnly && !options.skipValidation) {
|
|
38
|
+
const validate = require('../validation/validate');
|
|
39
|
+
payload.validation = await validate.validateAppOrFile(appName);
|
|
40
|
+
}
|
|
41
|
+
logger.log(JSON.stringify(payload, null, 2));
|
|
42
|
+
const v = payload.validation;
|
|
43
|
+
if (v && typeof v === 'object' && v.valid === false) {
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} appName
|
|
50
|
+
* @param {string} envPath
|
|
51
|
+
* @param {boolean} envOnly
|
|
52
|
+
* @param {{ skipValidation?: boolean }} options
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async function runResolveHumanOutput(appName, envPath, envOnly, options) {
|
|
56
|
+
logger.log(`✔ Generated .env file: ${envPath}`);
|
|
57
|
+
logger.log(chalk.gray(' Note: up-platform / up-miso / up-dataplane / register / build resolve secrets in memory only.'));
|
|
58
|
+
logger.log(chalk.gray(` Re-run "aifabrix resolve ${appName}" whenever you need an on-disk .env again.`));
|
|
59
|
+
if (envOnly) {
|
|
60
|
+
logger.log(chalk.gray(' (env-only mode: validation skipped; no application config file)'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (options.skipValidation) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const validate = require('../validation/validate');
|
|
67
|
+
const result = await validate.validateAppOrFile(appName);
|
|
68
|
+
validate.displayValidationResults(result);
|
|
69
|
+
if (!result.valid) {
|
|
70
|
+
logger.log(chalk.yellow('\n⚠ Validation found errors. Fix them before deploying.'));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} appName
|
|
77
|
+
* @param {{ force?: boolean, skipValidation?: boolean, json?: boolean }} options
|
|
78
|
+
* @returns {Promise<void>}
|
|
79
|
+
*/
|
|
80
|
+
async function runResolveAppAction(appName, options) {
|
|
81
|
+
const jsonMode = options.json === true;
|
|
82
|
+
const { appPath, envOnly } = await getResolveAppPath(appName);
|
|
83
|
+
const manifestEmit = require('../utils/manifest-source-emit');
|
|
84
|
+
manifestEmit.emitManifestMetadataLineIfTTY(logger, {
|
|
85
|
+
appKey: appName,
|
|
86
|
+
appPath,
|
|
87
|
+
envOnly,
|
|
88
|
+
json: jsonMode
|
|
89
|
+
});
|
|
90
|
+
const userCfg = await coreConfig.getConfig();
|
|
91
|
+
const preferLocalEnvOutputPath = await resolvePreferLocalEnvOutputPathFlag(userCfg, appName);
|
|
92
|
+
const envPath = await secrets.generateEnvFile(
|
|
93
|
+
appName,
|
|
94
|
+
undefined,
|
|
95
|
+
'docker',
|
|
96
|
+
options.force,
|
|
97
|
+
{
|
|
98
|
+
appPath,
|
|
99
|
+
envOnly,
|
|
100
|
+
skipOutputPath: false,
|
|
101
|
+
preserveFromPath: null,
|
|
102
|
+
preferLocalEnvOutputPath
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
if (jsonMode) {
|
|
106
|
+
await runResolveJsonOutput(appName, appPath, envOnly, envPath, options, manifestEmit);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
await runResolveHumanOutput(appName, envPath, envOnly, options);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {import('commander').Command} program
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
116
|
+
function setupResolveCommand(program) {
|
|
117
|
+
program.command('resolve <app>')
|
|
118
|
+
.description('Generate .env from template; optional validate after')
|
|
119
|
+
.option('-f, --force', 'Generate missing secret keys in secrets file')
|
|
120
|
+
.option('--skip-validation', 'Skip file validation after generating .env')
|
|
121
|
+
.option('--json', 'Print JSON summary (envPath, manifestSource, optional validation)')
|
|
122
|
+
.action(async(appName, options) => {
|
|
123
|
+
try {
|
|
124
|
+
await runResolveAppAction(appName, options);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
handleCommandError(error, 'resolve');
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { setupResolveCommand };
|
package/lib/cli/setup-utility.js
CHANGED
|
@@ -10,11 +10,11 @@ const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const chalk = require('chalk');
|
|
13
|
-
const secrets = require('../core/secrets');
|
|
14
13
|
const generator = require('../generator');
|
|
15
14
|
const logger = require('../utils/logger');
|
|
16
15
|
const { handleCommandError, logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
17
16
|
const { detectAppType, getDeployJsonPath, getResolveAppPath } = require('../utils/paths');
|
|
17
|
+
const { setupResolveCommand } = require('./setup-utility-resolve');
|
|
18
18
|
|
|
19
19
|
const JSON_HELP_AFTER = `
|
|
20
20
|
Example:
|
|
@@ -46,6 +46,8 @@ Examples:
|
|
|
46
46
|
|
|
47
47
|
$ aifabrix repair hubspot-demo --doc
|
|
48
48
|
Regenerate README.md from the current deployment manifest only (other drift fixes unchanged).
|
|
49
|
+
|
|
50
|
+
Before overwriting files, repair writes timestamped backups under integration/<systemKey>/backup/ (same as datasource capability copy). Use --no-backup to skip.
|
|
49
51
|
`;
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -119,46 +121,20 @@ function logSplitJsonResult(result) {
|
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
function setupResolveCommand(program) {
|
|
123
|
-
program.command('resolve <app>')
|
|
124
|
-
.description('Generate .env from template; optional validate after')
|
|
125
|
-
.option('-f, --force', 'Generate missing secret keys in secrets file')
|
|
126
|
-
.option('--skip-validation', 'Skip file validation after generating .env')
|
|
127
|
-
.action(async(appName, options) => {
|
|
128
|
-
try {
|
|
129
|
-
const { appPath, envOnly } = await getResolveAppPath(appName);
|
|
130
|
-
const envPath = await secrets.generateEnvFile(
|
|
131
|
-
appName,
|
|
132
|
-
undefined,
|
|
133
|
-
'docker',
|
|
134
|
-
options.force,
|
|
135
|
-
{ appPath, envOnly, skipOutputPath: false, preserveFromPath: null }
|
|
136
|
-
);
|
|
137
|
-
logger.log(`✔ Generated .env file: ${envPath}`);
|
|
138
|
-
if (envOnly) {
|
|
139
|
-
logger.log(chalk.gray(' (env-only mode: validation skipped; no application config file)'));
|
|
140
|
-
} else if (!options.skipValidation) {
|
|
141
|
-
const validate = require('../validation/validate');
|
|
142
|
-
const result = await validate.validateAppOrFile(appName);
|
|
143
|
-
validate.displayValidationResults(result);
|
|
144
|
-
if (!result.valid) {
|
|
145
|
-
logger.log(chalk.yellow('\n⚠ Validation found errors. Fix them before deploying.'));
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
handleCommandError(error, 'resolve');
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
124
|
function setupJsonCommand(program) {
|
|
157
125
|
program.command('json <app>')
|
|
158
126
|
.description('Write deployment JSON to disk for version control')
|
|
159
127
|
.addHelpText('after', JSON_HELP_AFTER)
|
|
160
128
|
.action(async(appName, options) => {
|
|
161
129
|
try {
|
|
130
|
+
const resolved = await getResolveAppPath(appName);
|
|
131
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
132
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
133
|
+
appKey: appName,
|
|
134
|
+
appPath: resolved.appPath,
|
|
135
|
+
envOnly: resolved.envOnly,
|
|
136
|
+
json: false
|
|
137
|
+
});
|
|
162
138
|
const result = await generator.generateDeployJsonWithValidation(appName, options);
|
|
163
139
|
if (result.success) {
|
|
164
140
|
const fileName = result.path.includes('application-schema.json') ? 'application-schema.json' : 'deployment JSON';
|
|
@@ -193,50 +169,93 @@ function setupSplitJsonCommand(program) {
|
|
|
193
169
|
});
|
|
194
170
|
}
|
|
195
171
|
|
|
172
|
+
function buildRepairExternalIntegrationOptions(appName, options, format) {
|
|
173
|
+
const all = options.all === true;
|
|
174
|
+
return {
|
|
175
|
+
auth: options.auth,
|
|
176
|
+
doc: options.doc || all,
|
|
177
|
+
dryRun: options.dryRun,
|
|
178
|
+
format,
|
|
179
|
+
rbac: options.rbac || all,
|
|
180
|
+
expose: options.expose || all,
|
|
181
|
+
sync: options.sync || all,
|
|
182
|
+
api: options.api || all,
|
|
183
|
+
test: options.test || all,
|
|
184
|
+
backup: options.backup,
|
|
185
|
+
noBackup: options.noBackup
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function logRepairResult(options, result) {
|
|
190
|
+
if (options.dryRun && result.updated && result.changes.length > 0) {
|
|
191
|
+
logger.log(chalk.yellow('\nWould apply:'));
|
|
192
|
+
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (Array.isArray(result.backupPaths) && result.backupPaths.length > 0) {
|
|
196
|
+
result.backupPaths.forEach((p) => logger.log(chalk.gray(`Backup: ${p}`)));
|
|
197
|
+
}
|
|
198
|
+
if (result.updated) {
|
|
199
|
+
logger.log(formatSuccessParagraph('Repaired external integration config.'));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (Array.isArray(result.changes) && result.changes.length > 0) {
|
|
203
|
+
logger.log(formatSuccessParagraph('Repair actions completed.'));
|
|
204
|
+
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (result.readmeRegenerated) {
|
|
208
|
+
logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Handler for `aifabrix repair <systemKey>`.
|
|
216
|
+
* @param {string} appName
|
|
217
|
+
* @param {object} options - Commander options
|
|
218
|
+
* @returns {Promise<void>}
|
|
219
|
+
*/
|
|
220
|
+
async function handleRepairCommand(appName, options) {
|
|
221
|
+
const { repairExternalIntegration } = require('../commands/repair');
|
|
222
|
+
const { detectAppType } = require('../utils/paths');
|
|
223
|
+
const { appPath } = await detectAppType(appName);
|
|
224
|
+
logOfflinePathWhenType(appPath);
|
|
225
|
+
let format = 'yaml';
|
|
226
|
+
try {
|
|
227
|
+
const config = require('../core/config');
|
|
228
|
+
format = (await config.getFormat()) || format;
|
|
229
|
+
} catch (_) {
|
|
230
|
+
/* default yaml when config unavailable */
|
|
231
|
+
}
|
|
232
|
+
const result = await repairExternalIntegration(
|
|
233
|
+
appName,
|
|
234
|
+
buildRepairExternalIntegrationOptions(appName, options, format)
|
|
235
|
+
);
|
|
236
|
+
logRepairResult(options, result);
|
|
237
|
+
}
|
|
238
|
+
|
|
196
239
|
function setupRepairCommand(program) {
|
|
197
240
|
program.command('repair <systemKey>')
|
|
198
241
|
.description('Fix external integration drift (files, RBAC, manifest, …)')
|
|
242
|
+
.option('--all', 'Run all repair actions (api, doc, expose, rbac, sync, test)')
|
|
243
|
+
.option(
|
|
244
|
+
'--api',
|
|
245
|
+
'Validate and sync API contracts needed for OpenAPI/MCP (uses local OpenAPI specs at integration/<systemKey>/openapi/*.json when present)'
|
|
246
|
+
)
|
|
199
247
|
.option('--auth <method>', 'Set authentication method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none); updates system file and env.template')
|
|
200
248
|
.option('--doc', 'Regenerate README.md from deployment manifest')
|
|
201
|
-
.option('--dry-run', 'Report changes only; do not write')
|
|
202
|
-
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
|
|
203
249
|
.option('--expose', 'Set exposed.schema on each datasource from all fieldMappings.attributes keys (metadata.<key>); removes deprecated exposed.attributes if present')
|
|
250
|
+
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
|
|
204
251
|
.option('--sync', 'Add default sync section to datasources that lack it')
|
|
205
252
|
.option('--test', 'Generate testPayload.payloadTemplate and testPayload.expectedResult from attributes')
|
|
253
|
+
.option('--no-backup', 'Skip timestamped copies under integration/<systemKey>/backup/')
|
|
254
|
+
.option('--dry-run', 'Report changes only; do not write')
|
|
206
255
|
.addHelpText('after', REPAIR_HELP_AFTER)
|
|
207
256
|
.action(async(appName, options) => {
|
|
208
257
|
try {
|
|
209
|
-
|
|
210
|
-
const { detectAppType } = require('../utils/paths');
|
|
211
|
-
const { appPath } = await detectAppType(appName);
|
|
212
|
-
logOfflinePathWhenType(appPath);
|
|
213
|
-
let format = 'yaml';
|
|
214
|
-
try {
|
|
215
|
-
const config = require('../core/config');
|
|
216
|
-
format = (await config.getFormat()) || format;
|
|
217
|
-
} catch (_) {
|
|
218
|
-
// use default yaml when config unavailable
|
|
219
|
-
}
|
|
220
|
-
const result = await repairExternalIntegration(appName, {
|
|
221
|
-
auth: options.auth,
|
|
222
|
-
doc: options.doc,
|
|
223
|
-
dryRun: options.dryRun,
|
|
224
|
-
format,
|
|
225
|
-
rbac: options.rbac,
|
|
226
|
-
expose: options.expose,
|
|
227
|
-
sync: options.sync,
|
|
228
|
-
test: options.test
|
|
229
|
-
});
|
|
230
|
-
if (options.dryRun && result.updated && result.changes.length > 0) {
|
|
231
|
-
logger.log(chalk.yellow('\nWould apply:'));
|
|
232
|
-
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
233
|
-
} else if (result.updated) {
|
|
234
|
-
logger.log(formatSuccessParagraph('Repaired external integration config.'));
|
|
235
|
-
} else if (result.readmeRegenerated) {
|
|
236
|
-
logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
|
|
237
|
-
} else {
|
|
238
|
-
logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
|
|
239
|
-
}
|
|
258
|
+
await handleRepairCommand(appName, options);
|
|
240
259
|
} catch (error) {
|
|
241
260
|
handleCommandError(error, 'repair');
|
|
242
261
|
process.exit(1);
|
|
@@ -308,28 +327,56 @@ function setupSplitJsonConvertShowCommands(program) {
|
|
|
308
327
|
setupShowCommand(program);
|
|
309
328
|
}
|
|
310
329
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
330
|
+
function emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat) {
|
|
331
|
+
if (outFormat === 'json' || !result?.appPath || !appOrFile || typeof appOrFile !== 'string') {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
if (fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile()) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const { emitManifestMetadataLineIfTTY } = require('../utils/manifest-source-emit');
|
|
339
|
+
emitManifestMetadataLineIfTTY(logger, {
|
|
340
|
+
appKey: appOrFile,
|
|
341
|
+
appPath: result.appPath,
|
|
342
|
+
envOnly: false,
|
|
343
|
+
json: false
|
|
344
|
+
});
|
|
345
|
+
} catch {
|
|
346
|
+
/* ignore emit failures */
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function runValidateBatchBranch(validate, opts) {
|
|
314
351
|
const integration = opts.integration === true;
|
|
315
352
|
const builder = opts.builder === true;
|
|
353
|
+
if (!integration && !builder) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
316
356
|
const outFormat = (opts.format || 'default').toLowerCase();
|
|
357
|
+
const batchResult = integration && builder
|
|
358
|
+
? await validate.validateAll(opts)
|
|
359
|
+
: integration
|
|
360
|
+
? await validate.validateAllIntegrations(opts)
|
|
361
|
+
: await validate.validateAllBuilderApps(opts);
|
|
362
|
+
if (outFormat === 'json') {
|
|
363
|
+
logger.log(JSON.stringify(batchResult, null, 2));
|
|
364
|
+
} else {
|
|
365
|
+
validate.displayBatchValidationResults(batchResult);
|
|
366
|
+
}
|
|
367
|
+
if (!batchResult.valid) process.exit(1);
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
317
370
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
? await validate.validateAllIntegrations(opts)
|
|
323
|
-
: await validate.validateAllBuilderApps(opts);
|
|
324
|
-
if (outFormat === 'json') {
|
|
325
|
-
logger.log(JSON.stringify(batchResult, null, 2));
|
|
326
|
-
} else {
|
|
327
|
-
validate.displayBatchValidationResults(batchResult);
|
|
328
|
-
}
|
|
329
|
-
if (!batchResult.valid) process.exit(1);
|
|
371
|
+
async function runValidateCommand(appOrFile, options) {
|
|
372
|
+
const validate = require('../validation/validate');
|
|
373
|
+
const opts = options.opts ? options.opts() : options;
|
|
374
|
+
if (await runValidateBatchBranch(validate, opts)) {
|
|
330
375
|
return;
|
|
331
376
|
}
|
|
332
377
|
|
|
378
|
+
const outFormat = (opts.format || 'default').toLowerCase();
|
|
379
|
+
|
|
333
380
|
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
334
381
|
logger.log(chalk.red('App name or file path is required, or use --integration / --builder'));
|
|
335
382
|
process.exit(1);
|
|
@@ -339,8 +386,20 @@ async function runValidateCommand(appOrFile, options) {
|
|
|
339
386
|
...opts,
|
|
340
387
|
certSync: opts.certSync === true
|
|
341
388
|
});
|
|
389
|
+
emitManifestAfterValidateIfApplicable(appOrFile, result, outFormat);
|
|
342
390
|
if (outFormat === 'json') {
|
|
343
|
-
|
|
391
|
+
let payload = result;
|
|
392
|
+
try {
|
|
393
|
+
if (result?.appPath && appOrFile && typeof appOrFile === 'string') {
|
|
394
|
+
if (!(fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile())) {
|
|
395
|
+
const { getManifestSourcePayload } = require('../utils/manifest-source-emit');
|
|
396
|
+
payload = { ...result, manifestSource: getManifestSourcePayload(appOrFile, result.appPath) };
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
/* keep plain result */
|
|
401
|
+
}
|
|
402
|
+
logger.log(JSON.stringify(payload, null, 2));
|
|
344
403
|
} else {
|
|
345
404
|
validate.displayValidationResults(result);
|
|
346
405
|
}
|