aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianren Dong <tianrendong.dtr@gmail.com>2026-05-13 01:55:49 -0400
committerTianren Dong <tianrendong.dtr@gmail.com>2026-05-13 01:55:49 -0400
commitc53af2f813347f8e36f3ff44ab97a3cdbbb15387 (patch)
tree10a963b4d6e2ef2f2a328372bab04c7ccce647d1
parentf0d86b4f73af9e8f3d4026ed74f35d4875046994 (diff)
pi-chrome 0.11.3: plain-English copy in /chrome-trusted + /chrome-doctor
User-facing strings rewritten in layman's terms — no more 'CDP', 'isTrusted', 'user-activation gates', 'synthetic events', 'debugger API'. /chrome-trusted picker now explains each mode by what users actually experience: yellow banner, which sites work, when to use it. /chrome-doctor reports 'Click mode' instead of 'Trusted-input mode'. Tool descriptions (read by the agent) keep precise terminology.
-rw-r--r--extensions/chrome-profile-bridge/browser-extension/manifest.json2
-rw-r--r--extensions/chrome-profile-bridge/browser-extension/service_worker.js12
-rw-r--r--extensions/chrome-profile-bridge/index.ts77
-rw-r--r--package.json2
4 files changed, 50 insertions, 43 deletions
diff --git a/extensions/chrome-profile-bridge/browser-extension/manifest.json b/extensions/chrome-profile-bridge/browser-extension/manifest.json
index c230cc9..4b0f971 100644
--- a/extensions/chrome-profile-bridge/browser-extension/manifest.json
+++ b/extensions/chrome-profile-bridge/browser-extension/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Pi Chrome Connector",
- "version": "0.11.2",
+ "version": "0.11.3",
"description": "Lets Pi control tabs in Chrome via a local connector at 127.0.0.1.",
"permissions": ["tabs", "scripting", "storage", "activeTab", "alarms", "webNavigation", "debugger"],
"host_permissions": ["<all_urls>", "http://127.0.0.1:17318/*"],
diff --git a/extensions/chrome-profile-bridge/browser-extension/service_worker.js b/extensions/chrome-profile-bridge/browser-extension/service_worker.js
index 0a836e8..04e8a4e 100644
--- a/extensions/chrome-profile-bridge/browser-extension/service_worker.js
+++ b/extensions/chrome-profile-bridge/browser-extension/service_worker.js
@@ -169,7 +169,7 @@ async function cdpMoveTo(tabId, x, y) {
const entry = attachedTabs.get(tabId);
const startX = entry?.pointer?.x ?? Math.max(20, Math.min(400, x - 200));
const startY = entry?.pointer?.y ?? Math.max(20, Math.min(400, y - 200));
- const n = Math.max(10, Math.min(36, Math.round(Math.hypot(x - startX, y - startY) / 20)));
+ const n = Math.max(18, Math.min(42, Math.round(Math.hypot(x - startX, y - startY) / 18)));
for (let i = 1; i <= n; i++) {
const t = i / n;
const ease = t * t * (3 - 2 * t);
@@ -368,13 +368,13 @@ async function trustedScroll(params) {
const y = resolved.rect ? resolved.rect.top + Math.min(resolved.rect.height, 600) / 2 : resolved.y;
const totalY = params.deltaY || 0, totalX = params.deltaX || 0;
// Per-event delta cap so IntersectionObserver / scroll-driven animations get gradient samples.
- // A real wheel notch is ~50-120px; we aim for the lower end so visibility transitions are visible.
- const MAX_STEP = 60;
+ // A trackpad inertia tick often delivers ~10-30px per frame; using ~25px keeps small-target
+ // visibility transitions detectable while not making large scrolls take forever.
+ const MAX_STEP = 25;
const peak = Math.max(Math.abs(totalY), Math.abs(totalX));
- // We weight events with mild front-loading. The peak weight = 1.5 / n (vs uniform 1/n), so the
- // first event is ~1.5× average. Choose n so even the peak event stays under MAX_STEP.
+ // Front-loaded weights peak at ~1.5× average, so choose n so peak event stays under MAX_STEP.
const minN = Math.ceil(peak * 1.5 / MAX_STEP);
- const n = Math.max(6, Math.min(80, params.steps || Math.max(minN, 12)));
+ const n = Math.max(6, Math.min(200, params.steps || Math.max(minN, 12)));
// Front-loaded but smooth weights: w[i] = 1 + 0.5 * (1 - i/(n-1)) so the first event has
// weight 1.5, the last has 1.0, average ~1.25; redistribution stays predictable.
const w = [];
diff --git a/extensions/chrome-profile-bridge/index.ts b/extensions/chrome-profile-bridge/index.ts
index fc1b6c5..0dd18b8 100644
--- a/extensions/chrome-profile-bridge/index.ts
+++ b/extensions/chrome-profile-bridge/index.ts
@@ -509,7 +509,7 @@ Usage rules:
lines.push(`… Skipped MAIN-world capability checks because the loaded extension is stale.`);
}
- // Trusted-input (chrome.debugger) probe.
+ // Real-input mode probe (plain English for the user).
if (extensionAlive && !versionMismatch) {
try {
const status = (await bridge.send("trusted.status", {}, 5_000)) as {
@@ -518,19 +518,20 @@ Usage rules:
permissionGranted?: boolean;
};
if (status.permissionGranted) {
- const attached = status.attachedTabs && status.attachedTabs.length ? `; attached to tab ${status.attachedTabs.join(",")}` : "";
+ const banner = status.attachedTabs && status.attachedTabs.length ? ` (yellow banner up on ${status.attachedTabs.length} tab(s))` : "";
const note =
status.mode === "auto"
- ? " — smart-retry enabled: synthetic input runs first; if a click/type produced no page change AND the target looks gated, the call is automatically re-run with trusted CDP (yellow debugger banner appears only for that retry)."
+ ? " Clicks/keys are quiet by default; if a site rejects a quiet click, pi-chrome retries it once with a real-looking click. Yellow 'pi-chrome is controlling Chrome' banner shows only when that retry happens."
: status.mode === "on"
- ? " — every chrome_* call goes through CDP; the yellow debugger banner is visible while attached."
- : " — synthetic events only; pass trusted=true on chrome_click/type/etc, or switch to auto/on with /chrome-trusted, when isTrusted or user-activation gates matter.";
- lines.push(`✓ Trusted-input mode available via chrome.debugger (current: ${status.mode ?? "off"}${attached}).${note}`);
+ ? " Every click and keystroke uses a real-looking event. Yellow 'pi-chrome is controlling Chrome' banner stays up on every tab pi-chrome touches."
+ : " All clicks are quiet, no yellow banner. Some sites (sign-ins, copy buttons, file pickers, paywalls) may silently ignore them. Switch to /chrome-trusted auto if a site isn't responding.";
+ const label = status.mode === "auto" ? "auto (smart upgrade)" : status.mode === "on" ? "on (always real-looking)" : "off (always quiet)";
+ lines.push(`✓ Click mode: ${label}${banner}.${note}`);
} else {
- lines.push(`⚠ chrome.debugger API unavailable. The extension is missing the "debugger" permission — reload the extension in chrome://extensions and accept the new permission prompt.`);
+ lines.push(`⚠ Can't send real-looking clicks yet — the companion extension is missing a permission. Open chrome://extensions, click reload on 'Pi Chrome Connector', and accept the new permission prompt.`);
}
} catch (error) {
- lines.push(`⚠ trusted.status probe failed: ${(error as Error).message}`);
+ lines.push(`⚠ Couldn't check click mode: ${(error as Error).message}`);
}
}
@@ -540,13 +541,13 @@ Usage rules:
pi.registerCommand("chrome-trusted", {
description:
- "Toggle trusted-input mode for chrome_* tools. ON: all clicks/types/etc go through chrome.debugger (CDP) so events are browser-trusted (isTrusted=true) and satisfy user-activation gates (clipboard, fullscreen, autoplay, file picker). Tradeoff: Chrome pins a yellow 'started debugging this browser' banner to the top of any tab in use. OFF (default): synthetic DOM events. AUTO: attach on demand only when a per-call trusted=true is passed. STATUS: print current mode and attached tabs.",
+ "Choose how realistically pi-chrome should drive Chrome. Real-looking clicks/keys unlock things like copy-to-clipboard buttons, file pickers, and sign-in pages, but show a yellow 'pi-chrome is controlling Chrome' banner. Three modes:\n auto (default) — quiet by default; auto-upgrade when a site blocks the quiet click.\n off — always quiet, no banner; some sites won't accept these clicks.\n on — always real-looking, banner stays up the whole session.\n status — show the current mode.",
getArgumentCompletions: (prefix) => {
const items = [
- { value: "on", label: "on", description: "All chrome_* tools dispatch via CDP. Yellow debugger banner appears." },
- { value: "off", label: "off", description: "Synthetic events only (default)." },
- { value: "auto", label: "auto", description: "Use CDP only when a tool passes trusted=true." },
- { value: "status", label: "status", description: "Show current trusted mode and any attached tabs." },
+ { value: "auto", label: "auto", description: "Default. Quiet clicks; upgrade to real ones only when a site rejects them." },
+ { value: "off", label: "off", description: "Always quiet. No banner. Some sites won't accept the clicks." },
+ { value: "on", label: "on", description: "Always real-looking clicks. Yellow banner stays up. Best for stubborn sites." },
+ { value: "status", label: "status", description: "Show the current mode." },
];
const lowered = prefix.toLowerCase();
const matches = items.filter((item) => item.value.startsWith(lowered));
@@ -560,37 +561,43 @@ Usage rules:
try {
status = (await bridge.send("trusted.status", {}, 5_000)) as typeof status;
} catch (error) {
- ctx.ui.notify(`Failed to read trusted mode: ${(error as Error).message}`, "warning");
+ ctx.ui.notify(`Couldn't check current mode: ${(error as Error).message}`, "warning");
return;
}
if (!status) return;
if (!status.permissionGranted) {
ctx.ui.notify(
- "chrome.debugger API unavailable — the extension is missing the 'debugger' permission. Open chrome://extensions, reload 'Pi Chrome Connector', and accept the new permission prompt.",
+ "pi-chrome can't drive real-looking clicks yet — the companion extension is missing a permission. Open chrome://extensions, click reload on 'Pi Chrome Connector', and accept the new permission prompt that appears.",
"warning",
);
return;
}
- const attached = status.attachedTabs?.length ? ` — currently attached to tab ${status.attachedTabs.join(",")}` : "";
+ const MODE_NAMES: Record<string, string> = {
+ auto: "auto (smart upgrade)",
+ off: "off (always quiet)",
+ on: "on (always real-looking)",
+ };
+ const friendly = (m: string) => MODE_NAMES[m] ?? m;
+ const attached = status.attachedTabs?.length ? ` — yellow banner currently up on ${status.attachedTabs.length} tab(s)` : "";
const current = status.mode;
let target = rawArg;
if (target === "status") {
- ctx.ui.notify(`Trusted-input mode: ${current}${attached}`, "info");
+ ctx.ui.notify(`Current mode: ${friendly(current)}${attached}`, "info");
return;
}
if (!target) {
- // Interactive picker. Show current mode + tradeoffs in each label.
+ // Interactive picker. Plain-English descriptions; no jargon.
const options = [
- `auto${current === "auto" ? " (current)" : ""} — default; synthetic first, retry with CDP only when a call looks gated`,
- `off${current === "off" ? " (current)" : ""} — synthetic DOM events only; never auto-retry`,
- `on${current === "on" ? " (current)" : ""} — every chrome_* call goes through CDP (yellow debugger banner permanently visible)`,
- `status — print current mode and any attached tabs\u2026`,
+ `auto${current === "auto" ? " (current)" : ""} — quiet by default; if a site rejects a quiet click, retry it once with a real-looking click. Yellow banner appears only when needed. Recommended for everyday use.`,
+ `off${current === "off" ? " (current)" : ""} — always quiet. No yellow banner, ever. Fast and unobtrusive, but some sites (sign-in pages, copy-to-clipboard buttons, file pickers, paywalls) will silently ignore the click.`,
+ `on${current === "on" ? " (current)" : ""} — every click and keystroke uses a real-looking event. Yellow 'pi-chrome is controlling Chrome' banner stays at the top of every Chrome window the whole session. Best when working a stubborn site for a long stretch.`,
+ `status — just show me which mode is on right now.`,
];
const picked = await ctx.ui.select(
- `Trusted-input mode (current: ${current}${attached})`,
+ `How realistic should pi-chrome's clicks be? (current: ${friendly(current)}${attached})`,
options,
);
if (!picked) return; // cancelled
@@ -598,29 +605,29 @@ Usage rules:
else if (picked.startsWith("off")) target = "off";
else if (picked.startsWith("auto")) target = "auto";
else if (picked.startsWith("status")) {
- ctx.ui.notify(`Trusted-input mode: ${current}${attached}`, "info");
+ ctx.ui.notify(`Current mode: ${friendly(current)}${attached}`, "info");
return;
}
}
if (!["on", "off", "auto"].includes(target)) {
- ctx.ui.notify(`Unknown argument '${rawArg}'. Use: on | off | auto | status, or run /chrome-trusted with no args for a picker.`, "warning");
+ ctx.ui.notify(`Unknown choice '${rawArg}'. Pick one of: auto | off | on | status, or just run /chrome-trusted with no argument.`, "warning");
return;
}
if (target === current) {
- ctx.ui.notify(`Trusted-input mode already ${current}.`, "info");
+ ctx.ui.notify(`Already in ${friendly(current)} mode.`, "info");
return;
}
- // Extra confirmation only on first-time "on" (warn about banner).
- if (target === "on" && current === "off") {
+ // Extra confirmation only on first-time "on" (warn about persistent banner).
+ if (target === "on" && current !== "on") {
const ok = await ctx.ui.confirm(
- "Turn on trusted-input mode?",
- "All chrome_* tools will dispatch through chrome.debugger (CDP). Events will arrive as isTrusted=true and satisfy user-activation gates (clipboard, fullscreen, autoplay, file picker).\n\nChrome will pin a yellow 'Pi Chrome Connector started debugging this browser' banner to the top of any debugged tab while attached. Clicking 'Cancel' on that banner detaches the debugger.",
+ "Always use real-looking clicks?",
+ "Every click and keystroke pi-chrome sends will now look like a real human action to websites. This unlocks copy-to-clipboard buttons, sign-in pages, file pickers, fullscreen, autoplay, and most bot-protected sites.\n\nThe tradeoff: Chrome will pin a yellow bar at the top of every Chrome window saying 'pi-chrome is controlling Chrome'. The bar stays visible for the rest of your pi session (or until you switch back to auto/off). Clicking 'Cancel' on the bar interrupts pi-chrome.",
);
if (!ok) {
- ctx.ui.notify("Trusted-input mode unchanged.", "info");
+ ctx.ui.notify("Mode unchanged.", "info");
return;
}
}
@@ -629,16 +636,16 @@ Usage rules:
const result = (await bridge.send("trusted.mode", { mode: target }, 5_000)) as { mode: string };
if (result.mode === "on") {
ctx.ui.notify(
- "Trusted-input mode ON. chrome_* tools now dispatch through chrome.debugger. The yellow debugger banner will appear when Chrome is next driven.",
+ "On. Every click and keystroke now looks real to websites. The yellow 'pi-chrome is controlling Chrome' banner will appear on every tab pi-chrome touches.",
"info",
);
} else if (result.mode === "off") {
- ctx.ui.notify("Trusted-input mode OFF. Synthetic events only. Any attached debugger sessions detached.", "info");
+ ctx.ui.notify("Off. All clicks are quiet now, no yellow banner. Note: some sites (sign-ins, copy buttons, file pickers, paywalls) may silently ignore these clicks.", "info");
} else {
- ctx.ui.notify("Trusted-input mode AUTO. CDP attaches only when a tool passes trusted=true.", "info");
+ ctx.ui.notify("Auto. Clicks stay quiet by default; pi-chrome will only switch to real-looking clicks when a site rejects a quiet one. The yellow banner will appear only when that retry happens.", "info");
}
} catch (error) {
- ctx.ui.notify(`Failed to set trusted mode: ${(error as Error).message}`, "warning");
+ ctx.ui.notify(`Couldn't switch mode: ${(error as Error).message}`, "warning");
}
},
});
diff --git a/package.json b/package.json
index 6da104d..711b911 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pi-chrome",
- "version": "0.11.2",
+ "version": "0.11.3",
"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).",
"keywords": [
"pi-package",