@nocobase/cli 2.1.0-alpha.23 → 2.1.0-alpha.25
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/README.md +37 -2
- package/README.zh-CN.md +6 -2
- package/bin/run.js +28 -11
- package/dist/commands/db/shared.js +19 -5
- package/dist/commands/dev.js +8 -1
- package/dist/commands/down.js +10 -6
- package/dist/commands/env/add.js +14 -34
- package/dist/commands/env/auth.js +6 -13
- package/dist/commands/env/list.js +10 -15
- package/dist/commands/env/remove.js +4 -10
- package/dist/commands/env/update.js +7 -13
- package/dist/commands/env/use.js +5 -13
- package/dist/commands/init.js +190 -62
- package/dist/commands/install.js +101 -33
- package/dist/commands/logs.js +8 -1
- package/dist/commands/pm/list.js +8 -1
- package/dist/commands/ps.js +18 -15
- package/dist/commands/restart.js +74 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +86 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +71 -0
- package/dist/commands/skills/update.js +71 -0
- package/dist/commands/start.js +8 -1
- package/dist/commands/stop.js +8 -1
- package/dist/commands/test.js +466 -0
- package/dist/commands/upgrade.js +12 -1
- package/dist/lib/api-client.js +3 -2
- package/dist/lib/app-runtime.js +16 -5
- package/dist/lib/auth-store.js +159 -43
- package/dist/lib/bootstrap.js +13 -12
- package/dist/lib/cli-home.js +33 -2
- package/dist/lib/env-auth.js +3 -3
- package/dist/lib/generated-command.js +10 -2
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/resource-command.js +10 -2
- package/dist/lib/run-npm.js +82 -8
- package/dist/lib/runtime-generator.js +1 -1
- package/dist/lib/self-manager.js +246 -0
- package/dist/lib/skills-manager.js +269 -0
- package/dist/lib/startup-update.js +203 -0
- package/dist/locale/en-US.json +4 -1
- package/dist/locale/zh-CN.json +4 -1
- package/package.json +10 -4
package/dist/commands/install.js
CHANGED
|
@@ -16,6 +16,7 @@ import path from 'node:path';
|
|
|
16
16
|
import { exit } from 'node:process';
|
|
17
17
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
18
18
|
import { applyCliLocale, localeText, resolveCliLocale, translateCli, } from "../lib/cli-locale.js";
|
|
19
|
+
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath, } from '../lib/cli-home.js';
|
|
19
20
|
import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
20
21
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
21
22
|
import { run, runNocoBaseCommand } from '../lib/run-npm.js';
|
|
@@ -53,7 +54,6 @@ const DEFAULT_INSTALL_ROOT_USERNAME = 'nocobase';
|
|
|
53
54
|
const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
54
55
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
55
56
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
56
|
-
const CONFIG_SCOPE = 'project';
|
|
57
57
|
const APP_HEALTH_CHECK_INTERVAL_MS = 2_000;
|
|
58
58
|
const APP_HEALTH_CHECK_TIMEOUT_MS = 600_000;
|
|
59
59
|
const APP_HEALTH_CHECK_REQUEST_TIMEOUT_MS = 5_000;
|
|
@@ -137,6 +137,11 @@ function argvHasToken(argv, tokens) {
|
|
|
137
137
|
function isInstallDbDialect(value) {
|
|
138
138
|
return INSTALL_DB_DIALECTS.includes(value);
|
|
139
139
|
}
|
|
140
|
+
function downloadVersionPromptValue(version) {
|
|
141
|
+
return version === 'latest' || version === 'beta' || version === 'alpha'
|
|
142
|
+
? version
|
|
143
|
+
: 'other';
|
|
144
|
+
}
|
|
140
145
|
function supportsBuiltinDbDialect(value) {
|
|
141
146
|
const dialect = String(value ?? '').trim();
|
|
142
147
|
return Object.prototype.hasOwnProperty.call(DEFAULT_INSTALL_BUILTIN_DB_IMAGES, dialect);
|
|
@@ -303,6 +308,7 @@ export default class Install extends Command {
|
|
|
303
308
|
required: false,
|
|
304
309
|
}),
|
|
305
310
|
'builtin-db': Flags.boolean({
|
|
311
|
+
allowNo: true,
|
|
306
312
|
description: 'Create and connect a CLI-managed built-in database for the app',
|
|
307
313
|
default: false,
|
|
308
314
|
}),
|
|
@@ -569,7 +575,7 @@ export default class Install extends Command {
|
|
|
569
575
|
if (argvHasToken(argv, ['--fetch-source'])) {
|
|
570
576
|
preset.fetchSource = flags['fetch-source'];
|
|
571
577
|
}
|
|
572
|
-
if (argvHasToken(argv, ['--builtin-db'])) {
|
|
578
|
+
if (argvHasToken(argv, ['--builtin-db', '--no-builtin-db'])) {
|
|
573
579
|
preset.builtinDb = flags['builtin-db'];
|
|
574
580
|
}
|
|
575
581
|
if (flags['db-dialect'] !== undefined) {
|
|
@@ -588,6 +594,7 @@ export default class Install extends Command {
|
|
|
588
594
|
const v = String(flags['db-host'] ?? '').trim();
|
|
589
595
|
if (v) {
|
|
590
596
|
preset.dbHost = v;
|
|
597
|
+
preset.builtinDb = false;
|
|
591
598
|
}
|
|
592
599
|
}
|
|
593
600
|
if (flags['db-port'] !== undefined) {
|
|
@@ -682,7 +689,14 @@ export default class Install extends Command {
|
|
|
682
689
|
};
|
|
683
690
|
const downloadPreset = {
|
|
684
691
|
...(source ? { source } : {}),
|
|
685
|
-
...(downloadVersion
|
|
692
|
+
...(downloadVersion
|
|
693
|
+
? {
|
|
694
|
+
version: downloadVersionPromptValue(downloadVersion),
|
|
695
|
+
...(downloadVersionPromptValue(downloadVersion) === 'other'
|
|
696
|
+
? { otherVersion: downloadVersion }
|
|
697
|
+
: {}),
|
|
698
|
+
}
|
|
699
|
+
: {}),
|
|
686
700
|
...(dockerRegistry ? { dockerRegistry } : {}),
|
|
687
701
|
...(dockerPlatform ? { dockerPlatform } : {}),
|
|
688
702
|
...(gitUrl ? { gitUrl } : {}),
|
|
@@ -746,7 +760,7 @@ export default class Install extends Command {
|
|
|
746
760
|
if (!parsed.resume) {
|
|
747
761
|
return undefined;
|
|
748
762
|
}
|
|
749
|
-
const env = await getEnv(parsed.env, { scope:
|
|
763
|
+
const env = await getEnv(parsed.env, { scope: resolveDefaultConfigScope() });
|
|
750
764
|
if (!env) {
|
|
751
765
|
throw new Error(formatMissingManagedAppEnvMessage(parsed.env));
|
|
752
766
|
}
|
|
@@ -861,7 +875,11 @@ export default class Install extends Command {
|
|
|
861
875
|
preset.source = String(flags.source).trim();
|
|
862
876
|
}
|
|
863
877
|
if (flags.version !== undefined) {
|
|
864
|
-
|
|
878
|
+
const version = String(flags.version).trim() || 'latest';
|
|
879
|
+
preset.version = downloadVersionPromptValue(version);
|
|
880
|
+
if (preset.version === 'other') {
|
|
881
|
+
preset.otherVersion = version;
|
|
882
|
+
}
|
|
865
883
|
}
|
|
866
884
|
if (flags['docker-registry'] !== undefined) {
|
|
867
885
|
const value = String(flags['docker-registry'] ?? '').trim();
|
|
@@ -891,7 +909,10 @@ export default class Install extends Command {
|
|
|
891
909
|
preset.npmRegistry =
|
|
892
910
|
typeof flags['npm-registry'] === 'string' ? flags['npm-registry'] : '';
|
|
893
911
|
}
|
|
894
|
-
if (argvHasToken(argv, ['--replace', '-r'])) {
|
|
912
|
+
if (flags.resume && !argvHasToken(argv, ['--replace', '-r'])) {
|
|
913
|
+
preset.replace = true;
|
|
914
|
+
}
|
|
915
|
+
else if (argvHasToken(argv, ['--replace', '-r'])) {
|
|
895
916
|
preset.replace = flags.replace;
|
|
896
917
|
}
|
|
897
918
|
if (argvHasToken(argv, ['--dev-dependencies', '--no-dev-dependencies', '-D'])) {
|
|
@@ -933,7 +954,7 @@ export default class Install extends Command {
|
|
|
933
954
|
: Install.defaultWorkspaceName();
|
|
934
955
|
}
|
|
935
956
|
static async ensureWorkspaceName() {
|
|
936
|
-
return await ensureWorkspaceName(Install.defaultWorkspaceName(), { scope:
|
|
957
|
+
return await ensureWorkspaceName(Install.defaultWorkspaceName(), { scope: resolveDefaultConfigScope() });
|
|
937
958
|
}
|
|
938
959
|
static buildBuiltinDbNetworkName(envName, workspaceName) {
|
|
939
960
|
return Install.buildBuiltinDbResourcePrefix(envName, workspaceName);
|
|
@@ -982,9 +1003,11 @@ export default class Install extends Command {
|
|
|
982
1003
|
&& dbHostInput !== 'localhost'
|
|
983
1004
|
? dbHostInput
|
|
984
1005
|
: containerName);
|
|
1006
|
+
const storagePath = resolveConfiguredEnvPath(params.storagePath)
|
|
1007
|
+
?? resolveEnvRelativePath(defaultInstallStoragePath(params.envName));
|
|
985
1008
|
if (dbDialect === 'postgres') {
|
|
986
1009
|
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
987
|
-
const dataDir = path.resolve(
|
|
1010
|
+
const dataDir = path.resolve(storagePath, 'db', 'postgres');
|
|
988
1011
|
const args = [
|
|
989
1012
|
'run',
|
|
990
1013
|
'-d',
|
|
@@ -1028,7 +1051,7 @@ export default class Install extends Command {
|
|
|
1028
1051
|
}
|
|
1029
1052
|
if (dbDialect === 'mysql') {
|
|
1030
1053
|
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1031
|
-
const dataDir = path.resolve(
|
|
1054
|
+
const dataDir = path.resolve(storagePath, 'db', 'mysql');
|
|
1032
1055
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1033
1056
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
1034
1057
|
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
@@ -1074,7 +1097,7 @@ export default class Install extends Command {
|
|
|
1074
1097
|
}
|
|
1075
1098
|
if (dbDialect === 'mariadb') {
|
|
1076
1099
|
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1077
|
-
const dataDir = path.resolve(
|
|
1100
|
+
const dataDir = path.resolve(storagePath, 'db', 'mariadb');
|
|
1078
1101
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1079
1102
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
1080
1103
|
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
@@ -1120,7 +1143,7 @@ export default class Install extends Command {
|
|
|
1120
1143
|
}
|
|
1121
1144
|
if (dbDialect === 'kingbase') {
|
|
1122
1145
|
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1123
|
-
const dataDir = path.resolve(
|
|
1146
|
+
const dataDir = path.resolve(storagePath, 'db', 'kingbase');
|
|
1124
1147
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1125
1148
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
1126
1149
|
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
@@ -1207,6 +1230,7 @@ export default class Install extends Command {
|
|
|
1207
1230
|
async removeDockerContainer(name) {
|
|
1208
1231
|
await run('docker', ['rm', '-f', name], {
|
|
1209
1232
|
errorName: 'docker rm',
|
|
1233
|
+
stdio: 'ignore',
|
|
1210
1234
|
});
|
|
1211
1235
|
}
|
|
1212
1236
|
async removeDockerContainerIfForced(params) {
|
|
@@ -1238,7 +1262,7 @@ export default class Install extends Command {
|
|
|
1238
1262
|
}
|
|
1239
1263
|
return env;
|
|
1240
1264
|
}
|
|
1241
|
-
async ensureBuiltinDbContainer(plan) {
|
|
1265
|
+
async ensureBuiltinDbContainer(plan, options) {
|
|
1242
1266
|
const exists = await this.dockerContainerExists(plan.containerName);
|
|
1243
1267
|
if (exists) {
|
|
1244
1268
|
p.log.info(`Built-in ${plan.dbDialect} container already exists: ${plan.containerName}`);
|
|
@@ -1247,6 +1271,7 @@ export default class Install extends Command {
|
|
|
1247
1271
|
await mkdir(plan.dataDir, { recursive: true });
|
|
1248
1272
|
await run('docker', plan.args, {
|
|
1249
1273
|
errorName: 'docker run',
|
|
1274
|
+
stdio: options?.stdio ?? 'ignore',
|
|
1250
1275
|
});
|
|
1251
1276
|
}
|
|
1252
1277
|
async startBuiltinDb(params) {
|
|
@@ -1267,18 +1292,20 @@ export default class Install extends Command {
|
|
|
1267
1292
|
});
|
|
1268
1293
|
p.log.step(`Preparing built-in ${plan.dbDialect} database`);
|
|
1269
1294
|
await this.ensureDockerNetwork(plan.networkName);
|
|
1270
|
-
await this.removeDockerContainerIfForced({
|
|
1295
|
+
const existingContainerKept = await this.removeDockerContainerIfForced({
|
|
1271
1296
|
containerName: plan.containerName,
|
|
1272
1297
|
displayName: `built-in ${plan.dbDialect} container`,
|
|
1273
1298
|
force: params.force,
|
|
1274
1299
|
});
|
|
1275
|
-
if (Install.shouldPublishBuiltinDbPort(params.downloadResults.source)) {
|
|
1300
|
+
if (!existingContainerKept && Install.shouldPublishBuiltinDbPort(params.downloadResults.source)) {
|
|
1276
1301
|
const portError = await validateAvailableTcpPort(plan.dbPort);
|
|
1277
1302
|
if (portError) {
|
|
1278
1303
|
throw new Error(`Built-in ${plan.dbDialect} needs host port ${plan.dbPort}, but ${portError}`);
|
|
1279
1304
|
}
|
|
1280
1305
|
}
|
|
1281
|
-
await this.ensureBuiltinDbContainer(plan
|
|
1306
|
+
await this.ensureBuiltinDbContainer(plan, {
|
|
1307
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1308
|
+
});
|
|
1282
1309
|
p.log.info(`Built-in ${plan.dbDialect} database is ready at ${plan.dbHost}:${plan.dbPort}`);
|
|
1283
1310
|
return plan;
|
|
1284
1311
|
}
|
|
@@ -1287,8 +1314,9 @@ export default class Install extends Command {
|
|
|
1287
1314
|
|| defaultDockerRegistryForLang(process.env.NB_LOCALE);
|
|
1288
1315
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || 'latest';
|
|
1289
1316
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
1290
|
-
const storagePath =
|
|
1291
|
-
|| defaultInstallStoragePath(params.envName))
|
|
1317
|
+
const storagePath = resolveConfiguredEnvPath(String(params.appResults.storagePath ?? '').trim()
|
|
1318
|
+
|| defaultInstallStoragePath(params.envName))
|
|
1319
|
+
?? resolveEnvRelativePath(defaultInstallStoragePath(params.envName));
|
|
1292
1320
|
const dbDialect = String(params.dbResults.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1293
1321
|
const dbHost = String(params.dbResults.dbHost ?? DEFAULT_INSTALL_DB_HOST).trim() || DEFAULT_INSTALL_DB_HOST;
|
|
1294
1322
|
const dbPort = String(params.dbResults.dbPort ?? defaultDbPortForDialect(dbDialect)).trim()
|
|
@@ -1332,7 +1360,7 @@ export default class Install extends Command {
|
|
|
1332
1360
|
args,
|
|
1333
1361
|
};
|
|
1334
1362
|
}
|
|
1335
|
-
async ensureDockerAppContainer(plan) {
|
|
1363
|
+
async ensureDockerAppContainer(plan, options) {
|
|
1336
1364
|
const exists = await this.dockerContainerExists(plan.containerName);
|
|
1337
1365
|
if (exists) {
|
|
1338
1366
|
p.log.info(`App container already exists: ${plan.containerName}`);
|
|
@@ -1341,6 +1369,7 @@ export default class Install extends Command {
|
|
|
1341
1369
|
await mkdir(plan.storagePath, { recursive: true });
|
|
1342
1370
|
await run('docker', plan.args, {
|
|
1343
1371
|
errorName: 'docker run',
|
|
1372
|
+
stdio: options?.stdio ?? 'ignore',
|
|
1344
1373
|
});
|
|
1345
1374
|
return 'created';
|
|
1346
1375
|
}
|
|
@@ -1363,7 +1392,9 @@ export default class Install extends Command {
|
|
|
1363
1392
|
displayName: 'app container',
|
|
1364
1393
|
force: params.force,
|
|
1365
1394
|
});
|
|
1366
|
-
const containerState = await this.ensureDockerAppContainer(plan
|
|
1395
|
+
const containerState = await this.ensureDockerAppContainer(plan, {
|
|
1396
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1397
|
+
});
|
|
1367
1398
|
if (containerState === 'existing') {
|
|
1368
1399
|
const env = await this.inspectDockerContainerEnv(plan.containerName);
|
|
1369
1400
|
plan.appKey = env.APP_KEY || plan.appKey;
|
|
@@ -1380,12 +1411,16 @@ export default class Install extends Command {
|
|
|
1380
1411
|
}
|
|
1381
1412
|
static buildDownloadArgvFromResults(results, options) {
|
|
1382
1413
|
const argv = ['-y', '--no-intro'];
|
|
1414
|
+
const source = String(results.source ?? '').trim();
|
|
1383
1415
|
if (options?.verbose) {
|
|
1384
1416
|
argv.push('--verbose');
|
|
1385
1417
|
}
|
|
1386
1418
|
Install.pushDownloadArgIfValue(argv, '--source', results.source);
|
|
1387
|
-
Install.pushDownloadArgIfValue(argv, '--version', results
|
|
1388
|
-
Install.pushDownloadArgIfValue(argv, '--output-dir',
|
|
1419
|
+
Install.pushDownloadArgIfValue(argv, '--version', downloadResultsValue(results, 'version'));
|
|
1420
|
+
Install.pushDownloadArgIfValue(argv, '--output-dir', source === 'npm' || source === 'git'
|
|
1421
|
+
? (resolveConfiguredEnvPath(results.outputDir)
|
|
1422
|
+
?? resolveConfiguredEnvPath(String(results.outputDir ?? '').trim() || defaultInstallAppRootPath(results.env)))
|
|
1423
|
+
: results.outputDir);
|
|
1389
1424
|
Install.pushDownloadArgIfValue(argv, '--git-url', results.gitUrl);
|
|
1390
1425
|
Install.pushDownloadArgIfValue(argv, '--docker-registry', results.dockerRegistry);
|
|
1391
1426
|
Install.pushDownloadArgIfValue(argv, '--docker-platform', results.dockerPlatform);
|
|
@@ -1415,26 +1450,49 @@ export default class Install extends Command {
|
|
|
1415
1450
|
const outputDir = String(params.downloadResults.outputDir ?? '').trim()
|
|
1416
1451
|
|| String(params.appResults.appRootPath ?? '').trim()
|
|
1417
1452
|
|| defaultInstallAppRootPath(params.envName);
|
|
1418
|
-
return
|
|
1453
|
+
return resolveConfiguredEnvPath(outputDir) ?? resolveEnvRelativePath(defaultInstallAppRootPath(params.envName));
|
|
1419
1454
|
}
|
|
1420
|
-
|
|
1455
|
+
static resolveLocalProjectConfigPath(params) {
|
|
1456
|
+
return (String(params.downloadResults.outputDir ?? '').trim()
|
|
1457
|
+
|| String(params.appResults.appRootPath ?? '').trim()
|
|
1458
|
+
|| defaultInstallAppRootPath(params.envName));
|
|
1459
|
+
}
|
|
1460
|
+
commandStdio(verbose) {
|
|
1461
|
+
return verbose ? 'inherit' : 'ignore';
|
|
1462
|
+
}
|
|
1463
|
+
async downloadManagedSource(params) {
|
|
1421
1464
|
const argv = Install.buildDownloadArgvFromResults(params.downloadResults, {
|
|
1422
1465
|
verbose: params.verbose,
|
|
1423
1466
|
});
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1467
|
+
const source = String(params.downloadResults.source ?? '').trim();
|
|
1468
|
+
p.log.step(source === 'docker'
|
|
1469
|
+
? 'Downloading Docker image'
|
|
1470
|
+
: 'Downloading local NocoBase app files');
|
|
1471
|
+
return await this.config.runCommand('download', argv);
|
|
1472
|
+
}
|
|
1473
|
+
async downloadLocalApp(params) {
|
|
1474
|
+
const result = await this.downloadManagedSource({
|
|
1475
|
+
downloadResults: params.downloadResults,
|
|
1476
|
+
verbose: params.verbose,
|
|
1477
|
+
});
|
|
1478
|
+
const downloadedProjectRoot = Install.resolveLocalProjectRoot({
|
|
1427
1479
|
envName: params.envName,
|
|
1428
1480
|
appResults: params.appResults,
|
|
1429
1481
|
downloadResults: params.downloadResults,
|
|
1430
1482
|
downloadCommandResult: result,
|
|
1431
1483
|
});
|
|
1432
|
-
params.appResults.appRootPath =
|
|
1433
|
-
|
|
1484
|
+
params.appResults.appRootPath = Install.resolveLocalProjectConfigPath({
|
|
1485
|
+
envName: params.envName,
|
|
1486
|
+
appResults: params.appResults,
|
|
1487
|
+
downloadResults: params.downloadResults,
|
|
1488
|
+
});
|
|
1489
|
+
return downloadedProjectRoot;
|
|
1434
1490
|
}
|
|
1435
1491
|
static buildLocalAppEnvVars(params) {
|
|
1436
|
-
const
|
|
1437
|
-
|| defaultInstallStoragePath(params.envName)
|
|
1492
|
+
const configuredStoragePath = String(params.appResults.storagePath ?? '').trim()
|
|
1493
|
+
|| defaultInstallStoragePath(params.envName);
|
|
1494
|
+
const storagePath = resolveConfiguredEnvPath(configuredStoragePath)
|
|
1495
|
+
?? resolveEnvRelativePath(defaultInstallStoragePath(params.envName));
|
|
1438
1496
|
const dbDialect = String(params.dbResults.dbDialect ?? 'postgres').trim()
|
|
1439
1497
|
|| 'postgres';
|
|
1440
1498
|
const appKey = crypto.randomBytes(32).toString('hex');
|
|
@@ -1476,6 +1534,7 @@ export default class Install extends Command {
|
|
|
1476
1534
|
await runNocoBaseCommand(['pm2', 'kill'], {
|
|
1477
1535
|
cwd: params.projectRoot,
|
|
1478
1536
|
env,
|
|
1537
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1479
1538
|
});
|
|
1480
1539
|
}
|
|
1481
1540
|
catch (error) {
|
|
@@ -1486,6 +1545,7 @@ export default class Install extends Command {
|
|
|
1486
1545
|
await runNocoBaseCommand(args, {
|
|
1487
1546
|
cwd: params.projectRoot,
|
|
1488
1547
|
env,
|
|
1548
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1489
1549
|
});
|
|
1490
1550
|
p.log.info(`Local app is starting at http://127.0.0.1:${env.APP_PORT}`);
|
|
1491
1551
|
return {
|
|
@@ -1625,8 +1685,6 @@ export default class Install extends Command {
|
|
|
1625
1685
|
const argv = [
|
|
1626
1686
|
params.envName,
|
|
1627
1687
|
'--no-intro',
|
|
1628
|
-
'--scope',
|
|
1629
|
-
CONFIG_SCOPE,
|
|
1630
1688
|
'--api-base-url',
|
|
1631
1689
|
apiBaseUrl,
|
|
1632
1690
|
'--auth-type',
|
|
@@ -1739,7 +1797,6 @@ export default class Install extends Command {
|
|
|
1739
1797
|
},
|
|
1740
1798
|
values: {
|
|
1741
1799
|
name: envName,
|
|
1742
|
-
scope: 'project',
|
|
1743
1800
|
...(resumePreset?.envAddPreset ?? {}),
|
|
1744
1801
|
},
|
|
1745
1802
|
yes,
|
|
@@ -1761,6 +1818,7 @@ export default class Install extends Command {
|
|
|
1761
1818
|
const parsed = {
|
|
1762
1819
|
...flags,
|
|
1763
1820
|
};
|
|
1821
|
+
const commandStdio = this.commandStdio(parsed.verbose);
|
|
1764
1822
|
if (!parsed['no-intro']) {
|
|
1765
1823
|
p.intro('Set Up NocoBase');
|
|
1766
1824
|
}
|
|
@@ -1787,6 +1845,7 @@ export default class Install extends Command {
|
|
|
1787
1845
|
downloadResults,
|
|
1788
1846
|
dbResults,
|
|
1789
1847
|
force: parsed.force,
|
|
1848
|
+
commandStdio,
|
|
1790
1849
|
});
|
|
1791
1850
|
dbResults.dbHost = builtinDbPlan.dbHost;
|
|
1792
1851
|
dbResults.dbPort = builtinDbPlan.dbPort;
|
|
@@ -1799,6 +1858,10 @@ export default class Install extends Command {
|
|
|
1799
1858
|
let localAppPlan;
|
|
1800
1859
|
if (Boolean(appResults.fetchSource)) {
|
|
1801
1860
|
if (source === 'docker') {
|
|
1861
|
+
await this.downloadManagedSource({
|
|
1862
|
+
downloadResults,
|
|
1863
|
+
verbose: parsed.verbose,
|
|
1864
|
+
});
|
|
1802
1865
|
dockerAppPlan = await this.installDockerApp({
|
|
1803
1866
|
envName,
|
|
1804
1867
|
workspaceName,
|
|
@@ -1808,6 +1871,7 @@ export default class Install extends Command {
|
|
|
1808
1871
|
rootResults,
|
|
1809
1872
|
builtinDbPlan,
|
|
1810
1873
|
force: parsed.force,
|
|
1874
|
+
commandStdio,
|
|
1811
1875
|
});
|
|
1812
1876
|
appResults.appKey = dockerAppPlan.appKey;
|
|
1813
1877
|
appResults.timeZone = dockerAppPlan.timeZone;
|
|
@@ -1826,6 +1890,7 @@ export default class Install extends Command {
|
|
|
1826
1890
|
appResults,
|
|
1827
1891
|
dbResults,
|
|
1828
1892
|
rootResults,
|
|
1893
|
+
commandStdio,
|
|
1829
1894
|
});
|
|
1830
1895
|
appResults.appKey = localAppPlan.appKey;
|
|
1831
1896
|
appResults.timeZone = localAppPlan.timeZone;
|
|
@@ -1855,5 +1920,8 @@ export default class Install extends Command {
|
|
|
1855
1920
|
}
|
|
1856
1921
|
}
|
|
1857
1922
|
function downloadResultsValue(downloadResults, key) {
|
|
1923
|
+
if (key === 'version' && String(downloadResults.version ?? '').trim() === 'other') {
|
|
1924
|
+
return downloadResults.otherVersion;
|
|
1925
|
+
}
|
|
1858
1926
|
return downloadResults[key];
|
|
1859
1927
|
}
|
package/dist/commands/logs.js
CHANGED
|
@@ -49,13 +49,20 @@ export default class Logs extends Command {
|
|
|
49
49
|
if (!runtime) {
|
|
50
50
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
51
51
|
}
|
|
52
|
-
if (runtime.kind === '
|
|
52
|
+
if (runtime.kind === 'http') {
|
|
53
53
|
this.error([
|
|
54
54
|
`Can't show runtime logs for "${runtime.envName}" from this machine.`,
|
|
55
55
|
'This env only has an API connection, so there is no saved local app or Docker container to read logs from.',
|
|
56
56
|
'Connect it to a local checkout or reinstall it with npm, git, or Docker if you want CLI-managed logs.',
|
|
57
57
|
].join('\n'));
|
|
58
58
|
}
|
|
59
|
+
if (runtime.kind === 'ssh') {
|
|
60
|
+
this.error([
|
|
61
|
+
`Can't show runtime logs for "${runtime.envName}" yet.`,
|
|
62
|
+
'SSH env support is reserved but not implemented yet.',
|
|
63
|
+
'Use a local or Docker env for CLI-managed logs for now.',
|
|
64
|
+
].join('\n'));
|
|
65
|
+
}
|
|
59
66
|
const tail = String(flags.tail ?? 100);
|
|
60
67
|
const follow = flags.follow !== false;
|
|
61
68
|
printInfo(follow
|
package/dist/commands/pm/list.js
CHANGED
|
@@ -11,7 +11,7 @@ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runDockerN
|
|
|
11
11
|
export default class PmList extends Command {
|
|
12
12
|
static args = {};
|
|
13
13
|
static summary = 'List plugins for the selected env';
|
|
14
|
-
static description = 'List installed plugins in the selected env (npm/git runs locally, Docker runs inside the saved app container,
|
|
14
|
+
static description = 'List installed plugins in the selected env (npm/git runs locally, Docker runs inside the saved app container, HTTP envs fall back to the API)';
|
|
15
15
|
static examples = [
|
|
16
16
|
'<%= config.bin %> <%= command.id %>',
|
|
17
17
|
'<%= config.bin %> <%= command.id %> -e local',
|
|
@@ -49,6 +49,13 @@ export default class PmList extends Command {
|
|
|
49
49
|
}
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
|
+
if (runtime.kind === 'ssh') {
|
|
53
|
+
this.error([
|
|
54
|
+
`Can't list plugins for "${runtime.envName}" yet.`,
|
|
55
|
+
'SSH env support is reserved but not implemented yet.',
|
|
56
|
+
'Use a local, Docker, or HTTP env for plugin inspection right now.',
|
|
57
|
+
].join('\n'));
|
|
58
|
+
}
|
|
52
59
|
await this.config.runCommand('api:pm:list', ['--mode=summary']);
|
|
53
60
|
}
|
|
54
61
|
}
|
package/dist/commands/ps.js
CHANGED
|
@@ -10,12 +10,15 @@ import { Command, Flags } from '@oclif/core';
|
|
|
10
10
|
import { buildDockerDbContainerName, dockerContainerExists, dockerContainerIsRunning, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../lib/app-runtime.js';
|
|
11
11
|
import { listEnvs } from '../lib/auth-store.js';
|
|
12
12
|
import { renderTable } from '../lib/ui.js';
|
|
13
|
+
function resolveApiBaseUrl(config) {
|
|
14
|
+
return String(config.apiBaseUrl ?? config.baseUrl ?? config.apibaseUrl ?? '').trim();
|
|
15
|
+
}
|
|
13
16
|
function appUrl(runtime) {
|
|
14
17
|
const port = String(runtime.env.config.appPort ?? '').trim();
|
|
15
18
|
if (port) {
|
|
16
19
|
return `http://127.0.0.1:${port}`;
|
|
17
20
|
}
|
|
18
|
-
const baseUrl =
|
|
21
|
+
const baseUrl = resolveApiBaseUrl(runtime.env.config);
|
|
19
22
|
return baseUrl.replace(/\/api\/?$/, '');
|
|
20
23
|
}
|
|
21
24
|
async function isLocalAppHealthy(runtime) {
|
|
@@ -47,30 +50,30 @@ async function dockerStatus(containerName) {
|
|
|
47
50
|
}
|
|
48
51
|
async function dbStatus(runtime) {
|
|
49
52
|
if (!runtime.env.config.builtinDb) {
|
|
50
|
-
return runtime.kind === '
|
|
53
|
+
return runtime.kind === 'http' ? 'external' : '-';
|
|
51
54
|
}
|
|
52
|
-
if (runtime.kind === '
|
|
55
|
+
if (runtime.kind === 'http') {
|
|
53
56
|
return 'external';
|
|
54
57
|
}
|
|
58
|
+
if (runtime.kind === 'ssh') {
|
|
59
|
+
return '-';
|
|
60
|
+
}
|
|
55
61
|
const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
56
62
|
const containerName = buildDockerDbContainerName(runtime.envName, dbDialect, runtime.workspaceName);
|
|
57
63
|
return await dockerStatus(containerName);
|
|
58
64
|
}
|
|
59
|
-
async function
|
|
60
|
-
if (runtime.kind === '
|
|
61
|
-
return '
|
|
65
|
+
async function runtimeStatus(runtime) {
|
|
66
|
+
if (runtime.kind === 'http') {
|
|
67
|
+
return 'http';
|
|
68
|
+
}
|
|
69
|
+
if (runtime.kind === 'ssh') {
|
|
70
|
+
return 'ssh';
|
|
62
71
|
}
|
|
63
72
|
if (runtime.kind === 'docker') {
|
|
64
73
|
return await dockerStatus(runtime.containerName);
|
|
65
74
|
}
|
|
66
75
|
return await isLocalAppHealthy(runtime) ? 'running' : 'stopped';
|
|
67
76
|
}
|
|
68
|
-
function sourceLabel(runtime) {
|
|
69
|
-
if (runtime.kind === 'remote') {
|
|
70
|
-
return 'remote';
|
|
71
|
-
}
|
|
72
|
-
return runtime.source;
|
|
73
|
-
}
|
|
74
77
|
export default class Ps extends Command {
|
|
75
78
|
static description = 'Show NocoBase runtime status for configured envs without starting or stopping anything.';
|
|
76
79
|
static examples = [
|
|
@@ -105,12 +108,12 @@ export default class Ps extends Command {
|
|
|
105
108
|
}
|
|
106
109
|
rows.push([
|
|
107
110
|
runtime.envName,
|
|
108
|
-
|
|
109
|
-
await
|
|
111
|
+
runtime.kind,
|
|
112
|
+
await runtimeStatus(runtime),
|
|
110
113
|
await dbStatus(runtime),
|
|
111
114
|
appUrl(runtime),
|
|
112
115
|
]);
|
|
113
116
|
}
|
|
114
|
-
this.log(renderTable(['Env', '
|
|
117
|
+
this.log(renderTable(['Env', 'Kind', 'Status', 'Database', 'URL'], rows));
|
|
115
118
|
}
|
|
116
119
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
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 { Command, Flags } from '@oclif/core';
|
|
10
|
+
function argvHasToken(argv, tokens) {
|
|
11
|
+
return tokens.some((token) => argv.includes(token));
|
|
12
|
+
}
|
|
13
|
+
function pushFlag(argv, flag, value) {
|
|
14
|
+
if (value !== undefined) {
|
|
15
|
+
argv.push(flag, String(value));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export default class Restart extends Command {
|
|
19
|
+
static description = 'Restart NocoBase for the selected env by stopping it first, then starting it again.';
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> <%= command.id %>',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> --env local',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> --env local --quickstart',
|
|
24
|
+
'<%= config.bin %> <%= command.id %> --env local --port 12000',
|
|
25
|
+
'<%= config.bin %> <%= command.id %> --env local --daemon',
|
|
26
|
+
'<%= config.bin %> <%= command.id %> --env local --no-daemon',
|
|
27
|
+
'<%= config.bin %> <%= command.id %> --env local --instances 2',
|
|
28
|
+
'<%= config.bin %> <%= command.id %> --env local --launch-mode pm2',
|
|
29
|
+
'<%= config.bin %> <%= command.id %> --env local --verbose',
|
|
30
|
+
'<%= config.bin %> <%= command.id %> --env local-docker',
|
|
31
|
+
];
|
|
32
|
+
static flags = {
|
|
33
|
+
env: Flags.string({
|
|
34
|
+
char: 'e',
|
|
35
|
+
description: 'CLI env name to restart. Defaults to the current env when omitted',
|
|
36
|
+
}),
|
|
37
|
+
quickstart: Flags.boolean({ description: 'Quickstart the application after stopping it', required: false }),
|
|
38
|
+
port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
|
|
39
|
+
daemon: Flags.boolean({
|
|
40
|
+
description: 'Run the application as a daemon after stopping it (default: true; use --no-daemon to stay in the foreground)',
|
|
41
|
+
char: 'd',
|
|
42
|
+
required: false,
|
|
43
|
+
default: true,
|
|
44
|
+
allowNo: true,
|
|
45
|
+
}),
|
|
46
|
+
instances: Flags.integer({ description: 'Number of instances to run after stopping it', char: 'i', required: false }),
|
|
47
|
+
'launch-mode': Flags.string({ description: 'Launch Mode', required: false, options: ['pm2', 'node'] }),
|
|
48
|
+
verbose: Flags.boolean({
|
|
49
|
+
description: 'Show raw shutdown/startup output from the underlying local or Docker command',
|
|
50
|
+
default: false,
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
async run() {
|
|
54
|
+
const { flags } = await this.parse(Restart);
|
|
55
|
+
const stopArgv = [];
|
|
56
|
+
const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
|
|
57
|
+
pushFlag(stopArgv, '--env', flags.env?.trim() || undefined);
|
|
58
|
+
if (flags.verbose) {
|
|
59
|
+
stopArgv.push('--verbose');
|
|
60
|
+
}
|
|
61
|
+
await this.config.runCommand('stop', stopArgv);
|
|
62
|
+
const startArgv = [...stopArgv];
|
|
63
|
+
if (flags.quickstart) {
|
|
64
|
+
startArgv.push('--quickstart');
|
|
65
|
+
}
|
|
66
|
+
pushFlag(startArgv, '--port', flags.port);
|
|
67
|
+
if (daemonFlagWasProvided) {
|
|
68
|
+
startArgv.push(flags.daemon === false ? '--no-daemon' : '--daemon');
|
|
69
|
+
}
|
|
70
|
+
pushFlag(startArgv, '--instances', flags.instances);
|
|
71
|
+
pushFlag(startArgv, '--launch-mode', flags['launch-mode']);
|
|
72
|
+
await this.config.runCommand('start', startArgv);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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 { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { getRecommendedSelfUpdateCommand, inspectSelfStatus, } from '../../lib/self-manager.js';
|
|
11
|
+
import { printInfo, renderTable } from '../../lib/ui.js';
|
|
12
|
+
export default class SelfCheck extends Command {
|
|
13
|
+
static summary = 'Check the installed NocoBase CLI version and self-update support';
|
|
14
|
+
static description = 'Inspect the current NocoBase CLI install, resolve the latest version for the selected channel, and report whether automatic self-update is supported.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --channel beta',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
channel: Flags.string({
|
|
22
|
+
description: 'Release channel to compare against. Defaults to the current CLI channel.',
|
|
23
|
+
options: ['auto', 'latest', 'beta', 'alpha'],
|
|
24
|
+
default: 'auto',
|
|
25
|
+
}),
|
|
26
|
+
json: Flags.boolean({
|
|
27
|
+
description: 'Output the result as JSON',
|
|
28
|
+
default: false,
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
async run() {
|
|
32
|
+
const { flags } = await this.parse(SelfCheck);
|
|
33
|
+
const status = await inspectSelfStatus({
|
|
34
|
+
channel: flags.channel,
|
|
35
|
+
});
|
|
36
|
+
if (flags.json) {
|
|
37
|
+
this.log(JSON.stringify({
|
|
38
|
+
ok: true,
|
|
39
|
+
kind: 'self',
|
|
40
|
+
packageName: status.packageName,
|
|
41
|
+
currentVersion: status.currentVersion,
|
|
42
|
+
latestVersion: status.latestVersion,
|
|
43
|
+
channel: status.channel,
|
|
44
|
+
updateAvailable: status.updateAvailable,
|
|
45
|
+
installMethod: status.installMethod,
|
|
46
|
+
updatable: status.updatable,
|
|
47
|
+
updateBlockedReason: status.updateBlockedReason,
|
|
48
|
+
recommendedCommand: getRecommendedSelfUpdateCommand(status),
|
|
49
|
+
registryError: status.registryError,
|
|
50
|
+
}, null, 2));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.log(renderTable(['Field', 'Value'], [
|
|
54
|
+
['Current version', status.currentVersion || 'unknown'],
|
|
55
|
+
['Latest version', status.latestVersion || 'unknown'],
|
|
56
|
+
['Channel', status.channel],
|
|
57
|
+
['Install method', status.installMethod],
|
|
58
|
+
['Auto-update', status.updatable ? 'supported' : 'not supported'],
|
|
59
|
+
['Update available', status.updateAvailable ? 'yes' : 'no'],
|
|
60
|
+
]));
|
|
61
|
+
if (status.updateAvailable && status.updatable) {
|
|
62
|
+
printInfo('Run `nb self update`.');
|
|
63
|
+
}
|
|
64
|
+
else if (status.updateAvailable && status.updateBlockedReason) {
|
|
65
|
+
printInfo(status.updateBlockedReason);
|
|
66
|
+
}
|
|
67
|
+
if (status.registryError) {
|
|
68
|
+
printInfo(`Version check warning: ${status.registryError}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|