@nocobase/cli 2.1.0-beta.44.test.1 → 2.1.0-beta.44.test.3
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/dist/lib/api-client.js +335 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +139 -0
- package/dist/lib/app-managed-resources.js +321 -0
- package/dist/lib/app-public-path.js +80 -0
- package/dist/lib/app-runtime.js +189 -0
- package/dist/lib/auth-store.js +498 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +409 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +398 -0
- package/dist/lib/cli-entry-error.js +44 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +141 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/db-connection-check.js +219 -0
- package/dist/lib/docker-env-file.js +60 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +960 -0
- package/dist/lib/env-command-config.js +45 -0
- package/dist/lib/env-config.js +100 -0
- package/dist/lib/env-guard.js +61 -0
- package/dist/lib/env-paths.js +101 -0
- package/dist/lib/env-proxy.js +1295 -0
- package/dist/lib/generated-command.js +203 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +243 -0
- package/dist/lib/managed-env-file.js +98 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/plugin-import.js +279 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/dist/lib/prompt-catalog.js +10 -0
- package/dist/lib/prompt-validators.js +258 -0
- package/dist/lib/prompt-web-ui.js +2227 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +393 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +498 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +301 -0
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/dist/lib/skills-manager.js +438 -0
- package/dist/lib/source-publish.js +326 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +309 -0
- package/dist/lib/ui.js +159 -0
- package/package.json +7 -1
- package/scripts/build.mjs +0 -34
- package/scripts/clean.mjs +0 -9
- package/tsconfig.json +0 -19
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { translateCli } from "./cli-locale.js";
|
|
10
|
+
import { validateTcpPort } from "./prompt-validators.js";
|
|
11
|
+
const DB_CONNECTION_TIMEOUT_MS = 5_000;
|
|
12
|
+
const externalDbValidationCache = new Map();
|
|
13
|
+
const mysqlLowerCaseTableNamesCache = new Map();
|
|
14
|
+
function trimPromptValue(value) {
|
|
15
|
+
return String(value ?? '').trim();
|
|
16
|
+
}
|
|
17
|
+
export function readExternalDbConnectionConfig(values) {
|
|
18
|
+
const builtinDb = values.builtinDb === undefined ? true : Boolean(values.builtinDb);
|
|
19
|
+
if (builtinDb) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const dialect = trimPromptValue(values.dbDialect || 'postgres');
|
|
23
|
+
if (dialect !== 'postgres' && dialect !== 'kingbase' && dialect !== 'mysql' && dialect !== 'mariadb') {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const host = trimPromptValue(values.dbHost);
|
|
27
|
+
const portText = trimPromptValue(values.dbPort);
|
|
28
|
+
const database = trimPromptValue(values.dbDatabase);
|
|
29
|
+
const user = trimPromptValue(values.dbUser);
|
|
30
|
+
const password = String(values.dbPassword ?? '');
|
|
31
|
+
if (!host || !portText || !database || !user || !password) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (validateTcpPort(portText)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
dialect,
|
|
39
|
+
host,
|
|
40
|
+
port: Number.parseInt(portText, 10),
|
|
41
|
+
database,
|
|
42
|
+
user,
|
|
43
|
+
password,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function formatDbCheckAddress(config) {
|
|
47
|
+
return `${config.host}:${config.port}/${config.database}`;
|
|
48
|
+
}
|
|
49
|
+
function buildValidationCacheKey(config) {
|
|
50
|
+
return JSON.stringify(config);
|
|
51
|
+
}
|
|
52
|
+
function formatDbConnectionError(config, error) {
|
|
53
|
+
const maybeError = error;
|
|
54
|
+
const code = String(maybeError?.code ?? '').trim().toUpperCase();
|
|
55
|
+
const errno = typeof maybeError?.errno === 'number' ? maybeError.errno : undefined;
|
|
56
|
+
const rawMessage = String(maybeError?.message || maybeError?.sqlMessage || error || '').trim();
|
|
57
|
+
if (code === 'ECONNREFUSED' || code === 'ENOTFOUND' || code === 'EHOSTUNREACH' || code === 'ECONNRESET') {
|
|
58
|
+
return translateCli('validators.dbConnection.unreachable', {
|
|
59
|
+
host: config.host,
|
|
60
|
+
port: config.port,
|
|
61
|
+
details: rawMessage,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (code === 'ETIMEDOUT') {
|
|
65
|
+
return translateCli('validators.dbConnection.timeout', {
|
|
66
|
+
host: config.host,
|
|
67
|
+
port: config.port,
|
|
68
|
+
seconds: Math.ceil(DB_CONNECTION_TIMEOUT_MS / 1000),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (code === '28P01' || code === '28000' || code === 'ER_ACCESS_DENIED_ERROR' || errno === 1045) {
|
|
72
|
+
return translateCli('validators.dbConnection.authenticationFailed', {
|
|
73
|
+
user: config.user,
|
|
74
|
+
database: config.database,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (code === '3D000' || code === 'ER_BAD_DB_ERROR' || errno === 1049) {
|
|
78
|
+
return translateCli('validators.dbConnection.databaseNotFound', {
|
|
79
|
+
database: config.database,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return translateCli('validators.dbConnection.connectionFailed', {
|
|
83
|
+
details: rawMessage || code || String(error),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async function checkPostgresFamilyConnection(config) {
|
|
87
|
+
const { default: pg } = await import('pg');
|
|
88
|
+
const client = new pg.Client({
|
|
89
|
+
host: config.host,
|
|
90
|
+
port: config.port,
|
|
91
|
+
user: config.user,
|
|
92
|
+
password: config.password,
|
|
93
|
+
database: config.database,
|
|
94
|
+
connectionTimeoutMillis: DB_CONNECTION_TIMEOUT_MS,
|
|
95
|
+
});
|
|
96
|
+
try {
|
|
97
|
+
await client.connect();
|
|
98
|
+
await client.query('SELECT 1');
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
await Promise.resolve(client.end()).catch(() => undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function checkMysqlFamilyConnection(config) {
|
|
105
|
+
const { default: mysql } = await import('mysql2/promise');
|
|
106
|
+
const connection = await mysql.createConnection({
|
|
107
|
+
host: config.host,
|
|
108
|
+
port: config.port,
|
|
109
|
+
user: config.user,
|
|
110
|
+
password: config.password,
|
|
111
|
+
database: config.database,
|
|
112
|
+
connectTimeout: DB_CONNECTION_TIMEOUT_MS,
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
await connection.query('SELECT 1');
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await Promise.resolve(connection.end()).catch(() => undefined);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function readMysqlFamilyLowerCaseTableNames(config) {
|
|
122
|
+
const { default: mysql } = await import('mysql2/promise');
|
|
123
|
+
const connection = await mysql.createConnection({
|
|
124
|
+
host: config.host,
|
|
125
|
+
port: config.port,
|
|
126
|
+
user: config.user,
|
|
127
|
+
password: config.password,
|
|
128
|
+
database: config.database,
|
|
129
|
+
connectTimeout: DB_CONNECTION_TIMEOUT_MS,
|
|
130
|
+
});
|
|
131
|
+
try {
|
|
132
|
+
const [rows] = await connection.query(`SHOW VARIABLES LIKE 'lower_case_table_names'`);
|
|
133
|
+
if (!Array.isArray(rows)) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
for (const row of rows) {
|
|
137
|
+
if (!row || typeof row !== 'object') {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const value = String(row.Value ?? '').trim();
|
|
141
|
+
if (value === '0' || value === '1' || value === '2') {
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
await Promise.resolve(connection.end()).catch(() => undefined);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function performExternalDbConnectionCheck(config) {
|
|
152
|
+
try {
|
|
153
|
+
switch (config.dialect) {
|
|
154
|
+
case 'postgres':
|
|
155
|
+
case 'kingbase': {
|
|
156
|
+
await checkPostgresFamilyConnection(config);
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
case 'mysql':
|
|
160
|
+
case 'mariadb': {
|
|
161
|
+
await checkMysqlFamilyConnection(config);
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return formatDbConnectionError(config, error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export async function checkExternalDbConnection(config) {
|
|
171
|
+
const cacheKey = buildValidationCacheKey(config);
|
|
172
|
+
const cached = externalDbValidationCache.get(cacheKey);
|
|
173
|
+
if (cached) {
|
|
174
|
+
return await cached;
|
|
175
|
+
}
|
|
176
|
+
const pending = performExternalDbConnectionCheck(config);
|
|
177
|
+
externalDbValidationCache.set(cacheKey, pending);
|
|
178
|
+
return await pending;
|
|
179
|
+
}
|
|
180
|
+
async function readMysqlLowerCaseTableNamesMode(config) {
|
|
181
|
+
if (config.dialect !== 'mysql' && config.dialect !== 'mariadb') {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const cacheKey = buildValidationCacheKey(config);
|
|
185
|
+
const cached = mysqlLowerCaseTableNamesCache.get(cacheKey);
|
|
186
|
+
if (cached) {
|
|
187
|
+
return await cached;
|
|
188
|
+
}
|
|
189
|
+
const pending = readMysqlFamilyLowerCaseTableNames(config);
|
|
190
|
+
mysqlLowerCaseTableNamesCache.set(cacheKey, pending);
|
|
191
|
+
return await pending;
|
|
192
|
+
}
|
|
193
|
+
export async function validateExternalDbConfig(values) {
|
|
194
|
+
const config = readExternalDbConnectionConfig(values);
|
|
195
|
+
if (!config) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
return await checkExternalDbConnection(config);
|
|
199
|
+
}
|
|
200
|
+
export async function validateMysqlLowerCaseTableNamesCompatibility(values) {
|
|
201
|
+
const config = readExternalDbConnectionConfig(values);
|
|
202
|
+
if (!config || (config.dialect !== 'mysql' && config.dialect !== 'mariadb')) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const mode = await readMysqlLowerCaseTableNamesMode(config);
|
|
207
|
+
if (mode === '1' && values.dbUnderscored !== true) {
|
|
208
|
+
return translateCli('validators.dbConnection.lowerCaseTableNamesRequiresUnderscored');
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
return formatDbConnectionError(config, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export function clearExternalDbValidationCache() {
|
|
217
|
+
externalDbValidationCache.clear();
|
|
218
|
+
mysqlLowerCaseTableNamesCache.clear();
|
|
219
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { access } from 'node:fs/promises';
|
|
10
|
+
import { resolveConfiguredEnvPath } from './cli-home.js';
|
|
11
|
+
import { inferConfiguredAppPathFromLegacyConfig } from './env-paths.js';
|
|
12
|
+
function trimValue(value) {
|
|
13
|
+
const text = String(value ?? '').trim();
|
|
14
|
+
return text || undefined;
|
|
15
|
+
}
|
|
16
|
+
function appendDotEnvPath(basePath) {
|
|
17
|
+
return `${basePath.replace(/[\\/]+$/, '')}/.env`;
|
|
18
|
+
}
|
|
19
|
+
export function defaultDockerEnvFilePath(envName, config) {
|
|
20
|
+
const appPath = inferConfiguredAppPathFromLegacyConfig(config ?? {});
|
|
21
|
+
if (appPath) {
|
|
22
|
+
return appendDotEnvPath(appPath);
|
|
23
|
+
}
|
|
24
|
+
return `${envName}/.env`;
|
|
25
|
+
}
|
|
26
|
+
export function resolveConfiguredDockerEnvFilePath(envName, config) {
|
|
27
|
+
return trimValue(config?.envFile) || defaultDockerEnvFilePath(envName, config);
|
|
28
|
+
}
|
|
29
|
+
export function hasExplicitDockerEnvFile(config) {
|
|
30
|
+
return Boolean(trimValue(config?.envFile));
|
|
31
|
+
}
|
|
32
|
+
export function resolveDockerEnvFilePath(envName, config) {
|
|
33
|
+
return resolveConfiguredEnvPath(resolveConfiguredDockerEnvFilePath(envName, config));
|
|
34
|
+
}
|
|
35
|
+
export async function dockerEnvFileExists(envName, config) {
|
|
36
|
+
const filePath = resolveDockerEnvFilePath(envName, config);
|
|
37
|
+
if (!filePath) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await access(filePath);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch (_error) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function resolveDockerEnvFileArg(envName, config) {
|
|
49
|
+
const filePath = resolveDockerEnvFilePath(envName, config);
|
|
50
|
+
if (!filePath) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
if (await dockerEnvFileExists(envName, config)) {
|
|
54
|
+
return filePath;
|
|
55
|
+
}
|
|
56
|
+
if (hasExplicitDockerEnvFile(config)) {
|
|
57
|
+
throw new Error(`The configured envFile for "${envName}" does not exist: ${resolveConfiguredDockerEnvFilePath(envName, config)}`);
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
|
|
10
|
+
export const DEFAULT_DOCKER_REGISTRY_ZH_CN = 'registry.cn-shanghai.aliyuncs.com/nocobase/nocobase';
|
|
11
|
+
export const DEFAULT_DOCKER_VERSION = 'alpha';
|
|
12
|
+
export const DOCKER_IMAGE_FULL_SUFFIX = '-full';
|
|
13
|
+
const OFFICIAL_FULL_IMAGE_REGISTRIES = new Set([
|
|
14
|
+
DEFAULT_DOCKER_REGISTRY,
|
|
15
|
+
DEFAULT_DOCKER_REGISTRY_ZH_CN,
|
|
16
|
+
]);
|
|
17
|
+
function trimValue(value) {
|
|
18
|
+
return String(value ?? '').trim();
|
|
19
|
+
}
|
|
20
|
+
export function shouldUseFullDockerImageTag(registry) {
|
|
21
|
+
return OFFICIAL_FULL_IMAGE_REGISTRIES.has(trimValue(registry));
|
|
22
|
+
}
|
|
23
|
+
export function normalizeDockerImageTag(registry, version) {
|
|
24
|
+
const tag = trimValue(version) || DEFAULT_DOCKER_VERSION;
|
|
25
|
+
if (!shouldUseFullDockerImageTag(registry)) {
|
|
26
|
+
return tag;
|
|
27
|
+
}
|
|
28
|
+
return tag.endsWith(DOCKER_IMAGE_FULL_SUFFIX)
|
|
29
|
+
? tag
|
|
30
|
+
: `${tag}${DOCKER_IMAGE_FULL_SUFFIX}`;
|
|
31
|
+
}
|
|
32
|
+
export function resolveDockerImageRef(registry, version, options) {
|
|
33
|
+
const resolvedRegistry = trimValue(registry) || options?.defaultRegistry || DEFAULT_DOCKER_REGISTRY;
|
|
34
|
+
const rawVersion = trimValue(version) || options?.defaultVersion || DEFAULT_DOCKER_VERSION;
|
|
35
|
+
const normalizedTag = normalizeDockerImageTag(resolvedRegistry, rawVersion);
|
|
36
|
+
return `${resolvedRegistry}:${normalizedTag}`;
|
|
37
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
const DEFAULT_DOCKER_LOG_TAIL = 50;
|
|
11
|
+
export function startDockerLogFollower(containerName, options) {
|
|
12
|
+
const tail = Math.max(0, options?.tail ?? DEFAULT_DOCKER_LOG_TAIL);
|
|
13
|
+
const child = spawn('docker', ['logs', '--tail', String(tail), '--follow', containerName], {
|
|
14
|
+
stdio: 'inherit',
|
|
15
|
+
});
|
|
16
|
+
let settled = false;
|
|
17
|
+
let resolveClosed;
|
|
18
|
+
const closed = new Promise((resolve) => {
|
|
19
|
+
resolveClosed = resolve;
|
|
20
|
+
});
|
|
21
|
+
const settle = () => {
|
|
22
|
+
if (!settled) {
|
|
23
|
+
settled = true;
|
|
24
|
+
resolveClosed();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
child.once('error', settle);
|
|
28
|
+
child.once('close', settle);
|
|
29
|
+
return {
|
|
30
|
+
stop: async () => {
|
|
31
|
+
if (settled) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
if (!child.kill()) {
|
|
36
|
+
settle();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
settle();
|
|
41
|
+
}
|
|
42
|
+
await closed;
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|