Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

87 lines
2.6 KiB
Go

//go:build windows
package agentstatus
import (
"context"
"errors"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"golang.org/x/sys/windows"
)
func newInstallExecCommand(ctx context.Context, executable string, args ...string) *exec.Cmd {
extension := strings.ToLower(filepath.Ext(executable))
var command *exec.Cmd
switch extension {
case ".cmd", ".bat":
// Batch files cannot be passed directly to CreateProcess. Pass the argv
// pieces to cmd.exe separately so Go performs the Windows quoting exactly
// once. `call` keeps nested batch-file semantics explicit.
command = exec.CommandContext(ctx, installCommandInterpreter(), append([]string{"/D", "/S", "/C", "call", executable}, args...)...)
case ".ps1":
// Cursor's official Windows installer exposes agent.ps1. PowerShell
// scripts are not CreateProcess entry points, so invoke them explicitly.
command = exec.CommandContext(ctx, "powershell.exe", append([]string{
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", executable,
}, args...)...)
default:
command = exec.CommandContext(ctx, executable, args...)
}
configureInstallProcessCommand(command, ctx)
return command
}
func newInstallShellCommand(ctx context.Context, command string) *exec.Cmd {
cmd := exec.CommandContext(ctx, resolveInstallerShell(), "/D", "/S", "/C", command)
configureInstallProcessCommand(cmd, ctx)
return cmd
}
// configureInstallProcessCommand makes context cancellation terminate the
// complete Windows process tree. npm/opencode and PowerShell launchers are
// wrappers; killing only the wrapper leaves the real child alive, which then
// makes the next ACP probe hang or contend for the provider's database.
func configureInstallProcessCommand(command *exec.Cmd, _ context.Context) {
command.SysProcAttr = &syscall.SysProcAttr{CreationFlags: windows.CREATE_NEW_PROCESS_GROUP}
command.WaitDelay = 500 * time.Millisecond
command.Cancel = func() error {
if command.Process == nil {
return nil
}
err := exec.Command("taskkill.exe", "/PID", strconv.Itoa(command.Process.Pid), "/T", "/F").Run()
if err == nil {
return nil
}
if killErr := command.Process.Kill(); killErr != nil {
return errors.Join(err, killErr)
}
return nil
}
}
func platformExecutableFile(os.FileInfo) bool {
return true
}
func resolveInstallerShell() string {
if value := strings.TrimSpace(os.Getenv("ComSpec")); value != "" {
return value
}
return "cmd.exe"
}
func installCommandInterpreter() string {
return resolveInstallerShell()
}
func structuredInstallRunner() string {
return "cmd.exe /D /S /C call"
}