Files
2026-06-18 22:56:52 +08:00

76 lines
1.9 KiB
Go

//go:build windows
package terminal
import (
"io"
"os"
"os/exec"
procgrp "github.com/yan1h/agent-coding-workflow/internal/infra/proc"
)
// stdioBridge 把 shell 的 stdin(写端)与合并后的 stdout/stderr(读端)合成一个
// io.ReadWriteCloser,供降级实现使用。
type stdioBridge struct {
stdin io.WriteCloser
stdout io.ReadCloser
}
func (b *stdioBridge) Read(p []byte) (int, error) { return b.stdout.Read(p) }
func (b *stdioBridge) Write(p []byte) (int, error) { return b.stdin.Write(p) }
func (b *stdioBridge) Close() error {
_ = b.stdin.Close()
return b.stdout.Close()
}
// startPTY 是 Windows 降级实现:用普通管道桥接 stdin/stdout,无 PTY、无 resize,
// 仅供本地开发调试,不追求生产可用(无行编辑/光标控制/作业控制)。
//
// stdout/stderr 合并:手动建 os.Pipe,把写端同时交给子进程的 Stdout/Stderr,父进程
// Start 后立即关闭自己持有的写端;子进程退出后读端 pr 收到 EOF,read pump 据此感知结束。
func startPTY(shell string, env []string, rows, cols uint16) (*ptySession, error) {
cmd := exec.Command(shell)
cmd.Env = env
pr, pw, err := os.Pipe()
if err != nil {
return nil, err
}
cmd.Stdout = pw
cmd.Stderr = pw
stdin, err := cmd.StdinPipe()
if err != nil {
_ = pr.Close()
_ = pw.Close()
return nil, err
}
group := procgrp.NewGroup()
group.Prepare(cmd) // Windows: no-op
if err := cmd.Start(); err != nil {
_ = pr.Close()
_ = pw.Close()
_ = stdin.Close()
return nil, err
}
_ = pw.Close() // 父进程放掉写端;子进程仍持有,退出后 pr 读到 EOF
if err := group.Adopt(cmd); err != nil {
if cmd.Process != nil {
_ = cmd.Process.Kill()
}
_ = pr.Close()
_ = stdin.Close()
return nil, err
}
return &ptySession{
cmd: cmd,
group: group,
rw: &stdioBridge{stdin: stdin, stdout: pr},
resizeFn: nil, // 降级实现不支持 resize
}, nil
}