chromeflow 0.1.38 → 0.1.39

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 CHANGED
@@ -155,14 +155,31 @@ screenshot to check what happened.
155
155
 
156
156
  **React Select / custom styled dropdowns** (e.g. "Select..." components on DataAnnotation):
157
157
  `click_element` and `fill_input` do NOT work on these — they intercept native events. Use
158
- `execute_script` directly:
158
+ `execute_script` with the hidden combobox input approach (most reliable):
159
159
 
160
160
  ```js
161
- // 1. Open the menu click the control div (filter by pageY if multiple)
161
+ // 1. Find the hidden combobox input (each React Select has one: input[id*="react-select-N-input"])
162
+ var input = document.querySelector('input[id*="react-select-3-input"]');
163
+ input.focus();
164
+
165
+ // 2. Set value via native setter to trigger React's onChange
166
+ var setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
167
+ setter.call(input, 'Target Option');
168
+ input.dispatchEvent(new Event('input', {bubbles: true}));
169
+
170
+ // 3. Wait 300ms for the dropdown to filter, then click the first matching option
171
+ // (run this as a separate execute_script call after a brief pause)
172
+ var option = document.querySelector('[id*="react-select-3-option-0"]');
173
+ if (option) option.click();
174
+
175
+ // 4. Verify — the control div should show the selected value
176
+ document.querySelector('[class*="singleValue"]').textContent.trim();
177
+ ```
178
+
179
+ Fallback if the combobox approach doesn't work (older React Select versions):
180
+ ```js
162
181
  var controls = document.querySelectorAll('[class*="control"]');
163
182
  controls[N].click();
164
-
165
- // 2. Pick an option by exact text
166
183
  var allEls = document.querySelectorAll('*');
167
184
  for (var i = 0; i < allEls.length; i++) {
168
185
  if (allEls[i].textContent.trim() === 'Target Option' && allEls[i].children.length === 0) {
@@ -171,9 +188,6 @@ for (var i = 0; i < allEls.length; i++) {
171
188
  break;
172
189
  }
173
190
  }
174
-
175
- // 3. Verify
176
- controls[N].textContent.trim(); // should show selected value
177
191
  ```
178
192
 
179
193
  **Page text with large embedded content** (e.g. uploaded log files previewed inline): full-page `get_page_text()` pagination becomes unwieldy. Scope to a specific section instead:
package/dist/setup.js CHANGED
@@ -169,7 +169,9 @@ const CHROMEFLOW_TOOLS = [
169
169
  // v0.1.32+
170
170
  "fill_form",
171
171
  // v0.1.36+
172
- "set_file_input"
172
+ "set_file_input",
173
+ // v0.1.39+
174
+ "get_console_logs"
173
175
  ].map((t) => `mcp__chromeflow__${t}`);
174
176
  function patchSettingsLocalJson(cwd) {
175
177
  const claudeDir = join(cwd, ".claude");
@@ -12,10 +12,11 @@ DO NOT use for: email address, password, payment/billing info, phone number \u20
12
12
  After filling, call wait_for_click only if the user needs to review/confirm; otherwise proceed directly to the next step.`,
13
13
  {
14
14
  textHint: z.string().describe("The label, placeholder, or nearby text identifying the input (e.g. 'Product name', 'Amount', 'Description')"),
15
- value: z.string().describe("The value to fill in")
15
+ value: z.string().describe("The value to fill in"),
16
+ nth: z.number().int().min(1).optional().describe("Which match to fill when multiple inputs share the same label (1 = first/topmost, default 1)")
16
17
  },
17
- async ({ textHint, value }) => {
18
- const response = await bridge.request({ type: "fill_input", textHint, value });
18
+ async ({ textHint, value, nth }) => {
19
+ const response = await bridge.request({ type: "fill_input", textHint, value, nth });
19
20
  if (response.type !== "fill_response") throw new Error("Unexpected response");
20
21
  const r = response;
21
22
  return {
@@ -117,6 +118,36 @@ The snapshot is read from the local temp file written by save_page_state.`,
117
118
  };
118
119
  }
119
120
  );
121
+ server.tool(
122
+ "get_console_logs",
123
+ `Read the browser console output (log, warn, error, info) captured since the page loaded.
124
+ Returns the last 200 messages with their level and timestamp.
125
+ Use this to check for JavaScript errors, debug React issues, or verify that an action produced the expected console output.
126
+ Pass level="error" to see only errors, or omit to see all levels.`,
127
+ {
128
+ level: z.enum(["log", "warn", "error", "info"]).optional().describe('Filter by log level (e.g. "error" to see only errors). Omit for all levels.')
129
+ },
130
+ async ({ level }) => {
131
+ const response = await bridge.request({ type: "execute_script", code: `JSON.stringify(window._consoleLogs || [])` });
132
+ if (response.type !== "script_response") throw new Error("Unexpected response");
133
+ let logs;
134
+ try {
135
+ logs = JSON.parse(response.result);
136
+ } catch {
137
+ return { content: [{ type: "text", text: "No console logs captured (console capture may not be injected on this page yet \u2014 navigate first)." }] };
138
+ }
139
+ if (level) logs = logs.filter((l) => l.level === level);
140
+ if (logs.length === 0) {
141
+ return { content: [{ type: "text", text: level ? `No ${level}-level console messages.` : "No console messages captured." }] };
142
+ }
143
+ const lines = logs.map((l) => {
144
+ const time = new Date(l.time).toISOString().slice(11, 23);
145
+ return `[${time}] ${l.level.toUpperCase()}: ${l.message.slice(0, 500)}`;
146
+ });
147
+ return { content: [{ type: "text", text: `Console logs (${logs.length} entries):
148
+ ${lines.join("\n")}` }] };
149
+ }
150
+ );
120
151
  server.tool(
121
152
  "write_to_env",
122
153
  "Write a key=value pair to a .env file. Use this after capturing an API key or ID from the page.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chromeflow",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "Browser guidance MCP server for Claude Code — highlights, clicks, fills, and captures from the web so you don't have to.",
5
5
  "type": "module",
6
6
  "bin": {