{"id":50732010,"url":"https://github.com/lugondev/flow-google-captcha","last_synced_at":"2026-06-10T09:32:24.418Z","repository":{"id":363301688,"uuid":"1261731562","full_name":"lugondev/flow-google-captcha","owner":"lugondev","description":"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.","archived":false,"fork":false,"pushed_at":"2026-06-08T09:03:22.000Z","size":137,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T11:06:08.149Z","etag":null,"topics":["api-proxy","automation","bearer-token","browser-extension","chrome-extension","firefox-extension","google-flow","labs-google","manifest-v3","recaptcha","recaptcha-solver","trpc","typescript","wxt"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lugondev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-07T04:38:37.000Z","updated_at":"2026-06-08T09:03:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lugondev/flow-google-captcha","commit_stats":null,"previous_names":["lugondev/flow-google-captcha"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lugondev/flow-google-captcha","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lugondev%2Fflow-google-captcha","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lugondev%2Fflow-google-captcha/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lugondev%2Fflow-google-captcha/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lugondev%2Fflow-google-captcha/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lugondev","download_url":"https://codeload.github.com/lugondev/flow-google-captcha/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lugondev%2Fflow-google-captcha/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34146871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-proxy","automation","bearer-token","browser-extension","chrome-extension","firefox-extension","google-flow","labs-google","manifest-v3","recaptcha","recaptcha-solver","trpc","typescript","wxt"],"created_at":"2026-06-10T09:32:23.627Z","updated_at":"2026-06-10T09:32:24.396Z","avatar_url":"https://github.com/lugondev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flow Helper — WXT\n\nA Chrome / Firefox extension that bridges a local Python agent to the Google\nFlow API on [labs.google](https://labs.google). It captures the user's bearer\ntoken, solves enterprise reCAPTCHA from a real Flow tab, and proxies\nauthenticated API + tRPC calls through the browser context so reCAPTCHA stays\nbound to a legitimate session.\n\n\u003e WXT rewrite of [lugondev/flow-google-captcha](https://github.com/lugondev/flow-google-captcha).\n\n## What it does\n\n| Subsystem | Where | What it does |\n| --- | --- | --- |\n| **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`. |\n| **reCAPTCHA solver** | `entrypoints/flow-bridge-main.ts` | Injected into the Flow page's MAIN world, calls `grecaptcha.enterprise.execute(SITE_KEY, { action })` when asked. |\n| **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. |\n| **tRPC proxy** | `entrypoints/background/api-proxy.ts` | Same pattern for `labs.google/fx/api/trpc/*` calls (no captcha needed). |\n| **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. |\n| **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. |\n| **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. |\n| **UI** | `entrypoints/popup/`, `entrypoints/sidepanel/` | Status, metrics, request log, ON/OFF toggle. |\n\n## Project layout\n\n```\nflow-google-captcha/\n├── entrypoints/\n│   ├── background/\n│   │   ├── index.ts        # main service worker\n│   │   ├── state.ts        # global state + persistent storage\n│   │   ├── websocket.ts    # agent \u003c-\u003e extension transport\n│   │   ├── captcha.ts      # captcha solving from the active Flow tab\n│   │   ├── api-proxy.ts    # API + tRPC request proxy\n│   │   ├── telemetry.ts    # human-like telemetry\n│   │   ├── log.ts          # in-memory request log\n│   │   ├── trpc-media.ts   # tRPC media URL extractor\n│   │   └── types.ts\n│   ├── flow-bridge.content/\n│   │   └── index.ts        # ISOLATED-world bridge (uses injectScript)\n│   ├── flow-bridge-main.ts # unlisted MAIN-world script (grecaptcha + fetch hook)\n│   ├── popup/\n│   │   ├── index.html\n│   │   └── main.ts\n│   └── sidepanel/\n│       ├── index.html\n│       └── main.ts\n├── public/\n│   ├── icon/{16,32,48,96,128}.png\n│   └── rules.json          # declarativeNetRequest (Referer/Origin)\n├── wxt.config.ts\n├── package.json\n├── tsconfig.json\n└── .gitignore\n```\n\n## Setup\n\n```bash\npnpm install            # or npm install / yarn\n```\n\n## Development\n\n```bash\n# Chrome / Edge / Brave (Chromium MV3)\npnpm dev\n\n# Firefox\npnpm dev:firefox\n```\n\nWXT will start a dev server with HMR for the UI, then print the path to a\nrebuilt `.output/chrome-mv3` directory. Load it as an **unpacked extension**\nin `chrome://extensions` (enable Developer mode first).\n\n## Build\n\n```bash\npnpm build              # .output/chrome-mv3\npnpm build:firefox      # .output/firefox-mv2\npnpm zip                # produces a .zip ready for the Chrome Web Store\n```\n\n## Message protocol with the local agent\n\nThe extension speaks two transports to the local Python agent:\n\n### WebSocket `ws://127.0.0.1:9222`\n\nInbound (extension → agent, unsolicited):\n\n```json\n{ \"type\": \"extension_ready\", \"flowKeyPresent\": true, \"tokenAge\": 12345 }\n{ \"type\": \"token_captured\",  \"flowKey\": \"ya29.…\" }\n{ \"type\": \"media_urls_refresh\", \"urls\": [{ \"mediaType\": \"video\", \"url\": \"…\", \"mediaId\": \"…\" }] }\n```\n\nOutbound (agent → extension, request/response):\n\n```json\n{ \"method\": \"api_request\", \"id\": \"abc\", \"params\": {\n    \"url\": \"https://aisandbox-pa.googleapis.com/v1/projects/.../flowMedia:batchGenerateImages\",\n    \"method\": \"POST\", \"headers\": {...}, \"body\": {...},\n    \"captchaAction\": \"IMAGE_GENERATION\"\n} }\n\n{ \"method\": \"trpc_request\", \"id\": \"def\", \"params\": {\n    \"url\": \"https://labs.google/fx/api/trpc/...\",\n    \"method\": \"POST\", \"body\": {...}\n} }\n\n{ \"method\": \"solve_captcha\", \"id\": \"ghi\", \"params\": { \"captchaAction\": \"VIDEO_GENERATION\" } }\n\n{ \"method\": \"get_status\", \"id\": \"jkl\" }\n```\n\n### HTTP callback `http://127.0.0.1:8100/api/ext/callback`\n\nAPI responses (`{id, status, data}` / `{id, error}`) are POSTed here so they\nsurvive a transient WS blip. WS is the fallback when the callback is\nunreachable.\n\n### `captchaAction` values\n\n`IMAGE_GENERATION` · `VIDEO_GENERATION` — passed to\n`grecaptcha.enterprise.execute(SITE_KEY, { action })`.\n\n## Permissions\n\n| Permission | Why |\n| --- | --- |\n| `storage` | Persist `flowKey` and metrics |\n| `alarms` | Keep-alive pings, token refresh, WS reconnect |\n| `tabs` | Find / open the active Flow tab |\n| `webRequest` + `extraHeaders` | Sniff bearer token from outgoing requests |\n| `scripting` | Manually re-inject content script when WS reconnect races page load |\n| `declarativeNetRequest` | Set `Referer` / `Origin` on API calls |\n| `sidePanel` | The side panel UI |\n\nHost permissions: `https://labs.google/*`, `https://aisandbox-pa.googleapis.com/*`,\n`http://127.0.0.1:8100/*`.\n\n## Caveats\n\n* This extension exists to drive Google Flow from a local Python agent. It\n  re-uses the user's existing browser session and token. It does not bypass\n  Google's reCAPTCHA — it solves it from the user's own logged-in Flow tab.\n* The site key `6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV` is Google's\n  enterprise key for `labs.google`. It is part of the public page source and\n  is not a secret.\n* Bearer tokens expire after ~60 min. The extension auto-refreshes every\n  45 min via a background alarm; the side panel will also auto-refresh when\n  the token is older than 55 min.\n\n## License\n\nSee [lugondev/flow-google-captcha](https://github.com/lugondev/flow-google-captcha) for license terms.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flugondev%2Fflow-google-captcha","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flugondev%2Fflow-google-captcha","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flugondev%2Fflow-google-captcha/lists"}