@aifabrix/builder 2.44.4 → 2.44.6
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 +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +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 +68 -17
- 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/types/wizard.types.js +2 -1
- 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/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.help.js +1 -1
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-app.test-commands.js +9 -5
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +138 -61
- 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.js +97 -33
- 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 +225 -19
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -354
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/test-e2e-external.js +4 -3
- package/lib/commands/up-common.js +97 -12
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +58 -15
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +72 -14
- package/lib/commands/wizard-headless.js +7 -3
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +210 -61
- package/lib/constants/infra-compose-service-names.js +40 -0
- 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 +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -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 +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -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/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +2 -2
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- 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/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/external-readme.js +117 -4
- 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 +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- 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 +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- 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 +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +73 -20
- 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 +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +7 -7
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +89 -102
- 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,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Datasource capability subcommands (copy, remove, create, diff, edit, validate).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Nested `aifabrix datasource capability` CLI (copy, relate, …)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const logger = require('../utils/logger');
|
|
11
|
+
const {
|
|
12
|
+
formatBlockingError,
|
|
13
|
+
formatSuccessLine,
|
|
14
|
+
headerKeyValue,
|
|
15
|
+
infoLine,
|
|
16
|
+
metadata
|
|
17
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
18
|
+
const { validateDatasourceFile, resolveValidateInputPath } = require('../datasource/validate');
|
|
19
|
+
const { runCapabilityCopy } = require('../datasource/capability/run-capability-copy');
|
|
20
|
+
const { runCapabilityRemove } = require('../datasource/capability/run-capability-remove');
|
|
21
|
+
const { runCapabilityDiff } = require('../datasource/capability/run-capability-diff');
|
|
22
|
+
const { runCapabilityEdit } = require('../datasource/capability/run-capability-edit');
|
|
23
|
+
const { checkCapabilitySlices } = require('../datasource/capability/validate-capability-slice');
|
|
24
|
+
const { logDatasourceValidateOutcome } = require('../datasource/datasource-validate-display');
|
|
25
|
+
const pathMod = require('path');
|
|
26
|
+
const { printCapabilitySuccessFooter } = require('./datasource-capability-output');
|
|
27
|
+
const { setupCapabilityRelateCommand } = require('./datasource-capability-relate-cli');
|
|
28
|
+
const { setupCapabilityDimensionCommand } = require('./datasource-capability-dimension-cli');
|
|
29
|
+
|
|
30
|
+
const CAP_COPY_HELP = `
|
|
31
|
+
Examples:
|
|
32
|
+
$ aifabrix datasource capability copy test-e2e-hubspot-companies --from create --as createBasicTrial --dry-run
|
|
33
|
+
$ af datasource cap copy integration/myapp/x-datasource-y.json --from list --as listVerbose --dry-run
|
|
34
|
+
|
|
35
|
+
When exposed.profiles.<from> exists it is copied to exposed.profiles.<as>.
|
|
36
|
+
With --test, matching testPayload.scenarios rows (same operation as the source capability / openapi / CIP keys) are cloned with operation set to --as.
|
|
37
|
+
|
|
38
|
+
Next step after mutating: aifabrix datasource validate <file-or-key>
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const CAP_CREATE_HELP = `
|
|
42
|
+
Requires exactly one source: **--from**, **--template**, or **--openapi-operation**.
|
|
43
|
+
|
|
44
|
+
**--from** clones openapi/CIP/profile like **capability copy**. **--openapi-operation** finds an existing **openapi.operations** entry by **operationId** and clones it with a minimal CIP fetch step. **--template** loads **lib/datasource/capability/templates/[name].json** (built-in **minimal-fetch**).
|
|
45
|
+
|
|
46
|
+
$ aifabrix datasource capability create my-datasource-key --from list --as listAlt
|
|
47
|
+
$ af datasource cap create ./x.json --openapi-operation get-crm-v3-objects-contacts --as listContacts
|
|
48
|
+
$ af datasource cap create ./x.json --template minimal-fetch --as probeRead
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const CAP_VALIDATE_HELP = `
|
|
52
|
+
$ aifabrix datasource capability validate test-e2e-hubspot-companies
|
|
53
|
+
$ af datasource cap validate ./integration/x/y-datasource-z.json --capability create
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const CAP_REMOVE_HELP = `
|
|
57
|
+
Examples:
|
|
58
|
+
$ aifabrix datasource capability remove test-e2e-hubspot-companies --capability createCliGolden --dry-run
|
|
59
|
+
$ af datasource cap remove ./integration/x/datasource.json --capability list
|
|
60
|
+
|
|
61
|
+
Next: aifabrix datasource validate <file-or-key>
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const CAP_DIFF_HELP = `
|
|
65
|
+
Compare OpenAPI + CIP slices (and optional exposed.profiles) for one capability between two files.
|
|
66
|
+
|
|
67
|
+
$ aifabrix datasource capability diff ./before.json ./after.json --capability create
|
|
68
|
+
$ af datasource cap diff a.json b.json --capability-a list --capability-b listVerbose --profile list
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
const CAP_EDIT_HELP = `
|
|
72
|
+
Uses $VISUAL or $EDITOR for the JSON editor; if both are unset and **nano** is on PATH, nano is used.
|
|
73
|
+
|
|
74
|
+
When **--section profile**, if **exposed.profiles** has a row for the capability you picked (interactive or **-c**), that row opens directly—no “which profile?” list. Use **--profile <key>** to edit a different row. Override the editor for one run: **--editor nano**.
|
|
75
|
+
|
|
76
|
+
$ aifabrix datasource capability edit my-datasource-key --capability create --section openapi
|
|
77
|
+
$ af datasource cap edit my-datasource-key -c updateAddress --section profile
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Copy/create from CLI: always calls copy with basicExposure false (not exposed on CLI).
|
|
82
|
+
*
|
|
83
|
+
* @param {string} fileOrKey
|
|
84
|
+
* @param {object} options - Commander options
|
|
85
|
+
* @returns {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
async function runCopyLikeAction(fileOrKey, options) {
|
|
88
|
+
const result = await runCapabilityCopy({
|
|
89
|
+
fileOrKey,
|
|
90
|
+
from: options.from,
|
|
91
|
+
as: options.as,
|
|
92
|
+
dryRun: Boolean(options.dryRun),
|
|
93
|
+
overwrite: Boolean(options.overwrite),
|
|
94
|
+
noBackup: Boolean(options.noBackup),
|
|
95
|
+
basicExposure: false,
|
|
96
|
+
includeTestPayload: Boolean(options.test),
|
|
97
|
+
openApiOperationId: options.openapiOperation,
|
|
98
|
+
template: options.template
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (result.dryRun) {
|
|
102
|
+
logger.log(infoLine('Dry run — planned JSON Patch operations:'));
|
|
103
|
+
logger.log('');
|
|
104
|
+
logger.log(JSON.stringify(result.patchOperations, null, 2));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (result.backupPath) {
|
|
109
|
+
logger.log(headerKeyValue('Backup:', result.backupPath));
|
|
110
|
+
}
|
|
111
|
+
printCapabilitySuccessFooter(result.resolvedPath, result.updatedSections);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} fileOrKey
|
|
116
|
+
* @param {object} options - Commander options
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
async function runRemoveAction(fileOrKey, options) {
|
|
120
|
+
const result = await runCapabilityRemove({
|
|
121
|
+
fileOrKey,
|
|
122
|
+
capability: options.capability,
|
|
123
|
+
dryRun: Boolean(options.dryRun),
|
|
124
|
+
noBackup: Boolean(options.noBackup),
|
|
125
|
+
force: Boolean(options.force)
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (result.dryRun) {
|
|
129
|
+
logger.log(infoLine('Dry run — planned JSON Patch operations:'));
|
|
130
|
+
logger.log('');
|
|
131
|
+
logger.log(JSON.stringify(result.patchOperations, null, 2));
|
|
132
|
+
if (!result.removed) {
|
|
133
|
+
logger.log(metadata('(no changes; capability already absent)'));
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (result.backupPath) {
|
|
139
|
+
logger.log(headerKeyValue('Backup:', result.backupPath));
|
|
140
|
+
}
|
|
141
|
+
const heading = result.removed ? 'Removed' : 'Unchanged';
|
|
142
|
+
printCapabilitySuccessFooter(result.resolvedPath, result.updatedSections, heading);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {import('commander').Command} cap - capability command group
|
|
147
|
+
* @returns {void}
|
|
148
|
+
*/
|
|
149
|
+
function setupCapabilityCopyCommand(cap) {
|
|
150
|
+
cap.command('copy <file-or-key>')
|
|
151
|
+
.description(
|
|
152
|
+
'Clone openapi + CIP operations (lowercase keys), profiles (--as casing); optional --test for scenarios'
|
|
153
|
+
)
|
|
154
|
+
.requiredOption('--from <key>', 'Source capability key (must exist)')
|
|
155
|
+
.requiredOption('--as <key>', 'Target capability key (must be unique unless --overwrite)')
|
|
156
|
+
.option('--dry-run', 'Print JSON Patch operations; do not write')
|
|
157
|
+
.option('--overwrite', 'Replace target capability and matching exposed.profiles.<as> if present')
|
|
158
|
+
.option('--no-backup', 'Skip backup copy under integration/<app>/backup/')
|
|
159
|
+
.option(
|
|
160
|
+
'--test',
|
|
161
|
+
'Also clone testPayload.scenarios rows whose operation matches the source capability'
|
|
162
|
+
)
|
|
163
|
+
.addHelpText('after', CAP_COPY_HELP)
|
|
164
|
+
.action(async(fileOrKey, options) => {
|
|
165
|
+
try {
|
|
166
|
+
await runCopyLikeAction(fileOrKey, options);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
logger.error(formatBlockingError(`capability copy failed: ${error.message}`));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {import('commander').Command} cap
|
|
176
|
+
* @returns {void}
|
|
177
|
+
*/
|
|
178
|
+
function setupCapabilityRemoveCommand(cap) {
|
|
179
|
+
cap.command('remove <file-or-key>')
|
|
180
|
+
.description(
|
|
181
|
+
'Remove one capability (capabilities[], openapi.operations, execution.cip.operations), exposed.profiles.<key>, and matching testPayload.scenarios rows'
|
|
182
|
+
)
|
|
183
|
+
.requiredOption('-c, --capability <key>', 'Capability key to delete')
|
|
184
|
+
.option('--dry-run', 'Print JSON Patch operations; do not write')
|
|
185
|
+
.option('--no-backup', 'Skip backup copy under integration/<app>/backup/')
|
|
186
|
+
.option('--force', 'Succeed if capability is already absent (no file change)')
|
|
187
|
+
.addHelpText('after', CAP_REMOVE_HELP)
|
|
188
|
+
.action(async(fileOrKey, options) => {
|
|
189
|
+
try {
|
|
190
|
+
await runRemoveAction(fileOrKey, options);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logger.error(formatBlockingError(`capability remove failed: ${error.message}`));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {import('commander').Command} cap
|
|
200
|
+
* @returns {void}
|
|
201
|
+
*/
|
|
202
|
+
function setupCapabilityCreateCommand(cap) {
|
|
203
|
+
const createCmd = cap
|
|
204
|
+
.command('create <file-or-key>')
|
|
205
|
+
.description(
|
|
206
|
+
'Create capability: exactly one of --from, --template, or --openapi-operation (alias: add)'
|
|
207
|
+
);
|
|
208
|
+
if (typeof createCmd.alias === 'function') {
|
|
209
|
+
createCmd.alias('add');
|
|
210
|
+
}
|
|
211
|
+
createCmd
|
|
212
|
+
.requiredOption('--as <key>', 'New capability key (must be unique unless --overwrite)')
|
|
213
|
+
.option('--from <key>', 'Clone from an existing capability (same as capability copy)')
|
|
214
|
+
.option(
|
|
215
|
+
'--openapi-operation <operationId>',
|
|
216
|
+
'Match openapi.operations[].operationId in this file; minimal CIP fetch step'
|
|
217
|
+
)
|
|
218
|
+
.option('--template <name>', 'Built-in templates under capability/templates (e.g. minimal-fetch)')
|
|
219
|
+
.option('--dry-run', 'Print JSON Patch operations; do not write')
|
|
220
|
+
.option('--overwrite', 'Replace target capability and matching exposed.profiles.<as> if present')
|
|
221
|
+
.option('--no-backup', 'Skip backup')
|
|
222
|
+
.option(
|
|
223
|
+
'--test',
|
|
224
|
+
'With --from: clone testPayload.scenarios rows whose operation matches the source capability'
|
|
225
|
+
)
|
|
226
|
+
.addHelpText('after', CAP_CREATE_HELP)
|
|
227
|
+
.action(async(fileOrKey, options) => {
|
|
228
|
+
try {
|
|
229
|
+
await runCopyLikeAction(fileOrKey, options);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
logger.error(formatBlockingError(`capability create failed: ${error.message}`));
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {import('commander').Command} cap
|
|
239
|
+
* @returns {void}
|
|
240
|
+
*/
|
|
241
|
+
function setupCapabilityDiffCommand(cap) {
|
|
242
|
+
cap
|
|
243
|
+
.command('diff <file-a> <file-b>')
|
|
244
|
+
.description(
|
|
245
|
+
'Compare capability slices between two datasource JSON files (optional exposed.profiles profile)'
|
|
246
|
+
)
|
|
247
|
+
.option(
|
|
248
|
+
'-c, --capability <key>',
|
|
249
|
+
'Capability key on both sides (use --capability-a/b to compare different keys)'
|
|
250
|
+
)
|
|
251
|
+
.option('--capability-a <key>', 'Capability key in first file')
|
|
252
|
+
.option('--capability-b <key>', 'Capability key in second file')
|
|
253
|
+
.option('--profile <name>', 'Include exposed.profiles.<name> on both sides')
|
|
254
|
+
.option('--profile-a <name>', 'Profile key for first file')
|
|
255
|
+
.option('--profile-b <name>', 'Profile key for second file')
|
|
256
|
+
.addHelpText('after', CAP_DIFF_HELP)
|
|
257
|
+
.action(async(fileA, fileB, options) => {
|
|
258
|
+
try {
|
|
259
|
+
const { identical } = runCapabilityDiff({
|
|
260
|
+
fileA,
|
|
261
|
+
fileB,
|
|
262
|
+
capability: options.capability,
|
|
263
|
+
capabilityA: options.capabilityA,
|
|
264
|
+
capabilityB: options.capabilityB,
|
|
265
|
+
profile: options.profile,
|
|
266
|
+
profileA: options.profileA,
|
|
267
|
+
profileB: options.profileB
|
|
268
|
+
});
|
|
269
|
+
if (!identical) {
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
logger.error(formatBlockingError(`capability diff failed: ${error.message}`));
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @param {import('commander').Command} cap
|
|
281
|
+
* @returns {void}
|
|
282
|
+
*/
|
|
283
|
+
function setupCapabilityEditCommand(cap) {
|
|
284
|
+
cap
|
|
285
|
+
.command('edit <file-or-key>')
|
|
286
|
+
.description(
|
|
287
|
+
'Interactively edit openapi/cip/exposed profile JSON for one capability (TTY + $EDITOR / nano)'
|
|
288
|
+
)
|
|
289
|
+
.option('-c, --capability <key>', 'Capability key (skip prompt)')
|
|
290
|
+
.option('--section <name>', 'openapi | cip | profile (skip prompt)')
|
|
291
|
+
.option('--profile <name>', 'When section=profile, exposed.profiles key (skip prompt)')
|
|
292
|
+
.option('--editor <cmd>', 'Editor command for this run (sets VISUAL and EDITOR)')
|
|
293
|
+
.option('--no-backup', 'Skip backup copy under integration/<app>/backup/')
|
|
294
|
+
.addHelpText('after', CAP_EDIT_HELP)
|
|
295
|
+
.action(async(fileOrKey, options) => {
|
|
296
|
+
try {
|
|
297
|
+
const editorArg = options.editor !== undefined && options.editor !== null ? String(options.editor).trim() : '';
|
|
298
|
+
if (editorArg) {
|
|
299
|
+
process.env.VISUAL = editorArg;
|
|
300
|
+
process.env.EDITOR = editorArg;
|
|
301
|
+
}
|
|
302
|
+
const section = normalizeCapabilityEditSection(options.section);
|
|
303
|
+
const result = await runCapabilityEdit({
|
|
304
|
+
fileOrKey,
|
|
305
|
+
capability: options.capability,
|
|
306
|
+
section,
|
|
307
|
+
profile: options.profile,
|
|
308
|
+
noBackup: Boolean(options.noBackup)
|
|
309
|
+
});
|
|
310
|
+
if (result.backupPath) {
|
|
311
|
+
logger.log(headerKeyValue('Backup:', result.backupPath));
|
|
312
|
+
}
|
|
313
|
+
printCapabilitySuccessFooter(result.resolvedPath, ['Saved capability slice JSON'], 'Updated');
|
|
314
|
+
} catch (error) {
|
|
315
|
+
logger.error(formatBlockingError(`capability edit failed: ${error.message}`));
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @param {import('commander').Command} cap
|
|
323
|
+
* @returns {void}
|
|
324
|
+
*/
|
|
325
|
+
function setupCapabilityValidateCommand(cap) {
|
|
326
|
+
cap.command('validate <file-or-key>')
|
|
327
|
+
.description('Validate datasource JSON; optional --capability slice presence check')
|
|
328
|
+
.option('-c, --capability <key>', 'Ensure capability exists in openapi + cip + capabilities[]')
|
|
329
|
+
.addHelpText('after', CAP_VALIDATE_HELP)
|
|
330
|
+
.action(async(fileOrKey, options) => {
|
|
331
|
+
try {
|
|
332
|
+
const trimmed = fileOrKey.trim();
|
|
333
|
+
const result = await validateDatasourceFile(trimmed);
|
|
334
|
+
const resolvedPath = result.resolvedPath;
|
|
335
|
+
const argResolved = pathMod.resolve(trimmed);
|
|
336
|
+
const showMapping = resolvedPath && argResolved !== resolvedPath && trimmed !== resolvedPath;
|
|
337
|
+
logDatasourceValidateOutcome(result, trimmed, showMapping);
|
|
338
|
+
|
|
339
|
+
if (!result.valid) {
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (options.capability) {
|
|
344
|
+
const readPath = resolveValidateInputPath(trimmed);
|
|
345
|
+
const parsed = JSON.parse(fs.readFileSync(readPath, 'utf8'));
|
|
346
|
+
const slice = checkCapabilitySlices(parsed, options.capability);
|
|
347
|
+
if (slice.missing.length > 0) {
|
|
348
|
+
logger.error(
|
|
349
|
+
formatBlockingError(
|
|
350
|
+
`Capability "${slice.key}" incomplete: ${slice.missing.join('; ')}`
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
logger.log(formatSuccessLine(`Capability slice OK: ${slice.key}`));
|
|
356
|
+
}
|
|
357
|
+
} catch (error) {
|
|
358
|
+
logger.error(formatBlockingError(`capability validate failed: ${error.message}`));
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Normalize `--section` for capability edit (openapi | cip | profile).
|
|
366
|
+
*
|
|
367
|
+
* @param {unknown} raw - Commander option value
|
|
368
|
+
* @returns {'openapi'|'cip'|'profile'|undefined}
|
|
369
|
+
* @throws {Error} When value is non-empty but not allowed
|
|
370
|
+
*/
|
|
371
|
+
function normalizeCapabilityEditSection(raw) {
|
|
372
|
+
const sec = raw !== undefined && raw !== null ? String(raw).trim().toLowerCase() : '';
|
|
373
|
+
if (sec && !['openapi', 'cip', 'profile'].includes(sec)) {
|
|
374
|
+
throw new Error('--section must be openapi, cip, or profile');
|
|
375
|
+
}
|
|
376
|
+
return sec || undefined;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Register nested datasource capability commands.
|
|
381
|
+
*
|
|
382
|
+
* @param {import('commander').Command} datasource - datasource command group
|
|
383
|
+
* @returns {void}
|
|
384
|
+
*/
|
|
385
|
+
function setupDatasourceCapabilityCommands(datasource) {
|
|
386
|
+
const cap = datasource
|
|
387
|
+
.command('capability')
|
|
388
|
+
.description(
|
|
389
|
+
'Copy, remove, relate (FK metadata), diff, edit, or validate per-capability slices in datasource JSON'
|
|
390
|
+
);
|
|
391
|
+
if (typeof cap.alias === 'function') {
|
|
392
|
+
cap.alias('cap');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
setupCapabilityCopyCommand(cap);
|
|
396
|
+
setupCapabilityRemoveCommand(cap);
|
|
397
|
+
setupCapabilityCreateCommand(cap);
|
|
398
|
+
setupCapabilityRelateCommand(cap);
|
|
399
|
+
setupCapabilityDimensionCommand(cap);
|
|
400
|
+
setupCapabilityDiffCommand(cap);
|
|
401
|
+
setupCapabilityEditCommand(cap);
|
|
402
|
+
setupCapabilityValidateCommand(cap);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = {
|
|
406
|
+
setupDatasourceCapabilityCommands,
|
|
407
|
+
printCapabilitySuccessFooter,
|
|
408
|
+
runCopyLikeAction,
|
|
409
|
+
runRemoveAction,
|
|
410
|
+
normalizeCapabilityEditSection
|
|
411
|
+
};
|
|
@@ -125,7 +125,7 @@ function attachDatasourceTestCommonOptions(cmd, opts) {
|
|
|
125
125
|
)
|
|
126
126
|
.option(
|
|
127
127
|
'--sync',
|
|
128
|
-
'Publish
|
|
128
|
+
'Publish local integration config to the dataplane before running the test (same as aifabrix upload <systemKey>; requires login)'
|
|
129
129
|
)
|
|
130
130
|
.option('--json', 'Print raw DatasourceTestRun JSON to stdout')
|
|
131
131
|
.option('--summary', 'Print compact summary line')
|
|
@@ -23,16 +23,28 @@ const {
|
|
|
23
23
|
setupDatasourceTestIntegrationCommand,
|
|
24
24
|
setupDatasourceTestE2ECommand
|
|
25
25
|
} = require('./datasource-unified-test-cli');
|
|
26
|
+
const { setupDatasourceCapabilityCommands } = require('./datasource-capability');
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Parent help only. Commander already prints the full "Commands:" list; do not duplicate it here.
|
|
30
|
+
* Use examples + alias note so `af datasource -h` stays short and non-repetitive.
|
|
31
|
+
*/
|
|
27
32
|
const DATASOURCE_HELP_AFTER = `
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
$ aifabrix datasource validate integration/<app>/<app>-datasource-contacts.json
|
|
36
|
+
$ aifabrix datasource list
|
|
37
|
+
$ aifabrix datasource list test # only datasource keys starting with "test"
|
|
38
|
+
$ af ds upload <datasourceKey> # same as: aifabrix datasource upload …
|
|
39
|
+
$ af ds test <datasourceKey> --app <integrationFolder> --debug
|
|
40
|
+
$ af ds test-e2e <datasourceKey> --app <integrationFolder> -v
|
|
41
|
+
$ aifabrix datasource capability copy <file-or-key> --from create --as createBasicTrial --dry-run
|
|
42
|
+
$ aifabrix datasource capability remove <file-or-key> --capability createTrial --dry-run
|
|
43
|
+
$ aifabrix datasource capability diff ./a.json ./b.json --capability list
|
|
44
|
+
$ aifabrix datasource capability edit <file-or-key> --capability create --section openapi
|
|
45
|
+
|
|
46
|
+
Per-command help: aifabrix datasource <command> --help
|
|
47
|
+
Shorthand: af ds (alias for datasource)
|
|
36
48
|
`;
|
|
37
49
|
|
|
38
50
|
const DATASOURCE_VALIDATE_HELP_AFTER = `
|
|
@@ -53,6 +65,25 @@ Examples:
|
|
|
53
65
|
$ af ds upload ../integration/hubspot/hubspot-datasource-deals.json
|
|
54
66
|
`;
|
|
55
67
|
|
|
68
|
+
const DATASOURCE_DIFF_HELP_AFTER = `
|
|
69
|
+
Examples:
|
|
70
|
+
$ aifabrix datasource diff integration/myapp/old-datasource.json integration/myapp/new-datasource.json
|
|
71
|
+
$ af ds diff ./before.json ./after.json
|
|
72
|
+
$ af ds diff /abs/path/a.json /abs/path/b.json
|
|
73
|
+
|
|
74
|
+
Note: Both arguments must be file paths. Use paths under integration/<app>/ or any two JSON files on disk.
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const DATASOURCE_LIST_HELP_AFTER = `
|
|
78
|
+
Examples:
|
|
79
|
+
$ aifabrix datasource list
|
|
80
|
+
$ aifabrix datasource list test # keys starting with "test" (e.g. test-e2e-hubspot-users)
|
|
81
|
+
$ af ds list hubspot # keys starting with "hubspot"
|
|
82
|
+
|
|
83
|
+
Filter applies to the datasource key field returned by the dataplane (prefix match, case-sensitive).
|
|
84
|
+
Omit [prefix] to list all datasources for the active environment.
|
|
85
|
+
`;
|
|
86
|
+
|
|
56
87
|
function setupDatasourceValidateCommand(datasource) {
|
|
57
88
|
datasource.command('validate <file-or-key>')
|
|
58
89
|
.description('Validate datasource JSON (file path or datasource key under integration/<app>/)')
|
|
@@ -76,11 +107,18 @@ function setupDatasourceValidateCommand(datasource) {
|
|
|
76
107
|
}
|
|
77
108
|
|
|
78
109
|
function setupDatasourceListCommand(datasource) {
|
|
79
|
-
datasource.command('list')
|
|
80
|
-
.description(
|
|
81
|
-
|
|
110
|
+
datasource.command('list [prefix]')
|
|
111
|
+
.description(
|
|
112
|
+
'List datasources for environment in config (optional prefix filters datasource keys)'
|
|
113
|
+
)
|
|
114
|
+
.addHelpText('after', DATASOURCE_LIST_HELP_AFTER)
|
|
115
|
+
.action(async(prefix) => {
|
|
82
116
|
try {
|
|
83
|
-
|
|
117
|
+
const opts = {};
|
|
118
|
+
if (typeof prefix === 'string' && prefix.trim()) {
|
|
119
|
+
opts.keyPrefix = prefix.trim();
|
|
120
|
+
}
|
|
121
|
+
await listDatasources(opts);
|
|
84
122
|
} catch (error) {
|
|
85
123
|
logger.error(formatBlockingError('Failed to list datasources:'), error.message);
|
|
86
124
|
process.exit(1);
|
|
@@ -90,7 +128,8 @@ function setupDatasourceListCommand(datasource) {
|
|
|
90
128
|
|
|
91
129
|
function setupDatasourceDiffCommand(datasource) {
|
|
92
130
|
datasource.command('diff <file1> <file2>')
|
|
93
|
-
.description('Diff two datasource JSON files')
|
|
131
|
+
.description('Diff two datasource JSON files (two file paths; not datasource keys)')
|
|
132
|
+
.addHelpText('after', DATASOURCE_DIFF_HELP_AFTER)
|
|
94
133
|
.action(async(file1, file2) => {
|
|
95
134
|
try {
|
|
96
135
|
await compareDatasources(file1, file2);
|
|
@@ -193,6 +232,7 @@ function setupDatasourceCommands(program) {
|
|
|
193
232
|
if (typeof datasource.alias === 'function') {
|
|
194
233
|
datasource.alias('ds');
|
|
195
234
|
}
|
|
235
|
+
setupDatasourceCapabilityCommands(datasource);
|
|
196
236
|
setupDatasourceValidateCommand(datasource);
|
|
197
237
|
setupDatasourceListCommand(datasource);
|
|
198
238
|
setupDatasourceDiffCommand(datasource);
|
package/lib/commands/dev-down.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* dev down – stop Mutagen sync sessions and optionally app containers for this developer.
|
|
4
4
|
*
|
|
@@ -39,7 +39,7 @@ async function stopMutagenSessions(developerId) {
|
|
|
39
39
|
const toTerminate = sessions.filter(name => name.startsWith(prefix));
|
|
40
40
|
for (const name of toTerminate) {
|
|
41
41
|
await execAsync(`"${mutagenPath}" sync terminate "${name}"`, { timeout: 5000 });
|
|
42
|
-
logger.log(chalk.green(
|
|
42
|
+
logger.log(chalk.green(' ') + formatSuccessLine(`Stopped sync session: ${name}`));
|
|
43
43
|
}
|
|
44
44
|
if (toTerminate.length === 0 && sessions.length > 0) {
|
|
45
45
|
logger.log(chalk.gray('No sync sessions for this developer.'));
|
|
@@ -99,7 +99,7 @@ async function handleDevDown(options = {}) {
|
|
|
99
99
|
if (!appName) continue;
|
|
100
100
|
try {
|
|
101
101
|
await appLib.downApp(appName, {});
|
|
102
|
-
logger.log(chalk.green(
|
|
102
|
+
logger.log(chalk.green(' ') + formatSuccessLine(`Stopped app: ${appName}`));
|
|
103
103
|
} catch (err) {
|
|
104
104
|
logger.log(chalk.yellow(` ⚠ Could not stop ${appName}: ${err.message}`));
|
|
105
105
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared strict check that developer Postgres/Redis infra is running.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const logger = require('../utils/logger');
|
|
8
|
+
const infra = require('../infrastructure');
|
|
9
|
+
const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
10
|
+
|
|
11
|
+
const DEV_INFRA_DOWN_MESSAGE = 'Infrastructure is not up. Run \'aifabrix up-infra\' first.';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Ensures this developer's Postgres/Redis (strict scope) are healthy.
|
|
15
|
+
*
|
|
16
|
+
* @param {{ quietSuccess?: boolean }} [opts] - When `quietSuccess`, omit the success log (e.g. guided spinner shows it).
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
* @throws {Error} When infra is not healthy
|
|
19
|
+
*/
|
|
20
|
+
async function assertDevInfraUp(opts = {}) {
|
|
21
|
+
const quietSuccess = Boolean(opts.quietSuccess);
|
|
22
|
+
const health = await infra.checkInfraHealth(undefined, { strict: true });
|
|
23
|
+
const allHealthy = Object.values(health).every((status) => status === 'healthy');
|
|
24
|
+
if (!allHealthy) {
|
|
25
|
+
throw new Error(DEV_INFRA_DOWN_MESSAGE);
|
|
26
|
+
}
|
|
27
|
+
if (!quietSuccess) {
|
|
28
|
+
logger.log(formatSuccessLine('Infrastructure is up'));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { assertDevInfraUp, DEV_INFRA_DOWN_MESSAGE };
|
package/lib/commands/dev-init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
|
+
const { formatSuccessLine, formatSuccessParagraph, successGlyph } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* @fileoverview aifabrix dev init – onboard with Builder Server (issue-cert, save cert, get settings, add SSH key, SSH config alias).
|
|
4
4
|
* Auth: first call (issue-cert) uses no client cert; other calls send the client cert (mTLS on https, X-Client-Cert header on http for getSettings/addSshKey).
|
|
@@ -195,12 +195,18 @@ async function saveCertAndConfig(configDir, devId, certificatePem, keyPem, caPem
|
|
|
195
195
|
if (caPem && typeof caPem === 'string' && caPem.trim()) {
|
|
196
196
|
const caNormalized = normalizePemNewlines(caPem.trim());
|
|
197
197
|
await fs.writeFile(path.join(certDir, 'ca.pem'), caNormalized, { mode: 0o600 });
|
|
198
|
-
logger.log(
|
|
198
|
+
logger.log(
|
|
199
|
+
`${chalk.green(' ')}${successGlyph()}${chalk.green(' Certificate and CA saved to ')}${chalk.cyan(path.join(certDir, 'cert.pem'))}`
|
|
200
|
+
);
|
|
199
201
|
} else {
|
|
200
|
-
logger.log(
|
|
202
|
+
logger.log(
|
|
203
|
+
`${chalk.green(' ')}${successGlyph()}${chalk.green(' Certificate saved to ')}${chalk.cyan(path.join(certDir, 'cert.pem'))}`
|
|
204
|
+
);
|
|
201
205
|
}
|
|
202
206
|
await config.setDeveloperId(devId);
|
|
203
|
-
logger.log(
|
|
207
|
+
logger.log(
|
|
208
|
+
`${chalk.green(' ')}${successGlyph()}${chalk.green(' Developer ID set to ')}${chalk.cyan(devId)}`
|
|
209
|
+
);
|
|
204
210
|
}
|
|
205
211
|
|
|
206
212
|
/**
|
|
@@ -217,7 +223,7 @@ async function registerSshKey(baseUrl, clientCertPem, clientKeyPem, devId, serve
|
|
|
217
223
|
publicKey,
|
|
218
224
|
label: 'aifabrix-init'
|
|
219
225
|
}, clientKeyPem, serverCaPem);
|
|
220
|
-
logger.log(chalk.green('
|
|
226
|
+
logger.log(chalk.green(' ') + formatSuccessLine('SSH key registered'));
|
|
221
227
|
} catch (err) {
|
|
222
228
|
if (err.status === 409) {
|
|
223
229
|
logger.log(chalk.yellow(' ⚠ SSH key already registered'));
|
|
@@ -262,7 +268,7 @@ async function applySettingsFromServer(baseUrl, devId, issueResponse, keyPem, se
|
|
|
262
268
|
const configDir = getConfigDirForPaths();
|
|
263
269
|
if (issueResponse.settings && typeof issueResponse.settings === 'object') {
|
|
264
270
|
await config.mergeRemoteSettings(issueResponse.settings);
|
|
265
|
-
logger.log(chalk.green('
|
|
271
|
+
logger.log(chalk.green(' ') + formatSuccessLine('Config updated from server (issue-cert response)'));
|
|
266
272
|
return;
|
|
267
273
|
}
|
|
268
274
|
logger.log(chalk.gray(' Fetching settings...'));
|
|
@@ -272,7 +278,7 @@ async function applySettingsFromServer(baseUrl, devId, issueResponse, keyPem, se
|
|
|
272
278
|
}
|
|
273
279
|
const settings = await devApi.getSettings(baseUrl, issueResponse.certificate, keyPem, serverCaPem);
|
|
274
280
|
await config.mergeRemoteSettings(settings);
|
|
275
|
-
logger.log(chalk.green('
|
|
281
|
+
logger.log(chalk.green(' ') + formatSuccessLine('Config updated from server'));
|
|
276
282
|
} catch (err) {
|
|
277
283
|
const msg = err.status === 400 ? getBadRequestHint() : (err.message || String(err));
|
|
278
284
|
logger.log(chalk.yellow(' ⚠ Could not fetch settings (server may not support cert yet): ' + msg));
|