ptyhost

package
v0.3.9 Latest Latest
Warning

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

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

Documentation

Overview

attachment.go —— 一次订阅的对外形态。

职责:定义调用方(agentd 的 pty_ws.go)看得见的四个字段与三个方法。

边界:它自己什么都不做——三个方法逐字转交注入进来的 ops。

为什么是「壳 + 注入」而不是接口:pty_ws.go 的 pumpPtyUplink 签名写死了 *ptyhost.Attachment。壳保住具体类型,让进程内引擎与 socket 客户端各自注入行为。

client.go —— agentd 侧的 Host:与进程内引擎逐字同签名,内部改为连 ptyhost socket。

职责:

  • Open:建会话目录、写 spec、拉起 detached 的 _ptyhost、等待 socket
  • List / Get:短连接查询 stat,拿到静态事实与活事实
  • Write / Attach:复用订阅长连接传输 PTY 字节
  • Close:显式发送 kill 帧;Adopt:登记启动扫描发现的活会话

边界:

  • 不认识 PTY:不开伪终端、不解析转义序列,真正的引擎在 ptyhost 进程里
  • 不做启动扫描:扫描由 agentd 调用 sessdir.Scan 后交给 Adopt
  • 不删还活着的会话目录;目录收摊由 ptyhost 进程负责

为什么方法签名一个都不改:pty_api.go 与 pty_ws.go 只跟这六个方法打交道,保持 这些边界不变,agentd 的 HTTP 与 WebSocket 层就不需要知道进程已经移到外面。

client_process_unix.go —— Unix 上 ptyhost detached 进程的最小系统调用适配。

职责:让 ptyhost 脱离 agentd 的会话/进程组,并在 Open 失败时按组回收。 边界:不决定何时启动或回收,不读 spec,不写日志;业务语义在 client.go。

closeall.go —— 显式停止路径的批量收口。

职责:不依赖 agentd,直接扫会话根目录并逐个 kill。

边界:

  • **只服务显式停止**(handoff service stop)。信号关停、升级换版、崩溃都 不该调它——那几条路必须让会话跨 agentd 生命周期活下来,那正是把 PTY 搬出 agentd 进程的全部意义
  • 不删还活着的会话目录:目录收摊由 ptyhost 进程自己做,这里只发 kill
  • 不改 agentd 的登记:调它的时候 agentd 可能已经不在了

为什么走目录扫描而不是让 agentd 代劳:显式停止的语义是「让这台机器上的 handoff 全停下来」,而它可能在 agentd 已经停掉之后才被执行(先 stop 服务 再想起来收口,或者 agentd 本来就崩着)。经 agentd 转一手就会在最需要它的 那种情形下失效。

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

职责:

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

边界:

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

supported_unix.go —— ptyhost 公共包的 Unix PTY 能力常量。

职责:为 Supported 提供编译期能力结论。 边界:不启动 PTY、不连接引擎;引擎自己的同名常量留在 engine 包,避免包依赖环。

types.go —— agentd 与 ptyhost 引擎共用的会话数据形态。

职责:定义会话快照、开会话参数,以及平台 PTY 能力查询。

边界:不持有会话、不启动 shell、不连接 socket;生命周期与实现分别由 engine 和 client 负责。这里的结构只描述跨实现边界所需的数据。

Index

Constants

This section is empty.

Variables

View Source
var ErrNoSession = errors.New("终端会话不存在")

ErrNoSession 表示会话 id 不存在(或已被显式关闭)。

View Source
var ErrNotSupported = errors.New("当前平台不支持 PTY 终端")

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

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

View Source
var ErrProtoMismatch = errors.New("会话由不兼容的版本托管")

ErrProtoMismatch 表示会话由当前客户端不认识的协议版本托管。

View Source
var ErrSessionExited = errors.New("终端会话已退出")

ErrSessionExited 表示 shell 已经退出,只能读历史不能再写。

View Source
var ErrTooManySubscribers = errors.New("终端会话的连接数已达上限")

ErrTooManySubscribers 表示该会话的订阅者已达上限。

Functions

func CloseAll added in v0.3.5

func CloseAll(root string, log *slog.Logger, budget time.Duration) (int, error)

CloseAll 扫描 root 下全部会话并 kill 掉还活着的那些。

参数:

  • root: 会话根目录,通常是 <DataDir>/ptys。**不存在不算错**——这台机器 可能从没开过终端
  • log: 日志入口,不能为 nil
  • budget: 总时间预算;到点就返回,不阻塞调用方

返回:成功发出 kill 并等到收摊的会话数,以及扫描失败时的错误。 单个会话关不掉只记 Warn 不算整体失败——它可能刚好自己退了。

注意:dead 与 broken 状态的目录一概不碰。dead 的由 agentd 下次启动时清; broken 的意味着「有个进程活着而我们不知道它是什么」,杀它不安全。

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 路径, 但这份清单是用户可配的,明天可能就有人往里加一个带凭据的变量。

func Supported added in v0.3.5

func Supported() bool

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

它是编译期常量而不是运行时探测:agentd 与 ptyhost 由同一个二进制在同一台机器上运行, 两者能力必然相同。

Types

type AttachOps added in v0.3.5

type AttachOps interface {
	Detach()
	ExitCode() *int
	Resize(cols, rows int) error
}

AttachOps 是一次订阅的三个行为,由构造它的一方提供。

导出它是因为 internal/ptyhost/engine 要在包外实现它;本包之外没有别的合法实现者。

type Attachment

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

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

注意:Backlog 与 Out 必须按构造方的协议语义消费;Detach 只退订,不杀会话。

func NewAttachment added in v0.3.5

func NewAttachment(backlog []byte, since uint64, truncated bool, out <-chan []byte, ops AttachOps) *Attachment

NewAttachment 组装一个订阅壳。

参数:backlog/since/truncated/out 是订阅结果;ops 提供 Detach、ExitCode、Resize 行为。 返回:可交给 pty_ws.go 的具体 Attachment。 注意:ops 不应为 nil;调用三个方法前必须由构造者注入有效行为。

func (*Attachment) Detach

func (a *Attachment) Detach()

Detach 退订,不杀会话;切 tab、切目录、关页面都走它。

func (*Attachment) ExitCode

func (a *Attachment) ExitCode() *int

ExitCode 返回 shell 的退出码;nil 表示还活着,或对端没给出退出码。

func (*Attachment) Resize

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

Resize 上报本订阅者的尺寸;实际尺寸由所有订阅者取最小值协商而来。

type Host

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

Host 是 agentd 侧的 ptyhost 客户端。零值不可用,请用 New。

func New

func New(root, selfExe string, log *slog.Logger) *Host

New 创建一个 ptyhost 客户端。

参数:root 是 <DataDir>/ptys 会话根目录;selfExe 是当前 handoff 可执行文件的绝对路径; log 是 agentd 日志入口,不能为 nil。 返回:一个尚未登记会话的 Host。 注意:New 不扫描 root,也不连接任何 socket;启动时认领由 Adopt 完成。

func (*Host) Adopt added in v0.3.5

func (h *Host) Adopt(entries []sessdir.Entry)

Adopt 登记启动扫描发现的活会话。

参数:entries 是 sessdir.Scan 的结果;只有 StateLive 会被登记,broken 与 dead 由调用方处理。 返回:无。已有同 id 登记会被新的静态元数据覆盖。 注意:Adopt 不连接 socket、不验证协议、不删除目录;它是启动路径的纯登记动作。

func (*Host) Attach

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

Attach 建立一条长期订阅连接。

参数:id 是会话 id;since 是环形缓冲的输出水位。 返回:带历史回放与实时输出通道的 Attachment。 注意:协议版本先从 meta.json 检查;Backlog 在返回前已从 attached 后的第一数据帧取出。

func (*Host) Close

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

Close 显式杀掉一个会话,并从本地登记中摘除它。

参数:id 是会话 id。 返回:kill 帧发送或等待 ptyhost 收摊失败时报错。 注意:这条路径才发送 kill;Attach 的 Detach 永远只关闭订阅 socket。

func (*Host) Get

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

Get 取一个已登记会话的最新快照。

返回:第二个返回值 false 表示客户端没有登记该 id;stat 失败时仍返回静态快照与 true。 注意:活事实不从旧缓存猜,查询失败的字段保持其零值。

func (*Host) List

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

List 返回已登记的全部会话。

返回:每条会话都保留;stat 失败时返回静态元数据与活事实零值,并记 Debug。 注意:查询并发进行,每条连接最多等待 statWait,避免 N 个会话串行拖慢恢复。

func (*Host) Open

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

Open 创建会话并拉起一个脱离 agentd 生命周期的 ptyhost 进程。

参数:opt 是 shell、cwd、环境与初始尺寸。 返回:成功时返回会话快照;失败时保证不会留下本次创建的会话目录。 注意:ptyhost 由自身进程负责收尸,agentd 不等待它退出。

func (*Host) Supported

func (h *Host) Supported() bool

Supported 报告本平台是否支持 PTY。

返回:Windows 与其它未实现平台为 false;Unix 为 true。 注意:这是编译期能力,不会为了探测能力而启动子进程。

func (*Host) Write

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

Write 把按键写进会话。

参数:id 是会话 id;p 是 PTY 原始字节。 返回:写入 socket 或 ptyhost 失败时报错。 注意:优先复用该会话已有的订阅连接;没有订阅时只建立一条短连接。

type OpenOptions

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

OpenOptions 是开会话的入参。

参数:Env 是完整环境,不会再自动追加 os.Environ;Cols/Rows <= 0 时实现使用默认尺寸。 返回:由 Open 消费,不保留调用方切片的生命周期承诺。 注意:Shell 与 BasePath 必须是目标机器上可执行且可访问的值。

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
	// Incompatible 表示该会话由当前客户端不认识的协议版本托管;进程仍活着,
	// 但本版不能 Attach,只能在界面走「重开一个终端」出口。
	Incompatible bool
	// Foreground 表示会话里当前有一个跑在前台的命令。
	Foreground bool
	BytesOut   uint64
}

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

参数:无;字段由引擎或客户端填充。 返回:作为值传递的静态事实与活事实快照。 注意:ExitCode 为 nil 表示还活着;Foreground 与 BytesOut 来自 stat,不能用旧元数据猜。

Directories

Path Synopsis
Package engine 托管伪终端(PTY)会话:开 shell、持有会话表、维护回放缓冲、 向多个订阅者广播输出、按进程组终止。
Package engine 托管伪终端(PTY)会话:开 shell、持有会话表、维护回放缓冲、 向多个订阅者广播输出、按进程组终止。
Package hostproc 是 ptyhost 进程的主体:一个进程托管一个 PTY 会话。
Package hostproc 是 ptyhost 进程的主体:一个进程托管一个 PTY 会话。
Package sessdir 是 PTY 会话在磁盘上的落点:目录布局、元数据、以及跨 agentd 重启的三态扫描。
Package sessdir 是 PTY 会话在磁盘上的落点:目录布局、元数据、以及跨 agentd 重启的三态扫描。
Package wire 是 agentd 与 ptyhost 进程之间的帧格式。
Package wire 是 agentd 与 ptyhost 进程之间的帧格式。

Jump to

Keyboard shortcuts

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