fnva 0.0.42 → 0.0.43

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
@@ -14,16 +14,18 @@ Cross-platform environment switcher for Java, Claude Code (CC), and LLM setups.
14
14
 
15
15
  ## Quick start
16
16
 
17
- - Init shell (Bash/Zsh): `eval "$(fnva env env --shell bash)"`
17
+ - Init shell (Bash/Zsh): `eval "$(fnva env env --shell bash)"`
18
18
  PowerShell: `fnva env env --shell powershell | Out-String | Invoke-Expression`
19
19
  - Scan Java: `fnva java scan`
20
20
  - Switch Java for current session: `eval "$(fnva java use jdk-17)"`
21
21
  - Switch CC profile: `eval "$(fnva cc use glmcc)"`
22
+ - New terminals auto-restore your last active environment
22
23
 
23
24
  ## What it does
24
25
 
25
26
  - Manages multiple Java, CC, and generic LLM configurations.
26
27
  - Generates shell snippets to activate environments per session or by default.
28
+ - **Auto-restore** — new terminals automatically restore the last active CC/Java environment.
27
29
  - Stores config at `~/.fnva/config.toml` (Windows: `%USERPROFILE%\.fnva\config.toml`).
28
30
  - Ships as a single binary; no background daemon.
29
31
 
package/bin/fnva.js CHANGED
@@ -44,16 +44,12 @@ function buildBinaryPath() {
44
44
  return null;
45
45
  }
46
46
 
47
- // 如果设置了 FNVA_AUTO_MODE,自动使用 Node.js 模式
48
- if (process.env.FNVA_AUTO_MODE === '1') {
49
- return null;
50
- }
51
-
52
47
  const platform = resolvePlatform();
53
48
  const binaryCandidates = [];
54
49
 
55
50
  // 1. Prebuilt binary shipped with the npm package
56
- binaryCandidates.push(platformBinaryPath(platform));
51
+ const npmBinaryPath = platformBinaryPath(platform);
52
+ binaryCandidates.push(npmBinaryPath);
57
53
 
58
54
  // 2. User-provided override via environment variable
59
55
  if (process.env.FNVA_NATIVE_PATH) {
@@ -70,12 +66,32 @@ function buildBinaryPath() {
70
66
  binaryCandidates.push(path.join(targetDir, 'debug', 'fnva'));
71
67
  }
72
68
 
69
+ // Debug: Show all candidates and their existence
70
+ if (process.env.FNVA_DEBUG === '1') {
71
+ console.log('[DEBUG] Looking for fnva binary...');
72
+ console.log('[DEBUG] Platform:', platform, 'Arch:', resolveArch());
73
+ console.log('[DEBUG] Binary candidates:');
74
+ binaryCandidates.forEach((candidate, index) => {
75
+ const exists = candidate && fs.existsSync(candidate);
76
+ console.log(` ${index + 1}. ${candidate} - ${exists ? 'EXISTS' : 'MISSING'}`);
77
+ });
78
+ }
79
+
73
80
  for (const candidate of binaryCandidates) {
74
81
  if (candidate && fs.existsSync(candidate)) {
82
+ if (process.env.FNVA_DEBUG === '1') {
83
+ console.log(`[DEBUG] Found binary at: ${candidate}`);
84
+ }
75
85
  return candidate;
76
86
  }
77
87
  }
78
88
 
89
+ if (process.env.FNVA_DEBUG === '1') {
90
+ console.log('[DEBUG] No binary found, falling back to Node.js mode');
91
+ console.log('[DEBUG] Expected npm package binary path:', npmBinaryPath);
92
+ console.log('[DEBUG] npmBinaryPath exists:', fs.existsSync(npmBinaryPath));
93
+ }
94
+
79
95
  return null;
80
96
  }
81
97
 
@@ -280,15 +296,20 @@ function handleNodeOnlyMode(args) {
280
296
  const path = require('path');
281
297
  const os = require('os');
282
298
 
283
- // 简单的命令处理
284
- if (args.length === 0) {
285
- console.log('fnva - 环境管理工具 (Node.js 模式)');
299
+ // 只支持基本帮助信息
300
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
301
+ console.log('fnva - 环境管理工具 (Node.js 降级模式)');
286
302
  console.log('');
287
- console.log('支持的命令:');
303
+ console.log('⚠️ 当前运行在 Node.js 降级模式,功能有限');
304
+ console.log('');
305
+ console.log('解决方法:');
306
+ console.log('1. 确保 npm 包包含平台二进制文件');
307
+ console.log('2. 重新安装: npm install -g fnva --force');
308
+ console.log('3. 或者直接下载原生二进制文件');
309
+ console.log('');
310
+ console.log('临时可用功能:');
288
311
  console.log(' java list - 列出 Java 环境');
289
312
  console.log(' java use <n> - 切换 Java 环境');
290
- console.log('');
291
- console.log('注意: Node.js 模式功能有限,建议使用原生二进制版本。');
292
313
  return;
293
314
  }
294
315
 
@@ -368,7 +389,14 @@ function handleNodeOnlyMode(args) {
368
389
  process.exit(1);
369
390
  }
370
391
  } else {
371
- console.error(`Command '${args[0]}' not supported in Node.js mode`);
392
+ console.error(`❌ Command '${args[0]}' requires native binary mode`);
393
+ console.error('');
394
+ console.error('当前运行在 Node.js 降级模式,不支持此命令');
395
+ console.error('');
396
+ console.error('解决方案:');
397
+ console.error('1. 重新安装 npm 包: npm install -g fnva --force');
398
+ console.error('2. 从 GitHub Release 下载原生二进制文件');
399
+ console.error('3. 或者设置 FNVA_SKIP_NATIVE=1 强制使用此模式(功能受限)');
372
400
  process.exit(1);
373
401
  }
374
402
  }
@@ -377,23 +405,90 @@ function run() {
377
405
  // 设置Windows控制台编码
378
406
  EncodingUtils.setWindowsConsoleEncoding();
379
407
 
408
+ // 强制显示调试信息
409
+ const showDebug = process.env.FNVA_DEBUG === '1' || process.argv.includes('--debug');
410
+
411
+ if (showDebug) {
412
+ console.log('=== FNVA DEBUG INFORMATION ===');
413
+ console.log('Node.js version:', process.version);
414
+ console.log('Platform:', process.platform);
415
+ console.log('Architecture:', process.arch);
416
+ console.log('Node binary:', process.execPath);
417
+ console.log('Script directory:', __dirname);
418
+ console.log('Working directory:', process.cwd());
419
+ console.log('Environment variables:');
420
+ console.log(' FNVA_DEBUG:', process.env.FNVA_DEBUG);
421
+ console.log(' FNVA_SKIP_NATIVE:', process.env.FNVA_SKIP_NATIVE);
422
+ console.log('Command line args:', process.argv);
423
+ console.log('');
424
+ }
425
+
380
426
  const binaryPath = buildBinaryPath();
381
427
 
428
+ if (showDebug) {
429
+ console.log('=== BINARY SEARCH RESULTS ===');
430
+ console.log('Binary path found:', binaryPath);
431
+
432
+ // 手动检查所有可能的路径
433
+ const fs = require('fs');
434
+ const path = require('path');
435
+
436
+ const scriptDir = __dirname;
437
+ const projectRoot = path.resolve(scriptDir, '..');
438
+ const platform = process.platform;
439
+ const arch = process.arch;
440
+ const platformDir = `${platform}-${arch}`;
441
+ const binaryName = platform === 'win32' ? 'fnva.exe' : 'fnva';
442
+ const expectedPath = path.join(projectRoot, 'platforms', platformDir, binaryName);
443
+
444
+ console.log('Expected binary path:', expectedPath);
445
+ console.log('Expected path exists:', fs.existsSync(expectedPath));
446
+
447
+ // 检查platforms目录结构
448
+ console.log('');
449
+ console.log('=== PLATFORMS DIRECTORY ===');
450
+ const platformsDir = path.join(projectRoot, 'platforms');
451
+ if (fs.existsSync(platformsDir)) {
452
+ const platforms = fs.readdirSync(platformsDir, { withFileTypes: true });
453
+ platforms.forEach(item => {
454
+ if (item.isDirectory()) {
455
+ const platformPath = path.join(platformsDir, item.name);
456
+ const files = fs.readdirSync(platformPath);
457
+ console.log(`platforms/${item.name}/:`, files);
458
+ }
459
+ });
460
+ } else {
461
+ console.log('platforms directory does not exist');
462
+ }
463
+
464
+ console.log('=== END DEBUG ===');
465
+ console.log('');
466
+ }
467
+
382
468
  if (!binaryPath) {
383
- if (process.env.FNVA_SKIP_NATIVE === '1' || process.env.FNVA_AUTO_MODE === '1') {
469
+ if (process.env.FNVA_SKIP_NATIVE === '1') {
470
+ if (showDebug) {
471
+ console.log('Falling back to Node.js mode (FNVA_SKIP_NATIVE set)');
472
+ }
384
473
  // 纯 Node.js 模式 - 实现基本的环境切换功能
385
474
  const args = process.argv.slice(2);
386
475
  handleNodeOnlyMode(args);
387
476
  return;
388
477
  }
389
478
 
390
- console.error('Error: fnva native binary not found.');
479
+ console.error('Error: fnva native binary not found.');
391
480
  console.error('');
392
- console.error("Please either:");
393
- console.error(" 1) Run 'npm run build' (or 'npm run build:all') to produce platform binaries,");
394
- console.error(" 2) Install a release package that includes the platforms directory, or");
395
- console.error(" 3) Set FNVA_NATIVE_PATH to the full path of an existing fnva executable.");
396
- console.error(" 4) Set FNVA_SKIP_NATIVE=1 to use Node.js mode (limited functionality).");
481
+
482
+ if (showDebug) {
483
+ console.error('🔍 Debug information is shown above');
484
+ console.error('');
485
+ }
486
+
487
+ console.error("💡 Solutions:");
488
+ console.error(" 1) Reinstall npm package: npm install -g fnva --force");
489
+ console.error(" 2) Download binary from GitHub Release");
490
+ console.error(" 3) Set FNVA_SKIP_NATIVE=1 to use Node.js mode (limited functionality)");
491
+ console.error(" 4) Set FNVA_DEBUG=1 to show debug information");
397
492
  process.exit(1);
398
493
  }
399
494
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fnva",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "跨平台环境切换工具,支持 Java 和 LLM 环境配置",
5
5
  "author": "protagonistss",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  "build:platforms:sh": "bash scripts/build-platforms.sh",
18
18
  "build:all": "scripts/build-all.sh",
19
19
  "check-permissions": "node scripts/check-permissions.js",
20
- "postuninstall": "echo 'Uninstalling fnva shell integration...' && node scripts/uninstall-shell-integration.js"
20
+ "postuninstall": "node scripts/uninstall-shell-integration.js"
21
21
  },
22
22
  "files": [
23
23
  "bin/",
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // Shell integration installer for fnva (npm postinstall helper).
4
- // Generates a minimal shell function that invokes the real fnva binary.
4
+ // Uses fnva's built-in template system for shell integration scripts.
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
  const os = require('os');
9
+ const { spawnSync } = require('child_process');
9
10
 
10
11
  // Absolute path to the packaged fnva shim (bin/fnva.js)
11
12
  const FNVA_SHIM = path.resolve(__dirname, '..', 'bin', 'fnva.js');
@@ -37,52 +38,70 @@ function getShellConfigPath(shell) {
37
38
  }
38
39
  }
39
40
 
41
+ // Get integration script from fnva command (uses Rust templates)
42
+ function getIntegrationScript(shell) {
43
+ try {
44
+ const result = spawnSync('node', [FNVA_SHIM, 'env', 'shell-integration', '-s', shell], {
45
+ encoding: 'utf8',
46
+ cwd: path.resolve(__dirname, '..')
47
+ });
48
+
49
+ if (result.status === 0 && result.stdout) {
50
+ return result.stdout;
51
+ }
52
+
53
+ console.log(`Warning: Failed to get integration script from fnva (exit code: ${result.status})`);
54
+ if (result.stderr) {
55
+ console.log(`stderr: ${result.stderr}`);
56
+ }
57
+ return '';
58
+ } catch (error) {
59
+ console.log(`Warning: Failed to call fnva for integration script: ${error.message}`);
60
+ return '';
61
+ }
62
+ }
63
+
40
64
  function getPowerShellFunction() {
65
+ // Get integration script from Rust template
66
+ const integrationScript = getIntegrationScript('powershell');
67
+
41
68
  return `
69
+ ${integrationScript}
70
+
42
71
  # fnva auto integration (added by npm install)
43
72
  function fnva {
44
73
  if ($args.Count -ge 2 -and ($args[0] -eq "java" -or $args[0] -eq "llm" -or $args[0] -eq "cc") -and ($args[1] -eq "use")) {
45
74
  $tempFile = Join-Path $env:TEMP ("fnva_script_" + (Get-Random) + ".ps1")
46
75
 
47
- $env:FNVAAUTOMODE = "1"
48
76
  try {
49
- $output = cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" 2>&1
77
+ # Directly pipe output to temp file to avoid encoding issues
78
+ & fnva.cmd @args 2>&1 | Out-File -FilePath $tempFile -Encoding UTF8
50
79
 
51
- if ($output -match '\\$env:' -or $output -match 'Write-Host') {
52
- $output | Out-File -FilePath $tempFile -Encoding UTF8
53
- try { & $tempFile } catch { Write-Host "Error executing script: $_" -ForegroundColor Red }
80
+ # Check if file contains environment variables
81
+ $content = Get-Content $tempFile -Raw -Encoding UTF8
82
+ if ($content -match '\\$env:' -or $content -match 'Write-Host') {
83
+ # Use dot sourcing to execute in current scope
84
+ . $tempFile
54
85
  } else {
55
- $output
86
+ $content
56
87
  }
57
88
  } finally {
58
- $env:FNVAAUTOMODE = ""
59
89
  if (Test-Path $tempFile) { Remove-Item $tempFile -ErrorAction SilentlyContinue }
60
90
  }
61
91
  } else {
62
- $env:FNVAAUTOMODE = "1"
63
- try { cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" } finally { $env:FNVAAUTOMODE = "" }
92
+ & fnva.cmd @args
64
93
  }
65
94
  }
66
95
  `;
67
96
  }
68
97
 
69
- function resolveBinaryPath() {
70
- // Prefer real binary, avoid this function name
71
- if (process.platform === 'win32') {
72
- return process.argv[0];
73
- }
74
- let bin = '';
75
- try {
76
- const which = require('child_process').spawnSync('which', ['fnva'], { encoding: 'utf8' });
77
- if (which.status === 0) {
78
- bin = (which.stdout || '').trim();
79
- }
80
- } catch {}
81
- return bin;
82
- }
83
-
84
98
  function getBashFunction() {
99
+ // Get integration script from Rust template
100
+ const integrationScript = getIntegrationScript('bash');
101
+
85
102
  return `
103
+ ${integrationScript}
104
+
86
105
  # fnva auto integration (added by npm install)
87
106
  fnva() {
88
107
  local __fnva_bin="${FNVA_SHIM}"
@@ -96,18 +115,23 @@ fnva() {
96
115
  temp_file="$(mktemp)"
97
116
  chmod +x "$temp_file"
98
117
 
99
- FNVA_AUTO_MODE=1 "$__fnva_bin" "$@" > "$temp_file"
118
+ "$__fnva_bin" "$@" > "$temp_file"
100
119
  source "$temp_file"
101
120
  rm -f "$temp_file"
102
121
  else
103
- FNVA_AUTO_MODE=1 "$__fnva_bin" "$@"
122
+ "$__fnva_bin" "$@"
104
123
  fi
105
124
  }
106
125
  `;
107
126
  }
108
127
 
109
128
  function getFishFunction() {
129
+ // Get integration script from Rust template
130
+ const integrationScript = getIntegrationScript('fish');
131
+
110
132
  return `
133
+ ${integrationScript}
134
+
111
135
  # fnva auto integration (added by npm install)
112
136
  function fnva
113
137
  set __fnva_bin "${FNVA_SHIM}"
@@ -119,11 +143,11 @@ function fnva
119
143
  if test (count $argv) -ge 2; and string match -q -r "^(java|llm|cc)$" $argv[1]; and test $argv[2] = "use"
120
144
  set temp_file (mktemp)
121
145
  chmod +x $temp_file
122
- env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv > $temp_file
146
+ env "$__fnva_bin" $argv > $temp_file
123
147
  source $temp_file
124
148
  rm -f $temp_file
125
149
  else
126
- env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv
150
+ env "$__fnva_bin" $argv
127
151
  end
128
152
  end
129
153
  `;
@@ -135,7 +159,7 @@ function getShellFunction(shell) {
135
159
  return getPowerShellFunction();
136
160
  case 'bash':
137
161
  case 'zsh':
138
- return getBashFunction();
162
+ return getBashFunction(); // zsh 使用和 bash 相同的语法
139
163
  case 'fish':
140
164
  return getFishFunction();
141
165
  default:
@@ -174,6 +198,11 @@ function installShellIntegration() {
174
198
 
175
199
  const functionCode = getShellFunction(shell);
176
200
 
201
+ if (!functionCode) {
202
+ console.log('Failed to generate shell integration script');
203
+ return false;
204
+ }
205
+
177
206
  if (fs.existsSync(configPath)) {
178
207
  const content = fs.readFileSync(configPath, 'utf8');
179
208
  fs.writeFileSync(configPath, content + '\n' + functionCode);
@@ -113,33 +113,48 @@ function removeShellIntegration(configPath, shell) {
113
113
 
114
114
  function main() {
115
115
  console.log('🧹 fnva shell integration uninstaller');
116
+ console.log('npm install location:', __dirname);
117
+ console.log('Current platform:', process.platform);
118
+ console.log('Detected shell:', process.env.SHELL || 'unknown');
116
119
 
117
120
  const shell = detectShell();
118
121
  const paths = getShellConfigPaths(shell);
119
122
 
123
+ console.log(`Config paths for ${shell}:`, paths);
124
+
120
125
  if (!paths.length) {
121
126
  console.log(`⚠️ Unsupported shell: ${shell}`);
127
+ console.log('No config files found for this shell');
122
128
  return;
123
129
  }
124
130
 
131
+ console.log(`Attempting to clean config files...`);
125
132
  const success = removeShellIntegration(null, shell);
126
133
 
127
134
  if (success) {
135
+ console.log('✅ Shell integration successfully removed');
128
136
  console.log('🔄 Reload your shell config:');
129
137
  switch (shell) {
130
138
  case 'powershell':
131
139
  console.log(' . $PROFILE');
140
+ console.log(' Or start new PowerShell instance');
132
141
  break;
133
142
  case 'bash':
134
143
  console.log(' source ~/.bashrc');
144
+ console.log(' Or: exec bash');
135
145
  break;
136
146
  case 'zsh':
137
147
  console.log(' source ~/.zshrc');
148
+ console.log(' Or: exec zsh');
138
149
  break;
139
150
  case 'fish':
140
151
  console.log(' source ~/.config/fish/config.fish');
152
+ console.log(' Or: exec fish');
141
153
  break;
142
154
  }
155
+ } else {
156
+ console.log('⚠️ No fnva shell integration found in any config files');
157
+ console.log(' (This is normal if shell integration was never installed)');
143
158
  }
144
159
  }
145
160