@nocobase/cli 2.1.4-test.1 → 2.1.4-test.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,89 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ export function normalizeEarlyCliLocale(value) {
7
+ const normalized = String(value ?? '')
8
+ .trim()
9
+ .replace(/\..*$/, '')
10
+ .replace(/_/g, '-')
11
+ .toLowerCase();
12
+
13
+ if (normalized === 'zh' || normalized.startsWith('zh-')) {
14
+ return 'zh-CN';
15
+ }
16
+
17
+ if (normalized === 'en' || normalized.startsWith('en-')) {
18
+ return 'en-US';
19
+ }
20
+
21
+ return undefined;
22
+ }
23
+
24
+ function readConfiguredEarlyCliLocale() {
25
+ try {
26
+ const cliHomeRoot = String(process.env.NB_CLI_ROOT ?? '').trim() || os.homedir();
27
+ const configPath = path.join(cliHomeRoot, '.nocobase', 'config.json');
28
+ const content = fs.readFileSync(configPath, 'utf8');
29
+ const parsed = JSON.parse(content);
30
+ return normalizeEarlyCliLocale(parsed?.settings?.locale);
31
+ } catch {
32
+ return undefined;
33
+ }
34
+ }
35
+
36
+ export function detectEarlyCliLocale() {
37
+ const candidates = [
38
+ process.env.NB_LOCALE,
39
+ readConfiguredEarlyCliLocale(),
40
+ process.env.LC_ALL,
41
+ process.env.LC_MESSAGES,
42
+ process.env.LANG,
43
+ Intl.DateTimeFormat().resolvedOptions().locale,
44
+ ];
45
+
46
+ for (const candidate of candidates) {
47
+ const locale = normalizeEarlyCliLocale(candidate);
48
+ if (locale) {
49
+ return locale;
50
+ }
51
+ }
52
+
53
+ return 'en-US';
54
+ }
55
+
56
+ function getEarlyLocalePathValue(input, key) {
57
+ let current = input;
58
+ for (const part of key.split('.')) {
59
+ if (!current || typeof current !== 'object' || !Object.prototype.hasOwnProperty.call(current, part)) {
60
+ return undefined;
61
+ }
62
+ current = current[part];
63
+ }
64
+ return typeof current === 'string' ? current : undefined;
65
+ }
66
+
67
+ function readEarlyLocaleMessages(locale) {
68
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
69
+ const packageRoot = path.resolve(moduleDir, '..');
70
+ const localePaths = [
71
+ path.join(packageRoot, 'src', 'locale', `${locale}.json`),
72
+ path.join(packageRoot, 'dist', 'locale', `${locale}.json`),
73
+ ];
74
+
75
+ for (const localePath of localePaths) {
76
+ try {
77
+ return JSON.parse(fs.readFileSync(localePath, 'utf8'));
78
+ } catch {
79
+ // Try the next runtime layout.
80
+ }
81
+ }
82
+
83
+ return undefined;
84
+ }
85
+
86
+ export function translateEarlyCli(key, fallback, locale = detectEarlyCliLocale()) {
87
+ const messages = readEarlyLocaleMessages(locale);
88
+ return getEarlyLocalePathValue(messages, key) ?? fallback;
89
+ }
package/bin/run.js CHANGED
@@ -8,6 +8,7 @@ import pc from 'picocolors';
8
8
  import { fileURLToPath, pathToFileURL } from 'node:url';
9
9
  import { formatUnsupportedNodeVersionMessage, isSupportedNodeVersion } from './node-version.js';
10
10
  import { normalizeNodeOptions, normalizeSessionEnv } from './session-env.js';
11
+ import { ensureWindowsAdministrator } from './windows-admin.js';
11
12
 
12
13
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
14
  const requireFromCli = createRequire(import.meta.url);
@@ -27,6 +28,8 @@ if (!isSupportedNodeVersion()) {
27
28
  normalizeSessionEnv();
28
29
  normalizeNodeOptions();
29
30
 
31
+ ensureWindowsAdministrator();
32
+
30
33
  /**
31
34
  * In the monorepo, plain `node` cannot load `.ts`. Re-exec once with `--import <tsx>`
32
35
  * (same effect as a dedicated dev entry with `#!/usr/bin/env -S node --import tsx`).
@@ -0,0 +1,60 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import pc from 'picocolors';
3
+ import { detectEarlyCliLocale, translateEarlyCli } from './early-locale.js';
4
+
5
+ const windowsAdministratorCheckScript = [
6
+ '$identity = [Security.Principal.WindowsIdentity]::GetCurrent();',
7
+ '$principal = New-Object Security.Principal.WindowsPrincipal($identity);',
8
+ 'if ($principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { exit 0 }',
9
+ 'exit 1',
10
+ ].join(' ');
11
+
12
+ export function formatWindowsAdministratorRequiredMessage() {
13
+ const locale = detectEarlyCliLocale();
14
+ const message = translateEarlyCli(
15
+ 'entry.windowsAdministratorRequired.message',
16
+ 'NocoBase CLI must be run as Administrator on Windows.',
17
+ locale,
18
+ );
19
+ const hint = translateEarlyCli(
20
+ 'entry.windowsAdministratorRequired.hint',
21
+ 'Open your terminal as Administrator, then run the command again.',
22
+ locale,
23
+ );
24
+
25
+ return [message, hint].join('\n');
26
+ }
27
+
28
+ function isWindowsAdministrator() {
29
+ for (const command of ['pwsh.exe', 'powershell.exe']) {
30
+ const result = spawnSync(
31
+ command,
32
+ ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', windowsAdministratorCheckScript],
33
+ {
34
+ stdio: 'ignore',
35
+ windowsHide: true,
36
+ },
37
+ );
38
+
39
+ if (result.error?.code === 'ENOENT') {
40
+ continue;
41
+ }
42
+
43
+ return result.status === 0;
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ export function ensureWindowsAdministrator() {
50
+ if (process.platform !== 'win32' || process.env.NB_CLI_WINDOWS_ADMIN_CHECKED === '1') {
51
+ return;
52
+ }
53
+
54
+ if (!isWindowsAdministrator()) {
55
+ console.error(pc.red(formatWindowsAdministratorRequiredMessage()));
56
+ process.exit(1);
57
+ }
58
+
59
+ process.env.NB_CLI_WINDOWS_ADMIN_CHECKED = '1';
60
+ }
@@ -20,7 +20,7 @@ export default class SelfCheck extends Command {
20
20
  static flags = {
21
21
  channel: Flags.string({
22
22
  description: 'Release channel to compare against. Defaults to the current CLI channel.',
23
- options: ['auto', 'latest', 'beta', 'alpha'],
23
+ options: ['auto', 'latest', 'test', 'beta', 'alpha'],
24
24
  default: 'auto',
25
25
  }),
26
26
  json: Flags.boolean({
@@ -31,12 +31,12 @@ export default class SelfUpdate extends Command {
31
31
  '<%= config.bin %> <%= command.id %>',
32
32
  '<%= config.bin %> <%= command.id %> --yes',
33
33
  '<%= config.bin %> <%= command.id %> --skills',
34
- '<%= config.bin %> <%= command.id %> --channel alpha --json',
34
+ '<%= config.bin %> <%= command.id %> --channel test --json',
35
35
  ];
36
36
  static flags = {
37
37
  channel: Flags.string({
38
38
  description: 'Release channel to update to. Defaults to the current CLI channel.',
39
- options: ['auto', 'latest', 'beta', 'alpha'],
39
+ options: ['auto', 'latest', 'test', 'beta', 'alpha'],
40
40
  default: 'auto',
41
41
  }),
42
42
  yes: Flags.boolean({
@@ -97,6 +97,9 @@ function detectChannel(currentVersion) {
97
97
  if (/-beta(?:[.-]|$)/i.test(currentVersion)) {
98
98
  return 'beta';
99
99
  }
100
+ if (/-test(?:[.-]|$)/i.test(currentVersion)) {
101
+ return 'test';
102
+ }
100
103
  return 'latest';
101
104
  }
102
105
  function readCurrentVersion(packageRoot) {
@@ -1,4 +1,10 @@
1
1
  {
2
+ "entry": {
3
+ "windowsAdministratorRequired": {
4
+ "message": "NocoBase CLI must be run as Administrator on Windows.",
5
+ "hint": "Open your terminal as Administrator, then run the command again."
6
+ }
7
+ },
2
8
  "promptCatalog": {
3
9
  "common": {
4
10
  "cancelled": "Cancelled.",
@@ -1,4 +1,10 @@
1
1
  {
2
+ "entry": {
3
+ "windowsAdministratorRequired": {
4
+ "message": "Windows 上运行 NocoBase CLI 必须使用管理员模式。",
5
+ "hint": "请以管理员身份打开终端,然后重新执行命令。"
6
+ }
7
+ },
2
8
  "promptCatalog": {
3
9
  "common": {
4
10
  "cancelled": "已取消。",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.1.4-test.1",
3
+ "version": "2.1.4-test.3",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",