@nocobase/cli 2.1.10 → 2.1.11-test.2

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.
@@ -15,7 +15,7 @@ import { appendAppPublicPath } from '../lib/app-public-path.js';
15
15
  import { runPromptCatalog, } from "../lib/prompt-catalog.js";
16
16
  import { applyCliLocale, localeText, resolveCliLocale, translateCli } from "../lib/cli-locale.js";
17
17
  import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
18
- import { defaultDockerContainerPrefix, defaultDockerNetworkName } from '../lib/app-runtime.js';
18
+ import { defaultDockerContainerPrefix, defaultDockerNetworkName, managedAppLifecycleEnvVars, } from '../lib/app-runtime.js';
19
19
  import { resolveDefaultApiHost, resolveDockerContainerPrefix, resolveDockerNetworkName } from '../lib/cli-config.js';
20
20
  import { DEFAULT_DOCKER_VERSION, resolveDockerImageRef } from "../lib/docker-image.js";
21
21
  import { findAvailableTcpPort, validateAppPublicPath, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
@@ -1960,7 +1960,7 @@ export default class Install extends Command {
1960
1960
  }
1961
1961
  static resolveAbsoluteStoragePath(envName, appResults) {
1962
1962
  const configuredStoragePath = resolveConfiguredStoragePathValue(appResults, envName);
1963
- return resolveConfiguredEnvPath(configuredStoragePath) ?? resolveEnvRelativePath(defaultInstallStoragePath(envName));
1963
+ return (resolveConfiguredEnvPath(configuredStoragePath) ?? resolveEnvRelativePath(defaultInstallStoragePath(envName)));
1964
1964
  }
1965
1965
  async prepareHookScriptForInstall(params) {
1966
1966
  const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
@@ -2057,7 +2057,9 @@ export default class Install extends Command {
2057
2057
  const dbDialect = String(params.dbResults.dbDialect ?? 'postgres').trim() || 'postgres';
2058
2058
  const appKey = Install.resolveManagedAppKey(params.appResults.appKey);
2059
2059
  const timeZone = Install.resolveManagedTimeZone(params.appResults.timeZone);
2060
+ const lifecycleEnvVars = managedAppLifecycleEnvVars();
2060
2061
  const env = {
2062
+ ...lifecycleEnvVars,
2061
2063
  STORAGE_PATH: storagePath,
2062
2064
  APP_PORT: String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT,
2063
2065
  APP_KEY: appKey,
@@ -26,7 +26,7 @@ function formatSkillsUpdateMessage(result, verbose) {
26
26
  }
27
27
  export default class SelfUpdate extends Command {
28
28
  static summary = 'Update the globally installed NocoBase CLI';
29
- static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm install.';
29
+ static description = 'Update the current NocoBase CLI install when it is managed by a standard global npm, pnpm, or yarn install.';
30
30
  static examples = [
31
31
  '<%= config.bin %> <%= command.id %>',
32
32
  '<%= config.bin %> <%= command.id %> --yes',
@@ -8,8 +8,9 @@
8
8
  */
9
9
  import { Command, Flags } from '@oclif/core';
10
10
  import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
11
- import { ensureLocalPostinstall } from '../../lib/app-managed-resources.js';
11
+ import { ensureLocalPostinstall, ensureNpmSourceDevDependencies } from '../../lib/app-managed-resources.js';
12
12
  import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
13
+ import { findAvailableTcpPort } from '../../lib/prompt-validators.js';
13
14
  import { announceTargetEnv, failTask, printInfo, startTask, succeedTask } from '../../lib/ui.js';
14
15
  function formatUnsupportedRuntimeMessage(kind, envName) {
15
16
  if (kind === 'docker') {
@@ -129,10 +130,7 @@ export default class SourceDev extends Command {
129
130
  this.error(formatUnsupportedRuntimeMessage(runtime.kind, runtime.envName));
130
131
  }
131
132
  announceTargetEnv(runtime.envName);
132
- const devPort = flags.port ||
133
- (runtime.env.appPort !== undefined && runtime.env.appPort !== null
134
- ? String(runtime.env.appPort).trim()
135
- : undefined);
133
+ const devPort = flags.port?.trim() || (await findAvailableTcpPort());
136
134
  const appUrl = appUrlForPort(devPort);
137
135
  if (await isAppAlreadyRunning(appUrl)) {
138
136
  this.error([
@@ -160,6 +158,12 @@ export default class SourceDev extends Command {
160
158
  }
161
159
  printInfo(`Starting NocoBase dev mode for "${runtime.envName}" from ${runtime.projectRoot}. Press Ctrl+C to stop.`);
162
160
  try {
161
+ await ensureNpmSourceDevDependencies(runtime, {
162
+ onStartTask: startTask,
163
+ onSucceedTask: succeedTask,
164
+ onFailTask: failTask,
165
+ verbose: true,
166
+ });
163
167
  await ensureLocalPostinstall(runtime, {
164
168
  onStartTask: startTask,
165
169
  onSucceedTask: succeedTask,
@@ -6,7 +6,9 @@
6
6
  * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
- import { mkdir, readdir } from 'node:fs/promises';
9
+ import { existsSync } from 'node:fs';
10
+ import path from 'node:path';
11
+ import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
10
12
  import { resolveAppPublicPath } from './app-public-path.js';
11
13
  import { dockerContainerExists, managedAppLifecycleEnvVars, runLocalNocoBaseCommand, startDockerContainer, } from './app-runtime.js';
12
14
  import { deriveBuiltinDbConnection, resolveBuiltinDbConnection } from './builtin-db.js';
@@ -91,6 +93,15 @@ function formatLocalPostinstallFailure(envName, message) {
91
93
  `Details: ${message}`,
92
94
  ].join('\n');
93
95
  }
96
+ function formatNpmSourceDevDependenciesFailure(envName, projectRoot, message) {
97
+ return [
98
+ `Couldn't prepare source dev dependencies for "${envName}".`,
99
+ '`nb source dev` requires @nocobase/devtools in npm source envs.',
100
+ `Source directory: ${projectRoot}`,
101
+ `Run \`cd ${projectRoot} && yarn install\` after fixing package.json, then try again.`,
102
+ `Details: ${message}`,
103
+ ].join('\n');
104
+ }
94
105
  function formatSavedDockerSettingsIncomplete(envName, missing) {
95
106
  return [
96
107
  `Can't start NocoBase for "${envName}" yet.`,
@@ -115,6 +126,45 @@ async function localProjectHasFiles(projectRoot) {
115
126
  return false;
116
127
  }
117
128
  }
129
+ function isRecord(value) {
130
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
131
+ }
132
+ async function readPackageJson(projectRoot) {
133
+ const packageJsonPath = path.join(projectRoot, 'package.json');
134
+ const content = await readFile(packageJsonPath, 'utf-8');
135
+ const parsed = JSON.parse(content);
136
+ if (!isRecord(parsed)) {
137
+ throw new Error(`${packageJsonPath} must contain a JSON object.`);
138
+ }
139
+ return parsed;
140
+ }
141
+ function getStringDependency(packageJson, section, packageName) {
142
+ const dependencies = packageJson[section];
143
+ if (!isRecord(dependencies)) {
144
+ return undefined;
145
+ }
146
+ const version = dependencies[packageName];
147
+ return typeof version === 'string' && version.trim() ? version.trim() : undefined;
148
+ }
149
+ function ensureDevDependencies(packageJson) {
150
+ const devDependencies = packageJson.devDependencies;
151
+ if (devDependencies === undefined) {
152
+ const next = {};
153
+ packageJson.devDependencies = next;
154
+ return next;
155
+ }
156
+ if (!isRecord(devDependencies)) {
157
+ throw new Error('package.json devDependencies must be an object.');
158
+ }
159
+ return devDependencies;
160
+ }
161
+ function hasNpmSourceDevtools(projectRoot) {
162
+ return existsSync(path.join(projectRoot, 'node_modules', '@nocobase', 'devtools', 'package.json'));
163
+ }
164
+ function npmRegistryEnv(runtime) {
165
+ const npmRegistry = String(runtime.env.config?.npmRegistry ?? '').trim();
166
+ return npmRegistry ? { npm_config_registry: npmRegistry } : undefined;
167
+ }
118
168
  export async function buildSavedDockerRunArgs(runtime, options) {
119
169
  const config = runtime.env.config ?? {};
120
170
  const storagePath = trimValue(resolveConfiguredStoragePath(config));
@@ -345,3 +395,43 @@ export async function ensureLocalPostinstall(runtime, options) {
345
395
  throw new Error(formatLocalPostinstallFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
346
396
  }
347
397
  }
398
+ export async function ensureNpmSourceDevDependencies(runtime, options) {
399
+ if (runtime.source !== 'npm') {
400
+ return;
401
+ }
402
+ let taskStarted = false;
403
+ try {
404
+ const packageJson = await readPackageJson(runtime.projectRoot);
405
+ const appVersion = getStringDependency(packageJson, 'dependencies', '@nocobase/app');
406
+ const devtoolsVersion = getStringDependency(packageJson, 'devDependencies', '@nocobase/devtools');
407
+ let updatedPackageJson = false;
408
+ if (!devtoolsVersion) {
409
+ if (!appVersion) {
410
+ throw new Error('Cannot determine @nocobase/devtools version because dependencies["@nocobase/app"] is missing.');
411
+ }
412
+ const devDependencies = ensureDevDependencies(packageJson);
413
+ devDependencies['@nocobase/devtools'] = appVersion;
414
+ updatedPackageJson = true;
415
+ await writeFile(path.join(runtime.projectRoot, 'package.json'), `${JSON.stringify(packageJson, null, 2)}\n`, 'utf-8');
416
+ }
417
+ const needsInstall = updatedPackageJson || !hasNpmSourceDevtools(runtime.projectRoot);
418
+ if (!needsInstall) {
419
+ return;
420
+ }
421
+ options?.onStartTask?.(`Preparing source dev dependencies for "${runtime.envName}"...`);
422
+ taskStarted = true;
423
+ await run('yarn', ['install'], {
424
+ cwd: runtime.projectRoot,
425
+ env: npmRegistryEnv(runtime),
426
+ errorName: 'yarn install',
427
+ stdio: commandStdio(options?.verbose),
428
+ });
429
+ options?.onSucceedTask?.(`Source dev dependencies are ready for "${runtime.envName}".`);
430
+ }
431
+ catch (error) {
432
+ if (taskStarted) {
433
+ options?.onFailTask?.(`Failed to prepare source dev dependencies for "${runtime.envName}".`);
434
+ }
435
+ throw new Error(formatNpmSourceDevDependenciesFailure(runtime.envName, runtime.projectRoot, error instanceof Error ? error.message : String(error)));
436
+ }
437
+ }
@@ -123,13 +123,19 @@ function normalizeAuthConfig(config) {
123
123
  },
124
124
  }
125
125
  : {}),
126
- ...(settings.bin?.docker || settings.bin?.caddy || settings.bin?.git || settings.bin?.nginx || settings.bin?.yarn
126
+ ...(settings.bin?.docker ||
127
+ settings.bin?.caddy ||
128
+ settings.bin?.git ||
129
+ settings.bin?.nginx ||
130
+ settings.bin?.pnpm ||
131
+ settings.bin?.yarn
127
132
  ? {
128
133
  bin: {
129
134
  ...(settings.bin?.docker ? { docker: normalizeOptionalString(settings.bin.docker) } : {}),
130
135
  ...(settings.bin?.caddy ? { caddy: normalizeOptionalString(settings.bin.caddy) } : {}),
131
136
  ...(settings.bin?.git ? { git: normalizeOptionalString(settings.bin.git) } : {}),
132
137
  ...(settings.bin?.nginx ? { nginx: normalizeOptionalString(settings.bin.nginx) } : {}),
138
+ ...(settings.bin?.pnpm ? { pnpm: normalizeOptionalString(settings.bin.pnpm) } : {}),
133
139
  ...(settings.bin?.yarn ? { yarn: normalizeOptionalString(settings.bin.yarn) } : {}),
134
140
  },
135
141
  }
@@ -16,6 +16,7 @@ export const DEFAULT_DOCKER_BIN = 'docker';
16
16
  export const DEFAULT_CADDY_BIN = 'caddy';
17
17
  export const DEFAULT_GIT_BIN = 'git';
18
18
  export const DEFAULT_NGINX_BIN = 'nginx';
19
+ export const DEFAULT_PNPM_BIN = 'pnpm';
19
20
  export const PROXY_PROVIDER_OPTIONS = ['nginx', 'caddy'];
20
21
  export const DEFAULT_PROXY_PROVIDER = 'nginx';
21
22
  export const NGINX_PROXY_DRIVER_OPTIONS = ['local', 'docker'];
@@ -40,6 +41,7 @@ export const SUPPORTED_CLI_CONFIG_KEYS = [
40
41
  'bin.caddy',
41
42
  'bin.git',
42
43
  'bin.nginx',
44
+ 'bin.pnpm',
43
45
  'proxy.nb-cli-root',
44
46
  'proxy.caddy-driver',
45
47
  'proxy.nginx-driver',
@@ -136,6 +138,7 @@ function pruneSettings(config) {
136
138
  !trimValue(bin.caddy) &&
137
139
  !trimValue(bin.git) &&
138
140
  !trimValue(bin.nginx) &&
141
+ !trimValue(bin.pnpm) &&
139
142
  !trimValue(bin.yarn)) {
140
143
  delete config.settings?.bin;
141
144
  }
@@ -187,6 +190,8 @@ export function getExplicitCliConfigValue(config, key) {
187
190
  return trimValue(config.settings?.bin?.git);
188
191
  case 'bin.nginx':
189
192
  return trimValue(config.settings?.bin?.nginx);
193
+ case 'bin.pnpm':
194
+ return trimValue(config.settings?.bin?.pnpm);
190
195
  case 'proxy.nb-cli-root':
191
196
  return trimValue(config.settings?.proxy?.nbCliRoot);
192
197
  case 'proxy.caddy-driver':
@@ -233,6 +238,8 @@ export function getEffectiveCliConfigValue(config, key) {
233
238
  return DEFAULT_GIT_BIN;
234
239
  case 'bin.nginx':
235
240
  return DEFAULT_NGINX_BIN;
241
+ case 'bin.pnpm':
242
+ return DEFAULT_PNPM_BIN;
236
243
  case 'proxy.nb-cli-root':
237
244
  return explicit ?? resolveCliHomeRoot();
238
245
  case 'proxy.caddy-driver':
@@ -387,6 +394,12 @@ export async function setCliConfigValue(key, value, options = {}) {
387
394
  nginx: normalized,
388
395
  };
389
396
  break;
397
+ case 'bin.pnpm':
398
+ config.settings.bin = {
399
+ ...(config.settings.bin ?? {}),
400
+ pnpm: normalized,
401
+ };
402
+ break;
390
403
  case 'proxy.nb-cli-root':
391
404
  config.settings.proxy = {
392
405
  ...(config.settings.proxy ?? {}),
@@ -496,6 +509,11 @@ export async function deleteCliConfigValue(key, options = {}) {
496
509
  delete config.settings.bin.nginx;
497
510
  }
498
511
  break;
512
+ case 'bin.pnpm':
513
+ if (config.settings.bin) {
514
+ delete config.settings.bin.pnpm;
515
+ }
516
+ break;
499
517
  case 'proxy.nb-cli-root':
500
518
  if (config.settings.proxy) {
501
519
  delete config.settings.proxy.nbCliRoot;
@@ -556,6 +574,7 @@ const CONFIGURABLE_COMMAND_KEYS = {
556
574
  caddy: 'bin.caddy',
557
575
  git: 'bin.git',
558
576
  nginx: 'bin.nginx',
577
+ pnpm: 'bin.pnpm',
559
578
  yarn: 'bin.yarn',
560
579
  };
561
580
  export function isConfigurableCommandName(value) {
@@ -44,6 +44,10 @@ const MISSING_COMMAND_SPECS = {
44
44
  displayName: 'Yarn',
45
45
  configKey: 'bin.yarn',
46
46
  },
47
+ pnpm: {
48
+ displayName: 'pnpm',
49
+ configKey: 'bin.pnpm',
50
+ },
47
51
  };
48
52
  const DOCKER_DAEMON_UNAVAILABLE_PATTERNS = [
49
53
  /cannot connect to the docker daemon/i,
@@ -108,25 +108,47 @@ function readCurrentVersion(packageRoot) {
108
108
  const pkg = JSON.parse(content);
109
109
  return String(pkg.version ?? '').trim();
110
110
  }
111
- function detectInstallMethod(packageRoot, globalPrefix) {
111
+ function detectInstallMethod(packageRoot, options = {}) {
112
112
  if (fs.existsSync(path.join(packageRoot, 'src'))
113
113
  && fs.existsSync(path.join(packageRoot, 'tsconfig.json'))) {
114
114
  return 'source';
115
115
  }
116
- if (globalPrefix && isSubPath(globalPrefix, packageRoot)) {
116
+ if (options.globalPrefix && isSubPath(options.globalPrefix, packageRoot)) {
117
117
  return 'npm-global';
118
118
  }
119
+ if (options.yarnGlobalDir && isSubPath(options.yarnGlobalDir, packageRoot)) {
120
+ return 'yarn-global';
121
+ }
122
+ if (isPnpmGlobalPath(packageRoot)) {
123
+ return 'pnpm-global';
124
+ }
119
125
  if (packageRoot.includes(`${path.sep}node_modules${path.sep}`)) {
120
126
  return 'package-local';
121
127
  }
122
128
  return 'unknown';
123
129
  }
130
+ function isPnpmGlobalPath(packageRoot) {
131
+ const normalized = normalizePath(packageRoot);
132
+ const segments = normalized.split(path.sep).filter(Boolean);
133
+ const globalIndex = segments.findIndex((segment, index) => segment === 'global' && segments[index - 1] === 'pnpm');
134
+ if (globalIndex === -1) {
135
+ return false;
136
+ }
137
+ return segments.slice(globalIndex + 2).includes('node_modules');
138
+ }
124
139
  function getInstallMethodCacheFile() {
125
140
  return path.join(resolveCliHomeDir('global'), INSTALL_METHOD_CACHE_FILE);
126
141
  }
127
142
  function getInstallMethodCacheKey(packageRoot) {
128
143
  return normalizePath(path.join(packageRoot, 'bin', 'run.js'));
129
144
  }
145
+ function readCurrentBinPath(currentBinPath) {
146
+ const candidate = String(currentBinPath ?? process.argv[1] ?? '').trim();
147
+ if (!candidate) {
148
+ return undefined;
149
+ }
150
+ return normalizePath(candidate);
151
+ }
130
152
  async function readInstallMethodCache() {
131
153
  try {
132
154
  const raw = await fsp.readFile(getInstallMethodCacheFile(), 'utf8');
@@ -163,6 +185,26 @@ async function readGlobalPrefix(commandOutputFn) {
163
185
  return undefined;
164
186
  }
165
187
  }
188
+ async function readYarnGlobalDir(commandOutputFn) {
189
+ try {
190
+ return (await commandOutputFn('yarn', ['global', 'dir'], {
191
+ errorName: 'yarn global dir',
192
+ })).trim();
193
+ }
194
+ catch {
195
+ return undefined;
196
+ }
197
+ }
198
+ async function readYarnGlobalBin(commandOutputFn) {
199
+ try {
200
+ return (await commandOutputFn('yarn', ['global', 'bin'], {
201
+ errorName: 'yarn global bin',
202
+ })).trim();
203
+ }
204
+ catch {
205
+ return undefined;
206
+ }
207
+ }
166
208
  async function readDistTags(packageName, commandOutputFn) {
167
209
  const output = await commandOutputFn('npm', ['view', packageName, 'dist-tags', '--json'], {
168
210
  errorName: 'npm view',
@@ -174,21 +216,21 @@ function getUnsupportedSelfUpdateReason(installMethod) {
174
216
  if (installMethod === 'source') {
175
217
  return [
176
218
  'This CLI is running from source in a repository checkout.',
177
- 'Automatic self-update is only supported for standard global npm installs.',
219
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
178
220
  'Upgrade this checkout through your repo workflow instead.',
179
221
  ].join(' ');
180
222
  }
181
223
  if (installMethod === 'package-local') {
182
224
  return [
183
225
  'This CLI is installed from a local project dependency tree.',
184
- 'Automatic self-update is only supported for standard global npm installs.',
226
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
185
227
  'Upgrade the parent project dependency that provides this CLI instead.',
186
228
  ].join(' ');
187
229
  }
188
230
  if (installMethod === 'unknown') {
189
231
  return [
190
- 'This CLI install could not be recognized as a standard global npm install.',
191
- 'Automatic self-update is only supported for standard global npm installs.',
232
+ 'This CLI install could not be recognized as a standard global npm, pnpm, or yarn install.',
233
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
192
234
  ].join(' ');
193
235
  }
194
236
  return undefined;
@@ -219,12 +261,25 @@ export async function inspectSelfInstall(options = {}) {
219
261
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
220
262
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
221
263
  const cachedInstallMethod = await readCachedInstallMethod(packageRoot);
264
+ const currentBinPath = readCurrentBinPath(options.currentBinPath);
222
265
  const globalPrefix = cachedInstallMethod?.globalPrefix ?? await readGlobalPrefix(commandOutputFn);
223
- const installMethod = cachedInstallMethod?.installMethod ?? detectInstallMethod(packageRoot, globalPrefix);
224
- if (!cachedInstallMethod) {
266
+ const yarnGlobalDir = cachedInstallMethod?.yarnGlobalDir ?? await readYarnGlobalDir(commandOutputFn);
267
+ const yarnGlobalBin = cachedInstallMethod?.yarnGlobalBin ?? await readYarnGlobalBin(commandOutputFn);
268
+ const installMethod = cachedInstallMethod?.installMethod
269
+ ?? detectInstallMethodFromSignals({
270
+ packageRoot,
271
+ currentBinPath,
272
+ globalPrefix,
273
+ yarnGlobalBin,
274
+ yarnGlobalDir,
275
+ });
276
+ if (!cachedInstallMethod || cachedInstallMethod.currentBinPath !== currentBinPath) {
225
277
  await writeCachedInstallMethod(packageRoot, {
226
278
  installMethod,
279
+ currentBinPath,
227
280
  globalPrefix,
281
+ yarnGlobalBin,
282
+ yarnGlobalDir,
228
283
  });
229
284
  }
230
285
  return {
@@ -233,6 +288,15 @@ export async function inspectSelfInstall(options = {}) {
233
288
  globalPrefix,
234
289
  };
235
290
  }
291
+ function detectInstallMethodFromSignals(options) {
292
+ if (options.currentBinPath && options.yarnGlobalBin && isSubPath(options.yarnGlobalBin, options.currentBinPath)) {
293
+ return 'yarn-global';
294
+ }
295
+ return detectInstallMethod(options.packageRoot, {
296
+ globalPrefix: options.globalPrefix,
297
+ yarnGlobalDir: options.yarnGlobalDir,
298
+ });
299
+ }
236
300
  export async function inspectSelfStatus(options = {}) {
237
301
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
238
302
  const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
@@ -261,7 +325,7 @@ export async function inspectSelfStatus(options = {}) {
261
325
  latestVersion,
262
326
  updateAvailable,
263
327
  installMethod,
264
- updatable: installMethod === 'npm-global',
328
+ updatable: installMethod === 'npm-global' || installMethod === 'pnpm-global' || installMethod === 'yarn-global',
265
329
  updateBlockedReason: getUnsupportedSelfUpdateReason(installMethod),
266
330
  globalPrefix,
267
331
  registryError,
@@ -270,9 +334,30 @@ export async function inspectSelfStatus(options = {}) {
270
334
  export function formatUnsupportedSelfUpdateMessage(status) {
271
335
  return status.updateBlockedReason
272
336
  ?? [
273
- 'Automatic self-update is only supported for standard global npm installs.',
337
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
274
338
  ].join('\n');
275
339
  }
340
+ function resolveSelfUpdateInstallCommand(installMethod, packageSpec) {
341
+ if (installMethod === 'pnpm-global') {
342
+ return {
343
+ command: 'pnpm',
344
+ args: ['add', '-g', packageSpec],
345
+ errorName: 'pnpm add',
346
+ };
347
+ }
348
+ if (installMethod === 'yarn-global') {
349
+ return {
350
+ command: 'yarn',
351
+ args: ['global', 'add', packageSpec],
352
+ errorName: 'yarn global add',
353
+ };
354
+ }
355
+ return {
356
+ command: 'npm',
357
+ args: ['install', '-g', packageSpec],
358
+ errorName: 'npm install',
359
+ };
360
+ }
276
361
  export async function updateSelf(options = {}) {
277
362
  const status = await inspectSelfStatus(options);
278
363
  if (!status.updatable) {
@@ -291,9 +376,10 @@ export async function updateSelf(options = {}) {
291
376
  };
292
377
  }
293
378
  const packageSpec = getSelfUpdatePackageSpec(status);
294
- await (options.runFn ?? run)('npm', ['install', '-g', packageSpec], {
379
+ const installCommand = resolveSelfUpdateInstallCommand(status.installMethod, packageSpec);
380
+ await (options.runFn ?? run)(installCommand.command, installCommand.args, {
295
381
  stdio: options.verbose ? 'inherit' : 'ignore',
296
- errorName: 'npm install',
382
+ errorName: installCommand.errorName,
297
383
  });
298
384
  return {
299
385
  action: 'updated',
@@ -151,7 +151,7 @@ export async function shouldRunStartupUpdateCheck(argv, now = new Date()) {
151
151
  return readCurrentInstallLastCheckedDate(state) !== todayStamp(now);
152
152
  }
153
153
  export function shouldEnableStartupUpdateForInstallMethod(installMethod) {
154
- return installMethod === 'npm-global';
154
+ return installMethod === 'npm-global' || installMethod === 'pnpm-global' || installMethod === 'yarn-global';
155
155
  }
156
156
  function hasPendingUpdates(selfStatus, skillsStatus) {
157
157
  return Boolean(selfStatus.updateAvailable || skillsStatus.updateAvailable === true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.1.10",
3
+ "version": "2.1.11-test.2",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",
@@ -142,6 +142,5 @@
142
142
  "repository": {
143
143
  "type": "git",
144
144
  "url": "git+https://github.com/nocobase/nocobase.git"
145
- },
146
- "gitHead": "65d5bc38d72d5a6fca8bcf9798a0b976e382cac9"
145
+ }
147
146
  }