pi-chrome 0.11.4 → 0.11.5

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 CHANGED
@@ -65,24 +65,25 @@ pi-chrome v<version>
65
65
  ✓ Companion Chrome extension responding (ID: <chrome-extension-id>, ext v<version>)
66
66
  ```
67
67
 
68
- ## Trusted-input mode (CDP)
68
+ ## Click modes
69
69
 
70
- By default, `chrome_*` clicks and keystrokes are **synthetic** DOM events (`event.isTrusted === false`). They drive React/Vue/Angular state correctly but **do not** satisfy Chrome's user-activation gates: clipboard write, fullscreen, file picker, and autoplay all need a real user gesture.
70
+ pi-chrome can drive Chrome two ways:
71
71
 
72
- pi-chrome can optionally route input through `chrome.debugger` (CDP `Input.dispatchMouseEvent` / `Input.dispatchKeyEvent`) so each event arrives as `isTrusted=true`, satisfies user-activation, and bypasses site bot-detection that filters synthetic events. The tradeoff: Chrome pins a *"Pi Chrome Connector started debugging this browser"* banner to the top of any debugged tab.
72
+ - **Quiet clicks** fast and unobtrusive. They work on most sites, but some pages (sign-in flows, copy-to-clipboard buttons, file pickers, autoplay videos, fullscreen, paywalls) ignore them because they don't look like a real human action.
73
+ - **Real-looking clicks** — indistinguishable from a person clicking. They unlock the cases above, but Chrome shows a *"Pi Chrome Connector started debugging this browser"* banner at the top of every tab pi-chrome touches while it's working.
73
74
 
74
- Usage:
75
+ Pick a mode with `/chrome-trusted`:
75
76
 
76
77
  ```text
77
- /chrome-trusted on # all chrome_* tools dispatch via CDP
78
- /chrome-trusted off # synthetic events only (default)
79
- /chrome-trusted auto # CDP only when a tool passes trusted=true
80
- /chrome-trusted status # show current mode and attached tabs
78
+ /chrome-trusted auto # default; quiet by default, real-looking only when needed
79
+ /chrome-trusted off # always quiet, no banner ever
80
+ /chrome-trusted on # always real-looking, banner stays up the whole session
81
+ /chrome-trusted status # show the current mode
81
82
  ```
82
83
 
83
- For a single call, pass `trusted: true` on `chrome_click`, `chrome_type`, `chrome_fill`, `chrome_key`, `chrome_hover`, `chrome_drag`, or `chrome_scroll`. The per-call value always wins over the session toggle.
84
+ For a one-off call, pass `trusted: true` (or `false`) on `chrome_click`, `chrome_type`, `chrome_fill`, `chrome_key`, `chrome_hover`, `chrome_drag`, or `chrome_scroll`. The per-call value wins over the global mode.
84
85
 
85
- First time you upgrade to a pi-chrome that uses trusted input, Chrome will prompt to re-approve the extension's new `debugger` permission do that once in `chrome://extensions`.
86
+ First time you update pi-chrome to a version that supports real-looking clicks, Chrome will ask you to re-approve the extension. Open `chrome://extensions` and accept the new permission once.
86
87
 
87
88
  ## Background mode
88
89
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Pi Chrome Connector",
4
- "version": "0.11.4",
4
+ "version": "0.11.5",
5
5
  "description": "Lets Pi control tabs in Chrome via a local connector at 127.0.0.1.",
6
6
  "permissions": ["tabs", "scripting", "storage", "activeTab", "alarms", "webNavigation", "debugger"],
7
7
  "host_permissions": ["<all_urls>", "http://127.0.0.1:17318/*"],
@@ -415,8 +415,8 @@ export default function (pi: ExtensionAPI): void {
415
415
  ctx.ui.setStatus("chrome", `Chrome bridge :${DEFAULT_PORT}`);
416
416
  ctx.ui.notify(
417
417
  status.mode === "client"
418
- ? `Chrome profile bridge connected to shared bridge at ${bridge.url}.`
419
- : `Chrome profile bridge listening at ${bridge.url}. Run /chrome-onboard to load the bundled browser extension in your normal Chrome profile.`,
418
+ ? `pi-chrome connected (sharing the Chrome connection an earlier pi session opened).`
419
+ : `pi-chrome is ready and waiting for the Chrome companion to connect. Run /chrome-onboard to install it.`,
420
420
  "info",
421
421
  );
422
422
  });
@@ -449,12 +449,13 @@ Usage rules:
449
449
 
450
450
  pi.registerCommand("chrome-doctor", {
451
451
  description:
452
- "Check Chrome bridge connectivity and capability tier. Probes the local bridge, the companion Chrome extension, MAIN-world evaluation, and CDP availability, and prints one-line fixes for common failures.",
452
+ "Run a quick health check on pi-chrome. Shows whether Chrome is connected, whether the companion extension is up to date, which click mode is active, and how to fix anything that's wrong.",
453
453
  handler: async (_args, ctx) => {
454
- ctx.ui.notify("Performing Chrome bridge health check", "info");
454
+ ctx.ui.notify("Checking pi-chrome…", "info");
455
455
  const lines: string[] = [`pi-chrome v${PI_CHROME_VERSION}`];
456
456
  const status = bridge.status();
457
- lines.push(`• Local bridge: mode=${status.mode}, url=${status.url}`);
457
+ const roleLabel = status.mode === "client" ? "sharing another pi session's connection" : "running the Chrome connection for this machine";
458
+ lines.push(`• This pi session is ${roleLabel}.`);
458
459
  let extensionAlive = false;
459
460
  let versionMismatch = false;
460
461
  try {
@@ -469,44 +470,44 @@ Usage rules:
469
470
  if (version.extensionVersion && version.extensionVersion !== PI_CHROME_VERSION) {
470
471
  versionMismatch = true;
471
472
  lines.push(
472
- `✗ EXTENSION VERSION MISMATCH: companion extension is v${version.extensionVersion}, but pi-chrome is v${PI_CHROME_VERSION}.`,
473
- ` All chrome_* tools will run with the OLD extension code until this is fixed.`,
474
- ` Fix: open chrome://extensions and click reload on "Pi Chrome Connector".`,
475
- ` (Future version drifts will self-heal: the extension now polls pi-chrome's expected version and reloads itself.)`,
473
+ `✗ The Chrome companion extension is on an old version (${version.extensionVersion}); this pi-chrome is ${PI_CHROME_VERSION}.`,
474
+ ` Every Chrome action will run with the old code until you reload the extension.`,
475
+ ` Fix: open chrome://extensions and click the refresh icon on 'Pi Chrome Connector'.`,
476
+ ` (After this one-time fix, future updates reload automatically.)`,
476
477
  );
477
478
  } else {
478
- lines.push(`✓ Companion Chrome extension responding (ID: ${version.extensionId ?? "?"}, ext v${version.extensionVersion ?? "?"}, latency ${latencyMs}ms)`);
479
+ lines.push(`✓ Chrome is connected (companion extension v${version.extensionVersion ?? "?"}, responded in ${latencyMs}ms).`);
479
480
  }
480
481
  } catch (error) {
481
482
  const message = (error as Error).message;
482
- lines.push(`✗ Companion Chrome extension not responding: ${message}`);
483
+ lines.push(`✗ Chrome isn't responding: ${message}`);
483
484
  if (message.includes("older pi-chrome without multi-session")) {
484
- lines.push(" Fix: restart the Pi session that owns the bridge (it was started on an older pi-chrome).");
485
+ lines.push(" Fix: quit and restart the pi session that first opened the Chrome connection (it was on an older pi-chrome).");
485
486
  } else {
486
- lines.push(" Fix: run /chrome-onboard, then load the bundled browser-extension folder in chrome://extensions and keep that Chrome window open.");
487
+ lines.push(" Fix: run /chrome-onboard to install the Chrome companion extension, then keep that Chrome window open.");
487
488
  }
488
489
  }
489
490
 
490
491
  if (extensionAlive && !versionMismatch) {
491
- // MAIN-world evaluate probe.
492
+ // Sanity-check that pi-chrome can actually run code in the active tab.
492
493
  try {
493
494
  const value = await bridge.send("page.evaluate", { expression: "1+1", awaitPromise: true, foreground: false }, 10_000);
494
- if (value === 2) lines.push(`✓ chrome_evaluate("1+1") = 2`);
495
- else lines.push(`⚠ chrome_evaluate("1+1") returned ${JSON.stringify(value)} (expected 2). The current tab may have a restrictive CSP or be a chrome:// URL.`);
495
+ if (value === 2) lines.push(`✓ pi-chrome can run code in the active Chrome tab.`);
496
+ else lines.push(`⚠ pi-chrome ran code in the active tab but got an unexpected result (${JSON.stringify(value)}). The current tab may be locked-down (a Chrome internal page or a strict site).`);
496
497
  } catch (error) {
497
- lines.push(`✗ chrome_evaluate failed: ${(error as Error).message}`);
498
+ lines.push(`✗ pi-chrome can't run code in the active tab: ${(error as Error).message}`);
498
499
  }
499
500
 
500
- // Capability probe via MAIN-world helper.
501
+ // Surface obvious site-side automation flags so the user knows why a site might block pi.
501
502
  try {
502
503
  const probe = (await bridge.send("page.probe", { foreground: false }, 10_000)) as Record<string, unknown>;
503
- if (probe && probe.arithmetic === 2) lines.push(`✓ MAIN-world helper injection works (location=${hostnameOf(String(probe.location))})`);
504
- if (probe && probe.webdriver) lines.push(`⚠ navigator.webdriver=true on current tab site fingerprinting may flag automation.`);
504
+ if (probe && probe.arithmetic === 2) lines.push(`✓ The active tab is ${hostnameOf(String(probe.location))} and accepts pi-chrome's commands.`);
505
+ if (probe && probe.webdriver) lines.push(`⚠ Your Chrome is reporting itself as automated to websites. Some sites use this signal to block sign-ins or bot checks.`);
505
506
  } catch (error) {
506
- lines.push(`⚠ page.probe failed: ${(error as Error).message}`);
507
+ lines.push(`⚠ Couldn't inspect the active tab: ${(error as Error).message}`);
507
508
  }
508
509
  } else if (versionMismatch) {
509
- lines.push(`… Skipped MAIN-world capability checks because the loaded extension is stale.`);
510
+ lines.push(`… Skipped the remaining checks until you reload the Chrome extension.`);
510
511
  }
511
512
 
512
513
  // Real-input mode probe (plain English for the user).
@@ -652,11 +653,11 @@ Usage rules:
652
653
 
653
654
  pi.registerCommand("chrome-background", {
654
655
  description:
655
- "Toggle silent/background mode for chrome_* tools. Background ON: chrome_* tools act silently your editor/terminal keeps focus, Chrome does not pop up, your workflow is not interrupted. Background OFF (default): Chrome focuses and activates the target tab so you can watch the agent work, useful for demos, pair-driving, and debugging — tradeoff: Chrome pops up and steals focus. Pass `on` / `off` to set explicitly, or no argument to toggle.",
656
+ "Choose whether Chrome jumps to the front when pi-chrome acts. ON: pi-chrome works silently and Chrome stays in the background your editor or terminal keeps focus. OFF (default): Chrome pops up and switches to the right tab so you can watch what pi-chrome is doing. Pass `on` / `off`, or run with no argument to flip it.",
656
657
  getArgumentCompletions: (prefix) => {
657
658
  const items = [
658
- { value: "on", label: "on", description: "Run chrome_* actions silently without focusing Chrome" },
659
- { value: "off", label: "off", description: "Bring Chrome to the foreground for chrome_* actions (default)" },
659
+ { value: "on", label: "on", description: "Work silently. Chrome stays in the background. Your editor keeps focus." },
660
+ { value: "off", label: "off", description: "Bring Chrome to the front so you can watch (default)." },
660
661
  ];
661
662
  const lowered = prefix.toLowerCase();
662
663
  const matches = items.filter((item) => item.value.startsWith(lowered));
@@ -669,23 +670,23 @@ Usage rules:
669
670
  else backgroundDefault = !backgroundDefault;
670
671
  ctx.ui.notify(
671
672
  backgroundDefault
672
- ? "Chrome background mode ON. chrome_* tools will run silently. Your current app keeps focus."
673
- : "Chrome background mode OFF. chrome_* tools will focus Chrome and activate the target tab so you can watch the agent work.",
673
+ ? "Quiet mode on. pi-chrome will work in the background; Chrome won't steal focus."
674
+ : "Watch mode on. Chrome will pop to the front and switch tabs so you can see what pi-chrome is doing.",
674
675
  "info",
675
676
  );
676
677
  },
677
678
  });
678
679
 
679
680
  pi.registerCommand("chrome-onboard", {
680
- description: "Guide Chrome extension setup for the existing-profile bridge",
681
+ description: "Walk through installing the Chrome companion extension that pi-chrome needs to control your browser.",
681
682
  handler: async (_args, ctx) => {
682
683
  const extensionPath = browserExtensionPath();
683
684
  const proceed = await ctx.ui.confirm(
684
- "Chrome bridge setup",
685
- `This will open chrome://extensions and reveal the extension folder in Finder.\n\nAfter the windows open: enable Developer mode Load unpacked select:\n${extensionPath}\n\nPress Enter to continue, or Esc to cancel.`,
685
+ "Install the pi-chrome Chrome extension?",
686
+ `This opens Chrome's extensions page and reveals the folder pi-chrome needs you to load.\n\nWhen the windows open, in Chrome:\n 1. Turn on 'Developer mode' (top-right toggle).\n 2. Click 'Load unpacked' and choose the folder that just opened in Finder, or paste this path:\n ${extensionPath}\n\nPress Enter to continue, or Esc to cancel.`,
686
687
  );
687
688
  if (!proceed) {
688
- ctx.ui.notify("Chrome bridge setup cancelled", "info");
689
+ ctx.ui.notify("Cancelled. You can run /chrome-onboard again whenever you're ready.", "info");
689
690
  return;
690
691
  }
691
692
  if (process.platform === "darwin") {
@@ -694,7 +695,7 @@ Usage rules:
694
695
  await pi.exec("sh", ["-lc", `printf %s ${JSON.stringify(extensionPath)} | pbcopy`], { cwd: workspaceCwd(ctx), timeout: 5_000 }).catch(() => undefined);
695
696
  }
696
697
  ctx.ui.notify(
697
- "Chrome bridge setup opened. The extension path has been copied to your clipboard. After loading it, run /chrome-doctor.",
698
+ "Chrome and Finder should be open. The extension folder path is on your clipboard. After you click 'Load unpacked' and pick it, run /chrome-doctor to confirm everything is connected.",
698
699
  "info",
699
700
  );
700
701
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-chrome",
3
- "version": "0.11.4",
3
+ "version": "0.11.5",
4
4
  "description": "Drive your existing logged-in Chrome from Pi — no re-login, no throwaway profile, watch the agent work in real time (or toggle quiet background mode).",
5
5
  "keywords": [
6
6
  "pi-package",