openzoo 0.50.57 → 0.50.58

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.
@@ -53,7 +53,7 @@ import {
53
53
  import {
54
54
  desktopAction, displayBounds, imageSize, noteShotMeta, resolveAppName,
55
55
  } from './grokbotDesktop.js';
56
- import { startHostMcps, hostMcpTools, hostMcpHas, callHostMcp, hostMcpServers } from './mcpbridge.js';
56
+ import { startHostMcps, hostMcpTools, hostMcpHas, callHostMcp, hostMcpServers, chromeStatus } from './mcpbridge.js';
57
57
  import * as ship from './ship.js';
58
58
 
59
59
  const TLS_DIR = path.join(os.homedir(), '.openzoo', 'cursor-tls');
@@ -2649,7 +2649,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
2649
2649
  return [
2650
2650
  `Host MCP tools from the user's local Claude/Grok config are attached (${servers.join(', ') || 'mcp'}): ${names.slice(0, 36).join(', ')}${names.length > 36 ? '…' : ''}.`,
2651
2651
  chrome.length
2652
- ? 'For any web page or HTML form, use chrome-devtools tools (navigate_page / take_snapshot / fill / click). Do NOT use osascript, Quartz, Python Foundation, or AppleScript to read Brave. screenshot/click/type_text are for native Mac UI only.'
2652
+ ? `For any web page or HTML form, use chrome-devtools tools (navigate_page / take_snapshot / fill / click). Do NOT use osascript, Quartz, Python Foundation, or AppleScript to read Brave. screenshot/click/type_text are for native Mac UI only. Browser mode: ${chromeStatus().mode}.${chromeStatus().hint ? ` If the human needs their logged-in browser, tell them once: ${chromeStatus().hint}` : ' This is the human\'s real logged-in browser; do not log out or change its settings.'}`
2653
2653
  : 'Use matching MCP tools instead of inventing shell one-liners.',
2654
2654
  ].join(' ');
2655
2655
  })(),
package/lib/mcpbridge.js CHANGED
@@ -240,16 +240,60 @@ function portOpen(port, host = '127.0.0.1', ms = 150) {
240
240
  });
241
241
  }
242
242
 
243
- async function chromeArgs(baseArgs) {
244
- const args = [...(baseArgs || ['-y', 'chrome-devtools-mcp@latest'])];
245
- if (args.some((a) => String(a).includes('browserUrl') || String(a) === '--browserUrl')) return args;
246
- for (const port of [9222, 9333]) {
247
- if (await portOpen(port)) {
248
- args.push('--browserUrl', `http://127.0.0.1:${port}`);
249
- break;
250
- }
243
+ /**
244
+ * "Chrome with Claude" for the hijack. Preference order:
245
+ * 1. the user's REAL Chrome (their logins): Chrome 144+ writes
246
+ * DevToolsActivePort under its user-data-dir once the human flips
247
+ * chrome://inspect/#remote-debugging -> "Allow remote debugging for this
248
+ * browser". chrome-devtools-mcp attaches with --autoConnect.
249
+ * 2. the user's real Brave the same way (Brave exposes a port, not the
250
+ * channel autoConnect expects) -> --browserUrl to that port.
251
+ * 3. any browser already listening on 9222/9333 -> --browserUrl.
252
+ * 4. chrome-devtools-mcp's own persistent profile (log in there once).
253
+ * Chrome 136+ refuses --remote-debugging-port on the default profile, so
254
+ * relaunching the user's browser with a flag is not an option — the toggle is.
255
+ */
256
+ export const CHROME_TOGGLE_HINT = 'To let bots drive your real Chrome (your logins): open chrome://inspect/#remote-debugging in Chrome, turn on "Allow remote debugging for this browser", then restart `openzoo bot`. Until then the bot drives its own Chrome profile — log into sites there once and it persists.';
257
+
258
+ let chromeMode = { mode: 'own-profile', detail: '' };
259
+ export function chromeStatus() {
260
+ return { ...chromeMode, hint: chromeMode.mode === 'own-profile' ? CHROME_TOGGLE_HINT : '' };
261
+ }
262
+
263
+ function readActivePort(dir) {
264
+ try {
265
+ const first = fs.readFileSync(path.join(dir, 'DevToolsActivePort'), 'utf8').split('\n')[0].trim();
266
+ const n = Number(first);
267
+ return Number.isFinite(n) && n > 0 ? n : 0;
268
+ } catch {
269
+ return 0;
251
270
  }
252
- return args;
271
+ }
272
+
273
+ /** Pure: decide the chrome-devtools-mcp argv from what is observable. */
274
+ export function chromeArgsFor(baseArgs, { chromePort = 0, bravePort = 0, openPorts = [] } = {}) {
275
+ const args = [...(baseArgs || ['-y', 'chrome-devtools-mcp@latest'])];
276
+ const has = (flag) => args.some((a) => String(a) === flag || String(a).startsWith(`${flag}=`));
277
+ if (has('--browserUrl') || has('--autoConnect') || has('--wsEndpoint')) return { args, mode: 'explicit', detail: '' };
278
+ if (chromePort) { args.push('--autoConnect'); return { args, mode: 'real-chrome', detail: `Chrome DevToolsActivePort ${chromePort}` }; }
279
+ if (bravePort) { args.push('--browserUrl', `http://127.0.0.1:${bravePort}`); return { args, mode: 'real-brave', detail: `Brave DevToolsActivePort ${bravePort}` }; }
280
+ const port = openPorts.find((n) => n === 9222 || n === 9333);
281
+ if (port) { args.push('--browserUrl', `http://127.0.0.1:${port}`); return { args, mode: `attached:${port}`, detail: `browser listening on ${port}` }; }
282
+ return { args, mode: 'own-profile', detail: '~/.cache/chrome-devtools-mcp/chrome-profile' };
283
+ }
284
+
285
+ async function chromeArgs(baseArgs, home = os.homedir()) {
286
+ const chromeDir = path.join(home, 'Library', 'Application Support', 'Google', 'Chrome');
287
+ const braveDir = path.join(home, 'Library', 'Application Support', 'BraveSoftware', 'Brave-Browser');
288
+ let chromePort = readActivePort(chromeDir);
289
+ if (chromePort && !(await portOpen(chromePort))) chromePort = 0;
290
+ let bravePort = readActivePort(braveDir);
291
+ if (bravePort && !(await portOpen(bravePort))) bravePort = 0;
292
+ const openPorts = [];
293
+ for (const port of [9222, 9333]) if (await portOpen(port)) openPorts.push(port);
294
+ const got = chromeArgsFor(baseArgs, { chromePort, bravePort, openPorts });
295
+ chromeMode = { mode: got.mode, detail: got.detail };
296
+ return got.args;
253
297
  }
254
298
 
255
299
  async function connectOne(cfg, log) {
@@ -263,7 +307,11 @@ async function connectOne(cfg, log) {
263
307
  });
264
308
  } else {
265
309
  let args = cfg.args || [];
266
- if (cfg.name === 'chrome-devtools') args = await chromeArgs(args);
310
+ if (cfg.name === 'chrome-devtools') {
311
+ args = await chromeArgs(args);
312
+ log?.(`cursor-backend: mcp chrome-devtools mode=${chromeMode.mode} ${chromeMode.detail}`);
313
+ if (chromeMode.mode === 'own-profile') log?.(`cursor-backend: mcp chrome-devtools ${CHROME_TOGGLE_HINT}`);
314
+ }
267
315
  const env = { ...getDefaultEnvironment(), PATH: process.env.PATH || '', ...(cfg.env || {}) };
268
316
  if (process.env.NVM_DIR) env.NVM_DIR = process.env.NVM_DIR;
269
317
  transport = new StdioClientTransport({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzoo",
3
- "version": "0.50.57",
3
+ "version": "0.50.58",
4
4
  "description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
5
5
  "license": "MIT",
6
6
  "type": "module",