@docyrus/docyrus 0.0.60 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docyrus/docyrus",
3
- "version": "0.0.60",
3
+ "version": "0.0.62",
4
4
  "private": false,
5
5
  "description": "Docyrus API CLI",
6
6
  "main": "./main.js",
@@ -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
+ }