返回 DeepSeek-Reasonix
pty_unix.go
根目录 / internal / persistentshell / pty_unix.go
1 //go:build !windows
2
3 package persistentshell
4
5 import (
6 "os"
7 "os/exec"
8 "sync"
9 "syscall"
10
11 "github.com/creack/pty"
12
13 "reasonix/internal/proc"
14 )
15
16 type unixPTY struct {
17 file *os.File
18 cmd *exec.Cmd
19 closeOnce sync.Once
20 }
21
22 func startPTY(argv []string, dir string, env []string) (ptyConn, error) {
23 if len(argv) == 0 {
24 return nil, errEmptyArgv
25 }
26 cmd := proc.Command(argv[0], argv[1:]...)
27 cmd.Dir = dir
28 cmd.Env = env
29 file, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 24, Cols: 80})
30 if err != nil {
31 return nil, err
32 }
33 // Reap the shell without blocking: its exit surfaces to callers as a read
34 // error on the master side, so the wait status itself carries no decision.
35 go func() { _ = cmd.Wait() }()
36 return &unixPTY{file: file, cmd: cmd}, nil
37 }
38
39 func (c *unixPTY) Read(p []byte) (int, error) {
40 return c.file.Read(p)
41 }
42
43 func (c *unixPTY) Write(p []byte) (int, error) {
44 return c.file.Write(p)
45 }
46
47 func (c *unixPTY) Close() error {
48 var closeErr error
49 c.closeOnce.Do(func() {
50 if c.cmd != nil && c.cmd.Process != nil {
51 _ = syscall.Kill(-c.cmd.Process.Pid, syscall.SIGKILL)
52 }
53 if c.file != nil {
54 closeErr = c.file.Close()
55 }
56 })
57 return closeErr
58 }
59
59 lines GO