mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-10 00:33:28 +08:00
🐛 fix(ai): 隐藏 Claude CLI 后台控制台窗口 (#749)
## 背景 Windows 下选择 Claude CLI 作为当前 AI Provider 后,执行 SQL 美化、AI 洞察等会调用 Claude CLI 的功能时,会弹出 Claude 控制台窗口并打断界面操作。 Closes #694 ## 变更点 - 统一 Claude CLI 认证检查、非流式聊天与流式聊天的命令创建入口 - Windows 子进程启用 `HideWindow` 与 `CREATE_NO_WINDOW`,同时保留已有启动标志 - 通过 build tag 保持 macOS/Linux 原有进程行为 - 新增 Windows 回归测试,覆盖隐藏窗口参数和既有 flags 保留逻辑 ## 影响范围 - 仅影响 Claude CLI Provider 在 Windows 上启动后台子进程的窗口行为 - 不修改 AI 配置、请求参数、认证流程或前端交互 - 不新增依赖,不涉及数据迁移;macOS/Linux 行为不变 ## 验证 - `go test ./internal/ai/provider -run ClaudeCLI -count=1` - `go test ./internal/ai/service -count=1` - `go vet ./internal/ai/provider` - Linux amd64 与 macOS amd64 包级交叉编译通过
This commit is contained in:
@@ -137,7 +137,7 @@ func CheckClaudeCLILocalAuth(ctx context.Context) error {
|
||||
defer cancel()
|
||||
|
||||
args := append(buildClaudeCLILocalAuthIsolationArgs(), "auth", "status", "--json")
|
||||
cmd := claudeCommandContext(ctx, command.Path, args...)
|
||||
cmd := newClaudeCLICommand(ctx, command.Path, args...)
|
||||
env, err := buildClaudeCLIEnv(ai.ProviderConfig{AuthMode: "local-cli"}, cmd.Environ(), runtime.GOOS, claudeLookPath, fileExists)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -224,7 +224,7 @@ func (p *ClaudeCLIProvider) Chat(ctx context.Context, req ai.ChatRequest) (*ai.C
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := claudeCommandContext(ctx, command.Path, args...)
|
||||
cmd := newClaudeCLICommand(ctx, command.Path, args...)
|
||||
if err := p.setEnv(cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -301,7 +301,7 @@ func (p *ClaudeCLIProvider) ChatStream(ctx context.Context, req ai.ChatRequest,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := claudeCommandContext(ctx, command.Path, args...)
|
||||
cmd := newClaudeCLICommand(ctx, command.Path, args...)
|
||||
if err := p.setEnv(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -469,6 +469,12 @@ func isClaudeCLITimeout(ctx context.Context, err error) bool {
|
||||
return errors.Is(ctx.Err(), context.DeadlineExceeded) || errors.Is(err, context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
func newClaudeCLICommand(ctx context.Context, name string, args ...string) *exec.Cmd {
|
||||
cmd := claudeCommandContext(ctx, name, args...)
|
||||
configureClaudeCLICommand(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func claudeCLIEndpointForLog(config ai.ProviderConfig) string {
|
||||
baseURL := strings.TrimRight(strings.TrimSpace(config.BaseURL), "/")
|
||||
if baseURL != "" {
|
||||
|
||||
7
internal/ai/provider/claude_cli_process_other.go
Normal file
7
internal/ai/provider/claude_cli_process_other.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
|
||||
package provider
|
||||
|
||||
import "os/exec"
|
||||
|
||||
func configureClaudeCLICommand(_ *exec.Cmd) {}
|
||||
21
internal/ai/provider/claude_cli_process_windows.go
Normal file
21
internal/ai/provider/claude_cli_process_windows.go
Normal file
@@ -0,0 +1,21 @@
|
||||
//go:build windows
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const claudeCLIWindowsCreateNoWindow uint32 = 0x08000000
|
||||
|
||||
func configureClaudeCLICommand(cmd *exec.Cmd) {
|
||||
if cmd == nil {
|
||||
return
|
||||
}
|
||||
if cmd.SysProcAttr == nil {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
cmd.SysProcAttr.HideWindow = true
|
||||
cmd.SysProcAttr.CreationFlags |= claudeCLIWindowsCreateNoWindow
|
||||
}
|
||||
31
internal/ai/provider/claude_cli_process_windows_test.go
Normal file
31
internal/ai/provider/claude_cli_process_windows_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build windows
|
||||
|
||||
package provider
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigureClaudeCLICommandHidesWindowsConsole(t *testing.T) {
|
||||
const existingCreationFlag uint32 = 0x00000004
|
||||
cmd := &exec.Cmd{
|
||||
SysProcAttr: &syscall.SysProcAttr{CreationFlags: existingCreationFlag},
|
||||
}
|
||||
|
||||
configureClaudeCLICommand(cmd)
|
||||
|
||||
if cmd.SysProcAttr == nil {
|
||||
t.Fatal("expected Windows process attributes to be configured")
|
||||
}
|
||||
if !cmd.SysProcAttr.HideWindow {
|
||||
t.Fatal("expected Claude CLI window to be hidden")
|
||||
}
|
||||
if cmd.SysProcAttr.CreationFlags&claudeCLIWindowsCreateNoWindow == 0 {
|
||||
t.Fatalf("expected CREATE_NO_WINDOW, got creation flags %#x", cmd.SysProcAttr.CreationFlags)
|
||||
}
|
||||
if cmd.SysProcAttr.CreationFlags&existingCreationFlag == 0 {
|
||||
t.Fatalf("expected existing creation flags to be preserved, got %#x", cmd.SysProcAttr.CreationFlags)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user