fnva 0.0.14 → 0.0.15
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 +16 -5
- package/package.json +5 -1
- package/platforms/fnva +0 -0
- package/platforms/fnva.exe +0 -0
- package/scripts/build-all.sh +94 -0
- package/scripts/build-local.sh +90 -0
- package/scripts/install-shell-integration.js +225 -0
- package/scripts/prepare-publish.sh +50 -0
- package/scripts/uninstall-shell-integration.js +159 -0
package/README.md
CHANGED
|
@@ -30,13 +30,24 @@ yarn global add fnva
|
|
|
30
30
|
pnpm add -g fnva
|
|
31
31
|
|
|
32
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") {
|
|
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
34
|
$tempFile = "$env:TEMP\fnva_script_$(Get-Random).ps1"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
|
|
36
|
+
$env:FNVAAUTOMODE = "1"
|
|
37
|
+
try {
|
|
38
|
+
cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" | Out-File -FilePath $tempFile -Encoding UTF8
|
|
39
|
+
& $tempFile
|
|
40
|
+
} finally {
|
|
41
|
+
$env:FNVAAUTOMODE = ""
|
|
42
|
+
Remove-Item $tempFile -ErrorAction SilentlyContinue
|
|
43
|
+
}
|
|
38
44
|
} else {
|
|
39
|
-
|
|
45
|
+
$env:FNVAAUTOMODE = "1"
|
|
46
|
+
try {
|
|
47
|
+
cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args"
|
|
48
|
+
} finally {
|
|
49
|
+
$env:FNVAAUTOMODE = ""
|
|
50
|
+
}
|
|
40
51
|
}
|
|
41
52
|
}
|
|
42
53
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fnva",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "跨平台环境切换工具,支持 Java 和 LLM 环境配置",
|
|
5
5
|
"author": "protagonistss",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"prepublishOnly": "scripts/prepare-publish.sh",
|
|
12
|
+
"postinstall": "node scripts/install-shell-integration.js --auto",
|
|
13
|
+
"install-shell": "node scripts/install-shell-integration.js",
|
|
14
|
+
"uninstall-shell": "node scripts/uninstall-shell-integration.js",
|
|
12
15
|
"build": "scripts/build-local.sh",
|
|
13
16
|
"build:all": "scripts/build-all.sh"
|
|
14
17
|
},
|
|
15
18
|
"files": [
|
|
16
19
|
"bin/",
|
|
17
20
|
"platforms/",
|
|
21
|
+
"scripts/",
|
|
18
22
|
"README.md",
|
|
19
23
|
"LICENSE"
|
|
20
24
|
],
|
package/platforms/fnva
CHANGED
|
Binary file
|
package/platforms/fnva.exe
CHANGED
|
Binary file
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 构建所有平台的二进制文件
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "开始构建所有平台的二进制文件..."
|
|
8
|
+
|
|
9
|
+
# 获取项目根目录
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
+
PLATFORMS_DIR="$PROJECT_ROOT/platforms"
|
|
13
|
+
|
|
14
|
+
# 创建 platforms 目录
|
|
15
|
+
mkdir -p "$PLATFORMS_DIR"
|
|
16
|
+
|
|
17
|
+
# 检查是否安装了 cross(用于交叉编译)
|
|
18
|
+
if ! command -v cross &> /dev/null; then
|
|
19
|
+
echo "警告: 未找到 cross 工具,尝试安装..."
|
|
20
|
+
cargo install cross --git https://github.com/cross-rs/cross
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# 定义目标平台
|
|
24
|
+
TARGETS=(
|
|
25
|
+
"x86_64-apple-darwin|darwin-x64"
|
|
26
|
+
"aarch64-apple-darwin|darwin-arm64"
|
|
27
|
+
"x86_64-unknown-linux-gnu|linux-x64"
|
|
28
|
+
"aarch64-unknown-linux-gnu|linux-arm64"
|
|
29
|
+
"x86_64-pc-windows-msvc|win32-x64"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# 构建函数
|
|
33
|
+
build_target() {
|
|
34
|
+
local target=$1
|
|
35
|
+
local platform_name=$2
|
|
36
|
+
|
|
37
|
+
echo ""
|
|
38
|
+
echo "=========================================="
|
|
39
|
+
echo "构建目标: $target"
|
|
40
|
+
echo "平台名称: $platform_name"
|
|
41
|
+
echo "=========================================="
|
|
42
|
+
|
|
43
|
+
# 使用 cross 交叉编译,Windows 使用原生 cargo
|
|
44
|
+
if [[ "$target" == *"windows"* ]]; then
|
|
45
|
+
cargo build --release --target "$target"
|
|
46
|
+
elif command -v cross &> /dev/null; then
|
|
47
|
+
cross build --release --target "$target"
|
|
48
|
+
else
|
|
49
|
+
# 如果没有 cross,尝试直接使用 cargo(仅适用于当前平台)
|
|
50
|
+
cargo build --release --target "$target"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# 确定输出目录和文件名
|
|
54
|
+
local output_dir="$PLATFORMS_DIR/$platform_name"
|
|
55
|
+
mkdir -p "$output_dir"
|
|
56
|
+
|
|
57
|
+
# 确定二进制文件名
|
|
58
|
+
if [[ "$target" == *"windows"* ]]; then
|
|
59
|
+
local binary_name="fnva.exe"
|
|
60
|
+
else
|
|
61
|
+
local binary_name="fnva"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# 复制二进制文件
|
|
65
|
+
local source_binary="$PROJECT_ROOT/target/$target/release/$binary_name"
|
|
66
|
+
|
|
67
|
+
if [ -f "$source_binary" ]; then
|
|
68
|
+
cp "$source_binary" "$output_dir/$binary_name"
|
|
69
|
+
echo "✓ 成功构建: $output_dir/$binary_name"
|
|
70
|
+
|
|
71
|
+
# 可选:压缩二进制文件(使用 strip)
|
|
72
|
+
if command -v strip &> /dev/null && [[ "$binary_name" != "*.exe" ]]; then
|
|
73
|
+
strip "$output_dir/$binary_name"
|
|
74
|
+
echo "✓ 已优化二进制文件大小"
|
|
75
|
+
fi
|
|
76
|
+
else
|
|
77
|
+
echo "✗ 错误: 未找到构建产物: $source_binary"
|
|
78
|
+
return 1
|
|
79
|
+
fi
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# 构建所有目标
|
|
83
|
+
for target_info in "${TARGETS[@]}"; do
|
|
84
|
+
IFS='|' read -r target platform <<< "$target_info"
|
|
85
|
+
|
|
86
|
+
build_target "$target" "$platform" || echo "跳过 $target(构建失败或平台不支持)"
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
echo ""
|
|
90
|
+
echo "=========================================="
|
|
91
|
+
echo "构建完成!"
|
|
92
|
+
echo "二进制文件位置: $PLATFORMS_DIR"
|
|
93
|
+
echo "=========================================="
|
|
94
|
+
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 本地构建脚本 - 仅构建当前平台的二进制文件
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "开始构建当前平台的二进制文件..."
|
|
8
|
+
|
|
9
|
+
# 获取项目根目录
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
+
PLATFORMS_DIR="$PROJECT_ROOT/platforms"
|
|
13
|
+
|
|
14
|
+
# 创建 platforms 目录
|
|
15
|
+
mkdir -p "$PLATFORMS_DIR"
|
|
16
|
+
|
|
17
|
+
# 检测当前平台
|
|
18
|
+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
19
|
+
ARCH=$(uname -m)
|
|
20
|
+
|
|
21
|
+
# 确定目标平台
|
|
22
|
+
case "$OS" in
|
|
23
|
+
darwin)
|
|
24
|
+
if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
|
|
25
|
+
TARGET="aarch64-apple-darwin"
|
|
26
|
+
PLATFORM="darwin-arm64"
|
|
27
|
+
else
|
|
28
|
+
TARGET="x86_64-apple-darwin"
|
|
29
|
+
PLATFORM="darwin-x64"
|
|
30
|
+
fi
|
|
31
|
+
BINARY_NAME="fnva"
|
|
32
|
+
;;
|
|
33
|
+
linux)
|
|
34
|
+
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
|
35
|
+
TARGET="aarch64-unknown-linux-gnu"
|
|
36
|
+
PLATFORM="linux-arm64"
|
|
37
|
+
else
|
|
38
|
+
TARGET="x86_64-unknown-linux-gnu"
|
|
39
|
+
PLATFORM="linux-x64"
|
|
40
|
+
fi
|
|
41
|
+
BINARY_NAME="fnva"
|
|
42
|
+
;;
|
|
43
|
+
*)
|
|
44
|
+
echo "错误: 不支持的操作系统: $OS"
|
|
45
|
+
echo "请使用 GitHub Actions 构建其他平台"
|
|
46
|
+
exit 1
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
|
|
50
|
+
echo "检测到平台: $OS ($ARCH)"
|
|
51
|
+
echo "目标平台: $TARGET"
|
|
52
|
+
echo "平台目录: $PLATFORM"
|
|
53
|
+
|
|
54
|
+
# 构建
|
|
55
|
+
echo ""
|
|
56
|
+
echo "开始构建..."
|
|
57
|
+
cargo build --release --target "$TARGET"
|
|
58
|
+
|
|
59
|
+
# 准备输出目录
|
|
60
|
+
OUTPUT_DIR="$PLATFORMS_DIR/$PLATFORM"
|
|
61
|
+
mkdir -p "$OUTPUT_DIR"
|
|
62
|
+
|
|
63
|
+
# 复制二进制文件
|
|
64
|
+
SOURCE_BINARY="$PROJECT_ROOT/target/$TARGET/release/$BINARY_NAME"
|
|
65
|
+
if [ -f "$SOURCE_BINARY" ]; then
|
|
66
|
+
cp "$SOURCE_BINARY" "$OUTPUT_DIR/$BINARY_NAME"
|
|
67
|
+
echo "✓ 成功构建: $OUTPUT_DIR/$BINARY_NAME"
|
|
68
|
+
|
|
69
|
+
# 优化二进制文件大小
|
|
70
|
+
if command -v strip &> /dev/null; then
|
|
71
|
+
strip "$OUTPUT_DIR/$BINARY_NAME"
|
|
72
|
+
echo "✓ 已优化二进制文件大小"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# 显示文件大小
|
|
76
|
+
ls -lh "$OUTPUT_DIR/$BINARY_NAME"
|
|
77
|
+
else
|
|
78
|
+
echo "✗ 错误: 未找到构建产物: $SOURCE_BINARY"
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo ""
|
|
83
|
+
echo "=========================================="
|
|
84
|
+
echo "构建完成!"
|
|
85
|
+
echo "二进制文件位置: $OUTPUT_DIR/$BINARY_NAME"
|
|
86
|
+
echo "=========================================="
|
|
87
|
+
echo ""
|
|
88
|
+
echo "注意: 此脚本仅构建当前平台。"
|
|
89
|
+
echo "要构建所有平台,请使用 GitHub Actions 或运行 scripts/build-all.sh"
|
|
90
|
+
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
|
|
8
|
+
function detectShell() {
|
|
9
|
+
if (process.platform === 'win32') {
|
|
10
|
+
return 'powershell';
|
|
11
|
+
}
|
|
12
|
+
return process.env.SHELL?.split('/').pop() || 'bash';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getShellConfigPath(shell) {
|
|
16
|
+
switch (shell) {
|
|
17
|
+
case 'powershell':
|
|
18
|
+
return path.join(process.env.USERPROFILE || os.homedir(), 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
19
|
+
case 'bash':
|
|
20
|
+
return path.join(os.homedir(), '.bashrc');
|
|
21
|
+
case 'zsh':
|
|
22
|
+
return path.join(os.homedir(), '.zshrc');
|
|
23
|
+
case 'fish':
|
|
24
|
+
return path.join(os.homedir(), '.config', 'fish', 'config.fish');
|
|
25
|
+
default:
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getPowerShellFunction() {
|
|
31
|
+
return `
|
|
32
|
+
# fnva 自动化函数 - 由 npm 安装自动添加
|
|
33
|
+
function fnva {
|
|
34
|
+
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
|
+
$tempFile = "$env:TEMP\fnva_script_$(Get-Random).ps1"
|
|
36
|
+
|
|
37
|
+
$env:FNVAAUTOMODE = "1"
|
|
38
|
+
try {
|
|
39
|
+
cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args" | Out-File -FilePath $tempFile -Encoding UTF8
|
|
40
|
+
& $tempFile
|
|
41
|
+
} finally {
|
|
42
|
+
$env:FNVAAUTOMODE = ""
|
|
43
|
+
Remove-Item $tempFile -ErrorAction SilentlyContinue
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
$env:FNVAAUTOMODE = "1"
|
|
47
|
+
try {
|
|
48
|
+
cmd.exe /c "set FNVA_AUTO_MODE=%FNVAAUTOMODE% && fnva $args"
|
|
49
|
+
} finally {
|
|
50
|
+
$env:FNVAAUTOMODE = ""
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getBashFunction() {
|
|
58
|
+
return `
|
|
59
|
+
# fnva 自动化函数 - 由 npm 安装自动添加
|
|
60
|
+
fnva() {
|
|
61
|
+
if [[ \$# -ge 2 && ("\$1" == "java" || "\$1" == "llm" || "\$1" == "cc") && "\$2" == "use" ]]; then
|
|
62
|
+
local temp_file=\$(mktemp)
|
|
63
|
+
chmod +x "\$temp_file"
|
|
64
|
+
|
|
65
|
+
FNVA_AUTO_MODE=1 fnva "\$@" > "\$temp_file"
|
|
66
|
+
source "\$temp_file"
|
|
67
|
+
rm -f "\$temp_file"
|
|
68
|
+
else
|
|
69
|
+
FNVA_AUTO_MODE=1 fnva "\$@"
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getFishFunction() {
|
|
76
|
+
return `
|
|
77
|
+
# fnva 自动化函数 - 由 npm 安装自动添加
|
|
78
|
+
function fnva
|
|
79
|
+
if test (count \$argv) -ge 2; and string match -q -r "^(java|llm|cc)\$" \$argv[1]; and test \$argv[2] = "use"
|
|
80
|
+
set temp_file (mktemp)
|
|
81
|
+
chmod +x \$temp_file
|
|
82
|
+
env FNVA_AUTO_MODE=1 fnva \$argv > \$temp_file
|
|
83
|
+
source \$temp_file
|
|
84
|
+
rm -f \$temp_file
|
|
85
|
+
else
|
|
86
|
+
env FNVA_AUTO_MODE=1 fnva \$argv
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getShellFunction(shell) {
|
|
93
|
+
switch (shell) {
|
|
94
|
+
case 'powershell':
|
|
95
|
+
return getPowerShellFunction();
|
|
96
|
+
case 'bash':
|
|
97
|
+
return getBashFunction();
|
|
98
|
+
case 'zsh':
|
|
99
|
+
return getBashFunction(); // zsh 使用和 bash 相同的语法
|
|
100
|
+
case 'fish':
|
|
101
|
+
return getFishFunction();
|
|
102
|
+
default:
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function isFunctionInstalled(configPath, shell) {
|
|
108
|
+
if (!fs.existsSync(configPath)) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
113
|
+
return content.includes('fnva 自动化函数 - 由 npm 安装自动添加');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function installShellIntegration() {
|
|
117
|
+
const shell = detectShell();
|
|
118
|
+
const configPath = getShellConfigPath(shell);
|
|
119
|
+
|
|
120
|
+
if (!configPath) {
|
|
121
|
+
console.log(`❌ 不支持的 shell: ${shell}`);
|
|
122
|
+
console.log('请手动配置 fnva,详见: https://github.com/your-repo/fnva');
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (isFunctionInstalled(configPath, shell)) {
|
|
127
|
+
console.log(`✅ fnva shell 集成已安装在: ${configPath}`);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
// 确保目录存在
|
|
133
|
+
const dir = path.dirname(configPath);
|
|
134
|
+
if (!fs.existsSync(dir)) {
|
|
135
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 获取函数定义
|
|
139
|
+
const functionCode = getShellFunction(shell);
|
|
140
|
+
|
|
141
|
+
// 添加到配置文件
|
|
142
|
+
if (fs.existsSync(configPath)) {
|
|
143
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
144
|
+
fs.writeFileSync(configPath, content + '\n' + functionCode);
|
|
145
|
+
} else {
|
|
146
|
+
fs.writeFileSync(configPath, functionCode);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log(`✅ fnva shell 集成已安装到: ${configPath}`);
|
|
150
|
+
console.log('🔄 请重新加载你的 shell 配置:');
|
|
151
|
+
|
|
152
|
+
switch (shell) {
|
|
153
|
+
case 'powershell':
|
|
154
|
+
console.log(' . $PROFILE');
|
|
155
|
+
break;
|
|
156
|
+
case 'bash':
|
|
157
|
+
console.log(' source ~/.bashrc');
|
|
158
|
+
break;
|
|
159
|
+
case 'zsh':
|
|
160
|
+
console.log(' source ~/.zshrc');
|
|
161
|
+
break;
|
|
162
|
+
case 'fish':
|
|
163
|
+
console.log(' source ~/.config/fish/config.fish');
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return true;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.log(`❌ 安装失败: ${error.message}`);
|
|
170
|
+
console.log('请手动配置 fnva');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 询问用户是否安装
|
|
176
|
+
function promptInstallation() {
|
|
177
|
+
if (process.env.FNVA_SKIP_SHELL_SETUP === '1') {
|
|
178
|
+
console.log('⏭️ 跳过 shell 集成安装');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const shell = detectShell();
|
|
183
|
+
console.log(`🔧 检测到 shell: ${shell}`);
|
|
184
|
+
console.log('🚀 是否安装 fnva shell 集成? (y/N)');
|
|
185
|
+
|
|
186
|
+
process.stdin.resume();
|
|
187
|
+
process.stdin.setEncoding('utf8');
|
|
188
|
+
|
|
189
|
+
process.stdin.on('data', function(data) {
|
|
190
|
+
const response = data.toString().trim().toLowerCase();
|
|
191
|
+
if (response === 'y' || response === 'yes') {
|
|
192
|
+
installShellIntegration();
|
|
193
|
+
} else {
|
|
194
|
+
console.log('⏭️ 跳过 shell 集成安装');
|
|
195
|
+
console.log('📖 手动配置指南: https://github.com/your-repo/fnva');
|
|
196
|
+
}
|
|
197
|
+
process.exit(0);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// 10秒后自动跳过
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
console.log('⏭️ 超时,跳过 shell 集成安装');
|
|
203
|
+
console.log('📖 手动配置指南: https://github.com/your-repo/fnva');
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}, 10000);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 主程序
|
|
209
|
+
if (require.main === module) {
|
|
210
|
+
console.log('🔧 fnva shell 集成安装器');
|
|
211
|
+
|
|
212
|
+
if (process.argv.includes('--auto') || process.argv.includes('--yes')) {
|
|
213
|
+
installShellIntegration();
|
|
214
|
+
} else {
|
|
215
|
+
promptInstallation();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = {
|
|
220
|
+
detectShell,
|
|
221
|
+
getShellConfigPath,
|
|
222
|
+
getShellFunction,
|
|
223
|
+
isFunctionInstalled,
|
|
224
|
+
installShellIntegration
|
|
225
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 发布前准备脚本
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "准备发布 npm 包..."
|
|
8
|
+
|
|
9
|
+
# 检查必要的文件
|
|
10
|
+
REQUIRED_FILES=(
|
|
11
|
+
"package.json"
|
|
12
|
+
"bin/fnva"
|
|
13
|
+
"README.md"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
for file in "${REQUIRED_FILES[@]}"; do
|
|
17
|
+
if [ ! -f "$file" ]; then
|
|
18
|
+
echo "错误: 缺少必需文件: $file"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# 检查版本号
|
|
24
|
+
echo "检查版本号..."
|
|
25
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
26
|
+
echo "当前版本: $VERSION"
|
|
27
|
+
|
|
28
|
+
# 在 CI/CD 环境中跳过平台二进制文件检查和交互式确认
|
|
29
|
+
if [ -n "$CI" ] || [ -n "$GITHUB_ACTIONS" ]; then
|
|
30
|
+
echo "CI/CD 环境: 跳过构建检查,自动确认发布版本 $VERSION"
|
|
31
|
+
echo "平台二进制文件将在发布时包含"
|
|
32
|
+
else
|
|
33
|
+
# 本地环境: 检查平台二进制文件
|
|
34
|
+
if [ ! -d "platforms" ] || [ -z "$(ls -A platforms 2>/dev/null)" ]; then
|
|
35
|
+
echo "警告: platforms 目录为空或不存在"
|
|
36
|
+
echo "请先运行 'npm run build' 或 'npm run build:all' 构建二进制文件"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# 本地环境: 提示用户确认
|
|
41
|
+
read -p "确认发布版本 $VERSION? (y/N) " -n 1 -r
|
|
42
|
+
echo
|
|
43
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
44
|
+
echo "已取消发布"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo "准备完成,准备发布到 NPM"
|
|
50
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
function detectShell() {
|
|
8
|
+
if (process.platform === 'win32') {
|
|
9
|
+
return 'powershell';
|
|
10
|
+
}
|
|
11
|
+
return process.env.SHELL?.split('/').pop() || 'bash';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getShellConfigPath(shell) {
|
|
15
|
+
switch (shell) {
|
|
16
|
+
case 'powershell':
|
|
17
|
+
return path.join(process.env.USERPROFILE || os.homedir(), 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
|
|
18
|
+
case 'bash':
|
|
19
|
+
return path.join(os.homedir(), '.bashrc');
|
|
20
|
+
case 'zsh':
|
|
21
|
+
return path.join(os.homedir(), '.zshrc');
|
|
22
|
+
case 'fish':
|
|
23
|
+
return path.join(os.homedir(), '.config', 'fish', 'config.fish');
|
|
24
|
+
default:
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function removeShellIntegration(configPath, shell) {
|
|
30
|
+
if (!fs.existsSync(configPath)) {
|
|
31
|
+
console.log(`⚠️ 配置文件不存在: ${configPath}`);
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
let content = fs.readFileSync(configPath, 'utf8');
|
|
37
|
+
const originalContent = content;
|
|
38
|
+
|
|
39
|
+
// 方法1: 查找标记,精确删除整个函数块
|
|
40
|
+
const marker = '# fnva 自动化函数 - 由 npm 安装自动添加';
|
|
41
|
+
const startIndex = content.indexOf(marker);
|
|
42
|
+
|
|
43
|
+
if (startIndex !== -1) {
|
|
44
|
+
// 找到标记前的换行符
|
|
45
|
+
const beforeMarker = content.substring(0, startIndex).trimEnd();
|
|
46
|
+
|
|
47
|
+
// 从标记开始查找完整的函数
|
|
48
|
+
const afterMarker = content.substring(startIndex);
|
|
49
|
+
const lines = afterMarker.split('\n');
|
|
50
|
+
|
|
51
|
+
let functionEndIndex = -1;
|
|
52
|
+
let braceCount = 0;
|
|
53
|
+
let foundFunction = false;
|
|
54
|
+
|
|
55
|
+
for (let i = 0; i < lines.length; i++) {
|
|
56
|
+
const line = lines[i];
|
|
57
|
+
if (line.includes('function fnva') || line.includes('fnva(')) {
|
|
58
|
+
foundFunction = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (foundFunction) {
|
|
62
|
+
// 计算大括号
|
|
63
|
+
for (const char of line) {
|
|
64
|
+
if (char === '{') braceCount++;
|
|
65
|
+
if (char === '}') braceCount--;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 当大括号平衡时,函数结束
|
|
69
|
+
if (braceCount === 0) {
|
|
70
|
+
functionEndIndex = i + 1;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (functionEndIndex !== -1) {
|
|
77
|
+
// 重建内容
|
|
78
|
+
const afterFunction = lines.slice(functionEndIndex).join('\n');
|
|
79
|
+
content = beforeMarker + '\n' + afterFunction;
|
|
80
|
+
} else {
|
|
81
|
+
console.log('⚠️ 无法确定函数结束位置');
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 方法2: 如果没找到标记,使用正则表达式清理任何 fnva 相关内容
|
|
87
|
+
if (content === originalContent) {
|
|
88
|
+
// 使用正则表达式删除任何包含 fnva 的行和相关的环境变量处理
|
|
89
|
+
content = content
|
|
90
|
+
// 删除标记到函数结束的所有内容
|
|
91
|
+
.replace(/# fnva 自动化函数 - 由 npm 安装自动添加[\s\S]*?(?=\n\S|\n$)/g, '')
|
|
92
|
+
// 删除剩余的 fnva 相关行
|
|
93
|
+
.replace(/.*fnva.*\n?/g, '')
|
|
94
|
+
// 删除 FNVAAUTOMODE 相关行
|
|
95
|
+
.replace(/.*FNVAAUTOMODE.*\n?/g, '')
|
|
96
|
+
// 删除 cmd.exe 调用 fnva 的行
|
|
97
|
+
.replace(/.*cmd\.exe.*fnva.*\n?/g, '')
|
|
98
|
+
// 清理多余的空行
|
|
99
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
100
|
+
.trim() + '\n';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 如果内容有变化,写入文件
|
|
104
|
+
if (content !== originalContent) {
|
|
105
|
+
fs.writeFileSync(configPath, content);
|
|
106
|
+
console.log(`✅ fnva shell 集成已从 ${configPath} 移除`);
|
|
107
|
+
return true;
|
|
108
|
+
} else {
|
|
109
|
+
console.log('⚠️ 未找到需要清理的内容');
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.log(`❌ 移除失败: ${error.message}`);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function main() {
|
|
119
|
+
console.log('🔧 fnva shell 集成卸载器');
|
|
120
|
+
|
|
121
|
+
const shell = detectShell();
|
|
122
|
+
const configPath = getShellConfigPath(shell);
|
|
123
|
+
|
|
124
|
+
if (!configPath) {
|
|
125
|
+
console.log(`❌ 不支持的 shell: ${shell}`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const success = removeShellIntegration(configPath, shell);
|
|
130
|
+
|
|
131
|
+
if (success) {
|
|
132
|
+
console.log('🔄 请重新加载你的 shell 配置:');
|
|
133
|
+
|
|
134
|
+
switch (shell) {
|
|
135
|
+
case 'powershell':
|
|
136
|
+
console.log(' . $PROFILE');
|
|
137
|
+
break;
|
|
138
|
+
case 'bash':
|
|
139
|
+
console.log(' source ~/.bashrc');
|
|
140
|
+
break;
|
|
141
|
+
case 'zsh':
|
|
142
|
+
console.log(' source ~/.zshrc');
|
|
143
|
+
break;
|
|
144
|
+
case 'fish':
|
|
145
|
+
console.log(' source ~/.config/fish/config.fish');
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (require.main === module) {
|
|
152
|
+
main();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = {
|
|
156
|
+
detectShell,
|
|
157
|
+
getShellConfigPath,
|
|
158
|
+
removeShellIntegration
|
|
159
|
+
};
|