@nocobase/cli 2.1.0-beta.23 → 2.1.0-beta.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.
Files changed (61) hide show
  1. package/README.md +24 -0
  2. package/README.zh-CN.md +4 -0
  3. package/dist/commands/app/down.js +12 -6
  4. package/dist/commands/app/logs.js +2 -2
  5. package/dist/commands/app/start.js +2 -1
  6. package/dist/commands/app/stop.js +2 -1
  7. package/dist/commands/app/upgrade.js +116 -129
  8. package/dist/commands/config/delete.js +30 -0
  9. package/dist/commands/config/get.js +29 -0
  10. package/dist/commands/config/index.js +20 -0
  11. package/dist/commands/config/list.js +29 -0
  12. package/dist/commands/config/set.js +35 -0
  13. package/dist/commands/db/check.js +238 -0
  14. package/dist/commands/db/logs.js +2 -2
  15. package/dist/commands/db/shared.js +6 -5
  16. package/dist/commands/db/start.js +2 -1
  17. package/dist/commands/db/stop.js +2 -1
  18. package/dist/commands/env/info.js +6 -2
  19. package/dist/commands/env/shared.js +1 -1
  20. package/dist/commands/init.js +0 -1
  21. package/dist/commands/install.js +87 -35
  22. package/dist/commands/license/activate.js +360 -0
  23. package/dist/commands/license/env.js +94 -0
  24. package/dist/commands/license/generate-id.js +108 -0
  25. package/dist/commands/license/id.js +56 -0
  26. package/dist/commands/license/index.js +20 -0
  27. package/dist/commands/license/plugins/clean.js +101 -0
  28. package/dist/commands/license/plugins/index.js +20 -0
  29. package/dist/commands/license/plugins/list.js +50 -0
  30. package/dist/commands/license/plugins/shared.js +325 -0
  31. package/dist/commands/license/plugins/sync.js +269 -0
  32. package/dist/commands/license/shared.js +414 -0
  33. package/dist/commands/license/status.js +50 -0
  34. package/dist/commands/plugin/disable.js +2 -0
  35. package/dist/commands/plugin/enable.js +2 -0
  36. package/dist/commands/source/dev.js +2 -1
  37. package/dist/lib/api-client.js +74 -3
  38. package/dist/lib/app-managed-resources.js +10 -6
  39. package/dist/lib/app-runtime.js +29 -11
  40. package/dist/lib/auth-store.js +36 -68
  41. package/dist/lib/bootstrap.js +0 -4
  42. package/dist/lib/build-config.js +8 -0
  43. package/dist/lib/builtin-db.js +86 -0
  44. package/dist/lib/cli-config.js +176 -0
  45. package/dist/lib/cli-home.js +6 -21
  46. package/dist/lib/db-connection-check.js +178 -0
  47. package/dist/lib/env-config.js +7 -0
  48. package/dist/lib/generated-command.js +24 -3
  49. package/dist/lib/plugin-storage.js +127 -0
  50. package/dist/lib/prompt-validators.js +4 -4
  51. package/dist/lib/run-npm.js +53 -0
  52. package/dist/lib/runtime-env-vars.js +32 -0
  53. package/dist/lib/runtime-generator.js +89 -10
  54. package/dist/lib/self-manager.js +57 -2
  55. package/dist/lib/skills-manager.js +2 -2
  56. package/dist/lib/startup-update.js +85 -7
  57. package/dist/lib/ui.js +3 -0
  58. package/dist/locale/en-US.json +16 -13
  59. package/dist/locale/zh-CN.json +16 -13
  60. package/nocobase-ctl.config.json +82 -0
  61. package/package.json +16 -4
@@ -0,0 +1,94 @@
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 { getEnvAsync } from '@nocobase/license-kit';
11
+ import { validateTcpPort } from "../../lib/prompt-validators.js";
12
+ import { licenseJsonFlag, withLicenseEnvVars } from './shared.js';
13
+ function trimValue(value) {
14
+ const text = String(value ?? '').trim();
15
+ return text || undefined;
16
+ }
17
+ function formatMissingFieldsMessage(missing) {
18
+ return [
19
+ 'Missing database settings for license environment inspection.',
20
+ `Required: ${missing.join(', ')}.`,
21
+ 'Pass all required `--db-*` flags explicitly.',
22
+ ].join('\n');
23
+ }
24
+ export default class LicenseEnv extends Command {
25
+ static hidden = true;
26
+ static flags = {
27
+ 'db-dialect': Flags.string({
28
+ description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
29
+ options: ['postgres', 'kingbase', 'mysql', 'mariadb'],
30
+ }),
31
+ 'db-host': Flags.string({
32
+ description: 'Database host name or IP address.',
33
+ }),
34
+ 'db-port': Flags.string({
35
+ description: 'Database TCP port.',
36
+ }),
37
+ 'db-database': Flags.string({
38
+ description: 'Database name.',
39
+ }),
40
+ 'db-user': Flags.string({
41
+ description: 'Database username.',
42
+ }),
43
+ 'db-password': Flags.string({
44
+ description: 'Database password.',
45
+ }),
46
+ json: licenseJsonFlag,
47
+ };
48
+ async run() {
49
+ const { flags } = await this.parse(LicenseEnv);
50
+ const envVars = {
51
+ DB_DIALECT: trimValue(flags['db-dialect']),
52
+ DB_HOST: trimValue(flags['db-host']),
53
+ DB_PORT: trimValue(flags['db-port']),
54
+ DB_DATABASE: trimValue(flags['db-database']),
55
+ DB_USER: trimValue(flags['db-user']),
56
+ DB_PASSWORD: flags['db-password'] !== undefined ? String(flags['db-password']) : undefined,
57
+ };
58
+ const missing = [];
59
+ if (!envVars.DB_DIALECT) {
60
+ missing.push('--db-dialect');
61
+ }
62
+ if (!envVars.DB_HOST) {
63
+ missing.push('--db-host');
64
+ }
65
+ if (!envVars.DB_PORT) {
66
+ missing.push('--db-port');
67
+ }
68
+ if (!envVars.DB_DATABASE) {
69
+ missing.push('--db-database');
70
+ }
71
+ if (!envVars.DB_USER) {
72
+ missing.push('--db-user');
73
+ }
74
+ if (!envVars.DB_PASSWORD) {
75
+ missing.push('--db-password');
76
+ }
77
+ if (missing.length > 0) {
78
+ this.error(formatMissingFieldsMessage(missing));
79
+ }
80
+ const portError = validateTcpPort(envVars.DB_PORT);
81
+ if (portError) {
82
+ this.error(portError);
83
+ }
84
+ const env = await withLicenseEnvVars(envVars, async () => await getEnvAsync());
85
+ if (flags.json) {
86
+ this.log(JSON.stringify({
87
+ ok: true,
88
+ env,
89
+ }, null, 2));
90
+ return;
91
+ }
92
+ this.log(JSON.stringify(env, null, 2));
93
+ }
94
+ }
@@ -0,0 +1,108 @@
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 { validateTcpPort } from "../../lib/prompt-validators.js";
11
+ import { generateValidatedInstanceIdFromEnvVars, licenseJsonFlag } from './shared.js';
12
+ function trimValue(value) {
13
+ const text = String(value ?? '').trim();
14
+ return text || undefined;
15
+ }
16
+ function formatMissingFieldsMessage(missing) {
17
+ return [
18
+ 'Missing database settings for instance ID generation.',
19
+ `Required: ${missing.join(', ')}.`,
20
+ 'Pass all required `--db-*` flags explicitly.',
21
+ ].join('\n');
22
+ }
23
+ export default class LicenseGenerateId extends Command {
24
+ static hidden = true;
25
+ static summary = 'Generate a commercial license instance ID from explicit database settings';
26
+ static description = 'Generate the commercial licensing instance ID from explicit `--db-*` flags. This command only prints the generated ID and does not save it.';
27
+ static examples = [
28
+ '<%= config.bin %> <%= command.id %> --db-dialect postgres --db-host 127.0.0.1 --db-port 5432 --db-database nocobase --db-user nocobase --db-password secret',
29
+ '<%= config.bin %> <%= command.id %> --db-dialect postgres --db-host 127.0.0.1 --db-port 5432 --db-database nocobase --db-user nocobase --db-password secret --json',
30
+ ];
31
+ static flags = {
32
+ 'db-dialect': Flags.string({
33
+ description: 'Database dialect: postgres, kingbase, mysql, or mariadb.',
34
+ options: ['postgres', 'kingbase', 'mysql', 'mariadb'],
35
+ required: false,
36
+ }),
37
+ 'db-host': Flags.string({
38
+ description: 'Database host name or IP address.',
39
+ }),
40
+ 'db-port': Flags.string({
41
+ description: 'Database TCP port.',
42
+ }),
43
+ 'db-database': Flags.string({
44
+ description: 'Database name.',
45
+ }),
46
+ 'db-user': Flags.string({
47
+ description: 'Database username.',
48
+ }),
49
+ 'db-password': Flags.string({
50
+ description: 'Database password.',
51
+ }),
52
+ json: licenseJsonFlag,
53
+ };
54
+ async run() {
55
+ const { flags } = await this.parse(LicenseGenerateId);
56
+ const dbConfig = {
57
+ dbDialect: trimValue(flags['db-dialect']),
58
+ dbHost: trimValue(flags['db-host']),
59
+ dbPort: trimValue(flags['db-port']),
60
+ dbDatabase: trimValue(flags['db-database']),
61
+ dbUser: trimValue(flags['db-user']),
62
+ dbPassword: flags['db-password'] !== undefined ? String(flags['db-password']) : undefined,
63
+ };
64
+ const missing = [];
65
+ if (!dbConfig.dbDialect) {
66
+ missing.push('--db-dialect');
67
+ }
68
+ if (!dbConfig.dbHost) {
69
+ missing.push('--db-host');
70
+ }
71
+ if (!dbConfig.dbPort) {
72
+ missing.push('--db-port');
73
+ }
74
+ if (!dbConfig.dbDatabase) {
75
+ missing.push('--db-database');
76
+ }
77
+ if (!dbConfig.dbUser) {
78
+ missing.push('--db-user');
79
+ }
80
+ if (!dbConfig.dbPassword) {
81
+ missing.push('--db-password');
82
+ }
83
+ if (missing.length > 0) {
84
+ this.error(formatMissingFieldsMessage(missing));
85
+ }
86
+ const portError = validateTcpPort(dbConfig.dbPort);
87
+ if (portError) {
88
+ this.error(portError);
89
+ }
90
+ const envVars = {
91
+ DB_DIALECT: dbConfig.dbDialect,
92
+ DB_HOST: dbConfig.dbHost,
93
+ DB_PORT: dbConfig.dbPort,
94
+ DB_DATABASE: dbConfig.dbDatabase,
95
+ DB_USER: dbConfig.dbUser,
96
+ DB_PASSWORD: dbConfig.dbPassword,
97
+ };
98
+ const instanceId = await generateValidatedInstanceIdFromEnvVars(envVars);
99
+ if (flags.json) {
100
+ this.log(JSON.stringify({
101
+ ok: true,
102
+ instanceId,
103
+ }, null, 2));
104
+ return;
105
+ }
106
+ this.log(instanceId);
107
+ }
108
+ }
@@ -0,0 +1,56 @@
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 { generateAndSaveInstanceId, licenseEnvFlag, licenseJsonFlag, readSavedInstanceId, requireLicenseRuntime, resolveInstanceIdFile, } from './shared.js';
11
+ import { announceTargetEnv } from '../../lib/ui.js';
12
+ export default class LicenseId extends Command {
13
+ static summary = 'Show the instance ID for the selected env';
14
+ static description = 'Show the commercial licensing instance ID for the selected env, generating and saving it if needed.';
15
+ static examples = [
16
+ '<%= config.bin %> <%= command.id %>',
17
+ '<%= config.bin %> <%= command.id %> --env app1',
18
+ '<%= config.bin %> <%= command.id %> --env app1 --force',
19
+ '<%= config.bin %> <%= command.id %> --env app1 --json',
20
+ ];
21
+ static flags = {
22
+ env: licenseEnvFlag,
23
+ json: licenseJsonFlag,
24
+ force: Flags.boolean({
25
+ description: 'Force regenerate the instance ID even if one is already saved',
26
+ default: false,
27
+ }),
28
+ };
29
+ async run() {
30
+ const { flags } = await this.parse(LicenseId);
31
+ const runtime = await requireLicenseRuntime(flags.env);
32
+ if (!flags.json) {
33
+ announceTargetEnv(runtime.envName);
34
+ }
35
+ const savedBefore = await readSavedInstanceId(runtime);
36
+ const shouldGenerate = Boolean(flags.force) || !savedBefore;
37
+ const instanceId = shouldGenerate
38
+ ? await generateAndSaveInstanceId(runtime)
39
+ : savedBefore;
40
+ const filePath = resolveInstanceIdFile(runtime);
41
+ const generated = shouldGenerate;
42
+ if (flags.json) {
43
+ this.log(JSON.stringify({
44
+ ok: true,
45
+ env: runtime.envName,
46
+ kind: runtime.kind,
47
+ instanceId,
48
+ filePath,
49
+ generated,
50
+ }, null, 2));
51
+ return;
52
+ }
53
+ this.log(`Instance ID for env "${runtime.envName}": ${instanceId}`);
54
+ this.log(`${generated ? 'Saved' : 'Loaded'} instance ID at ${filePath}`);
55
+ }
56
+ }
@@ -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 License extends Command {
11
+ static summary = 'Manage NocoBase commercial licensing';
12
+ async run() {
13
+ await this.parse(License);
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 ?? 'license',
17
+ ...this.argv,
18
+ ]);
19
+ }
20
+ }
@@ -0,0 +1,101 @@
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 pc from 'picocolors';
11
+ import { licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, requireLicenseRuntime } from '../shared.js';
12
+ import { cleanLicensedPlugins } from './shared.js';
13
+ import { resolvePluginStoragePath } from '../../../lib/plugin-storage.js';
14
+ import { announceTargetEnv } from '../../../lib/ui.js';
15
+ function formatActionLabel(action) {
16
+ switch (action) {
17
+ case 'removed':
18
+ return pc.yellow('removed');
19
+ case 'skipped':
20
+ return pc.dim('skipped');
21
+ }
22
+ }
23
+ export default class LicensePluginsClean extends Command {
24
+ static summary = 'Remove downloaded commercial plugins for the selected env';
25
+ static description = 'Remove local commercial plugin downloads for the selected env without changing license activation.';
26
+ static examples = [
27
+ '<%= config.bin %> <%= command.id %>',
28
+ '<%= config.bin %> <%= command.id %> --env app1',
29
+ '<%= config.bin %> <%= command.id %> --env app1 --dry-run',
30
+ '<%= config.bin %> <%= command.id %> --env app1 --verbose',
31
+ '<%= config.bin %> <%= command.id %> --env app1 --json',
32
+ ];
33
+ static flags = {
34
+ env: licenseEnvFlag,
35
+ json: licenseJsonFlag,
36
+ 'pkg-url': licensePkgUrlFlag,
37
+ 'dry-run': Flags.boolean({
38
+ description: 'Preview which commercial plugins would be removed without deleting anything',
39
+ default: false,
40
+ }),
41
+ verbose: Flags.boolean({
42
+ description: 'Show detailed per-plugin clean logs',
43
+ default: false,
44
+ }),
45
+ };
46
+ async run() {
47
+ const { flags } = await this.parse(LicensePluginsClean);
48
+ const runtime = await requireLicenseRuntime(flags.env);
49
+ if (!flags.json) {
50
+ announceTargetEnv(runtime.envName);
51
+ }
52
+ const shouldStreamLogs = !flags.json && Boolean(flags.verbose);
53
+ const pluginStoragePath = resolvePluginStoragePath(runtime.env.storagePath);
54
+ if (!flags.json) {
55
+ this.log(pc.bold(flags['dry-run']
56
+ ? `Commercial plugin clean preview for env "${runtime.envName}"`
57
+ : `Commercial plugin clean for env "${runtime.envName}"`));
58
+ this.log(pc.dim(`Plugin storage path: ${pluginStoragePath}`));
59
+ }
60
+ const result = await cleanLicensedPlugins(runtime, {
61
+ pkgUrl: flags['pkg-url'],
62
+ dryRun: Boolean(flags['dry-run']),
63
+ onProgress: shouldStreamLogs
64
+ ? async (detail) => {
65
+ this.log(`${formatActionLabel(detail.action)} ${pc.bold(detail.packageName)}`);
66
+ this.log(pc.dim(` output: ${detail.outputDir}`));
67
+ if (detail.action === 'removed') {
68
+ this.log(pc.dim(` symlink: ${detail.removedSymlink ? 'removed' : 'not found'}`));
69
+ }
70
+ }
71
+ : undefined,
72
+ });
73
+ const payload = {
74
+ ok: true,
75
+ env: runtime.envName,
76
+ kind: runtime.kind,
77
+ dryRun: Boolean(flags['dry-run']),
78
+ ...result,
79
+ };
80
+ if (flags.json) {
81
+ this.log(JSON.stringify(payload, null, 2));
82
+ return;
83
+ }
84
+ if (!flags.verbose) {
85
+ const changes = [];
86
+ if (result.removed.length > 0) {
87
+ changes.push(pc.yellow(`${result.removed.length} removed`));
88
+ }
89
+ if (result.skipped.length > 0) {
90
+ changes.push(pc.dim(`${result.skipped.length} skipped`));
91
+ }
92
+ if (changes.length === 0) {
93
+ changes.push(pc.dim('no plugin changes'));
94
+ }
95
+ this.log(`Result: ${changes.join(', ')}`);
96
+ }
97
+ else {
98
+ this.log(`Summary: ${result.removed.length} removed, ${result.skipped.length} skipped`);
99
+ }
100
+ }
101
+ }
@@ -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 LicensePlugins extends Command {
11
+ static summary = 'List or synchronize commercial plugins allowed by the current license';
12
+ async run() {
13
+ await this.parse(LicensePlugins);
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 ?? 'license plugins',
17
+ ...this.argv,
18
+ ]);
19
+ }
20
+ }
@@ -0,0 +1,50 @@
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 } from '@oclif/core';
10
+ import { licenseEnvFlag, licenseJsonFlag, licensePkgUrlFlag, requireLicenseRuntime } from '../shared.js';
11
+ import { fetchLicensedPluginPackages } from './shared.js';
12
+ import { renderTable } from '../../../lib/ui.js';
13
+ export default class LicensePluginsList extends Command {
14
+ static summary = 'List commercial plugins available to the selected env';
15
+ static description = 'Show the commercial plugins associated with the current saved license key for the selected env.';
16
+ static examples = [
17
+ '<%= config.bin %> <%= command.id %>',
18
+ '<%= config.bin %> <%= command.id %> --env app1',
19
+ '<%= config.bin %> <%= command.id %> --env app1 --json',
20
+ ];
21
+ static flags = {
22
+ env: licenseEnvFlag,
23
+ json: licenseJsonFlag,
24
+ 'pkg-url': licensePkgUrlFlag,
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(LicensePluginsList);
28
+ const runtime = await requireLicenseRuntime(flags.env);
29
+ const { commercialPlugins, licensedPlugins } = await fetchLicensedPluginPackages(runtime, {
30
+ pkgUrl: flags['pkg-url'],
31
+ });
32
+ const payload = {
33
+ ok: true,
34
+ env: runtime.envName,
35
+ kind: runtime.kind,
36
+ commercialPlugins,
37
+ licensedPlugins,
38
+ };
39
+ if (flags.json) {
40
+ this.log(JSON.stringify(payload, null, 2));
41
+ return;
42
+ }
43
+ const rows = commercialPlugins.map((pluginName) => [
44
+ pluginName,
45
+ licensedPlugins.includes(pluginName) ? 'licensed' : 'unlicensed',
46
+ ]);
47
+ this.log(`Commercial plugins for env "${runtime.envName}"`);
48
+ this.log(renderTable(['Package', 'Status'], rows));
49
+ }
50
+ }