Documentation
¶
Overview ¶
Package devtools drives the Web UI through Chrome DevTools MCP: it starts the server, calls its tools, and reads what they print.
Index ¶
- Constants
- Variables
- func BuildCommand(ctx context.Context, command, args, environ []string) (*exec.Cmd, error)
- func ChildEnv(environ []string) []string
- func ParseCLS(text string) (float64, error)
- func ParseLighthouse(text string) (map[string]float64, error)
- func ParseScriptResult(text string) ([]byte, error)
- type CallOption
- type ConsoleMessage
- type Driver
- func (d *Driver) AwaitReply(ctx context.Context) (domain.Reply, error)
- func (d *Driver) Click(ctx context.Context, q Query) error
- func (d *Driver) Close(ctx context.Context) error
- func (d *Driver) Collect(ctx context.Context) (domain.UIObservations, error)
- func (d *Driver) Conversation() string
- func (d *Driver) DeleteChat(ctx context.Context) error
- func (d *Driver) Eval(ctx context.Context, function string, out any) error
- func (d *Driver) ExpectNewReply(ctx context.Context)
- func (d *Driver) Fill(ctx context.Context, q Query, value string) error
- func (d *Driver) Login(ctx context.Context, email, password string) error
- func (d *Driver) PressRowButton(ctx context.Context, action RowAction, conversation string) error
- func (d *Driver) Progress(ctx context.Context) (status domain.ReplyStatus, chars int, found bool, err error)
- func (d *Driver) ReadRaw(ctx context.Context, reply domain.Reply) (string, error)
- func (d *Driver) Reload(ctx context.Context) error
- func (d *Driver) Screenshot(ctx context.Context, path string) error
- func (d *Driver) StartChat(ctx context.Context) error
- func (d *Driver) Stop(ctx context.Context) error
- func (d *Driver) Submit(ctx context.Context, text string) error
- func (d *Driver) Tool(ctx context.Context, name string, args map[string]any, opts ...CallOption) (Result, error)
- func (d *Driver) Viewport(ctx context.Context, spec string) error
- type DriverConfig
- type Factory
- type Image
- type NetworkRequest
- type Node
- type NotFoundError
- type Options
- type Page
- type ProbePrompts
- type Probes
- func (p *Probes) AgentDown(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Close(ctx context.Context) error
- func (p *Probes) KeyboardOnly(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) LayoutShift(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Lighthouse(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Mobile(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Regenerate(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) ReloadMidStream(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) RenameDelete(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Stop(ctx context.Context) (domain.ProbeOutcome, error)
- func (p *Probes) Theme(ctx context.Context) (domain.ProbeOutcome, error)
- type Query
- type Result
- type RowAction
- type ServerInfo
- type Session
- type Snapshot
- type ToolError
Constants ¶
const DefaultViewport = "1280x800x1"
DefaultViewport is the window the driver works in unless told otherwise.
const SupportedContract = "1"
SupportedContract is the Web UI contract version this driver was written for (internal/contract). A page that says another version is refused. A test keeps it equal to the contract package's, so that raising one makes you look at the other.
Variables ¶
var ErrNoCLS = errors.New("the performance trace reported no layout shift")
ErrNoCLS is returned when the trace summary holds no layout shift figure.
var ErrNoLighthouseScores = errors.New("the Lighthouse audit printed no category scores")
ErrNoLighthouseScores is returned when the audit's output holds no scores.
var ErrNoScriptResult = errors.New("evaluate_script printed no result")
ErrNoScriptResult is returned when evaluate_script's output holds no result.
var ErrNoStopButton = errors.New("there is no Stop button on the page")
ErrNoStopButton is returned by Stop when the page shows no Stop button: the reply has finished, or the page does not offer one.
Functions ¶
func BuildCommand ¶
BuildCommand returns the process that runs Chrome DevTools MCP, or says why it must not be run: the version is not pinned, or one of the flags that keep it from sending usage statistics and from touching a real browser profile is missing (see config.CheckMCP).
The process gets a trimmed environment (see ChildEnv), and it is killed, along with everything it started, when ctx ends.
Pattern: Pinned, Trimmed, Isolated Child
func ChildEnv ¶
ChildEnv keeps, from an environment in the form os.Environ gives, what Chrome DevTools MCP needs. The server is a package fetched from a registry and it drives a browser; it has no use for the Hugging Face token, the Web UI password or the database settings the evaluator's own environment holds, so it does not get them. A name that looks like a secret is dropped even when it would otherwise pass.
func ParseCLS ¶
ParseCLS reads the lab cumulative layout shift from the summary of a stopped performance trace.
func ParseLighthouse ¶
ParseLighthouse reads the category scores of a Lighthouse audit, by category id ("accessibility", "best-practices", "seo"...).
func ParseScriptResult ¶
ParseScriptResult returns the JSON an evaluated function returned, which the tool prints between a "```json" line and a closing "```". The closing fence is the last one in the text, because the result may hold fences of its own: an answer with a code block in it, for one. A function that returns nothing prints an empty block, and the result is "null".
Types ¶
type CallOption ¶
type CallOption func(*callConfig)
CallOption changes one call.
func WithTimeout ¶
func WithTimeout(d time.Duration) CallOption
WithTimeout gives one call more (or less) time than the session's default, for tools that are slow by nature, such as a Lighthouse audit.
type ConsoleMessage ¶
ConsoleMessage is one line of list_console_messages.
func ParseConsole ¶
func ParseConsole(text string) []ConsoleMessage
ParseConsole reads list_console_messages. The arguments count the tool adds ("(1 args)") and the repeat count of an issue ("(count: 2)") are left in the text, which is only ever shown, never compared.
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver uses the Web UI the way a person does, through Chrome DevTools MCP: it finds elements by their accessible names in the page's accessibility tree, clicks and types, and reads the page's state through the data attributes of the contract. One driver is one browser tab and is not safe for concurrent use.
func NewDriver ¶
func NewDriver(s *Session, cfg DriverConfig) *Driver
NewDriver wraps a session. The tab is opened by Login.
func (*Driver) AwaitReply ¶
AwaitReply waits until the assistant message has a final status. When ctx ends first, it returns what it knows and an error wrapping domain.ErrReplyTimeout.
func (*Driver) Collect ¶
Collect returns what the browser observed since the page was opened: the observer's record of the reply, the console, and the requests.
func (*Driver) Conversation ¶
Conversation is the id of the conversation in the tab, once it has one.
func (*Driver) DeleteChat ¶
DeleteChat deletes the conversation in the tab through the sidebar: the delete button of its row, then Delete in the confirmation.
func (*Driver) Eval ¶
Eval runs a function in the page and decodes the JSON it returns into out. It is for the probes and the tests, which do things to the page that an attempt never does (scroll away, resize, reload).
func (*Driver) ExpectNewReply ¶
ExpectNewReply notes the newest reply on the page, so that AwaitReply waits for the one that the next step brings (Regenerate, Retry), not for this one.
func (*Driver) Login ¶
Login opens a tab on the sign-in page, checks the page speaks the contract this driver knows, and signs in.
func (*Driver) PressRowButton ¶
PressRowButton presses the rename or delete button of a conversation's row in the sidebar, however many other rows there are and whatever their titles. Every question of an evaluation opens with the same prompt, so every row has the same title and every button the same name; the page says how many come first, and that many are passed over. Pressing the first button with the name would delete another conversation, possibly one that is still being written.
func (*Driver) Progress ¶
func (d *Driver) Progress(ctx context.Context) (status domain.ReplyStatus, chars int, found bool, err error)
Progress says how far the newest reply has got: its status and how many characters of text it shows. found is false when the page has no reply yet. A reply that is there tells which conversation it is in, so that a probe that goes no further than this can still tidy up after itself.
func (*Driver) ReadRaw ¶
ReadRaw returns the reply as it is stored, the Markdown the agent wrote, from the endpoint behind the Copy button. It is the one place the evaluator asks the Web UI for data without going through what a person sees, and it does so from inside the page, with the page's own session.
func (*Driver) Reload ¶
Reload loads the page again, with the observer in it, and waits for the page to show its composer.
func (*Driver) Screenshot ¶
Screenshot saves the page as a PNG at path.
func (*Driver) StartChat ¶
StartChat opens the start page, where a message starts a new conversation.
type DriverConfig ¶
type DriverConfig struct {
// BaseURL is the Web UI, such as http://localhost:8300. It can carry
// credentials: the browser is given it as it is, and an error names it as
// redact.URL shows it.
BaseURL string
// HumanTyping types the prompt as keystrokes, a word at a time with a pause
// between words, instead of putting it in the field at once.
HumanTyping bool
// TypingDelay is the pause per character when HumanTyping is on.
TypingDelay time.Duration
// Viewport is the size of the browser window as the emulate tool spells it
// (width x height x pixel ratio); zero means a laptop, 1280x800x1. The default
// window of a headless Chrome is taller than most replies, which would leave
// the scroll check with nothing to do, and differs from machine to machine.
Viewport string
// PollInterval is how often AwaitReply looks at the reply; zero means 250 ms.
PollInterval time.Duration
// SettleTimeout is how long to wait for a page to show what it should after
// a navigation or a click; zero means 15 seconds.
SettleTimeout time.Duration
Logger *slog.Logger
}
DriverConfig says how a driver behaves.
type Factory ¶
type Factory struct {
Session Options
Driver DriverConfig
// contains filtered or unexported fields
}
Factory makes drivers, each with a browser of its own. Use it by pointer.
type NetworkRequest ¶
type NetworkRequest struct {
ID int
Method string
URL string
// Status is the HTTP status; 0 when the line has none (still pending).
Status int
// Failure is the text in brackets when it is not a status, such as
// "failed - net::ERR_CONNECTION_REFUSED".
Failure string
}
NetworkRequest is one line of list_network_requests.
func ParseRequests ¶
func ParseRequests(text string) []NetworkRequest
ParseRequests reads list_network_requests.
type Node ¶
type Node struct {
// UID is what the tools take to say which element to act on. It is only good
// for the snapshot it came from.
UID string
Role string
// Name is the accessible name; empty when the element has none.
Name string
// Attrs are the key="value" parts after the name (url, level, value...).
Attrs map[string]string
// Flags are the bare words after the name (focusable, focused, disabled...).
Flags []string
}
Node is one line of a page snapshot: an element of the accessibility tree.
type NotFoundError ¶
type NotFoundError struct {
Query Query
// Roles lists a few elements the page does have, for the message.
Roles []string
}
NotFoundError says that the snapshot has no element like the one asked for.
func (*NotFoundError) Error ¶
func (e *NotFoundError) Error() string
type Options ¶
type Options struct {
// Command and Args are EVAL_MCP_COMMAND and EVAL_MCP_ARGS.
Command, Args []string
// Environ is the environment to take the server's from; nil means this
// process's. Only what ChildEnv lets through is passed on.
Environ []string
// StartTimeout bounds the start and the handshake.
StartTimeout time.Duration
// CallTimeout bounds one tool call, unless the call asks for another.
CallTimeout time.Duration
Logger *slog.Logger
}
Options say how to start the server and how long to wait for it.
type Page ¶
Page is one entry of list_pages.
func ParsePages ¶
ParsePages reads the "## Pages" list that list_pages, new_page and several other tools print.
func SelectedPage ¶
SelectedPage is the page the tools act on by default.
type ProbePrompts ¶
type ProbePrompts struct {
// Long asks for a reply that takes seconds to write, so that there is time to
// stop it or reload the page in the middle.
Long string
// Short asks for a reply of a word or two, for the probes that need an
// answer and nothing more.
Short string
// Flaky asks for a reply that fails the first time and succeeds the second,
// which only the scripted agent can give. Empty means no such agent, and the
// probe that needs one is not run.
Flaky string
}
ProbePrompts are what the probes say to the agent.
type Probes ¶
type Probes struct {
Factory *Factory
Email, Password string
Prompts ProbePrompts
Thresholds domain.Thresholds
// KeepChats leaves the conversations the probes start in place.
KeepChats bool
Logger *slog.Logger
// contains filtered or unexported fields
}
Probes carries out the UI probes in a browser, one conversation at a time. It opens its browser on first use and closes it with Close.
func (*Probes) AgentDown ¶
AgentDown asks for a reply that the scripted agent fails to give the first time, and checks that the page says so and that Retry gets the answer.
func (*Probes) KeyboardOnly ¶
KeyboardOnly sends a message, stops a reply and opens a conversation with the keyboard alone.
func (*Probes) LayoutShift ¶
LayoutShift records a performance trace while a long reply arrives and reads how much the layout shifted.
func (*Probes) Lighthouse ¶
Lighthouse audits the page, on a desktop and on a phone, and reads the scores for accessibility and best practices.
func (*Probes) Regenerate ¶
Regenerate asks for a new answer to the last message of a conversation of two replies, and checks that only that reply was replaced.
func (*Probes) ReloadMidStream ¶
ReloadMidStream loads the page again while a reply is being written.
func (*Probes) RenameDelete ¶
RenameDelete renames a conversation and deletes it, and checks that both hold after the page is loaded again.
type Query ¶
type Query struct {
Role string
// Name must equal the accessible name.
Name string
// NameContains must be part of the accessible name.
NameContains string
// NameMatches is a regular expression the accessible name must match.
NameMatches *regexp.Regexp
// Flag must be one of the node's flags.
Flag string
}
Query picks nodes. Every field that is set must match.
type RowAction ¶
type RowAction string
RowAction is a button on a conversation's row in the sidebar.
type ServerInfo ¶
type ServerInfo struct{ Name, Version string }
ServerInfo is what the server says about itself in the handshake.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is a connection to one Chrome DevTools MCP server, and so to one headless Chrome. It is for one driver: calls to it should not overlap.
func Open ¶
Open starts the server and completes the handshake. The server is not tied to ctx, which only bounds the start: it runs until Close.
func (*Session) Call ¶
func (s *Session) Call(ctx context.Context, tool string, args map[string]any, opts ...CallOption) (Result, error)
Call runs a tool.
Errors are told apart by what the caller can do about them: ToolError means the page refused and the session is fine; the caller's context ending is returned as that context's error; anything that means the server or the browser is gone, or stopped answering, wraps domain.ErrDriverDied, and the session says why in its log and refuses further calls. A tool that does not answer in time counts as gone: a browser stuck in the middle of a call is not one to carry on with.
Pattern: Three-Outcome Call
func (*Session) Close ¶
Close ends the session: the server is asked to stop by closing its input, and if ctx ends first it, and the browser it started, are killed. It is safe to call more than once.
func (*Session) Info ¶
func (s *Session) Info() ServerInfo
Info returns what the server called itself and its version.
type Snapshot ¶
type Snapshot struct {
Nodes []Node
}
Snapshot is the accessibility tree of a page, in document order.
func ParseSnapshot ¶
ParseSnapshot reads the text take_snapshot returns. Lines that are not elements (the heading, a note about truncation) are ignored. The result has no nodes when the text holds none; the caller decides whether that is an error.