@nocobase/cli 2.1.0-alpha.2 → 2.1.0-alpha.20
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/LICENSE.txt +107 -0
- package/README.md +165 -19
- package/bin/run.cmd +3 -0
- package/bin/run.js +95 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/build.js +51 -0
- package/dist/commands/db/start.js +22 -0
- package/dist/commands/dev.js +58 -0
- package/dist/commands/download.js +293 -0
- package/dist/commands/env/add.js +198 -0
- package/dist/commands/env/auth.js +61 -0
- package/dist/commands/env/list.js +41 -0
- package/dist/commands/env/remove.js +65 -0
- package/dist/commands/env/update.js +73 -0
- package/dist/commands/env/use.js +36 -0
- package/dist/commands/init.js +186 -0
- package/dist/commands/install.js +703 -0
- package/dist/commands/pm/disable.js +31 -0
- package/dist/commands/pm/enable.js +31 -0
- package/dist/commands/pm/list.js +21 -0
- package/dist/commands/restart.js +32 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/start.js +55 -0
- package/dist/commands/upgrade.js +35 -0
- package/dist/generated/command-registry.js +133 -0
- package/dist/help/runtime-help.js +20 -0
- package/dist/lib/api-client.js +199 -0
- package/dist/lib/auth-store.js +200 -0
- package/dist/lib/bootstrap.js +383 -0
- package/dist/lib/build-config.js +10 -0
- package/dist/lib/cli-home.js +30 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/env-auth.js +527 -0
- package/dist/lib/generated-command.js +142 -0
- package/dist/lib/init-browser-wizard.js +431 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/resource-command.js +335 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +59 -0
- package/dist/lib/runtime-generator.js +408 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/ui.js +175 -0
- package/dist/post-processors/data-modeling.js +66 -0
- package/dist/post-processors/data-source-manager.js +114 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +287 -0
- package/package.json +52 -26
- package/LICENSE +0 -661
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/cli.js +0 -19
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/index.js +0 -17
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/index.js +0 -14
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/models/index.ts +0 -12
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,31 @@
|
|
|
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 { Args, Command } from '@oclif/core';
|
|
10
|
+
export default class PmDisable extends Command {
|
|
11
|
+
static args = {
|
|
12
|
+
packages: Args.string({
|
|
13
|
+
required: true,
|
|
14
|
+
multiple: true,
|
|
15
|
+
description: 'Plugin package name(s) to disable (e.g. `@nocobase/plugin-sample`). Pass one or more names as separate arguments.',
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
static description = 'Disable one or more plugins';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> @nocobase/plugin-sample',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> @nocobase/plugin-a @nocobase/plugin-b',
|
|
22
|
+
];
|
|
23
|
+
async run() {
|
|
24
|
+
const { args } = await this.parse(PmDisable);
|
|
25
|
+
const packages = args.packages;
|
|
26
|
+
if (!Array.isArray(packages) || packages.length === 0) {
|
|
27
|
+
this.error('Pass at least one plugin package name.');
|
|
28
|
+
}
|
|
29
|
+
await this.config.runCommand('api:pm:disable', ['--await-response', '--filter-by-tk', packages.join(',')]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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 { Args, Command } from '@oclif/core';
|
|
10
|
+
export default class PmEnable extends Command {
|
|
11
|
+
static args = {
|
|
12
|
+
packages: Args.string({
|
|
13
|
+
required: true,
|
|
14
|
+
multiple: true,
|
|
15
|
+
description: 'Plugin package name(s) to enable (e.g. `@nocobase/plugin-sample`). Pass one or more names as separate arguments.',
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
static description = 'Enable one or more plugins';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> @nocobase/plugin-sample',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> @nocobase/plugin-a @nocobase/plugin-b',
|
|
22
|
+
];
|
|
23
|
+
async run() {
|
|
24
|
+
const { args } = await this.parse(PmEnable);
|
|
25
|
+
const packages = args.packages;
|
|
26
|
+
if (!Array.isArray(packages) || packages.length === 0) {
|
|
27
|
+
this.error('Pass at least one plugin package name.');
|
|
28
|
+
}
|
|
29
|
+
await this.config.runCommand('api:pm:enable', ['--await-response', '--filter-by-tk', packages.join(',')]);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
export default class PmList extends Command {
|
|
11
|
+
static args = {};
|
|
12
|
+
static summary = 'List all plugins';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> <%= command.id %>',
|
|
15
|
+
];
|
|
16
|
+
static flags = {};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(PmList);
|
|
19
|
+
await this.config.runCommand('api:pm:list', ['--mode=summary']);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { Args, Command, Flags } from '@oclif/core';
|
|
10
|
+
export default class Restart extends Command {
|
|
11
|
+
static args = {
|
|
12
|
+
file: Args.string({ description: 'file to read' }),
|
|
13
|
+
};
|
|
14
|
+
static description = 'describe the command here';
|
|
15
|
+
static examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %>',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
// flag with no value (-f, --force)
|
|
20
|
+
force: Flags.boolean({ char: 'f' }),
|
|
21
|
+
// flag with a value (-n, --name=VALUE)
|
|
22
|
+
name: Flags.string({ char: 'n', description: 'name to print' }),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { args, flags } = await this.parse(Restart);
|
|
26
|
+
const name = flags.name ?? 'world';
|
|
27
|
+
this.log(`hello ${name} from packages/core/cli/src/commands/restart.ts`);
|
|
28
|
+
if (args.file && flags.force) {
|
|
29
|
+
this.log(`you input --force and --file: ${args.file}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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 { Args, Command, Flags } from '@oclif/core';
|
|
10
|
+
import { runNocoBaseCommand } from "../../lib/run-npm.js";
|
|
11
|
+
export default class ScaffoldMigration extends Command {
|
|
12
|
+
static args = {
|
|
13
|
+
name: Args.string({ description: 'migration name', required: true }),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Run the legacy NocoBase scaffold migration (forwards to `npm run scaffold:migration` in the repo root)';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> <%= command.id %> migration-name --pkg @nocobase/plugin-acl',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> migration-name --pkg @nocobase/plugin-acl --on afterLoad',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
pkg: Flags.string({ description: 'plugin package name', required: true }),
|
|
22
|
+
on: Flags.string({ description: 'on', required: false, options: ['beforeLoad', 'afterSync', 'afterLoad'] }),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
const { args, flags } = await this.parse(ScaffoldMigration);
|
|
26
|
+
const npmArgs = ['create-migration', args.name, '--pkg', flags.pkg];
|
|
27
|
+
if (flags.on) {
|
|
28
|
+
npmArgs.push('--on', flags.on);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
await runNocoBaseCommand(npmArgs, { env: { LOGGER_SILENT: 'true' } });
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
+
this.error(message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -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
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
+
import { runNocoBaseCommand } from "../../lib/run-npm.js";
|
|
11
|
+
export default class ScaffoldPlugin extends Command {
|
|
12
|
+
static args = {
|
|
13
|
+
pkg: Args.string({ description: 'plugin package name', required: true }),
|
|
14
|
+
};
|
|
15
|
+
static description = 'Run the legacy NocoBase scaffold plugin (forwards to `npm run scaffold:plugin` in the repo root)';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> <%= command.id %> @nocobase-example/plugin-hello',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> @nocobase-example/plugin-hello --force-recreate',
|
|
19
|
+
];
|
|
20
|
+
static flags = {
|
|
21
|
+
'force-recreate': Flags.boolean({ description: 'Force recreate the plugin', char: 'f', required: false }),
|
|
22
|
+
};
|
|
23
|
+
async run() {
|
|
24
|
+
const { args, flags } = await this.parse(ScaffoldPlugin);
|
|
25
|
+
const npmArgs = ['pm', 'create', args.pkg];
|
|
26
|
+
if (flags['force-recreate']) {
|
|
27
|
+
npmArgs.push('--force-recreate');
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
await runNocoBaseCommand(npmArgs, { env: { LOGGER_SILENT: 'true' } });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
+
this.error(message);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 { runNocoBaseCommand } from "../lib/run-npm.js";
|
|
11
|
+
export default class Start extends Command {
|
|
12
|
+
static description = 'Run the legacy NocoBase start (forwards to `npm run start` in the repo root)';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> <%= command.id %>',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --quickstart',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> --port 12000',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --daemon',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --instances 2',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --launch-mode pm2',
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
env: Flags.string({ description: 'Environment', char: 'e', required: false }),
|
|
23
|
+
quickstart: Flags.boolean({ description: 'Quickstart the application', required: false }),
|
|
24
|
+
port: Flags.string({ description: 'Port', char: 'p', required: false }),
|
|
25
|
+
daemon: Flags.boolean({ description: 'Run the application as a daemon', char: 'd', required: false }),
|
|
26
|
+
instances: Flags.integer({ description: 'Number of instances to run', char: 'i', required: false }),
|
|
27
|
+
'launch-mode': Flags.string({ description: 'Launch Mode', required: false, options: ['pm2', 'node'] }),
|
|
28
|
+
};
|
|
29
|
+
async run() {
|
|
30
|
+
const { flags } = await this.parse(Start);
|
|
31
|
+
const npmArgs = ['start'];
|
|
32
|
+
if (flags.quickstart) {
|
|
33
|
+
npmArgs.push('--quickstart');
|
|
34
|
+
}
|
|
35
|
+
if (flags.port) {
|
|
36
|
+
npmArgs.push('--port', flags.port);
|
|
37
|
+
}
|
|
38
|
+
if (flags.daemon) {
|
|
39
|
+
npmArgs.push('--daemon');
|
|
40
|
+
}
|
|
41
|
+
if (flags.instances) {
|
|
42
|
+
npmArgs.push('--instances', flags.instances.toString());
|
|
43
|
+
}
|
|
44
|
+
if (flags['launch-mode']) {
|
|
45
|
+
npmArgs.push('--launch-mode', flags['launch-mode']);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
await runNocoBaseCommand(npmArgs);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
this.error(message);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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 { runNocoBaseCommand } from "../lib/run-npm.js";
|
|
11
|
+
export default class Upgrade extends Command {
|
|
12
|
+
static description = 'Run the legacy NocoBase upgrade (forwards to `npm run upgrade` in the repo root)';
|
|
13
|
+
static examples = [
|
|
14
|
+
'<%= config.bin %> <%= command.id %>',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --skip-code-update',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
env: Flags.string({ description: 'Environment', char: 'e', required: false }),
|
|
19
|
+
'skip-code-update': Flags.boolean({ description: 'Skip code update', char: 'S', required: false }),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { flags } = await this.parse(Upgrade);
|
|
23
|
+
const npmArgs = ['upgrade'];
|
|
24
|
+
if (flags['skip-code-update']) {
|
|
25
|
+
npmArgs.push('--skip-code-update');
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
await runNocoBaseCommand(npmArgs);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
+
this.error(message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
10
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
11
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
12
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return path;
|
|
16
|
+
};
|
|
17
|
+
import { Command, loadHelpClass } from '@oclif/core';
|
|
18
|
+
import { dirname, join, relative } from 'node:path';
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
20
|
+
import { collectCommandModulePaths, commandRelativePathToRegistryKey, } from "../lib/command-discovery.js";
|
|
21
|
+
import { getCurrentEnvName, getEnv } from "../lib/auth-store.js";
|
|
22
|
+
import { createGeneratedFlags, GeneratedApiCommand } from "../lib/generated-command.js";
|
|
23
|
+
import { toKebabCase } from "../lib/naming.js";
|
|
24
|
+
import { loadRuntimeSync } from "../lib/runtime-store.js";
|
|
25
|
+
const registryFilePath = fileURLToPath(import.meta.url);
|
|
26
|
+
const commandsRoot = join(dirname(registryFilePath), '../commands');
|
|
27
|
+
const commandModuleExtension = registryFilePath.endsWith('.ts') ? '.ts' : '.js';
|
|
28
|
+
async function loadCommandsFromDirectory() {
|
|
29
|
+
const absolutePaths = await collectCommandModulePaths(commandsRoot, commandModuleExtension);
|
|
30
|
+
const entries = await Promise.all(absolutePaths.map(async (absolutePath) => {
|
|
31
|
+
const rel = relative(commandsRoot, absolutePath).replace(/\\/g, '/');
|
|
32
|
+
const key = commandRelativePathToRegistryKey(rel);
|
|
33
|
+
const mod = await import(__rewriteRelativeImportExtension(pathToFileURL(absolutePath).href));
|
|
34
|
+
return [key, mod.default];
|
|
35
|
+
}));
|
|
36
|
+
return Object.fromEntries(entries);
|
|
37
|
+
}
|
|
38
|
+
function readEnvName(argv) {
|
|
39
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
40
|
+
const token = argv[index];
|
|
41
|
+
if (token === '--env') {
|
|
42
|
+
return argv[index + 1];
|
|
43
|
+
}
|
|
44
|
+
if (token === '-e') {
|
|
45
|
+
return argv[index + 1];
|
|
46
|
+
}
|
|
47
|
+
if (token.startsWith('--env=')) {
|
|
48
|
+
return token.slice('--env='.length);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
function createRuntimeCommand(operation) {
|
|
54
|
+
return class RuntimeCommand extends GeneratedApiCommand {
|
|
55
|
+
static summary = operation.summary;
|
|
56
|
+
static description = operation.description;
|
|
57
|
+
static examples = operation.examples;
|
|
58
|
+
static flags = createGeneratedFlags(operation);
|
|
59
|
+
static operation = operation;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createRuntimeIndexCommand(commandId, metadata) {
|
|
63
|
+
const summary = metadata.summary || `Work with ${commandId}`;
|
|
64
|
+
const description = metadata.description && metadata.description !== summary ? metadata.description : undefined;
|
|
65
|
+
return class RuntimeIndexCommand extends Command {
|
|
66
|
+
static summary = summary;
|
|
67
|
+
static description = description;
|
|
68
|
+
async run() {
|
|
69
|
+
await this.parse(RuntimeIndexCommand);
|
|
70
|
+
const Help = await loadHelpClass(this.config);
|
|
71
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? this.config.pjson.helpOptions).showHelp([
|
|
72
|
+
this.id ?? commandId.replaceAll(' ', ':'),
|
|
73
|
+
...this.argv,
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function getRuntimeTopicEntries(operation) {
|
|
79
|
+
const commandSegments = operation.commandId.split(' ');
|
|
80
|
+
const topLevelCommandId = commandSegments[0];
|
|
81
|
+
const modulePrefix = toKebabCase(operation.moduleDisplayName || operation.moduleName || '');
|
|
82
|
+
const isTopLevelResource = Boolean(topLevelCommandId && modulePrefix && topLevelCommandId !== modulePrefix);
|
|
83
|
+
const entries = [];
|
|
84
|
+
if (!topLevelCommandId) {
|
|
85
|
+
return entries;
|
|
86
|
+
}
|
|
87
|
+
if (isTopLevelResource) {
|
|
88
|
+
entries.push([
|
|
89
|
+
topLevelCommandId,
|
|
90
|
+
{
|
|
91
|
+
summary: operation.resourceDescription || operation.resourceDisplayName,
|
|
92
|
+
description: operation.resourceDescription,
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
return entries;
|
|
96
|
+
}
|
|
97
|
+
entries.push([
|
|
98
|
+
topLevelCommandId,
|
|
99
|
+
{
|
|
100
|
+
summary: operation.moduleDescription || operation.moduleDisplayName || operation.moduleName,
|
|
101
|
+
description: operation.moduleDescription,
|
|
102
|
+
},
|
|
103
|
+
]);
|
|
104
|
+
const resourceCommandId = commandSegments.slice(0, 2).join(' ');
|
|
105
|
+
if (commandSegments[1]) {
|
|
106
|
+
entries.push([
|
|
107
|
+
resourceCommandId,
|
|
108
|
+
{
|
|
109
|
+
summary: operation.resourceDescription || operation.resourceDisplayName,
|
|
110
|
+
description: operation.resourceDescription,
|
|
111
|
+
},
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
return entries;
|
|
115
|
+
}
|
|
116
|
+
const registry = {
|
|
117
|
+
...(await loadCommandsFromDirectory()),
|
|
118
|
+
};
|
|
119
|
+
const envName = readEnvName(process.argv.slice(2)) ?? (await getCurrentEnvName());
|
|
120
|
+
const env = await getEnv(envName);
|
|
121
|
+
const runtime = loadRuntimeSync(env?.runtime?.version);
|
|
122
|
+
for (const operation of runtime?.commands ?? []) {
|
|
123
|
+
const commandSegments = operation.commandId.split(' ');
|
|
124
|
+
const commandKey = commandSegments.join(':');
|
|
125
|
+
registry[`api:${commandKey}`] = createRuntimeCommand(operation);
|
|
126
|
+
for (const [topicCommandId, metadata] of getRuntimeTopicEntries(operation)) {
|
|
127
|
+
const topicKey = `api:${topicCommandId.split(' ').join(':')}`;
|
|
128
|
+
if (!registry[topicKey]) {
|
|
129
|
+
registry[topicKey] = createRuntimeIndexCommand(`api ${topicCommandId}`, metadata);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export default registry;
|
|
@@ -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 { Help } from '@oclif/core';
|
|
10
|
+
export function isTopicIndexCommand(commandId, topics) {
|
|
11
|
+
if (!commandId) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return topics.some((topic) => topic.name.startsWith(`${commandId}:`));
|
|
15
|
+
}
|
|
16
|
+
export default class RuntimeHelp extends Help {
|
|
17
|
+
get sortedCommands() {
|
|
18
|
+
return super.sortedCommands.filter((command) => !isTopicIndexCommand(command.id, this.config.topics));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { resolveServerRequestTarget } from './env-auth.js';
|
|
3
|
+
function normalizeBaseUrl(baseUrl) {
|
|
4
|
+
return baseUrl.replace(/\/+$/, '');
|
|
5
|
+
}
|
|
6
|
+
async function parseResponse(response) {
|
|
7
|
+
const text = await response.text();
|
|
8
|
+
let data = text;
|
|
9
|
+
if (text) {
|
|
10
|
+
try {
|
|
11
|
+
data = JSON.parse(text);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
data = text;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
ok: response.ok,
|
|
19
|
+
status: response.status,
|
|
20
|
+
data,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function parseScalarValue(value, type) {
|
|
24
|
+
if (value === undefined) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (type === 'boolean') {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
if (type === 'integer' || type === 'number') {
|
|
31
|
+
return Number(value);
|
|
32
|
+
}
|
|
33
|
+
if (typeof value !== 'string') {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
const trimmed = value.trim();
|
|
37
|
+
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(trimmed);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
function hasParameterValue(flags, parameter) {
|
|
48
|
+
const value = flags[parameter.flagName];
|
|
49
|
+
if (parameter.type === 'boolean') {
|
|
50
|
+
return value !== undefined;
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return value.length > 0;
|
|
54
|
+
}
|
|
55
|
+
return value !== undefined && value !== '';
|
|
56
|
+
}
|
|
57
|
+
function listProvidedBodyFlags(flags, parameters) {
|
|
58
|
+
return parameters
|
|
59
|
+
.filter((parameter) => hasParameterValue(flags, parameter))
|
|
60
|
+
.map((parameter) => `--${parameter.flagName}`);
|
|
61
|
+
}
|
|
62
|
+
export async function parseBody(flags, operation) {
|
|
63
|
+
const inlineBody = flags.body;
|
|
64
|
+
const bodyFile = flags['body-file'];
|
|
65
|
+
const bodyParameters = operation.parameters.filter((parameter) => parameter.in === 'body');
|
|
66
|
+
const hasBodyFlags = bodyParameters.some((parameter) => hasParameterValue(flags, parameter));
|
|
67
|
+
if ((inlineBody || bodyFile) && hasBodyFlags) {
|
|
68
|
+
const providedBodyFlags = listProvidedBodyFlags(flags, bodyParameters);
|
|
69
|
+
const rawBodyInput = inlineBody ? '--body' : '--body-file';
|
|
70
|
+
throw new Error(`Conflicting request body inputs: received ${rawBodyInput} together with body field flags (${providedBodyFlags.join(', ')}). Use either body field flags or --body/--body-file.`);
|
|
71
|
+
}
|
|
72
|
+
if (inlineBody) {
|
|
73
|
+
return JSON.parse(inlineBody);
|
|
74
|
+
}
|
|
75
|
+
if (bodyFile) {
|
|
76
|
+
return fs.readFile(bodyFile, 'utf8').then((content) => JSON.parse(content));
|
|
77
|
+
}
|
|
78
|
+
if (!bodyParameters.length) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const body = {};
|
|
82
|
+
for (const parameter of bodyParameters) {
|
|
83
|
+
const rawValue = flags[parameter.flagName];
|
|
84
|
+
const value = parameter.isArray && !parameter.jsonEncoded
|
|
85
|
+
? (Array.isArray(rawValue) ? rawValue : rawValue ? [rawValue] : undefined)
|
|
86
|
+
: parseScalarValue(rawValue, parameter.type);
|
|
87
|
+
if (parameter.required && (value === undefined || value === '')) {
|
|
88
|
+
throw new Error(`Missing required body field --${parameter.flagName}`);
|
|
89
|
+
}
|
|
90
|
+
if (value === undefined) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
body[parameter.name] = value;
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(body).length > 0) {
|
|
96
|
+
return body;
|
|
97
|
+
}
|
|
98
|
+
if (operation.hasBody && operation.bodyRequired) {
|
|
99
|
+
throw new Error('Missing request body. Use body field flags or --body/--body-file.');
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
export async function executeApiRequest(options) {
|
|
104
|
+
const { baseUrl, token } = await resolveServerRequestTarget(options);
|
|
105
|
+
const headers = new Headers();
|
|
106
|
+
if (token) {
|
|
107
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
108
|
+
}
|
|
109
|
+
if (options.role) {
|
|
110
|
+
headers.set('x-role', options.role);
|
|
111
|
+
}
|
|
112
|
+
const query = new URLSearchParams();
|
|
113
|
+
let requestPath = options.operation.pathTemplate;
|
|
114
|
+
for (const parameter of options.operation.parameters) {
|
|
115
|
+
if (parameter.in === 'body') {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const rawValue = options.flags[parameter.flagName];
|
|
119
|
+
const value = parameter.isArray
|
|
120
|
+
? (Array.isArray(rawValue) ? rawValue : rawValue ? [rawValue] : undefined)
|
|
121
|
+
: parseScalarValue(rawValue, parameter.type);
|
|
122
|
+
if (parameter.required && (value === undefined || value === '')) {
|
|
123
|
+
throw new Error(`Missing required parameter --${parameter.flagName}`);
|
|
124
|
+
}
|
|
125
|
+
if (value === undefined) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (parameter.in === 'path') {
|
|
129
|
+
requestPath = requestPath.replace(`{${parameter.name}}`, encodeURIComponent(String(value)));
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (parameter.in === 'query') {
|
|
133
|
+
if (Array.isArray(value)) {
|
|
134
|
+
value.forEach((item) => query.append(parameter.name, String(parseScalarValue(item, parameter.type))));
|
|
135
|
+
}
|
|
136
|
+
else if (typeof value === 'object') {
|
|
137
|
+
query.set(parameter.name, JSON.stringify(value));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
query.set(parameter.name, String(value));
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (parameter.in === 'header') {
|
|
145
|
+
headers.set(parameter.name, typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const body = await parseBody(options.flags, options.operation);
|
|
150
|
+
if (body !== undefined) {
|
|
151
|
+
headers.set('content-type', 'application/json');
|
|
152
|
+
}
|
|
153
|
+
const url = new URL(`${normalizeBaseUrl(baseUrl)}${requestPath}`);
|
|
154
|
+
query.forEach((value, key) => url.searchParams.append(key, value));
|
|
155
|
+
const response = await fetch(url, {
|
|
156
|
+
method: options.operation.method.toUpperCase(),
|
|
157
|
+
headers,
|
|
158
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
159
|
+
});
|
|
160
|
+
return parseResponse(response);
|
|
161
|
+
}
|
|
162
|
+
export async function executeRawApiRequest(options) {
|
|
163
|
+
const { baseUrl, token } = await resolveServerRequestTarget(options);
|
|
164
|
+
const headers = new Headers();
|
|
165
|
+
if (token) {
|
|
166
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
167
|
+
}
|
|
168
|
+
if (options.role) {
|
|
169
|
+
headers.set('x-role', options.role);
|
|
170
|
+
}
|
|
171
|
+
for (const [name, value] of Object.entries(options.headers ?? {})) {
|
|
172
|
+
if (value === undefined || value === null || value === '') {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
headers.set(name, typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
176
|
+
}
|
|
177
|
+
if (options.body !== undefined) {
|
|
178
|
+
headers.set('content-type', 'application/json');
|
|
179
|
+
}
|
|
180
|
+
const url = new URL(`${normalizeBaseUrl(baseUrl)}${options.path}`);
|
|
181
|
+
for (const [key, value] of Object.entries(options.query ?? {})) {
|
|
182
|
+
if (value === undefined) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(value)) {
|
|
186
|
+
for (const item of value) {
|
|
187
|
+
url.searchParams.append(key, typeof item === 'object' ? JSON.stringify(item) : String(item));
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
url.searchParams.set(key, typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
192
|
+
}
|
|
193
|
+
const response = await fetch(url, {
|
|
194
|
+
method: options.method.toUpperCase(),
|
|
195
|
+
headers,
|
|
196
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
197
|
+
});
|
|
198
|
+
return parseResponse(response);
|
|
199
|
+
}
|