Skip to content

Commit a91cf6e

Browse files
committed
优化了交互式初始化逻辑
1 parent b826394 commit a91cf6e

File tree

5 files changed

+205
-10
lines changed

5 files changed

+205
-10
lines changed

.claude/settings.local.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"Bash(ssh-add:*)",
1919
"Bash(./fssh:*)",
2020
"Bash(go list:*)",
21-
"Bash(ssh-keygen:*)"
21+
"Bash(ssh-keygen:*)",
22+
"Bash(go run:*)",
23+
"Bash(chmod:*)"
2224
],
2325
"deny": [],
2426
"ask": []

README_CN.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,30 @@ go build ./cmd/fssh
5656
5. **启动 agent** - 立即启动 fssh agent
5757
6. **配置 SSH 客户端** - 自动更新 ~/.ssh/config
5858

59-
完成后,你就可以直接使用 SSH 并通过 Touch ID/OTP 认证了!
59+
**完成后的重要步骤:设置环境变量**
60+
61+
向导完成后会显示需要设置的环境变量。将以下内容添加到你的 shell 配置文件:
62+
63+
```bash
64+
export SSH_AUTH_SOCK=~/.fssh/agent.sock
65+
```
66+
67+
配置文件位置:
68+
- **bash**: `~/.bashrc``~/.bash_profile`
69+
- **zsh**: `~/.zshrc`
70+
- **fish**: `~/.config/fish/config.fish`
71+
72+
然后重新加载配置:
73+
```bash
74+
source ~/.zshrc # 或你的 shell 配置文件
75+
```
76+
77+
或使用自动设置脚本:
78+
```bash
79+
./contrib/setup-env.sh
80+
```
81+
82+
设置完成后,你就可以直接使用 SSH 并通过 Touch ID/OTP 认证了!
6083

6184
### 方式二:手动设置(进阶)
6285

cmd/fssh/shell_commands.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,33 @@ import (
1111
"github.com/peterh/liner"
1212
)
1313

14+
// cleanLinerInput 清理 liner 输入,移除 ANSI 转义序列和控制字符
15+
func cleanLinerInput(input string) string {
16+
input = strings.TrimSpace(input)
17+
var cleaned strings.Builder
18+
inEscape := false
19+
for _, r := range input {
20+
if r == '\x1b' || r == '\033' {
21+
inEscape = true
22+
continue
23+
}
24+
if inEscape {
25+
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
26+
inEscape = false
27+
}
28+
continue
29+
}
30+
if r < 32 || r == 127 {
31+
continue
32+
}
33+
if r == '\u3000' || r == '\u00A0' {
34+
continue
35+
}
36+
cleaned.WriteRune(r)
37+
}
38+
return strings.TrimSpace(cleaned.String())
39+
}
40+
1441
// ShellContext holds state for shell commands
1542
type ShellContext struct {
1643
infos []sshconfig.HostInfo
@@ -184,7 +211,8 @@ func cmdAdd(ctx *ShellContext) error {
184211
}
185212

186213
confirm, _ := ctx.liner.Prompt("\nSave this configuration? [Y/n]: ")
187-
if strings.ToLower(strings.TrimSpace(confirm)) == "n" {
214+
confirm = cleanLinerInput(confirm)
215+
if strings.ToLower(confirm) == "n" {
188216
fmt.Println("Cancelled")
189217
return nil
190218
}
@@ -330,7 +358,8 @@ func cmdEdit(ctx *ShellContext, args string) error {
330358

331359
// Confirm and save
332360
confirm, _ := ctx.liner.Prompt("\nSave changes? [Y/n]: ")
333-
if strings.ToLower(strings.TrimSpace(confirm)) == "n" {
361+
confirm = cleanLinerInput(confirm)
362+
if strings.ToLower(confirm) == "n" {
334363
fmt.Println("Cancelled")
335364
return nil
336365
}
@@ -369,7 +398,8 @@ func cmdDelete(ctx *ShellContext, args string) error {
369398
}
370399

371400
line, _ := ctx.liner.Prompt("\nType 'yes' to confirm deletion: ")
372-
if strings.TrimSpace(line) != "yes" {
401+
line = cleanLinerInput(line)
402+
if line != "yes" {
373403
fmt.Println("Cancelled")
374404
return nil
375405
}
@@ -801,7 +831,8 @@ func cmdGlobalEdit(ctx *ShellContext) error {
801831
// Confirm and save
802832
fmt.Println()
803833
confirm, _ := ctx.liner.Prompt("Save global configuration? [Y/n]: ")
804-
if strings.ToLower(strings.TrimSpace(confirm)) == "n" {
834+
confirm = cleanLinerInput(confirm)
835+
if strings.ToLower(confirm) == "n" {
805836
fmt.Println("Cancelled")
806837
return nil
807838
}

contrib/setup-env.sh

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/bin/bash
2+
# fssh Environment Setup Script
3+
# This script helps you set up SSH_AUTH_SOCK environment variable
4+
5+
set -e
6+
7+
# Detect socket path
8+
SOCKET_PATH="$HOME/.fssh/agent.sock"
9+
if [ -f "$HOME/.fssh/config.json" ]; then
10+
# Try to extract socket path from config
11+
CUSTOM_SOCKET=$(grep -o '"socket":\s*"[^"]*"' "$HOME/.fssh/config.json" | cut -d'"' -f4)
12+
if [ -n "$CUSTOM_SOCKET" ]; then
13+
# Expand ~ to $HOME
14+
SOCKET_PATH="${CUSTOM_SOCKET/#\~/$HOME}"
15+
fi
16+
fi
17+
18+
echo "fssh Environment Setup"
19+
echo "======================"
20+
echo ""
21+
echo "Socket path: $SOCKET_PATH"
22+
echo ""
23+
24+
# Detect shell
25+
SHELL_NAME=$(basename "$SHELL")
26+
case "$SHELL_NAME" in
27+
bash)
28+
if [ -f "$HOME/.bash_profile" ]; then
29+
CONFIG_FILE="$HOME/.bash_profile"
30+
else
31+
CONFIG_FILE="$HOME/.bashrc"
32+
fi
33+
;;
34+
zsh)
35+
CONFIG_FILE="$HOME/.zshrc"
36+
;;
37+
fish)
38+
CONFIG_FILE="$HOME/.config/fish/config.fish"
39+
mkdir -p "$(dirname "$CONFIG_FILE")"
40+
;;
41+
*)
42+
echo "Warning: Unknown shell '$SHELL_NAME'"
43+
CONFIG_FILE="$HOME/.profile"
44+
;;
45+
esac
46+
47+
echo "Detected shell: $SHELL_NAME"
48+
echo "Config file: $CONFIG_FILE"
49+
echo ""
50+
51+
# Check if already configured
52+
if [ -f "$CONFIG_FILE" ] && grep -q "SSH_AUTH_SOCK.*fssh" "$CONFIG_FILE"; then
53+
echo "✓ SSH_AUTH_SOCK already configured in $CONFIG_FILE"
54+
echo ""
55+
echo "Current configuration:"
56+
grep "SSH_AUTH_SOCK.*fssh" "$CONFIG_FILE"
57+
echo ""
58+
read -p "Do you want to update it? (y/N): " -n 1 -r
59+
echo
60+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
61+
echo "No changes made."
62+
exit 0
63+
fi
64+
# Remove old configuration
65+
if [[ "$OSTYPE" == "darwin"* ]]; then
66+
sed -i '' '/SSH_AUTH_SOCK.*fssh/d' "$CONFIG_FILE"
67+
else
68+
sed -i '/SSH_AUTH_SOCK.*fssh/d' "$CONFIG_FILE"
69+
fi
70+
fi
71+
72+
# Add configuration
73+
echo "" >> "$CONFIG_FILE"
74+
echo "# fssh SSH agent" >> "$CONFIG_FILE"
75+
echo "export SSH_AUTH_SOCK=\"$SOCKET_PATH\"" >> "$CONFIG_FILE"
76+
77+
echo "✓ Added SSH_AUTH_SOCK to $CONFIG_FILE"
78+
echo ""
79+
echo "To apply changes:"
80+
echo " source $CONFIG_FILE"
81+
echo ""
82+
echo "Or open a new terminal window."
83+
echo ""
84+
85+
# Ask if user wants to apply now
86+
read -p "Apply changes to current shell? (y/N): " -n 1 -r
87+
echo
88+
if [[ $REPLY =~ ^[Yy]$ ]]; then
89+
export SSH_AUTH_SOCK="$SOCKET_PATH"
90+
echo "✓ Environment variable set for current shell"
91+
echo ""
92+
echo "Test with: ssh-add -l"
93+
fi

internal/otp/prompt.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ func PromptPassword(prompt string) (string, error) {
1919
// 不是终端,从标准输入读取
2020
scanner := bufio.NewScanner(os.Stdin)
2121
scanner.Scan()
22-
return scanner.Text(), scanner.Err()
22+
// 对于密码,只清理 ANSI 转义序列,保留其他字符
23+
return cleanInput(scanner.Text()), scanner.Err()
2324
}
2425

2526
// 终端环境,使用不回显的密码输入
@@ -61,7 +62,8 @@ func PromptCode(prompt string) (string, error) {
6162
return "", fmt.Errorf("读取验证码失败: %w", err)
6263
}
6364

64-
code := strings.TrimSpace(scanner.Text())
65+
// 使用 cleanInput 清理控制字符
66+
code := cleanInput(scanner.Text())
6567

6668
// 验证格式(6或8位数字)
6769
if len(code) != 6 && len(code) != 8 {
@@ -83,11 +85,54 @@ func PromptConfirm(prompt string) bool {
8385

8486
scanner := bufio.NewScanner(os.Stdin)
8587
scanner.Scan()
86-
answer := strings.ToLower(strings.TrimSpace(scanner.Text()))
88+
answer := scanner.Text()
89+
90+
// 清理输入:移除空白和控制字符
91+
answer = cleanInput(answer)
92+
answer = strings.ToLower(answer)
8793

8894
return answer == "y" || answer == "yes"
8995
}
9096

97+
// cleanInput 清理用户输入,移除控制序列和多余空白
98+
func cleanInput(input string) string {
99+
// 移除前后空白
100+
input = strings.TrimSpace(input)
101+
102+
// 移除 ANSI 转义序列和其他控制字符
103+
var cleaned strings.Builder
104+
inEscape := false
105+
for _, r := range input {
106+
// 检测 ANSI 转义序列开始
107+
if r == '\x1b' || r == '\033' {
108+
inEscape = true
109+
continue
110+
}
111+
112+
// 如果在转义序列中,跳过直到遇到字母
113+
if inEscape {
114+
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
115+
inEscape = false
116+
}
117+
continue
118+
}
119+
120+
// 过滤其他控制字符(除了换行和制表符,虽然我们也不需要它们)
121+
if r < 32 || r == 127 {
122+
continue
123+
}
124+
125+
// 过滤 Unicode 空白字符
126+
if r == '\u3000' || r == '\u00A0' {
127+
continue
128+
}
129+
130+
cleaned.WriteRune(r)
131+
}
132+
133+
return strings.TrimSpace(cleaned.String())
134+
}
135+
91136
// PromptInput 提示输入普通文本(可见回显)
92137
func PromptInput(prompt string) (string, error) {
93138
fmt.Print(prompt)
@@ -100,7 +145,8 @@ func PromptInput(prompt string) (string, error) {
100145
return "", nil // EOF
101146
}
102147

103-
return strings.TrimSpace(scanner.Text()), nil
148+
// 使用 cleanInput 清理控制字符
149+
return cleanInput(scanner.Text()), nil
104150
}
105151

106152
// ValidatePasswordStrength 验证密码强度

0 commit comments

Comments
 (0)