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 URL
  • http://127.0.0.1:9823/json/list — array of page targets with webSocketDebuggerUrl fields

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

  1. Open Chrome (or any Chromium-based browser) and navigate to chrome://inspect
  2. Click "Configure" next to "Discover network targets"
  3. Add the CDP address: 127.0.0.1:9823
  4. Wait for detection — the OpenWork window appears under "Remote Target"
  5. 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:

  1. Queries CDP HTTP endpoints for availability
  2. Verifies at least one page target exists
  3. Runs probe_electron_page_cdp — a Node.js WebSocket client that evaluates 1+1 via Runtime.evaluate
  4. 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 responding
  • renderer-crashed — CDP up but no page targets
  • renderer-hung-hot — high CPU, unresponsive to evaluation
  • renderer-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.getBoxModel for 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:9823 when launched with pnpm --filter @openwork/desktop dev:electron
  • Attach Chrome DevTools via chrome://inspect by adding the CDP address and clicking "Inspect"
  • Verify connectivity with curl http://127.0.0.1:9823/json/list and openwork-debug.sh diagnose-hang
  • Automate debugging using scripts/openwork-debug.sh for snapshots, hang diagnosis, and stack resets
  • Build custom tools by connecting to the webSocketDebuggerUrl with any WebSocket client and issuing CDP commands like Runtime.evaluate
  • Reference implementation lives in packages/automations/src/engine.ts and packages/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →