How to Debug the OpenWork Electron App Using Chrome DevTools Protocol
OpenWork's desktop client exposes a Chrome DevTools Protocol (CDP) server on 127.0.0.1:9823 in development mode, allowing you to attach Chrome DevTools or programmatic clients directly to the Electron main and renderer processes for live debugging, performance profiling, and automated diagnostics.
The different-ai/openwork repository treats CDP as a first-class debugging interface. Whether you're hunting UI bugs in the renderer, profiling JavaScript execution, or building automated health checks, understanding how to leverage the Chrome DevTools Protocol unlocks deep visibility into the Electron stack.
How CDP Integration Works in OpenWork
Electron applications are built on Chromium, which natively supports the Chrome DevTools Protocol. OpenWork capitalizes on this by launching Electron with the --remote-debugging-port flag and wrapping common CDP operations in dedicated helper scripts.
The CDP Architecture
| Component | Role | Source Location |
|---|---|---|
| Electron launch command | Starts supervisor with --remote-debugging-port=9823 |
scripts/openwork-debug.sh lines 127-139 |
| CDP port configuration | TCP endpoint for debugger connections | scripts/openwork-debug.sh lines 38-44 |
| Endpoint discovery | HTTP endpoints /json/version and /json/list expose WebSocket URLs |
scripts/openwork-debug.sh lines 246-255 |
| Diagnostic probe | Node.js CDP client that verifies renderer responsiveness | scripts/openwork-debug.sh lines 84-119 |
| Main helper script | Orchestrates start, stop, snapshot, and hang diagnosis | scripts/openwork-debug.sh lines 336-361 |
When Electron starts, Chromium listens for CDP traffic on the configured port. The protocol exposes two HTTP endpoints for discovery:
http://127.0.0.1:9823/json/version— browser metadata and WebSocket URLhttp://127.0.0.1:9823/json/list— array of page targets withwebSocketDebuggerUrlfields
Each page target's webSocketDebuggerUrl (e.g., ws://127.0.0.1:9823/devtools/page/XXXXXXXX) is the actual attachment point for DevTools UI or programmatic clients.
Starting Electron with CDP Enabled
The standard development launch command automatically enables remote debugging:
# From repository root
pnpm --filter @openwork/desktop dev:electron
This invokes apps/desktop/scripts/electron-dev.mjs, which spawns the Electron binary with --remote-debugging-port=9823. The openwork-debug.sh wrapper prints confirmation:
[openwork-debug] starting pnpm dev:electron (log sink: /home/you/.openwork/debug/openwork-dev.log, CDP: 127.0.0.1:9823)
To use a custom port, export OPENWORK_ELECTRON_REMOTE_DEBUG_PORT:
OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=9999 pnpm --filter @openwork/desktop dev:electron
Attaching Chrome DevTools to OpenWork
The most direct way to debug OpenWork using Chrome DevTools Protocol is through Chrome's built-in inspector interface.
Step-by-Step Attachment
- Open Chrome (or any Chromium-based browser) and navigate to
chrome://inspect - Click "Configure" next to "Discover network targets"
- Add the CDP address:
127.0.0.1:9823 - Wait for detection — the OpenWork window appears under "Remote Target"
- Click "Inspect" to open DevTools connected to the Electron renderer
You now have full access to Elements, Console, Sources, Network, Performance, and Application panels — exactly as you would with any web page, but inspecting the actual Electron renderer process.
Command-Line CDP Verification
Before attaching DevTools, verify the CDP server is responsive using standard HTTP tools.
Check CDP Version and Page Targets
# Display CDP metadata
curl http://127.0.0.1:9823/json/version | jq .
# List available page targets
curl http://127.0.0.1:9823/json/list | jq .
A healthy response from /json/list includes page targets with webSocketDebuggerUrl fields:
[
{
"type": "page",
"webSocketDebuggerUrl": "ws://127.0.0.1:9823/devtools/page/A1B2C3D4E5F6..."
}
]
If this array is empty or the HTTP request times out, the Electron renderer has not started correctly or has crashed.
Automated Diagnostics with openwork-debug.sh
The repository includes scripts/openwork-debug.sh, a comprehensive helper that automates common CDP debugging workflows.
Run Full Health Check
./scripts/openwork-debug.sh diagnose-hang
This command executes a complete diagnostic sequence:
- Queries CDP HTTP endpoints for availability
- Verifies at least one page target exists
- Runs
probe_electron_page_cdp— a Node.js WebSocket client that evaluates1+1viaRuntime.evaluate - Collects renderer process statistics, CPU usage, and crash reports
Sample output:
=== browser CDP ===
browser: responsive
{
"Browser": "Chrome/127.0.0.0",
"webSocketDebuggerUrl": "ws://127.0.0.1:9823/devtools/browser/..."
}
=== page target ===
[
{"type":"page","webSocketDebuggerUrl":"ws://127.0.0.1:9823/devtools/page/..."}
]
=== page CDP probe ===
page: responsive (ok)
The script categorizes failures automatically:
no-cdp— port not respondingrenderer-crashed— CDP up but no page targetsrenderer-hung-hot— high CPU, unresponsive to evaluationrenderer-unresponsive— evaluation timeout
Capture System Snapshot
./scripts/openwork-debug.sh snapshot
Outputs process tree, active CDP endpoints, recent logs, and environment state — useful for bug reports.
Reset Development Stack
./scripts/openwork-debug.sh reset
Stops Electron, wipes Vite caches, truncates log files, and restarts with fresh CDP exposure. Use when encountering persistent state corruption or cache-related issues.
Programmatic CDP Usage in Node.js
For automated testing or custom debugging tools, connect directly via WebSocket using the webSocketDebuggerUrl from the /json/list endpoint.
Minimal CDP Client Example
// probe_electron_page_cdp.js
const fetch = require('node-fetch');
const WebSocket = require('ws');
(async () => {
const port = process.env.OPENWORK_ELECTRON_REMOTE_DEBUG_PORT || "9823";
// Discover page target
const list = await fetch(`http://127.0.0.1:${port}/json/list`).then(r => r.json());
const page = list.find(t => t.type === "page" && t.webSocketDebuggerUrl);
if (!page) throw new Error("No page target found — renderer may be crashed");
// Connect via WebSocket
const ws = new WebSocket(page.webSocketDebuggerUrl);
ws.on('open', () => {
ws.send(JSON.stringify({
id: 1,
method: "Runtime.evaluate",
params: { expression: "1+1", returnByValue: true }
}));
});
ws.on('message', data => {
const msg = JSON.parse(data);
if (msg.id === 1) {
console.log('CDP eval result:', msg.result?.result?.value);
ws.close();
}
});
ws.on('error', err => console.error('WebSocket error:', err.message));
})();
Run with:
node probe_electron_page_cdp.js
# Output: CDP eval result: 2
This pattern — fetch discovery endpoint, extract webSocketDebuggerUrl, open WebSocket, send CDP commands — is the foundation of all CDP clients including Playwright, Puppeteer, and OpenWork's own automation engine.
Advanced CDP Applications in OpenWork
The repository demonstrates several sophisticated CDP use cases beyond basic debugging.
UI Automation in packages/automations
packages/automations/src/engine.ts builds higher-level CDP steering for automated workflows:
- DOM interaction —
DOM.querySelector,DOM.getBoxModelfor element location - Input simulation —
Input.dispatchMouseEvent,Input.dispatchKeyEvent - Execution context management — isolating automation scripts from page JavaScript
This enables headless or semi-headless operation of OpenWork for testing and batch processing.
E2E Testing with packages/handsfree
packages/handsfree/test/e2e/run.mjs shows how the test suite launches a controlled Chrome instance with CDP, navigates to OpenWork surfaces, and validates behavior programmatically — useful reference for building your own CDP-based test harnesses.
Environment Variables for CDP Configuration
| Variable | Default | Purpose |
|---|---|---|
OPENWORK_ELECTRON_REMOTE_DEBUG_PORT |
9823 |
TCP port for CDP server |
ELECTRON_CDP_PORT |
9823 |
Legacy alias, used in some scripts |
Set either before running pnpm dev:electron or openwork-debug.sh commands to customize the debugging endpoint.
Common Issues and Resolution
| Symptom | Likely Cause | Fix |
|---|---|---|
curl: (7) Failed to connect |
Electron not running or port blocked | Verify pnpm dev:electron started; check lsof -i :9823 |
/json/list returns [] |
Renderer process crashed | Run diagnose-hang; check ~/.openwork/debug/ logs |
| DevTools opens but shows blank | Wrong target selected in chrome://inspect |
Use /json/list to identify correct webSocketDebuggerUrl |
| Probe times out | Renderer hung or high CPU | Run diagnose-hang for automatic categorization |
| Changes not reflecting | Vite cache stale | Execute openwork-debug.sh reset |
Summary
- OpenWork exposes CDP on
127.0.0.1:9823when launched withpnpm --filter @openwork/desktop dev:electron - Attach Chrome DevTools via
chrome://inspectby adding the CDP address and clicking "Inspect" - Verify connectivity with
curl http://127.0.0.1:9823/json/listandopenwork-debug.sh diagnose-hang - Automate debugging using
scripts/openwork-debug.shfor snapshots, hang diagnosis, and stack resets - Build custom tools by connecting to the
webSocketDebuggerUrlwith any WebSocket client and issuing CDP commands likeRuntime.evaluate - Reference implementation lives in
packages/automations/src/engine.tsandpackages/handsfree/test/e2e/run.mjs
Frequently Asked Questions
How do I change the default CDP port for OpenWork Electron debugging?
Export OPENWORK_ELECTRON_REMOTE_DEBUG_PORT with your desired port number before launching. For example: OPENWORK_ELECTRON_REMOTE_DEBUG_PORT=9999 pnpm --filter @openwork/desktop dev:electron. The scripts/openwork-debug.sh helper reads this variable and passes it through to the Electron supervisor.
Can I debug the main process, or only the renderer?
The --remote-debugging-port flag exposes the renderer process by default. To debug the Electron main process (Node.js context), you need to launch with --inspect or --inspect-brk on a separate port, or use Electron's built-in webContents.openDevTools() for renderer DevTools with main process console output visible in the terminal.
What CDP methods does OpenWork's diagnostic probe actually use?
The probe_electron_page_cdp function in scripts/openwork-debug.sh (lines 84-119) uses a minimal check: it sends Runtime.evaluate with expression 1+1 and expects returnByValue: true. This verifies the JavaScript execution context is responsive without side effects. More complex diagnostics in the repository use DOM.getDocument, Performance.getMetrics, and Profiler.start for deeper inspection.
Why does diagnose-hang report "renderer-hung-hot" vs "renderer-unresponsive"?
renderer-hung-hot indicates the process is consuming high CPU (detected via /proc or ps inspection) while failing to respond to CDP evaluation — typical of infinite loops or heavy synchronous computation. renderer-unresponsive means the process appears idle but times out on CDP commands, suggesting deadlock, memory pressure, or crash without exit. The distinction helps prioritize troubleshooting steps.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →