fnva 0.0.26 → 0.0.28
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/package.json
CHANGED
package/platforms/fnva
CHANGED
|
Binary file
|
package/platforms/fnva.exe
CHANGED
|
Binary file
|
|
@@ -2,130 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*/
|
|
10
|
-
function ensureExecutablePermissions() {
|
|
11
|
-
try {
|
|
12
|
-
const scriptDir = __dirname;
|
|
13
|
-
const projectRoot = path.resolve(scriptDir, '..');
|
|
14
|
-
const platformsDir = path.join(projectRoot, 'platforms');
|
|
15
|
-
|
|
16
|
-
console.log('✅ Ensuring fnva binary permissions...');
|
|
7
|
+
function log(msg) {
|
|
8
|
+
console.log(msg);
|
|
9
|
+
}
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
function ensureExecutable(filePath, label) {
|
|
12
|
+
try {
|
|
13
|
+
const stats = fs.statSync(filePath);
|
|
14
|
+
const hasExec = (stats.mode & 0o111) !== 0;
|
|
15
|
+
log(`Checking ${label}: ${filePath}`);
|
|
16
|
+
log(` Current permissions: ${(stats.mode & 0o777).toString(8)}`);
|
|
17
|
+
|
|
18
|
+
if (!hasExec) {
|
|
19
|
+
fs.chmodSync(filePath, 0o755);
|
|
20
|
+
const newStats = fs.statSync(filePath);
|
|
21
|
+
log(` Updated permissions: ${(newStats.mode & 0o777).toString(8)}`);
|
|
22
|
+
} else {
|
|
23
|
+
log(' Executable bit already set');
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 确保指定路径的二进制文件具有可执行权限,并做一次简单的运行测试
|
|
34
|
-
*/
|
|
35
|
-
function ensureExecutable(binaryPath, label) {
|
|
36
|
-
try {
|
|
37
|
-
const stats = fs.statSync(binaryPath);
|
|
38
|
-
const hasExecPermission = (stats.mode & 0o111) !== 0;
|
|
39
|
-
|
|
40
|
-
console.log(`📍 Checking binary (${label}): ${binaryPath}`);
|
|
41
|
-
console.log(` Current permissions: ${(stats.mode & 0o777).toString(8)}`);
|
|
42
|
-
|
|
43
|
-
if (!hasExecPermission) {
|
|
44
|
-
console.log('🔧 Setting executable permissions...');
|
|
45
|
-
fs.chmodSync(binaryPath, 0o755); // rwxr-xr-x
|
|
46
|
-
|
|
47
|
-
const newStats = fs.statSync(binaryPath);
|
|
48
|
-
const newHasExecPermission = (newStats.mode & 0o111) !== 0;
|
|
49
|
-
|
|
50
|
-
if (newHasExecPermission) {
|
|
51
|
-
console.log(`✅ Successfully set executable permissions (${label})`);
|
|
52
|
-
} else {
|
|
53
|
-
console.log(`❌ Failed to set executable permissions (${label})`);
|
|
54
|
-
console.log(` New permissions: ${(newStats.mode & 0o777).toString(8)}`);
|
|
55
|
-
console.log(` Manual fix may be required: chmod +x "${binaryPath}"`);
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
console.log(`✅ fnva binary already has executable permissions (${label})`);
|
|
59
|
-
}
|
|
26
|
+
const res = spawnSync(filePath, ['--version'], { encoding: 'utf8', timeout: 3000, stdio: 'pipe' });
|
|
27
|
+
if (res.error && res.error.code === 'EACCES') {
|
|
28
|
+
log('WARNING: still not executable; please chmod +x manually');
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {
|
|
31
|
+
log(`WARNING: could not ensure permissions for ${label}: ${err.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
60
34
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
encoding: 'utf8',
|
|
66
|
-
timeout: 3000,
|
|
67
|
-
stdio: 'pipe',
|
|
68
|
-
});
|
|
35
|
+
function ensureExecutablePermissions() {
|
|
36
|
+
const scriptDir = __dirname;
|
|
37
|
+
const projectRoot = path.resolve(scriptDir, '..');
|
|
38
|
+
const platformsDir = path.join(projectRoot, 'platforms');
|
|
69
39
|
|
|
70
|
-
|
|
71
|
-
console.log('✅ fnva binary is executable and responding');
|
|
72
|
-
} else if (testResult.error && testResult.error.code === 'EACCES') {
|
|
73
|
-
console.log('❌ fnva binary still has permission issues');
|
|
74
|
-
console.log(` Manual fix required: chmod +x "${binaryPath}"`);
|
|
75
|
-
}
|
|
76
|
-
} catch {
|
|
77
|
-
// 测试失败不视为致命错误,可能是二进制本身的问题
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.warn(`⚠️ Could not fix binary permissions (${label}): ${error.message}`);
|
|
81
|
-
console.log(` Manual fix required: chmod +x "${binaryPath}"`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
40
|
+
log('Ensuring fnva binary permissions...');
|
|
84
41
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// 优先处理新的平台子目录结构: platforms/<platform>-<arch>/fnva
|
|
90
|
-
ensureExecutable(archBinaryPath, platformDir);
|
|
91
|
-
} else if (fs.existsSync(flatBinaryPath)) {
|
|
92
|
-
// 兼容旧版本扁平结构: platforms/fnva
|
|
93
|
-
console.log('ℹ️ Platform-specific binary not found, falling back to legacy flat layout');
|
|
94
|
-
ensureExecutable(flatBinaryPath, 'platforms/fnva');
|
|
95
|
-
} else {
|
|
96
|
-
console.log(`❌ Binary not found: ${archBinaryPath}`);
|
|
97
|
-
console.log(` Also checked legacy path: ${flatBinaryPath}`);
|
|
98
|
-
console.log(' This might indicate an incomplete installation');
|
|
99
|
-
}
|
|
42
|
+
if (!fs.existsSync(platformsDir)) {
|
|
43
|
+
log('Info: no platforms directory found; skipping (dev install)');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
100
46
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
47
|
+
const platform = process.platform;
|
|
48
|
+
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
49
|
+
const platformDir = `${platform}-${arch}`;
|
|
50
|
+
const binaryName = platform === 'win32' ? 'fnva.exe' : 'fnva';
|
|
51
|
+
const archBinaryPath = path.join(platformsDir, platformDir, binaryName);
|
|
52
|
+
const flatBinaryPath = path.join(platformsDir, binaryName);
|
|
106
53
|
|
|
107
|
-
|
|
108
|
-
|
|
54
|
+
if (platform === 'win32') {
|
|
55
|
+
log('Info: Windows detected; chmod not required; skipping permission changes');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
109
58
|
|
|
110
|
-
|
|
111
|
-
|
|
59
|
+
if (fs.existsSync(archBinaryPath)) {
|
|
60
|
+
ensureExecutable(archBinaryPath, platformDir);
|
|
61
|
+
} else if (fs.existsSync(flatBinaryPath)) {
|
|
62
|
+
log('Info: falling back to legacy platforms/fnva layout');
|
|
63
|
+
ensureExecutable(flatBinaryPath, 'platforms/fnva');
|
|
64
|
+
} else {
|
|
65
|
+
log(`Warning: binary not found: ${archBinaryPath}`);
|
|
66
|
+
log(` Also checked: ${flatBinaryPath}`);
|
|
67
|
+
}
|
|
112
68
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
} catch {
|
|
121
|
-
console.log('ℹ️ Could not verify global installation');
|
|
69
|
+
if (process.env.npm_config_global === 'true') {
|
|
70
|
+
try {
|
|
71
|
+
const which = spawnSync('which', ['fnva'], { encoding: 'utf8' });
|
|
72
|
+
const globalPath = which.stdout?.trim();
|
|
73
|
+
if (globalPath && fs.existsSync(globalPath)) {
|
|
74
|
+
ensureExecutable(globalPath, 'global fnva');
|
|
122
75
|
}
|
|
76
|
+
} catch {
|
|
77
|
+
// ignore
|
|
123
78
|
}
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.warn(`⚠️ Permission check failed: ${error.message}`);
|
|
126
79
|
}
|
|
127
80
|
}
|
|
128
81
|
|
|
129
|
-
|
|
130
|
-
ensureExecutablePermissions();
|
|
131
|
-
|
|
82
|
+
ensureExecutablePermissions();
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Shell integration installer for fnva (npm postinstall helper).
|
|
4
|
+
// Generates a lightweight function wrapper per shell that calls the real
|
|
5
|
+
// `fnva` binary and applies environment switch scripts.
|
|
6
|
+
|
|
3
7
|
const fs = require('fs');
|
|
4
8
|
const path = require('path');
|
|
5
9
|
const os = require('os');
|
|
6
|
-
const { spawn } = require('child_process');
|
|
7
10
|
|
|
8
11
|
function detectShell() {
|
|
9
12
|
if (process.platform === 'win32') {
|
|
@@ -15,7 +18,12 @@ function detectShell() {
|
|
|
15
18
|
function getShellConfigPath(shell) {
|
|
16
19
|
switch (shell) {
|
|
17
20
|
case 'powershell':
|
|
18
|
-
return path.join(
|
|
21
|
+
return path.join(
|
|
22
|
+
process.env.USERPROFILE || os.homedir(),
|
|
23
|
+
'Documents',
|
|
24
|
+
'WindowsPowerShell',
|
|
25
|
+
'Microsoft.PowerShell_profile.ps1'
|
|
26
|
+
);
|
|
19
27
|
case 'bash':
|
|
20
28
|
return path.join(os.homedir(), '.bashrc');
|
|
21
29
|
case 'zsh':
|
|
@@ -29,41 +37,28 @@ function getShellConfigPath(shell) {
|
|
|
29
37
|
|
|
30
38
|
function getPowerShellFunction() {
|
|
31
39
|
return `
|
|
32
|
-
# fnva
|
|
40
|
+
# fnva auto integration (added by npm install)
|
|
33
41
|
function fnva {
|
|
34
42
|
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")) {
|
|
35
43
|
$tempFile = Join-Path $env:TEMP ("fnva_script_" + (Get-Random) + ".ps1")
|
|
36
44
|
|
|
37
45
|
$env:FNVAAUTOMODE = "1"
|
|
38
46
|
try {
|
|
39
|
-
# 捕获 fnva 输出并保存到临时文件
|
|
40
47
|
$output = cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" 2>&1
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
if ($output -match '\$env:' -or $output -match 'Write-Host') {
|
|
49
|
+
if ($output -match '\\$env:' -or $output -match 'Write-Host') {
|
|
44
50
|
$output | Out-File -FilePath $tempFile -Encoding UTF8
|
|
45
|
-
try {
|
|
46
|
-
& $tempFile
|
|
47
|
-
} catch {
|
|
48
|
-
Write-Host "执行脚本时出错: $_" -ForegroundColor Red
|
|
49
|
-
}
|
|
51
|
+
try { & $tempFile } catch { Write-Host "Error executing script: $_" -ForegroundColor Red }
|
|
50
52
|
} else {
|
|
51
|
-
# 如果不是脚本内容,直接输出
|
|
52
53
|
$output
|
|
53
54
|
}
|
|
54
55
|
} finally {
|
|
55
56
|
$env:FNVAAUTOMODE = ""
|
|
56
|
-
if (Test-Path $tempFile) {
|
|
57
|
-
Remove-Item $tempFile -ErrorAction SilentlyContinue
|
|
58
|
-
}
|
|
57
|
+
if (Test-Path $tempFile) { Remove-Item $tempFile -ErrorAction SilentlyContinue }
|
|
59
58
|
}
|
|
60
59
|
} else {
|
|
61
60
|
$env:FNVAAUTOMODE = "1"
|
|
62
|
-
try {
|
|
63
|
-
cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args"
|
|
64
|
-
} finally {
|
|
65
|
-
$env:FNVAAUTOMODE = ""
|
|
66
|
-
}
|
|
61
|
+
try { cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" } finally { $env:FNVAAUTOMODE = "" }
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
`;
|
|
@@ -71,17 +66,25 @@ function fnva {
|
|
|
71
66
|
|
|
72
67
|
function getBashFunction() {
|
|
73
68
|
return `
|
|
74
|
-
# fnva
|
|
69
|
+
# fnva auto integration (added by npm install)
|
|
75
70
|
fnva() {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
local __fnva_bin
|
|
72
|
+
__fnva_bin="$(command -v fnva | head -n 1)"
|
|
73
|
+
if [[ -z "$__fnva_bin" ]]; then
|
|
74
|
+
echo "fnva: binary not found in PATH" >&2
|
|
75
|
+
return 127
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ $# -ge 2 && ("$1" == "java" || "$1" == "llm" || "$1" == "cc") && "$2" == "use" ]]; then
|
|
79
|
+
local temp_file
|
|
80
|
+
temp_file="$(mktemp)"
|
|
81
|
+
chmod +x "$temp_file"
|
|
79
82
|
|
|
80
|
-
FNVA_AUTO_MODE=1
|
|
81
|
-
source "
|
|
82
|
-
rm -f "
|
|
83
|
+
FNVA_AUTO_MODE=1 "$__fnva_bin" "$@" > "$temp_file"
|
|
84
|
+
source "$temp_file"
|
|
85
|
+
rm -f "$temp_file"
|
|
83
86
|
else
|
|
84
|
-
FNVA_AUTO_MODE=1
|
|
87
|
+
FNVA_AUTO_MODE=1 "$__fnva_bin" "$@"
|
|
85
88
|
fi
|
|
86
89
|
}
|
|
87
90
|
`;
|
|
@@ -89,16 +92,22 @@ fnva() {
|
|
|
89
92
|
|
|
90
93
|
function getFishFunction() {
|
|
91
94
|
return `
|
|
92
|
-
# fnva
|
|
95
|
+
# fnva auto integration (added by npm install)
|
|
93
96
|
function fnva
|
|
94
|
-
|
|
97
|
+
set __fnva_bin (command -v fnva | head -n 1)
|
|
98
|
+
if test -z "$__fnva_bin"
|
|
99
|
+
echo "fnva: binary not found in PATH" >&2
|
|
100
|
+
return 127
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if test (count $argv) -ge 2; and string match -q -r "^(java|llm|cc)$" $argv[1]; and test $argv[2] = "use"
|
|
95
104
|
set temp_file (mktemp)
|
|
96
|
-
chmod +x
|
|
97
|
-
env FNVA_AUTO_MODE=1
|
|
98
|
-
source
|
|
99
|
-
rm -f
|
|
105
|
+
chmod +x $temp_file
|
|
106
|
+
env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv > $temp_file
|
|
107
|
+
source $temp_file
|
|
108
|
+
rm -f $temp_file
|
|
100
109
|
else
|
|
101
|
-
env FNVA_AUTO_MODE=1
|
|
110
|
+
env FNVA_AUTO_MODE=1 "$__fnva_bin" $argv
|
|
102
111
|
end
|
|
103
112
|
end
|
|
104
113
|
`;
|
|
@@ -109,9 +118,8 @@ function getShellFunction(shell) {
|
|
|
109
118
|
case 'powershell':
|
|
110
119
|
return getPowerShellFunction();
|
|
111
120
|
case 'bash':
|
|
112
|
-
return getBashFunction();
|
|
113
121
|
case 'zsh':
|
|
114
|
-
return getBashFunction();
|
|
122
|
+
return getBashFunction();
|
|
115
123
|
case 'fish':
|
|
116
124
|
return getFishFunction();
|
|
117
125
|
default:
|
|
@@ -119,13 +127,12 @@ function getShellFunction(shell) {
|
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
|
|
122
|
-
function isFunctionInstalled(configPath
|
|
130
|
+
function isFunctionInstalled(configPath) {
|
|
123
131
|
if (!fs.existsSync(configPath)) {
|
|
124
132
|
return false;
|
|
125
133
|
}
|
|
126
|
-
|
|
127
134
|
const content = fs.readFileSync(configPath, 'utf8');
|
|
128
|
-
return content.includes('fnva
|
|
135
|
+
return content.includes('fnva auto integration (added by npm install)');
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
function installShellIntegration() {
|
|
@@ -133,27 +140,24 @@ function installShellIntegration() {
|
|
|
133
140
|
const configPath = getShellConfigPath(shell);
|
|
134
141
|
|
|
135
142
|
if (!configPath) {
|
|
136
|
-
console.log(
|
|
137
|
-
console.log('
|
|
143
|
+
console.log(`⚠️ Unsupported shell: ${shell}`);
|
|
144
|
+
console.log('Please configure fnva manually (see README).');
|
|
138
145
|
return false;
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
if (isFunctionInstalled(configPath
|
|
142
|
-
console.log(`✅ fnva shell
|
|
148
|
+
if (isFunctionInstalled(configPath)) {
|
|
149
|
+
console.log(`✅ fnva shell integration already present: ${configPath}`);
|
|
143
150
|
return true;
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
try {
|
|
147
|
-
// 确保目录存在
|
|
148
154
|
const dir = path.dirname(configPath);
|
|
149
155
|
if (!fs.existsSync(dir)) {
|
|
150
156
|
fs.mkdirSync(dir, { recursive: true });
|
|
151
157
|
}
|
|
152
158
|
|
|
153
|
-
// 获取函数定义
|
|
154
159
|
const functionCode = getShellFunction(shell);
|
|
155
160
|
|
|
156
|
-
// 添加到配置文件
|
|
157
161
|
if (fs.existsSync(configPath)) {
|
|
158
162
|
const content = fs.readFileSync(configPath, 'utf8');
|
|
159
163
|
fs.writeFileSync(configPath, content + '\n' + functionCode);
|
|
@@ -161,9 +165,8 @@ function installShellIntegration() {
|
|
|
161
165
|
fs.writeFileSync(configPath, functionCode);
|
|
162
166
|
}
|
|
163
167
|
|
|
164
|
-
console.log(`✅ fnva shell
|
|
165
|
-
console.log('🔄
|
|
166
|
-
|
|
168
|
+
console.log(`✅ fnva shell integration installed at: ${configPath}`);
|
|
169
|
+
console.log('🔄 Reload your shell config:');
|
|
167
170
|
switch (shell) {
|
|
168
171
|
case 'powershell':
|
|
169
172
|
console.log(' . $PROFILE');
|
|
@@ -178,68 +181,63 @@ function installShellIntegration() {
|
|
|
178
181
|
console.log(' source ~/.config/fish/config.fish');
|
|
179
182
|
break;
|
|
180
183
|
}
|
|
181
|
-
|
|
182
184
|
return true;
|
|
183
185
|
} catch (error) {
|
|
184
|
-
console.log(`❌
|
|
185
|
-
console.log('
|
|
186
|
+
console.log(`❌ Install failed: ${error.message}`);
|
|
187
|
+
console.log('Please configure fnva manually (see README).');
|
|
186
188
|
return false;
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
191
|
|
|
190
|
-
// 询问用户是否安装
|
|
191
192
|
function promptInstallation() {
|
|
192
193
|
if (process.env.FNVA_SKIP_SHELL_SETUP === '1') {
|
|
193
|
-
console.log('⏭️
|
|
194
|
+
console.log('⏭️ Skipping shell integration (FNVA_SKIP_SHELL_SETUP=1)');
|
|
194
195
|
return;
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
const shell = detectShell();
|
|
198
|
-
console.log(
|
|
199
|
-
console.log('
|
|
199
|
+
console.log(`🔍 Detected shell: ${shell}`);
|
|
200
|
+
console.log('❓ Install fnva shell integration? (y/N)');
|
|
200
201
|
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
const readline = require('readline');
|
|
203
|
+
const rl = readline.createInterface({
|
|
204
|
+
input: process.stdin,
|
|
205
|
+
output: process.stdout,
|
|
206
|
+
});
|
|
203
207
|
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
if (
|
|
208
|
+
rl.question('> ', (answer) => {
|
|
209
|
+
const normalized = answer.trim().toLowerCase();
|
|
210
|
+
if (normalized === 'y' || normalized === 'yes') {
|
|
207
211
|
installShellIntegration();
|
|
208
212
|
} else {
|
|
209
|
-
console.log('
|
|
210
|
-
console.log('📖 手动配置指南: https://github.com/your-repo/fnva');
|
|
213
|
+
console.log('⏩ Skipped shell integration.');
|
|
211
214
|
}
|
|
212
|
-
|
|
215
|
+
rl.close();
|
|
213
216
|
});
|
|
214
|
-
|
|
215
|
-
// 10秒后自动跳过
|
|
216
|
-
setTimeout(() => {
|
|
217
|
-
console.log('⏭️ 超时,跳过 shell 集成安装');
|
|
218
|
-
console.log('📖 手动配置指南: https://github.com/your-repo/fnva');
|
|
219
|
-
process.exit(0);
|
|
220
|
-
}, 10000);
|
|
221
217
|
}
|
|
222
218
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
console.log(
|
|
226
|
-
console.log(`📍
|
|
227
|
-
console.log(`📂 工作目录: ${process.cwd()}`);
|
|
228
|
-
console.log(`🎯 参数: ${process.argv.join(' ')}`);
|
|
219
|
+
function main() {
|
|
220
|
+
console.log('🛠️ fnva shell integration installer');
|
|
221
|
+
console.log(`📦 Node.js version: ${process.version}`);
|
|
222
|
+
console.log(`📍 CWD: ${process.cwd()}`);
|
|
229
223
|
|
|
230
224
|
if (process.argv.includes('--auto') || process.argv.includes('--yes')) {
|
|
231
|
-
console.log('
|
|
225
|
+
console.log('🤖 Auto mode: installing...');
|
|
232
226
|
const result = installShellIntegration();
|
|
233
|
-
console.log(
|
|
227
|
+
console.log(`📄 Install result: ${result ? 'success' : 'failed'}`);
|
|
234
228
|
} else {
|
|
235
229
|
promptInstallation();
|
|
236
230
|
}
|
|
237
231
|
}
|
|
238
232
|
|
|
233
|
+
if (require.main === module) {
|
|
234
|
+
main();
|
|
235
|
+
}
|
|
236
|
+
|
|
239
237
|
module.exports = {
|
|
240
238
|
detectShell,
|
|
241
239
|
getShellConfigPath,
|
|
242
240
|
getShellFunction,
|
|
243
241
|
isFunctionInstalled,
|
|
244
|
-
installShellIntegration
|
|
242
|
+
installShellIntegration,
|
|
245
243
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Shell integration uninstaller for fnva (npm postuninstall helper).
|
|
4
|
+
// Cleans fnva wrapper functions from common shell config files.
|
|
5
|
+
|
|
3
6
|
const fs = require('fs');
|
|
4
7
|
const path = require('path');
|
|
5
8
|
const os = require('os');
|
|
6
9
|
|
|
7
10
|
function detectShell() {
|
|
8
|
-
if (process.platform === 'win32')
|
|
9
|
-
return 'powershell';
|
|
10
|
-
}
|
|
11
|
+
if (process.platform === 'win32') return 'powershell';
|
|
11
12
|
return process.env.SHELL?.split('/').pop() || 'bash';
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -33,7 +34,7 @@ function cleanConfigFile(cfgPath) {
|
|
|
33
34
|
let content = fs.readFileSync(cfgPath, 'utf8');
|
|
34
35
|
const originalContent = content;
|
|
35
36
|
|
|
36
|
-
const marker = '# fnva
|
|
37
|
+
const marker = '# fnva auto integration (added by npm install)';
|
|
37
38
|
const startIndex = content.indexOf(marker);
|
|
38
39
|
|
|
39
40
|
if (startIndex !== -1) {
|
|
@@ -69,10 +70,9 @@ function cleanConfigFile(cfgPath) {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
// 正则兜底:移除残留 fnva 片段
|
|
73
73
|
if (content === originalContent) {
|
|
74
74
|
content = content
|
|
75
|
-
.replace(/# fnva
|
|
75
|
+
.replace(/# fnva auto integration \(added by npm install\)[\s\S]*?(?=\n\S|\n$)/g, '')
|
|
76
76
|
.replace(/.*fnva.*\n?/g, '')
|
|
77
77
|
.replace(/.*FNVAAUTOMODE.*\n?/g, '')
|
|
78
78
|
.replace(/.*cmd\.exe.*fnva.*\n?/g, '')
|
|
@@ -82,50 +82,50 @@ function cleanConfigFile(cfgPath) {
|
|
|
82
82
|
|
|
83
83
|
if (content !== originalContent) {
|
|
84
84
|
fs.writeFileSync(cfgPath, content);
|
|
85
|
-
console.log(`✅ fnva shell
|
|
85
|
+
console.log(`✅ fnva shell integration removed from ${cfgPath}`);
|
|
86
86
|
return true;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
console.log(`⚠️
|
|
89
|
+
console.log(`⚠️ No fnva block found in ${cfgPath}`);
|
|
90
90
|
return false;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function removeShellIntegration(configPath, shell) {
|
|
94
|
-
const paths = getShellConfigPaths(shell);
|
|
95
|
-
if (configPath) paths.unshift(configPath); // 兼容传入单一路径
|
|
96
|
-
|
|
94
|
+
const paths = configPath ? [configPath] : getShellConfigPaths(shell);
|
|
97
95
|
let removedAny = false;
|
|
96
|
+
|
|
98
97
|
for (const cfgPath of paths) {
|
|
99
98
|
if (!cfgPath || !fs.existsSync(cfgPath)) continue;
|
|
100
99
|
try {
|
|
101
100
|
const removed = cleanConfigFile(cfgPath);
|
|
102
101
|
removedAny = removedAny || removed;
|
|
103
102
|
} catch (error) {
|
|
104
|
-
console.log(`❌
|
|
103
|
+
console.log(`❌ Remove failed (${cfgPath}): ${error.message}`);
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
if (!removedAny) {
|
|
109
|
-
console.log('⚠️
|
|
108
|
+
console.log('⚠️ No shell config cleaned (file missing or no fnva block)');
|
|
110
109
|
}
|
|
110
|
+
|
|
111
111
|
return removedAny;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
function main() {
|
|
115
|
-
console.log('🧹 fnva shell
|
|
115
|
+
console.log('🧹 fnva shell integration uninstaller');
|
|
116
116
|
|
|
117
117
|
const shell = detectShell();
|
|
118
118
|
const paths = getShellConfigPaths(shell);
|
|
119
119
|
|
|
120
|
-
if (paths.length
|
|
121
|
-
console.log(`⚠️
|
|
120
|
+
if (!paths.length) {
|
|
121
|
+
console.log(`⚠️ Unsupported shell: ${shell}`);
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
const success = removeShellIntegration(null, shell);
|
|
126
126
|
|
|
127
127
|
if (success) {
|
|
128
|
-
console.log('🔄
|
|
128
|
+
console.log('🔄 Reload your shell config:');
|
|
129
129
|
switch (shell) {
|
|
130
130
|
case 'powershell':
|
|
131
131
|
console.log(' . $PROFILE');
|
|
@@ -151,4 +151,4 @@ module.exports = {
|
|
|
151
151
|
detectShell,
|
|
152
152
|
getShellConfigPaths,
|
|
153
153
|
removeShellIntegration,
|
|
154
|
-
};
|
|
154
|
+
};
|