fnva 0.0.46 → 0.0.48

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,9 +14,9 @@ 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 --shell bash)"`
18
- PowerShell: `fnva env --shell powershell | Out-String | Invoke-Expression`
19
- Fish: `fnva env --shell fish | source`
17
+ - Init shell (Bash/Zsh): `eval "$(fnva env env --shell bash)"`
18
+ PowerShell: `fnva env env --shell powershell | Out-String | Invoke-Expression`
19
+ Fish: `fnva env env --shell fish | source`
20
20
  - Scan Java: `fnva java scan`
21
21
  - Switch Java for current session: `fnva java use jdk-17` (with shell integration)
22
22
  - Switch CC profile: `fnva cc use glmcc` (with shell integration)
package/bin/fnva.js CHANGED
@@ -501,6 +501,27 @@ function run() {
501
501
  }
502
502
  const isSwitchCommand = isEnvironmentSwitchCommand(args);
503
503
 
504
+ // Unix: 直接透传给 Rust 二进制,不拦截参数
505
+ // shell wrapper 函数负责捕获输出并 source
506
+ if (process.platform !== 'win32') {
507
+ const result = spawnSync(binaryPath, args, {
508
+ stdio: 'inherit',
509
+ });
510
+
511
+ if (result.error) {
512
+ if (result.error.code === 'EACCES') {
513
+ console.error(`[ERROR] Permission denied. The fnva binary is not executable.`);
514
+ console.error(`[INFO] To fix this, run: sudo chmod +x "${binaryPath}"`);
515
+ console.error(`[INFO] Or reinstall: npm install -g fnva --force`);
516
+ } else {
517
+ console.error(`[ERROR] Failed to execute fnva: ${result.error.message}`);
518
+ }
519
+ process.exit(result.status ?? 1);
520
+ }
521
+
522
+ process.exit(result.status ?? 0);
523
+ }
524
+
504
525
  if (isSwitchCommand) {
505
526
  const shellArg = getShellArg(args);
506
527
  if (!shellArg || shellArg === 'auto') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fnva",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "description": "跨平台环境切换工具,支持 Java 和 LLM 环境配置",
5
5
  "author": "protagonistss",
6
6
  "license": "MIT",
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -41,12 +41,12 @@ function getShellConfigPath(shell) {
41
41
  function getIntegrationLine(shell) {
42
42
  switch (shell) {
43
43
  case 'powershell':
44
- return 'fnva env --shell powershell | Out-String | Invoke-Expression';
44
+ return 'fnva env env --shell powershell | Out-String | Invoke-Expression';
45
45
  case 'bash':
46
46
  case 'zsh':
47
- return 'eval "$(fnva env --shell bash)"';
47
+ return 'eval "$(fnva env env --shell bash)"';
48
48
  case 'fish':
49
- return 'fnva env --shell fish | source';
49
+ return 'fnva env env --shell fish | source';
50
50
  default:
51
51
  return null;
52
52
  }
@@ -57,7 +57,7 @@ function isInstalled(configPath) {
57
57
  return false;
58
58
  }
59
59
  const content = fs.readFileSync(configPath, 'utf8');
60
- return content.includes('fnva env --shell');
60
+ return content.includes('fnva env env --shell');
61
61
  }
62
62
 
63
63
  function installShellIntegration() {
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 从 Adoptium GitHub Releases 查询最新 Java 版本,
5
+ * 生成简化的 config/java_versions.toml(LTS + 最新非 LTS)
6
+ */
7
+
8
+ const https = require('https');
9
+
10
+ const REPOS = [
11
+ { repo: 'temurin8-binaries', major: 8, lts: true },
12
+ { repo: 'temurin11-binaries', major: 11, lts: true },
13
+ { repo: 'temurin17-binaries', major: 17, lts: true },
14
+ { repo: 'temurin21-binaries', major: 21, lts: true },
15
+ { repo: 'temurin25-binaries', major: 25, lts: false },
16
+ ];
17
+
18
+ const PLATFORMS = [
19
+ { key: 'windows-x64', pattern: /jdk_x64_windows_hotspot/ },
20
+ { key: 'linux-x64', pattern: /jdk_x64_linux_hotspot/ },
21
+ { key: 'linux-aarch64', pattern: /jdk_aarch64_linux_hotspot/ },
22
+ { key: 'macos-x64', pattern: /jdk_x64_mac_hotspot/ },
23
+ { key: 'macos-aarch64', pattern: /jdk_aarch64_mac_hotspot/ },
24
+ ];
25
+
26
+ function fetchJSON(url) {
27
+ return new Promise((resolve, reject) => {
28
+ https.get(url, { headers: { 'User-Agent': 'fnva-ci' } }, (res) => {
29
+ let data = '';
30
+ res.on('data', (chunk) => data += chunk);
31
+ res.on('end', () => {
32
+ try { resolve(JSON.parse(data)); }
33
+ catch (e) { reject(new Error(`Parse error: ${e.message}`)); }
34
+ });
35
+ }).on('error', reject);
36
+ });
37
+ }
38
+
39
+ function parseVersion(tagName) {
40
+ // jdk-21.0.10+7 -> { version: "21.0.10", tag: "jdk-21.0.10+7" }
41
+ // jdk8u482-b08 -> { version: "8u482b08", tag: "jdk8u482-b08" }
42
+ const match = tagName.match(/^jdk-?([\d.]+u?\d*)(?:\+|b|-b?)(\d+)$/i);
43
+ if (!match) return null;
44
+ const base = match[1].replace(/\.0(?=\d)/g, '.');
45
+ const build = match[2];
46
+ if (tagName.includes('jdk8u')) {
47
+ return { version: `8u${base.replace(/^8u?/, '')}b${build.padStart(2, '0')}`, tag: tagName };
48
+ }
49
+ return { version: `${base}+${build}`, tag: tagName };
50
+ }
51
+
52
+ async function getLatestVersion(repo, major) {
53
+ const url = `https://api.github.com/repos/adoptium/${repo}/releases?per_page=5`;
54
+ const releases = await fetchJSON(url);
55
+
56
+ for (const release of releases) {
57
+ if (release.prerelease) continue;
58
+ const parsed = parseVersion(release.tag_name);
59
+ if (!parsed) continue;
60
+
61
+ const assets = {};
62
+ for (const asset of release.assets) {
63
+ for (const p of PLATFORMS) {
64
+ if (p.pattern.test(asset.name) && (asset.name.endsWith('.zip') || asset.name.endsWith('.tar.gz'))) {
65
+ assets[p.key] = asset.name;
66
+ }
67
+ }
68
+ }
69
+
70
+ if (Object.keys(assets).length >= 3) {
71
+ return { ...parsed, major, assets };
72
+ }
73
+ }
74
+ return null;
75
+ }
76
+
77
+ function toToml(entry) {
78
+ let lines = [
79
+ `[[versions]]`,
80
+ `version = "${entry.version}"`,
81
+ `major = ${entry.major}`,
82
+ `lts = ${entry.lts}`,
83
+ `tag_name = "${entry.tag}"`,
84
+ `[versions.assets]`,
85
+ ];
86
+ for (const [key, name] of Object.entries(entry.assets)) {
87
+ lines.push(`${key} = "${name}"`);
88
+ }
89
+ return lines.join('\n');
90
+ }
91
+
92
+ async function main() {
93
+ console.log('🔍 查询 Adoptium 最新版本...');
94
+
95
+ const entries = [];
96
+ for (const { repo, major, lts } of REPOS) {
97
+ console.log(` 检查 ${repo}...`);
98
+ try {
99
+ const entry = await getLatestVersion(repo, major);
100
+ if (entry) {
101
+ entries.push({ ...entry, lts });
102
+ console.log(` ✅ Java ${major}: ${entry.version}`);
103
+ } else {
104
+ console.log(` ⚠️ Java ${major}: 未找到可用版本`);
105
+ }
106
+ } catch (e) {
107
+ console.log(` ❌ Java ${major}: ${e.message}`);
108
+ }
109
+ }
110
+
111
+ entries.sort((a, b) => b.major - a.major);
112
+
113
+ const toml = entries.map(toToml).join('\n\n') + '\n';
114
+
115
+ const fs = require('fs');
116
+ const path = require('path');
117
+ const targetPath = path.join(__dirname, '..', 'config', 'java_versions.toml');
118
+
119
+ const existing = fs.readFileSync(targetPath, 'utf-8');
120
+ if (existing.trim() === toml.trim()) {
121
+ console.log('\n📋 版本注册表无变化');
122
+ process.exit(0);
123
+ }
124
+
125
+ fs.writeFileSync(targetPath, toml, 'utf-8');
126
+ console.log(`\n✅ 已更新 ${targetPath}`);
127
+ }
128
+
129
+ main().catch((e) => {
130
+ console.error(e);
131
+ process.exit(1);
132
+ });