@nocobase/cli 2.1.0-alpha.22 → 2.1.0-alpha.24
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 +31 -0
- package/bin/run.js +13 -11
- package/dist/commands/download.js +143 -21
- package/dist/commands/init.js +23 -7
- package/dist/commands/install.js +67 -19
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +76 -0
- package/dist/commands/skills/check.js +63 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +58 -0
- package/dist/commands/skills/update.js +58 -0
- package/dist/commands/test.js +466 -0
- package/dist/lib/prompt-catalog.js +11 -4
- package/dist/lib/prompt-web-ui.js +45 -10
- package/dist/lib/run-npm.js +91 -17
- package/dist/lib/self-manager.js +246 -0
- package/dist/lib/skills-manager.js +202 -0
- package/dist/locale/en-US.json +56 -5
- package/dist/locale/zh-CN.json +57 -6
- package/package.json +11 -4
package/dist/commands/install.js
CHANGED
|
@@ -15,7 +15,7 @@ import { mkdir } from 'node:fs/promises';
|
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { exit } from 'node:process';
|
|
17
17
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
18
|
-
import { applyCliLocale, localeText, translateCli, } from "../lib/cli-locale.js";
|
|
18
|
+
import { applyCliLocale, localeText, resolveCliLocale, translateCli, } from "../lib/cli-locale.js";
|
|
19
19
|
import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
20
20
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
21
21
|
import { run, runNocoBaseCommand } from '../lib/run-npm.js';
|
|
@@ -40,6 +40,12 @@ const DEFAULT_INSTALL_BUILTIN_DB_IMAGES = {
|
|
|
40
40
|
mariadb: 'mariadb:11',
|
|
41
41
|
kingbase: 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86',
|
|
42
42
|
};
|
|
43
|
+
const DEFAULT_INSTALL_BUILTIN_DB_IMAGES_ZH_CN = {
|
|
44
|
+
postgres: 'registry.cn-shanghai.aliyuncs.com/nocobase/postgres:16',
|
|
45
|
+
mysql: 'registry.cn-shanghai.aliyuncs.com/nocobase/mysql:8',
|
|
46
|
+
mariadb: 'registry.cn-shanghai.aliyuncs.com/nocobase/mariadb:11',
|
|
47
|
+
kingbase: 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86',
|
|
48
|
+
};
|
|
43
49
|
const DEFAULT_INSTALL_DB_DATABASE = 'nocobase';
|
|
44
50
|
const DEFAULT_INSTALL_DB_USER = 'nocobase';
|
|
45
51
|
const DEFAULT_INSTALL_DB_PASSWORD = 'nocobase';
|
|
@@ -141,9 +147,12 @@ export function defaultDbPortForDialect(value) {
|
|
|
141
147
|
}
|
|
142
148
|
function defaultBuiltinDbImageForDialect(value) {
|
|
143
149
|
const dialect = String(value ?? 'postgres').trim();
|
|
150
|
+
const defaults = resolveCliLocale(process.env.NB_LOCALE) === 'zh-CN'
|
|
151
|
+
? DEFAULT_INSTALL_BUILTIN_DB_IMAGES_ZH_CN
|
|
152
|
+
: DEFAULT_INSTALL_BUILTIN_DB_IMAGES;
|
|
144
153
|
return supportsBuiltinDbDialect(dialect)
|
|
145
|
-
?
|
|
146
|
-
:
|
|
154
|
+
? defaults[dialect]
|
|
155
|
+
: defaults.postgres;
|
|
147
156
|
}
|
|
148
157
|
function defaultDbDatabaseForDialect(value) {
|
|
149
158
|
return String(value ?? '').trim() === 'kingbase'
|
|
@@ -254,6 +263,10 @@ export default class Install extends Command {
|
|
|
254
263
|
description: 'Resume a previous unfinished setup for this env using the saved workspace env config',
|
|
255
264
|
default: false,
|
|
256
265
|
}),
|
|
266
|
+
verbose: Flags.boolean({
|
|
267
|
+
description: 'Show detailed command output',
|
|
268
|
+
default: false,
|
|
269
|
+
}),
|
|
257
270
|
env: Flags.string({
|
|
258
271
|
char: 'e',
|
|
259
272
|
description: 'App/env name to create or update. Defaults app paths to ./<envName>/source/ and ./<envName>/storage/.',
|
|
@@ -804,15 +817,15 @@ export default class Install extends Command {
|
|
|
804
817
|
};
|
|
805
818
|
}
|
|
806
819
|
/**
|
|
807
|
-
* When install runs {@link Download.prompts} after app prompts, align
|
|
808
|
-
* output directory
|
|
820
|
+
* When install runs {@link Download.prompts} after app prompts, align the download
|
|
821
|
+
* output directory with app settings, while Docker registry defaults follow the CLI locale.
|
|
809
822
|
*/
|
|
810
823
|
static buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
811
824
|
const appRoot = String(appResults.appRootPath ?? '').trim() || defaultInstallAppRootPath(envName);
|
|
812
825
|
const lang = String(appResults.lang ?? DEFAULT_INSTALL_LANG).trim() || DEFAULT_INSTALL_LANG;
|
|
813
826
|
const initialValues = {
|
|
814
827
|
lang,
|
|
815
|
-
dockerRegistry: defaultDockerRegistryForLang(
|
|
828
|
+
dockerRegistry: defaultDockerRegistryForLang(process.env.NB_LOCALE),
|
|
816
829
|
outputDir: appRoot,
|
|
817
830
|
};
|
|
818
831
|
const values = {
|
|
@@ -970,7 +983,7 @@ export default class Install extends Command {
|
|
|
970
983
|
? dbHostInput
|
|
971
984
|
: containerName);
|
|
972
985
|
if (dbDialect === 'postgres') {
|
|
973
|
-
const image = String(params.builtinDbImage ?? '').trim() ||
|
|
986
|
+
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
974
987
|
const dataDir = path.resolve(params.storagePath, 'db', 'postgres');
|
|
975
988
|
const args = [
|
|
976
989
|
'run',
|
|
@@ -1014,7 +1027,7 @@ export default class Install extends Command {
|
|
|
1014
1027
|
};
|
|
1015
1028
|
}
|
|
1016
1029
|
if (dbDialect === 'mysql') {
|
|
1017
|
-
const image = String(params.builtinDbImage ?? '').trim() ||
|
|
1030
|
+
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1018
1031
|
const dataDir = path.resolve(params.storagePath, 'db', 'mysql');
|
|
1019
1032
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1020
1033
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
@@ -1060,7 +1073,7 @@ export default class Install extends Command {
|
|
|
1060
1073
|
};
|
|
1061
1074
|
}
|
|
1062
1075
|
if (dbDialect === 'mariadb') {
|
|
1063
|
-
const image = String(params.builtinDbImage ?? '').trim() ||
|
|
1076
|
+
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1064
1077
|
const dataDir = path.resolve(params.storagePath, 'db', 'mariadb');
|
|
1065
1078
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1066
1079
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
@@ -1106,7 +1119,7 @@ export default class Install extends Command {
|
|
|
1106
1119
|
};
|
|
1107
1120
|
}
|
|
1108
1121
|
if (dbDialect === 'kingbase') {
|
|
1109
|
-
const image = String(params.builtinDbImage ?? '').trim() ||
|
|
1122
|
+
const image = String(params.builtinDbImage ?? '').trim() || defaultBuiltinDbImageForDialect(dbDialect);
|
|
1110
1123
|
const dataDir = path.resolve(params.storagePath, 'db', 'kingbase');
|
|
1111
1124
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1112
1125
|
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
@@ -1194,6 +1207,7 @@ export default class Install extends Command {
|
|
|
1194
1207
|
async removeDockerContainer(name) {
|
|
1195
1208
|
await run('docker', ['rm', '-f', name], {
|
|
1196
1209
|
errorName: 'docker rm',
|
|
1210
|
+
stdio: 'ignore',
|
|
1197
1211
|
});
|
|
1198
1212
|
}
|
|
1199
1213
|
async removeDockerContainerIfForced(params) {
|
|
@@ -1225,7 +1239,7 @@ export default class Install extends Command {
|
|
|
1225
1239
|
}
|
|
1226
1240
|
return env;
|
|
1227
1241
|
}
|
|
1228
|
-
async ensureBuiltinDbContainer(plan) {
|
|
1242
|
+
async ensureBuiltinDbContainer(plan, options) {
|
|
1229
1243
|
const exists = await this.dockerContainerExists(plan.containerName);
|
|
1230
1244
|
if (exists) {
|
|
1231
1245
|
p.log.info(`Built-in ${plan.dbDialect} container already exists: ${plan.containerName}`);
|
|
@@ -1234,6 +1248,7 @@ export default class Install extends Command {
|
|
|
1234
1248
|
await mkdir(plan.dataDir, { recursive: true });
|
|
1235
1249
|
await run('docker', plan.args, {
|
|
1236
1250
|
errorName: 'docker run',
|
|
1251
|
+
stdio: options?.stdio ?? 'ignore',
|
|
1237
1252
|
});
|
|
1238
1253
|
}
|
|
1239
1254
|
async startBuiltinDb(params) {
|
|
@@ -1265,13 +1280,15 @@ export default class Install extends Command {
|
|
|
1265
1280
|
throw new Error(`Built-in ${plan.dbDialect} needs host port ${plan.dbPort}, but ${portError}`);
|
|
1266
1281
|
}
|
|
1267
1282
|
}
|
|
1268
|
-
await this.ensureBuiltinDbContainer(plan
|
|
1283
|
+
await this.ensureBuiltinDbContainer(plan, {
|
|
1284
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1285
|
+
});
|
|
1269
1286
|
p.log.info(`Built-in ${plan.dbDialect} database is ready at ${plan.dbHost}:${plan.dbPort}`);
|
|
1270
1287
|
return plan;
|
|
1271
1288
|
}
|
|
1272
1289
|
static buildDockerAppPlan(params) {
|
|
1273
1290
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim()
|
|
1274
|
-
|| defaultDockerRegistryForLang(
|
|
1291
|
+
|| defaultDockerRegistryForLang(process.env.NB_LOCALE);
|
|
1275
1292
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || 'latest';
|
|
1276
1293
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
1277
1294
|
const storagePath = path.resolve(String(params.appResults.storagePath ?? '').trim()
|
|
@@ -1319,7 +1336,7 @@ export default class Install extends Command {
|
|
|
1319
1336
|
args,
|
|
1320
1337
|
};
|
|
1321
1338
|
}
|
|
1322
|
-
async ensureDockerAppContainer(plan) {
|
|
1339
|
+
async ensureDockerAppContainer(plan, options) {
|
|
1323
1340
|
const exists = await this.dockerContainerExists(plan.containerName);
|
|
1324
1341
|
if (exists) {
|
|
1325
1342
|
p.log.info(`App container already exists: ${plan.containerName}`);
|
|
@@ -1328,6 +1345,7 @@ export default class Install extends Command {
|
|
|
1328
1345
|
await mkdir(plan.storagePath, { recursive: true });
|
|
1329
1346
|
await run('docker', plan.args, {
|
|
1330
1347
|
errorName: 'docker run',
|
|
1348
|
+
stdio: options?.stdio ?? 'ignore',
|
|
1331
1349
|
});
|
|
1332
1350
|
return 'created';
|
|
1333
1351
|
}
|
|
@@ -1350,7 +1368,9 @@ export default class Install extends Command {
|
|
|
1350
1368
|
displayName: 'app container',
|
|
1351
1369
|
force: params.force,
|
|
1352
1370
|
});
|
|
1353
|
-
const containerState = await this.ensureDockerAppContainer(plan
|
|
1371
|
+
const containerState = await this.ensureDockerAppContainer(plan, {
|
|
1372
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1373
|
+
});
|
|
1354
1374
|
if (containerState === 'existing') {
|
|
1355
1375
|
const env = await this.inspectDockerContainerEnv(plan.containerName);
|
|
1356
1376
|
plan.appKey = env.APP_KEY || plan.appKey;
|
|
@@ -1365,8 +1385,11 @@ export default class Install extends Command {
|
|
|
1365
1385
|
argv.push(flag, text);
|
|
1366
1386
|
}
|
|
1367
1387
|
}
|
|
1368
|
-
static buildDownloadArgvFromResults(results) {
|
|
1388
|
+
static buildDownloadArgvFromResults(results, options) {
|
|
1369
1389
|
const argv = ['-y', '--no-intro'];
|
|
1390
|
+
if (options?.verbose) {
|
|
1391
|
+
argv.push('--verbose');
|
|
1392
|
+
}
|
|
1370
1393
|
Install.pushDownloadArgIfValue(argv, '--source', results.source);
|
|
1371
1394
|
Install.pushDownloadArgIfValue(argv, '--version', results.version);
|
|
1372
1395
|
Install.pushDownloadArgIfValue(argv, '--output-dir', results.outputDir);
|
|
@@ -1401,10 +1424,24 @@ export default class Install extends Command {
|
|
|
1401
1424
|
|| defaultInstallAppRootPath(params.envName);
|
|
1402
1425
|
return path.resolve(process.cwd(), outputDir);
|
|
1403
1426
|
}
|
|
1427
|
+
commandStdio(verbose) {
|
|
1428
|
+
return verbose ? 'inherit' : 'ignore';
|
|
1429
|
+
}
|
|
1430
|
+
async downloadManagedSource(params) {
|
|
1431
|
+
const argv = Install.buildDownloadArgvFromResults(params.downloadResults, {
|
|
1432
|
+
verbose: params.verbose,
|
|
1433
|
+
});
|
|
1434
|
+
const source = String(params.downloadResults.source ?? '').trim();
|
|
1435
|
+
p.log.step(source === 'docker'
|
|
1436
|
+
? 'Downloading Docker image'
|
|
1437
|
+
: 'Downloading local NocoBase app files');
|
|
1438
|
+
return await this.config.runCommand('download', argv);
|
|
1439
|
+
}
|
|
1404
1440
|
async downloadLocalApp(params) {
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1441
|
+
const result = await this.downloadManagedSource({
|
|
1442
|
+
downloadResults: params.downloadResults,
|
|
1443
|
+
verbose: params.verbose,
|
|
1444
|
+
});
|
|
1408
1445
|
const projectRoot = Install.resolveLocalProjectRoot({
|
|
1409
1446
|
envName: params.envName,
|
|
1410
1447
|
appResults: params.appResults,
|
|
@@ -1458,6 +1495,7 @@ export default class Install extends Command {
|
|
|
1458
1495
|
await runNocoBaseCommand(['pm2', 'kill'], {
|
|
1459
1496
|
cwd: params.projectRoot,
|
|
1460
1497
|
env,
|
|
1498
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1461
1499
|
});
|
|
1462
1500
|
}
|
|
1463
1501
|
catch (error) {
|
|
@@ -1468,6 +1506,7 @@ export default class Install extends Command {
|
|
|
1468
1506
|
await runNocoBaseCommand(args, {
|
|
1469
1507
|
cwd: params.projectRoot,
|
|
1470
1508
|
env,
|
|
1509
|
+
stdio: params.commandStdio ?? 'ignore',
|
|
1471
1510
|
});
|
|
1472
1511
|
p.log.info(`Local app is starting at http://127.0.0.1:${env.APP_PORT}`);
|
|
1473
1512
|
return {
|
|
@@ -1743,6 +1782,7 @@ export default class Install extends Command {
|
|
|
1743
1782
|
const parsed = {
|
|
1744
1783
|
...flags,
|
|
1745
1784
|
};
|
|
1785
|
+
const commandStdio = this.commandStdio(parsed.verbose);
|
|
1746
1786
|
if (!parsed['no-intro']) {
|
|
1747
1787
|
p.intro('Set Up NocoBase');
|
|
1748
1788
|
}
|
|
@@ -1769,6 +1809,7 @@ export default class Install extends Command {
|
|
|
1769
1809
|
downloadResults,
|
|
1770
1810
|
dbResults,
|
|
1771
1811
|
force: parsed.force,
|
|
1812
|
+
commandStdio,
|
|
1772
1813
|
});
|
|
1773
1814
|
dbResults.dbHost = builtinDbPlan.dbHost;
|
|
1774
1815
|
dbResults.dbPort = builtinDbPlan.dbPort;
|
|
@@ -1781,6 +1822,10 @@ export default class Install extends Command {
|
|
|
1781
1822
|
let localAppPlan;
|
|
1782
1823
|
if (Boolean(appResults.fetchSource)) {
|
|
1783
1824
|
if (source === 'docker') {
|
|
1825
|
+
await this.downloadManagedSource({
|
|
1826
|
+
downloadResults,
|
|
1827
|
+
verbose: parsed.verbose,
|
|
1828
|
+
});
|
|
1784
1829
|
dockerAppPlan = await this.installDockerApp({
|
|
1785
1830
|
envName,
|
|
1786
1831
|
workspaceName,
|
|
@@ -1790,6 +1835,7 @@ export default class Install extends Command {
|
|
|
1790
1835
|
rootResults,
|
|
1791
1836
|
builtinDbPlan,
|
|
1792
1837
|
force: parsed.force,
|
|
1838
|
+
commandStdio,
|
|
1793
1839
|
});
|
|
1794
1840
|
appResults.appKey = dockerAppPlan.appKey;
|
|
1795
1841
|
appResults.timeZone = dockerAppPlan.timeZone;
|
|
@@ -1799,6 +1845,7 @@ export default class Install extends Command {
|
|
|
1799
1845
|
envName,
|
|
1800
1846
|
appResults,
|
|
1801
1847
|
downloadResults,
|
|
1848
|
+
verbose: parsed.verbose,
|
|
1802
1849
|
});
|
|
1803
1850
|
localAppPlan = await this.startLocalApp({
|
|
1804
1851
|
envName,
|
|
@@ -1807,6 +1854,7 @@ export default class Install extends Command {
|
|
|
1807
1854
|
appResults,
|
|
1808
1855
|
dbResults,
|
|
1809
1856
|
rootResults,
|
|
1857
|
+
commandStdio,
|
|
1810
1858
|
});
|
|
1811
1859
|
appResults.appKey = localAppPlan.appKey;
|
|
1812
1860
|
appResults.timeZone = localAppPlan.timeZone;
|
|
@@ -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` to update the CLI.');
|
|
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
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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, loadHelpClass } from '@oclif/core';
|
|
10
|
+
export default class Self extends Command {
|
|
11
|
+
static summary = 'Inspect or update the NocoBase CLI itself';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(Self);
|
|
14
|
+
const Help = await loadHelpClass(this.config);
|
|
15
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? this.config.pjson.helpOptions).showHelp([
|
|
16
|
+
this.id ?? 'self',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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 { confirmAction } from '../../lib/ui.js';
|
|
11
|
+
import { formatSelfUpdateUnavailableMessage, formatUnsupportedSelfUpdateMessage, inspectSelfStatus, updateSelf, } from '../../lib/self-manager.js';
|
|
12
|
+
export default class SelfUpdate extends Command {
|
|
13
|
+
static summary = 'Update the globally installed NocoBase CLI';
|
|
14
|
+
static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm install.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --yes',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --channel alpha --json',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
channel: Flags.string({
|
|
22
|
+
description: 'Release channel to update to. Defaults to the current CLI channel.',
|
|
23
|
+
options: ['auto', 'latest', 'beta', 'alpha'],
|
|
24
|
+
default: 'auto',
|
|
25
|
+
}),
|
|
26
|
+
yes: Flags.boolean({
|
|
27
|
+
char: 'y',
|
|
28
|
+
description: 'Skip the update confirmation prompt',
|
|
29
|
+
default: false,
|
|
30
|
+
}),
|
|
31
|
+
json: Flags.boolean({
|
|
32
|
+
description: 'Output the result as JSON',
|
|
33
|
+
default: false,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
async run() {
|
|
37
|
+
const { flags } = await this.parse(SelfUpdate);
|
|
38
|
+
const status = await inspectSelfStatus({
|
|
39
|
+
channel: flags.channel,
|
|
40
|
+
});
|
|
41
|
+
if (!status.updatable) {
|
|
42
|
+
this.error(formatUnsupportedSelfUpdateMessage(status));
|
|
43
|
+
}
|
|
44
|
+
if (!status.latestVersion && status.registryError) {
|
|
45
|
+
this.error(formatSelfUpdateUnavailableMessage(status));
|
|
46
|
+
}
|
|
47
|
+
if (!flags.yes && status.updateAvailable) {
|
|
48
|
+
const confirmed = await confirmAction(`Update ${status.packageName} from ${status.currentVersion} to ${status.latestVersion}?`, { defaultValue: false });
|
|
49
|
+
if (!confirmed) {
|
|
50
|
+
this.log('Skipped CLI update.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const result = await updateSelf({
|
|
55
|
+
channel: flags.channel,
|
|
56
|
+
});
|
|
57
|
+
if (flags.json) {
|
|
58
|
+
this.log(JSON.stringify({
|
|
59
|
+
ok: true,
|
|
60
|
+
kind: 'self',
|
|
61
|
+
action: result.action,
|
|
62
|
+
packageName: result.status.packageName,
|
|
63
|
+
packageSpec: result.packageSpec,
|
|
64
|
+
channel: result.status.channel,
|
|
65
|
+
fromVersion: result.status.currentVersion,
|
|
66
|
+
toVersion: result.targetVersion,
|
|
67
|
+
}, null, 2));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (result.action === 'noop') {
|
|
71
|
+
this.log(`NocoBase CLI is already up to date at ${result.status.currentVersion}.`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.log(`Updated NocoBase CLI from ${result.status.currentVersion} using ${result.packageSpec}${result.targetVersion ? ` (latest ${result.status.channel} resolves to ${result.targetVersion})` : ''}.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
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 { inspectSkillsStatus } from '../../lib/skills-manager.js';
|
|
11
|
+
import { printInfo, renderTable } from '../../lib/ui.js';
|
|
12
|
+
export default class SkillsCheck extends Command {
|
|
13
|
+
static summary = 'Check the NocoBase AI coding skills installed for this workspace';
|
|
14
|
+
static description = 'Inspect the current workspace for NocoBase AI coding skills and report whether they are managed by the CLI and whether an update is available.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
json: Flags.boolean({
|
|
21
|
+
description: 'Output the result as JSON',
|
|
22
|
+
default: false,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { flags } = await this.parse(SkillsCheck);
|
|
27
|
+
const status = await inspectSkillsStatus();
|
|
28
|
+
if (flags.json) {
|
|
29
|
+
this.log(JSON.stringify({
|
|
30
|
+
ok: true,
|
|
31
|
+
kind: 'skills',
|
|
32
|
+
workspaceRoot: status.workspaceRoot,
|
|
33
|
+
installed: status.installed,
|
|
34
|
+
managedByNb: status.managedByNb,
|
|
35
|
+
sourcePackage: status.sourcePackage,
|
|
36
|
+
installedSkillNames: status.installedSkillNames,
|
|
37
|
+
installedRef: status.installedRef,
|
|
38
|
+
latestRef: status.latestRef,
|
|
39
|
+
updateAvailable: status.updateAvailable,
|
|
40
|
+
recommendedCommand: status.installed ? 'nb skills update --yes' : 'nb skills install --yes',
|
|
41
|
+
registryError: status.registryError,
|
|
42
|
+
}, null, 2));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
this.log(renderTable(['Field', 'Value'], [
|
|
46
|
+
['Workspace', status.workspaceRoot],
|
|
47
|
+
['Installed', status.installed ? 'yes' : 'no'],
|
|
48
|
+
['Managed by nb', status.managedByNb ? 'yes' : 'no'],
|
|
49
|
+
['Installed skills', status.installedSkillNames.length ? status.installedSkillNames.join(', ') : '(none)'],
|
|
50
|
+
['Update available', status.updateAvailable === null ? 'unknown' : status.updateAvailable ? 'yes' : 'no'],
|
|
51
|
+
]));
|
|
52
|
+
if (!status.installed) {
|
|
53
|
+
printInfo('Run `nb skills install` to install the NocoBase AI coding skills for this workspace.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (status.updateAvailable) {
|
|
57
|
+
printInfo('Run `nb skills update` to refresh the NocoBase AI coding skills for this workspace.');
|
|
58
|
+
}
|
|
59
|
+
if (status.registryError) {
|
|
60
|
+
printInfo(`Update check warning: ${status.registryError}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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, loadHelpClass } from '@oclif/core';
|
|
10
|
+
export default class Skills extends Command {
|
|
11
|
+
static summary = 'Inspect or synchronize NocoBase AI coding skills for this workspace';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(Skills);
|
|
14
|
+
const Help = await loadHelpClass(this.config);
|
|
15
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? this.config.pjson.helpOptions).showHelp([
|
|
16
|
+
this.id ?? 'skills',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { confirmAction } from '../../lib/ui.js';
|
|
11
|
+
import { installNocoBaseSkills } from '../../lib/skills-manager.js';
|
|
12
|
+
export default class SkillsInstall extends Command {
|
|
13
|
+
static summary = 'Install the NocoBase AI coding skills for this workspace';
|
|
14
|
+
static description = 'Install the NocoBase AI coding skills for the current workspace. If they are already installed, this command does not update them.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --yes',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the install confirmation prompt',
|
|
24
|
+
default: false,
|
|
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(SkillsInstall);
|
|
33
|
+
if (!flags.yes) {
|
|
34
|
+
const confirmed = await confirmAction('Install the NocoBase AI coding skills for this workspace?', { defaultValue: true });
|
|
35
|
+
if (!confirmed) {
|
|
36
|
+
this.log('Skipped skills install.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const result = await installNocoBaseSkills();
|
|
41
|
+
if (flags.json) {
|
|
42
|
+
this.log(JSON.stringify({
|
|
43
|
+
ok: true,
|
|
44
|
+
kind: 'skills',
|
|
45
|
+
action: result.action,
|
|
46
|
+
workspaceRoot: result.status.workspaceRoot,
|
|
47
|
+
installedSkillNames: result.status.installedSkillNames,
|
|
48
|
+
installedRef: result.status.installedRef,
|
|
49
|
+
}, null, 2));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (result.action === 'noop') {
|
|
53
|
+
this.log('NocoBase AI coding skills are already installed for this workspace. Run `nb skills update` to refresh them.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.log('Installed the NocoBase AI coding skills for this workspace.');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
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 { confirmAction } from '../../lib/ui.js';
|
|
11
|
+
import { updateNocoBaseSkills } from '../../lib/skills-manager.js';
|
|
12
|
+
export default class SkillsUpdate extends Command {
|
|
13
|
+
static summary = 'Update the NocoBase AI coding skills for this workspace';
|
|
14
|
+
static description = 'Refresh the NocoBase AI coding skills for the current workspace. This command only updates an existing nocobase/skills install.';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --yes',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
yes: Flags.boolean({
|
|
22
|
+
char: 'y',
|
|
23
|
+
description: 'Skip the update confirmation prompt',
|
|
24
|
+
default: false,
|
|
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(SkillsUpdate);
|
|
33
|
+
if (!flags.yes) {
|
|
34
|
+
const confirmed = await confirmAction('Update the NocoBase AI coding skills for this workspace?', { defaultValue: true });
|
|
35
|
+
if (!confirmed) {
|
|
36
|
+
this.log('Skipped skills update.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const result = await updateNocoBaseSkills();
|
|
41
|
+
if (flags.json) {
|
|
42
|
+
this.log(JSON.stringify({
|
|
43
|
+
ok: true,
|
|
44
|
+
kind: 'skills',
|
|
45
|
+
action: result.action,
|
|
46
|
+
workspaceRoot: result.status.workspaceRoot,
|
|
47
|
+
installedSkillNames: result.status.installedSkillNames,
|
|
48
|
+
installedRef: result.status.installedRef,
|
|
49
|
+
}, null, 2));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (result.action === 'noop') {
|
|
53
|
+
this.log('NocoBase AI coding skills are already up to date for this workspace.');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.log('Updated the NocoBase AI coding skills for this workspace.');
|
|
57
|
+
}
|
|
58
|
+
}
|