Documentation
¶
Overview ¶
Package browser holds the server half of the browser takeover bridge: a WebSocket endpoint the ogcode companion browser extension connects to, and the request/response plumbing that turns an agent's tool call into one operation executed in the user's real Chrome tab.
The protocol is documented in browser-extension/PROTOCOL.md and mirrored by the extension in browser-extension/background.js. Every message is one JSON object: hello (extension announcements), ping, op (server -> extension) and result (extension -> server). Nothing else is accepted.
Index ¶
- Constants
- Variables
- func AcceptOptions() *websocket.AcceptOptions
- type Bridge
- func (b *Bridge) Dispatch(ctx context.Context, op string, tab *int64, args map[string]any) (map[string]any, error)
- func (b *Bridge) MatchesToken(presented string) bool
- func (b *Bridge) RotateToken() string
- func (b *Bridge) Serve(ctx context.Context, conn *websocket.Conn)
- func (b *Bridge) Status() Status
- func (b *Bridge) Token() string
- type Dispatcher
- type Status
- type TabInfo
Constants ¶
const ( OpSnapshot = "snapshot" OpContent = "content" OpReadText = "read_text" OpClick = "click" OpType = "type" OpKey = "key" OpScroll = "scroll" OpScreenshot = "screenshot" OpNetwork = "network" OpConsole = "console" )
Op names understood by the extension. The server does not parse them further; a new op only needs matching support on both sides.
Variables ¶
var ErrNotConnected = errors.New("browser extension is not connected to this server")
ErrNotConnected is returned when no extension holds the bridge: no takeover is possible and the caller should steer the user to pairing.
Functions ¶
func AcceptOptions ¶
func AcceptOptions() *websocket.AcceptOptions
AcceptOptions is the origin policy the bridge handshake enforces.
A Chrome extension service worker presents Origin: chrome-extension://<id>, which can never match our host, so the handshake allowlists exactly that scheme. A web page that tries to pair cannot forge it — and the pairing token on top of that is still checked server-side.
coder/websocket matches a pattern against the bare origin host unless the pattern contains "://" — so without the explicit scheme the id alone would be compared and every legitimate extension would be rejected.
Types ¶
type Bridge ¶
type Bridge struct {
// contains filtered or unexported fields
}
Bridge is the server half of the websocket: it holds the single connected extension, relays operations to it and routes the results back. One Bridge serves every session in the process — the takeover browser is the user's, not a session's.
func NewWithToken ¶
NewWithToken returns a bridge pinned to the hex pairing token hexToken. It is how a server that persists the token across restarts keeps the same secret, so an already-paired extension never sees a 401 after the server reboots. An empty or malformed token falls back to a fresh one, and the caller should persist whatever Token() ends up serving.
func (*Bridge) Dispatch ¶
func (b *Bridge) Dispatch(ctx context.Context, op string, tab *int64, args map[string]any) (map[string]any, error)
Dispatch implements Dispatcher: it frames the op, sends it to the extension and waits for the matching result, bounded by the op's own budget and by ctx. The result channel is registered under the state mutex before the frame is written, so a fast reply can never lose the race against registration.
func (*Bridge) MatchesToken ¶
MatchesToken reports whether a presented token equals the live one in constant time.
func (*Bridge) RotateToken ¶
RotateToken replaces the pairing token. An already-connected extension keeps control until its socket drops; the old URL simply stops accepting new connections.
func (*Bridge) Serve ¶
Serve takes over an accepted connection: it becomes the bridge's extension link until the socket drops, replacing any previous one. It returns when the connection closes (cleanly, in error, or because the server is shutting down); the caller closes the conn once Serve returns.
type Dispatcher ¶
type Dispatcher interface {
// Status reports the current connection and the tabs it announced.
Status() Status
// Dispatch runs one named operation and returns its data payload.
Dispatch(ctx context.Context, op string, tab *int64, args map[string]any) (map[string]any, error)
}
Dispatcher is what a browser tool calls: the live bridge, or a test double.
type Status ¶
type Status struct {
Connected bool `json:"connected"`
Version string `json:"version,omitempty"`
Tabs []TabInfo `json:"tabs"`
}
Status is what GET /api/browser/status and the browser_status tool report.
func (Status) GrantedCount ¶
GrantedCount counts the granted tabs the latest hello announced — the one number the takeover steer in the system prompt turns on.