fnva 0.0.22 → 0.0.24

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/README.md CHANGED
@@ -487,7 +487,7 @@ description = "AnyCC 代理服务"
487
487
  # 仓库配置
488
488
  [repositories]
489
489
  java = [
490
- "https://mirrors.aliyun.com/eclipse/temurin-compliance/temurin",
490
+ "https://mirrors.tuna.tsinghua.edu.cn/Adoptium",
491
491
  "https://api.adoptium.net/v3"
492
492
  ]
493
493
  maven = [
@@ -496,6 +496,8 @@ maven = [
496
496
  ]
497
497
  ```
498
498
 
499
+ > Java downloads default to the Tsinghua Adoptium mirror (https://mirrors.tuna.tsinghua.edu.cn/Adoptium). For fully stable setups, manually maintain versions in ~/.fnva/config.toml [[java_environments]] and JAVA_HOME.
500
+
499
501
  ### 常用命令速查
500
502
 
501
503
  | 命令 | 功能 | 示例 |
package/bin/fnva.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const { spawnSync } = require('child_process');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const EncodingUtils = require('../lib/encoding-utils');
6
7
 
7
8
  function resolvePlatform() {
8
9
  switch (process.platform) {
@@ -132,21 +133,18 @@ function removeAutoFlag(args) {
132
133
  function createTempScriptFile(script, envType, envName) {
133
134
  try {
134
135
  const os = require('os');
135
- const fs = require('fs');
136
136
  const path = require('path');
137
137
 
138
- const tempDir = os.tmpdir();
139
- const scriptFile = path.join(tempDir, `fnva_${envType}_${envName}_${Date.now()}.ps1`);
140
-
141
- fs.writeFileSync(scriptFile, script, 'utf8');
138
+ const prefix = `fnva_${envType}_${envName}`;
139
+ const scriptFile = EncodingUtils.createTempPowerShellScript(script, prefix);
142
140
 
143
141
  console.log('');
144
- console.log('💡 环境已切换到当前进程。要在新的 PowerShell 窗口中使用此环境,运行:');
142
+ console.log('[INFO] 环境已切换到当前进程。要在新的 PowerShell 窗口中使用此环境,运行:');
145
143
  console.log(` ${scriptFile}`);
146
144
  console.log(' 或者: fnva', envType, 'use', envName, '--auto');
147
145
 
148
146
  } catch (error) {
149
- console.warn('⚠️ 无法创建临时脚本文件:', error.message);
147
+ console.warn('[WARN] 无法创建临时脚本文件:', error.message);
150
148
  }
151
149
  }
152
150
 
@@ -208,18 +206,18 @@ function applyEnvironmentVariables(envVars) {
208
206
  }
209
207
 
210
208
  function displaySuccessMessage(envType, envName, envVars) {
211
- console.log(`✅ Switched to ${envType} environment: ${envName}`);
209
+ console.log(`[OK] Switched to ${envType} environment: ${envName}`);
212
210
 
213
211
  if (envVars.JAVA_HOME) {
214
- console.log(`📁 JAVA_HOME: ${envVars.JAVA_HOME}`);
212
+ console.log(`[DIR] JAVA_HOME: ${envVars.JAVA_HOME}`);
215
213
  }
216
214
 
217
215
  if (envVars.ANTHROPIC_AUTH_TOKEN) {
218
- console.log(`🔑 ANTHROPIC_AUTH_TOKEN: [已设置]`);
216
+ console.log(`[KEY] ANTHROPIC_AUTH_TOKEN: [已设置]`);
219
217
  }
220
218
 
221
219
  if (envVars.OPENAI_API_KEY) {
222
- console.log(`🔑 OPENAI_API_KEY: [已设置]`);
220
+ console.log(`[KEY] OPENAI_API_KEY: [已设置]`);
223
221
  }
224
222
  }
225
223
 
@@ -227,7 +225,8 @@ function generateSimpleScript(envVars, envType, envName) {
227
225
  const lines = [];
228
226
 
229
227
  if (process.platform === 'win32') {
230
- // Windows PowerShell
228
+ // Windows PowerShell - 使用编码工具设置
229
+ lines.push(EncodingUtils.generatePowerShellEncodingSetup());
231
230
  lines.push(`Write-Host "Switched to ${envType} environment: ${envName}" -ForegroundColor Green`);
232
231
 
233
232
  if (envVars.JAVA_HOME) {
@@ -381,6 +380,9 @@ function handleNodeOnlyMode(args) {
381
380
  }
382
381
 
383
382
  function run() {
383
+ // 设置Windows控制台编码
384
+ EncodingUtils.setWindowsConsoleEncoding();
385
+
384
386
  const binaryPath = buildBinaryPath();
385
387
 
386
388
  if (!binaryPath) {
@@ -432,11 +434,11 @@ function run() {
432
434
 
433
435
  if (result.error) {
434
436
  if (result.error.code === 'EACCES' && process.platform !== 'win32') {
435
- console.error(`❌ Permission denied. The fnva binary is not executable.`);
436
- console.error(`💡 To fix this, run: sudo chmod +x "${binaryPath}"`);
437
- console.error(` Or reinstall: npm install -g fnva --force`);
437
+ console.error(`[ERROR] Permission denied. The fnva binary is not executable.`);
438
+ console.error(`[INFO] To fix this, run: sudo chmod +x "${binaryPath}"`);
439
+ console.error(`[INFO] Or reinstall: npm install -g fnva --force`);
438
440
  } else {
439
- console.error(`Failed to execute fnva: ${result.error.message}`);
441
+ console.error(`[ERROR] Failed to execute fnva: ${result.error.message}`);
440
442
  }
441
443
  process.exit(result.status ?? 1);
442
444
  }
@@ -456,16 +458,16 @@ function run() {
456
458
  // Windows:默认不启动新的会话;可通过 --session 开启旧行为
457
459
  if (process.platform === 'win32') {
458
460
  if (hasSessionFlag(args)) {
459
- console.log(`✅ Switched to ${envType} environment: ${envName}`);
460
- console.log(`🚀 Starting new PowerShell session with ${envName} environment...`);
461
+ console.log(`[OK] Switched to ${envType} environment: ${envName}`);
462
+ console.log(`[INFO] Starting new PowerShell session with ${envName} environment...`);
461
463
  console.log(`Type "exit" to return to previous session\n`);
462
464
 
463
465
  try {
464
466
  const os = require('os');
465
- const fs = require('fs');
466
467
  const tempScript = os.tmpdir() + '\\fnva_env_' + Date.now() + '.ps1';
467
468
  const fullScript = script + '\n';
468
- fs.writeFileSync(tempScript, fullScript, 'utf8');
469
+ // 使用编码工具写入文件
470
+ EncodingUtils.writeFileWithEncoding(tempScript, fullScript);
469
471
  const { spawn } = require('child_process');
470
472
  const ps = spawn('powershell', ['-NoExit', '-ExecutionPolicy', 'Bypass', '-File', tempScript], {
471
473
  stdio: 'inherit',
@@ -473,7 +475,7 @@ function run() {
473
475
  });
474
476
  ps.on('exit', () => {
475
477
  try { fs.unlinkSync(tempScript); } catch (_) {}
476
- console.log('👋 Returned to original session');
478
+ console.log('[INFO] Returned to original session');
477
479
  });
478
480
  return;
479
481
  } catch (error) {
@@ -501,7 +503,8 @@ function run() {
501
503
 
502
504
  try {
503
505
  const tempFile = path.join(os.tmpdir(), `fnva_auto_${Date.now()}.ps1`);
504
- fs.writeFileSync(tempFile, simpleScript, 'utf8');
506
+ // 使用编码工具写入文件
507
+ EncodingUtils.writeFileWithEncoding(tempFile, simpleScript);
505
508
 
506
509
  // 使用 PowerShell 执行脚本
507
510
  spawn('powershell', ['-ExecutionPolicy', 'Bypass', '-File', tempFile], {
@@ -511,10 +514,10 @@ function run() {
511
514
  try { fs.unlinkSync(tempFile); } catch (_) {}
512
515
  });
513
516
 
514
- console.log(' 环境已自动切换');
517
+ console.log('[OK] 环境已自动切换');
515
518
  return;
516
519
  } catch (error) {
517
- console.warn('⚠️ 自动执行失败,回退到脚本输出');
520
+ console.warn('[WARN] 自动执行失败,回退到脚本输出');
518
521
  }
519
522
  }
520
523
 
@@ -524,9 +527,9 @@ function run() {
524
527
  }
525
528
  } else {
526
529
  // Unix-like systems: 显示使用说明
527
- console.log(`✅ Switched to ${envType} environment: ${envName}`);
530
+ console.log(`[OK] Switched to ${envType} environment: ${envName}`);
528
531
  console.log('');
529
- console.log('💡 To apply this environment, run:');
532
+ console.log('[INFO] To apply this environment, run:');
530
533
  console.log(` node bin/fnva.js ${args.join(' ')} | bash`);
531
534
  }
532
535
  } else {
@@ -548,11 +551,11 @@ function run() {
548
551
 
549
552
  if (result.error) {
550
553
  if (result.error.code === 'EACCES' && process.platform !== 'win32') {
551
- console.error(`❌ Permission denied. The fnva binary is not executable.`);
552
- console.error(`💡 To fix this, run: sudo chmod +x "${binaryPath}"`);
553
- console.error(` Or reinstall: npm install -g fnva --force`);
554
+ console.error(`[ERROR] Permission denied. The fnva binary is not executable.`);
555
+ console.error(`[INFO] To fix this, run: sudo chmod +x "${binaryPath}"`);
556
+ console.error(`[INFO] Or reinstall: npm install -g fnva --force`);
554
557
  } else {
555
- console.error(`Failed to execute fnva: ${result.error.message}`);
558
+ console.error(`[ERROR] Failed to execute fnva: ${result.error.message}`);
556
559
  }
557
560
  process.exit(result.status ?? 1);
558
561
  }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * 编码处理工具模块
3
+ * 用于统一处理跨平台字符编码问题
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ /**
10
+ * 编码处理工具类
11
+ */
12
+ class EncodingUtils {
13
+ /**
14
+ * 设置Windows控制台编码为UTF-8
15
+ */
16
+ static setWindowsConsoleEncoding() {
17
+ if (process.platform === 'win32') {
18
+ try {
19
+ // 尝试设置控制台编码为UTF-8
20
+ const { execSync } = require('child_process');
21
+ execSync('chcp 65001 > nul 2>&1', { stdio: 'ignore' });
22
+
23
+ // 设置Node.js输出编码
24
+ if (process.stdout._handle && process.stdout._handle.setEncoding) {
25
+ process.stdout._handle.setEncoding('utf8');
26
+ }
27
+ if (process.stderr._handle && process.stderr._handle.setEncoding) {
28
+ process.stderr._handle.setEncoding('utf8');
29
+ }
30
+ } catch (error) {
31
+ // 静默忽略错误,避免影响正常功能
32
+ }
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 为文件内容添加适当的编码标记
38
+ * @param {string} content - 文件内容
39
+ * @param {string} filePath - 文件路径(用于确定文件类型)
40
+ * @returns {string} - 处理后的内容
41
+ */
42
+ static addEncodingSignature(content, filePath) {
43
+ // 为Windows PowerShell脚本添加BOM
44
+ if (process.platform === 'win32' && this.isPowerShellScript(filePath)) {
45
+ return '\ufeff' + content;
46
+ }
47
+ return content;
48
+ }
49
+
50
+ /**
51
+ * 检查文件是否为PowerShell脚本
52
+ * @param {string} filePath - 文件路径
53
+ * @returns {boolean} - 是否为PowerShell脚本
54
+ */
55
+ static isPowerShellScript(filePath) {
56
+ const ext = path.extname(filePath).toLowerCase();
57
+ return ext === '.ps1';
58
+ }
59
+
60
+ /**
61
+ * 安全地写入文件,自动处理编码
62
+ * @param {string} filePath - 文件路径
63
+ * @param {string} content - 文件内容
64
+ * @param {string} encoding - 编码格式,默认为utf8
65
+ */
66
+ static writeFileWithEncoding(filePath, content, encoding = 'utf8') {
67
+ const processedContent = this.addEncodingSignature(content, filePath);
68
+ fs.writeFileSync(filePath, processedContent, encoding);
69
+ }
70
+
71
+ /**
72
+ * 生成PowerShell编码设置脚本
73
+ * @returns {string} - PowerShell编码设置代码
74
+ */
75
+ static generatePowerShellEncodingSetup() {
76
+ return [
77
+ '# 设置UTF-8编码以正确显示中文',
78
+ '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
79
+ '$OutputEncoding = [System.Console]::OutputEncoding',
80
+ ''
81
+ ].join('\n');
82
+ }
83
+
84
+ /**
85
+ * 检测系统默认编码
86
+ * @returns {string} - 系统编码名称
87
+ */
88
+ static detectSystemEncoding() {
89
+ if (process.platform === 'win32') {
90
+ try {
91
+ const { execSync } = require('child_process');
92
+ const result = execSync('chcp', { encoding: 'utf8' });
93
+ const match = result.match(/活动代码页: (\d+)/);
94
+ return match ? `cp${match[1]}` : 'utf8';
95
+ } catch (e) {
96
+ return 'cp936'; // Windows中文默认编码
97
+ }
98
+ } else {
99
+ return 'utf8'; // Unix-like系统默认UTF-8
100
+ }
101
+ }
102
+
103
+ /**
104
+ * 创建临时PowerShell脚本文件
105
+ * @param {string} content - 脚本内容
106
+ * @param {string} prefix - 文件名前缀
107
+ * @returns {string} - 临时文件路径
108
+ */
109
+ static createTempPowerShellScript(content, prefix = 'fnva') {
110
+ const os = require('os');
111
+ const path = require('path');
112
+
113
+ const tempDir = os.tmpdir();
114
+ const timestamp = Date.now();
115
+ const scriptFile = path.join(tempDir, `${prefix}_${timestamp}.ps1`);
116
+
117
+ // 在脚本开头添加编码设置
118
+ const encodingSetup = this.generatePowerShellEncodingSetup();
119
+ const fullContent = encodingSetup + content;
120
+
121
+ this.writeFileWithEncoding(scriptFile, fullContent);
122
+
123
+ return scriptFile;
124
+ }
125
+
126
+ /**
127
+ * 安全地执行PowerShell脚本,确保编码正确
128
+ * @param {string} scriptPath - 脚本路径
129
+ * @param {Array} args - 传递给PowerShell的参数
130
+ * @param {Object} options - 执行选项
131
+ * @returns {Object} - 执行结果
132
+ */
133
+ static executePowerShellScript(scriptPath, args = [], options = {}) {
134
+ const { spawn } = require('child_process');
135
+
136
+ const defaultOptions = {
137
+ stdio: 'inherit',
138
+ shell: false,
139
+ ...options
140
+ };
141
+
142
+ const psArgs = [
143
+ '-ExecutionPolicy', 'Bypass',
144
+ '-File', scriptPath,
145
+ ...args
146
+ ];
147
+
148
+ return spawn('powershell', psArgs, defaultOptions);
149
+ }
150
+ }
151
+
152
+ module.exports = EncodingUtils;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fnva",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "跨平台环境切换工具,支持 Java 和 LLM 环境配置",
5
5
  "author": "protagonistss",
6
6
  "license": "MIT",
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "files": [
20
20
  "bin/",
21
+ "lib/",
21
22
  "platforms/",
22
23
  "scripts/",
23
24
  "README.md",
package/platforms/fnva CHANGED
Binary file
Binary file
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+
3
+ # 代码风格检查脚本
4
+ # 用于检查和格式化fnva项目的代码风格
5
+
6
+ set -e
7
+
8
+ echo "🔍 开始代码风格检查..."
9
+
10
+ # 检查是否安装了必要的工具
11
+ check_tool() {
12
+ if ! command -v $1 &> /dev/null; then
13
+ echo "❌ $1 未安装,请先安装 $1"
14
+ echo "安装命令: cargo install $1"
15
+ exit 1
16
+ fi
17
+ }
18
+
19
+ echo "📦 检查工具是否安装..."
20
+ check_tool "rustfmt"
21
+ check_tool "clippy"
22
+
23
+ # 运行rustfmt格式化代码
24
+ echo "🎨 格式化代码..."
25
+ cargo fmt --all
26
+
27
+ # 运行clippy检查
28
+ echo "🔍 运行Clippy静态分析..."
29
+ cargo clippy --all-targets --all-features -- -D warnings
30
+
31
+ # 检查是否有未提交的格式化更改
32
+ echo "📝 检查格式化结果..."
33
+ if ! git diff --exit-code --quiet; then
34
+ echo "⚠️ 代码格式化产生了更改,请提交这些更改"
35
+ echo "运行 'git add .' 和 'git commit' 来提交格式化结果"
36
+ exit 1
37
+ fi
38
+
39
+ # 检查文档注释
40
+ echo "📚 检查文档注释..."
41
+ cargo doc --no-deps --document-private-items 2>/dev/null | grep -E "(warning|error)" || true
42
+
43
+ # 检查重复的代码
44
+ echo "🔄 检查重复代码..."
45
+ if command -v cargo-dup &> /dev/null; then
46
+ cargo dup
47
+ else
48
+ echo "💡 提示: 安装 cargo-dup 可以检查重复代码 (cargo install cargo-dup)"
49
+ fi
50
+
51
+ # 检查依赖安全性
52
+ echo "🔒 检查依赖安全性..."
53
+ if command -v cargo-audit &> /dev/null; then
54
+ cargo audit
55
+ else
56
+ echo "💡 提示: 安装 cargo-audit 可以检查依赖安全性 (cargo install cargo-audit)"
57
+ fi
58
+
59
+ # 检查未使用的依赖
60
+ echo "🧹 检查未使用的依赖..."
61
+ if command -v cargo-udeps &> /dev/null; then
62
+ cargo udeps --all-targets
63
+ else
64
+ echo "💡 提示: 安装 cargo-udeps 可以检查未使用的依赖 (cargo install cargo-udeps)"
65
+ fi
66
+
67
+ # 统计代码行数
68
+ echo "📊 代码统计:"
69
+ echo "总Rust代码行数: $(find src -name '*.rs' -exec wc -l {} + | tail -1)"
70
+ echo "测试代码行数: $(find tests -name '*.rs' -exec wc -l {} + 2>/dev/null | tail -1 || echo "0")"
71
+
72
+ echo "✅ 代码风格检查完成!"
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ # 代码格式化脚本
4
+ # 自动格式化fnva项目的代码
5
+
6
+ set -e
7
+
8
+ echo "🎨 开始格式化代码..."
9
+
10
+ # 格式化所有Rust代码
11
+ cargo fmt --all
12
+
13
+ # 检查格式化结果
14
+ if git diff --exit-code --quiet; then
15
+ echo "✅ 代码已经是标准格式"
16
+ else
17
+ echo "📝 代码已格式化,请查看更改:"
18
+ git diff --stat
19
+ echo ""
20
+ echo "💡 提示: 使用 'git add .' 和 'git commit' 提交格式化更改"
21
+ fi
22
+
23
+ echo "✅ 格式化完成!"