@aifabrix/builder 2.44.5 → 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 +48 -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/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.js +20 -2
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +134 -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 +78 -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 +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 +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/up-common.js +79 -19
- 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 +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/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/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 +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-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 +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 +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 +23 -12
- 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 +1 -1
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- 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,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Dimensions API type definitions (Controller)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Dimension create/update payload (Controller catalog)
|
|
9
|
+
* @typedef {Object} DimensionCreateRequest
|
|
10
|
+
* @property {string} key
|
|
11
|
+
* @property {string} displayName
|
|
12
|
+
* @property {string} [description]
|
|
13
|
+
* @property {'string'|'number'|'boolean'} dataType
|
|
14
|
+
* @property {boolean} [isRequired]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Dimension entity
|
|
19
|
+
* @typedef {Object} Dimension
|
|
20
|
+
* @property {string} id
|
|
21
|
+
* @property {string} key
|
|
22
|
+
* @property {string} displayName
|
|
23
|
+
* @property {string|null} [description]
|
|
24
|
+
* @property {'string'|'number'|'boolean'} dataType
|
|
25
|
+
* @property {boolean} isRequired
|
|
26
|
+
* @property {string|null} [createdBy]
|
|
27
|
+
* @property {string|null} [updatedBy]
|
|
28
|
+
* @property {string} [createdAt]
|
|
29
|
+
* @property {string} [updatedAt]
|
|
30
|
+
*/
|
|
31
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Integration clients API type definitions (Controller camelCase)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Integration client create request body (builder → controller)
|
|
9
|
+
* @typedef {Object} IntegrationClientCreateRequest
|
|
10
|
+
* @property {string} key - Stable key (lowercase alphanumeric + hyphens)
|
|
11
|
+
* @property {string} displayName - Human-readable name
|
|
12
|
+
* @property {string[]} redirectUris - Allowed redirect URIs for OAuth2 (min 1)
|
|
13
|
+
* @property {string[]} [groupNames] - Optional group names (RBAC); omit or empty for OAuth-only clients
|
|
14
|
+
* @property {string} [description] - Optional description
|
|
15
|
+
* @property {string} [keycloakClientId] - Optional fixed Keycloak client id
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create response (clientSecret is one-time-only)
|
|
20
|
+
* @typedef {Object} IntegrationClientCreateResponseData
|
|
21
|
+
* @property {Object} [integrationClient] - Created record
|
|
22
|
+
* @property {string} [integrationClient.id] - Integration client id
|
|
23
|
+
* @property {string} [integrationClient.key] - Key
|
|
24
|
+
* @property {string} [integrationClient.displayName] - Display name
|
|
25
|
+
* @property {string} [integrationClient.keycloakClientId] - OAuth client id in Keycloak
|
|
26
|
+
* @property {string} [integrationClient.status] - Status
|
|
27
|
+
* @property {string} [clientSecret] - One-time secret
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Single integration client in list response
|
|
32
|
+
* @typedef {Object} ListIntegrationClientItem
|
|
33
|
+
* @property {string} id - Integration client id
|
|
34
|
+
* @property {string} [key] - Key
|
|
35
|
+
* @property {string} [displayName] - Display name
|
|
36
|
+
* @property {string} [keycloakClientId] - OAuth client id
|
|
37
|
+
* @property {string} [status] - Status (e.g. active)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Regenerate secret response
|
|
42
|
+
* @typedef {Object} RegenerateIntegrationClientSecretResponse
|
|
43
|
+
* @property {Object} [data] - Nested data
|
|
44
|
+
* @property {string} [data.clientSecret] - New secret (one-time)
|
|
45
|
+
*/
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
const { extractTestRunId } = require('./validation-run.api');
|
|
10
10
|
const { postValidationRunWithTransportRetry } = require('../utils/validation-run-post-retry');
|
|
11
11
|
const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll');
|
|
12
|
+
const { createValidationPollHandlers } = require('../utils/validation-poll-ui');
|
|
13
|
+
const logger = require('../utils/logger');
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* POST /api/v1/validation/run and (when async) poll GET until reportCompleteness is full.
|
|
@@ -20,6 +22,8 @@ const { pollValidationRunUntilComplete } = require('../utils/validation-run-poll
|
|
|
20
22
|
* @param {number} opts.timeoutMs
|
|
21
23
|
* @param {boolean} opts.useAsync
|
|
22
24
|
* @param {boolean} opts.noAsync
|
|
25
|
+
* @param {boolean} [opts.verbosePoll] - Throttled poll lines when no TTY poll UI (see validation-run-poll)
|
|
26
|
+
* @param {Function} [opts.onPollProgress] - Extra hook: `(envelope, attemptIndex, meta)` during poll
|
|
23
27
|
* @returns {Promise<{ envelope: Object|null, apiError: Object|null, pollTimedOut: boolean, incompleteNoAsync: boolean }>}
|
|
24
28
|
*/
|
|
25
29
|
/* eslint-disable max-lines-per-function, max-statements, complexity -- POST + poll orchestration */
|
|
@@ -54,31 +58,48 @@ async function postValidationRunAndOptionalPoll(opts) {
|
|
|
54
58
|
if (needsPoll && testRunId) {
|
|
55
59
|
const elapsed = Date.now() - started;
|
|
56
60
|
const remaining = Math.max(0, timeoutMs - elapsed);
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
61
|
+
const deadlineMs = Date.now() + remaining;
|
|
62
|
+
|
|
63
|
+
logger.log('');
|
|
64
|
+
const pollUi = createValidationPollHandlers(deadlineMs);
|
|
65
|
+
const mergeOnPollProgress =
|
|
66
|
+
typeof opts.onPollProgress === 'function'
|
|
67
|
+
? (envelope, attemptIndex, meta) => {
|
|
68
|
+
pollUi.onPollProgress(envelope, attemptIndex, meta);
|
|
69
|
+
opts.onPollProgress(envelope, attemptIndex, meta);
|
|
70
|
+
}
|
|
71
|
+
: pollUi.onPollProgress;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const pollResult = await pollValidationRunUntilComplete({
|
|
75
|
+
dataplaneUrl,
|
|
76
|
+
authConfig,
|
|
77
|
+
testRunId,
|
|
78
|
+
budgetMs: remaining,
|
|
79
|
+
verbosePoll: verbosePoll === true,
|
|
80
|
+
pollRequestTimeoutMs:
|
|
81
|
+
Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : undefined,
|
|
82
|
+
onPollProgress: mergeOnPollProgress
|
|
83
|
+
});
|
|
84
|
+
if (!pollResult.lastApiResult || !pollResult.lastApiResult.success) {
|
|
85
|
+
return {
|
|
86
|
+
envelope: pollResult.envelope,
|
|
87
|
+
apiError: pollResult.lastApiResult,
|
|
88
|
+
pollTimedOut: pollResult.timedOut,
|
|
89
|
+
incompleteNoAsync: false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
envelope = pollResult.envelope;
|
|
93
|
+
if (pollResult.timedOut) {
|
|
94
|
+
return {
|
|
95
|
+
envelope,
|
|
96
|
+
apiError: null,
|
|
97
|
+
pollTimedOut: true,
|
|
98
|
+
incompleteNoAsync: false
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
} finally {
|
|
102
|
+
pollUi.finish();
|
|
82
103
|
}
|
|
83
104
|
}
|
|
84
105
|
|
package/lib/app/deploy-config.js
CHANGED
|
@@ -62,11 +62,21 @@ async function extractDeploymentConfig(options, _variables) {
|
|
|
62
62
|
const controllerUrl = await resolveControllerUrl();
|
|
63
63
|
const envKey = await resolveEnvironment();
|
|
64
64
|
|
|
65
|
+
const pollIntervalExplicit =
|
|
66
|
+
options.pollInterval !== undefined && options.pollInterval !== null
|
|
67
|
+
? Number(options.pollInterval)
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
65
70
|
return {
|
|
66
71
|
controllerUrl,
|
|
67
72
|
envKey,
|
|
68
73
|
poll: options.poll !== false,
|
|
69
|
-
|
|
74
|
+
// When true, polling still happens but is not rendered (no nested ora spinner).
|
|
75
|
+
// Used by guided installers which already show a top-level spinner.
|
|
76
|
+
silentPoll: options.silentPoll === true,
|
|
77
|
+
repositoryUrl: options.repositoryUrl,
|
|
78
|
+
// Omit default here so deployer can resolve 2s vs 5s from controller deploymentType (GET /api/v1/health)
|
|
79
|
+
pollInterval: Number.isFinite(pollIntervalExplicit) ? pollIntervalExplicit : undefined,
|
|
70
80
|
pollMaxAttempts: options.pollMaxAttempts || 60
|
|
71
81
|
};
|
|
72
82
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* @version 2.0.0
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const chalk = require('chalk');
|
|
9
8
|
const logger = require('../utils/logger');
|
|
9
|
+
const { formatSuccessLine, metadata } = require('../utils/cli-test-layout-chalk');
|
|
10
10
|
const { getApplicationStatus } = require('../api/applications.api');
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -69,9 +69,9 @@ async function displayAppUrlFromController(controllerUrl, envKey, appKey, authCo
|
|
|
69
69
|
url = buildAppUrlFromControllerAndPort(controllerUrl, port);
|
|
70
70
|
}
|
|
71
71
|
if (url) {
|
|
72
|
-
logger.log(
|
|
72
|
+
logger.log(formatSuccessLine(`App running at ${url}`));
|
|
73
73
|
} else {
|
|
74
|
-
logger.log(
|
|
74
|
+
logger.log(metadata('App deployed. Resolve URL from the controller dashboard.'));
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
package/lib/app/deploy.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatSuccessParagraph,
|
|
4
|
+
sectionTitle,
|
|
5
|
+
headerKeyValue,
|
|
6
|
+
metadata
|
|
7
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
8
|
/**
|
|
3
9
|
* AI Fabrix Builder Application Deployment Module
|
|
4
10
|
*
|
|
@@ -13,6 +19,8 @@ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test
|
|
|
13
19
|
const fs = require('fs').promises;
|
|
14
20
|
const chalk = require('chalk');
|
|
15
21
|
const pushUtils = require('../deployment/push');
|
|
22
|
+
|
|
23
|
+
const SEP = chalk.gray('────────────────────────────────────────');
|
|
16
24
|
const logger = require('../utils/logger');
|
|
17
25
|
const { detectAppType, getBuilderPath, getIntegrationPath } = require('../utils/paths');
|
|
18
26
|
const { checkApplicationExists } = require('../utils/app-existence');
|
|
@@ -156,7 +164,10 @@ async function pushApp(appName, options = {}) {
|
|
|
156
164
|
* @throws {Error} If generation or validation fails
|
|
157
165
|
*/
|
|
158
166
|
async function generateAndValidateManifest(appName, options = {}) {
|
|
159
|
-
logger.log(
|
|
167
|
+
logger.log('');
|
|
168
|
+
logger.log(sectionTitle('Deployment manifest'));
|
|
169
|
+
logger.log(SEP);
|
|
170
|
+
logger.log(metadata(`Generating manifest for ${appName}...`));
|
|
160
171
|
const generator = require('../generator');
|
|
161
172
|
|
|
162
173
|
// generateDeployJson already validates against schema and throws on error
|
|
@@ -175,10 +186,10 @@ async function generateAndValidateManifest(appName, options = {}) {
|
|
|
175
186
|
*/
|
|
176
187
|
function displayDeploymentInfo(manifest, manifestPath) {
|
|
177
188
|
logger.log(formatSuccessLine(`Manifest generated: ${manifestPath}`));
|
|
178
|
-
logger.log(
|
|
179
|
-
logger.log(
|
|
180
|
-
logger.log(
|
|
181
|
-
logger.log(
|
|
189
|
+
logger.log(headerKeyValue('Key:', manifest.key));
|
|
190
|
+
logger.log(headerKeyValue('Display Name:', manifest.displayName));
|
|
191
|
+
logger.log(headerKeyValue('Image:', manifest.image));
|
|
192
|
+
logger.log(headerKeyValue('Port:', String(manifest.port)));
|
|
182
193
|
}
|
|
183
194
|
|
|
184
195
|
/**
|
|
@@ -189,7 +200,11 @@ function displayDeploymentInfo(manifest, manifestPath) {
|
|
|
189
200
|
* @returns {Promise<Object>} Deployment result
|
|
190
201
|
*/
|
|
191
202
|
async function executeDeployment(manifest, deploymentConfig) {
|
|
192
|
-
logger.log(
|
|
203
|
+
logger.log('');
|
|
204
|
+
logger.log(sectionTitle('Deploy to controller'));
|
|
205
|
+
logger.log(SEP);
|
|
206
|
+
logger.log(headerKeyValue('URL:', deploymentConfig.controllerUrl));
|
|
207
|
+
logger.log(headerKeyValue('Environment:', deploymentConfig.envKey));
|
|
193
208
|
const deployer = require('../deployment/deployer');
|
|
194
209
|
return await deployer.deployToController(
|
|
195
210
|
manifest,
|
|
@@ -199,7 +214,9 @@ async function executeDeployment(manifest, deploymentConfig) {
|
|
|
199
214
|
{
|
|
200
215
|
poll: deploymentConfig.poll,
|
|
201
216
|
pollInterval: deploymentConfig.pollInterval,
|
|
202
|
-
pollMaxAttempts: deploymentConfig.pollMaxAttempts
|
|
217
|
+
pollMaxAttempts: deploymentConfig.pollMaxAttempts,
|
|
218
|
+
repositoryUrl: deploymentConfig.repositoryUrl,
|
|
219
|
+
silentPoll: deploymentConfig.silentPoll === true
|
|
203
220
|
}
|
|
204
221
|
);
|
|
205
222
|
}
|
|
@@ -209,17 +226,22 @@ async function executeDeployment(manifest, deploymentConfig) {
|
|
|
209
226
|
* @param {Object} result - Deployment result
|
|
210
227
|
*/
|
|
211
228
|
function displayDeploymentResults(result) {
|
|
212
|
-
logger.log(
|
|
229
|
+
logger.log(formatSuccessParagraph('Deployment initiated successfully'));
|
|
213
230
|
if (result.deploymentUrl) {
|
|
214
|
-
logger.log(
|
|
231
|
+
logger.log(headerKeyValue('URL:', result.deploymentUrl));
|
|
215
232
|
}
|
|
216
233
|
if (result.deploymentId) {
|
|
217
|
-
logger.log(
|
|
234
|
+
logger.log(headerKeyValue('Deployment ID:', result.deploymentId));
|
|
218
235
|
}
|
|
219
236
|
if (result.status) {
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
237
|
+
const st = result.status.status;
|
|
238
|
+
const statusText =
|
|
239
|
+
st === 'completed'
|
|
240
|
+
? formatSuccessLine(st)
|
|
241
|
+
: st === 'failed' || st === 'cancelled'
|
|
242
|
+
? chalk.red(`✖ ${st}`)
|
|
243
|
+
: chalk.yellow(`⏳ ${st}`);
|
|
244
|
+
logger.log(`${chalk.gray('Status:')} ${statusText}`);
|
|
223
245
|
}
|
|
224
246
|
}
|
|
225
247
|
|
package/lib/app/display.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
|
+
const { formatNextActions, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
2
2
|
/**
|
|
3
3
|
* Application Display Utilities
|
|
4
4
|
*
|
|
@@ -24,11 +24,13 @@ const logger = require('../utils/logger');
|
|
|
24
24
|
function displayExternalSystemSuccess(appName, config, location) {
|
|
25
25
|
logger.log(chalk.blue('Type: External System'));
|
|
26
26
|
logger.log(chalk.blue(`System Key: ${config.systemKey || appName}`));
|
|
27
|
-
logger.log(
|
|
28
|
-
logger.log(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
logger.log('');
|
|
28
|
+
logger.log(formatNextActions([
|
|
29
|
+
`Edit external system JSON files in ${location}`,
|
|
30
|
+
`Run: aifabrix validate ${appName}`,
|
|
31
|
+
'Run: aifabrix login',
|
|
32
|
+
`Run: aifabrix deploy ${appName}`
|
|
33
|
+
]));
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -49,11 +51,13 @@ function displayWebappSuccess(appName, config, envConversionMessage) {
|
|
|
49
51
|
|
|
50
52
|
logger.log(chalk.gray(envConversionMessage));
|
|
51
53
|
|
|
52
|
-
logger.log(
|
|
53
|
-
logger.log(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
logger.log('');
|
|
55
|
+
logger.log(formatNextActions([
|
|
56
|
+
'Copy env.template to .env and fill in your values',
|
|
57
|
+
'Run: aifabrix up-infra',
|
|
58
|
+
`Run: aifabrix build ${appName}`,
|
|
59
|
+
`Run: aifabrix run ${appName}`
|
|
60
|
+
]));
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
/**
|
package/lib/app/push.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
formatSuccessLine,
|
|
3
|
+
formatSuccessParagraph,
|
|
4
|
+
sectionTitle,
|
|
5
|
+
headerKeyValue,
|
|
6
|
+
metadata,
|
|
7
|
+
formatWarningLine,
|
|
8
|
+
formatNextActions
|
|
9
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
10
|
/**
|
|
3
11
|
* Application Push Utilities
|
|
4
12
|
*
|
|
@@ -9,9 +17,9 @@ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test
|
|
|
9
17
|
* @version 2.0.0
|
|
10
18
|
*/
|
|
11
19
|
|
|
12
|
-
const chalk = require('chalk');
|
|
13
20
|
const pushUtils = require('../deployment/push');
|
|
14
21
|
const logger = require('../utils/logger');
|
|
22
|
+
const { detectAppType } = require('../utils/paths');
|
|
15
23
|
|
|
16
24
|
/**
|
|
17
25
|
* Validate application name format
|
|
@@ -150,6 +158,33 @@ async function authenticateWithRegistry(registry) {
|
|
|
150
158
|
}
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Layout-aligned notice when push is skipped for external integrations.
|
|
163
|
+
* @param {string} appName
|
|
164
|
+
*/
|
|
165
|
+
function logExternalIntegrationPushNotice(appName) {
|
|
166
|
+
logger.log('');
|
|
167
|
+
logger.log(sectionTitle('Push'));
|
|
168
|
+
logger.log(headerKeyValue('Application:', appName));
|
|
169
|
+
logger.log(formatWarningLine('External integrations have no application image to push.'));
|
|
170
|
+
logger.log(metadata('Use upload or deploy for integration artifacts.'));
|
|
171
|
+
logger.log(formatNextActions([`aifabrix upload ${appName}`, `aifabrix deploy ${appName}`]));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Header after validation passes for a regular (non-external) push.
|
|
176
|
+
* @param {string} appName
|
|
177
|
+
* @param {string} registry
|
|
178
|
+
* @param {string} imageName
|
|
179
|
+
*/
|
|
180
|
+
function logPushCommandHeader(appName, registry, imageName) {
|
|
181
|
+
logger.log('');
|
|
182
|
+
logger.log(sectionTitle('Push'));
|
|
183
|
+
logger.log(headerKeyValue('Application:', appName));
|
|
184
|
+
logger.log(metadata(`Registry: ${registry}`));
|
|
185
|
+
logger.log(metadata(`Repository: ${imageName}`));
|
|
186
|
+
}
|
|
187
|
+
|
|
153
188
|
/**
|
|
154
189
|
* Pushes image tags to registry
|
|
155
190
|
* @async
|
|
@@ -171,7 +206,9 @@ async function pushImageTags(imageName, registry, tags) {
|
|
|
171
206
|
(errorMessage.includes('authentication') && errorMessage.includes('401'));
|
|
172
207
|
|
|
173
208
|
if (isAuthError) {
|
|
174
|
-
logger.log(
|
|
209
|
+
logger.log(
|
|
210
|
+
formatWarningLine('Registry authentication expired; re-authenticating, then retrying push.')
|
|
211
|
+
);
|
|
175
212
|
await authenticateWithRegistry(registry);
|
|
176
213
|
// Retry push after re-authentication
|
|
177
214
|
await Promise.all(tags.map(async(tag) => {
|
|
@@ -190,9 +227,9 @@ async function pushImageTags(imageName, registry, tags) {
|
|
|
190
227
|
* @param {Array<string>} tags - Image tags
|
|
191
228
|
*/
|
|
192
229
|
function displayPushResults(registry, imageName, tags) {
|
|
193
|
-
logger.log(formatSuccessParagraph(`
|
|
194
|
-
logger.log(
|
|
195
|
-
logger.log(
|
|
230
|
+
logger.log(formatSuccessParagraph(`Pushed ${tags.length} tag(s) to ${registry}`));
|
|
231
|
+
logger.log(headerKeyValue('Image:', `${registry}/${imageName}`));
|
|
232
|
+
logger.log(headerKeyValue('Tags:', tags.join(', ')));
|
|
196
233
|
}
|
|
197
234
|
|
|
198
235
|
/**
|
|
@@ -204,38 +241,25 @@ function displayPushResults(registry, imageName, tags) {
|
|
|
204
241
|
* @returns {Promise<void>} Resolves when push is complete
|
|
205
242
|
*/
|
|
206
243
|
async function pushApp(appName, options = {}) {
|
|
207
|
-
// Check if app type is external - skip push
|
|
208
|
-
const { detectAppType } = require('../utils/paths');
|
|
209
244
|
try {
|
|
210
245
|
const { isExternal } = await detectAppType(appName);
|
|
211
246
|
if (isExternal) {
|
|
212
|
-
|
|
247
|
+
logExternalIntegrationPushNotice(appName);
|
|
213
248
|
return;
|
|
214
249
|
}
|
|
215
|
-
} catch (
|
|
250
|
+
} catch (_error) {
|
|
216
251
|
// If detection fails, continue with normal push
|
|
217
252
|
// (detectAppType throws if app doesn't exist, which is fine for push command)
|
|
218
253
|
}
|
|
219
254
|
try {
|
|
220
|
-
// Validate app name
|
|
221
255
|
validateAppName(appName);
|
|
222
|
-
|
|
223
|
-
// Load configuration
|
|
224
256
|
const { registry, imageName } = await loadPushConfig(appName, options);
|
|
225
|
-
|
|
226
|
-
// Validate push configuration
|
|
227
257
|
await validatePushConfig(registry, imageName, appName);
|
|
228
|
-
|
|
229
|
-
// Authenticate with registry
|
|
258
|
+
logPushCommandHeader(appName, registry, imageName);
|
|
230
259
|
await authenticateWithRegistry(registry);
|
|
231
|
-
|
|
232
|
-
// Push image tags
|
|
233
260
|
const tags = options.tag ? options.tag.split(',').map(t => t.trim()) : ['latest'];
|
|
234
261
|
await pushImageTags(imageName, registry, tags);
|
|
235
|
-
|
|
236
|
-
// Display results
|
|
237
262
|
displayPushResults(registry, imageName, tags);
|
|
238
|
-
|
|
239
263
|
} catch (error) {
|
|
240
264
|
throw new Error(`Failed to push application: ${error.message}`);
|
|
241
265
|
}
|
|
@@ -245,4 +269,3 @@ module.exports = {
|
|
|
245
269
|
pushApp,
|
|
246
270
|
validateAppName
|
|
247
271
|
};
|
|
248
|
-
|
package/lib/app/register.js
CHANGED
|
@@ -99,7 +99,7 @@ async function saveLocalCredentials(responseData, apiUrl) {
|
|
|
99
99
|
|
|
100
100
|
// Regenerate .env file with updated credentials
|
|
101
101
|
try {
|
|
102
|
-
await generateEnvFile(registeredAppKey, null, 'local');
|
|
102
|
+
await generateEnvFile(registeredAppKey, null, 'local', true);
|
|
103
103
|
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
104
104
|
} catch (error) {
|
|
105
105
|
logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* After `docker restart`, describe dev workspace mounts for developer clarity.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Bind mount vs remote-engine hints (aligns with run --reload messaging)
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const config = require('../core/config');
|
|
13
|
+
const { execWithDockerEnv } = require('../utils/docker-exec');
|
|
14
|
+
const { sectionTitle, headerKeyValue, metadata } = require('../utils/cli-test-layout-chalk');
|
|
15
|
+
const { isReloadBindMountOnEngineHost } = require('../utils/docker-reload-mount');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {unknown} mounts - docker inspect .Mounts
|
|
19
|
+
* @returns {{ Type: string, Source: string, Destination: string }|null}
|
|
20
|
+
*/
|
|
21
|
+
function findAppBindMount(mounts) {
|
|
22
|
+
if (!Array.isArray(mounts)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const hit = mounts.find((m) => m && m.Type === 'bind' && m.Destination === '/app');
|
|
26
|
+
return hit && typeof hit.Source === 'string' ? hit : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} stdout
|
|
31
|
+
* @returns {unknown|null}
|
|
32
|
+
*/
|
|
33
|
+
function parseInspectMountsStdout(stdout) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(String(stdout || '').trim());
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {string} containerName
|
|
43
|
+
* @returns {Promise<unknown|null>}
|
|
44
|
+
*/
|
|
45
|
+
async function fetchContainerMountsJson(containerName) {
|
|
46
|
+
try {
|
|
47
|
+
const { stdout } = await execWithDockerEnv(
|
|
48
|
+
`docker inspect --format '{{json .Mounts}}' ${containerName}`,
|
|
49
|
+
{ maxBuffer: 2 * 1024 * 1024 }
|
|
50
|
+
);
|
|
51
|
+
return parseInspectMountsStdout(stdout);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Log workspace transport after a successful container restart (mounts unchanged).
|
|
59
|
+
* @param {string} containerName - Docker container name
|
|
60
|
+
* @returns {Promise<void>}
|
|
61
|
+
*/
|
|
62
|
+
async function logRestartDevMountSummary(containerName) {
|
|
63
|
+
if (!containerName || typeof containerName !== 'string') {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const mounts = await fetchContainerMountsJson(containerName);
|
|
67
|
+
const appBind = findAppBindMount(mounts);
|
|
68
|
+
if (!appBind) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const endpoint = await config.getDockerEndpoint();
|
|
72
|
+
const localEngine = isReloadBindMountOnEngineHost(endpoint);
|
|
73
|
+
|
|
74
|
+
logger.log('');
|
|
75
|
+
logger.log(sectionTitle('Dev workspace (unchanged by restart)'));
|
|
76
|
+
if (localEngine) {
|
|
77
|
+
logger.log(headerKeyValue('Transport:', 'Direct bind mount on the Docker host (no Mutagen).'));
|
|
78
|
+
logger.log(headerKeyValue('Host path → container:', `${appBind.Source} → /app`));
|
|
79
|
+
logger.log(metadata('Edits under the host path are visible inside the container immediately.'));
|
|
80
|
+
} else {
|
|
81
|
+
logger.log(
|
|
82
|
+
headerKeyValue(
|
|
83
|
+
'Transport:',
|
|
84
|
+
'Bind mount on the Docker engine (see path below; Mutagen may sync to this path when using --reload).'
|
|
85
|
+
)
|
|
86
|
+
);
|
|
87
|
+
logger.log(headerKeyValue('Engine path → container:', `${appBind.Source} → /app`));
|
|
88
|
+
}
|
|
89
|
+
logger.log('');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
findAppBindMount,
|
|
94
|
+
logRestartDevMountSummary
|
|
95
|
+
};
|
package/lib/app/rotate-secret.js
CHANGED
|
@@ -287,7 +287,7 @@ async function saveCredentialsLocally(appKey, credentials, actualControllerUrl)
|
|
|
287
287
|
|
|
288
288
|
// Regenerate .env file with updated credentials
|
|
289
289
|
try {
|
|
290
|
-
await generateEnvFile(appKey, null, 'local');
|
|
290
|
+
await generateEnvFile(appKey, null, 'local', true);
|
|
291
291
|
logger.log(formatSuccessLine('.env file updated with new credentials'));
|
|
292
292
|
} catch (error) {
|
|
293
293
|
logger.warn(chalk.yellow(`⚠ Could not regenerate .env file: ${error.message}`));
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
formatSuccessLine,
|
|
10
|
+
formatProgress,
|
|
11
|
+
formatWarningLine
|
|
12
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
9
13
|
|
|
10
14
|
const fs = require('fs').promises;
|
|
11
15
|
const chalk = require('chalk');
|
|
@@ -31,8 +35,8 @@ const execAsync = promisify(exec);
|
|
|
31
35
|
async function emitAndRunDockerFallback(appName, appConfig, port, opts) {
|
|
32
36
|
const { debug, runEnvPath, runOptions, misoEnvironment } = opts;
|
|
33
37
|
logger.log(
|
|
34
|
-
|
|
35
|
-
'Docker Compose not found; using docker run (apps without
|
|
38
|
+
formatWarningLine(
|
|
39
|
+
'Docker Compose not found; using docker run (apps without Postgres/Redis only). ' +
|
|
36
40
|
'Install docker-compose-plugin for full compose support.'
|
|
37
41
|
)
|
|
38
42
|
);
|
|
@@ -116,7 +120,7 @@ async function waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, o) {
|
|
|
116
120
|
const displayUrl = (healthUrl && typeof healthUrl === 'string')
|
|
117
121
|
? healthUrl
|
|
118
122
|
: `http://localhost:${port}${healthCheckPath}`;
|
|
119
|
-
logger.log(
|
|
123
|
+
logger.log(formatProgress(`Waiting for healthy: ${displayUrl}…`));
|
|
120
124
|
await healthCheck.waitForHealthCheck(appName, 90, port, appConfig, debug, ro);
|
|
121
125
|
|
|
122
126
|
for (const p of [runEnvPath, runEnvAdminPath]) {
|
|
@@ -124,7 +128,9 @@ async function waitForHealthyAndCleanupEnvFiles(appName, port, appConfig, o) {
|
|
|
124
128
|
try {
|
|
125
129
|
await fs.unlink(p);
|
|
126
130
|
} catch (err) {
|
|
127
|
-
if (err.code !== 'ENOENT')
|
|
131
|
+
if (err.code !== 'ENOENT') {
|
|
132
|
+
logger.log(formatWarningLine(`Could not remove run .env: ${err.message}`));
|
|
133
|
+
}
|
|
128
134
|
}
|
|
129
135
|
}
|
|
130
136
|
}
|
|
@@ -148,7 +154,7 @@ async function startContainer(appName, composePath, port, appConfig = null, opts
|
|
|
148
154
|
devMountPath = null,
|
|
149
155
|
misoEnvironment = 'dev'
|
|
150
156
|
} = opts;
|
|
151
|
-
logger.log(
|
|
157
|
+
logger.log(formatProgress(`Starting ${appName}…`));
|
|
152
158
|
|
|
153
159
|
let composeCmdBase;
|
|
154
160
|
let usedDockerRunFallback = false;
|