@nocobase/cli 3.0.0-alpha.1 → 3.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/api/swagger/get.js +96 -0
- package/dist/commands/api/swagger/index.js +20 -0
- package/dist/commands/api/swagger/list.js +92 -0
- package/dist/commands/init.js +16 -0
- package/dist/lib/bootstrap.js +3 -1
- package/dist/lib/portal-create.js +2 -2
- package/dist/lib/portal-deploy.js +17 -2
- package/dist/lib/portal-dev.js +2 -2
- package/dist/lib/portal-source.js +2 -2
- package/dist/lib/run-npm.js +22 -4
- package/dist/lib/swagger-command.js +52 -0
- package/dist/locale/en-US.json +26 -0
- package/dist/locale/zh-CN.json +26 -0
- package/package.json +2 -2
|
@@ -0,0 +1,96 @@
|
|
|
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 { promises as fs } from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { Command, Flags } from '@oclif/core';
|
|
12
|
+
import { translateCli } from '../../../lib/cli-locale.js';
|
|
13
|
+
import { executeSwaggerRequest, swaggerRequestFlags } from '../../../lib/swagger-command.js';
|
|
14
|
+
import { renderTable } from '../../../lib/ui.js';
|
|
15
|
+
const swaggerText = (key, values, fallback) => translateCli(`commands.swagger.${key}`, values, { fallback });
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
18
|
+
}
|
|
19
|
+
function unwrapData(value) {
|
|
20
|
+
if (!isRecord(value) || typeof value.openapi === 'string') {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return Object.prototype.hasOwnProperty.call(value, 'data') ? value.data : value;
|
|
24
|
+
}
|
|
25
|
+
function normalizeDocument(value) {
|
|
26
|
+
const document = unwrapData(value);
|
|
27
|
+
if (!isRecord(document) ||
|
|
28
|
+
typeof document.openapi !== 'string' ||
|
|
29
|
+
!isRecord(document.info) ||
|
|
30
|
+
!isRecord(document.paths)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return document;
|
|
34
|
+
}
|
|
35
|
+
export default class SwaggerGet extends Command {
|
|
36
|
+
static summary = 'Get a NocoBase OpenAPI document';
|
|
37
|
+
static examples = [
|
|
38
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
39
|
+
'<%= config.bin %> <%= command.id %> --namespace collections/orders --json',
|
|
40
|
+
'<%= config.bin %> <%= command.id %> --namespace plugins/ai --output ./openapi/ai.json',
|
|
41
|
+
];
|
|
42
|
+
static flags = {
|
|
43
|
+
...swaggerRequestFlags,
|
|
44
|
+
namespace: Flags.string({
|
|
45
|
+
aliases: ['ns'],
|
|
46
|
+
description: 'Document namespace, such as core, plugins/ai, or collections/orders',
|
|
47
|
+
}),
|
|
48
|
+
output: Flags.string({
|
|
49
|
+
char: 'o',
|
|
50
|
+
description: 'Write the OpenAPI document to a file',
|
|
51
|
+
}),
|
|
52
|
+
'json-output': Flags.boolean({
|
|
53
|
+
char: 'j',
|
|
54
|
+
aliases: ['json'],
|
|
55
|
+
description: 'Print the complete OpenAPI document as JSON',
|
|
56
|
+
default: false,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
async run() {
|
|
60
|
+
const { flags } = await this.parse(SwaggerGet);
|
|
61
|
+
const namespace = flags.namespace?.trim() || undefined;
|
|
62
|
+
const response = await executeSwaggerRequest(this, flags, '/swagger:get', { ns: namespace });
|
|
63
|
+
if (!response) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const details = JSON.stringify(response.data, null, 2);
|
|
68
|
+
this.error(response.status === 404
|
|
69
|
+
? swaggerText('errors.pluginDisabled', undefined, 'The API documentation plugin is not enabled. Enable it before requesting Swagger documents.')
|
|
70
|
+
: swaggerText('errors.requestFailed', { status: response.status, details }, `Swagger request failed with status ${response.status}\n${details}`));
|
|
71
|
+
}
|
|
72
|
+
const document = normalizeDocument(response.data);
|
|
73
|
+
if (!document) {
|
|
74
|
+
this.error(swaggerText('errors.invalidDocument', undefined, 'swagger:get returned an invalid OpenAPI document.'));
|
|
75
|
+
}
|
|
76
|
+
const json = `${JSON.stringify(document, null, 2)}\n`;
|
|
77
|
+
if (flags.output) {
|
|
78
|
+
const outputPath = path.resolve(flags.output);
|
|
79
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
80
|
+
await fs.writeFile(outputPath, json);
|
|
81
|
+
this.log(swaggerText('messages.saved', { output: outputPath }, `Saved Swagger document to ${outputPath}.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (flags['json-output']) {
|
|
85
|
+
this.log(json.trimEnd());
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.log(renderTable([swaggerText('table.field', undefined, 'Field'), swaggerText('table.value', undefined, 'Value')], [
|
|
89
|
+
[swaggerText('fields.namespace', undefined, 'Namespace'), namespace ?? 'all'],
|
|
90
|
+
[swaggerText('fields.title', undefined, 'Title'), document.info.title ?? ''],
|
|
91
|
+
[swaggerText('fields.version', undefined, 'Version'), document.info.version ?? ''],
|
|
92
|
+
[swaggerText('fields.openapi', undefined, 'OpenAPI'), document.openapi],
|
|
93
|
+
[swaggerText('fields.paths', undefined, 'Paths'), String(Object.keys(document.paths).length)],
|
|
94
|
+
]));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -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 Swagger extends Command {
|
|
11
|
+
static summary = 'Inspect the current NocoBase OpenAPI documentation';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(Swagger);
|
|
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 ?? 'api:swagger',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 { translateCli } from '../../../lib/cli-locale.js';
|
|
11
|
+
import { executeSwaggerRequest, swaggerRequestFlags } from '../../../lib/swagger-command.js';
|
|
12
|
+
import { renderTable } from '../../../lib/ui.js';
|
|
13
|
+
const swaggerText = (key, values, fallback) => translateCli(`commands.swagger.${key}`, values, { fallback });
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
16
|
+
}
|
|
17
|
+
function unwrapData(value) {
|
|
18
|
+
return isRecord(value) && Object.prototype.hasOwnProperty.call(value, 'data') ? value.data : value;
|
|
19
|
+
}
|
|
20
|
+
function readNamespace(url) {
|
|
21
|
+
try {
|
|
22
|
+
return new URL(url, 'http://localhost').searchParams.get('ns') || 'all';
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'all';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function normalizeDestinations(value) {
|
|
29
|
+
const data = unwrapData(value);
|
|
30
|
+
if (!Array.isArray(data)) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const destinations = [];
|
|
34
|
+
for (const item of data) {
|
|
35
|
+
if (!isRecord(item) || typeof item.name !== 'string' || typeof item.url !== 'string') {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
destinations.push({
|
|
39
|
+
name: item.name,
|
|
40
|
+
namespace: readNamespace(item.url),
|
|
41
|
+
url: item.url,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return destinations;
|
|
45
|
+
}
|
|
46
|
+
export default class SwaggerList extends Command {
|
|
47
|
+
static summary = 'List available NocoBase OpenAPI document namespaces';
|
|
48
|
+
static examples = [
|
|
49
|
+
'<%= config.bin %> <%= command.id %>',
|
|
50
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
51
|
+
'<%= config.bin %> <%= command.id %> --env dev --yes --json',
|
|
52
|
+
];
|
|
53
|
+
static flags = {
|
|
54
|
+
...swaggerRequestFlags,
|
|
55
|
+
'json-output': Flags.boolean({
|
|
56
|
+
char: 'j',
|
|
57
|
+
aliases: ['json'],
|
|
58
|
+
description: 'Print namespaces as JSON',
|
|
59
|
+
default: false,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
async run() {
|
|
63
|
+
const { flags } = await this.parse(SwaggerList);
|
|
64
|
+
const response = await executeSwaggerRequest(this, flags, '/swagger:getUrls');
|
|
65
|
+
if (!response) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const details = JSON.stringify(response.data, null, 2);
|
|
70
|
+
this.error(response.status === 404
|
|
71
|
+
? swaggerText('errors.pluginDisabled', undefined, 'The API documentation plugin is not enabled. Enable it before requesting Swagger documents.')
|
|
72
|
+
: swaggerText('errors.requestFailed', { status: response.status, details }, `Swagger request failed with status ${response.status}\n${details}`));
|
|
73
|
+
}
|
|
74
|
+
const destinations = normalizeDestinations(response.data);
|
|
75
|
+
if (!destinations) {
|
|
76
|
+
this.error(swaggerText('errors.invalidDestinations', undefined, 'swagger:getUrls returned an invalid destination list.'));
|
|
77
|
+
}
|
|
78
|
+
if (flags['json-output']) {
|
|
79
|
+
this.log(JSON.stringify(destinations, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!destinations.length) {
|
|
83
|
+
this.log(swaggerText('messages.empty', undefined, 'No Swagger document namespaces are available.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.log(renderTable([
|
|
87
|
+
swaggerText('table.name', undefined, 'Name'),
|
|
88
|
+
swaggerText('table.namespace', undefined, 'Namespace'),
|
|
89
|
+
swaggerText('table.url', undefined, 'URL'),
|
|
90
|
+
], destinations.map((item) => [item.name, item.namespace, item.url])));
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -109,6 +109,15 @@ function resolveInitDownloadVersion(results) {
|
|
|
109
109
|
function initVersionPromptValue(version) {
|
|
110
110
|
return version === 'latest' || version === 'beta' || version === 'alpha' ? version : 'other';
|
|
111
111
|
}
|
|
112
|
+
export function defaultInitDownloadVersionForCliVersion(cliVersion) {
|
|
113
|
+
if (/-alpha(?:[.-]|$)/i.test(cliVersion)) {
|
|
114
|
+
return 'alpha';
|
|
115
|
+
}
|
|
116
|
+
if (/-beta(?:[.-]|$)/i.test(cliVersion)) {
|
|
117
|
+
return 'beta';
|
|
118
|
+
}
|
|
119
|
+
return 'latest';
|
|
120
|
+
}
|
|
112
121
|
function yesInitialValue(def, fallback) {
|
|
113
122
|
if ('yesInitialValue' in def && def.yesInitialValue !== undefined) {
|
|
114
123
|
return String(def.yesInitialValue);
|
|
@@ -409,8 +418,15 @@ Prompt modes:
|
|
|
409
418
|
installAccessToken: installConnectionAccessTokenPrompt,
|
|
410
419
|
};
|
|
411
420
|
buildPromptCatalog(flags, options) {
|
|
421
|
+
const downloadVersion = defaultInitDownloadVersionForCliVersion(String(this.config.pjson?.version ?? '').trim());
|
|
422
|
+
const versionPrompt = Init.prompts.version;
|
|
412
423
|
const prompts = {
|
|
413
424
|
...Init.prompts,
|
|
425
|
+
version: {
|
|
426
|
+
...versionPrompt,
|
|
427
|
+
initialValue: downloadVersion,
|
|
428
|
+
yesInitialValue: downloadVersion,
|
|
429
|
+
},
|
|
414
430
|
installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
|
|
415
431
|
};
|
|
416
432
|
if (flags['skip-auth']) {
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -72,7 +72,9 @@ function hasVersionFlag(argv) {
|
|
|
72
72
|
function isBuiltinCommand(argv) {
|
|
73
73
|
const commandTokens = argv.filter((token) => token && !token.startsWith('-'));
|
|
74
74
|
const [topic, subtopic] = commandTokens;
|
|
75
|
-
return topic === 'env' ||
|
|
75
|
+
return (topic === 'env' ||
|
|
76
|
+
topic === 'resource' ||
|
|
77
|
+
(topic === 'api' && (subtopic === 'resource' || subtopic === 'swagger')));
|
|
76
78
|
}
|
|
77
79
|
export function shouldSkipRuntimeBootstrap(argv) {
|
|
78
80
|
return hasVersionFlag(argv) || isBuiltinCommand(argv);
|
|
@@ -21,7 +21,7 @@ import { resolveEnvRelativePath } from './cli-home.js';
|
|
|
21
21
|
import { translateCli } from './cli-locale.js';
|
|
22
22
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
23
23
|
import { buildPortalConfig, mergePortalConfigIntoOptions, writePortalConfig, } from './portal-config.js';
|
|
24
|
-
import { run } from './run-npm.js';
|
|
24
|
+
import { run, runPnpmCommand } from './run-npm.js';
|
|
25
25
|
const DEFAULT_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
26
26
|
const DEFAULT_PORTAL_APP_NAME = 'main';
|
|
27
27
|
const TEMPLATE_COPY_EXCLUDED_NAMES = new Set(['.git', 'node_modules', '.DS_Store']);
|
|
@@ -392,7 +392,7 @@ export async function createPortalWorkspace(options) {
|
|
|
392
392
|
const hasPackageJson = await pathExists(path.join(portalDir, 'package.json'));
|
|
393
393
|
if (hasPackageJson) {
|
|
394
394
|
const runCommand = options.runCommand ?? run;
|
|
395
|
-
await runCommand
|
|
395
|
+
await runPnpmCommand(runCommand, ['install'], {
|
|
396
396
|
cwd: portalDir,
|
|
397
397
|
env: buildPortalCommandEnv(),
|
|
398
398
|
envMode: 'replace',
|
|
@@ -16,7 +16,7 @@ import { buildPortalBasePath, resolvePortalAppFromApiBaseUrl, resolvePortalStora
|
|
|
16
16
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
17
17
|
import { updatePortalEnvFiles } from './portal-env-files.js';
|
|
18
18
|
import { mergePortalConfigIntoOptions, readPortalConfig } from './portal-config.js';
|
|
19
|
-
import { run } from './run-npm.js';
|
|
19
|
+
import { run, runPnpmCommand } from './run-npm.js';
|
|
20
20
|
const portalDeployText = (key, values, fallback) => translateCli(`commands.portalDeploy.${key}`, values, { fallback });
|
|
21
21
|
const DEPLOY_OPERATION = {
|
|
22
22
|
method: 'POST',
|
|
@@ -208,7 +208,13 @@ export async function deployPortalWorkspace(options) {
|
|
|
208
208
|
portalBase,
|
|
209
209
|
});
|
|
210
210
|
const runCommand = options.runCommand ?? run;
|
|
211
|
-
await runCommand
|
|
211
|
+
await runPnpmCommand(runCommand, ['install'], {
|
|
212
|
+
cwd: portalDir,
|
|
213
|
+
env: buildPortalCommandEnv(),
|
|
214
|
+
envMode: 'replace',
|
|
215
|
+
errorName: 'pnpm install',
|
|
216
|
+
});
|
|
217
|
+
await runPnpmCommand(runCommand, ['build'], {
|
|
212
218
|
cwd: portalDir,
|
|
213
219
|
env: buildPortalCommandEnv({
|
|
214
220
|
NOCOBASE_API_URL: apiBaseUrl,
|
|
@@ -217,6 +223,15 @@ export async function deployPortalWorkspace(options) {
|
|
|
217
223
|
envMode: 'replace',
|
|
218
224
|
errorName: 'pnpm build',
|
|
219
225
|
});
|
|
226
|
+
await runPnpmCommand(runCommand, ['build:html'], {
|
|
227
|
+
cwd: portalDir,
|
|
228
|
+
env: buildPortalCommandEnv({
|
|
229
|
+
NOCOBASE_API_URL: apiBaseUrl,
|
|
230
|
+
NOCOBASE_PORTAL_BASE: portalBase,
|
|
231
|
+
}),
|
|
232
|
+
envMode: 'replace',
|
|
233
|
+
errorName: 'pnpm build:html',
|
|
234
|
+
});
|
|
220
235
|
await assertFileExists(path.join(distDir, 'index.html'), portalDeployText('errors.distMissing', { distDir }, `Portal build did not produce ${path.join(distDir, 'index.html')}.`));
|
|
221
236
|
await ensurePortalDistPublicReadable({
|
|
222
237
|
storagePath,
|
package/dist/lib/portal-dev.js
CHANGED
|
@@ -12,7 +12,7 @@ import { translateCli } from './cli-locale.js';
|
|
|
12
12
|
import { buildPortalBasePath, resolvePortalAppFromApiBaseUrl, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
13
13
|
import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
14
14
|
import { updatePortalEnvFiles } from './portal-env-files.js';
|
|
15
|
-
import { run } from './run-npm.js';
|
|
15
|
+
import { run, runPnpmCommand } from './run-npm.js';
|
|
16
16
|
const portalDevText = (key, values, fallback) => translateCli(`commands.portalDev.${key}`, values, { fallback });
|
|
17
17
|
function trimValue(value) {
|
|
18
18
|
return String(value ?? '').trim();
|
|
@@ -66,7 +66,7 @@ export async function devPortalWorkspace(options) {
|
|
|
66
66
|
};
|
|
67
67
|
options.onStart?.(result);
|
|
68
68
|
const runCommand = options.runCommand ?? run;
|
|
69
|
-
await runCommand
|
|
69
|
+
await runPnpmCommand(runCommand, ['dev'], {
|
|
70
70
|
cwd: portalDir,
|
|
71
71
|
env: buildPortalCommandEnv({
|
|
72
72
|
NOCOBASE_API_URL: apiBaseUrl,
|
|
@@ -19,7 +19,7 @@ import { buildPortalCommandEnv } from './portal-command-env.js';
|
|
|
19
19
|
import { buildPortalBasePath, resolvePortalAppFromApiBaseUrl, resolvePortalStoragePath, validatePortalSlug, } from './portal-create.js';
|
|
20
20
|
import { listPortalWorkspaces } from './portal-list.js';
|
|
21
21
|
import { findPortalListItem } from './portal-info.js';
|
|
22
|
-
import { run } from './run-npm.js';
|
|
22
|
+
import { run, runPnpmCommand } from './run-npm.js';
|
|
23
23
|
const execFileAsync = promisify(execFile);
|
|
24
24
|
const portalSourceText = (key, values, fallback) => translateCli(`commands.portalSource.${key}`, values, { fallback });
|
|
25
25
|
const PULL_SOURCE_OPERATION = {
|
|
@@ -194,7 +194,7 @@ async function installPortalDependencies(params) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
const runCommand = params.runCommand ?? run;
|
|
197
|
-
await runCommand
|
|
197
|
+
await runPnpmCommand(runCommand, ['install'], {
|
|
198
198
|
cwd: params.portalDir,
|
|
199
199
|
env: buildPortalCommandEnv(),
|
|
200
200
|
envMode: 'replace',
|
package/dist/lib/run-npm.js
CHANGED
|
@@ -75,18 +75,36 @@ function buildProcessEnv(options) {
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
function createMissingCommandError(name, label, error) {
|
|
78
|
-
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined;
|
|
79
|
-
if (code !== 'ENOENT') {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
78
|
if (!Object.prototype.hasOwnProperty.call(MISSING_COMMAND_SPECS, name)) {
|
|
83
79
|
return undefined;
|
|
84
80
|
}
|
|
85
81
|
const spec = MISSING_COMMAND_SPECS[name];
|
|
82
|
+
if (!isMissingCommandError(name, spec.displayName, error)) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
86
85
|
return new Error(translateCli('commands.shared.missingCommand', { action: label, displayName: spec.displayName, configKey: spec.configKey }, {
|
|
87
86
|
fallback: `Couldn't run \`${label}\` because the ${spec.displayName} executable could not be found. Install ${spec.displayName} or update \`nb config set ${spec.configKey} <path>\` and try again.`,
|
|
88
87
|
}));
|
|
89
88
|
}
|
|
89
|
+
function isMissingCommandError(name, displayName, error) {
|
|
90
|
+
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined;
|
|
91
|
+
if (code === 'ENOENT') {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
const lowerMessage = message.toLowerCase();
|
|
96
|
+
return (lowerMessage.includes(`spawn ${name.toLowerCase()} enoent`) ||
|
|
97
|
+
lowerMessage.includes(`${name.toLowerCase()} executable could not be found`) ||
|
|
98
|
+
lowerMessage.includes(`${displayName.toLowerCase()} executable could not be found`));
|
|
99
|
+
}
|
|
100
|
+
export async function runPnpmCommand(runCommand, args, options) {
|
|
101
|
+
try {
|
|
102
|
+
await runCommand('pnpm', args, options);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw createMissingCommandError('pnpm', options.errorName ?? `pnpm ${args.join(' ')}`.trim(), error) ?? error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
90
108
|
function isDockerDaemonUnavailableError(error) {
|
|
91
109
|
const message = error instanceof Error ? error.message : String(error);
|
|
92
110
|
return DOCKER_DAEMON_UNAVAILABLE_PATTERNS.some((pattern) => pattern.test(message));
|
|
@@ -0,0 +1,52 @@
|
|
|
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 { Flags } from '@oclif/core';
|
|
10
|
+
import { executeRawApiRequest } from './api-client.js';
|
|
11
|
+
import { ensureCrossEnvConfirmed } from './env-guard.js';
|
|
12
|
+
export const swaggerRequestFlags = {
|
|
13
|
+
env: Flags.string({
|
|
14
|
+
char: 'e',
|
|
15
|
+
description: 'CLI env name; omitted uses the current env',
|
|
16
|
+
}),
|
|
17
|
+
yes: Flags.boolean({
|
|
18
|
+
char: 'y',
|
|
19
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
20
|
+
default: false,
|
|
21
|
+
}),
|
|
22
|
+
'api-base-url': Flags.string({
|
|
23
|
+
description: 'NocoBase API base URL, for example http://localhost:13000/api',
|
|
24
|
+
}),
|
|
25
|
+
role: Flags.string({
|
|
26
|
+
description: 'Role override, sent as X-Role',
|
|
27
|
+
}),
|
|
28
|
+
token: Flags.string({
|
|
29
|
+
char: 't',
|
|
30
|
+
description: 'API key or access token override',
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
export async function executeSwaggerRequest(command, flags, path, query) {
|
|
34
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
35
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
36
|
+
command,
|
|
37
|
+
requestedEnv,
|
|
38
|
+
yes: flags.yes,
|
|
39
|
+
});
|
|
40
|
+
if (!confirmed) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return executeRawApiRequest({
|
|
44
|
+
envName: requestedEnv,
|
|
45
|
+
baseUrl: flags['api-base-url'],
|
|
46
|
+
token: flags.token,
|
|
47
|
+
role: flags.role,
|
|
48
|
+
method: 'GET',
|
|
49
|
+
path,
|
|
50
|
+
query,
|
|
51
|
+
});
|
|
52
|
+
}
|
package/dist/locale/en-US.json
CHANGED
|
@@ -259,6 +259,32 @@
|
|
|
259
259
|
"localSynced": "Local synced"
|
|
260
260
|
}
|
|
261
261
|
},
|
|
262
|
+
"swagger": {
|
|
263
|
+
"errors": {
|
|
264
|
+
"pluginDisabled": "The API documentation plugin is not enabled. Enable it before requesting Swagger documents.",
|
|
265
|
+
"requestFailed": "Swagger request failed with status {{status}}\n{{details}}",
|
|
266
|
+
"invalidDestinations": "swagger:getUrls returned an invalid destination list.",
|
|
267
|
+
"invalidDocument": "swagger:get returned an invalid OpenAPI document."
|
|
268
|
+
},
|
|
269
|
+
"messages": {
|
|
270
|
+
"empty": "No Swagger document namespaces are available.",
|
|
271
|
+
"saved": "Saved Swagger document to {{output}}."
|
|
272
|
+
},
|
|
273
|
+
"table": {
|
|
274
|
+
"name": "Name",
|
|
275
|
+
"namespace": "Namespace",
|
|
276
|
+
"url": "URL",
|
|
277
|
+
"field": "Field",
|
|
278
|
+
"value": "Value"
|
|
279
|
+
},
|
|
280
|
+
"fields": {
|
|
281
|
+
"namespace": "Namespace",
|
|
282
|
+
"title": "Title",
|
|
283
|
+
"version": "Version",
|
|
284
|
+
"openapi": "OpenAPI",
|
|
285
|
+
"paths": "Paths"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
262
288
|
"portalInfo": {
|
|
263
289
|
"errors": {
|
|
264
290
|
"envNotConfigured": "Env \"{{envName}}\" is not configured. Run `nb env add {{envName}} --api-base-url <url>` first.",
|
package/dist/locale/zh-CN.json
CHANGED
|
@@ -259,6 +259,32 @@
|
|
|
259
259
|
"localSynced": "本地已同步"
|
|
260
260
|
}
|
|
261
261
|
},
|
|
262
|
+
"swagger": {
|
|
263
|
+
"errors": {
|
|
264
|
+
"pluginDisabled": "API 文档插件尚未启用。请先启用该插件,再获取 Swagger 文档。",
|
|
265
|
+
"requestFailed": "Swagger 请求失败,状态码 {{status}}\n{{details}}",
|
|
266
|
+
"invalidDestinations": "swagger:getUrls 返回了无效的文档列表。",
|
|
267
|
+
"invalidDocument": "swagger:get 返回了无效的 OpenAPI 文档。"
|
|
268
|
+
},
|
|
269
|
+
"messages": {
|
|
270
|
+
"empty": "没有可用的 Swagger 文档命名空间。",
|
|
271
|
+
"saved": "Swagger 文档已保存到 {{output}}。"
|
|
272
|
+
},
|
|
273
|
+
"table": {
|
|
274
|
+
"name": "名称",
|
|
275
|
+
"namespace": "命名空间",
|
|
276
|
+
"url": "URL",
|
|
277
|
+
"field": "字段",
|
|
278
|
+
"value": "值"
|
|
279
|
+
},
|
|
280
|
+
"fields": {
|
|
281
|
+
"namespace": "命名空间",
|
|
282
|
+
"title": "标题",
|
|
283
|
+
"version": "版本",
|
|
284
|
+
"openapi": "OpenAPI",
|
|
285
|
+
"paths": "路径数"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
262
288
|
"portalInfo": {
|
|
263
289
|
"errors": {
|
|
264
290
|
"envNotConfigured": "env \"{{envName}}\" 尚未配置。请先运行 `nb env add {{envName}} --api-base-url <url>`。",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.3",
|
|
4
4
|
"description": "NocoBase Command Line Tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/generated/command-registry.js",
|
|
@@ -147,5 +147,5 @@
|
|
|
147
147
|
"type": "git",
|
|
148
148
|
"url": "git+https://github.com/nocobase/nocobase.git"
|
|
149
149
|
},
|
|
150
|
-
"gitHead": "
|
|
150
|
+
"gitHead": "9899cd799c070c7bd85140148ea5b100b046bdc7"
|
|
151
151
|
}
|