Skip to main content

surf

Control Chrome browser via CLI for testing, automation, and debugging. Use when the user needs browser automation, screenshots, form filling, page inspection, network/CPU emulation, DevTools streaming, or AI queries via ChatGPT/Gemini/Perplexity/Grok/AI Studio.

Jump to install

Source facts

Repository
nicobailon/surf-cli
Last source activity
September 21, 2026 at 07:21
Detected SKILL.md language
English
Stars
620
Forks
57

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
surf
description
Control Chrome browser via CLI for testing, automation, and debugging. Use when the user needs browser automation, screenshots, form filling, page inspection, network/CPU emulation, DevTools streaming, or AI queries via ChatGPT/Gemini/Perplexity/Grok/AI Studio.
# Surf Browser Automation Control Chrome browser via CLI or Unix socket. Ordinary socket-backed CLI commands report top-level host tool-response errors on stderr with a supplied `[code]` on the first line and exit 1. `--json` additionally writes `{error:{code,message,details?}}` on stdout (missing code becomes `"error"`). `--soft-fail` instead keeps the original stderr warning, empty stdout and exit 0, even with `--json`. This does not cover local validation, transport/parser failures or compound-command errors: do not assume every failure produces JSON. Connection failures remain stderr-only and exit 1, including with `--soft-fail`. ## Native Host / Socket Notes For WSL2 with Windows Chrome, run `surf install <extension-id>` inside WSL2. Surf detects WSL2 and writes the Windows-side native messaging manifest plus a wrapper that launches the WSL host. Use `surf install <extension-id> --target linux` only for Linux browsers running inside WSLg. On macOS, Chrome reads the native messaging manifest at `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/surf.browser.host.json`. If native messaging fails, confirm that file exists, its `allowed_origins` extension ID matches `chrome://extensions`, then rerun `surf install <extension-id>`, restart Chrome, and reload the extension. Open Surf's service-worker console from `chrome://extensions`; in **Details > Extension options**, enable **Debug Mode**, reproduce the failure, then disable it when finished. If a command reports `Socket connect failed`, run `surf doctor` first, then check the `Attempted socket:` line. Default sockets are `/tmp/surf.sock` on macOS/Linux/WSL2 and `//./pipe/surf` on Windows. If `SURF_SOCKET` is set, the browser-launched host and the shell running `surf` must use the same value. For opt-in POSIX group sharing, install with `surf install <extension-id> --socket-mode 660 --socket-group <group>`. The default remains `0600`; mode `660` grants every member of that group full Surf authority, so use a dedicated narrow group. Re-run `surf install` without those flags to clear the wrapper settings. Remote Surf credentials remain the revocable per-client alternative. ## Remote Surf Remote clients require a per-client credential; Tailnet reachability alone is not authorization. On the POSIX browser host, authorize the client before installing the listener: ```bash surf remote authorize agent-macbook --output ~/agent-macbook.surf-credential.json surf install <extension-id> --listen 100.101.102.103:4321 surf remote list ``` Move the mode-0600 credential to the client through a secure channel. It grants full trusted Surf authority. Use it explicitly or through `SURF_REMOTE` and `SURF_REMOTE_CREDENTIAL`: ```bash surf --remote 100.101.102.103:4321 \ --remote-credential ~/.config/surf/agent-macbook.json \ page.read # TLS is client-side and requires a TLS-terminating reverse proxy in front of SURF_LISTEN surf --remote surf.example.com:443 --remote-tls \ --remote-tls-ca ~/.config/surf/private-ca.pem \ --remote-credential ~/.config/surf/agent-macbook.json page.read surf remote revoke agent-macbook # Run on the browser host ``` Environment equivalents are `SURF_REMOTE_TLS=1`, `SURF_REMOTE_TLS_CA`, and `SURF_REMOTE_TLS_SERVER_NAME`. A custom CA replaces system roots; Ed25519 credentials remain mandatory after TLS validation. Remote paths are client-local by default. `local:./file` is explicit client-local syntax; only `remote:/absolute/path` accesses the browser host directly. Remote transfer supports one upload or ChatGPT/Gemini input and one screenshot, network-export, or Gemini image output. Limits are 256 MiB per file, 512 MiB and 32 files per connection, and 256 KiB decoded chunks. `record`, `aistudio.build`, smoke screenshot directories, directories, and multi-file inputs are not supported remotely. Successful action screenshots and failure `--auto-capture` diagnostics are transferred back to client-local paths. ## CLI Quick Reference ```bash surf --help # Basic help surf <group> # Group help (tab, scroll, page, wait, dialog, emulate, form, perf, ai) surf --help-full # All commands surf --find <term> # Search tools surf --help-topic <topic> # Topic guide (refs, semantic, frames, devices, windows) ``` ## First Command for Independent Agents Before the first browser command in each independent agent shell, choose a unique valid session name and ensure its target exists: ```bash export SURF_SESSION="$(basename "$PWD" | sed 's/[^A-Za-z0-9._-]/-/g')" surf session.ensure "$SURF_SESSION" about:blank ``` `session.ensure` is idempotent. It creates a missing session, reuses a live binding, and reopens a stale or closed tab. Keep `SURF_SESSION` set for every later tab-scoped command in that shell. Use a distinct worktree/directory name per agent; when agents share one directory, append a stable agent identifier. Use `surf session.info "$SURF_SESSION"` to inspect the target and queue state. ## Core Workflow ```bash # 1. Navigate to page surf navigate "https://example.com" # 2. Read page to get element refs surf page.read # 3. Click by ref or coordinates surf click --ref "e1" surf click --x 100 --y 200 # 4. Type text surf type --text "hello" # 5. Full-page screenshot surf screenshot --full-page --output /tmp/shot.png # Inspect animation/style changes as JSON surf animate-audit --selector ".thing" --duration 2000 --fps 10 ``` ## Optional semantic decisions Only `semantic.*` sends bounded, value-free page text to TypeSafe. ```bash surf semantic.find "the settings control" surf semantic.verify "Settings were saved" --json surf semantic.filter "settings" surf semantic.act "Open settings" --max-steps 5 surf semantic.act "Fill email" --input email="$EMAIL" --allow-write --allow-ref e3 printf '%s\n' "$TYPESAFE_KEY" | surf semantic auth set surf semantic auth status surf semantic auth clear ``` For reusable bounded recipes, put only linear `semantic.step` operations in a workflow with `"semantic":{"version":1}`. Check it offline with `surf workflow.validate flow.json` or `surf do --file flow.json --dry-run`, then run with `--allow-semantic`; declared fill/check/click operations also require `--allow-write`. Supply private fill slots as a bounded JSON object on stdin: ```bash printf '%s' '{"quantity":"2"}' | SURF_SESSION=shopping surf do --file flow.json \ --inputs-stdin --allow-semantic --allow-write --json ``` The closed operations are `find`, same-origin direct `open`, `ensureChecked`, `fill`, one-shot `click` with an explicit expectation, and `assert`. Search is bounded overlapping coverage, not global ranking. Unknown write outcomes stop without replay; a later new run can still repeat an external effect. Input values never enter provider state, workflow variables, events, or checkpoints. Every click/fill requires `--allow-write`; repeat `--allow-ref` to narrow it. Broad writes use threshold `0.95`; exactly one allowed ref with one applicable write uses `0.65`. The applied threshold is included in decision/trace output. `TYPESAFE_API_KEY` is the ephemeral/CI override. The shared credential schema is `{"version":1,"apiKey":"..."}` at `${XDG_CONFIG_HOME:-~/.config}/typesafe/credentials.json` (Unix/macOS) or `%APPDATA%\TypeSafe\credentials.json` (Windows), independent of Surf state and the project. POSIX directories/files use `0700`/`0600`; Windows relies on the current user's profile ACL. `TYPESAFE_API_KEY` wins. Status reveals only source and fingerprint; clear affects all clients using the shared file. ## AI Assistants (No API Keys) Query AI models using your browser's logged-in session. Must be logged into the respective service in Chrome. ### ChatGPT ```bash surf chatgpt "explain this code" surf chatgpt "summarize" --with-page # Include current page context surf chatgpt "review" --model gpt-5.5 # Specify model surf chatgpt "analyze" --file document.pdf # With file attachment ``` ### Oracle Use `surf chatgpt` for quick one-shot questions. Use `surf oracle` for long-running or Pro coding consults that need a durable job, explicit model and effort selection, file context, a direct local attachment, recovery, or follow-up turns. Oracle is local-only. For agent workflows, detach after dispatch and keep the returned `.id`: ```bash surf oracle ask "Review this change and identify release risks" \ --files "src/**/*.ts" --files "package.json" \ --model gpt-5.5 --effort pro --file ./design.md --github --detach --json surf oracle status <job-id> --json surf oracle result <job-id> --json # Or let Surf keep polling until capture: surf oracle result <job-id> --wait --json ``` `status` reads persisted state without touching Chrome. `result` attempts to harvest the answer and returns the job object with `response` once its state is `captured`. A Ctrl-C during waiting exits with status 130 and prints `Recover with: surf oracle result <id>`. Once the job is `awaiting`, the persisted ChatGPT conversation URL is its durable key, so `surf oracle result <id>` can recover after CLI exit, native-host restart, or Chrome restart by reopening that conversation. Treat Pro quota as scarce. Oracle never selects Pro effort implicitly; request it with `--effort pro`. ChatGPT model aliases include `gpt-6-astra`, `latest`, `gpt-5.6-sol`, and `gpt-5.5`; `latest` is an explicit floating choice, while `gpt-6-astra` must read back as model 6 before submission. Accepted `--effort` values are `instant`, `medium`, `high`, `xhigh`/`extra-high`, and `pro`. Use `--model gpt-6-astra --effort pro` for GPT-6 Astra with Pro effort. Requested model and effort selections are read back before submission, and an unverifiable selection fails with `model_verification_failed` instead of silently continuing. Capacity is one non-terminal oracle job. A `capacity` error includes the in-flight job ID; poll that job or wait for it to finish rather than submitting the same consult again. ChatGPT can hide the model version at lower effort settings. Use `--model latest` if floating model selection is intended; do not retry an unverifiable `gpt-6-astra` request as `latest` without the user's approval. When loaded as a Pi extension, Surf also registers a `surf-oracle` external-job provider when the runtime exposes that bridge. The provider maps `start`, `status`, `result`, and `reattach` to durable Surf Oracle jobs and returns pi-subagents' external-job contract shape: `providerJobId`, a contract state (`queued`, `running`, `completed`, `failed`), the conversation URL, the captured result text as `output`, and failure code and message. It honors `options.model`, `options.effort`, `options.file`, and `options.github` for starts and follow-ups, so `model: gpt-6-astra` plus `effort: pro` selects ChatGPT GPT-6 Astra with Pro effort through the browser, while `github: true` requires Chat mode and the connected GitHub tool. `reattach` only harvests an existing job by ID; it never submits the prompt again. When Surf is installed as a Pi package, it exposes an optional `gpt-pro` package agent for `pi-subagents`. That profile uses `runner.type: external-job`, provider `surf-oracle`, `options.model: gpt-6-astra`, and `options.effort: pro`. Surf remains useful without Pi or `pi-subagents`. Context comes from repeatable `--files` globs. Use `--file <path>` for one additional local attachment; `--github` requires Chat mode and a connected GitHub tool. Surf fails closed when a glob matches nothing or a matched file is unreadable, binary, or invalid UTF-8. It also blocks gitignored files and basenames matching `.env*`, `*.pem`, `*.key`, `id_rsa*`, `id_ed25519*`, `*.p12`, `*.pfx`, `credentials*`, or `secrets*`. Use `--allow-sensitive` only after intentionally reviewing those files; it overrides the block rather than redacting content. Context up to 60,000 evidence characters is inserted inline, while larger context becomes one private text attachment. The assembly manifest records each path, byte count, SHA-256, inline or bundle disposition, and deny-list outcome. Continue a captured consult with `follow`. Use the ID returned by each turn for the next turn: ```bash surf oracle follow <job-id> "Challenge your recommendation. What could invalidate it?" --file ./follow-up.md --github --detach --json surf oracle result <follow-job-id> --wait --json surf oracle follow <follow-job-id> "Give the final decision and concrete next steps." --detach --json ``` ### Gemini ```bash surf gemini "explain quantum computing" surf gemini "summarize" --with-page # Include page context surf gemini "analyze" --file data.csv # Attach file surf gemini "a robot surfing" --generate-image /tmp/robot.png surf gemini "add sunglasses" --edit-image photo.jpg --output out.jpg surf gemini "summarize" --youtube "https://youtube.com/..." surf gemini "hello" --model gemini-3.5-flash # Models: gemini-3.1-pro (default), gemini-3.5-flash, gemini-3.1-flash-lite surf gemini "wide banner" --generate-image /tmp/banner.png --aspect-ratio 16:9 ``` ### Perplexity ```bash surf perplexity "what is quantum computing" surf perplexity "explain this page" --with-page # Include page context surf perplexity "deep dive" --mode research # Research mode (Pro) surf perplexity "latest news" --model sonar # Model selection (Pro) ``` ### Grok (via x.com - requires X.com login in Chrome) ```bash surf grok "what are the latest AI trends on X" # Search X posts surf grok "analyze @username recent activity" # Profile analysis surf grok "summarize this page" --with-page # Include page context surf grok "find viral AI posts" --deep-search # DeepSearch mode surf grok "quick question" --model fast # Models: auto, fast, expert, grok-4.20-beta ``` For exhaustive, multi-angle X research with categorized findings and full post-URL traceability, use the `deep-x-research` skill (`skills/deep-x-research/`) instead of a single Grok query. **Grok Validation & Troubleshooting:** ```bash # Validate Grok UI and check available models (no query sent) surf grok --validate # If models changed, save discovered models to surf.json config surf grok --validate --save-models ``` ### AI Studio (via aistudio.google.com - requires Google login in Chrome) ```bash surf aistudio "explain quantum computing" surf aistudio "redteam this" --with-page # Include current page context surf aistudio "quick answer" --model gemini-3-flash-preview # Model selection surf aistudio "analyze" --timeout 600 # Custom timeout (default: 300s) ``` **Why AI Studio over Gemini?** AI Studio gives access to less restricted Gemini models. For Gemini 3 Pro the difference can be significant with certain prompts. Downside: aggressive per-day rate limits on Pro and Flash models. **Model selection is best-effort:** Pass any AI Studio model id (e.g. `gemini-3.1-pro-preview`, `gemini-3-flash-preview`, `gemini-flash-lite-latest`). If the model isn't found, AI Studio uses whatever model was last selected in the UI. ### AI Studio App Builder ```bash surf aistudio.build "build a portfolio site" surf aistudio.build "todo app" --model gemini-3.1-pro-preview # Model override surf aistudio.build "crm dashboard" --output ./out # Extract zip to directory surf aistudio.build "game" --keep-open --timeout 600 # Keep tab open, 10min timeout ``` Automates AI Studio's App Builder at `aistudio.google.com/apps`. Types your prompt, clicks Build, waits for completion, downloads the generated zip, and optionally extracts it. - `--output <dir>` extracts the zip to a directory - `--model <id>` overrides the model in Advanced Settings - `--timeout <seconds>` build timeout (default: 600s) - `--keep-open` leaves the AI Studio tab open after completion Returns `zipPath`, `extractedPath`, `model`, `buildDuration`, and `tookMs`. ### AI Tool Troubleshooting When AI queries fail, check these common issues: 1. **Not logged in**: The error "login required" means you need to log into the service in Chrome (chatgpt.com, gemini.google.com, perplexity.ai, x.com, or aistudio.google.com) 2. **Model selection failed**: The UI may have changed. Run `surf grok --validate` to check 3. **Response timeout**: Reasoning-heavy models (ChatGPT o1, Grok Expert) can take 45+ seconds. AI Studio builds can take several minutes. 4. **Element not found**: The service's UI changed. Check for surf-cli updates **Debugging workflow for agents:** ```bash # 1. Check if the service is accessible and UI is valid surf grok --validate # 2. If models mismatch, update the local settings surf grok --validate --save-models
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub