surf-cli 2.18.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,6 +135,30 @@ SURF_REMOTE_CREDENTIAL=~/.config/surf/agent-macbook.json \
135
135
  surf tab.list
136
136
  ```
137
137
 
138
+ For a TLS-terminating reverse proxy in front of the native host's existing clear-TCP
139
+ listener, opt in on the client:
140
+
141
+ ```bash
142
+ surf --remote surf.example.com:443 --remote-tls \
143
+ --remote-credential ~/.config/surf/agent-macbook.json tab.list
144
+
145
+ # Private CA and an IP destination with a DNS certificate identity
146
+ surf --remote 100.101.102.103:443 --remote-tls \
147
+ --remote-tls-ca ~/.config/surf/private-ca.pem \
148
+ --remote-tls-server-name surf.example.com \
149
+ --remote-credential ~/.config/surf/agent-macbook.json tab.list
150
+ ```
151
+
152
+ `--remote-tls-ca` replaces Node's system roots rather than adding to them. DNS endpoints
153
+ use their hostname for SNI and certificate validation; IP endpoints omit SNI and validate
154
+ the certificate's IP SAN unless `--remote-tls-server-name` supplies a DNS identity. TLS
155
+ certificate validation finishes before the mandatory Ed25519 authentication. Surf has no
156
+ insecure mode, downgrade, or plaintext retry. `SURF_REMOTE_TLS=1` is the only accepted
157
+ environment spelling; unset it to disable TLS. There is no CLI negation for env-enabled TLS.
158
+ CLI values override `SURF_REMOTE_TLS_CA` and `SURF_REMOTE_TLS_SERVER_NAME` independently.
159
+ `SURF_LISTEN` remains a plaintext listener behind the reverse proxy; Surf does not terminate
160
+ TLS on the browser host.
161
+
138
162
  Surf performs mutual Ed25519 challenge-response with fresh nonces and checks authorization throughout the connection. A credential grants the same browser and host-file authority as a trusted local Surf user. Give each client its own credential, do not share it, and revoke it immediately if the client or file is lost:
139
163
 
140
164
  ```bash
@@ -162,7 +186,9 @@ Keep Tailscale policy restrictions as defense in depth. For example:
162
186
  }
163
187
  ```
164
188
 
165
- Adapt tags and ports to your Tailnet. Surf authentication does not replace Tailnet policy, and Surf does not add a separate TLS or SSH tunnel.
189
+ Adapt tags and ports to your Tailnet. Surf authentication does not replace Tailnet policy.
190
+ Optional outbound remote TLS protects the client-to-proxy connection; Surf does not add an
191
+ SSH tunnel or a TLS listener.
166
192
 
167
193
  **Operations and troubleshooting**
168
194
 
@@ -280,6 +306,13 @@ surf locate.role button --action click
280
306
  surf frame.main # Return to main page
281
307
  ```
282
308
 
309
+ When a selector never matches, `frame.diagnose` shows the three frame views side by side (DOM `<iframe>` elements, the extension's frames with content-script reachability, and the CDP frame tree) and explains the mismatches: `srcdoc`/`about:blank` frames (matched to their CDP frame by `name`/`id`), sandboxes without `allow-scripts`, cross-origin frames, out-of-process frames that the CDP tree does not list (`frame.js` cannot reach them; `frame.switch` and `page.read` can when the content script answers), and frames still loading. The DOM inventory walks open shadow roots, so frames rendered by custom elements are listed with their `shadowHost` path. The text report abbreviates long frame URLs; `--json` keeps them whole.
310
+
311
+ ```bash
312
+ surf frame.diagnose # Human-readable report with warnings
313
+ surf frame.diagnose --json # Full inventories
314
+ ```
315
+
283
316
  ### Interaction
284
317
 
285
318
  ```bash
@@ -569,12 +602,55 @@ surf wait 2 # Wait 2 seconds
569
602
  surf wait.element ".loaded" # Wait for element
570
603
  surf wait.network # Wait for network idle
571
604
  surf wait.url "/dashboard" # Wait for URL pattern
605
+ surf wait.ready --selector ".results" # Wait for content, fail fast on a bounce
606
+ surf page.readiness --json # Classify the page once
572
607
  ```
573
608
 
609
+ `wait.ready` polls with a bounded budget and reports a typed state instead of timing out silently: `ready`, `empty` (the page showed its own no-results message, `--empty-text`), or one of the negative states `login`, `challenge` (anti-bot interstitial), `not-found`, `error`. A negative state exits non-zero with codes `page_login`, `page_challenge`, `page_not_found`, `page_error`; `page_timeout` reports the last observed state. Pass `--accept login` to return a state to the caller instead. Detection uses visible UI state (a rendered password field, a login-looking route, the page's own wording, `--url-prefix` bounces), never site-specific selectors.
610
+
611
+ ```bash
612
+ surf wait.ready --url-prefix "https://app.example.com/" --empty-text "No results"
613
+ surf wait.ready --accept login --json # {"state":"login","evidence":[...]} instead of an error
614
+ ```
615
+
616
+ ### Extracting structured data
617
+
618
+ `surf extract` composes existing client-side tools: it opens an owned tab,
619
+ waits for explicit readiness, runs a page script, validates its JSON result,
620
+ and closes the tab. It prints concise Markdown by default or structured JSON
621
+ with `--json`. Return an array, or an object containing a conventional row key
622
+ such as `rows`, `items`, or `results`; use `--rows <key>` for another key.
623
+
624
+ ```bash
625
+ surf extract "https://example.com/list" --file rows.js --ready-selector ".item"
626
+ surf extract "https://example.com/search" --file rows.js --options '{"limit":20}' --empty-text "No results" --json
627
+ surf extract --tab-id 42 --code 'return [...document.querySelectorAll("h2")].map(h => ({title: h.textContent}))'
628
+ ```
629
+
630
+ Owned-tab failures always attempt cleanup. Zero rows retry unless
631
+ `--allow-empty` is set or `--empty-text` identifies the page's accepted empty
632
+ state. Fresh-tab retries are bounded (`--retry`, default 1, maximum 5) and are
633
+ limited to readiness timeouts, zero rows, and lost tab/execution-context
634
+ failures. Login, challenge, not-found, page-error, script/output, and cleanup
635
+ failures do not retry. `--tab-id` and `--session` target an existing page and
636
+ never retry or close it; `--keep-tab` preserves a successfully owned tab.
637
+
638
+ Extract is intended for read-only or otherwise idempotent caller scripts.
639
+ JavaScript is not inherently read-only: a retry can replay the script, so avoid
640
+ mutations or make them idempotent.
641
+
574
642
  ### Other
575
643
 
644
+ `js` and `frame.js` accept `--options '{"limit": 20}'` with inline code or
645
+ `--file`. This defines `SURF_OPTIONS` by parsing the JSON and freezing the
646
+ result; use an explicit `return` for the script's result. The freeze is shallow.
647
+ Invalid JSON and non-object values are rejected before sending a request;
648
+ `--options ''` defines an empty object. Without `--options`, code is unchanged.
649
+
576
650
  ```bash
577
651
  surf js "return document.title" # Execute JavaScript
652
+ surf js "piHelpers.setValue(document.querySelector('#q'), 'hello')" # Native value setter + input/change events
653
+ surf js --file script.js --options '{"limit": 20}' # Script reads SURF_OPTIONS.limit
578
654
  surf record --duration 2000 --fps 10 --output /tmp/anim.gif # Animated GIF capture
579
655
  surf animate-audit --selector ".thing" --duration 2000 --fps 10 # JSON animation timeline
580
656
  surf perf-audit --duration 3000 --output /tmp/perf.json # PerformanceObserver snapshot
@@ -650,6 +726,9 @@ surf do 'go "url" | click e5 | screenshot' --dry-run
650
726
  - `--step-delay <ms>` - Delay between steps (default: 100, use 0 to disable)
651
727
  - `--no-auto-wait` - Disable automatic waits between steps
652
728
  - `--json` - Output structured JSON result
729
+ - `--allow-semantic` - Opt in to bounded TypeSafe decisions for a semantic workflow
730
+ - `--allow-write` - Additionally authorize declared `fill`, `ensureChecked`, and `click` steps
731
+ - `--inputs-stdin` - Read one bounded JSON object of private local input slots from stdin
653
732
  - `--<arg> <value>` - Pass arguments to workflow (e.g., `--url "..."`)
654
733
 
655
734
  **Auto-waits:** Commands that trigger page changes automatically wait for completion:
@@ -744,7 +823,67 @@ surf workflow.info my-workflow
744
823
  surf workflow.validate ./my-workflow.json
745
824
  ```
746
825
 
747
- **Supported commands:** All surf commands work in workflows. Use aliases (`go`, `snap`, `read`) or full names (`navigate`, `screenshot`, `page.read`).
826
+ #### Bounded semantic workflow steps
827
+
828
+ Semantic workflow files declare `"semantic": { "version": 1 }` and use only
829
+ linear `semantic.step` operations: `find`, `open`, `ensureChecked`, `fill`,
830
+ `click`, and `assert`. Validate or dry-run them offline, then opt in explicitly:
831
+
832
+ ```bash
833
+ surf workflow.validate ./product.json
834
+ surf do --file product.json --dry-run
835
+ printf '%s' '{"quantity":"2"}' | SURF_SESSION=shopping surf do \
836
+ --file product.json --inputs-stdin --allow-semantic --allow-write --json
837
+ ```
838
+
839
+ ```json
840
+ {
841
+ "name": "configure-matching-product",
842
+ "semantic": { "version": 1, "deadlineMs": 60000, "maxProviderCalls": 32 },
843
+ "steps": [
844
+ { "id": "find-product", "tool": "semantic.step", "as": "product", "args": {
845
+ "op": "find", "target": { "query": "The in-stock blue insulated bottle", "role": "link" },
846
+ "search": { "mode": "scroll", "maxObservations": 12 }
847
+ } },
848
+ { "id": "open-product", "tool": "semantic.step", "args": {
849
+ "op": "open", "target": { "binding": "product" }
850
+ } },
851
+ { "id": "select-gift-wrap", "tool": "semantic.step", "args": {
852
+ "op": "ensureChecked", "target": { "query": "Gift wrap", "role": "checkbox" },
853
+ "checked": true
854
+ } },
855
+ { "id": "set-quantity", "tool": "semantic.step", "args": {
856
+ "op": "fill", "target": { "query": "Quantity", "role": "spinbutton" },
857
+ "input": "quantity"
858
+ } },
859
+ { "id": "add-to-cart", "tool": "semantic.step", "args": {
860
+ "op": "click", "target": { "query": "Add to cart", "role": "button" },
861
+ "expect": { "kind": "visible", "target": { "query": "Remove from cart", "role": "button" } }
862
+ } },
863
+ { "id": "verify-cart", "tool": "semantic.step", "args": {
864
+ "op": "assert", "mode": "semantic",
865
+ "claim": "The cart contains the selected product with gift wrap enabled",
866
+ "bindings": ["product"]
867
+ } }
868
+ ]
869
+ }
870
+ ```
871
+
872
+ This example includes every supported operation and the required `claim` for a
873
+ semantic `assert`. Use it as a valid starting shape, then remove steps the task
874
+ does not need.
875
+
876
+ `open` performs freshly validated same-origin HTTP(S) navigation; it never falls
877
+ back to a click. Model-derived writes retain the `0.95` gate, dispatch at most
878
+ once in a run, and require local/read-only verification. A stopped or uncertain
879
+ step fails the workflow. Search reports bounded overlapping coverage and does
880
+ not prove global ranking or absence outside that scope. Local input values are
881
+ sent only to their browser fill/compare operation, not to TypeSafe, workflow
882
+ variables, events, output, or checkpoints. A new run can repeat an external
883
+ effect: Surf does not claim exactly-once server behavior or automatic resume.
884
+
885
+ **Supported commands:** Ordinary workflows support all Surf commands. Semantic
886
+ v1 workflows intentionally support only the six closed operations above.
748
887
 
749
888
  ### Playbooks
750
889
 
@@ -811,21 +950,142 @@ Generated manifests declare provenance and authentication environment inputs. Su
811
950
  --window-id <id> # Target a specific window
812
951
  --no-wait # Return tab_busy/browser_busy instead of queueing
813
952
  --json # Raw JSON including resolved target metadata
814
- --soft-fail # Warn instead of error (exit 0) on restricted pages
953
+ --soft-fail # Host tool errors: stderr warning, exit 0, no JSON error output
815
954
  --no-lock # Bypass the legacy lock for compound client-side commands
816
955
  --no-screenshot # Skip auto-screenshot after actions
817
956
  --full # Full resolution screenshots (skip resize)
818
957
  ```
819
958
 
959
+ ### Host tool-response errors
960
+
961
+ For ordinary socket-backed commands, a host response with a top-level `error`
962
+ exits 1 and prints `Error: ...` on stderr. A supplied code is appended as `[code]`
963
+ to the first line unless already present there; subsequent recovery lines are
964
+ preserved. Without a code, no suffix is added.
965
+
966
+ `--json` additionally writes `{"error":{"code":"...","message":"...","details":{...}}}`
967
+ to stdout, while retaining stderr and exit 1. The JSON code defaults to `"error"`;
968
+ the message uses the host's message, or the first display line if absent. Optional
969
+ details retain the host's fields except redundant `code` and `message` fields.
970
+
971
+ `--soft-fail` takes precedence: the original host display text is printed as a
972
+ stderr warning, without adding a code, stdout stays empty even with `--json`,
973
+ and the command exits 0. This is **not a universal JSON error envelope**: local
974
+ validation, transport/parser failures, compound commands and errors embedded in
975
+ successful result payloads retain their existing behavior. In particular, a
976
+ connection failure still prints stderr, leaves stdout empty and exits 1 with
977
+ `--json`, even with `--soft-fail`.
978
+
979
+ ## Optional Jev semantic commands
980
+
981
+ `semantic.act` is a bounded, goal-driven website controller. Give it an outcome
982
+ and it repeatedly observes the current page, asks Jev to select the next action
983
+ from Surf's allowed menu, validates and executes that action, then checks whether
984
+ the overall goal is complete. It stops when the goal is satisfied, a decision is
985
+ uncertain, or a step, provider-call, or time budget is exhausted.
986
+
987
+ ```text
988
+ agent goal
989
+ |
990
+ v
991
+ Surf observes -> Jev selects -> Surf validates + acts -> Jev checks goal
992
+ ^ |
993
+ +---------------- goal incomplete -----------------------+
994
+ |
995
+ complete / uncertain / budget reached
996
+ |
997
+ v
998
+ return result
999
+ ```
1000
+
1001
+ The other semantic commands expose individual parts of that loop:
1002
+
1003
+ ```text
1004
+ semantic.find select one control matching a goal
1005
+ semantic.filter rank the page regions relevant to a goal
1006
+ semantic.verify check whether one outcome is visible
1007
+ semantic.act run the bounded observe/choose/act/verify loop
1008
+ ```
1009
+
1010
+ This is most useful when the agent does not yet know a site's structure or happy
1011
+ path: Jev handles next-action selection and goal verification while Surf builds
1012
+ the allowed action menu and enforces permissions, confidence thresholds, and
1013
+ element freshness. The agent owns the goal and final confirmation. Once the path
1014
+ is known and stable, deterministic Surf commands are usually faster and more
1015
+ reliable for repeated execution.
1016
+
1017
+ Semantic commands are an explicit remote-AI boundary: only `surf semantic.*`
1018
+ sends a bounded, value-free current-page observation to TypeSafe. Existing Surf
1019
+ commands do not read a TypeSafe credential, load the SDK, or make provider calls.
1020
+
1021
+ ```bash
1022
+ surf semantic.find "the control for notification preferences"
1023
+ surf semantic.verify "Notification preferences were saved" --json
1024
+ surf semantic.filter "notification preferences" --top 6
1025
+ surf semantic.act "Open notification settings" --max-steps 5
1026
+ surf semantic.act "Fill the email field" --input email="$EMAIL" --allow-write
1027
+ surf semantic.act 'Add the selected item to the cart' --allow-write --threshold write=0.85
1028
+ surf semantic auth set # hidden prompt, or exactly one stdin line
1029
+ surf semantic auth status # source and redacted fingerprint only
1030
+ surf semantic auth clear # removes the shared credential for all clients
1031
+
1032
+ # Ephemeral/CI override (highest precedence; does not modify the stored key)
1033
+ TYPESAFE_API_KEY="$CI_TYPESAFE_KEY" surf semantic.find "the checkout link"
1034
+
1035
+ # Non-interactive persisted setup (exactly one bounded line on stdin)
1036
+ printf '%s\n' "$TYPESAFE_KEY" | surf semantic auth set
1037
+ ```
1038
+
1039
+ The provider-neutral shared schema is `{"version":1,"apiKey":"..."}`. Persisted
1040
+ setup lives at `${XDG_CONFIG_HOME:-~/.config}/typesafe/credentials.json` on
1041
+ Unix/macOS and `%APPDATA%\TypeSafe\credentials.json` on Windows, independent of
1042
+ `SURF_STATE_DIR`, project, and cwd. On POSIX, directories use mode `0700` and the
1043
+ file mode `0600`; writes are atomic and symlinked paths are rejected. Windows
1044
+ uses the current user's profile and ACL semantics. A nonblank `TYPESAFE_API_KEY`
1045
+ always wins over the shared file; `auth status` prints only `environment`,
1046
+ `shared-store`, or `not-configured` plus a short fingerprint. `auth clear`
1047
+ removes the shared file for every client that uses it while an environment
1048
+ override remains effective. Keys are never accepted on argv or from
1049
+ `surf.json`, project config, or auto-loaded `.env` files, and are never sent to
1050
+ the host/extension or included in logs, errors, or JSON output. Install,
1051
+ configuration, doctor, startup, and non-semantic commands never prompt for a key
1052
+ or load the TypeSafe SDK.
1053
+
1054
+ `semantic.act` is bounded to observed same-origin HTTP(S) links, fixed scrolling
1055
+ and waits, and observed refs. Every DOM click and fill is mutation-capable and is
1056
+ excluded unless `--allow-write` is present. That flag intentionally permits
1057
+ high-impact submit, purchase, delete, send, and publish controls; repeat
1058
+ `--allow-ref <ref>` to narrow authorization to exact current refs. Fill values
1059
+ come only from named `--input name=value` slots and are never sent to TypeSafe or
1060
+ included in traces. Broad and ambiguous writes require probability `0.95`. The
1061
+ threshold is `0.65` only when exactly one `--allow-ref` names exactly one
1062
+ applicable click, or one fill with one input slot; the applied threshold appears
1063
+ in decision/trace output and never grants authority. Repeatable
1064
+ `--threshold name=value` overrides applicable confidence thresholds for one run
1065
+ only; defaults remain safer, and overrides never replace `--allow-write` or
1066
+ `--allow-ref` authority. Actions are
1067
+ freshness-guarded and uncertain writes are not replayed. Each provider choice is
1068
+ capped at 70 actions: six fixed scroll/wait
1069
+ actions plus at least one action for each of the 64 observed refs explicitly
1070
+ authorized with `--allow-ref`; additional variants are omitted deterministically.
1071
+ An oversized mandatory authorized set fails before provider selection. Page text
1072
+ remains adversarial data; model output never grants authority.
1073
+
1074
+ The real-Jev evaluation harness is opt-in and excluded from CI:
1075
+ `SURF_REAL_JEV=1 TYPESAFE_API_KEY=... npm run eval:jev`.
1076
+
820
1077
  ## Environment Variables
821
1078
 
822
1079
  ```bash
823
1080
  SURF_NETWORK_PATH # Native-host network state root (default: ~/.surf/state/network)
824
- SURF_STATE_DIR # Private Surf state root, including browser sessions (default: ~/.surf/state)
1081
+ SURF_STATE_DIR # Private Surf state root; does not affect shared TypeSafe credentials
825
1082
  SURF_SESSION # Default named browser session for tab-scoped commands
826
1083
  SURF_SOCKET # Socket path or named pipe (default: /tmp/surf.sock, Windows: //./pipe/surf)
827
1084
  SURF_REMOTE # Remote Surf endpoint as host:port (overrides SURF_SOCKET)
828
1085
  SURF_REMOTE_CREDENTIAL # Client Ed25519 credential for the selected remote endpoint
1086
+ SURF_REMOTE_TLS # Exactly 1 enables TLS for a selected remote endpoint
1087
+ SURF_REMOTE_TLS_CA # Custom CA bundle that replaces system roots
1088
+ SURF_REMOTE_TLS_SERVER_NAME # DNS SNI and certificate identity override
829
1089
  SURF_REMOTE_STATE_DIR # Host identity/authorization directory (default: ~/.surf/remote)
830
1090
  SURF_LISTEN # Native-host Tailnet bind address as <tailscale-ip>:<port>
831
1091
  SURF_SOCKET_MODE # Advanced POSIX local socket mode: 600 (default) or 660
@@ -833,6 +1093,9 @@ SURF_SOCKET_GROUP # Group name or numeric gid required with mode 660
833
1093
  SURF_NODE_PATH # Path to node binary (for native host wrapper)
834
1094
  SURF_HOST_PATH # Path to native/host.cjs (for native host wrapper)
835
1095
  SURF_EXTENSION_PATH # Path to extension dist/ directory
1096
+ TYPESAFE_API_KEY # Optional semantic-command credential; overrides the shared store
1097
+ SURF_JEV_MODEL # Optional observable Jev model override (default: jev-1.13.0)
1098
+ XDG_CONFIG_HOME # Unix/macOS base for shared TypeSafe credentials (default: ~/.config)
836
1099
  ```
837
1100
 
838
1101
  **Use cases:**
@@ -841,11 +1104,16 @@ SURF_EXTENSION_PATH # Path to extension dist/ directory
841
1104
  - `SURF_SOCKET`: Advanced socket override. Set it for both the native host and CLI when separate browser/profile instances need hard isolation.
842
1105
  - `SURF_REMOTE`: Remote client endpoint. `--remote <host>:<port>` overrides it; both override `SURF_SOCKET`.
843
1106
  - `SURF_REMOTE_CREDENTIAL`: Credential used for mutual remote authentication. `--remote-credential <path>` overrides it.
1107
+ - `SURF_REMOTE_TLS`: Set exactly `1` for TLS through a terminating reverse proxy; `--remote-tls` also enables it and cannot negate an env-enabled setting.
1108
+ - `SURF_REMOTE_TLS_CA`: CA bundle for remote TLS, replacing system roots. `--remote-tls-ca <path>` overrides it.
1109
+ - `SURF_REMOTE_TLS_SERVER_NAME`: DNS SNI and certificate identity override. `--remote-tls-server-name <name>` overrides it.
844
1110
  - `SURF_REMOTE_STATE_DIR`: Advanced host-side override for the mode-0700 identity and client registry directory.
845
1111
  - `SURF_LISTEN`: Native-host listener address on the browser machine. Use `surf install ... --listen <tailscale-ip>:<port>` to persist it in that host's wrapper.
846
1112
  - `SURF_SOCKET_MODE` / `SURF_SOCKET_GROUP`: Advanced POSIX native-host settings. Use `surf install ... --socket-mode 660 --socket-group <group>` to persist group access; mode `660` grants full Surf authority to every member of that group.
847
1113
  - `SURF_NODE_PATH` / `SURF_HOST_PATH`: Package manager installs (e.g., Nix) that store binaries in non-standard locations
848
1114
  - `SURF_EXTENSION_PATH`: Package managers that create stable symlinks instead of changing paths on reinstall
1115
+ - `TYPESAFE_API_KEY`: Used only by explicit `semantic.*` networked commands. Otherwise use `surf semantic auth set` for the provider-neutral shared store.
1116
+ - `SURF_JEV_MODEL`: Explicit model override for semantic commands; Surf otherwise pins `jev-1.13.0`.
849
1117
 
850
1118
  **Example (Nix):**
851
1119
  ```bash
@@ -879,7 +1147,7 @@ macOS checklist:
879
1147
  - Confirm the manifest `allowed_origins` entry uses the same extension ID shown on `chrome://extensions` for the Surf extension.
880
1148
  - Reinstall the manifest with `surf install <extension-id>` after copying a fresh extension build or if the extension ID changed.
881
1149
  - Fully restart Chrome, then reload the Surf extension on `chrome://extensions`.
882
- - Open the extension service worker from `chrome://extensions` and check its console for native messaging or socket errors.
1150
+ - Open Surf's service worker console from `chrome://extensions`. In Surf's **Details > Extension options**, enable **Debug Mode**, reproduce the failure, then disable **Debug Mode** when finished.
883
1151
  - If `SURF_SOCKET` is set in your shell, make sure Chrome launches the native host with the same value; otherwise both sides should use `/tmp/surf.sock`.
884
1152
  - Run a simple CLI command such as `surf tab.list`; if it fails, compare its `Attempted socket:` line with the socket expected by the native host.
885
1153
 
@@ -938,11 +1206,11 @@ echo '{"type":"tool_request","method":"execute_tool","params":{"tool":"tab.list"
938
1206
  | `window.*` | `new`, `list`, `focus`, `close`, `resize` |
939
1207
  | `tab.*` | `list`, `new`, `switch`, `close`, `name`, `unname`, `named`, `group`, `ungroup`, `groups`, `reload` |
940
1208
  | `scroll.*` | `top`, `bottom`, `to`, `info` |
941
- | `page.*` | `read`, `text`, `state` |
1209
+ | `page.*` | `read`, `text`, `state`, `readiness` |
942
1210
  | `locate.*` | `role`, `text`, `label` |
943
1211
  | `element.*` | `styles` |
944
- | `frame.*` | `list`, `switch`, `main`, `js` |
945
- | `wait.*` | `element`, `network`, `url`, `dom`, `load` |
1212
+ | `frame.*` | `list`, `diagnose`, `switch`, `main`, `js` |
1213
+ | `wait.*` | `element`, `network`, `url`, `dom`, `load`, `ready` |
946
1214
  | `cookie` / `cookie.*` | `list`, `get`, `set`, `clear`, `delete` |
947
1215
  | `bookmark.*` | `add`, `remove`, `list` |
948
1216
  | `history.*` | `list`, `search` |