@docyrus/docyrus 0.0.59 → 0.0.62
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 +46 -0
- package/agent-loader.js +1 -1
- package/agent-loader.js.map +2 -2
- package/main.js +321 -25
- package/main.js.map +2 -2
- package/package.json +1 -1
- package/resources/browser-tools/browser-click.js +74 -0
- package/resources/browser-tools/browser-client.js +236 -0
- package/resources/browser-tools/browser-close.js +19 -0
- package/resources/browser-tools/browser-console.js +73 -0
- package/resources/browser-tools/browser-content.js +36 -75
- package/resources/browser-tools/browser-cookies.js +19 -14
- package/resources/browser-tools/browser-daemon.js +452 -0
- package/resources/browser-tools/browser-devtools.js +62 -0
- package/resources/browser-tools/browser-eval.js +16 -22
- package/resources/browser-tools/browser-fill.js +70 -0
- package/resources/browser-tools/browser-info.js +13 -0
- package/resources/browser-tools/browser-nav.js +21 -22
- package/resources/browser-tools/browser-network.js +91 -0
- package/resources/browser-tools/browser-run-script.js +12 -30
- package/resources/browser-tools/browser-screenshot.js +22 -22
- package/resources/browser-tools/browser-select.js +59 -0
- package/resources/browser-tools/browser-snapshot.js +100 -0
- package/resources/browser-tools/browser-start.js +101 -85
- package/resources/browser-tools/browser-tabs.js +38 -0
- package/resources/browser-tools/browser-wait.js +50 -0
- package/resources/pi-agent/extensions/browser-tools.ts +229 -0
- package/resources/pi-agent/skills/docyrus-chrome-devtools-cli/SKILL.md +157 -46
- package/server-loader.js +580 -232
- package/server-loader.js.map +4 -4
- package/resources/browser-tools/browser-connect.js +0 -172
- package/resources/browser-tools/browser-pick.js +0 -143
- package/resources/pi-agent/extensions/docyrus-web-browser.ts +0 -31
- package/resources/pi-agent/shared/docyrusWebBrowserProtocol.ts +0 -169
- package/resources/pi-agent/skills/agent-browser/SKILL.md +0 -779
- package/resources/pi-agent/skills/agent-browser/references/authentication.md +0 -303
- package/resources/pi-agent/skills/agent-browser/references/commands.md +0 -295
- package/resources/pi-agent/skills/agent-browser/references/profiling.md +0 -120
- package/resources/pi-agent/skills/agent-browser/references/proxy-support.md +0 -194
- package/resources/pi-agent/skills/agent-browser/references/session-management.md +0 -193
- package/resources/pi-agent/skills/agent-browser/references/snapshot-refs.md +0 -219
- package/resources/pi-agent/skills/agent-browser/references/video-recording.md +0 -173
- package/resources/pi-agent/skills/agent-browser/templates/authenticated-session.sh +0 -105
- package/resources/pi-agent/skills/agent-browser/templates/capture-workflow.sh +0 -69
- package/resources/pi-agent/skills/agent-browser/templates/form-automation.sh +0 -62
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ensureDaemon, cdp, evaluate, getMode, setSession } from "./browser-client.js";
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const switchIdx = args.indexOf("--switch");
|
|
7
|
+
const switchTo = switchIdx !== -1 ? parseInt(args[switchIdx + 1], 10) : null;
|
|
8
|
+
|
|
9
|
+
await ensureDaemon();
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const { targetInfos } = cdp("Target.getTargets");
|
|
13
|
+
const pages = targetInfos.filter((t) =>
|
|
14
|
+
t.type === "page" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-extension://") && !t.url.startsWith("devtools://"),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
if (switchTo !== null) {
|
|
18
|
+
if (switchTo < 0 || switchTo >= pages.length) {
|
|
19
|
+
console.error(`✗ Tab index ${switchTo} out of range (0-${pages.length - 1})`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const target = pages[switchTo];
|
|
23
|
+
cdp("Target.activateTarget", { targetId: target.targetId });
|
|
24
|
+
const { sessionId } = cdp("Target.attachToTarget", { targetId: target.targetId, flatten: true });
|
|
25
|
+
setSession(sessionId);
|
|
26
|
+
// Enable domains on new session
|
|
27
|
+
for (const domain of ["Page", "DOM", "Runtime", "Network"]) {
|
|
28
|
+
try { cdp(`${domain}.enable`, {}, sessionId); } catch {}
|
|
29
|
+
}
|
|
30
|
+
console.log(JSON.stringify({ mode: getMode(), switched: switchTo, url: target.url, title: target.title }));
|
|
31
|
+
} else {
|
|
32
|
+
const tabs = pages.map((t, i) => ({ index: i, url: t.url, title: t.title }));
|
|
33
|
+
console.log(JSON.stringify({ mode: getMode(), count: tabs.length, tabs }));
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error(`✗ Tabs failed: ${e.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ensureDaemon, evaluate, waitForCondition, getMode, drainEvents } from "./browser-client.js";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const idle = args.includes("--idle");
|
|
8
|
+
const selectorIdx = args.indexOf("--selector");
|
|
9
|
+
const urlIdx = args.indexOf("--url");
|
|
10
|
+
const timeoutIdx = args.indexOf("--timeout");
|
|
11
|
+
const selector = selectorIdx !== -1 ? args[selectorIdx + 1] : null;
|
|
12
|
+
const urlPattern = urlIdx !== -1 ? args[urlIdx + 1] : null;
|
|
13
|
+
const timeout = timeoutIdx !== -1 ? parseInt(args[timeoutIdx + 1], 10) : 15000;
|
|
14
|
+
const fixedDelay = args.find((a) => !a.startsWith("--") && /^\d+$/.test(a));
|
|
15
|
+
|
|
16
|
+
if (!idle && !selector && !urlPattern && !fixedDelay) {
|
|
17
|
+
console.log("Usage: browser-wait.js [--idle] [--selector <css>] [--url <pattern>] [<ms>] [--timeout <ms>]");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
await ensureDaemon();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
if (fixedDelay) {
|
|
25
|
+
const ms = parseInt(fixedDelay, 10);
|
|
26
|
+
execFileSync(process.execPath, ["-e", `setTimeout(()=>{},${ms})`], { timeout: ms + 1000 });
|
|
27
|
+
} else if (idle) {
|
|
28
|
+
// Wait for network idle: no network events for 500ms
|
|
29
|
+
const deadline = Date.now() + timeout;
|
|
30
|
+
let lastActivity = Date.now();
|
|
31
|
+
while (Date.now() < deadline) {
|
|
32
|
+
const evts = drainEvents();
|
|
33
|
+
const networkEvts = evts.filter((e) => e.method.startsWith("Network."));
|
|
34
|
+
if (networkEvts.length > 0) { lastActivity = Date.now(); }
|
|
35
|
+
if (Date.now() - lastActivity > 500) { break; }
|
|
36
|
+
execFileSync(process.execPath, ["-e", "setTimeout(()=>{},100)"], { timeout: 1000 });
|
|
37
|
+
}
|
|
38
|
+
} else if (selector) {
|
|
39
|
+
waitForCondition(`!!document.querySelector(${JSON.stringify(selector)})`, timeout);
|
|
40
|
+
} else if (urlPattern) {
|
|
41
|
+
const regex = "^" + urlPattern.replace(/\*\*/g, ".*").replace(/(?<!\.\*)\*/g, "[^/]*") + "$";
|
|
42
|
+
waitForCondition(`new RegExp(${JSON.stringify(regex)}).test(window.location.href)`, timeout);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const url = evaluate("window.location.href");
|
|
46
|
+
console.log(JSON.stringify({ mode: getMode(), waited: true, url }));
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.error(`✗ Wait failed: ${e.message}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser automation tools for the pi coding agent (desktop mode).
|
|
3
|
+
*
|
|
4
|
+
* Active when DOCYRUS_DESKTOP_TOOLS=1 env var is set (passed by --desktop flag).
|
|
5
|
+
*
|
|
6
|
+
* Uses pi agent's ctx.ui.input() which sends an extension_ui_request to the
|
|
7
|
+
* client and blocks until extension_ui_response arrives. The client detects
|
|
8
|
+
* the docyrus_browser_* prefix in the request title and executes the browser
|
|
9
|
+
* command via Electron webContents API / CDP.
|
|
10
|
+
*
|
|
11
|
+
* Flow:
|
|
12
|
+
* 1. Agent calls docyrus_browser_snapshot
|
|
13
|
+
* 2. execute() calls ctx.ui.input("docyrus_browser_snapshot", paramsJson)
|
|
14
|
+
* 3. Pi sends extension_ui_request { method: "input", title: "docyrus_browser_snapshot", placeholder: paramsJson }
|
|
15
|
+
* 4. Client detects docyrus_browser_ prefix, executes via Electron IPC
|
|
16
|
+
* 5. Client responds with extension_ui_response { value: resultJson }
|
|
17
|
+
* 6. ctx.ui.input() resolves → execute() returns result to agent
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { Type } from "@sinclair/typebox";
|
|
21
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
22
|
+
|
|
23
|
+
const TOOL_TIMEOUT_MS = 60_000;
|
|
24
|
+
|
|
25
|
+
function browserToolResult(output: unknown) {
|
|
26
|
+
const text = typeof output === "string" ? output : JSON.stringify(output, null, 2);
|
|
27
|
+
return { content: [{ type: "text" as const, text }] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function browserToolError(message: string) {
|
|
31
|
+
return { content: [{ type: "text" as const, text: message }], isError: true };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function createExecuteHandler(toolName: string) {
|
|
35
|
+
return async(
|
|
36
|
+
_toolCallId: string,
|
|
37
|
+
params: Record<string, unknown>,
|
|
38
|
+
signal: AbortSignal | undefined,
|
|
39
|
+
_onUpdate: unknown,
|
|
40
|
+
ctx: { ui: { input: (title: string, placeholder?: string, opts?: { signal?: AbortSignal; timeout?: number }) => Promise<string | undefined> }; hasUI: boolean },
|
|
41
|
+
) => {
|
|
42
|
+
if (!ctx.hasUI) {
|
|
43
|
+
return browserToolError("Browser tools require a connected UI (desktop app or RPC client)");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const paramsJson = JSON.stringify(params);
|
|
48
|
+
const resultJson = await ctx.ui.input(toolName, paramsJson, {
|
|
49
|
+
signal,
|
|
50
|
+
timeout: TOOL_TIMEOUT_MS,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (!resultJson) {
|
|
54
|
+
return browserToolError("Browser tool execution cancelled or timed out");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(resultJson);
|
|
59
|
+
return browserToolResult(parsed);
|
|
60
|
+
} catch {
|
|
61
|
+
return browserToolResult(resultJson);
|
|
62
|
+
}
|
|
63
|
+
} catch (e: unknown) {
|
|
64
|
+
return browserToolError(e instanceof Error ? e.message : String(e));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default function browserTools(pi: ExtensionAPI) {
|
|
70
|
+
if (process.env.DOCYRUS_DESKTOP_TOOLS !== "1") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pi.registerTool({
|
|
75
|
+
name: "docyrus_browser_navigate",
|
|
76
|
+
label: "Browser Navigate",
|
|
77
|
+
description: "Navigate the preview browser to a URL.",
|
|
78
|
+
parameters: Type.Object({
|
|
79
|
+
url: Type.String({ description: "URL to navigate to" }),
|
|
80
|
+
reload: Type.Optional(Type.Boolean({ description: "Force reload after navigation" })),
|
|
81
|
+
}),
|
|
82
|
+
execute: createExecuteHandler("docyrus_browser_navigate"),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
pi.registerTool({
|
|
86
|
+
name: "docyrus_browser_wait",
|
|
87
|
+
label: "Browser Wait",
|
|
88
|
+
description: "Wait for a condition: network idle, CSS selector, URL pattern, or fixed delay.",
|
|
89
|
+
parameters: Type.Object({
|
|
90
|
+
idle: Type.Optional(Type.Boolean({ description: "Wait for network idle" })),
|
|
91
|
+
selector: Type.Optional(Type.String({ description: "Wait for CSS selector to appear" })),
|
|
92
|
+
url: Type.Optional(Type.String({ description: "Wait for URL to match glob pattern" })),
|
|
93
|
+
ms: Type.Optional(Type.Number({ description: "Wait for fixed milliseconds" })),
|
|
94
|
+
timeout: Type.Optional(Type.Number({ description: "Maximum wait time in ms (default: 15000)" })),
|
|
95
|
+
}),
|
|
96
|
+
execute: createExecuteHandler("docyrus_browser_wait"),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
pi.registerTool({
|
|
100
|
+
name: "docyrus_browser_snapshot",
|
|
101
|
+
label: "Browser Snapshot",
|
|
102
|
+
description: "Get a compact snapshot of interactive page elements with refs (@e1, @e2, ...) for use in click/fill/select.",
|
|
103
|
+
parameters: Type.Object({
|
|
104
|
+
all: Type.Optional(Type.Boolean({ description: "Include all elements, not just interactive ones" })),
|
|
105
|
+
selector: Type.Optional(Type.String({ description: "Scope snapshot to a CSS selector subtree" })),
|
|
106
|
+
}),
|
|
107
|
+
execute: createExecuteHandler("docyrus_browser_snapshot"),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
pi.registerTool({
|
|
111
|
+
name: "docyrus_browser_click",
|
|
112
|
+
label: "Browser Click",
|
|
113
|
+
description: "Click an element by snapshot ref (@e1), CSS selector, or x,y coordinates. Coordinate clicks pass through iframes and shadow DOM.",
|
|
114
|
+
parameters: Type.Object({
|
|
115
|
+
target: Type.String({ description: "Snapshot ref (@e1), CSS selector, or x coordinate" }),
|
|
116
|
+
y: Type.Optional(Type.Number({ description: "Y coordinate (when using coordinate mode with target as x)" })),
|
|
117
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in ms (default: 5000)" })),
|
|
118
|
+
}),
|
|
119
|
+
execute: createExecuteHandler("docyrus_browser_click"),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
pi.registerTool({
|
|
123
|
+
name: "docyrus_browser_fill",
|
|
124
|
+
label: "Browser Fill",
|
|
125
|
+
description: "Clear and type a value into an input or textarea by snapshot ref or CSS selector.",
|
|
126
|
+
parameters: Type.Object({
|
|
127
|
+
target: Type.String({ description: "Snapshot ref (@e1) or CSS selector" }),
|
|
128
|
+
value: Type.String({ description: "Value to type into the element" }),
|
|
129
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in ms (default: 5000)" })),
|
|
130
|
+
}),
|
|
131
|
+
execute: createExecuteHandler("docyrus_browser_fill"),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
pi.registerTool({
|
|
135
|
+
name: "docyrus_browser_select",
|
|
136
|
+
label: "Browser Select",
|
|
137
|
+
description: "Select a dropdown option by snapshot ref or CSS selector.",
|
|
138
|
+
parameters: Type.Object({
|
|
139
|
+
target: Type.String({ description: "Snapshot ref (@e1) or CSS selector" }),
|
|
140
|
+
value: Type.String({ description: "Option text or value to select" }),
|
|
141
|
+
}),
|
|
142
|
+
execute: createExecuteHandler("docyrus_browser_select"),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
pi.registerTool({
|
|
146
|
+
name: "docyrus_browser_eval",
|
|
147
|
+
label: "Browser Eval",
|
|
148
|
+
description: "Evaluate JavaScript in the preview page and return the result.",
|
|
149
|
+
parameters: Type.Object({
|
|
150
|
+
code: Type.String({ description: "JavaScript code to evaluate" }),
|
|
151
|
+
}),
|
|
152
|
+
execute: createExecuteHandler("docyrus_browser_eval"),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
pi.registerTool({
|
|
156
|
+
name: "docyrus_browser_screenshot",
|
|
157
|
+
label: "Browser Screenshot",
|
|
158
|
+
description: "Capture a screenshot of the preview browser. Returns base64-encoded PNG.",
|
|
159
|
+
parameters: Type.Object({
|
|
160
|
+
full: Type.Optional(Type.Boolean({ description: "Capture full page instead of just the viewport" })),
|
|
161
|
+
}),
|
|
162
|
+
execute: createExecuteHandler("docyrus_browser_screenshot"),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
pi.registerTool({
|
|
166
|
+
name: "docyrus_browser_console",
|
|
167
|
+
label: "Browser Console",
|
|
168
|
+
description: "Read captured console messages from the preview page.",
|
|
169
|
+
parameters: Type.Object({
|
|
170
|
+
level: Type.Optional(Type.String({ description: "Filter by level: log, warn, error, info, debug" })),
|
|
171
|
+
}),
|
|
172
|
+
execute: createExecuteHandler("docyrus_browser_console"),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
pi.registerTool({
|
|
176
|
+
name: "docyrus_browser_network",
|
|
177
|
+
label: "Browser Network",
|
|
178
|
+
description: "Inspect captured network requests.",
|
|
179
|
+
parameters: Type.Object({
|
|
180
|
+
method: Type.Optional(Type.String({ description: "Filter by HTTP method" })),
|
|
181
|
+
status: Type.Optional(Type.String({ description: "Filter by status code or pattern (200, 4xx)" })),
|
|
182
|
+
url: Type.Optional(Type.String({ description: "Filter by URL substring" })),
|
|
183
|
+
}),
|
|
184
|
+
execute: createExecuteHandler("docyrus_browser_network"),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
pi.registerTool({
|
|
188
|
+
name: "docyrus_browser_cookies",
|
|
189
|
+
label: "Browser Cookies",
|
|
190
|
+
description: "List cookies for the preview page.",
|
|
191
|
+
parameters: Type.Object({
|
|
192
|
+
name: Type.Optional(Type.String({ description: "Filter by cookie name" })),
|
|
193
|
+
domain: Type.Optional(Type.String({ description: "Filter by domain" })),
|
|
194
|
+
}),
|
|
195
|
+
execute: createExecuteHandler("docyrus_browser_cookies"),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
pi.registerTool({
|
|
199
|
+
name: "docyrus_browser_content",
|
|
200
|
+
label: "Browser Content",
|
|
201
|
+
description: "Extract readable markdown content from the current preview page.",
|
|
202
|
+
parameters: Type.Object({}),
|
|
203
|
+
execute: createExecuteHandler("docyrus_browser_content"),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
pi.registerTool({
|
|
207
|
+
name: "docyrus_browser_info",
|
|
208
|
+
label: "Browser Info",
|
|
209
|
+
description: "Get current page info: URL, title, viewport, scroll, page size.",
|
|
210
|
+
parameters: Type.Object({}),
|
|
211
|
+
execute: createExecuteHandler("docyrus_browser_info"),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
pi.registerTool({
|
|
215
|
+
name: "docyrus_browser_devtools",
|
|
216
|
+
label: "Browser Devtools",
|
|
217
|
+
description: "Read @docyrus/devtools runtime diagnostics.",
|
|
218
|
+
parameters: Type.Object({
|
|
219
|
+
subcommand: Type.Union([
|
|
220
|
+
Type.Literal("state"),
|
|
221
|
+
Type.Literal("errors"),
|
|
222
|
+
Type.Literal("issues"),
|
|
223
|
+
Type.Literal("console"),
|
|
224
|
+
], { description: "What to read: state, errors, issues, or console" }),
|
|
225
|
+
level: Type.Optional(Type.String({ description: "Filter console entries by level" })),
|
|
226
|
+
}),
|
|
227
|
+
execute: createExecuteHandler("docyrus_browser_devtools"),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
@@ -1,97 +1,208 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: docyrus-browser-cli
|
|
3
|
-
description: Use Docyrus CLI browser commands for browser automation (local Chrome or remote Cloudflare Browser Rendering). Use when you need to start a browser session, navigate pages, inspect
|
|
3
|
+
description: Use Docyrus CLI browser commands for browser automation (local Chrome or remote Cloudflare Browser Rendering). Use when you need to start a browser session, navigate pages, inspect page state, interact with elements, capture screenshots, evaluate JavaScript, read console output, inspect network requests, or run CDP scripts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Docyrus Browser CLI
|
|
7
7
|
|
|
8
|
-
Browser automation
|
|
8
|
+
Browser automation via a persistent daemon that holds a raw CDP (Chrome DevTools Protocol) WebSocket connection. No Puppeteer/Playwright framework — direct CDP for speed and flexibility.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Works in both **local mode** (Chrome on `:9222`) and **remote sandbox mode** (Cloudflare Browser Rendering).
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
docyrus browser <command>
|
|
16
|
+
→ tool script (Node.js)
|
|
17
|
+
→ HTTP request to daemon (localhost:9333)
|
|
18
|
+
→ raw CDP WebSocket to Chrome/Cloudflare
|
|
19
|
+
```
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
The daemon starts automatically on the first command and stays alive for 5 minutes of idle time. All commands share the same browser connection — no reconnection overhead.
|
|
22
|
+
|
|
23
|
+
## Core Workflow for Self-Testing
|
|
15
24
|
|
|
16
25
|
```bash
|
|
26
|
+
# 1. Start browser + daemon
|
|
17
27
|
docyrus browser start
|
|
18
|
-
|
|
28
|
+
|
|
29
|
+
# 2. Navigate to preview
|
|
30
|
+
docyrus browser nav https://preview-url.example.com
|
|
31
|
+
|
|
32
|
+
# 3. Wait for page to fully load
|
|
33
|
+
docyrus browser wait --idle
|
|
34
|
+
|
|
35
|
+
# 4. Discover interactive elements
|
|
36
|
+
docyrus browser snapshot
|
|
37
|
+
|
|
38
|
+
# 5. Interact using refs from snapshot
|
|
39
|
+
docyrus browser fill @e2 "user@example.com"
|
|
40
|
+
docyrus browser click @e3
|
|
41
|
+
|
|
42
|
+
# 6. Verify results
|
|
43
|
+
docyrus browser wait --selector ".success-message"
|
|
44
|
+
docyrus browser screenshot
|
|
45
|
+
docyrus browser console --level error
|
|
46
|
+
docyrus browser network --method POST --status 2xx
|
|
19
47
|
```
|
|
20
48
|
|
|
21
|
-
|
|
22
|
-
- Sandbox mode: creates a Cloudflare Browser Rendering session and opens the app preview URL.
|
|
49
|
+
## Commands
|
|
23
50
|
|
|
24
|
-
|
|
51
|
+
### Session Management
|
|
25
52
|
|
|
26
53
|
```bash
|
|
27
|
-
docyrus browser
|
|
28
|
-
docyrus browser
|
|
29
|
-
docyrus browser
|
|
54
|
+
docyrus browser start # Start Chrome + daemon
|
|
55
|
+
docyrus browser start --profile # Start with user's Chrome profile (local only)
|
|
56
|
+
docyrus browser close # Stop daemon and disconnect
|
|
30
57
|
```
|
|
31
58
|
|
|
32
|
-
|
|
59
|
+
### Navigation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
docyrus browser nav <url> # Navigate active tab
|
|
63
|
+
docyrus browser nav <url> --new # Open in new tab
|
|
64
|
+
docyrus browser nav <url> --reload # Navigate and force reload
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Waiting
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
docyrus browser wait --idle # Wait for network idle
|
|
71
|
+
docyrus browser wait --selector "h1" # Wait for element to appear
|
|
72
|
+
docyrus browser wait --url "**/dashboard" # Wait for URL to match pattern
|
|
73
|
+
docyrus browser wait 2000 # Wait fixed milliseconds
|
|
74
|
+
docyrus browser wait --idle --timeout 30000
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Page Snapshot (Element Discovery)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
docyrus browser snapshot # Interactive elements with refs (@e1, @e2, ...)
|
|
81
|
+
docyrus browser snapshot --all # Include non-interactive elements
|
|
82
|
+
docyrus browser snapshot --selector "#app" # Scope to a subtree
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"snapshot": [
|
|
89
|
+
{ "ref": "@e1", "tag": "input", "type": "email", "placeholder": "Email" },
|
|
90
|
+
{ "ref": "@e2", "tag": "input", "type": "password" },
|
|
91
|
+
{ "ref": "@e3", "tag": "button", "text": "Sign In" }
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Element Interaction
|
|
97
|
+
|
|
98
|
+
Three targeting modes — refs, CSS selectors, or x,y coordinates:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
docyrus browser click @e3 # Click by snapshot ref
|
|
102
|
+
docyrus browser click "button.submit" # Click by CSS selector
|
|
103
|
+
docyrus browser click 350 200 # Coordinate click (compositor-level)
|
|
104
|
+
docyrus browser fill @e1 "user@test.com" # Clear + type into input
|
|
105
|
+
docyrus browser select @e4 "Option A" # Select dropdown option
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Coordinate clicks pass through iframes, shadow DOM, and cross-origin frames at the compositor level — no DOM piercing needed. Use `info` for viewport dimensions.
|
|
109
|
+
|
|
110
|
+
### JavaScript Evaluation
|
|
33
111
|
|
|
34
112
|
```bash
|
|
35
113
|
docyrus browser eval 'document.title'
|
|
36
|
-
docyrus browser eval 'document.querySelectorAll("
|
|
114
|
+
docyrus browser eval 'const rows = document.querySelectorAll("tr"); return rows.length'
|
|
115
|
+
docyrus browser eval 'await fetch("/api/health").then(r => r.json())'
|
|
37
116
|
```
|
|
38
117
|
|
|
39
|
-
|
|
118
|
+
Supports expressions and multi-statement code. Uses CDP `Runtime.evaluate` with `awaitPromise: true`.
|
|
119
|
+
|
|
120
|
+
### Screenshots
|
|
40
121
|
|
|
41
122
|
```bash
|
|
42
|
-
docyrus browser screenshot
|
|
123
|
+
docyrus browser screenshot # Viewport screenshot → /tmp
|
|
124
|
+
docyrus browser screenshot --full # Full page
|
|
125
|
+
docyrus browser screenshot --base64 # Base64 in JSON (for remote/sandbox mode)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Console Messages
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
docyrus browser console # Captured logs (last 50)
|
|
132
|
+
docyrus browser console --level error # Only errors
|
|
133
|
+
docyrus browser console --listen 5000 # Listen for 5 seconds via CDP events
|
|
43
134
|
```
|
|
44
135
|
|
|
45
|
-
|
|
136
|
+
Run `console` once early to install the interceptor.
|
|
137
|
+
|
|
138
|
+
### Network Inspection
|
|
46
139
|
|
|
47
140
|
```bash
|
|
48
|
-
docyrus browser
|
|
141
|
+
docyrus browser network # All captured requests
|
|
142
|
+
docyrus browser network --method POST # Only POST requests
|
|
143
|
+
docyrus browser network --status 4xx # Only 4xx responses
|
|
144
|
+
docyrus browser network --url "/api/" # URL substring filter
|
|
145
|
+
docyrus browser network --listen 5000 # Listen for 5 seconds
|
|
49
146
|
```
|
|
50
147
|
|
|
51
|
-
|
|
148
|
+
Network events are captured automatically by the daemon (CDP `Network.enable`).
|
|
149
|
+
|
|
150
|
+
### Docyrus Devtools
|
|
151
|
+
|
|
152
|
+
Read runtime diagnostics from `@docyrus/devtools` (must be loaded on the page):
|
|
52
153
|
|
|
53
154
|
```bash
|
|
54
|
-
docyrus browser
|
|
155
|
+
docyrus browser devtools state # Full devtools state
|
|
156
|
+
docyrus browser devtools errors # Collected API errors
|
|
157
|
+
docyrus browser devtools issues # Detected API usage issues (e.g. missing fields, bad queries)
|
|
158
|
+
docyrus browser devtools console # Console entries captured by devtools
|
|
159
|
+
docyrus browser devtools console --level error # Filtered by level
|
|
55
160
|
```
|
|
56
161
|
|
|
57
|
-
|
|
162
|
+
Use after interacting with a Docyrus-backed app to catch API misuse, failed requests, and runtime issues that `console --level error` alone would miss.
|
|
163
|
+
|
|
164
|
+
### Content Extraction
|
|
58
165
|
|
|
59
166
|
```bash
|
|
60
|
-
docyrus browser content
|
|
167
|
+
docyrus browser content <url> # Extract readable markdown from URL
|
|
61
168
|
```
|
|
62
169
|
|
|
63
|
-
|
|
170
|
+
### Cookies
|
|
64
171
|
|
|
65
172
|
```bash
|
|
66
|
-
docyrus browser
|
|
67
|
-
docyrus browser
|
|
68
|
-
docyrus browser
|
|
173
|
+
docyrus browser cookies # All cookies (via CDP Network.getCookies)
|
|
174
|
+
docyrus browser cookies --name "session"
|
|
175
|
+
docyrus browser cookies --domain ".app.com"
|
|
69
176
|
```
|
|
70
177
|
|
|
71
|
-
|
|
72
|
-
|
|
178
|
+
### Page Info
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
docyrus browser info # URL, title, viewport, scroll, page dimensions
|
|
182
|
+
```
|
|
73
183
|
|
|
74
|
-
|
|
184
|
+
### Tab Management
|
|
75
185
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return { title };
|
|
186
|
+
```bash
|
|
187
|
+
docyrus browser tabs # List all tabs
|
|
188
|
+
docyrus browser tabs --switch 1 # Switch to tab by index
|
|
80
189
|
```
|
|
81
190
|
|
|
82
|
-
|
|
191
|
+
### CDP Scripts
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
docyrus browser run-script script.js
|
|
195
|
+
```
|
|
83
196
|
|
|
84
|
-
|
|
85
|
-
- A page needs JavaScript execution before content is available.
|
|
86
|
-
- You need to inspect DOM state without guessing selectors.
|
|
87
|
-
- You want the user to click elements directly through an interactive picker.
|
|
88
|
-
- You need quick page extraction or screenshot capture from a real browser session.
|
|
89
|
-
- You need to run CDP scripts against a remote headless browser from within a sandbox.
|
|
197
|
+
The script receives raw CDP helpers: `cdp`, `evaluate`, `navigate`, `captureScreenshot`, `clickAt`, `typeText`, `pressKey`, `pageInfo`, `drainEvents`, `waitForCondition`.
|
|
90
198
|
|
|
91
|
-
##
|
|
199
|
+
## Tips for AI Agents
|
|
92
200
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
|
|
201
|
+
1. `start` auto-launches the daemon — subsequent commands are fast (no reconnection)
|
|
202
|
+
2. Always `wait --idle` after `nav` before taking snapshots or screenshots
|
|
203
|
+
3. Use `snapshot` → refs → `click`/`fill` for reliable element targeting
|
|
204
|
+
4. Re-snapshot after interactions to get fresh refs
|
|
205
|
+
5. Use coordinate `click 350 200` for elements inside iframes or shadow DOM
|
|
206
|
+
6. Check `console --level error` and `network --status 5xx` to catch runtime issues
|
|
207
|
+
7. Use `screenshot --base64` in remote/sandbox mode
|
|
208
|
+
8. `close` stops the daemon; it auto-stops after 5 minutes idle anyway
|