chromeflow 0.1.39 → 0.1.41
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/CLAUDE.md +1 -1
- package/dist/index.js +48 -0
- package/dist/setup.js +3 -1
- package/dist/tools/browser.js +64 -1
- package/dist/tools/capture.js +2 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -24,7 +24,7 @@ Do NOT ask "should I open the browser?" — just do it. The user expects seamles
|
|
|
24
24
|
|
|
25
25
|
2. **Never use `take_screenshot` to read page content.** After `scroll_page`, after
|
|
26
26
|
`click_element`, after navigation — always call `get_page_text`, not `take_screenshot`.
|
|
27
|
-
`get_page_text` returns up to
|
|
27
|
+
`get_page_text` returns up to 10,000 characters; if truncated it tells you the next
|
|
28
28
|
`startIndex` to paginate. Screenshots are only for locating an element's pixel position
|
|
29
29
|
when DOM queries have already failed. Never take more than 1–2 screenshots in a row.
|
|
30
30
|
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,54 @@ async function main() {
|
|
|
38
38
|
registerHighlightTools(server, bridge);
|
|
39
39
|
registerCaptureTools(server, bridge);
|
|
40
40
|
registerFlowTools(server, bridge);
|
|
41
|
+
server.prompt(
|
|
42
|
+
"chromeflow-status",
|
|
43
|
+
"Check if the chromeflow Chrome extension is connected and which tab is active",
|
|
44
|
+
async () => {
|
|
45
|
+
const connected = bridge.isConnected();
|
|
46
|
+
if (!connected) {
|
|
47
|
+
return {
|
|
48
|
+
messages: [{
|
|
49
|
+
role: "user",
|
|
50
|
+
content: {
|
|
51
|
+
type: "text",
|
|
52
|
+
text: "Check chromeflow status. The Chrome extension is NOT connected. Tell the user to reload the chromeflow extension in chrome://extensions."
|
|
53
|
+
}
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const response = await bridge.request({ type: "list_tabs" }, 3e3);
|
|
59
|
+
const tabs = response.tabs;
|
|
60
|
+
const active = tabs.find((t) => t.active);
|
|
61
|
+
const tabList = tabs.map((t) => `${t.index}. ${t.active ? "[active] " : ""}${t.title} \u2014 ${t.url}`).join("\n");
|
|
62
|
+
return {
|
|
63
|
+
messages: [{
|
|
64
|
+
role: "user",
|
|
65
|
+
content: {
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Check chromeflow status.
|
|
68
|
+
|
|
69
|
+
Extension: Connected
|
|
70
|
+
Active tab: ${active?.title ?? "none"} \u2014 ${active?.url ?? ""}
|
|
71
|
+
All tabs:
|
|
72
|
+
${tabList}`
|
|
73
|
+
}
|
|
74
|
+
}]
|
|
75
|
+
};
|
|
76
|
+
} catch {
|
|
77
|
+
return {
|
|
78
|
+
messages: [{
|
|
79
|
+
role: "user",
|
|
80
|
+
content: {
|
|
81
|
+
type: "text",
|
|
82
|
+
text: "Check chromeflow status. Extension is connected but not responding. The user may need to reload it."
|
|
83
|
+
}
|
|
84
|
+
}]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
41
89
|
const transport = new StdioServerTransport();
|
|
42
90
|
await server.connect(transport);
|
|
43
91
|
console.error("[chromeflow] MCP server running. Waiting for Claude...");
|
package/dist/setup.js
CHANGED
|
@@ -171,7 +171,9 @@ const CHROMEFLOW_TOOLS = [
|
|
|
171
171
|
// v0.1.36+
|
|
172
172
|
"set_file_input",
|
|
173
173
|
// v0.1.39+
|
|
174
|
-
"get_console_logs"
|
|
174
|
+
"get_console_logs",
|
|
175
|
+
// v0.1.40+
|
|
176
|
+
"capture_terminal"
|
|
175
177
|
].map((t) => `mcp__chromeflow__${t}`);
|
|
176
178
|
function patchSettingsLocalJson(cwd) {
|
|
177
179
|
const claudeDir = join(cwd, ".claude");
|
package/dist/tools/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { writeFileSync, copyFileSync } from "fs";
|
|
2
|
+
import { writeFileSync, copyFileSync, readFileSync } from "fs";
|
|
3
3
|
import { tmpdir, homedir } from "os";
|
|
4
4
|
import { join } from "path";
|
|
5
5
|
import { execSync } from "child_process";
|
|
@@ -106,6 +106,69 @@ save_to controls where the PNG is saved: "downloads" (default) saves to ~/Downlo
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
);
|
|
109
|
+
server.tool(
|
|
110
|
+
"capture_terminal",
|
|
111
|
+
`Capture a screenshot of the terminal window (Terminal, iTerm2, Warp, VS Code, Ghostty, etc.) and save it as a PNG.
|
|
112
|
+
Use this when you need a screenshot of terminal output \u2014 e.g. test results, build logs, or command output \u2014 to upload to a form via set_file_input.
|
|
113
|
+
Auto-detects the terminal app. Returns the image to Claude AND saves the PNG file.
|
|
114
|
+
The saved file path can be passed directly to set_file_input(hint, file_path) to upload it.`,
|
|
115
|
+
{
|
|
116
|
+
save_to: z.enum(["downloads", "cwd"]).optional().describe('Where to save the PNG: "downloads" (~/Downloads, default) or "cwd" (working directory)')
|
|
117
|
+
},
|
|
118
|
+
async ({ save_to = "downloads" }) => {
|
|
119
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
120
|
+
const filename = `terminal-${timestamp}.png`;
|
|
121
|
+
const savePath = save_to === "cwd" ? join(process.cwd(), filename) : join(homedir(), "Downloads", filename);
|
|
122
|
+
let captured = false;
|
|
123
|
+
try {
|
|
124
|
+
const bounds = execSync(`osascript -e '
|
|
125
|
+
tell application "System Events"
|
|
126
|
+
set termApps to {"Terminal", "iTerm2", "Warp", "kitty", "Alacritty", "Ghostty", "Code", "Cursor", "Windsurf"}
|
|
127
|
+
repeat with appName in termApps
|
|
128
|
+
if exists process (contents of appName) then
|
|
129
|
+
tell process (contents of appName)
|
|
130
|
+
if (count of windows) > 0 then
|
|
131
|
+
set win to window 1
|
|
132
|
+
set pos to position of win
|
|
133
|
+
set sz to size of win
|
|
134
|
+
return (item 1 of pos as text) & "," & (item 2 of pos as text) & "," & (item 1 of sz as text) & "," & (item 2 of sz as text)
|
|
135
|
+
end if
|
|
136
|
+
end tell
|
|
137
|
+
end if
|
|
138
|
+
end repeat
|
|
139
|
+
error "No terminal window found"
|
|
140
|
+
end tell
|
|
141
|
+
'`, { timeout: 5e3 }).toString().trim();
|
|
142
|
+
execSync(`screencapture -x -R${bounds} "${savePath}"`, { timeout: 5e3 });
|
|
143
|
+
captured = true;
|
|
144
|
+
} catch {
|
|
145
|
+
try {
|
|
146
|
+
execSync(`screencapture -x "${savePath}"`, { timeout: 5e3 });
|
|
147
|
+
captured = true;
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!captured) {
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: "Failed to capture terminal. Ensure Screen Recording permission is granted to your terminal app in System Settings > Privacy & Security > Screen Recording." }]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const imageBuffer = readFileSync(savePath);
|
|
157
|
+
const base64 = imageBuffer.toString("base64");
|
|
158
|
+
let clipboardNote = "";
|
|
159
|
+
try {
|
|
160
|
+
execSync(`osascript -e 'set the clipboard to (read (POSIX file "${savePath}") as \xABclass PNGf\xBB)'`);
|
|
161
|
+
clipboardNote = "Copied to clipboard. ";
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{ type: "image", data: base64, mimeType: "image/png" },
|
|
167
|
+
{ type: "text", text: `${clipboardNote}Saved to ${savePath}` }
|
|
168
|
+
]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
);
|
|
109
172
|
server.tool(
|
|
110
173
|
"clear_overlays",
|
|
111
174
|
"Remove all highlights and callout annotations from the current page. Does NOT remove the guide panel \u2014 the guide panel persists until the next flow starts.",
|
package/dist/tools/capture.js
CHANGED
|
@@ -61,7 +61,8 @@ After filling, call wait_for_click only if the user needs to review/confirm; oth
|
|
|
61
61
|
"get_page_text",
|
|
62
62
|
`Get the visible text content of the current page without taking a screenshot.
|
|
63
63
|
Use this instead of take_screenshot whenever you need to read what's on the page \u2014 errors, build status, form labels, confirmation messages, etc.
|
|
64
|
-
Returns up to
|
|
64
|
+
Returns up to 10,000 characters per call (~3k tokens). If the response ends with "... (N more characters)", call again with startIndex to read the next chunk.
|
|
65
|
+
Use the selector parameter to scope extraction to a specific section and avoid pulling unnecessary content.
|
|
65
66
|
Never use take_screenshot just to read page content \u2014 paginate with startIndex instead.`,
|
|
66
67
|
{
|
|
67
68
|
selector: z.string().optional().describe(
|
package/package.json
CHANGED