You've already forked agentic-coding-workflow
74 lines
1.9 KiB
Go
74 lines
1.9 KiB
Go
package terminal
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
procgrp "github.com/yan1h/agent-coding-workflow/internal/infra/proc"
|
|
)
|
|
|
|
// 终端窗口的兜底尺寸(前端尚未上报 resize 前使用)。
|
|
const (
|
|
defaultRows uint16 = 24
|
|
defaultCols uint16 = 80
|
|
)
|
|
|
|
// ptySession 持有平台相关的 shell 进程与 IO。resizeFn 由 startPTY 注入:
|
|
// Unix 调 pty.Setsize,Windows 为 nil(降级实现无 resize)。这样 ptySession
|
|
// 类型与生命周期方法可保持平台无关,仅 startPTY 按 build tag 特化。
|
|
type ptySession struct {
|
|
cmd *exec.Cmd
|
|
group procgrp.Group
|
|
rw io.ReadWriteCloser // PTY master(Unix)或 stdio 桥接(Windows)
|
|
resizeFn func(rows, cols uint16) error
|
|
}
|
|
|
|
func (p *ptySession) resize(rows, cols uint16) error {
|
|
if p.resizeFn == nil {
|
|
return nil
|
|
}
|
|
return p.resizeFn(rows, cols)
|
|
}
|
|
|
|
// terminate 是 Open 失败路径的尽力清理(kill 进程、释放句柄、关闭 IO)。
|
|
// 正常关闭走 Supervisor.Close → group.TerminateGroup 的整树终止。
|
|
func (p *ptySession) terminate() {
|
|
if p.cmd.Process != nil {
|
|
_ = p.cmd.Process.Kill()
|
|
}
|
|
p.group.Close()
|
|
_ = p.rw.Close()
|
|
}
|
|
|
|
// buildEnv 从宿主环境按白名单过滤出传给 shell 的环境变量,并补上 TERM 以便
|
|
// 终端程序正确渲染。白名单确保 APP_MASTER_KEY、DB DSN、git 凭据等敏感变量
|
|
// 不会透传给交互式 shell。
|
|
func buildEnv(whitelist []string) []string {
|
|
allow := make(map[string]bool, len(whitelist))
|
|
for _, k := range whitelist {
|
|
allow[k] = true
|
|
}
|
|
out := make([]string, 0, len(whitelist)+1)
|
|
hasTerm := false
|
|
for _, kv := range os.Environ() {
|
|
i := strings.IndexByte(kv, '=')
|
|
if i < 0 {
|
|
continue
|
|
}
|
|
name := kv[:i]
|
|
if !allow[name] {
|
|
continue
|
|
}
|
|
if name == "TERM" {
|
|
hasTerm = true
|
|
}
|
|
out = append(out, kv)
|
|
}
|
|
if !hasTerm {
|
|
out = append(out, "TERM=xterm-256color")
|
|
}
|
|
return out
|
|
}
|