ptyrelay

module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT

README

ptyrelay

Relay your shell over any pty.

让 Claude Code 等本地 agent 通过既有 pty 通道(tmux+SSH、code-local WebSocket 等)操控受限网络环境下的远端服务器——把"只是一个字节流"的 shell session 变成一个结构化的远端能力面:文件读写、命令执行。

Status

v0.2.0 — Agent + Router. 既有 ShellBackend(什么都不预装就能跑),也有 AgentBackend(远端跑一个 Go 二进制走 JSON RPC,binary-safe、stderr 独立、错误分类)。RouterBackend 按 op 幂等性自动选路: agent 健康时走 RPC,agent 缺失或 ReadOnly/Idempotent 失败时透明回落 到 shell;NonIdempotent 失败上抛由调用方决策。Bootstrap 会自动把 agent 二进制 atomic-write 到远端(sha256 校验 + 平台探测)。

v0.3.0+ — 三种 transport(tmux / WebSocket / subprocess)、命令行 入口 (cmd/ptyrelay) 和 MCP server (cmd/ptyrelay-mcp) 都已就绪。 pkg/channel/websocket 是通用 gorilla/websocket Channel, BinarySafe=true,hook 化 envelope 适配 ttyd / wetty 等; pkg/channel/subprocess 把任意 stdin/stdout 命令包成 Channel, 等价于一行 docker exec / kubectl exec / ssh -T 直通。Agent REPL 比一次性模式快 ~283×(见 pkg/backend/agent/bench_test.go)。 Bootstrap 既能本地上传二进制(--provider-dir),也能让远端 curl 自取(--from-url,支持 {os}/{arch} 模板和 sha256 校验)。

# 通过本机 tmux pane 操作远端
ptyrelay exec --tmux work:0.0 -- uname -a

# 通过 code-server / ttyd 的 WS 桥
ptyrelay get  --ws ws://host:8765/term /etc/hostname

# 进容器调试
ptyrelay exec --exec "docker exec -i my-container bash" -- ps aux
ptyrelay get  --exec "kubectl exec -i -n prod api-0 -- bash" /var/log/app.log

详见 TODOs.mddocs/TRANSPORTS.mddocs/ARCHITECTURE.mddocs/PROTOCOL.mddocs/SECURITY.md

Quickstart

go get github.com/FanBB2333/ptyrelay@latest

通过现有 tmux pane 操作远端:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/FanBB2333/ptyrelay/pkg/backend/shell"
	"github.com/FanBB2333/ptyrelay/pkg/channel/tmux"
	"github.com/FanBB2333/ptyrelay/pkg/session"
)

func main() {
	ctx := context.Background()

	// 1. 在某个 tmux pane 里准备好你想"借用"的 shell 通道。
	//    常见做法:tmux 内 ssh 到跳板机,再 ssh 到目标。
	ch, err := tmux.New(ctx, tmux.Options{Pane: "my-session:0.0"})
	if err != nil {
		log.Fatal(err)
	}
	defer ch.Close()

	// 2. Session 在 Channel 之上加 sentinel framing:每条命令都被
	//    BEG/END 包起来,这样 ptyrelay 能从混杂的 shell 输出里准确
	//    捞出本次命令的 stdout 和 exit code。
	sess := session.New(ch, session.ShellBash)
	defer sess.Close()

	// 3. ShellBackend 把 Session 包装成 RemoteFS + RemoteExec。
	be := shell.New(sess)

	// 文件读写
	if err := be.Write(ctx, "/tmp/hello.txt", []byte("hi from ptyrelay\n"), 0o644); err != nil {
		log.Fatal(err)
	}
	data, err := be.Read(ctx, "/tmp/hello.txt")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("read: %q\n", data)

	// 命令执行
	res, err := be.Run(ctx, "uname -a", nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("uname: %s (exit=%d)\n", res.Stdout, res.ExitCode)
}

需要先有 tmux pane?用内置的 InitSession helper 起一个:

opts, _ := tmux.InitSession(ctx, tmux.InitOptions{
	SessionName: "ptyrelay-demo",
	Command:     "ssh user@jumphost",
})
defer tmux.KillSession(ctx, tmux.InitOptions{SessionName: "ptyrelay-demo"})

ch, _ := tmux.New(ctx, opts)
// ...同上

Build / Test

go build ./...

# 日常开发:跳过 multi-MB PTY 上传集成测试,~50s 跑完
go test -short -race ./...

# 完整集成验证(含 Bootstrap / e2e_FullStack / SessionOverWebSocket)
# 三个包并行跑 PTY 上传会互相争抢,所以用 -p 1 串行
go test -p 1 ./...

测试矩阵需要 bash + tmux,缺哪个对应的集成测试会自动 skip。

License

MIT — see LICENSE.

Directories

Path Synopsis
cmd
ptyrelay command
Command ptyrelay is the ptyrelay CLI.
Command ptyrelay is the ptyrelay CLI.
ptyrelay-agent command
Command ptyrelay-agent is the remote-side binary that ptyrelay's AgentBackend talks to.
Command ptyrelay-agent is the remote-side binary that ptyrelay's AgentBackend talks to.
ptyrelay-mcp command
Command ptyrelay-mcp is a Model Context Protocol (MCP) server that exposes ptyrelay's RemoteFS / RemoteExec surface as MCP tools.
Command ptyrelay-mcp is a Model Context Protocol (MCP) server that exposes ptyrelay's RemoteFS / RemoteExec surface as MCP tools.
internal
shellquote
Package shellquote escapes strings for safe inclusion in POSIX shell command lines.
Package shellquote escapes strings for safe inclusion in POSIX shell command lines.
testpty
Package testpty provides a channel.Channel backed by a real PTY + shell subprocess, for integration tests that need PTY semantics (cooked-mode echo, line discipline, real flushing) the way production transports deliver them.
Package testpty provides a channel.Channel backed by a real PTY + shell subprocess, for integration tests that need PTY semantics (cooked-mode echo, line discipline, real flushing) the way production transports deliver them.
pkg
backend
Package backend defines the high-level remote capability surface — RemoteFS + RemoteExec — that callers (Claude Code, etc.) consume.
Package backend defines the high-level remote capability surface — RemoteFS + RemoteExec — that callers (Claude Code, etc.) consume.
backend/agent
Package agent implements backend.Backend by talking to a remote ptyrelay-agent over a session.FramedSession.
Package agent implements backend.Backend by talking to a remote ptyrelay-agent over a session.FramedSession.
backend/router
Package router implements backend.Backend by composing an agent.Backend (preferred for performance + binary safety + separated stderr) with a shell.Backend (always-available fallback).
Package router implements backend.Backend by composing an agent.Backend (preferred for performance + binary safety + separated stderr) with a shell.Backend (always-available fallback).
backend/shell
Package shell implements backend.Backend by composing shell commands over a session.Session.
Package shell implements backend.Backend by composing shell commands over a session.Session.
bootstrap
Package bootstrap installs the ptyrelay-agent binary on the remote.
Package bootstrap installs the ptyrelay-agent binary on the remote.
channel
Package channel defines the byte-stream transport abstraction that the Session layer rides on top of.
Package channel defines the byte-stream transport abstraction that the Session layer rides on top of.
channel/subprocess
Package subprocess provides a generic channel.Channel that runs a local command and treats its stdio as the byte stream.
Package subprocess provides a generic channel.Channel that runs a local command and treats its stdio as the byte stream.
channel/tmux
Package tmux provides a channel.Channel backed by a tmux pane.
Package tmux provides a channel.Channel backed by a tmux pane.
channel/websocket
Package websocket provides a generic channel.Channel backed by a single WebSocket connection (RFC 6455).
Package websocket provides a generic channel.Channel backed by a single WebSocket connection (RFC 6455).
proto
Package proto defines the wire types and the framing codec for talking to ptyrelay-agent.
Package proto defines the wire types and the framing codec for talking to ptyrelay-agent.
session
Package session defines the framing contract that turns a raw Channel into a request/response RPC channel.
Package session defines the framing contract that turns a raw Channel into a request/response RPC channel.

Jump to

Keyboard shortcuts

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