terminal

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 21, 2026 License: MIT Imports: 47 Imported by: 0

README

deepwork-terminal

A standalone web terminal with authentication, Cloudflare tunnel support, and an embedded Vue frontend. Drop it into any Go application via a single HTTP handler.

Features

  • Full PTY terminal over WebSocket
  • Session management with reconnect support
  • Clipboard paste support
  • Settings portal (workbench API)
  • Optional Cloudflare Tunnel (auto-downloads cloudflared)
  • Embedded Vue SPA — zero static file serving required
  • Hook points for auth, session lifecycle, and shell customization

Screenshots

会话接管与抢占 — 手机 + PC 随时切换

多端共享同一终端会话,手机和 PC 可随时接管或抢占控制权,无缝远程切换操作。

会话接管与抢占


Textarea 文本输入 — 历史不丢失

内置多行文本输入框,发送历史持久保留,复杂命令编辑更方便,告别误触清空的烦恼。

Textarea 输入


快捷键盘输入

针对移动端优化的快捷键盘面板,常用控制键一触即达(Ctrl、Esc、Tab、方向键等)。

快捷键盘


Snippets 片段管理 — 快捷输入

保存常用命令片段,点击即插入,减少重复输入,提升效率。

Snippets 管理


tmux 专项面板 — 快捷切换 Pane

内置 tmux 集成面板,直观展示所有 pane,一键切换,无需记忆 tmux 快捷键。

tmux 面板


截图 / 文件上传为图片 — PC 与移动端均支持

从 PC 浏览器或移动端上传截图和文件,自动转为图片链接,供 AI 工具(Codex / Claude)直接访问,快速排查问题。

文件上传

移动端上传

Quick Start (as a library)

go get github.com/brightman-ai/deepwork-terminal
import terminal "github.com/brightman-ai/deepwork-terminal"

srv := terminal.New(terminal.DefaultConfig())
http.Handle("/terminal/", srv.Handler())
http.ListenAndServe(":8080", nil)

Or run the CLI directly:

go run ./cmd/dw-terminal

See guide/ for full documentation.

Build from source

git clone https://github.com/brightman-ai/deepwork-terminal
cd deepwork-terminal
./build.sh

build.sh handles everything in one step:

  1. Clones (or pulls to latest) the CE App Shell (brightman-ai/deepwork) into a sibling directory
  2. Runs npm install to pick up any new frontend dependencies
  3. Builds the Vue frontend and embeds it into internal/spa/dist/
  4. Downloads any new Go module dependencies (go mod download)
  5. Compiles the Go binary → ./dw-terminal

To update to the latest version after an initial clone:

git pull
./build.sh

Requires: Go 1.21+, Node.js 18+, npm.

Headless servers: npm install browser-download hooks (Playwright/Puppeteer) are suppressed automatically via PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1.

Go binary only (no frontend changes)

The frontend is pre-built and committed to internal/spa/dist/. If you only need to recompile the Go binary:

./build.sh --skip-frontend

Or manually:

go build -o dw-terminal ./cmd/dw-terminal/

License

MIT

Documentation

Overview

Package terminal — observability declarations (BS-08 Terminal).

Package terminal — protocol.go: T5 §5 WebSocket 终端消息协议定义 (r2) BS-08 v2 消息类型:input/output/resize/ping/pong/closed

Package terminal — pty_manager.go: PTY spawn/stop/watch goroutines [T5 §4]

Package terminal — session.go: SessionV2 内存对象 [T5 §2.1] BS-08 v2: Int64 SessionID, WorkspaceID, PTY, Ring, WS 连接

Package terminal — shell_cmd.go: 跨平台 shell 命令构造辅助

Package terminal implements the BS-08 Terminal subsystem. It provides PTY management, WebSocket-based terminal I/O, and session lifecycle for browser-based terminal access. All state is held in memory (IR-03).

Index

Constants

View Source
const (
	// MsgInput 键盘输入 / 控制序列 (客户端→服务端)
	MsgInput = "input"
	// MsgResize 窗口大小变化 (客户端→服务端)
	MsgResize = "resize"
	// MsgPing 心跳 (客户端→服务端)
	MsgPing = "ping"
)
View Source
const (
	// MsgOutput PTY 输出 ANSI 透传 (服务端→客户端)
	MsgOutput = "output"
	// MsgClosed 终端关闭通知 (服务端→客户端)
	MsgClosed = "closed"
	// MsgPong 心跳响应 (服务端→客户端)
	MsgPong = "pong"
)
View Source
const (
	ClosedReasonNormal     = "normal"
	ClosedReasonCrashed    = "crashed"
	ClosedReasonStopCalled = "stop_called"
)
View Source
const (
	// AdapterStateSpawning PTY 正在启动
	AdapterStateSpawning = "spawning"
	// AdapterStateRunning PTY 运行中
	AdapterStateRunning = "running"
	// AdapterStateStopping PTY 正在停止
	AdapterStateStopping = "stopping"
	// AdapterStateStopped PTY 已停止(正常)
	AdapterStateStopped = "stopped"
	// AdapterStateFailed PTY 异常退出
	AdapterStateFailed = "failed"
)
View Source
const (
	MsgTypeResize       = "resize"
	MsgTypeHeartbeat    = "heartbeat"
	MsgTypeHeartbeatAck = "heartbeat_ack"
	MsgTypePing         = "ping"
	MsgTypePong         = "pong"
	MsgTypeAuthRefresh  = "auth_refresh"
	MsgTypeShellExit    = "shell_exit"
	MsgTypeError        = "error"
	MsgTypePreempted    = "preempted"
	MsgTypeInput        = "input"        // client → server: terminal input as text frame (WKWebView binary frame fix)
	MsgTypeTmuxNav      = "tmux_nav"     // client → server: navigate tmux windows/sessions
	MsgTypeSessionMeta  = "session_meta" // server → client: pushed once after WS handshake
	MsgTypeAgentState   = "agent_state"  // server → client: agent state push (replaces SSE)
	MsgTypeTmuxState    = "tmux_state"   // server → client: tmux topology/prefix/agent-status push (terminal-owned)
)

Control message type constants.

View Source
const DefaultBufferCapacity = 1024 * 1024

DefaultBufferCapacity is the default size for the RingBuffer (1 MB). [Ref: T5-B3, CAP-terminal-io S4, DDC-03]

Variables

This section is empty.

Functions

func DefaultPTYFactory

func DefaultPTYFactory(opts PTYStartOptions) (*os.File, *exec.Cmd, error)

DefaultPTYFactory creates a real PTY. Tries independent process group (Setpgid) for DDC-01 SIGHUP isolation; falls back gracefully in restricted environments (containers, seccomp) where Setpgid is denied.

The shell string may carry args (config.go documents e.g. "/bin/bash --login", and "tmux attach -t x" is a common case), so we tokenize it shell-words style before exec rather than passing the whole string as a single program path.

func DialTestWS

func DialTestWS(t *testing.T, server *httptest.Server, sessionID string, token string) *websocket.Conn

DialTestWS opens a WebSocket connection to the test server for the given session.

func NewTestCLIServer

func NewTestCLIServer(t *testing.T) (*httptest.Server, *SessionManager, *SafeWriteEnds)

NewTestCLIServer creates a test HTTP server with terminal routes. Returns the server, SessionManager, and write-end pipes for data injection.

func NewTestCLIServerWithAuth

func NewTestCLIServerWithAuth(t *testing.T, authCode string) (*httptest.Server, *SessionManager, *SafeWriteEnds)

NewTestCLIServerWithAuth creates a test server with a specific auth code.

func WaitForBinaryContaining

func WaitForBinaryContaining(t *testing.T, ws *websocket.Conn, substr string, timeout time.Duration) []byte

WaitForBinaryContaining reads binary WS messages until one contains substr.

func WaitForPTYReady

func WaitForPTYReady(t *testing.T, sm *SessionManager, sessionID string, timeout time.Duration)

WaitForPTYReady waits for the session to appear in the SessionManager.

Types

type AgentDetectFunc

type AgentDetectFunc func(ctx context.Context, shellPID int, cwd string) (tool, status string)

AgentDetectFunc detects an AI agent running under the given shell PID. Returns (tool, status) strings; empty tool means no agent detected.

type AgentStatePushFunc

type AgentStatePushFunc func(ctx context.Context, sessionID string) (<-chan json.RawMessage, func(), error)

AgentStatePushFunc subscribes to agent state changes for a session. Returns a channel of JSON-encoded AgentIntelResponse and a cleanup function. Injected by the webui layer to avoid terminal → agent_intel import cycle.

type Config

type Config struct {
	Addr         string // listen address, e.g. ":8022" (always 0.0.0.0)
	DefaultShell string // e.g. "/bin/bash --login"
	BufferSize   int    // ring buffer size in bytes, default 1MB
	MaxSessions  int    // max concurrent sessions, default 100
	AuthCode     string // auto-generated auth code, printed to console on start
	DataDir      string // data directory for persistence

	// VapidSubscriber is the VAPID JWT "sub" claim — a contact identifying the app
	// server to the push service. Apple APNs (iOS Web Push) REJECTS a token whose sub
	// is not a valid mailto: (real-format domain) or https: URL → 403 BadJwtToken.
	// Pass a BARE email (e.g. "you@your.dev") or an https: URL — webpush-go prepends
	// "mailto:" automatically (a "mailto:" prefix here would double to "mailto:mailto:…").
	// Empty → resolved from DW_VAPID_SUBSCRIBER, then defaultVapidSubscriber.
	VapidSubscriber string
}

Config for the terminal session server.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults.

type CreateOptions

type CreateOptions struct {
	Name   string
	Title  string
	Engine string
	Shell  string
	CWD    string
}

CreateOptions describes the product-level terminal session metadata and runtime options supplied by the WebUI.

type ErrorPayload

type ErrorPayload struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

ErrorPayload is the payload for an "error" control message.

type Hooks

type Hooks struct {
	OnSessionStart func(ctx context.Context, id string, meta SessionMeta) error
	OnSessionEnd   func(ctx context.Context, id string, exitCode int) error
	OnOutput       func(ctx context.Context, id string, data []byte) error
	OnCommand      func(ctx context.Context, id string, cmd string) (handled bool, err error)
	Store          SessionStore

	// AgentDetect optionally enriches session list with agent tool/status.
	// Injected by the host to avoid import cycles.
	AgentDetect AgentDetectFunc

	// AgentStatePush optionally subscribes to agent state changes for WS push.
	// Injected by the host to avoid import cycles.
	AgentStatePush AgentStatePushFunc
}

Hooks defines optional integration points for a host application. All fields are optional — nil means standalone behavior.

type HudLogRequest

type HudLogRequest struct {
	SessionID string          `json:"sessionId"`
	Timestamp string          `json:"timestamp"`
	UserAgent string          `json:"userAgent"`
	Screen    json.RawMessage `json:"screen"`
	Events    json.RawMessage `json:"events"`
	Snapshot  json.RawMessage `json:"snapshot"`
}

HudLogRequest is the request body for POST /api/cli/debug/logs. [Ref: CAP-hud-diagnostics S4]

type InProcessService

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

InProcessService wraps SessionManager and implements TerminalSessionService. All calls are in-process; no network hop occurs. SessionManager is already thread-safe via sync.Map, so InProcessService inherits that guarantee.

func NewInProcessService

func NewInProcessService(manager *SessionManager) *InProcessService

NewInProcessService creates an InProcessService backed by the given manager.

func (*InProcessService) Close

func (s *InProcessService) Close() error

Close implements TerminalSessionService.

func (*InProcessService) Create

Create implements TerminalSessionService.

func (*InProcessService) Delete

func (s *InProcessService) Delete(_ context.Context, id string) error

Delete implements TerminalSessionService.

func (*InProcessService) Get

Get implements TerminalSessionService.

func (*InProcessService) Input

func (s *InProcessService) Input(_ context.Context, id string, data []byte) error

Input implements TerminalSessionService. Writes raw bytes directly to the session's PTY file descriptor.

func (*InProcessService) List

List implements TerminalSessionService.

func (*InProcessService) PasteUpload

func (s *InProcessService) PasteUpload(_ context.Context, id string, filename string, content io.Reader, sessionCWD string) (string, error)

PasteUpload implements TerminalSessionService. Saves content to a temp directory under the session's CWD (or sessionCWD if non-empty) and returns the absolute path of the saved file. The logic mirrors clipboard_paste.go but operates on io.Reader rather than gin.Context so that it can be called without an HTTP layer.

func (*InProcessService) Resize

func (s *InProcessService) Resize(_ context.Context, id string, cols, rows int) error

Resize implements TerminalSessionService.

func (*InProcessService) TailOutput

func (s *InProcessService) TailOutput(_ context.Context, id string, lines int) ([]string, error)

TailOutput implements TerminalSessionService.

type InputPayload

type InputPayload struct {
	Data []byte `json:"data"` // raw terminal bytes (JSON base64-encoded)
}

InputPayload carries terminal input bytes as a JSON text frame. [TH-0501-m9j] WKWebView drops rapid binary WS frames; text frames are reliable.

type Option

type Option func(*Server)

Option configures a Server.

func WithAddr

func WithAddr(addr string) Option

WithAddr sets the listen address.

func WithAuthCode

func WithAuthCode(code string) Option

WithAuthCode sets the auth code. When left empty, NewServer generates one.

func WithConfig

func WithConfig(c Config) Option

WithConfig replaces the entire config.

func WithHooks

func WithHooks(h Hooks) Option

WithHooks sets integration hooks.

func WithTmuxProvider added in v0.2.0

func WithTmuxProvider(p TmuxStateProvider) Option

WithTmuxProvider overrides the default in-process tmux provider. Hosts use this to supply a richer snapshot; standalone needs nothing.

type PTYFactory

type PTYFactory func(opts PTYStartOptions) (master *os.File, cmd *exec.Cmd, err error)

PTYFactory creates a PTY-backed process. Returns the master side file descriptor, the command (may be nil for mock implementations), and an error. This abstraction allows testing without fork/exec.

type PTYStartOptions

type PTYStartOptions struct {
	Shell string
	CWD   string
}

PTYStartOptions describes how a PTY-backed process should be started.

type PreemptedPayload

type PreemptedPayload struct {
	Message string `json:"message"`
}

PreemptedPayload is the payload for a "preempted" control message.

type ResizePayload

type ResizePayload struct {
	Cols int `json:"cols"`
	Rows int `json:"rows"`
}

ResizePayload is the payload for a "resize" control message. [Ref: T5-B3]

type RingBuffer

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

RingBuffer is a fixed-capacity circular byte buffer used to store recent terminal output for replay on WebSocket reconnection. It is safe for concurrent use. [Ref: T5-B3, CAP-terminal-io S4, DDC-03] Testability: Len() int + IsFull() bool [Ref: T6-A4.1]

func NewRingBuffer

func NewRingBuffer(capacity int) *RingBuffer

NewRingBuffer creates a RingBuffer with the given capacity. If capacity <= 0, DefaultBufferCapacity is used.

func (*RingBuffer) IsFull

func (rb *RingBuffer) IsFull() bool

IsFull returns true if the buffer has been completely filled at least once. [Ref: T6-A4.1 testability requirement]

func (*RingBuffer) Len

func (rb *RingBuffer) Len() int

Len returns the number of valid bytes currently stored. [Ref: T6-A4.1 testability requirement]

func (*RingBuffer) Read

func (rb *RingBuffer) Read() []byte

Read returns all buffered data in chronological order. If the buffer has never wrapped, returns data from the start to writePos. If wrapped, returns data from writePos to end + start to writePos.

func (*RingBuffer) ReadTail

func (rb *RingBuffer) ReadTail(n int) []byte

ReadTail returns the last n bytes from the buffer without copying the entire buffer. This minimizes mutex hold time — critical for avoiding PTY input backpressure.

func (*RingBuffer) Reset

func (rb *RingBuffer) Reset()

Reset clears the buffer.

func (*RingBuffer) Write

func (rb *RingBuffer) Write(p []byte) (int, error)

Write appends data to the ring buffer, overwriting oldest data when full (FIFO). Returns the number of bytes written (always len(p)).

type SafeWriteEnds

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

SafeWriteEnds provides thread-safe access to pipe write ends.

func (*SafeWriteEnds) CloseAt

func (s *SafeWriteEnds) CloseAt(index int)

CloseAt closes the write end at the given index.

func (*SafeWriteEnds) Get

func (s *SafeWriteEnds) Get(index int) *os.File

Get returns the write end at the given index (thread-safe).

type Server

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

Server is a complete terminal session HTTP service. Standalone: ListenAndServe() runs API + SPA. Embedded: Handler() returns API routes for a host to mount.

func NewServer

func NewServer(opts ...Option) (*Server, error)

NewServer creates a terminal session server.

func (*Server) Close

func (s *Server) Close() error

Close shuts down all sessions and stops the background push notifier.

func (*Server) Handler

func (s *Server) Handler() http.Handler

Handler returns the API routes (no SPA) for embedding into a host server. Routes are relative: GET /sessions, POST /sessions, GET /sessions/{id}/ws, etc. The host uses http.StripPrefix to mount at any path prefix.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(ctx context.Context) error

ListenAndServe starts the standalone server (API + SPA). The SPA is the embedded Vue frontend (built by build.sh).

func (*Server) Port

func (s *Server) Port() int

Port returns the actual listening port (useful when Addr is ":0").

func (*Server) Service added in v0.2.0

func (s *Server) Service() TerminalSessionService

Service returns the in-process terminal session service owned by this server. Hosts should depend on this interface for product integrations such as agent state enrichment instead of reaching into SessionManager internals.

type Session

type Session struct {
	ID         string        `json:"id"`
	Name       string        `json:"name"`
	Title      string        `json:"title"`
	Engine     string        `json:"engine"`
	CWD        string        `json:"cwd"`
	ShellPath  string        `json:"-"`
	PTY        *os.File      `json:"-"`
	Cmd        *exec.Cmd     `json:"-"`
	Buffer     *RingBuffer   `json:"-"`
	Status     SessionStatus `json:"status"`
	CreatedAt  time.Time     `json:"createdAt"`
	LastActive time.Time     `json:"lastActive"`

	// TmuxDetected indicates whether the shell is running inside tmux.
	// Set after session creation by checking /proc/{pid}/environ.
	// [Ref: BUG-6, DDC-13]
	TmuxDetected bool `json:"tmuxDetected"`
	// contains filtered or unexported fields
}

Session represents a single terminal session backed by a PTY. [Ref: T5-B3]

func (*Session) Done

func (s *Session) Done() <-chan struct{}

Done returns a channel that is closed when the PTY read loop exits.

func (*Session) GetExitCode

func (s *Session) GetExitCode() int

GetExitCode returns the shell exit code (thread-safe).

func (*Session) GetLastActive

func (s *Session) GetLastActive() time.Time

GetLastActive returns when the session last received PTY output (thread-safe).

func (*Session) GetStatus

func (s *Session) GetStatus() SessionStatus

GetStatus returns the session status (thread-safe).

func (*Session) GetTmuxDetected

func (s *Session) GetTmuxDetected() bool

GetTmuxDetected returns whether tmux was detected (thread-safe).

func (*Session) ShellPID

func (s *Session) ShellPID() int

ShellPID returns the PID of the shell process running in the PTY.

func (*Session) TailOutput

func (s *Session) TailOutput(n int) []string

TailOutput returns the last n lines of terminal output from the RingBuffer. Used by agent intel for output analysis in direct (non-tmux) mode.

func (*Session) WorkingDir

func (s *Session) WorkingDir() string

WorkingDir returns the working directory of the session.

type SessionInfo

type SessionInfo struct {
	ID           string        `json:"id"`
	Name         string        `json:"name"`
	Title        string        `json:"title,omitempty"`
	Engine       string        `json:"engine"`
	CWD          string        `json:"cwd"`
	Status       SessionStatus `json:"status"`
	CreatedAt    string        `json:"created_at"`
	LastActive   string        `json:"last_active"`
	ShellPID     int           `json:"shell_pid,omitempty"`
	TmuxDetected bool          `json:"tmux_detected,omitempty"`
	ExitCode     int           `json:"exit_code,omitempty"`
}

SessionInfo is the service-layer representation of a terminal session. It avoids exposing the internal Session struct (with PTY, Cmd, etc.) to callers outside the package.

type SessionManager

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

SessionManager manages terminal sessions with PTY processes. All state is held in memory (IR-03: no DB, no persistence). [Ref: T5-B3, CAP-session-lifecycle S2, DDC-11]

func NewSessionManager

func NewSessionManager(bufferSize int, defaultShell string) *SessionManager

NewSessionManager creates a new SessionManager.

func NewSessionManagerWithFactory

func NewSessionManagerWithFactory(bufferSize int, defaultShell string, factory PTYFactory) *SessionManager

NewSessionManagerWithFactory creates a SessionManager with a custom PTY factory (for testing).

func (*SessionManager) ClearActiveConn

func (m *SessionManager) ClearActiveConn(sessionID string, conn *websocket.Conn)

ClearActiveConn removes the active connection entry for a session if it matches the given conn.

func (*SessionManager) CloseAll

func (m *SessionManager) CloseAll() error

CloseAll terminates all sessions. Called during server shutdown.

func (*SessionManager) Create

func (m *SessionManager) Create(name string) (*Session, error)

Create creates a new terminal session with a PTY process. [Ref: T5-B3, T5-B4.M1, CAP-session-lifecycle S2]

func (*SessionManager) CreateWithOptions

func (m *SessionManager) CreateWithOptions(opts CreateOptions) (*Session, error)

CreateWithOptions creates a new terminal session with product metadata.

func (*SessionManager) Destroy

func (m *SessionManager) Destroy(id string) error

Destroy terminates a session's PTY process and removes it from the manager. [Ref: CAP-session-lifecycle S2]

func (*SessionManager) DestroyAll

func (m *SessionManager) DestroyAll()

DestroyAll terminates all sessions. Kept for compatibility.

func (*SessionManager) Get

func (m *SessionManager) Get(id string) (*Session, error)

Get returns a session by ID or an error if not found.

func (*SessionManager) List

func (m *SessionManager) List() []*Session

List returns all sessions.

func (*SessionManager) SetActiveConn

func (m *SessionManager) SetActiveConn(sessionID string, conn *websocket.Conn, cancel context.CancelFunc)

SetActiveConn registers a new active WS connection for a session, preempting any existing one. BUG-3: Only one WS connection per session is allowed at a time.

func (*SessionManager) Subscribe

func (m *SessionManager) Subscribe(sess *Session, subID string) (<-chan []byte, func())

Subscribe adds a subscriber channel for receiving PTY output. Returns a channel and an unsubscribe function.

type SessionMeta

type SessionMeta struct {
	Shell string
	CWD   string
}

SessionMeta is passed to OnSessionStart.

type SessionMetaPayload

type SessionMetaPayload struct {
	TmuxDetected bool `json:"tmux_detected"`
}

SessionMetaPayload is pushed to the client once after the WS replay buffer is sent. The client uses TmuxDetected to decide whether to show tmux gesture hints.

type SessionSnapshot

type SessionSnapshot struct {
	ID     string
	Name   string
	Shell  string
	CWD    string
	Status string
}

SessionSnapshot is a point-in-time session state for persistence.

type SessionStatus

type SessionStatus string

SessionStatus represents the lifecycle state of a terminal session. [Ref: T5-B3, CAP-session-lifecycle S2]

const (
	StatusRunning SessionStatus = "running"
	StatusExited  SessionStatus = "exited"
)

type SessionStore

type SessionStore interface {
	SaveSession(ctx context.Context, s *SessionSnapshot) error
	ListSessions(ctx context.Context) ([]SessionSummary, error)
}

SessionStore is optional persistence. nil = in-memory only (data lost on restart).

type SessionSummary

type SessionSummary struct {
	ID     string
	Name   string
	Status string
}

SessionSummary is a brief session descriptor.

type SessionV2

type SessionV2 struct {
	SessionID   int64           // 关联 BS-03 Session.id (Int64)
	WorkspaceID int64           // 关联 BS-10 Workspace.id (Int64)
	RootDir     string          // Workspace.root_dir (PTY cwd)
	Pty         *os.File        // PTY master fd
	Cmd         *exec.Cmd       // Shell 进程
	Ring        *RingBuffer     // 环形缓冲 (一屏内容 ≈ 4KB)
	WSConn      *websocket.Conn // 当前 WebSocket 连接 (可为 nil)
	State       string          // spawning | running | stopping | stopped | failed
	// contains filtered or unexported fields
}

SessionV2 是 BS-08 v2 维护的运行时状态(内存,不持久化)。 Session 持久化由 BS-03 的 conversationDB 负责。[T5 §2.1]

func (*SessionV2) GetState

func (cs *SessionV2) GetState() string

GetState 返回当前状态(线程安全)

func (*SessionV2) Lock

func (cs *SessionV2) Lock()

Lock 加锁保护 WSConn / State 访问

func (*SessionV2) SetState

func (cs *SessionV2) SetState(s string)

SetState 设置状态(线程安全)

func (*SessionV2) SetWSConn

func (cs *SessionV2) SetWSConn(conn *websocket.Conn) bool

SetWSConn 替换 WebSocket 连接(关闭旧连接),[T5 §6.1] 返回是否关闭了旧连接

func (*SessionV2) Unlock

func (cs *SessionV2) Unlock()

Unlock 释放锁

func (*SessionV2) WriteWS

func (cs *SessionV2) WriteWS(data []byte)

WriteWS 向当前 WebSocket 发送消息(线程安全) 若无连接则静默忽略

type ShellExitPayload

type ShellExitPayload struct {
	ExitCode int `json:"exitCode"`
}

ShellExitPayload is the payload for a "shell_exit" control message.

type TermMsg

type TermMsg struct {
	Type     string `json:"type"`
	Data     string `json:"data,omitempty"`      // input / output
	Cols     int    `json:"cols,omitempty"`      // resize
	Rows     int    `json:"rows,omitempty"`      // resize
	Reason   string `json:"reason,omitempty"`    // closed
	ExitCode int    `json:"exit_code,omitempty"` // closed
}

TermMsg 是 WebSocket 文本帧的统一 JSON 格式 [T5 §5.2 §5.3]

type TerminalSessionService

type TerminalSessionService interface {
	// List returns all active sessions.
	List(ctx context.Context) ([]SessionInfo, error)

	// Create starts a new PTY-backed session with the given options.
	Create(ctx context.Context, opts CreateOptions) (*SessionInfo, error)

	// Get returns the session info for the given ID.
	// Returns an error wrapping "not found" when the session does not exist.
	Get(ctx context.Context, id string) (*SessionInfo, error)

	// Delete destroys the session identified by id, killing the PTY process.
	Delete(ctx context.Context, id string) error

	// Resize sets the terminal window size for the session.
	Resize(ctx context.Context, id string, cols, rows int) error

	// Input writes raw terminal bytes to the session PTY.
	// Used by the HTTP fallback path (WKWebView POST /sessions/:id/input).
	Input(ctx context.Context, id string, data []byte) error

	// PasteUpload saves clipboard content to a temp file under the session CWD
	// and returns the absolute path.  filename is the original filename hint;
	// content is the raw file body; sessionCWD is the working directory to use
	// (pass empty string to use the session's own CWD).
	PasteUpload(ctx context.Context, id string, filename string, content io.Reader, sessionCWD string) (string, error)

	// TailOutput returns the last n lines of terminal output for agent intel.
	TailOutput(ctx context.Context, id string, lines int) ([]string, error)

	// Close shuts down the service and destroys all sessions.
	Close() error
}

TerminalSessionService is the service-layer interface for terminal session management. Implementations: InProcessService (delegates to SessionManager directly) or a future RemoteService (proxies to a TerminalMuxHost over HTTP). The webui layer and tests depend only on this interface, enabling the underlying transport to be swapped without touching callers.

type TmuxCopyMotioner added in v0.2.0

type TmuxCopyMotioner interface {
	CopyMotion(ctx context.Context, session, motion string) error
}

TmuxCopyMotioner is an OPTIONAL provider capability: drive a copy-mode scroll motion directly against the tmux server (bypassing the PTY keystroke stream, which silently no-ops for these motions). The default provider implements it; a host-injected provider may omit it, in which case the endpoint 501s gracefully.

type TmuxNavPayload

type TmuxNavPayload struct {
	Action string `json:"action"` // "window_next"|"window_prev"|"session_next"|"session_prev"
}

TmuxNavPayload is the payload for a "tmux_nav" control message. The backend silently ignores the action when TmuxDetected=false.

type TmuxSessionMaker added in v0.2.0

type TmuxSessionMaker interface {
	NewSession(ctx context.Context, shellPID int) (string, error)
}

TmuxSessionMaker is an OPTIONAL provider capability: create a new tmux session and switch the requesting client onto it (server-side, since keystroke-driven new-session is unreliable and refuses to nest inside a client). Default provider implements it.

type TmuxStateProvider added in v0.2.0

type TmuxStateProvider interface {
	TmuxState(ctx context.Context, shellPID int) (json.RawMessage, error)
}

TmuxStateProvider yields a JSON-encoded tmux topology snapshot for the host.

The terminal ships a default, in-process provider (defaultTmuxProvider) so the standalone build gets tmux topology/prefix/agent-status without any host wiring. A host (e.g. the pro repo) may inject a richer provider via WithTmuxProvider; this is purely additive and never required.

shellPID is the calling session's shell PID (0 when unknown); it lets the provider compute whether that specific shell is attached inside tmux.

type WSControlMessage

type WSControlMessage struct {
	Type    string          `json:"type"`
	Payload json.RawMessage `json:"payload,omitempty"`
}

WSControlMessage represents a JSON control message on the WebSocket. Binary frames carry raw terminal I/O; Text/JSON frames carry control messages. [Ref: T5-B3, CAP-terminal-io S3, DDC-02]

func WaitForControlMessage

func WaitForControlMessage(t *testing.T, ws *websocket.Conn, msgType string, timeout time.Duration) WSControlMessage

WaitForControlMessage reads WS messages until a control message of the given type is found.

Directories

Path Synopsis
cmd
dw-terminal command
internal
spa

Jump to

Keyboard shortcuts

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