diff options
| author | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-13 01:55:49 -0400 |
|---|---|---|
| committer | Tianren Dong <tianrendong.dtr@gmail.com> | 2026-05-13 01:55:49 -0400 |
| commit | c53af2f813347f8e36f3ff44ab97a3cdbbb15387 (patch) | |
| tree | 10a963b4d6e2ef2f2a328372bab04c7ccce647d1 | |
| parent | f0d86b4f73af9e8f3d4026ed74f35d4875046994 (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.json | 2 | ||||
| -rw-r--r-- | extensions/chrome-profile-bridge/browser-extension/service_worker.js | 12 | ||||
| -rw-r--r-- | extensions/chrome-profile-bridge/index.ts | 77 | ||||
| -rw-r--r-- | package.json | 2 |
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", |
