browser

package
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package browser implements the Browser Use capability: a CDP-driven browser the agent can see (text a11y snapshots + screenshots) and operate (click, fill, navigate) behind tiered approvals. Two backends share one TabConn abstraction: a managed Chrome launched by jcode, and the user's own Chrome reached through the jcode extension bridge. See internal-doc/browser-use-design.md.

Index

Constants

View Source
const ExtensionID = "ekcnniaefmnhnemnpphikhgfoofnojnd"

ExtensionID is the unpacked/dev extension id, derived from the committed public key ("key" field) in extension/manifest.json — stable across loads.

View Source
const NativeHostName = "com.jcode.bridge"

NativeHostName is the Chrome Native Messaging host id the extension connects to via chrome.runtime.connectNative. Must match the extension's usage.

Variables

View Source
var AllowedExtensionIDs = []string{
	ExtensionID,
	"olkapiiikpfhaccmjphakolinkcggcbd",
}

AllowedExtensionIDs is every id the Browser Bridge extension can have: the unpacked dev build (above) plus the published store builds, which the stores re-sign under their own ids. All of these must appear in the native-host manifest's allowed_origins, or a store-installed extension can't open the native host (the browser enforces the origin before launching it).

View Source
var ErrControlInterrupted = fmt.Errorf("browser control interrupted")

ErrControlInterrupted is returned when the user (or the extension) takes back control of a tab mid-action. Tools surface this so the model stops and reports naturally rather than retrying.

Functions

func ChromeVersion

func ChromeVersion(ctx context.Context, path string) string

ChromeVersion returns the version string reported by the executable.

func FindChrome

func FindChrome(configPath string) string

FindChrome returns the path to a Chromium-based browser executable, or "" when none is found. Explicit configPath (config.browser.chrome_path) wins.

func InstallNativeHost

func InstallNativeHost(binPath string) error

InstallNativeHost writes/refreshes the native-messaging host manifest so the extension can reach this jcode binary. Best-effort: it targets every browser dir it can and returns the first hard error (a missing browser dir is skipped, not an error). binPath should be os.Executable().

func IsLocalOrigin

func IsLocalOrigin(origin string) bool

IsLocalOrigin reports whether an origin points at the local machine — the primary browser-use case (localhost dev-loop). Local targets get lighter treatment in some UIs but still follow the same approval tiers.

func MaybeRunNativeHost

func MaybeRunNativeHost(args []string) bool

MaybeRunNativeHost checks argv for the native-messaging launch signature and, if present, runs the host loop and returns true (the caller should exit).

func OriginOf

func OriginOf(raw string) string

OriginOf returns the scheme://host[:port] origin of a raw URL, or "" when it cannot be parsed (e.g. about:blank).

func WriteEndpoint

func WriteEndpoint(ws, token string) error

WriteEndpoint persists the current server WS URL + a valid bridge token so a freshly-spawned native host process (a separate process from the server) can read it and hand it to the extension. 0600 — it grants browser control.

Types

type ActRequest

type ActRequest struct {
	Action string  // click|dblclick|fill|press|hover|scroll|select|upload|dialog
	UID    string  // element uid from the latest snapshot (most actions)
	X, Y   float64 // coordinate fallback for scroll/click
	Value  string  // fill text, select value, dialog decision (accept|dismiss)
	Key    string  // for action=press (e.g. "Enter")
	Files  []string
}

ActRequest describes a single browser_act call.

type Backend

type Backend interface {
	Kind() string // "managed" | "extension"
	NewTab(ctx context.Context, url string) (TabConn, error)
	ListTabs(ctx context.Context) ([]TabInfo, error)
	// AttachTab takes control of an existing tab (claim). Managed backend
	// attaches to its own targets; extension backend claims a user tab.
	AttachTab(ctx context.Context, id string) (TabConn, error)
	Close() error
}

Backend abstracts a browser jcode can drive.

func Launch

func Launch(ctx context.Context, opts LaunchOptions) (Backend, error)

Launch starts Chrome with --remote-debugging-port=0, waits for the DevTools websocket announcement on stderr, and returns a connected managed backend.

type Bridge

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

Bridge is the server side of the jcode Chrome extension channel. The extension's service worker connects over a websocket, presents a long-lived token (obtained via native-messaging Auto-connect), and then relays CDP commands to the user's Chrome via chrome.debugger. See §5.3 of the design.

func NewBridge

func NewBridge() *Bridge

NewBridge creates a bridge. tokens are persisted to ~/.jcode/browser/ext-tokens.json.

func (*Bridge) Backend

func (b *Bridge) Backend() (Backend, error)

Backend returns an extension-backed Backend, or an error when offline.

func (*Bridge) Connected

func (b *Bridge) Connected() bool

Connected reports whether an extension is currently attached.

func (*Bridge) HandleWS

func (b *Bridge) HandleWS(w http.ResponseWriter, r *http.Request)

HandleWS upgrades an extension connection and runs its read loop.

func (*Bridge) IssueToken

func (b *Bridge) IssueToken() string

IssueToken mints and persists a token without a pairing code. Used by the native-messaging path, where the running server hands the extension a token directly (the OS-level native host launch is the trust anchor).

func (*Bridge) StableToken

func (b *Bridge) StableToken() string

StableToken returns one long-lived token, reused across restarts — the "key" the extension stores once and re-presents forever. Combined with a stable server port, the extension reconnects silently with no re-auth. Persisted to ~/.jcode/browser/server-token (0600) and kept in the valid-token set. Preferred over IssueToken for the native/auto-connect path so tokens don't accumulate a fresh entry on every launch.

type Config

type Config struct {
	Enabled    bool
	Backend    string // auto | managed | extension
	ChromePath string
	Headless   bool
	Viewport   string
	DevMode    bool
}

Config mirrors config.BrowserConfig, decoupled so internal/browser does not import a specific config layout beyond what it needs.

type Endpoint

type Endpoint struct {
	WS    string `json:"ws"`
	Token string `json:"token"`
}

Endpoint is what the native host hands back to the extension so it can dial the running jcode server without the user typing anything.

func ReadEndpoint

func ReadEndpoint() (Endpoint, error)

ReadEndpoint loads the endpoint written by the running server.

type EventHandler

type EventHandler func(method string, params json.RawMessage)

EventHandler receives CDP events for one tab.

type ExtensionInstallState

type ExtensionInstallState struct {
	Installed bool   `json:"installed"`
	Enabled   bool   `json:"enabled"`
	Profile   string `json:"profile,omitempty"`
	Path      string `json:"path,omitempty"` // unpacked path when known
}

ExtensionInstallState reports whether a jcode extension is present in the user's Chrome profiles by scanning Preferences JSON — the same technique as Codex's check-extension-installed.js.

func CheckExtensionInstalled

func CheckExtensionInstalled(profileRoots []string, extDir string) ExtensionInstallState

CheckExtensionInstalled scans profileRoots (or the default Chrome dirs when nil) for an extension whose unpacked path points at extDir, or whose id equals ExtensionID when set.

type LaunchOptions

type LaunchOptions struct {
	ChromePath string // empty → FindChrome
	Headless   bool
	ProfileDir string // empty → ~/.jcode/browser/profile
	Viewport   string // "1280x720"
}

LaunchOptions controls the managed Chrome launch.

type Manager

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

Manager is the process-wide owner of browser-use infrastructure: the extension bridge, managed-Chrome lifecycle, screenshot store, and the resolved config. Tasks obtain a per-task Session from it. One per server.

func NewManager

func NewManager(cfg Config) *Manager

NewManager creates the manager. shotDir defaults to ~/.jcode/browser/shots.

func (*Manager) Bridge

func (m *Manager) Bridge() *Bridge

Bridge exposes the extension bridge for route wiring.

func (*Manager) Close

func (m *Manager) Close() error

Close tears down the managed Chrome (if any).

func (*Manager) DevMode

func (m *Manager) DevMode() bool

DevMode reports whether high-risk actions (eval / raw CDP) are unlocked.

func (*Manager) GetConfig

func (m *Manager) GetConfig() Config

GetConfig returns a copy of the live config.

func (*Manager) OpenSession

func (m *Manager) OpenSession(ctx context.Context) (*Session, error)

OpenSession creates a per-task Session, choosing a backend per config: "extension" requires the bridge; "managed" launches Chrome; "auto" prefers a connected extension, else managed.

func (*Manager) SaveScreenshot

func (m *Manager) SaveScreenshot(png []byte) (string, error)

SaveScreenshot writes PNG bytes to the shot store and returns its id.

func (*Manager) ScreenshotPath

func (m *Manager) ScreenshotPath(id string) string

ScreenshotPath returns the file path for a shot id (for the HTTP endpoint).

func (*Manager) SetConfig

func (m *Manager) SetConfig(cfg Config)

SetConfig updates the live config (from the settings endpoint).

func (*Manager) Status

func (m *Manager) Status(ctx context.Context) Status

Status computes the current status.

type Session

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

Session is the per-task browser state: one backend, a set of controlled tabs, the active tab, and the latest snapshot generation for stale-uid detection. It is safe for concurrent use by the tool layer.

func NewSession

func NewSession(backend Backend) *Session

NewSession wraps a backend into a per-task session.

func (*Session) Act

func (s *Session) Act(ctx context.Context, req ActRequest) (string, error)

Act performs an interaction and returns a short "what changed" summary so the model usually does not need a follow-up snapshot.

func (*Session) BackendKind

func (s *Session) BackendKind() string

Backend returns the underlying backend kind ("managed"/"extension").

func (*Session) ClaimTab

func (s *Session) ClaimTab(ctx context.Context, tabID string) error

ClaimTab takes control of a pre-existing (user) tab without closing it later.

func (*Session) Close

func (s *Session) Close() error

Close releases this task's tabs. It deliberately does NOT close the backend: the managed Chrome and the extension bridge are owned by the Manager and reused across tasks, so tearing the backend down here would kill the browser out from under every other (and future) task and leave the Manager caching a dead backend. Backend teardown belongs to Manager.Close.

func (*Session) CloseTab

func (s *Session) CloseTab(ctx context.Context, tabID string) error

CloseTab closes a controlled tab.

func (*Session) CurrentOrigin

func (s *Session) CurrentOrigin() string

CurrentOrigin returns the scheme://host of the active tab's last known URL, or "" when there is no active tab or its URL has no real origin (e.g. about:blank). It reads cached state — refreshed on every snapshot, which the model takes before acting — so the approval layer can scope per-site permissions for actions whose args carry no URL (clicks, fills) without a blocking CDP call.

func (*Session) Eval

func (s *Session) Eval(ctx context.Context, expr string) (string, error)

Eval runs a read-only expression and returns its JSON value (dev mode gate is enforced by the tool/approval layer, not here).

func (*Session) ListTabs

func (s *Session) ListTabs(ctx context.Context) ([]TabInfo, error)

ListTabs returns the tabs known to the backend, marking which are controlled.

func (*Session) NewTab

func (s *Session) NewTab(ctx context.Context) (string, error)

NewTab opens a blank controlled tab.

func (*Session) Open

func (s *Session) Open(ctx context.Context, url string, newTab bool) (string, error)

Open navigates the active tab (or a new tab) to url and returns a fresh snapshot header.

func (*Session) PageText

func (s *Session) PageText(ctx context.Context, limit int) (string, error)

PageText returns document.body innerText (bounded).

func (*Session) Reload

func (s *Session) Reload(ctx context.Context) (string, error)

Reload reloads the active tab.

func (*Session) Screenshot

func (s *Session) Screenshot(ctx context.Context, fullPage bool) ([]byte, error)

Screenshot captures the active tab as PNG bytes.

func (*Session) SelectTab

func (s *Session) SelectTab(ctx context.Context, tabID string) error

SelectTab makes tabID active, attaching it if not yet controlled.

func (*Session) Snapshot

func (s *Session) Snapshot(ctx context.Context, filter string, maxLines int) (string, error)

Snapshot returns a uid-annotated text snapshot of the active tab.

type Snapshot

type Snapshot struct {
	Text string
	UIDs map[string]int64 // uid → backendDOMNodeId
	Gen  int
}

Snapshot is one serialized page state. UIDs are only valid for the generation they were minted in; actions verify this to reject stale refs.

type Status

type Status struct {
	Enabled         bool   `json:"enabled"`
	Backend         string `json:"backend"`
	ChromeFound     bool   `json:"chrome_found"`
	ChromePath      string `json:"chrome_path,omitempty"`
	ChromeVersion   string `json:"chrome_version,omitempty"`
	ExtensionOnline bool   `json:"extension_online"`
	DevMode         bool   `json:"dev_mode"`
}

Status describes browser-use availability for the settings UI.

type TabConn

type TabConn interface {
	// ID is the backend-scoped tab identifier (targetId or extension tab id).
	ID() string
	// Send issues a CDP command against this tab and returns its raw result.
	Send(ctx context.Context, method string, params any) (json.RawMessage, error)
	// SetEventHandler registers the sink for CDP events from this tab.
	// Only one handler is active at a time; nil clears it.
	SetEventHandler(h EventHandler)
	// Close closes the underlying page/tab.
	Close(ctx context.Context) error
	// Detach releases control of the tab without closing it (extension backend
	// leaves the page to the user; managed backend is equivalent to a no-op
	// because nobody else is driving that Chrome).
	Detach(ctx context.Context) error
}

TabConn is a single controllable tab, regardless of backend.

type TabInfo

type TabInfo struct {
	ID       string `json:"id"`
	Title    string `json:"title"`
	URL      string `json:"url"`
	UserTab  bool   `json:"user_tab"` // pre-existing user tab (extension backend)
	Attached bool   `json:"attached"` // currently under jcode control
}

TabInfo describes a tab visible to a backend.

Jump to

Keyboard shortcuts

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