🐛 fix(ai): 隐藏 Claude CLI 后台控制台窗口

- 统一认证检查、非流式与流式请求的 Claude CLI 命令创建
- Windows 启用 HideWindow 与 CREATE_NO_WINDOW,并保留已有启动标志
- 增加跨平台进程配置与 Windows 回归测试
This commit is contained in:
AutumnNazi
2026-07-27 22:15:52 +08:00
parent 70282286fc
commit cdc484fd96
4 changed files with 68 additions and 3 deletions

View File

@@ -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 != "" {

View File

@@ -0,0 +1,7 @@
//go:build !windows
package provider
import "os/exec"
func configureClaudeCLICommand(_ *exec.Cmd) {}

View 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
}

View 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)
}
}