ptyhost

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

会话级环境变量转发:把 SSH_AUTH_SOCK 这类「由会话注入、不来自 dotfile」的 变量解析出来,注入单个终端会话的环境。

职责:

  • 按三级顺序解析每个变量:继承 → 平台查询 → 探不到
  • 逐个变量记录三态结论,让「终端里 git push 失败」变成一行可搜的日志

边界:

  • 只产出**这个会话的** cmd.Env,**绝不写回 agentd 自身环境**。这与 internal/pathenv 相反:PATH 是进程级恒定事实,socket 路径是会话级易变 事实,写回会让后续所有 fork 拿到一个可能已经失效的路径。
  • 探不到就是探不到,不编造默认值(spec §4.2)
  • 解析失败一律降级为 unavailable,不阻断会话创建

PTY 的 unix 平台原语:开伪终端、起 login shell、调尺寸、按进程组终止。

职责:

  • 把 openpty 与进程组信号这两件平台相关的事收敛在本文件
  • 向上只暴露与 platform_other.go 完全同签名的五个函数

边界:

  • 不认识会话、缓冲、订阅者——那是 ptyhost.go 的事
  • 不做参数校验(shell 是否存在等),失败原样上抛

日志:本文件不打日志。所有错误原样上抛,由 ptyhost.go 带着会话 id 统一记录, 避免同一个失败在两层各留一条无法关联的记录。

Package ptyhost 托管伪终端(PTY)会话:开 shell、持有会话表、维护回放缓冲、 向多个订阅者广播输出、按进程组终止。

职责:

  • 会话的完整生命周期:创建、写入、订阅、调尺寸、显式关闭、自然退出
  • 每会话一个环形缓冲,支撑断线重连的 since 续传
  • 多方接入时的尺寸协商(取所有订阅者中的最小值)

边界:

  • 不认识 HTTP / WebSocket / JSON,也不认识 agentd 的任务模型
  • 不做鉴权,不做 base_path 白名单校验(那是 agentd 接口层的参数校验)
  • 不落盘:会话表只在内存里,随 agentd 生死(spec §3.1、§10)
  • 不解析终端转义序列,只搬字节

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoSession 表示会话 id 不存在(或已被显式关闭)。
	ErrNoSession = errors.New("终端会话不存在")
	// ErrTooManySubscribers 表示该会话的订阅者已达上限。
	ErrTooManySubscribers = errors.New("终端会话的连接数已达上限")
	// ErrSessionExited 表示 shell 已经退出,只能读历史不能再写。
	ErrSessionExited = errors.New("终端会话已退出")
)
View Source
var ErrNotSupported = errors.New("当前平台不支持 PTY 终端")

ErrNotSupported 表示当前平台没有 PTY 实现(Windows:ConPTY 是另一套 API, 本轮如实降级而不假装支持,见 spec §10)。

这个变量刻意放在**无构建标签**的文件里:两套 platform_*.go 都要引用它, 放进任一带标签的文件都会让另一套编译不过。

Functions

func DefaultEnvForward

func DefaultEnvForward() []string

DefaultEnvForward 返回内置默认清单的副本。

返回副本而不是切片本身:调用方(config 解析、测试)拿到后可能就地排序或改写, 那会污染进程内所有后续会话。

func ResolveEnvForward

func ResolveEnvForward(names []string, base []string, log *slog.Logger) []string

ResolveEnvForward 把 names 里每个变量按三级顺序解析后追加到 base,返回新环境。

参数:

  • names: 要转发的变量名清单(调用方已按 nil→默认清单 归一化)
  • base: 会话的基础环境(PATH / TERM 等),原样保留
  • log: 逐个变量记录三态结论,不得为 nil

返回:base + 解析成功的 `NAME=VALUE`。探不到的变量**不出现**在结果里。

注意:日志只记变量名与结论来源,**不记变量值**——今天转发的是 socket 路径, 但这份清单是用户可配的,明天可能就有人往里加一个带凭据的变量。

Types

type Attachment

type Attachment struct {
	Backlog   []byte
	Since     uint64
	Truncated bool
	Out       <-chan []byte
	// contains filtered or unexported fields
}

Attachment 是一次订阅。Backlog 是建连瞬间的历史回放,Out 是后续实时输出; Out 被关闭意味着会话结束(不是网络抖动),客户端应停止重连。

func (*Attachment) Detach

func (a *Attachment) Detach()

Detach 退订。**只断连接,不动进程**——这是 spec §3.2 的核心分工: 关页面、切设备、组件卸载一律走这里,杀会话只有 Close 一条路。

func (*Attachment) ExitCode

func (a *Attachment) ExitCode() *int

ExitCode 返回会话的退出码,nil = 还活着。

func (*Attachment) Resize

func (a *Attachment) Resize(cols, rows int) error

Resize 上报本订阅者的尺寸,并按「所有订阅者取最小」重新协商实际尺寸。

type Host

type Host struct {
	// contains filtered or unexported fields
}

Host 是本机所有 PTY 会话的持有者。零值不可用,请用 New。

func New

func New(log *slog.Logger) *Host

New 创建一个 Host。log 用于记录会话生命周期与错误,不得为 nil。

func (*Host) Attach

func (h *Host) Attach(id string, since uint64) (*Attachment, error)

Attach 订阅一个会话,并原子地取回 since 之后的历史。

「原子」是关键:回放与订阅必须在同一把锁里完成,否则两者之间产生的输出 会两头都不落,用户看到的历史就缺了一段。

func (*Host) Close

func (h *Host) Close(id string) error

Close 显式关闭会话:整组 SIGTERM,宽限 termGrace 后 SIGKILL,并立即 把会话从列表里摘掉。

注意:摘除是同步的、杀进程的兜底是异步的——DELETE 请求不该为了等一个 赖着不走的进程而挂 2 秒。用户点了 ×,列表里就该立刻没有它。

func (*Host) Get

func (h *Host) Get(id string) (Session, bool)

Get 取单个会话快照。第二个返回值 false = 不存在。

func (*Host) List

func (h *Host) List() []Session

List 返回全部会话快照(含已退出但未被显式关闭的)。

func (*Host) Open

func (h *Host) Open(opt OpenOptions) (Session, error)

Open 起一个新会话。失败时不留残骸。

func (*Host) Supported

func (h *Host) Supported() bool

Supported 报告本平台是否支持 PTY,供 /api/status 的 pty_supported 上报。

func (*Host) Write

func (h *Host) Write(id string, p []byte) error

Write 把用户按键送进 PTY。会话已退出时返回 ErrSessionExited。

type OpenOptions

type OpenOptions struct {
	BasePath string
	BaseKind string
	Shell    string
	Env      []string
	Cols     int
	Rows     int
}

OpenOptions 是开会话的入参。Env 是**完整环境**,不会再追加 os.Environ()—— 这正是 spec §4.2 要绕开的那个坑(封存分支 service_unix.go:43 的写法)。

type Session

type Session struct {
	ID        string
	BasePath  string
	BaseKind  string
	Shell     string
	CreatedAt time.Time
	Cols      int
	Rows      int
	Attached  int
	PID       int
	ExitCode  *int
	// Foreground 表示会话里当前有一个跑在前台的命令(前台进程组 ≠ shell 自己)。
	//
	// 为什么是 bool 而不是三态:读不到(shell 已退出、平台不支持)时结论是
	// **false**——两种情形下「关掉它会打断什么」的答案都是「不会」,与真的空闲
	// 同解。这与 PtySupported 那种「不知道」不是一回事,不要照抄那条纪律。
	Foreground bool
	BytesOut   uint64
}

Session 是一个会话的**快照**,跨出锁之后可以自由持有。

ExitCode 用指针表达三态里的两态:nil = 还活着,非 nil = 已退出且这是退出码。 这条与项目里 Watchers / Live / Procs 同一纪律——绝不用 0 或 -1 冒充「不知道」。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL