fnva 0.0.9 → 0.0.11
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 +11 -0
- package/bin/fnva.js +218 -71
- package/package.json +1 -1
- package/platforms/fnva +0 -0
- package/platforms/fnva.exe +0 -0
package/README.md
CHANGED
|
@@ -28,6 +28,17 @@ yarn global add fnva
|
|
|
28
28
|
|
|
29
29
|
# 使用 pnpm
|
|
30
30
|
pnpm add -g fnva
|
|
31
|
+
|
|
32
|
+
function fnva {
|
|
33
|
+
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") {
|
|
34
|
+
$tempFile = "$env:TEMP\fnva_script_$(Get-Random).ps1"
|
|
35
|
+
& node bin\fnva.js $args | Out-File -FilePath $tempFile -Encoding UTF8
|
|
36
|
+
& $tempFile
|
|
37
|
+
Remove-Item $tempFile -ErrorAction SilentlyContinue
|
|
38
|
+
} else {
|
|
39
|
+
& node bin\fnva.js $args
|
|
40
|
+
}
|
|
41
|
+
}
|
|
31
42
|
```
|
|
32
43
|
|
|
33
44
|
### 方式二:从 Releases 下载二进制文件
|
package/bin/fnva.js
CHANGED
|
@@ -84,6 +84,62 @@ function hasDirectExecuteFlag(args) {
|
|
|
84
84
|
return args.includes('--exec') || args.includes('-e');
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function getShellArg(args) {
|
|
88
|
+
const idx = args.indexOf('--shell');
|
|
89
|
+
if (idx !== -1 && idx + 1 < args.length) {
|
|
90
|
+
return args[idx + 1];
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function detectShell() {
|
|
96
|
+
if (process.platform === 'win32') {
|
|
97
|
+
return 'powershell';
|
|
98
|
+
}
|
|
99
|
+
return 'bash';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hasSessionFlag(args) {
|
|
103
|
+
return args.includes('--session');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function hasApplyFlag(args) {
|
|
107
|
+
return args.includes('--apply');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function hasAutoExecuteFlag(args) {
|
|
111
|
+
return args.includes('--auto');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function removeAutoFlag(args) {
|
|
115
|
+
const index = args.indexOf('--auto');
|
|
116
|
+
if (index > -1) {
|
|
117
|
+
return args.slice(0, index).concat(args.slice(index + 1));
|
|
118
|
+
}
|
|
119
|
+
return args;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function createTempScriptFile(script, envType, envName) {
|
|
123
|
+
try {
|
|
124
|
+
const os = require('os');
|
|
125
|
+
const fs = require('fs');
|
|
126
|
+
const path = require('path');
|
|
127
|
+
|
|
128
|
+
const tempDir = os.tmpdir();
|
|
129
|
+
const scriptFile = path.join(tempDir, `fnva_${envType}_${envName}_${Date.now()}.ps1`);
|
|
130
|
+
|
|
131
|
+
fs.writeFileSync(scriptFile, script, 'utf8');
|
|
132
|
+
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('💡 环境已切换到当前进程。要在新的 PowerShell 窗口中使用此环境,运行:');
|
|
135
|
+
console.log(` ${scriptFile}`);
|
|
136
|
+
console.log(' 或者: fnva', envType, 'use', envName, '--auto');
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.warn('⚠️ 无法创建临时脚本文件:', error.message);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
87
143
|
function parseEnvironmentScript(scriptContent) {
|
|
88
144
|
if (!scriptContent || scriptContent.trim() === '') {
|
|
89
145
|
return {};
|
|
@@ -101,10 +157,25 @@ function parseEnvironmentScript(scriptContent) {
|
|
|
101
157
|
const trimmedLine = line.trim();
|
|
102
158
|
|
|
103
159
|
// 解析 PowerShell 环境变量设置
|
|
104
|
-
if (trimmedLine.
|
|
105
|
-
|
|
160
|
+
if (trimmedLine.includes('$env:')) {
|
|
161
|
+
// 匹配 $env:VARNAME = "value" 格式
|
|
162
|
+
let match = trimmedLine.match(/\$env:(\w+)\s*=\s*"([^"]*)"/);
|
|
106
163
|
if (match) {
|
|
107
164
|
envVars[match[1]] = match[2];
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 匹配 $env:VARNAME = 'value' 格式
|
|
169
|
+
match = trimmedLine.match(/\$env:(\w+)\s*=\s*'([^']*)'/);
|
|
170
|
+
if (match) {
|
|
171
|
+
envVars[match[1]] = match[2];
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 匹配 $env:VARNAME = value 格式(不带引号)
|
|
176
|
+
match = trimmedLine.match(/\$env:(\w+)\s*=\s*([^;]+)/);
|
|
177
|
+
if (match) {
|
|
178
|
+
envVars[match[1]] = match[2].trim().replace(/['"]/g, '');
|
|
108
179
|
}
|
|
109
180
|
}
|
|
110
181
|
|
|
@@ -115,12 +186,6 @@ function parseEnvironmentScript(scriptContent) {
|
|
|
115
186
|
envVars[match[1]] = match[2];
|
|
116
187
|
}
|
|
117
188
|
}
|
|
118
|
-
|
|
119
|
-
// 解析不带引号的环境变量设置
|
|
120
|
-
const unquotedMatch = trimmedLine.match(/\$env:(\w+)\s*=\s*([^;]+)/);
|
|
121
|
-
if (unquotedMatch) {
|
|
122
|
-
envVars[unquotedMatch[1]] = unquotedMatch[2].trim();
|
|
123
|
-
}
|
|
124
189
|
}
|
|
125
190
|
|
|
126
191
|
return envVars;
|
|
@@ -148,6 +213,65 @@ function displaySuccessMessage(envType, envName, envVars) {
|
|
|
148
213
|
}
|
|
149
214
|
}
|
|
150
215
|
|
|
216
|
+
function generateSimpleScript(envVars, envType, envName) {
|
|
217
|
+
const lines = [];
|
|
218
|
+
|
|
219
|
+
if (process.platform === 'win32') {
|
|
220
|
+
// Windows PowerShell
|
|
221
|
+
lines.push(`Write-Host "Switched to ${envType} environment: ${envName}" -ForegroundColor Green`);
|
|
222
|
+
|
|
223
|
+
if (envVars.JAVA_HOME) {
|
|
224
|
+
lines.push(`$env:JAVA_HOME = "${envVars.JAVA_HOME}"`);
|
|
225
|
+
// 对于 PATH,我们需要智能处理:移除旧的 Java 路径,添加新的
|
|
226
|
+
lines.push(`# Remove existing Java paths from PATH`);
|
|
227
|
+
lines.push(`$pathParts = $env:PATH -split ';'`);
|
|
228
|
+
lines.push(`$cleanPath = @()`);
|
|
229
|
+
lines.push(`foreach ($part in $pathParts) {`);
|
|
230
|
+
lines.push(` if ($part -notmatch 'java' -and $part -notmatch 'jdk') {`);
|
|
231
|
+
lines.push(` $cleanPath += $part`);
|
|
232
|
+
lines.push(` }`);
|
|
233
|
+
lines.push(`}`);
|
|
234
|
+
lines.push(`$env:PATH = "${envVars.JAVA_HOME}\\bin;" + ($cleanPath -join ';')`);
|
|
235
|
+
lines.push(`Write-Host "JAVA_HOME: $env:JAVA_HOME" -ForegroundColor Yellow`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (envVars.ANTHROPIC_AUTH_TOKEN) {
|
|
239
|
+
lines.push(`$env:ANTHROPIC_AUTH_TOKEN = "${envVars.ANTHROPIC_AUTH_TOKEN}"`);
|
|
240
|
+
lines.push(`Write-Host "ANTHROPIC_AUTH_TOKEN: [已设置]" -ForegroundColor Yellow`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (envVars.OPENAI_API_KEY) {
|
|
244
|
+
lines.push(`$env:OPENAI_API_KEY = "${envVars.OPENAI_API_KEY}"`);
|
|
245
|
+
lines.push(`Write-Host "OPENAI_API_KEY: [已设置]" -ForegroundColor Yellow`);
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// Unix-like systems
|
|
249
|
+
lines.push(`echo "Switched to ${envType} environment: ${envName}"`);
|
|
250
|
+
|
|
251
|
+
if (envVars.JAVA_HOME) {
|
|
252
|
+
lines.push(`export JAVA_HOME="${envVars.JAVA_HOME}"`);
|
|
253
|
+
// 对于 PATH,我们也需要智能处理
|
|
254
|
+
lines.push(`# Remove existing Java paths from PATH`);
|
|
255
|
+
lines.push(`echo $PATH | tr ':' '\\n' | grep -v java | grep -v jdk | tr '\\n' ':' | sed 's/:$//' > /tmp/clean_path`);
|
|
256
|
+
lines.push(`export PATH="${envVars.JAVA_HOME}/bin:$(cat /tmp/clean_path)"`);
|
|
257
|
+
lines.push(`rm -f /tmp/clean_path`);
|
|
258
|
+
lines.push(`echo "JAVA_HOME: $JAVA_HOME"`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (envVars.ANTHROPIC_AUTH_TOKEN) {
|
|
262
|
+
lines.push(`export ANTHROPIC_AUTH_TOKEN="${envVars.ANTHROPIC_AUTH_TOKEN}"`);
|
|
263
|
+
lines.push(`echo "ANTHROPIC_AUTH_TOKEN: [已设置]"`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (envVars.OPENAI_API_KEY) {
|
|
267
|
+
lines.push(`export OPENAI_API_KEY="${envVars.OPENAI_API_KEY}"`);
|
|
268
|
+
lines.push(`echo "OPENAI_API_KEY: [已设置]"`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return lines.join('\n');
|
|
273
|
+
}
|
|
274
|
+
|
|
151
275
|
function run() {
|
|
152
276
|
const binaryPath = buildBinaryPath();
|
|
153
277
|
|
|
@@ -161,14 +285,33 @@ function run() {
|
|
|
161
285
|
process.exit(1);
|
|
162
286
|
}
|
|
163
287
|
|
|
164
|
-
|
|
288
|
+
let args = process.argv.slice(2);
|
|
289
|
+
|
|
290
|
+
// 如果设置了 FNVA_AUTO_EXECUTE,则为环境切换命令启用自动执行
|
|
291
|
+
if (process.env.FNVA_AUTO_EXECUTE === '1' && isEnvironmentSwitchCommand(args) && !hasSessionFlag(args)) {
|
|
292
|
+
// 添加 --auto 标志来启用自动执行
|
|
293
|
+
args = args.concat('--auto');
|
|
294
|
+
}
|
|
165
295
|
const isSwitchCommand = isEnvironmentSwitchCommand(args);
|
|
166
296
|
|
|
167
297
|
if (isSwitchCommand) {
|
|
298
|
+
const shellArg = getShellArg(args);
|
|
299
|
+
if (!shellArg || shellArg === 'auto') {
|
|
300
|
+
const detected = detectShell();
|
|
301
|
+
if (shellArg === 'auto') {
|
|
302
|
+
const idx = args.indexOf('--shell');
|
|
303
|
+
if (idx !== -1 && idx + 1 < args.length) {
|
|
304
|
+
args[idx + 1] = detected;
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
args.push('--shell', detected);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
168
311
|
const { spawnSync } = require('child_process');
|
|
169
312
|
const result = spawnSync(binaryPath, args, {
|
|
170
313
|
encoding: 'utf8',
|
|
171
|
-
shell:
|
|
314
|
+
shell: false
|
|
172
315
|
});
|
|
173
316
|
|
|
174
317
|
if (result.error) {
|
|
@@ -188,70 +331,74 @@ function run() {
|
|
|
188
331
|
const envType = args[0];
|
|
189
332
|
const envName = args[2];
|
|
190
333
|
|
|
191
|
-
//
|
|
334
|
+
// Windows:默认不启动新的会话;可通过 --session 开启旧行为
|
|
192
335
|
if (process.platform === 'win32') {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
'
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
336
|
+
if (hasSessionFlag(args)) {
|
|
337
|
+
console.log(`✅ Switched to ${envType} environment: ${envName}`);
|
|
338
|
+
console.log(`🚀 Starting new PowerShell session with ${envName} environment...`);
|
|
339
|
+
console.log(`Type "exit" to return to previous session\n`);
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
const os = require('os');
|
|
343
|
+
const fs = require('fs');
|
|
344
|
+
const tempScript = os.tmpdir() + '\\fnva_env_' + Date.now() + '.ps1';
|
|
345
|
+
const fullScript = script + '\n';
|
|
346
|
+
fs.writeFileSync(tempScript, fullScript, 'utf8');
|
|
347
|
+
const { spawn } = require('child_process');
|
|
348
|
+
const ps = spawn('powershell', ['-NoExit', '-ExecutionPolicy', 'Bypass', '-File', tempScript], {
|
|
349
|
+
stdio: 'inherit',
|
|
350
|
+
shell: false
|
|
351
|
+
});
|
|
352
|
+
ps.on('exit', () => {
|
|
353
|
+
try { fs.unlinkSync(tempScript); } catch (_) {}
|
|
354
|
+
console.log('👋 Returned to original session');
|
|
355
|
+
});
|
|
356
|
+
return;
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error(`Failed to start PowerShell session: ${error.message}`);
|
|
359
|
+
console.log(`📝 Script was: ${script}`);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
// 检查是否使用了 --apply 参数
|
|
363
|
+
if (hasApplyFlag(args)) {
|
|
364
|
+
// 直接应用环境变量到当前进程
|
|
365
|
+
const envVars = parseEnvironmentScript(script);
|
|
366
|
+
applyEnvironmentVariables(envVars);
|
|
367
|
+
displaySuccessMessage(envType, envName, envVars);
|
|
368
|
+
} else {
|
|
369
|
+
// 在 Windows 中,智能处理环境设置
|
|
370
|
+
const envVars = parseEnvironmentScript(script);
|
|
371
|
+
const simpleScript = generateSimpleScript(envVars, envType, envName);
|
|
372
|
+
|
|
373
|
+
// 尝试自动执行(如果可能)
|
|
374
|
+
if (process.env.FNVA_AUTO_EXECUTE === '1') {
|
|
375
|
+
const os = require('os');
|
|
376
|
+
const fs = require('fs');
|
|
377
|
+
const path = require('path');
|
|
378
|
+
const { spawn } = require('child_process');
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
const tempFile = path.join(os.tmpdir(), `fnva_auto_${Date.now()}.ps1`);
|
|
382
|
+
fs.writeFileSync(tempFile, simpleScript, 'utf8');
|
|
383
|
+
|
|
384
|
+
// 使用 PowerShell 执行脚本
|
|
385
|
+
spawn('powershell', ['-ExecutionPolicy', 'Bypass', '-File', tempFile], {
|
|
386
|
+
stdio: 'inherit',
|
|
387
|
+
shell: false
|
|
388
|
+
}).on('exit', () => {
|
|
389
|
+
try { fs.unlinkSync(tempFile); } catch (_) {}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
console.log('✅ 环境已自动切换');
|
|
393
|
+
return;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.warn('⚠️ 自动执行失败,回退到脚本输出');
|
|
396
|
+
}
|
|
245
397
|
}
|
|
246
|
-
console.log('👋 Returned to original session');
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// 保持当前进程运行直到子进程结束
|
|
250
|
-
return;
|
|
251
398
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
399
|
+
// 默认输出脚本
|
|
400
|
+
process.stdout.write(simpleScript);
|
|
401
|
+
}
|
|
255
402
|
}
|
|
256
403
|
} else {
|
|
257
404
|
// Unix-like systems: 显示使用说明
|
package/package.json
CHANGED
package/platforms/fnva
CHANGED
|
Binary file
|
package/platforms/fnva.exe
CHANGED
|
Binary file
|