Files
DeepSeek-Reasonix/internal/platform/terminal/pty_unix.go
T
yhh 83bdeb81f0 feat(studio): the workbench gets a terminal of your own, as the Terminal board draws it
A fourth kind of tab opens a real shell (ConPTY on Windows, a pty elsewhere) in
the project folder, outside the model's permissions and sandbox and with the
host's provider keys left out of its environment. The kernel keeps each shell's
output, so a tab left or a window reloaded comes back as it was; a tab is named
for the command submitted while the shell is busy and carries a blue dot, and
closing a busy one asks first.

Also: the composer's note stack no longer clips to a few pixels when it holds
two notes, which left the second one overlapping the input.
2026-11-06 04:44:41 -08:00

73 lines
1.5 KiB
Go

//go:build !windows
package terminal
import (
"errors"
"os"
"os/exec"
"sync"
cpty "github.com/creack/pty"
"golang.org/x/sys/unix"
)
type unixPty struct {
f *os.File
cmd *exec.Cmd
once sync.Once
}
func startPty(o Options) (pty, error) {
cmd := exec.Command(o.Shell, o.Args...)
cmd.Dir = o.Dir
if len(o.Env) > 0 {
cmd.Env = o.Env
}
f, err := cpty.StartWithSize(cmd, &cpty.Winsize{Cols: o.Cols, Rows: o.Rows})
if err != nil {
return nil, fail(CodeStartFailed, "%v", err)
}
return &unixPty{f: f, cmd: cmd}, nil
}
func (p *unixPty) Read(b []byte) (int, error) { return p.f.Read(b) }
func (p *unixPty) Write(b []byte) (int, error) { return p.f.Write(b) }
func (p *unixPty) Close() error {
var err error
p.once.Do(func() { err = p.f.Close() })
return err
}
func (p *unixPty) Resize(cols, rows uint16) error {
return cpty.Setsize(p.f, &cpty.Winsize{Cols: cols, Rows: rows})
}
func (p *unixPty) Wait() (int, error) {
err := p.cmd.Wait()
var exit *exec.ExitError
if errors.As(err, &exit) {
return exit.ExitCode(), nil
}
if err != nil {
return -1, err
}
return 0, nil
}
func (p *unixPty) Kill() error {
if p.cmd.Process == nil {
return nil
}
return p.cmd.Process.Kill()
}
// Busy is whether the terminal's foreground process group is not the shell's
// own: a command it started and is waiting on holds the terminal.
func (p *unixPty) Busy() bool {
if p.cmd.Process == nil {
return false
}
group, err := unix.IoctlGetInt(int(p.f.Fd()), unix.TIOCGPGRP)
return err == nil && group != p.cmd.Process.Pid
}