@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.
@@ -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
+ }
@@ -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']) {
@@ -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' || topic === 'resource' || (topic === 'api' && subtopic === 'resource');
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('pnpm', ['install'], {
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('pnpm', ['build'], {
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,
@@ -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('pnpm', ['dev'], {
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('pnpm', ['install'], {
197
+ await runPnpmCommand(runCommand, ['install'], {
198
198
  cwd: params.portalDir,
199
199
  env: buildPortalCommandEnv(),
200
200
  envMode: 'replace',
@@ -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
+ }
@@ -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.",
@@ -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.1",
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": "22d8be8ece179cfa5c8a4ebb2c896c92bd418e03"
150
+ "gitHead": "9899cd799c070c7bd85140148ea5b100b046bdc7"
151
151
  }