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 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.startsWith('$env:')) {
105
- const match = trimmedLine.match(/\$env:(\w+)\s*=\s*"([^"]*)"/);
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
- const args = process.argv.slice(2);
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: true
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
- // Windows 上:启动新的 PowerShell 会话并自动执行环境切换
334
+ // Windows:默认不启动新的会话;可通过 --session 开启旧行为
192
335
  if (process.platform === 'win32') {
193
- console.log(`✅ Switched to ${envType} environment: ${envName}`);
194
- console.log(`🚀 Starting new PowerShell session with ${envName} environment...`);
195
- console.log(`Type "exit" to return to previous session\n`);
196
-
197
- try {
198
- // 创建临时脚本文件
199
- const os = require('os');
200
- const fs = require('fs');
201
- const tempScript = os.tmpdir() + '\\fnva_env_' + Date.now() + '.ps1';
202
-
203
- // 写入环境设置脚本 + 启动交互式会话
204
- const fullScript = script + '\n\n' +
205
- 'Write-Host "✨ Environment ready! You are now in fnva: ' + envName + '" -ForegroundColor Green\n' +
206
- 'Write-Host "Current Java version:" -ForegroundColor Yellow\n' +
207
- 'java --version\n' +
208
- 'Write-Host ""\n' +
209
- 'Write-Host "Type "exit" to return to previous session." -ForegroundColor Cyan\n' +
210
- 'Write-Host ""\n' +
211
- '$Host.UI.RawUI.WindowTitle = "fnva: ' + envName + '"\n' +
212
- '# Start interactive prompt\n' +
213
- 'while ($true) {\n' +
214
- ' try {\n' +
215
- ' $input = Read-Host "PS fnva:' + envName + '"\n' +
216
- ' if ($input -eq "exit") { break }\n' +
217
- ' if ($input.Trim() -ne "") {\n' +
218
- ' try {\n' +
219
- ' Invoke-Expression $input\n' +
220
- ' } catch {\n' +
221
- ' Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red\n' +
222
- ' }\n' +
223
- ' }\n' +
224
- ' } catch {\n' +
225
- ' break\n' +
226
- ' }\n' +
227
- '}\n' +
228
- 'Write-Host "👋 Returning to original session..." -ForegroundColor Cyan\n';
229
-
230
- fs.writeFileSync(tempScript, fullScript, 'utf8');
231
-
232
- // 启动新的交互式 PowerShell 会话
233
- const { spawn } = require('child_process');
234
- const ps = spawn('powershell', ['-NoExit', '-ExecutionPolicy', 'Bypass', '-File', tempScript], {
235
- stdio: 'inherit',
236
- shell: false // 避免 shell 注入问题
237
- });
238
-
239
- ps.on('exit', (code) => {
240
- // 清理临时文件
241
- try {
242
- fs.unlinkSync(tempScript);
243
- } catch (e) {
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
- } catch (error) {
253
- console.error(`Failed to start PowerShell session: ${error.message}`);
254
- console.log(`📝 Script was: ${script}`);
399
+ // 默认输出脚本
400
+ process.stdout.write(simpleScript);
401
+ }
255
402
  }
256
403
  } else {
257
404
  // Unix-like systems: 显示使用说明
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fnva",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "跨平台环境切换工具,支持 Java 和 LLM 环境配置",
5
5
  "author": "protagonistss",
6
6
  "license": "MIT",
package/platforms/fnva CHANGED
Binary file
Binary file