@nocobase/cli 2.2.0-alpha.1 → 2.2.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.
Files changed (52) hide show
  1. package/bin/early-locale.js +89 -0
  2. package/bin/node-version.js +35 -0
  3. package/bin/run.js +9 -0
  4. package/bin/windows-admin.js +60 -0
  5. package/dist/commands/app/destroy.js +4 -3
  6. package/dist/commands/app/restart.js +38 -0
  7. package/dist/commands/app/shared.js +49 -3
  8. package/dist/commands/app/start.js +92 -0
  9. package/dist/commands/app/upgrade.js +11 -0
  10. package/dist/commands/examples/prompts-stages.js +2 -2
  11. package/dist/commands/examples/prompts-test.js +2 -2
  12. package/dist/commands/init.js +22 -10
  13. package/dist/commands/install.js +135 -4
  14. package/dist/commands/license/activate.js +4 -1
  15. package/dist/commands/license/shared.js +24 -15
  16. package/dist/commands/self/check.js +1 -1
  17. package/dist/commands/self/update.js +4 -4
  18. package/dist/commands/skills/check.js +4 -5
  19. package/dist/commands/skills/install.js +18 -1
  20. package/dist/commands/skills/update.js +19 -4
  21. package/dist/commands/source/dev.js +9 -5
  22. package/dist/commands/source/download.js +67 -2
  23. package/dist/lib/api-command-compat.js +51 -8
  24. package/dist/lib/app-managed-resources.js +101 -1
  25. package/dist/lib/auth-store.js +34 -12
  26. package/dist/lib/cli-config.js +19 -0
  27. package/dist/lib/env-auth.js +291 -45
  28. package/dist/lib/env-config.js +6 -0
  29. package/dist/lib/hook-script.js +160 -0
  30. package/dist/lib/prompt-validators.js +1 -1
  31. package/dist/lib/prompt-web-ui.js +7 -11
  32. package/dist/lib/run-npm.js +4 -0
  33. package/dist/lib/self-manager.js +254 -46
  34. package/dist/lib/skills-manager.js +116 -23
  35. package/dist/lib/source-publish.js +2 -2
  36. package/dist/lib/startup-update.js +1 -1
  37. package/dist/locale/en-US.json +11 -5
  38. package/dist/locale/zh-CN.json +11 -5
  39. package/package.json +7 -2
  40. package/assets/env-proxy/nginx/app.conf.tpl +0 -23
  41. package/assets/env-proxy/nginx/nocobase.conf.tpl +0 -5
  42. package/assets/env-proxy/nginx/snippets/dist-location.conf +0 -5
  43. package/assets/env-proxy/nginx/snippets/gzip.conf +0 -17
  44. package/assets/env-proxy/nginx/snippets/log-format-http.conf +0 -13
  45. package/assets/env-proxy/nginx/snippets/maps-http.conf +0 -14
  46. package/assets/env-proxy/nginx/snippets/mime-types.conf +0 -98
  47. package/assets/env-proxy/nginx/snippets/proxy-location.conf +0 -17
  48. package/assets/env-proxy/nginx/snippets/spa-location.conf +0 -6
  49. package/assets/env-proxy/nginx/snippets/uploads-location.conf +0 -21
  50. package/scripts/build.mjs +0 -34
  51. package/scripts/clean.mjs +0 -9
  52. package/tsconfig.json +0 -19
@@ -19,7 +19,8 @@ export const PWC_FORM_META_STEP = '_pwcStep';
19
19
  /** Form POST JSON meta field: current field key when validating a single field. */
20
20
  export const PWC_FORM_META_FIELD = '_pwcField';
21
21
  const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
22
- const DEFAULT_HOST = '127.0.0.1';
22
+ const DEFAULT_PUBLIC_HOST = '127.0.0.1';
23
+ const LISTEN_HOST = '0.0.0.0';
23
24
  function resolveUiText(text, locale, fallback = '') {
24
25
  return resolveLocalizedText(text, { locale, fallback });
25
26
  }
@@ -705,7 +706,7 @@ function runPromptCatalogWebUIImpl(options) {
705
706
  const initialShow = reflowWebFormState(merged, Object.fromEntries(Object.entries(formDefaults).map(([k, v]) => [k, v])), userSeed).show;
706
707
  const submitPath = options.submitPath ?? DEFAULT_SUBMIT;
707
708
  const reflowPath = options.reflowPath ?? DEFAULT_REFLOW;
708
- const host = options.host ?? DEFAULT_HOST;
709
+ const publicHost = options.host ?? DEFAULT_PUBLIC_HOST;
709
710
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
710
711
  const pageTitle = resolveUiText(options.pageTitle, locale, t('promptCatalog.web.pageTitle'));
711
712
  const h1 = resolveUiText(options.documentHeading, locale, t('promptCatalog.web.documentHeading'));
@@ -753,7 +754,7 @@ function runPromptCatalogWebUIImpl(options) {
753
754
  }
754
755
  };
755
756
  const servePage = (port) => {
756
- const base = `http://${host}:${port}`;
757
+ const base = `http://${publicHost}:${port}`;
757
758
  const formInner = buildPwcFormHtml(catalog, formDefaults, initialShow, pwcStepDefs, 0, pwcNSteps, locale, uiText);
758
759
  const wizardClientJson = JSON.stringify({ n: pwcNSteps, stepDefs: pwcStepDefs });
759
760
  const pwcValStepUrl = pwcNSteps > 1 ? JSON.stringify(base + resolveValidateStepPath) : 'null';
@@ -2071,11 +2072,6 @@ function runPromptCatalogWebUIImpl(options) {
2071
2072
  return page;
2072
2073
  };
2073
2074
  server = createServer((req, res) => {
2074
- if (!req.socket.remoteAddress ||
2075
- !['127.0.0.1', '::1', '::ffff:127.0.0.1'].includes(req.socket.remoteAddress)) {
2076
- res.writeHead(403).end();
2077
- return;
2078
- }
2079
2075
  if (req.method === 'GET' && (req.url === '/' || req.url === '')) {
2080
2076
  const addr = server?.address();
2081
2077
  const port = typeof addr === 'object' && addr ? Number(addr.port) : 0;
@@ -2212,15 +2208,15 @@ function runPromptCatalogWebUIImpl(options) {
2212
2208
  }
2213
2209
  res.writeHead(404).end();
2214
2210
  });
2215
- server.listen(options.port ?? 0, host, () => {
2211
+ server.listen(options.port ?? 0, LISTEN_HOST, () => {
2216
2212
  const addr = server?.address();
2217
2213
  if (typeof addr !== 'object' || !addr) {
2218
2214
  rejectAndClose(new Error('Failed to bind HTTP server'));
2219
2215
  return;
2220
2216
  }
2221
2217
  const port = addr.port;
2222
- const startUrl = `http://${host}:${port}/`;
2223
- options.onServerStart?.({ host, port, url: startUrl });
2218
+ const startUrl = `http://${publicHost}:${port}/`;
2219
+ options.onServerStart?.({ host: publicHost, listenHost: LISTEN_HOST, port, url: startUrl });
2224
2220
  const onOpenBrowserError = options.onOpenBrowserError ?? ((u, err) => console.warn(String(err), u));
2225
2221
  try {
2226
2222
  openUrlInDefaultBrowser(startUrl, onOpenBrowserError);
@@ -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,
@@ -7,14 +7,12 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import fs from 'node:fs';
10
- import fsp from 'node:fs/promises';
10
+ import os from 'node:os';
11
11
  import path from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
- import { resolveCliHomeDir } from './cli-home.js';
14
13
  import { commandOutput, run } from './run-npm.js';
15
14
  const DEFAULT_PACKAGE_NAME = '@nocobase/cli';
16
15
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
17
- const INSTALL_METHOD_CACHE_FILE = 'self-install-methods.json';
18
16
  function normalizePath(value) {
19
17
  return path.resolve(value);
20
18
  }
@@ -97,6 +95,9 @@ function detectChannel(currentVersion) {
97
95
  if (/-beta(?:[.-]|$)/i.test(currentVersion)) {
98
96
  return 'beta';
99
97
  }
98
+ if (/-test(?:[.-]|$)/i.test(currentVersion)) {
99
+ return 'test';
100
+ }
100
101
  return 'latest';
101
102
  }
102
103
  function readCurrentVersion(packageRoot) {
@@ -105,56 +106,216 @@ function readCurrentVersion(packageRoot) {
105
106
  const pkg = JSON.parse(content);
106
107
  return String(pkg.version ?? '').trim();
107
108
  }
108
- function detectInstallMethod(packageRoot, globalPrefix) {
109
+ function detectInstallMethod(packageRoot, options = {}) {
109
110
  if (fs.existsSync(path.join(packageRoot, 'src'))
110
111
  && fs.existsSync(path.join(packageRoot, 'tsconfig.json'))) {
111
112
  return 'source';
112
113
  }
113
- if (globalPrefix && isSubPath(globalPrefix, packageRoot)) {
114
+ if (options.globalPrefix && isSubPath(options.globalPrefix, packageRoot)) {
114
115
  return 'npm-global';
115
116
  }
117
+ if (options.yarnGlobalDir && isSubPath(options.yarnGlobalDir, packageRoot)) {
118
+ return 'yarn-global';
119
+ }
120
+ if (isPnpmGlobalInstallPath({
121
+ packageName: options.packageName ?? DEFAULT_PACKAGE_NAME,
122
+ packageRoot,
123
+ pnpmGlobalBin: options.pnpmGlobalBin,
124
+ pnpmGlobalRoot: options.pnpmGlobalRoot,
125
+ })) {
126
+ return 'pnpm-global';
127
+ }
128
+ // Best-effort fallback for environments where pnpm probes are unavailable.
129
+ if (isPnpmGlobalPath(packageRoot)) {
130
+ return 'pnpm-global';
131
+ }
116
132
  if (packageRoot.includes(`${path.sep}node_modules${path.sep}`)) {
117
133
  return 'package-local';
118
134
  }
119
135
  return 'unknown';
120
136
  }
121
- function getInstallMethodCacheFile() {
122
- return path.join(resolveCliHomeDir('global'), INSTALL_METHOD_CACHE_FILE);
137
+ function isPnpmGlobalPath(packageRoot) {
138
+ const normalized = normalizePath(packageRoot);
139
+ const segments = normalized.split(path.sep).filter(Boolean);
140
+ const globalIndex = segments.findIndex((segment, index) => segment === 'global' && segments[index - 1] === 'pnpm');
141
+ if (globalIndex === -1) {
142
+ return false;
143
+ }
144
+ return segments.slice(globalIndex + 2).includes('node_modules');
123
145
  }
124
- function getInstallMethodCacheKey(packageRoot) {
125
- return normalizePath(path.join(packageRoot, 'bin', 'run.js'));
146
+ function normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot) {
147
+ const normalizedGlobalRoot = normalizePath(pnpmGlobalRoot);
148
+ return path.basename(normalizedGlobalRoot) === 'node_modules'
149
+ ? normalizedGlobalRoot
150
+ : path.join(normalizedGlobalRoot, 'node_modules');
126
151
  }
127
- async function readInstallMethodCache() {
152
+ function readSiblingPnpmGlobalNodeModulesRoots(pnpmGlobalRoot) {
153
+ const globalNodeModulesDir = normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot);
154
+ const globalProjectDir = path.dirname(globalNodeModulesDir);
155
+ let entries;
128
156
  try {
129
- const raw = await fsp.readFile(getInstallMethodCacheFile(), 'utf8');
130
- return JSON.parse(raw);
157
+ entries = fs.readdirSync(globalProjectDir);
131
158
  }
132
159
  catch {
133
- return {};
160
+ return [];
134
161
  }
162
+ return entries
163
+ .filter((entry) => {
164
+ if (entry === path.basename(globalNodeModulesDir)) {
165
+ return false;
166
+ }
167
+ try {
168
+ return fs.statSync(path.join(globalProjectDir, entry)).isDirectory();
169
+ }
170
+ catch {
171
+ return false;
172
+ }
173
+ })
174
+ .map((entry) => path.join(globalProjectDir, entry, 'node_modules'));
135
175
  }
136
- async function writeInstallMethodCache(state) {
137
- const filePath = getInstallMethodCacheFile();
138
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
139
- await fsp.writeFile(filePath, JSON.stringify(state, null, 2));
176
+ function readPnpmGlobalNodeModulesRoots(pnpmGlobalRoot) {
177
+ return [normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot), ...readSiblingPnpmGlobalNodeModulesRoots(pnpmGlobalRoot)];
140
178
  }
141
- async function readCachedInstallMethod(packageRoot) {
142
- const state = await readInstallMethodCache();
143
- return state.entries?.[getInstallMethodCacheKey(packageRoot)];
179
+ function packageNameToPath(packageName) {
180
+ return packageName.split('/').filter(Boolean);
144
181
  }
145
- async function writeCachedInstallMethod(packageRoot, entry) {
146
- const state = await readInstallMethodCache();
147
- const entries = {
148
- ...(state.entries ?? {}),
149
- [getInstallMethodCacheKey(packageRoot)]: entry,
150
- };
151
- await writeInstallMethodCache({ entries });
182
+ function isSameRealPath(left, right) {
183
+ try {
184
+ return normalizePath(fs.realpathSync(left)) === normalizePath(fs.realpathSync(right));
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ function isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot) {
191
+ if (isSubPath(pnpmGlobalRoot, packageRoot)) {
192
+ return true;
193
+ }
194
+ const globalNodeModulesDir = normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot);
195
+ const globalProjectDir = path.dirname(globalNodeModulesDir);
196
+ if (!isSubPath(globalProjectDir, packageRoot)) {
197
+ return false;
198
+ }
199
+ const segments = normalizePath(packageRoot).split(path.sep).filter(Boolean);
200
+ const pnpmStoreIndex = segments.indexOf('.pnpm');
201
+ if (pnpmStoreIndex === -1) {
202
+ return false;
203
+ }
204
+ return segments.slice(pnpmStoreIndex + 1).includes('node_modules');
205
+ }
206
+ function isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName) {
207
+ return readPnpmGlobalNodeModulesRoots(pnpmGlobalRoot).some((pnpmGlobalNodeModulesRoot) => isSameRealPath(path.join(pnpmGlobalNodeModulesRoot, ...packageNameToPath(packageName)), packageRoot));
208
+ }
209
+ function isPnpmGlobalBinProjectPath(pnpmGlobalBin, packageRoot, packageName) {
210
+ const pnpmHome = path.dirname(normalizePath(pnpmGlobalBin));
211
+ const globalDir = path.join(pnpmHome, 'global');
212
+ if (isSubPath(globalDir, packageRoot)) {
213
+ return true;
214
+ }
215
+ let entries;
216
+ try {
217
+ entries = fs.readdirSync(globalDir);
218
+ }
219
+ catch {
220
+ return false;
221
+ }
222
+ return entries.some((entry) => {
223
+ const pnpmGlobalRoot = path.join(globalDir, entry);
224
+ return (isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot)
225
+ || isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName));
226
+ });
227
+ }
228
+ function isPnpmGlobalInstallPath(options) {
229
+ if (options.pnpmGlobalRoot) {
230
+ const matchedRoot = isPnpmGlobalRootPath(options.pnpmGlobalRoot, options.packageRoot)
231
+ || isPnpmGlobalPackagePath(options.pnpmGlobalRoot, options.packageRoot, options.packageName);
232
+ if (matchedRoot) {
233
+ return true;
234
+ }
235
+ }
236
+ if (options.pnpmGlobalBin) {
237
+ return isPnpmGlobalBinProjectPath(options.pnpmGlobalBin, options.packageRoot, options.packageName);
238
+ }
239
+ return false;
240
+ }
241
+ function readCurrentBinPath(currentBinPath) {
242
+ const candidate = String(currentBinPath ?? process.argv[1] ?? '').trim();
243
+ if (!candidate) {
244
+ return undefined;
245
+ }
246
+ return normalizePath(candidate);
247
+ }
248
+ function cleanCommandPath(value) {
249
+ const trimmed = value.trim();
250
+ return trimmed ? trimmed : undefined;
251
+ }
252
+ function resolvePackageManagerProbeCwd() {
253
+ const candidates = [os.homedir(), os.tmpdir(), path.parse(process.cwd()).root];
254
+ for (const candidate of candidates) {
255
+ if (!candidate) {
256
+ continue;
257
+ }
258
+ try {
259
+ if (fs.statSync(candidate).isDirectory()) {
260
+ return candidate;
261
+ }
262
+ }
263
+ catch {
264
+ // Ignore invalid probe cwd candidates and continue to the next one.
265
+ }
266
+ }
267
+ return undefined;
152
268
  }
153
269
  async function readGlobalPrefix(commandOutputFn) {
154
270
  try {
155
- return (await commandOutputFn('npm', ['prefix', '-g'], {
271
+ return cleanCommandPath(await commandOutputFn('npm', ['prefix', '-g'], {
272
+ cwd: resolvePackageManagerProbeCwd(),
156
273
  errorName: 'npm prefix',
157
- })).trim();
274
+ }));
275
+ }
276
+ catch {
277
+ return undefined;
278
+ }
279
+ }
280
+ async function readPnpmGlobalBin(commandOutputFn) {
281
+ try {
282
+ return cleanCommandPath(await commandOutputFn('pnpm', ['bin', '-g'], {
283
+ cwd: resolvePackageManagerProbeCwd(),
284
+ errorName: 'pnpm bin',
285
+ }));
286
+ }
287
+ catch {
288
+ return undefined;
289
+ }
290
+ }
291
+ async function readPnpmGlobalRoot(commandOutputFn) {
292
+ try {
293
+ return cleanCommandPath(await commandOutputFn('pnpm', ['root', '-g'], {
294
+ cwd: resolvePackageManagerProbeCwd(),
295
+ errorName: 'pnpm root',
296
+ }));
297
+ }
298
+ catch {
299
+ return undefined;
300
+ }
301
+ }
302
+ async function readYarnGlobalDir(commandOutputFn) {
303
+ try {
304
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'dir'], {
305
+ cwd: resolvePackageManagerProbeCwd(),
306
+ errorName: 'yarn global dir',
307
+ }));
308
+ }
309
+ catch {
310
+ return undefined;
311
+ }
312
+ }
313
+ async function readYarnGlobalBin(commandOutputFn) {
314
+ try {
315
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'bin'], {
316
+ cwd: resolvePackageManagerProbeCwd(),
317
+ errorName: 'yarn global bin',
318
+ }));
158
319
  }
159
320
  catch {
160
321
  return undefined;
@@ -171,21 +332,21 @@ function getUnsupportedSelfUpdateReason(installMethod) {
171
332
  if (installMethod === 'source') {
172
333
  return [
173
334
  'This CLI is running from source in a repository checkout.',
174
- 'Automatic self-update is only supported for standard global npm installs.',
335
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
175
336
  'Upgrade this checkout through your repo workflow instead.',
176
337
  ].join(' ');
177
338
  }
178
339
  if (installMethod === 'package-local') {
179
340
  return [
180
341
  'This CLI is installed from a local project dependency tree.',
181
- 'Automatic self-update is only supported for standard global npm installs.',
342
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
182
343
  'Upgrade the parent project dependency that provides this CLI instead.',
183
344
  ].join(' ');
184
345
  }
185
346
  if (installMethod === 'unknown') {
186
347
  return [
187
- 'This CLI install could not be recognized as a standard global npm install.',
188
- 'Automatic self-update is only supported for standard global npm installs.',
348
+ 'This CLI install could not be recognized as a standard global npm, pnpm, or yarn install.',
349
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
189
350
  ].join(' ');
190
351
  }
191
352
  return undefined;
@@ -214,22 +375,45 @@ export function getSelfUpdatePackageSpec(status) {
214
375
  }
215
376
  export async function inspectSelfInstall(options = {}) {
216
377
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
378
+ const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
217
379
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
218
- const cachedInstallMethod = await readCachedInstallMethod(packageRoot);
219
- const globalPrefix = cachedInstallMethod?.globalPrefix ?? await readGlobalPrefix(commandOutputFn);
220
- const installMethod = cachedInstallMethod?.installMethod ?? detectInstallMethod(packageRoot, globalPrefix);
221
- if (!cachedInstallMethod) {
222
- await writeCachedInstallMethod(packageRoot, {
223
- installMethod,
224
- globalPrefix,
225
- });
226
- }
380
+ const currentBinPath = readCurrentBinPath(options.currentBinPath);
381
+ const globalPrefix = await readGlobalPrefix(commandOutputFn);
382
+ const pnpmGlobalBin = await readPnpmGlobalBin(commandOutputFn);
383
+ const pnpmGlobalRoot = await readPnpmGlobalRoot(commandOutputFn);
384
+ const yarnGlobalDir = await readYarnGlobalDir(commandOutputFn);
385
+ const yarnGlobalBin = await readYarnGlobalBin(commandOutputFn);
386
+ const installMethod = detectInstallMethodFromSignals({
387
+ packageRoot,
388
+ currentBinPath,
389
+ globalPrefix,
390
+ packageName,
391
+ pnpmGlobalBin,
392
+ pnpmGlobalRoot,
393
+ yarnGlobalBin,
394
+ yarnGlobalDir,
395
+ });
227
396
  return {
228
397
  packageRoot,
229
398
  installMethod,
230
399
  globalPrefix,
231
400
  };
232
401
  }
402
+ function detectInstallMethodFromSignals(options) {
403
+ if (options.currentBinPath && options.yarnGlobalBin && isSubPath(options.yarnGlobalBin, options.currentBinPath)) {
404
+ return 'yarn-global';
405
+ }
406
+ if (options.currentBinPath && options.pnpmGlobalBin && isSubPath(options.pnpmGlobalBin, options.currentBinPath)) {
407
+ return 'pnpm-global';
408
+ }
409
+ return detectInstallMethod(options.packageRoot, {
410
+ globalPrefix: options.globalPrefix,
411
+ packageName: options.packageName,
412
+ pnpmGlobalBin: options.pnpmGlobalBin,
413
+ pnpmGlobalRoot: options.pnpmGlobalRoot,
414
+ yarnGlobalDir: options.yarnGlobalDir,
415
+ });
416
+ }
233
417
  export async function inspectSelfStatus(options = {}) {
234
418
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
235
419
  const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
@@ -237,6 +421,8 @@ export async function inspectSelfStatus(options = {}) {
237
421
  const channel = options.channel && options.channel !== 'auto' ? options.channel : detectChannel(currentVersion);
238
422
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
239
423
  const { installMethod, globalPrefix } = await inspectSelfInstall({
424
+ currentBinPath: options.currentBinPath,
425
+ packageName,
240
426
  packageRoot,
241
427
  commandOutputFn,
242
428
  });
@@ -258,7 +444,7 @@ export async function inspectSelfStatus(options = {}) {
258
444
  latestVersion,
259
445
  updateAvailable,
260
446
  installMethod,
261
- updatable: installMethod === 'npm-global',
447
+ updatable: installMethod === 'npm-global' || installMethod === 'pnpm-global' || installMethod === 'yarn-global',
262
448
  updateBlockedReason: getUnsupportedSelfUpdateReason(installMethod),
263
449
  globalPrefix,
264
450
  registryError,
@@ -267,9 +453,30 @@ export async function inspectSelfStatus(options = {}) {
267
453
  export function formatUnsupportedSelfUpdateMessage(status) {
268
454
  return status.updateBlockedReason
269
455
  ?? [
270
- 'Automatic self-update is only supported for standard global npm installs.',
456
+ 'Automatic self-update is only supported for standard global npm, pnpm, or yarn installs.',
271
457
  ].join('\n');
272
458
  }
459
+ function resolveSelfUpdateInstallCommand(installMethod, packageSpec) {
460
+ if (installMethod === 'pnpm-global') {
461
+ return {
462
+ command: 'pnpm',
463
+ args: ['add', '-g', packageSpec],
464
+ errorName: 'pnpm add',
465
+ };
466
+ }
467
+ if (installMethod === 'yarn-global') {
468
+ return {
469
+ command: 'yarn',
470
+ args: ['global', 'add', packageSpec],
471
+ errorName: 'yarn global add',
472
+ };
473
+ }
474
+ return {
475
+ command: 'npm',
476
+ args: ['install', '-g', packageSpec],
477
+ errorName: 'npm install',
478
+ };
479
+ }
273
480
  export async function updateSelf(options = {}) {
274
481
  const status = await inspectSelfStatus(options);
275
482
  if (!status.updatable) {
@@ -288,9 +495,10 @@ export async function updateSelf(options = {}) {
288
495
  };
289
496
  }
290
497
  const packageSpec = getSelfUpdatePackageSpec(status);
291
- await (options.runFn ?? run)('npm', ['install', '-g', packageSpec], {
498
+ const installCommand = resolveSelfUpdateInstallCommand(status.installMethod, packageSpec);
499
+ await (options.runFn ?? run)(installCommand.command, installCommand.args, {
292
500
  stdio: options.verbose ? 'inherit' : 'ignore',
293
- errorName: 'npm install',
501
+ errorName: installCommand.errorName,
294
502
  });
295
503
  return {
296
504
  action: 'updated',