kimi-webbridge-fleet
Let one AI agent drive several of your real Chrome profiles at once — each with its own Google login — instead of one at a time. A drop-in layer over Kimi WebBridge: no patching, your existing :10086 calls keep working — you just add a "profile" field.
Kimi WebBridge lets an AI agent drive your real Chrome with your real logins. By design it has a single connection slot: one daemon, one extension, one profile. If you have a work account and a personal account, only one can be driven at a time — the other is rejected until you quit its Chrome or toggle its extension off.
kimi-webbridge-fleet removes that limit without patching anything. It runs one stock daemon per profile on its own port, and puts a small router on the usual :10086 so your existing calls keep working — you just add a "profile" field.
Before / after
How it works
The stock daemon enforces two singletons:
-
Daemon singleton —
kimi-webbridge startrefuses to launch if anything answershttp://127.0.0.1:10086/status(the probe is hardcoded to:10086, regardless of--addr). -
Slot singleton — one daemon accepts exactly one extension connection.
The key observation: the daemon singleton only guards :10086. Leave :10086 empty and you can start as many daemons as you like on other ports — each its own independent slot. So fleet:
-
starts one stock daemon per profile on a deterministic port (
10100 + hash(profileDir)), each with its own state dir; -
runs a router on
:10086that proxies/commandto the right daemon based on a top-level"profile"field; -
leaves each profile's extension to connect to its own daemon's
/wsdirectly (the router only proxies HTTP).
No binary patching, nothing that breaks on a Kimi WebBridge upgrade.
Implementing this natively? If you maintain Kimi WebBridge (or want to send a patch), docs/UPSTREAM-NATIVE-MULTIPROFILE.md is an explicit, agent-followable spec for adding multi-profile support inside the daemon + extension — grounded in the observed protocol, with acceptance tests. With those changes, fleet becomes unnecessary.
Platform & scope
macOS + Google Chrome only, right now. The implementation leans on macOS-specific commands and Chrome-specific paths:
Used for macOS command / path (this tool) Linux/Windows equivalent (not yet implemented)
Open a profile window / wake the extension
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --profile-directory=… <urls> (binary direct, headful)
google-chrome --profile-directory=… / start chrome …
Is Chrome running
pgrep -x "Google Chrome"
pgrep chrome / tasklist
Quit Chrome (to write storage.local)
osascript … quit then pkill
pkill chrome / taskkill
Force-install policy
defaults write com.google.Chrome …
policy JSON in /etc/opt/chrome/policies/… / registry
Profiles + sessions + extension registry
~/Library/Application Support/Google/Chrome
~/.config/google-chrome / %LOCALAPPDATA%\…\Chrome\User Data
Porting is mostly swapping those helpers (src/profiles.mjs, src/extension.mjs).
PRs welcome. Chromium/Brave/Edge would also need their own paths and extension id.
Requirements
-
macOS + Google Chrome, and a working Kimi WebBridge install (see below).
-
Node.js ≥ 18 (no npm dependencies — pure built-ins; runs under
buntoo).
Installing Kimi WebBridge (prerequisite)
Two steps, straight from kimi.com/features/webbridge (the "With Local Agent" flow):
1. Get the Chrome extension — Kimi WebBridge on the Chrome Web Store.
2. Install the local daemon with the official bootstrap installer:
curl -fsSL https://cdn.kimi.com/webbridge/install.sh | bash
This downloads the binary to ~/.kimi-webbridge/bin/kimi-webbridge (the path fleet orchestrates), starts the daemon, and installs the WebBridge skill into detected AI-agent runtimes.
# verify it's installed and the stock daemon answers
~/.kimi-webbridge/bin/kimi-webbridge status
# keep it current
~/.kimi-webbridge/bin/kimi-webbridge upgrade
Install
As a Claude Code skill/plugin — so your agent can drive the fleet itself:
/plugin marketplace add jeet-dhandha/kimi-webbridge-fleet
/plugin install kimi-webbridge-fleet@kwb
As a CLI — via npm, no clone:
npx kimi-webbridge-fleet profiles # one-off
npm i -g kimi-webbridge-fleet # puts `kwb` on your PATH
From source — for hacking / PRs:
git clone https://github.com/jeet-dhandha/kimi-webbridge-fleet.git
cd kimi-webbridge-fleet
npm link # optional: puts `kwb` on your PATH
# or just run: node bin/kwb.mjs <cmd>
Quickstart
# 1. See your profiles, their assigned ports, and which have the extension
kwb profiles
# 2. Point each profile's extension at its OWN daemon — zero clicks (no popup).
# This writes the extension's local_url on disk, which needs Chrome closed, so
# kwb connect quits Chrome for you (your session is saved for restore).
kwb connect "Work" "Personal"
# 3. Bring up the daemons + router on :10086, reopen each window, WAKE each extension,
# and poll until each reports "✓ connected". (If Chrome is already fully quit,
# `kwb up` alone performs the connect write for you — step 2 is optional then.)
kwb up "Work" "Personal"
# 4. Drive any profile by name — same call, one extra field:
curl -s -X POST http://127.0.0.1:10086/command \
-H 'Content-Type: application/json' \
-d '{"action":"navigate","args":{"url":"https://search.google.com/search-console"},"session":"audit","profile":"Work"}'
# 5. When done
kwb down # stops the fleet, restores the stock :10086 daemon
A request with no "profile" is routed to your default (last-used profile whose daemon is up, or KWB_DEFAULT_PROFILE).
Example — two live logins, no extension dance
You want a morning digest: unread Reddit DMs from your Personal account and unread Slack mentions from your Work email — pulled, cross-referenced, summarized. Stock Kimi WebBridge gives you one slot, so you'd drive one account, then quit Chrome or toggle its extension off to free the slot for the other — serial, and you lose the live session. Fleet keeps both connected at once; same call, you just name the profile:
# Reddit — Personal profile
curl -s -X POST http://127.0.0.1:10086/command -H 'Content-Type: application/json' \
-d '{"action":"navigate","args":{"url":"https://www.reddit.com/message/unread/"},"session":"digest","profile":"Personal"}'
# Slack — Work profile, same instant, nothing toggled off
curl -s -X POST http://127.0.0.1:10086/command -H 'Content-Type: application/json' \
-d '{"action":"navigate","args":{"url":"https://app.slack.com/client"},"session":"digest","profile":"Work"}'
Read both, merge, act — two real sessions, zero juggling. Same trick scales to many Google accounts (Gmail / Drive / Ads / Search Console) running side by side.
Zero-click connect (no popup)
Pointing a profile's extension at its own daemon port used to be a manual click in the extension popup. Fleet now does it for you, with no popup, no CDP, no dependencies — in two halves, both pure Node:
-
Write the URL. The extension's daemon URL is a plain
local_urlkey in itschrome.storage.localLevelDB (which, unlike Secure Preferences, is not integrity-protected).kwb connectwrites it directly while Chrome is closed (LevelDB is single-writer), replacing the popup's Connect click. The value persists, so laterkwb upruns just reconnect. -
Wake the worker. Writing the URL isn't enough on its own: the Kimi WebBridge MV3 service worker only re-reads
local_urlwhen it starts, but it registers nochrome.runtime.onStartuplistener, so Chrome never auto-starts it on launch — on an already-set-up profile the write would otherwise sit unused.kwb upwakes the worker by opening the extension's own popup page (chrome-extension://<id>/popup.html) as a tab — the headful equivalent of clicking the toolbar icon. Chrome is launched via its binary directly (headful), since macOSopendoesn't reliably forward achrome-extension://URL.
This works for both Chrome-Web-Store installs and unpacked / developer-mode
builds (whose extension id Chrome assigns unpredictably — fleet identifies the extension
by name and reads back whichever id it was given). To point a profile back at the stock
single :10086 bridge: kwb connect "Work" --restore.
Why not CDP / --remote-debugging-port? Branded Chrome 136+ refuses remote debugging on
the default user-data-dir where real profiles live, and --load-extension is ignored in
Google Chrome 137+. The on-disk write + popup wake sidesteps both.
CLI
Command What it does
kwb profiles
List profiles, hashed ports, e