fnva 0.0.53 → 0.0.56
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 +44 -27
- package/bin/fnva.js +0 -34
- package/package.json +1 -1
- package/platforms/darwin-arm64/fnva +0 -0
- package/platforms/darwin-x64/fnva +0 -0
- package/platforms/linux-arm64/fnva +0 -0
- package/platforms/linux-x64/fnva +0 -0
- package/platforms/win32-x64/fnva.exe +0 -0
- package/scripts/update-java-versions.js +71 -0
package/README.md
CHANGED
|
@@ -1,41 +1,58 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# fnva - Fast Environment Version Alter
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
**Fast, cross-platform environment version switcher**
|
|
4
6
|
|
|
5
|
-
[中文文档](README_zh.md) · [
|
|
7
|
+
[中文文档](README_zh.md) · [Architecture](docs/architecture/core-design.md) · [Releases](https://github.com/Protagonistss/fnva/releases)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
[](https://www.npmjs.com/package/fnva)
|
|
10
|
+
[](https://crates.io/crates/fnva)
|
|
11
|
+
[](LICENSE)
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
<!-- TODO: Add demo.gif here -->
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
- Cargo: `cargo install fnva`
|
|
13
|
-
- Binary: download from [Releases](https://github.com/Protagonistss/fnva/releases) and add to `PATH`.
|
|
15
|
+
</div>
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
fnva is a cross-platform environment switcher for Java, Claude Code (CC), and general LLM setups. Written in Rust, it starts instantly, has zero runtime dependencies, and works via shell snippets without background daemons.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
PowerShell: `fnva env env --shell powershell | Out-String | Invoke-Expression`
|
|
19
|
-
Fish: `fnva env env --shell fish | source`
|
|
20
|
-
- Scan Java: `fnva java scan`
|
|
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
|
|
19
|
+
## Core Features
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
- ⚡ **Fast & Zero Dependencies**: Single static binary.
|
|
22
|
+
- 🔄 **Session & Global Switching**: Per-shell activation and global defaults.
|
|
23
|
+
- 🐚 **Broad Shell Support**: PowerShell, Bash, Zsh, Fish, CMD.
|
|
24
|
+
- 🧠 **Auto-restore**: Opening a new terminal automatically restores your last active environment.
|
|
25
|
+
- ☕ **Smart Java Management**: Scan and dedupe local JDKs.
|
|
26
|
+
- 🤖 **Unified LLM Setup**: Configure LLM API keys in one place.
|
|
26
27
|
|
|
27
|
-
|
|
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.
|
|
30
|
-
- Stores config at `~/.fnva/config.toml` (Windows: `%USERPROFILE%\.fnva\config.toml`).
|
|
31
|
-
- Ships as a single binary; no background daemon.
|
|
28
|
+
## Documentation Navigation
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
- [Core Architecture](docs/architecture/core-design.md) (Chinese)
|
|
31
|
+
- [Roadmap](docs/development/roadmap.md) (Chinese)
|
|
32
|
+
- [Contributing](docs/development/contributing.md) (Chinese)
|
|
33
|
+
- [Shell Integration Guide](docs/user-guide/shell-integration.md)
|
|
34
34
|
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# npm (recommended)
|
|
39
|
+
npm install -g fnva
|
|
40
|
+
|
|
41
|
+
# Cargo
|
|
42
|
+
cargo install fnva
|
|
35
43
|
```
|
|
36
|
-
cargo fmt && cargo clippy --all-targets -- -D warnings
|
|
37
|
-
cargo test
|
|
38
|
-
cargo build --release
|
|
39
|
-
```
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
Or download from [Releases](https://github.com/Protagonistss/fnva/releases) and add to `PATH`.
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
- Shell Integration: `eval "$(fnva env env --shell bash)"`
|
|
50
|
+
- Scan Java: `fnva java scan`
|
|
51
|
+
- Switch Java: `fnva java use jdk-17` (with shell integration)
|
|
52
|
+
- Switch CC profile: `fnva cc use glmcc`
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
Config is stored at `~/.fnva/config.toml` (Windows: `%USERPROFILE%\.fnva\config.toml`).
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
MIT License.
|
package/bin/fnva.js
CHANGED
|
@@ -101,10 +101,6 @@ function isEnvironmentSwitchCommand(args) {
|
|
|
101
101
|
args[1] === 'use';
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function hasDirectExecuteFlag(args) {
|
|
105
|
-
return args.includes('--exec') || args.includes('-e');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
104
|
function getShellArg(args) {
|
|
109
105
|
const idx = args.indexOf('--shell');
|
|
110
106
|
if (idx !== -1 && idx + 1 < args.length) {
|
|
@@ -128,36 +124,6 @@ function hasApplyFlag(args) {
|
|
|
128
124
|
return args.includes('--apply');
|
|
129
125
|
}
|
|
130
126
|
|
|
131
|
-
function hasAutoExecuteFlag(args) {
|
|
132
|
-
return args.includes('--auto');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function removeAutoFlag(args) {
|
|
136
|
-
const index = args.indexOf('--auto');
|
|
137
|
-
if (index > -1) {
|
|
138
|
-
return args.slice(0, index).concat(args.slice(index + 1));
|
|
139
|
-
}
|
|
140
|
-
return args;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function createTempScriptFile(script, envType, envName) {
|
|
144
|
-
try {
|
|
145
|
-
const os = require('os');
|
|
146
|
-
const path = require('path');
|
|
147
|
-
|
|
148
|
-
const prefix = `fnva_${envType}_${envName}`;
|
|
149
|
-
const scriptFile = EncodingUtils.createTempPowerShellScript(script, prefix);
|
|
150
|
-
|
|
151
|
-
console.log('');
|
|
152
|
-
console.log('[INFO] 环境已切换到当前进程。要在新的 PowerShell 窗口中使用此环境,运行:');
|
|
153
|
-
console.log(` ${scriptFile}`);
|
|
154
|
-
console.log(' 或者: fnva', envType, 'use', envName, '--auto');
|
|
155
|
-
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.warn('[WARN] 无法创建临时脚本文件:', error.message);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
127
|
function parseEnvironmentScript(scriptContent) {
|
|
162
128
|
if (!scriptContent || scriptContent.trim() === '') {
|
|
163
129
|
return {};
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/platforms/linux-x64/fnva
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const https = require('https');
|
|
9
|
+
const http = require('http');
|
|
9
10
|
|
|
10
11
|
const REPOS = [
|
|
11
12
|
{ repo: 'temurin8-binaries', major: 8, lts: true },
|
|
@@ -89,6 +90,68 @@ function toToml(entry) {
|
|
|
89
90
|
return lines.join('\n');
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// --- Mirror URL verification ---
|
|
94
|
+
|
|
95
|
+
const MIRRORS = [
|
|
96
|
+
{ name: 'tsinghua', template: '{base_url}/{major}/jdk/{arch}/{os}/{filename}',
|
|
97
|
+
base_url: 'https://mirrors.tuna.tsinghua.edu.cn/Adoptium' },
|
|
98
|
+
{ name: 'aliyun', template: '{base_url}/{major}/{tag}/{filename}',
|
|
99
|
+
base_url: 'https://mirrors.aliyun.com/eclipse/temurin-compliance/temurin' },
|
|
100
|
+
{ name: 'github', template: 'https://github.com/adoptium/temurin{major}-binaries/releases/download/{tag}/{filename}',
|
|
101
|
+
base_url: '' },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
function renderMirrorUrl(template, base_url, major, tag, filename, os, arch) {
|
|
105
|
+
return template
|
|
106
|
+
.replace('{base_url}', base_url)
|
|
107
|
+
.replace('{major}', String(major))
|
|
108
|
+
.replace('{tag}', tag)
|
|
109
|
+
.replace('{filename}', filename)
|
|
110
|
+
.replace('{os}', os)
|
|
111
|
+
.replace('{arch}', arch);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function headCheck(url) {
|
|
115
|
+
return new Promise((resolve) => {
|
|
116
|
+
const mod = url.startsWith('https') ? https : http;
|
|
117
|
+
const req = mod.request(url, { method: 'HEAD', timeout: 10000 }, (res) => {
|
|
118
|
+
resolve(res.statusCode >= 200 && res.statusCode < 400);
|
|
119
|
+
});
|
|
120
|
+
req.on('error', () => resolve(false));
|
|
121
|
+
req.on('timeout', () => { req.destroy(); resolve(false); });
|
|
122
|
+
req.end();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function verifyMirrorUrls(entries) {
|
|
127
|
+
console.log('\n--- Mirror URL verification ---');
|
|
128
|
+
let hasFailure = false;
|
|
129
|
+
|
|
130
|
+
for (const entry of entries) {
|
|
131
|
+
const filename = entry.assets['linux-x64'];
|
|
132
|
+
if (!filename) {
|
|
133
|
+
console.log(` [SKIP] Java ${entry.major}: no linux-x64 asset`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let anyOk = false;
|
|
138
|
+
for (const mirror of MIRRORS) {
|
|
139
|
+
const url = renderMirrorUrl(mirror.template, mirror.base_url, entry.major, entry.tag, filename, 'linux', 'x64');
|
|
140
|
+
const ok = await headCheck(url);
|
|
141
|
+
const mark = ok ? 'OK' : 'FAIL';
|
|
142
|
+
console.log(` [${mark}] Java ${entry.major} ${mirror.name}: ${url.substring(0, 80)}...`);
|
|
143
|
+
if (ok) anyOk = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!anyOk) {
|
|
147
|
+
console.log(` [ERROR] Java ${entry.major}: ALL mirrors unreachable!`);
|
|
148
|
+
hasFailure = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return !hasFailure;
|
|
153
|
+
}
|
|
154
|
+
|
|
92
155
|
async function main() {
|
|
93
156
|
console.log('🔍 查询 Adoptium 最新版本...');
|
|
94
157
|
|
|
@@ -124,6 +187,14 @@ async function main() {
|
|
|
124
187
|
|
|
125
188
|
fs.writeFileSync(targetPath, toml, 'utf-8');
|
|
126
189
|
console.log(`\n✅ 已更新 ${targetPath}`);
|
|
190
|
+
|
|
191
|
+
// Verify all mirror URLs are reachable
|
|
192
|
+
const ok = await verifyMirrorUrls(entries);
|
|
193
|
+
if (!ok) {
|
|
194
|
+
console.error('\n❌ Mirror URL verification failed! Check templates in config.rs');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
console.log('\n✅ All mirror URLs verified');
|
|
127
198
|
}
|
|
128
199
|
|
|
129
200
|
main().catch((e) => {
|