https://github.com/lugondev/flow-google-captcha
WXT-based Chrome/Firefox extension that bridges a local agent to the Google Flow API on labs.google — captures the bearer token, solves enterprise reCAPTCHA from a real Flow tab, and proxies authenticated REST + tRPC calls through the browser so reCAPTCHA stays bound to a legitimate session.
https://github.com/lugondev/flow-google-captcha
api-proxy automation bearer-token browser-extension chrome-extension firefox-extension google-flow labs-google manifest-v3 recaptcha recaptcha-solver trpc typescript wxt
Last synced: 14 days ago
JSON representation
WXT-based Chrome/Firefox extension that bridges a local agent to the Google Flow API on labs.google — captures the bearer token, solves enterprise reCAPTCHA from a real Flow tab, and proxies authenticated REST + tRPC calls through the browser so reCAPTCHA stays bound to a legitimate session.
- Host: GitHub
- URL: https://github.com/lugondev/flow-google-captcha
- Owner: lugondev
- Created: 2026-06-07T04:38:37.000Z (17 days ago)
- Default Branch: main
- Last Pushed: 2026-06-08T09:03:22.000Z (16 days ago)
- Last Synced: 2026-06-08T11:06:08.149Z (16 days ago)
- Topics: api-proxy, automation, bearer-token, browser-extension, chrome-extension, firefox-extension, google-flow, labs-google, manifest-v3, recaptcha, recaptcha-solver, trpc, typescript, wxt
- Language: TypeScript
- Size: 134 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Flow Helper — WXT
A Chrome / Firefox extension that bridges a local Python agent to the Google
Flow API on [labs.google](https://labs.google). It captures the user's bearer
token, solves enterprise reCAPTCHA from a real Flow tab, and proxies
authenticated API + tRPC calls through the browser context so reCAPTCHA stays
bound to a legitimate session.
> WXT rewrite of [lugondev/flow-google-captcha](https://github.com/lugondev/flow-google-captcha).
## What it does
| Subsystem | Where | What it does |
| --- | --- | --- |
| **Token capture** | `entrypoints/background/index.ts` | Sniffs the `Authorization: Bearer ya29.…` header on every `aisandbox-pa.googleapis.com` request via `chrome.webRequest` and stores it in `chrome.storage.local`. |
| **reCAPTCHA solver** | `entrypoints/flow-bridge-main.ts` | Injected into the Flow page's MAIN world, calls `grecaptcha.enterprise.execute(SITE_KEY, { action })` when asked. |
| **API proxy** | `entrypoints/background/api-proxy.ts` | Solves a captcha, splices the token into the request body's `recaptchaContext.token`, then re-emits the request with the captured bearer token from the browser context. |
| **tRPC proxy** | `entrypoints/background/api-proxy.ts` | Same pattern for `labs.google/fx/api/trpc/*` calls (no captcha needed). |
| **TRPC media-URL harvester** | `entrypoints/flow-bridge-main.ts` | Monkey-patches `window.fetch` to clone TRPC responses and forward fresh signed GCS media URLs back to the agent. |
| **Telemetry** | `entrypoints/background/telemetry.ts` | Sends randomized `FLOW_*_LATENCY` / `GRID_SCROLL_DEPTH` events to Google's analytics endpoints every 45–120s to keep the session looking organic. |
| **WS transport** | `entrypoints/background/websocket.ts` | Connects to a local agent over `ws://127.0.0.1:9222`. API responses are also POSTed to `http://127.0.0.1:8100/api/ext/callback` so they survive transient WS disconnects. |
| **UI** | `entrypoints/popup/`, `entrypoints/sidepanel/` | Status, metrics, request log, ON/OFF toggle. |
## Project layout
```
flow-google-captcha/
├── entrypoints/
│ ├── background/
│ │ ├── index.ts # main service worker
│ │ ├── state.ts # global state + persistent storage
│ │ ├── websocket.ts # agent <-> extension transport
│ │ ├── captcha.ts # captcha solving from the active Flow tab
│ │ ├── api-proxy.ts # API + tRPC request proxy
│ │ ├── telemetry.ts # human-like telemetry
│ │ ├── log.ts # in-memory request log
│ │ ├── trpc-media.ts # tRPC media URL extractor
│ │ └── types.ts
│ ├── flow-bridge.content/
│ │ └── index.ts # ISOLATED-world bridge (uses injectScript)
│ ├── flow-bridge-main.ts # unlisted MAIN-world script (grecaptcha + fetch hook)
│ ├── popup/
│ │ ├── index.html
│ │ └── main.ts
│ └── sidepanel/
│ ├── index.html
│ └── main.ts
├── public/
│ ├── icon/{16,32,48,96,128}.png
│ └── rules.json # declarativeNetRequest (Referer/Origin)
├── wxt.config.ts
├── package.json
├── tsconfig.json
└── .gitignore
```
## Setup
```bash
pnpm install # or npm install / yarn
```
## Development
```bash
# Chrome / Edge / Brave (Chromium MV3)
pnpm dev
# Firefox
pnpm dev:firefox
```
WXT will start a dev server with HMR for the UI, then print the path to a
rebuilt `.output/chrome-mv3` directory. Load it as an **unpacked extension**
in `chrome://extensions` (enable Developer mode first).
## Build
```bash
pnpm build # .output/chrome-mv3
pnpm build:firefox # .output/firefox-mv2
pnpm zip # produces a .zip ready for the Chrome Web Store
```
## Message protocol with the local agent
The extension speaks two transports to the local Python agent:
### WebSocket `ws://127.0.0.1:9222`
Inbound (extension → agent, unsolicited):
```json
{ "type": "extension_ready", "flowKeyPresent": true, "tokenAge": 12345 }
{ "type": "token_captured", "flowKey": "ya29.…" }
{ "type": "media_urls_refresh", "urls": [{ "mediaType": "video", "url": "…", "mediaId": "…" }] }
```
Outbound (agent → extension, request/response):
```json
{ "method": "api_request", "id": "abc", "params": {
"url": "https://aisandbox-pa.googleapis.com/v1/projects/.../flowMedia:batchGenerateImages",
"method": "POST", "headers": {...}, "body": {...},
"captchaAction": "IMAGE_GENERATION"
} }
{ "method": "trpc_request", "id": "def", "params": {
"url": "https://labs.google/fx/api/trpc/...",
"method": "POST", "body": {...}
} }
{ "method": "solve_captcha", "id": "ghi", "params": { "captchaAction": "VIDEO_GENERATION" } }
{ "method": "get_status", "id": "jkl" }
```
### HTTP callback `http://127.0.0.1:8100/api/ext/callback`
API responses (`{id, status, data}` / `{id, error}`) are POSTed here so they
survive a transient WS blip. WS is the fallback when the callback is
unreachable.
### `captchaAction` values
`IMAGE_GENERATION` · `VIDEO_GENERATION` — passed to
`grecaptcha.enterprise.execute(SITE_KEY, { action })`.
## Permissions
| Permission | Why |
| --- | --- |
| `storage` | Persist `flowKey` and metrics |
| `alarms` | Keep-alive pings, token refresh, WS reconnect |
| `tabs` | Find / open the active Flow tab |
| `webRequest` + `extraHeaders` | Sniff bearer token from outgoing requests |
| `scripting` | Manually re-inject content script when WS reconnect races page load |
| `declarativeNetRequest` | Set `Referer` / `Origin` on API calls |
| `sidePanel` | The side panel UI |
Host permissions: `https://labs.google/*`, `https://aisandbox-pa.googleapis.com/*`,
`http://127.0.0.1:8100/*`.
## Caveats
* This extension exists to drive Google Flow from a local Python agent. It
re-uses the user's existing browser session and token. It does not bypass
Google's reCAPTCHA — it solves it from the user's own logged-in Flow tab.
* The site key `6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV` is Google's
enterprise key for `labs.google`. It is part of the public page source and
is not a secret.
* Bearer tokens expire after ~60 min. The extension auto-refreshes every
45 min via a background alarm; the side panel will also auto-refresh when
the token is older than 55 min.
## License
See [lugondev/flow-google-captcha](https://github.com/lugondev/flow-google-captcha) for license terms.