//go:build !windows package terminal import ( "os/exec" "github.com/creack/pty" procgrp "github.com/yan1h/agent-coding-workflow/internal/infra/proc" ) // startPTY 在真 PTY 中拉起 shell(Unix)。 // // 关于进程组:pty.StartWithSize 内部会设置 SysProcAttr.Setsid,使 shell 成为新 // 会话的首进程,其进程组 pgid == pid。因此这里**不**调用 group.Prepare(它设的 // Setpgid 与 Setsid 同时设置在 Go exec 下是非法/冗余的);group.Adopt 直接记录 // pgid = cmd.Process.Pid,TerminateGroup 向 -pgid 发信号即可整树终止 shell 及其 // 派生的全部子孙进程。 func startPTY(shell string, env []string, rows, cols uint16) (*ptySession, error) { cmd := exec.Command(shell) cmd.Env = env ptmx, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: rows, Cols: cols}) if err != nil { return nil, err } group := procgrp.NewGroup() if err := group.Adopt(cmd); err != nil { _ = ptmx.Close() if cmd.Process != nil { _ = cmd.Process.Kill() } return nil, err } return &ptySession{ cmd: cmd, group: group, rw: ptmx, resizeFn: func(r, c uint16) error { return pty.Setsize(ptmx, &pty.Winsize{Rows: r, Cols: c}) }, }, nil }