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 +276 -8
- package/dist/content/index.js +4 -4
- package/dist/content/index.js.map +1 -1
- package/dist/options/options.html +1 -18
- package/dist/service-worker/index.js +44 -15
- package/dist/service-worker/index.js.map +1 -1
- package/native/cli.cjs +430 -14
- package/native/do-executor.cjs +35 -8
- package/native/doctor.cjs +211 -42
- package/native/endpoint.cjs +121 -36
- package/native/extract.cjs +362 -0
- package/native/host-helpers.cjs +133 -11
- package/native/host-sessions.cjs +10 -0
- package/native/host.cjs +53 -23
- package/native/mcp-server.cjs +25 -0
- package/native/native-host-launch-probe.cjs +69 -0
- package/native/private-state.cjs +25 -1
- package/native/script-options.cjs +33 -0
- package/native/semantic-cli.cjs +764 -0
- package/native/semantic-core.cjs +369 -0
- package/native/semantic-credentials.cjs +207 -0
- package/native/semantic-provider.cjs +61 -0
- package/native/semantic-workflow-executor.cjs +65 -0
- package/native/semantic-workflow-state.cjs +271 -0
- package/native/semantic-workflow.cjs +398 -0
- package/native/stdin-frames.cjs +33 -0
- package/native/tool-scope.cjs +3 -2
- package/native/workflow-definition.cjs +125 -1
- package/native/workflow-runtime.cjs +71 -5
- package/package.json +11 -7
- package/scripts/install-native-host.cjs +103 -60
- package/scripts/uninstall-native-host.cjs +40 -38
- package/scripts/windows-interop.cjs +89 -0
- package/skills/surf/SKILL.md +96 -1
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
|
|
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
|
-
|
|
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 #
|
|
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
|
|
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
|
|
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` |
|