fnva 0.0.42 → 0.0.44

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,19 @@ 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)"`
18
- PowerShell: `fnva env env --shell powershell | Out-String | Invoke-Expression`
17
+ - Init shell (Bash/Zsh): `eval "$(fnva env --shell bash)"`
18
+ PowerShell: `fnva env --shell powershell | Out-String | Invoke-Expression`
19
+ Fish: `fnva env --shell fish | source`
19
20
  - Scan Java: `fnva java scan`
20
- - Switch Java for current session: `eval "$(fnva java use jdk-17)"`
21
- - Switch CC profile: `eval "$(fnva cc use glmcc)"`
21
+ - Switch Java for current session: `fnva java use jdk-17` (with shell integration)
22
+ - Switch CC profile: `fnva cc use glmcc` (with shell integration)
23
+ - New terminals auto-restore your last active environment
22
24
 
23
25
  ## What it does
24
26
 
25
27
  - Manages multiple Java, CC, and generic LLM configurations.
26
28
  - Generates shell snippets to activate environments per session or by default.
29
+ - **Auto-restore** — new terminals automatically restore the last active CC/Java environment.
27
30
  - Stores config at `~/.fnva/config.toml` (Windows: `%USERPROFILE%\.fnva\config.toml`).
28
31
  - Ships as a single binary; no background daemon.
29
32
 
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.44",
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,13 +1,14 @@
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
+ // Writes a single line into the shell profile: eval "$(fnva env --shell <shell>)"
5
+ // The fnva binary generates the full init script (autoload + wrapper).
5
6
 
6
7
  const fs = require('fs');
7
8
  const path = require('path');
8
9
  const os = require('os');
10
+ const { spawnSync } = require('child_process');
9
11
 
10
- // Absolute path to the packaged fnva shim (bin/fnva.js)
11
12
  const FNVA_SHIM = path.resolve(__dirname, '..', 'bin', 'fnva.js');
12
13
 
13
14
  function detectShell() {
@@ -37,118 +38,26 @@ function getShellConfigPath(shell) {
37
38
  }
38
39
  }
39
40
 
40
- function getPowerShellFunction() {
41
- return `
42
- # fnva auto integration (added by npm install)
43
- function fnva {
44
- 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
- $tempFile = Join-Path $env:TEMP ("fnva_script_" + (Get-Random) + ".ps1")
46
-
47
- $env:FNVAAUTOMODE = "1"
48
- try {
49
- $output = cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" 2>&1
50
-
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 }
54
- } else {
55
- $output
56
- }
57
- } finally {
58
- $env:FNVAAUTOMODE = ""
59
- if (Test-Path $tempFile) { Remove-Item $tempFile -ErrorAction SilentlyContinue }
60
- }
61
- } else {
62
- $env:FNVAAUTOMODE = "1"
63
- try { cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" } finally { $env:FNVAAUTOMODE = "" }
64
- }
65
- }
66
- `;
67
- }
68
-
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
- function getBashFunction() {
85
- return `
86
- # fnva auto integration (added by npm install)
87
- fnva() {
88
- local __fnva_bin="${FNVA_SHIM}"
89
- if [[ ! -x "$__fnva_bin" ]]; then
90
- echo "fnva: binary not found in PATH" >&2
91
- return 127
92
- fi
93
-
94
- if [[ $# -ge 2 && ("$1" == "java" || "$1" == "llm" || "$1" == "cc") && "$2" == "use" ]]; then
95
- local temp_file
96
- temp_file="$(mktemp)"
97
- chmod +x "$temp_file"
98
-
99
- FNVA_AUTO_MODE=1 "$__fnva_bin" "$@" > "$temp_file"
100
- source "$temp_file"
101
- rm -f "$temp_file"
102
- else
103
- FNVA_AUTO_MODE=1 "$__fnva_bin" "$@"
104
- fi
105
- }
106
- `;
107
- }
108
-
109
- function getFishFunction() {
110
- return `
111
- # fnva auto integration (added by npm install)
112
- function fnva
113
- set __fnva_bin "${FNVA_SHIM}"
114
- if test ! -x "$__fnva_bin"
115
- echo "fnva: binary not found in PATH" >&2
116
- return 127
117
- end
118
-
119
- if test (count $argv) -ge 2; and string match -q -r "^(java|llm|cc)$" $argv[1]; and test $argv[2] = "use"
120
- set temp_file (mktemp)
121
- chmod +x $temp_file
122
- env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv > $temp_file
123
- source $temp_file
124
- rm -f $temp_file
125
- else
126
- env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv
127
- end
128
- end
129
- `;
130
- }
131
-
132
- function getShellFunction(shell) {
41
+ function getIntegrationLine(shell) {
133
42
  switch (shell) {
134
43
  case 'powershell':
135
- return getPowerShellFunction();
44
+ return 'fnva env --shell powershell | Out-String | Invoke-Expression';
136
45
  case 'bash':
137
46
  case 'zsh':
138
- return getBashFunction();
47
+ return 'eval "$(fnva env --shell bash)"';
139
48
  case 'fish':
140
- return getFishFunction();
49
+ return 'fnva env --shell fish | source';
141
50
  default:
142
- return '';
51
+ return null;
143
52
  }
144
53
  }
145
54
 
146
- function isFunctionInstalled(configPath) {
55
+ function isInstalled(configPath) {
147
56
  if (!fs.existsSync(configPath)) {
148
57
  return false;
149
58
  }
150
59
  const content = fs.readFileSync(configPath, 'utf8');
151
- return content.includes('fnva auto integration (added by npm install)');
60
+ return content.includes('fnva env --shell');
152
61
  }
153
62
 
154
63
  function installShellIntegration() {
@@ -161,24 +70,30 @@ function installShellIntegration() {
161
70
  return false;
162
71
  }
163
72
 
164
- if (isFunctionInstalled(configPath)) {
73
+ if (isInstalled(configPath)) {
165
74
  console.log(`fnva shell integration already present: ${configPath}`);
166
75
  return true;
167
76
  }
168
77
 
78
+ const line = getIntegrationLine(shell);
79
+ if (!line) {
80
+ console.log(`Unsupported shell: ${shell}`);
81
+ return false;
82
+ }
83
+
169
84
  try {
170
85
  const dir = path.dirname(configPath);
171
86
  if (!fs.existsSync(dir)) {
172
87
  fs.mkdirSync(dir, { recursive: true });
173
88
  }
174
89
 
175
- const functionCode = getShellFunction(shell);
90
+ const marker = `\n# fnva shell integration\n${line}\n`;
176
91
 
177
92
  if (fs.existsSync(configPath)) {
178
93
  const content = fs.readFileSync(configPath, 'utf8');
179
- fs.writeFileSync(configPath, content + '\n' + functionCode);
94
+ fs.writeFileSync(configPath, content + marker);
180
95
  } else {
181
- fs.writeFileSync(configPath, functionCode);
96
+ fs.writeFileSync(configPath, marker);
182
97
  }
183
98
 
184
99
  console.log(`fnva shell integration installed at: ${configPath}`);
@@ -238,7 +153,7 @@ if (require.main === module) {
238
153
  module.exports = {
239
154
  detectShell,
240
155
  getShellConfigPath,
241
- getShellFunction,
242
- isFunctionInstalled,
156
+ getIntegrationLine,
157
+ isInstalled,
243
158
  installShellIntegration,
244
159
  };
@@ -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