surf-cli 2.7.2 → 2.9.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 +208 -13
- package/dist/content/index.js +116 -0
- package/dist/content/index.js.map +1 -0
- package/dist/manifest.json +2 -11
- package/dist/options/options.js +3 -3
- package/dist/options/options.js.map +1 -1
- package/dist/service-worker/index.js +261 -61
- package/dist/service-worker/index.js.map +1 -1
- package/native/abort.cjs +65 -0
- package/native/ai-queue.cjs +64 -0
- package/native/aistudio-build.cjs +21 -13
- package/native/aistudio-client.cjs +40 -20
- package/native/browser-lock.cjs +169 -0
- package/native/chatgpt-client.cjs +63 -30
- package/native/cli.cjs +947 -460
- package/native/client-transport.cjs +168 -0
- package/native/config.cjs +2 -2
- package/native/do-executor.cjs +25 -51
- package/native/do-parser.cjs +12 -0
- package/native/doctor.cjs +633 -0
- package/native/endpoint.cjs +174 -0
- package/native/file-transfer.cjs +734 -0
- package/native/gemini-client.cjs +244 -88
- package/native/grok-client.cjs +321 -212
- package/native/host-helpers.cjs +88 -16
- package/native/host-sessions.cjs +283 -0
- package/native/host.cjs +811 -616
- package/native/listener.cjs +20 -0
- package/native/mcp-server.cjs +60 -62
- package/native/network-export.cjs +113 -0
- package/native/perplexity-client.cjs +46 -17
- package/native/remote-auth.cjs +279 -0
- package/native/remote-transport.cjs +337 -0
- package/native/request-pending.cjs +148 -0
- package/native/socket-path.cjs +46 -0
- package/package.json +11 -9
- package/scripts/install-native-host.cjs +184 -51
- package/scripts/uninstall-native-host.cjs +93 -15
- package/skills/README.md +11 -5
- package/skills/deep-x-research/SKILL.md +106 -0
- package/skills/surf/SKILL.md +77 -22
- package/dist/content/accessibility-tree.js +0 -11
- package/dist/content/accessibility-tree.js.map +0 -1
- package/dist/content/visual-indicator.js +0 -111
- package/dist/content/visual-indicator.js.map +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const os = require("os");
|
|
5
|
-
const { execSync } = require("child_process");
|
|
5
|
+
const { execFileSync, execSync } = require("child_process");
|
|
6
6
|
|
|
7
7
|
const HOST_NAME = "surf.browser.host";
|
|
8
8
|
|
|
@@ -12,43 +12,80 @@ const BROWSERS = {
|
|
|
12
12
|
darwin: "Library/Application Support/Google/Chrome/NativeMessagingHosts",
|
|
13
13
|
linux: ".config/google-chrome/NativeMessagingHosts",
|
|
14
14
|
win32: "Google\\Chrome",
|
|
15
|
+
wsl: "Google/Chrome/User Data/NativeMessagingHosts",
|
|
15
16
|
},
|
|
16
17
|
chromium: {
|
|
17
18
|
name: "Chromium",
|
|
18
19
|
darwin: "Library/Application Support/Chromium/NativeMessagingHosts",
|
|
19
20
|
linux: ".config/chromium/NativeMessagingHosts",
|
|
20
21
|
win32: "Chromium",
|
|
22
|
+
wsl: "Chromium/User Data/NativeMessagingHosts",
|
|
21
23
|
},
|
|
22
24
|
brave: {
|
|
23
25
|
name: "Brave",
|
|
24
26
|
darwin: "Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts",
|
|
25
27
|
linux: ".config/BraveSoftware/Brave-Browser/NativeMessagingHosts",
|
|
26
28
|
win32: "BraveSoftware\\Brave-Browser",
|
|
29
|
+
wsl: "BraveSoftware/Brave-Browser/User Data/NativeMessagingHosts",
|
|
27
30
|
},
|
|
28
31
|
edge: {
|
|
29
32
|
name: "Microsoft Edge",
|
|
30
33
|
darwin: "Library/Application Support/Microsoft Edge/NativeMessagingHosts",
|
|
31
34
|
linux: ".config/microsoft-edge/NativeMessagingHosts",
|
|
32
35
|
win32: "Microsoft\\Edge",
|
|
36
|
+
wsl: "Microsoft/Edge/User Data/NativeMessagingHosts",
|
|
33
37
|
},
|
|
34
38
|
arc: {
|
|
35
39
|
name: "Arc",
|
|
36
40
|
darwin: "Library/Application Support/Arc/User Data/NativeMessagingHosts",
|
|
37
41
|
linux: null,
|
|
38
42
|
win32: null,
|
|
43
|
+
wsl: null,
|
|
39
44
|
},
|
|
40
45
|
helium: {
|
|
41
46
|
name: "Helium",
|
|
42
47
|
darwin: "Library/Application Support/net.imput.helium/NativeMessagingHosts",
|
|
43
48
|
linux: null,
|
|
44
49
|
win32: null,
|
|
50
|
+
wsl: null,
|
|
45
51
|
},
|
|
46
52
|
};
|
|
47
53
|
|
|
48
|
-
function
|
|
49
|
-
|
|
54
|
+
function isWsl() {
|
|
55
|
+
if (process.platform !== "linux") return false;
|
|
56
|
+
if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return true;
|
|
57
|
+
try {
|
|
58
|
+
return /microsoft|wsl/i.test(fs.readFileSync("/proc/version", "utf8"));
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getWindowsEnv(name) {
|
|
65
|
+
try {
|
|
66
|
+
return execFileSync("cmd.exe", ["/c", "echo", `%${name}%`], { encoding: "utf8" })
|
|
67
|
+
.trim()
|
|
68
|
+
.replace(/\r/g, "");
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function windowsPathToWslPath(winPath) {
|
|
75
|
+
const normalized = winPath.replace(/\\/g, "/");
|
|
76
|
+
const match = normalized.match(/^([A-Za-z]):\/(.*)$/);
|
|
77
|
+
if (!match) return normalized;
|
|
78
|
+
return `/mnt/${match[1].toLowerCase()}/${match[2]}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getWrapperDir(target = process.platform) {
|
|
50
82
|
const home = os.homedir();
|
|
51
|
-
|
|
83
|
+
if (target === "wsl-windows") {
|
|
84
|
+
const localAppData = getWindowsEnv("LOCALAPPDATA");
|
|
85
|
+
if (!localAppData) return null;
|
|
86
|
+
return path.join(windowsPathToWslPath(localAppData), "surf-cli");
|
|
87
|
+
}
|
|
88
|
+
switch (process.platform) {
|
|
52
89
|
case "darwin":
|
|
53
90
|
return path.join(home, "Library/Application Support/surf-cli");
|
|
54
91
|
case "linux":
|
|
@@ -60,14 +97,31 @@ function getWrapperDir() {
|
|
|
60
97
|
}
|
|
61
98
|
}
|
|
62
99
|
|
|
63
|
-
function
|
|
64
|
-
const
|
|
100
|
+
function getWslWindowsManifestPath(browserConfig) {
|
|
101
|
+
const localAppData = getWindowsEnv("LOCALAPPDATA");
|
|
102
|
+
if (!localAppData || !browserConfig.wsl) return null;
|
|
103
|
+
return path.join(windowsPathToWslPath(localAppData), browserConfig.wsl, `${HOST_NAME}.json`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function removeManifest(browser, target) {
|
|
65
107
|
const browserConfig = BROWSERS[browser];
|
|
66
108
|
|
|
67
|
-
if (!browserConfig
|
|
68
|
-
|
|
109
|
+
if (!browserConfig) return null;
|
|
110
|
+
|
|
111
|
+
if (target === "wsl-windows") {
|
|
112
|
+
const manifestPath = getWslWindowsManifestPath(browserConfig);
|
|
113
|
+
if (!manifestPath) return null;
|
|
114
|
+
try {
|
|
115
|
+
fs.unlinkSync(manifestPath);
|
|
116
|
+
return manifestPath;
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
69
120
|
}
|
|
70
121
|
|
|
122
|
+
const platform = process.platform;
|
|
123
|
+
if (!browserConfig[platform]) return null;
|
|
124
|
+
|
|
71
125
|
if (platform === "win32") {
|
|
72
126
|
return removeWindowsRegistry(browser);
|
|
73
127
|
}
|
|
@@ -98,8 +152,8 @@ function removeWindowsRegistry(browser) {
|
|
|
98
152
|
}
|
|
99
153
|
}
|
|
100
154
|
|
|
101
|
-
function removeWrapperDir() {
|
|
102
|
-
const wrapperDir = getWrapperDir();
|
|
155
|
+
function removeWrapperDir(target) {
|
|
156
|
+
const wrapperDir = getWrapperDir(target);
|
|
103
157
|
if (!wrapperDir) return null;
|
|
104
158
|
|
|
105
159
|
try {
|
|
@@ -112,7 +166,7 @@ function removeWrapperDir() {
|
|
|
112
166
|
|
|
113
167
|
function parseArgs() {
|
|
114
168
|
const args = process.argv.slice(2);
|
|
115
|
-
const result = { browsers: ["chrome"], all: false };
|
|
169
|
+
const result = { browsers: ["chrome"], all: false, target: "auto" };
|
|
116
170
|
|
|
117
171
|
for (let i = 0; i < args.length; i++) {
|
|
118
172
|
const arg = args[i];
|
|
@@ -127,6 +181,8 @@ function parseArgs() {
|
|
|
127
181
|
} else if (arg === "--all" || arg === "-a") {
|
|
128
182
|
result.browsers = Object.keys(BROWSERS);
|
|
129
183
|
result.all = true;
|
|
184
|
+
} else if (arg === "--target") {
|
|
185
|
+
result.target = args[++i];
|
|
130
186
|
} else if (arg === "--help" || arg === "-h") {
|
|
131
187
|
printHelp();
|
|
132
188
|
process.exit(0);
|
|
@@ -145,18 +201,40 @@ Options:
|
|
|
145
201
|
-b, --browser Browser(s) to uninstall from (default: chrome)
|
|
146
202
|
Values: chrome, chromium, brave, edge, arc, helium, all
|
|
147
203
|
-a, --all Uninstall from all browsers and remove wrapper
|
|
204
|
+
--target Install target to remove: auto, linux, windows
|
|
205
|
+
On WSL2, auto removes Windows-browser manifests. Use linux for WSLg/Linux browsers.
|
|
148
206
|
|
|
149
207
|
Examples:
|
|
150
208
|
node uninstall-native-host.cjs
|
|
151
209
|
node uninstall-native-host.cjs --browser brave
|
|
152
210
|
node uninstall-native-host.cjs --all
|
|
211
|
+
node uninstall-native-host.cjs --target linux
|
|
153
212
|
`);
|
|
154
213
|
}
|
|
155
214
|
|
|
156
215
|
function main() {
|
|
157
|
-
const { browsers, all } = parseArgs();
|
|
216
|
+
const { browsers, all, target } = parseArgs();
|
|
217
|
+
|
|
218
|
+
if (!["auto", "linux", "windows"].includes(target)) {
|
|
219
|
+
console.error("Error: Invalid --target value. Expected auto, linux, or windows");
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const runningInWsl = isWsl();
|
|
224
|
+
if (target === "windows" && !runningInWsl && process.platform !== "win32") {
|
|
225
|
+
console.error("Error: --target windows is only supported on Windows or WSL2");
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (target === "linux" && process.platform !== "linux") {
|
|
230
|
+
console.error("Error: --target linux is only supported on Linux or WSL2");
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const effectiveTarget = runningInWsl && target !== "linux" ? "wsl-windows" : process.platform;
|
|
158
235
|
|
|
159
|
-
console.log(`Platform: ${process.platform}`);
|
|
236
|
+
console.log(`Platform: ${process.platform}${runningInWsl ? " (WSL2 detected)" : ""}`);
|
|
237
|
+
console.log(`Target: ${effectiveTarget === "wsl-windows" ? "Windows browser from WSL2" : effectiveTarget}`);
|
|
160
238
|
console.log("");
|
|
161
239
|
|
|
162
240
|
const removed = [];
|
|
@@ -168,7 +246,7 @@ function main() {
|
|
|
168
246
|
continue;
|
|
169
247
|
}
|
|
170
248
|
|
|
171
|
-
const result = removeManifest(browser);
|
|
249
|
+
const result = removeManifest(browser, effectiveTarget);
|
|
172
250
|
if (result) {
|
|
173
251
|
removed.push({ browser: BROWSERS[browser].name, path: result });
|
|
174
252
|
} else {
|
|
@@ -188,7 +266,7 @@ function main() {
|
|
|
188
266
|
}
|
|
189
267
|
|
|
190
268
|
if (all) {
|
|
191
|
-
const wrapperDir = removeWrapperDir();
|
|
269
|
+
const wrapperDir = removeWrapperDir(effectiveTarget);
|
|
192
270
|
if (wrapperDir) {
|
|
193
271
|
console.log(`\nRemoved wrapper directory: ${wrapperDir}`);
|
|
194
272
|
}
|
package/skills/README.md
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
# Surf Skills
|
|
2
2
|
|
|
3
|
-
This directory contains skill files for AI coding agents
|
|
3
|
+
This directory contains skill files for AI coding agents:
|
|
4
|
+
|
|
5
|
+
- **`surf/`** — the core browser-automation reference: every surf command, workflows, AI assistants, troubleshooting.
|
|
6
|
+
- **`deep-x-research/`** — a research procedure built on surf: exhaustive, multi-angle X (Twitter) research with categorized findings and full post-URL traceability. Requires x.com login in Chrome.
|
|
7
|
+
|
|
8
|
+
Install each skill folder the same way (symlink or copy).
|
|
4
9
|
|
|
5
10
|
## Pi Agent
|
|
6
11
|
|
|
7
|
-
To use
|
|
12
|
+
To use a skill with [Pi coding agent](https://github.com/badlogic/pi-mono):
|
|
8
13
|
|
|
9
14
|
```bash
|
|
10
15
|
# Option 1: Symlink (auto-updates)
|
|
11
16
|
ln -s "$(pwd)/skills/surf" ~/.agents/skills/surf
|
|
17
|
+
ln -s "$(pwd)/skills/deep-x-research" ~/.agents/skills/deep-x-research
|
|
12
18
|
|
|
13
19
|
# Option 2: Copy
|
|
14
|
-
cp -r skills/surf ~/.agents/skills/
|
|
20
|
+
cp -r skills/surf skills/deep-x-research ~/.agents/skills/
|
|
15
21
|
```
|
|
16
22
|
|
|
17
|
-
The
|
|
23
|
+
The skills will be available when pi detects browser automation or X research tasks.
|
|
18
24
|
|
|
19
25
|
## Other Agents
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
Each `SKILL.md` file can be adapted for other AI coding agents (Claude Code, Codex) or used as documentation for LLM prompts — copy the skill folder into the agent's skills directory (e.g. `~/.claude/skills/`, `~/.agents/skills/`).
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deep-x-research
|
|
3
|
+
description: Deep, exhaustive research on a topic across X (Twitter) by driving Grok (x.com/i/grok) through surf. Use when the user wants comprehensive X research on a concept, technique, trend, tool, or creator scene; needs categorized findings with every claim traceable to post URLs; or when a single Grok query is not enough.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deep X Research
|
|
7
|
+
|
|
8
|
+
Research a topic across X by putting Grok to work from multiple angles — it runs keyword and semantic X searches and watches videos natively — then deliver categorized findings where every claim is traceable to a post URL.
|
|
9
|
+
|
|
10
|
+
Requires: surf installed and connected (`surf doctor`), Chrome logged into x.com. Command reference: the `surf` skill or `surf --help`.
|
|
11
|
+
|
|
12
|
+
**Quota:** X caps Grok requests (typically 15 per 20 hours on a standard plan). Every `surf grok` call spends one. Budget the session before the first query and make each query do multi-angle work — never spend a request on what a quota-free step can answer.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
### 1. Decompose the topic and budget the queries
|
|
17
|
+
|
|
18
|
+
Break the topic into angles: showcases/examples, techniques & tutorials, tools, notable creators, community discussion — adapt to the topic. Plan a Grok budget of **4-8 queries** covering every angle (combine related angles into one query rather than spending two). Done when each angle is assigned to a budgeted query.
|
|
19
|
+
|
|
20
|
+
### 2. Grok sweep
|
|
21
|
+
|
|
22
|
+
Run the budgeted queries sequentially. Engineer each so Grok does the fan-out internally and returns traceable sources:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Broad pass — force multi-angle search and URLs
|
|
26
|
+
surf grok "Do deep research on TOPIC on X. Search both latest and top posts, keyword and semantic. Return the most relevant posts with full post URLs (x.com/user/status/ID) and a one-line description of each."
|
|
27
|
+
|
|
28
|
+
# Focused passes — one per remaining angle group
|
|
29
|
+
surf grok "TOPIC on X: tutorials, techniques, and the tools people use. Include post URLs for every example."
|
|
30
|
+
|
|
31
|
+
# Deepest pass — spend DeepSearch on the highest-value angle
|
|
32
|
+
surf grok "TOPIC: notable creators, how the trend is evolving, and the standout posts of the last 6 months. Post URLs required." --deep-search
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Record every post Grok cites: author, one-line gist, full `https://x.com/USER/status/ID` URL. If a response gives claims without URLs, the *next* query in the budget re-asks for sources — never leave an angle sourceless. Done when every planned angle has been queried and the final response adds no new relevant posts, or the budget is spent.
|
|
36
|
+
|
|
37
|
+
### 3. Video pass (visual topics)
|
|
38
|
+
|
|
39
|
+
When the topic involves video, editing, or visual style, spend 1-3 budgeted queries having Grok analyze the strongest video posts — it can watch X videos natively:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
surf grok "Analyze the videos in these posts: URL1 URL2 URL3 — for each, describe the techniques, pacing, and style, and why it works."
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Batch several URLs per query to conserve budget. Done when each analyzed video has notes on what it shows and why it matters for the topic.
|
|
46
|
+
|
|
47
|
+
### 4. Enrich and verify — quota-free
|
|
48
|
+
|
|
49
|
+
For each cited post, open it directly with surf (no Grok spend) to verify the URL resolves and harvest detail Grok didn't give:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
surf navigate "https://x.com/USER/status/ID" && surf wait 2
|
|
53
|
+
surf page.read --compact # engagement numbers, thread context
|
|
54
|
+
surf network | grep video.twimg # direct video URL after playback
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Done when every URL destined for the References section has been resolved (dead or hallucinated links dropped or replaced).
|
|
58
|
+
|
|
59
|
+
### 5. Categorize and analyze
|
|
60
|
+
|
|
61
|
+
Group findings into categories that fit the topic. Extract trends: momentum on X, recurring techniques, notable creators, how the topic is evolving. Done when every recorded post is either placed in a category or deliberately dropped as irrelevant.
|
|
62
|
+
|
|
63
|
+
### 6. Report with full traceability
|
|
64
|
+
|
|
65
|
+
```md
|
|
66
|
+
# Deep Research on [Topic]
|
|
67
|
+
|
|
68
|
+
## Summary
|
|
69
|
+
[2-4 paragraphs: state of the topic on X]
|
|
70
|
+
|
|
71
|
+
## Key Trends
|
|
72
|
+
- ...
|
|
73
|
+
|
|
74
|
+
## Categorized Findings
|
|
75
|
+
### [Category]
|
|
76
|
+
- [Finding with inline post reference]
|
|
77
|
+
|
|
78
|
+
## Notable Creators & Techniques
|
|
79
|
+
- ...
|
|
80
|
+
|
|
81
|
+
## References
|
|
82
|
+
1. [Author — one-line description]
|
|
83
|
+
https://x.com/USER/status/ID
|
|
84
|
+
Video: https://video.twimg.com/... (when captured)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The report is done when **every post mentioned anywhere in it appears in References with its full, verified URL** — no bare @handles, no "a viral post showed…" without a link.
|
|
88
|
+
|
|
89
|
+
## Fallback: direct search when the Grok quota is exhausted
|
|
90
|
+
|
|
91
|
+
The x.com search UI costs no Grok requests. Slower and keyword-only, but it keeps the sweep going:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
surf navigate "https://x.com/search?q=QUERY&f=live" && surf wait 3 # Latest
|
|
95
|
+
surf page.read --compact
|
|
96
|
+
surf scroll down 2000 # then page.read again — repeat to load more
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Modes via the `f=` param: **Top is the default (no `f` param)** — there is no `f=top`; `f=live` = Latest, `f=user` = People, `f=media` = Media. Operators compose into the URL-encoded `q=`: `"exact phrase"`, `filter:videos`, `min_faves:100`, `min_retweets:50`, `from:user`, `since:2026-01-01`, `until:2026-06-01`.
|
|
100
|
+
|
|
101
|
+
## Troubleshooting
|
|
102
|
+
|
|
103
|
+
- Grok replies with a rate-limit message → quota exhausted; switch to the fallback sweep and tell the user when the quota resets.
|
|
104
|
+
- Grok queries fail outright → `surf grok --validate`, then retry with a model from the validation output (see the `surf` skill's AI troubleshooting section).
|
|
105
|
+
- Grok cites posts without URLs → re-ask in the next budgeted query; do not invent URLs.
|
|
106
|
+
- Search page shows a login wall → Chrome isn't logged into x.com; ask the user to log in.
|
package/skills/surf/SKILL.md
CHANGED
|
@@ -7,6 +7,36 @@ description: Control Chrome browser via CLI for testing, automation, and debuggi
|
|
|
7
7
|
|
|
8
8
|
Control Chrome browser via CLI or Unix socket.
|
|
9
9
|
|
|
10
|
+
## Native Host / Socket Notes
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
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, reload the extension, and inspect the extension service-worker console.
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
## Remote Surf
|
|
19
|
+
|
|
20
|
+
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:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
surf remote authorize agent-macbook --output ~/agent-macbook.surf-credential.json
|
|
24
|
+
surf install <extension-id> --listen 100.101.102.103:4321
|
|
25
|
+
surf remote list
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
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`:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
surf --remote 100.101.102.103:4321 \
|
|
32
|
+
--remote-credential ~/.config/surf/agent-macbook.json \
|
|
33
|
+
page.read
|
|
34
|
+
|
|
35
|
+
surf remote revoke agent-macbook # Run on the browser host
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
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.
|
|
39
|
+
|
|
10
40
|
## CLI Quick Reference
|
|
11
41
|
|
|
12
42
|
```bash
|
|
@@ -35,6 +65,9 @@ surf type --text "hello"
|
|
|
35
65
|
|
|
36
66
|
# 5. Screenshot
|
|
37
67
|
surf screenshot --output /tmp/shot.png
|
|
68
|
+
|
|
69
|
+
# Inspect animation/style changes as JSON
|
|
70
|
+
surf animate-audit --selector ".thing" --duration 2000 --fps 10
|
|
38
71
|
```
|
|
39
72
|
|
|
40
73
|
## AI Assistants (No API Keys)
|
|
@@ -57,7 +90,7 @@ surf gemini "analyze" --file data.csv # Attach file
|
|
|
57
90
|
surf gemini "a robot surfing" --generate-image /tmp/robot.png
|
|
58
91
|
surf gemini "add sunglasses" --edit-image photo.jpg --output out.jpg
|
|
59
92
|
surf gemini "summarize" --youtube "https://youtube.com/..."
|
|
60
|
-
surf gemini "hello" --model gemini-
|
|
93
|
+
surf gemini "hello" --model gemini-3.5-flash # Models: gemini-3.1-pro (default), gemini-3.5-flash, gemini-3.1-flash-lite
|
|
61
94
|
surf gemini "wide banner" --generate-image /tmp/banner.png --aspect-ratio 16:9
|
|
62
95
|
```
|
|
63
96
|
|
|
@@ -75,9 +108,11 @@ surf grok "what are the latest AI trends on X" # Search X posts
|
|
|
75
108
|
surf grok "analyze @username recent activity" # Profile analysis
|
|
76
109
|
surf grok "summarize this page" --with-page # Include page context
|
|
77
110
|
surf grok "find viral AI posts" --deep-search # DeepSearch mode
|
|
78
|
-
surf grok "quick question" --model fast # Models: auto, fast, expert,
|
|
111
|
+
surf grok "quick question" --model fast # Models: auto, fast, expert, grok-4.20-beta
|
|
79
112
|
```
|
|
80
113
|
|
|
114
|
+
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.
|
|
115
|
+
|
|
81
116
|
**Grok Validation & Troubleshooting:**
|
|
82
117
|
```bash
|
|
83
118
|
# Validate Grok UI and check available models (no query sent)
|
|
@@ -122,7 +157,7 @@ When AI queries fail, check these common issues:
|
|
|
122
157
|
|
|
123
158
|
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)
|
|
124
159
|
2. **Model selection failed**: The UI may have changed. Run `surf grok --validate` to check
|
|
125
|
-
3. **Response timeout**:
|
|
160
|
+
3. **Response timeout**: Reasoning-heavy models (ChatGPT o1, Grok Expert) can take 45+ seconds. AI Studio builds can take several minutes.
|
|
126
161
|
4. **Element not found**: The service's UI changed. Check for surf-cli updates
|
|
127
162
|
|
|
128
163
|
**Debugging workflow for agents:**
|
|
@@ -147,6 +182,7 @@ surf tab.list
|
|
|
147
182
|
surf tab.new "https://google.com"
|
|
148
183
|
surf tab.switch 12345
|
|
149
184
|
surf tab.close 12345
|
|
185
|
+
surf tab.move 12345 --to-window 67890
|
|
150
186
|
surf tab.reload # Reload current tab
|
|
151
187
|
|
|
152
188
|
# Named tabs (aliases)
|
|
@@ -165,6 +201,8 @@ surf tab.groups # List all tab groups
|
|
|
165
201
|
|
|
166
202
|
```bash
|
|
167
203
|
surf window.list # List all windows
|
|
204
|
+
surf resize 1280 720 # Resize current browser window
|
|
205
|
+
surf resize 1280 # Set current window width only
|
|
168
206
|
surf window.list --tabs # Include tab details
|
|
169
207
|
surf window.new # New window
|
|
170
208
|
surf window.new --url "https://example.com" # New window with URL
|
|
@@ -176,24 +214,31 @@ surf window.resize --id 123 --width 1920 --height 1080
|
|
|
176
214
|
surf window.resize --id 123 --state maximized # States: normal, minimized, maximized, fullscreen
|
|
177
215
|
```
|
|
178
216
|
|
|
179
|
-
**
|
|
217
|
+
**Multi-agent isolation:**
|
|
180
218
|
```bash
|
|
181
|
-
# Create
|
|
219
|
+
# Create a separate window for one agent and keep using its ID
|
|
182
220
|
surf window.new "https://example.com"
|
|
183
|
-
# Returns window ID, use with subsequent commands:
|
|
184
221
|
surf --window-id 123 tab.list
|
|
185
222
|
surf --window-id 123 go "https://other.com"
|
|
223
|
+
|
|
224
|
+
# Pin work to a specific tab, or name it for easier handoff
|
|
225
|
+
surf read --tab-id 456
|
|
226
|
+
surf tab.name agent-a --tab-id 456
|
|
227
|
+
surf tab.switch agent-a
|
|
186
228
|
```
|
|
187
229
|
|
|
230
|
+
Use `window.new`, `--window-id`, `--tab-id`, and named tabs to keep parallel agents on separate targets. Surf serializes non-streaming browser CLI requests per socket with a file-based lock, so agents sharing one native host wait instead of interleaving commands. Use `--no-lock` only for intentional bypasses. For hard isolation, run separate browser/profile instances with separate native hosts and `SURF_SOCKET` values; each socket gets its own lock. Surf does not yet have `session.new`, session IDs, or independent per-agent CDP sessions.
|
|
231
|
+
|
|
188
232
|
## Input Methods
|
|
189
233
|
|
|
190
234
|
```bash
|
|
191
|
-
# CDP method (real events)
|
|
235
|
+
# CDP method (real events) types at the current focus
|
|
192
236
|
surf type --text "hello"
|
|
193
237
|
surf click --x 100 --y 200
|
|
194
238
|
|
|
195
|
-
#
|
|
196
|
-
surf type
|
|
239
|
+
# Selector/ref targets use frame-aware DOM input
|
|
240
|
+
surf type "hello" --into "#input"
|
|
241
|
+
surf type "hello" --ref e5
|
|
197
242
|
|
|
198
243
|
# Keys
|
|
199
244
|
surf key Enter
|
|
@@ -210,9 +255,11 @@ surf drag --from-x 100 --from-y 100 --to-x 200 --to-y 200
|
|
|
210
255
|
```bash
|
|
211
256
|
surf page.read # Accessibility tree with refs + page text
|
|
212
257
|
surf page.read --no-text # Interactive elements only (no text content)
|
|
258
|
+
surf animate-audit --selector ".thing" --duration 2000 --fps 10 # JSON animation timeline
|
|
213
259
|
surf page.read --ref e5 # Get specific element details
|
|
214
260
|
surf page.read --depth 3 # Limit tree depth
|
|
215
261
|
surf page.read --compact # Minimal output for LLM efficiency
|
|
262
|
+
surf page.read --max-bytes 2000 # Cap visible text at a UTF-8 byte boundary
|
|
216
263
|
surf page.text # Plain text content only
|
|
217
264
|
surf page.state # Modals, loading state, scroll info
|
|
218
265
|
```
|
|
@@ -258,11 +305,13 @@ surf element.styles ".card" # Or by CSS selector
|
|
|
258
305
|
## Scrolling
|
|
259
306
|
|
|
260
307
|
```bash
|
|
261
|
-
surf scroll
|
|
262
|
-
surf scroll
|
|
263
|
-
surf scroll
|
|
308
|
+
surf scroll down 800 # Scroll down 800px
|
|
309
|
+
surf scroll up 400 # Scroll up 400px
|
|
310
|
+
surf scroll bottom # Scroll to bottom
|
|
311
|
+
surf scroll top # Scroll to top
|
|
312
|
+
surf scroll.bottom # Dot command form also works
|
|
313
|
+
surf scroll.top
|
|
264
314
|
surf scroll.to --ref e5 # Scroll element into view
|
|
265
|
-
surf scroll.by --y 200 # Scroll by amount
|
|
266
315
|
surf scroll.info # Get scroll position
|
|
267
316
|
```
|
|
268
317
|
|
|
@@ -395,6 +444,7 @@ surf screenshot # Auto-saves to /tmp/surf-snap-*.png
|
|
|
395
444
|
surf screenshot --output /tmp/shot.png # Save to specific file
|
|
396
445
|
surf screenshot --selector ".card" # Element only
|
|
397
446
|
surf screenshot --full-page # Full page scroll capture
|
|
447
|
+
surf screenshot --full-page /tmp/full.png # Full page saved to path
|
|
398
448
|
surf screenshot --no-save # Return base64 only, don't save file
|
|
399
449
|
```
|
|
400
450
|
|
|
@@ -409,11 +459,12 @@ surf zoom 1 # Reset to 100%
|
|
|
409
459
|
## Cookies & Storage
|
|
410
460
|
|
|
411
461
|
```bash
|
|
412
|
-
surf cookie
|
|
413
|
-
surf cookie
|
|
414
|
-
surf cookie
|
|
415
|
-
surf cookie
|
|
416
|
-
surf cookie
|
|
462
|
+
surf cookie list # List cookies for current page
|
|
463
|
+
surf cookie list --domain .google.com
|
|
464
|
+
surf cookie set --name "token" --value "abc123"
|
|
465
|
+
surf cookie get "token"
|
|
466
|
+
surf cookie clear --all # Clear all cookies
|
|
467
|
+
surf cookie delete "token" # Clear one cookie
|
|
417
468
|
```
|
|
418
469
|
|
|
419
470
|
## History & Bookmarks
|
|
@@ -558,13 +609,17 @@ surf wait.element ".missing" --auto-capture --timeout 2000
|
|
|
558
609
|
5. **Auto-capture for debugging** - `--auto-capture` saves diagnostics on failure
|
|
559
610
|
6. **AI tools use browser session** - Must be logged into the service (ChatGPT, Gemini, Perplexity, Grok, AI Studio), no API keys needed
|
|
560
611
|
7. **Grok validation** - Run `surf grok --validate` if queries fail to check UI changes
|
|
561
|
-
8. **Long timeouts for
|
|
612
|
+
8. **Long timeouts for reasoning-heavy models** - ChatGPT o1 and Grok Expert can take 60+ seconds. AI Studio builds default to 600s.
|
|
562
613
|
9. **AI Studio for unrestricted Gemini** - `surf aistudio` gives less filtered responses than `surf gemini` for the same models
|
|
563
614
|
10. **Use `surf do` for multi-step tasks** - Reduces token overhead and improves reliability
|
|
564
615
|
11. **Dry-run workflows first** - `surf do '...' --dry-run` validates without executing
|
|
565
|
-
12. **Window isolation** - Use `window.new` + `--window-id` to keep agent work separate from your browsing
|
|
566
|
-
13. **
|
|
567
|
-
14. **
|
|
616
|
+
12. **Window isolation** - Use `window.new` + `--window-id` or `--tab-id` to keep agent work separate from your browsing
|
|
617
|
+
13. **Request lock** - Non-streaming browser CLI requests serialize per socket; use `--no-lock` only when you intentionally want to bypass it
|
|
618
|
+
14. **Native host diagnostics** - If commands fail with socket/native-host errors, run `surf doctor` or `surf doctor --browser all` before guessing at reinstall steps
|
|
619
|
+
15. **Animation capture** - Use `surf record --duration 2000 --fps 10 --output /tmp/anim.gif` when the agent needs to see motion; use `animate-audit` for numeric timelines and `perf-audit` for jank/layout-shift snapshots
|
|
620
|
+
16. **Hard isolation** - Use separate browser/profile instances plus separate `SURF_SOCKET` values when agents must not share a host or target
|
|
621
|
+
17. **Semantic locators** - `locate.role`, `locate.text`, `locate.label` for more robust element finding
|
|
622
|
+
18. **Frame context** - Use `frame.switch` before interacting with iframe content
|
|
568
623
|
|
|
569
624
|
## Socket API
|
|
570
625
|
|