Skip to content

Instantly share code, notes, and snippets.

@heyalexej
Created July 6, 2026 14:35
Show Gist options
  • Select an option

  • Save heyalexej/26d734579cab5e616dcf0599e235ba21 to your computer and use it in GitHub Desktop.

Select an option

Save heyalexej/26d734579cab5e616dcf0599e235ba21 to your computer and use it in GitHub Desktop.
cdb: Chrome Debug Browser - launch Chrome with a copied non-default profile and CDP enabled

cdb - Chrome Debug Browser

cdb is a tiny shell shim that starts Chrome/Chromium with a non-default copied profile and the Chrome DevTools Protocol (CDP) enabled.

It is meant for local developer/agent workflows where a tool needs a classic CDP endpoint such as:

127.0.0.1:9222
http://127.0.0.1:9222/json/version

Why this exists

Since Chrome 136, Chrome no longer honors --remote-debugging-port or --remote-debugging-pipe against the default Chrome user-data directory.

This common command can therefore be misleading:

open -a "Google Chrome" --args --remote-debugging-port=9222

You may see the flag in ps, but no listener appears on 127.0.0.1:9222.

When launched directly, Chrome explains why:

DevTools remote debugging requires a non-default data directory.
Specify this using --user-data-dir.

Official Chrome announcement:

https://developer.chrome.com/blog/remote-debugging-port

cdb solves this by copying your normal signed-in Chrome user-data directory to a non-default temp directory, then launching Chrome from that copy with CDP enabled.

What it is

  • A generic Chrome/CDP launcher.
  • Useful for Playwright, Puppeteer, browser-tools, MCP servers, coding agents, or custom scripts.
  • A temporary session/profile manager for debug Chrome instances.

What it is not

  • Not tied to any specific AI/agent/oracle tool.
  • Not a browser automation client itself.
  • Not a permanent secret/session vault unless you deliberately set CDB_BASE outside /tmp.
  • Not the official Chrome DevTools MCP autoConnect flow.

Security warning

cdb copies browser profile data, including login state/cookies, into another directory.

Default location:

/tmp/cdb-$USER/<session>

Use only on a trusted local machine. Any CDP client connected to the debug port can inspect and control that browser session.

The temp profile is not deleted when Chrome closes. It is reused until /tmp cleanup/reboot or until you run cdb --fresh for that session.

If you need stronger isolation, use:

cdb --empty clean

Install

curl -fsSL https://gist.githubusercontent.com/heyalexej/26d734579cab5e616dcf0599e235ba21/raw/cdb -o ~/bin/cdb
chmod +x ~/bin/cdb

Or copy the cdb file somewhere on your PATH.

Requirements:

  • Bash
  • rsync
  • curl
  • Google Chrome or Chromium

Usage

cdb

Starts/reuses session default on port 9222:

/tmp/cdb-$USER/default

Named session:

cdb pro

Custom port:

cdb pro 9333

Open a URL:

cdb pro 9222 https://example.com

Pass normal Chrome flags/args after --:

cdb work 9222 -- --auto-open-devtools-for-tabs
cdb test 9333 https://example.com -- --incognito
cdb --empty clean 9224 -- --disable-extensions

Fresh copy after changing login/account in your real Chrome:

cdb --fresh pro

Kill a session:

cdb --kill pro

Check endpoint:

cdb --status 9222

Environment overrides

CDB_CHROME=/path/to/chrome cdb
CDB_SOURCE_PROFILE=/path/to/chrome/user-data-dir cdb
CDB_BASE=$HOME/.chrome-debug cdb
CDB_URL=https://example.com cdb

CDB_BASE=$HOME/.chrome-debug makes copied sessions persist across reboots.

CDP endpoints

After cdb starts, use:

127.0.0.1:9222

Useful browser URLs:

http://127.0.0.1:9222/json/version
http://127.0.0.1:9222/json/list

/json/version includes the webSocketDebuggerUrl needed by many Playwright/Puppeteer-style clients.

Alternatives

Depending on your use case, these may be better:

  • Chrome for Testing - recommended by Chrome for automation compatibility.
  • Chrome DevTools MCP --autoConnect - Chrome >=144 permission-based connection to the real running browser session.

Official autoConnect docs:

https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session

cdb is for the classic generic CDP-port workflow when you want a reusable host:port endpoint for arbitrary tools.

#!/usr/bin/env bash
set -euo pipefail
mode="copy"
session="default"
port="9222"
detect_chrome() {
if [[ -n "${CDB_CHROME:-}" ]]; then
printf '%s\n' "$CDB_CHROME"
return 0
fi
if [[ -x "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" ]]; then
printf '%s\n' "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
return 0
fi
command -v google-chrome-stable 2>/dev/null && return 0
command -v google-chrome 2>/dev/null && return 0
command -v chromium 2>/dev/null && return 0
command -v chromium-browser 2>/dev/null && return 0
return 1
}
detect_source_profile() {
if [[ -n "${CDB_SOURCE_PROFILE:-}" ]]; then
printf '%s\n' "$CDB_SOURCE_PROFILE"
return 0
fi
if [[ -d "$HOME/Library/Application Support/Google/Chrome" ]]; then
printf '%s\n' "$HOME/Library/Application Support/Google/Chrome"
return 0
fi
if [[ -d "$HOME/.config/google-chrome" ]]; then
printf '%s\n' "$HOME/.config/google-chrome"
return 0
fi
if [[ -d "$HOME/.config/chromium" ]]; then
printf '%s\n' "$HOME/.config/chromium"
return 0
fi
return 1
}
chrome="$(detect_chrome || true)"
source_profile="$(detect_source_profile || true)"
base="${CDB_BASE:-/tmp/cdb-$USER}"
chrome_args=()
cdb_args=()
# Everything after -- is passed through to Chrome unchanged.
while [[ $# -gt 0 ]]; do
if [[ "$1" == "--" ]]; then
shift
chrome_args=("$@")
break
fi
cdb_args+=("$1")
shift
done
set -- "${cdb_args[@]}"
usage() {
cat <<'EOF'
Chrome Debug Browser (cdb)
Purpose / LLM notes:
cdb is a small universal Chrome DevTools Protocol (CDP) launcher.
It exists because modern Chrome refuses remote debugging on the default
user-data directory. Running:
open -a "Google Chrome" --args --remote-debugging-port=9222
may show the flag in `ps`, but often no listener appears on 127.0.0.1:9222.
Chrome prints the real reason when launched directly:
DevTools remote debugging requires a non-default data directory.
This is official Chrome behavior since Chrome 136, not a local macOS bug:
https://developer.chrome.com/blog/remote-debugging-port
cdb solves this by copying the normal signed-in Chrome user-data directory to
a non-default profile under /tmp and launching Chrome from that copy with CDP
enabled. This keeps browser automation tools from mutating the live default
Chrome profile while still preserving cookies/logins from the copied profile.
What cdb is:
- a generic Chrome/CDP shim, not tied to any one agent/tool
- usable by any CDP client: Playwright, Puppeteer, browser-tools, MCP, custom scripts
- a session/profile manager for temporary debug Chrome profiles
What cdb is not:
- not a Gmail/OpenAI/Oracle tool
- not a browser automation client itself
- not a permanent password/session vault unless CDB_BASE points outside /tmp
- not the official Chrome DevTools MCP autoConnect flow
Official/alternative approaches:
- Chrome recommends a non-default --user-data-dir for classic CDP port usage.
- Chrome recommends Chrome for Testing for automation compatibility.
- Chrome >=144 also has a permission-based Chrome DevTools MCP autoConnect
flow via chrome://inspect/#remote-debugging, but that is MCP-specific and
not the same as exposing a generic http://127.0.0.1:9222/json/version CDP
endpoint for arbitrary tools.
- autoConnect docs:
https://developer.chrome.com/blog/chrome-devtools-mcp-debug-your-browser-session
Profile semantics:
- Default source profile: auto-detected Chrome/Chromium user-data dir
- Override source: CDB_SOURCE_PROFILE=/path/to/profile
- Override browser: CDB_CHROME=/path/to/chrome
- Default temp copy: /tmp/cdb-$USER/<session>
- The copy is reused until /tmp cleanup/reboot or `cdb --fresh`.
- Closing Chrome does not delete the copied profile.
- Use `--fresh` after changing login/account in real Chrome.
- Use `--empty` for a clean non-copied profile.
- Use CDB_BASE=$HOME/.chrome-debug to persist sessions across reboots.
Usage:
cdb Start/reuse copied Chrome profile 'default' on 9222
cdb <session> Start/reuse copied Chrome profile session on 9222
cdb <session> <port> Start/reuse session on custom port
cdb <session> <port> <url> Start/reuse session and open url
cdb [args...] -- <chrome flags/args...>
Pass everything after -- through to Chrome
cdb --fresh [session] [port] [url]
Delete session copy, copy current Chrome profile again, start
cdb --empty [session] [port] [url]
Start empty non-default profile, no copy
cdb --kill [session] Kill Chrome processes using that session profile
cdb --status [port] Check DevTools endpoint and print /json/version
Default temp base:
/tmp/cdb-$USER/<session>
Persistent base across reboots:
export CDB_BASE="$HOME/.chrome-debug"
Environment overrides:
CDB_CHROME=/path/to/chrome
CDB_SOURCE_PROFILE=/path/to/Chrome/UserData
CDB_BASE=/path/to/debug-profiles
After start, connect any CDP client to:
127.0.0.1:9222
Useful local links:
http://127.0.0.1:9222/json/version # browser/version/webSocketDebuggerUrl
http://127.0.0.1:9222/json/list # open tabs/targets
Optional default URL:
CDB_URL=https://example.com cdb
Examples:
cdb
cdb pro
cdb pro 9222 https://chatgpt.com
cdb work 9222 -- --auto-open-devtools-for-tabs
cdb test 9333 https://example.com -- --incognito
cdb --fresh pro
cdb --empty clean 9224 -- --disable-extensions
cdb --kill pro
cdb --status 9222
Reserved by cdb:
--remote-debugging-port, --remote-debugging-address, --user-data-dir
Common CDP client examples:
# Generic endpoint:
127.0.0.1:9222
# Playwright-style clients usually need the websocket URL from:
http://127.0.0.1:9222/json/version
# Tools with a host:port option usually use:
127.0.0.1:9222
EOF
}
case "${1:-}" in
-h|--help|help)
usage
exit 0
;;
--fresh)
mode="fresh"; shift
;;
--empty)
mode="empty"; shift
;;
--kill)
mode="kill"; shift
;;
--status)
mode="status"; shift
;;
esac
if [[ "$mode" == "status" ]]; then
port="${1:-9222}"
if curl -fsS --max-time 1 "http://127.0.0.1:$port/json/version"; then
exit 0
fi
echo "Kein Chrome DevTools endpoint auf 127.0.0.1:$port" >&2
exit 1
fi
session="${1:-$session}"
port="${2:-$port}"
start_url="${3:-${CDB_URL:-}}"
session="${session//[^A-Za-z0-9._-]/_}"
profile="$base/$session"
log="$profile/chrome.log"
if [[ "$mode" == "kill" ]]; then
pkill -f -- "--user-data-dir=$profile" 2>/dev/null || true
echo "Chrome Debug Browser beendet: $session ($profile)"
exit 0
fi
mkdir -p "$base"
chmod 700 "$base" 2>/dev/null || true
if [[ "$mode" == "fresh" ]]; then
echo "Loesche Session-Kopie: $profile"
pkill -f -- "--user-data-dir=$profile" 2>/dev/null || true
rm -rf "$profile"
fi
if [[ -z "$chrome" || ! -x "$chrome" ]]; then
echo "Chrome executable not found. Set CDB_CHROME=/path/to/chrome" >&2
exit 1
fi
if [[ "$mode" != "empty" && ! -f "$profile/Local State" ]]; then
if [[ -z "$source_profile" || ! -d "$source_profile" ]]; then
echo "Chrome source profile not found. Set CDB_SOURCE_PROFILE=/path/to/user-data-dir or use --empty" >&2
exit 1
fi
echo "Kopiere Chrome-Profil nach: $profile"
echo "Quelle: $source_profile"
mkdir -p "$profile"
rsync -a --delete \
--exclude='Singleton*' \
--exclude='Crashpad' \
--exclude='BrowserMetrics' \
--exclude='Safe Browsing*' \
--exclude='GrShaderCache' \
--exclude='ShaderCache' \
--exclude='*/Cache' \
--exclude='*/Code Cache' \
--exclude='*/GPUCache' \
--exclude='*/Service Worker/CacheStorage' \
--exclude='*/Service Worker/ScriptCache' \
"$source_profile/" "$profile/"
else
mkdir -p "$profile"
fi
chmod 700 "$profile" 2>/dev/null || true
if curl -fsS --max-time 1 "http://127.0.0.1:$port/json/version" >/dev/null 2>&1; then
echo "Chrome Debug Browser laeuft bereits: http://127.0.0.1:$port"
echo "Profil: $profile"
echo "CDP endpoint: 127.0.0.1:$port"
echo "Version: http://127.0.0.1:$port/json/version"
echo "Targets: http://127.0.0.1:$port/json/list"
exit 0
fi
echo "Starte Chrome Debug Browser"
echo "Profil: $profile"
echo "Port: $port"
if [[ -n "$start_url" ]]; then
echo "URL: $start_url"
fi
if [[ ${#chrome_args[@]} -gt 0 ]]; then
echo "Chrome args: ${chrome_args[*]}"
fi
cmd=(
"$chrome"
--remote-debugging-port="$port"
--remote-debugging-address=127.0.0.1
--user-data-dir="$profile"
--no-first-run
--no-default-browser-check
)
if [[ ${#chrome_args[@]} -gt 0 ]]; then
cmd+=("${chrome_args[@]}")
fi
if [[ -n "$start_url" ]]; then
cmd+=("$start_url")
fi
"${cmd[@]}" >"$log" 2>&1 &
pid=$!
echo "PID: $pid"
echo "Log: $log"
for _ in $(seq 1 50); do
if curl -fsS --max-time 1 "http://127.0.0.1:$port/json/version" >/dev/null 2>&1; then
echo "CDP bereit: http://127.0.0.1:$port"
echo "CDP endpoint: 127.0.0.1:$port"
echo "Version: http://127.0.0.1:$port/json/version"
echo "Targets: http://127.0.0.1:$port/json/list"
exit 0
fi
sleep 0.2
done
echo "WARNUNG: Chrome gestartet, aber CDP-Port antwortet noch nicht. Siehe Log: $log" >&2
exit 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment