# How to Debug the OpenWork Electron App Using Chrome DevTools Protocol

> Debug the OpenWork Electron app with Chrome DevTools Protocol. Connect directly to processes for live debugging, profiling, and automated diagnostics. Learn how to use CDP.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-15

---

**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`](https://github.com/different-ai/openwork/blob/main/scripts/openwork-debug.sh) lines 127-139 |
| **CDP port configuration** | TCP endpoint for debugger connections | [`scripts/openwork-debug.sh`](https://github.com/different-ai/openwork/blob/main/scripts/openwork-debug.sh) lines 38-44 |
| **Endpoint discovery** | HTTP endpoints `/json/version` and `/json/list` expose WebSocket URLs | [`scripts/openwork-debug.sh`](https://github.com/different-ai/openwork/blob/main/scripts/openwork-debug.sh) lines 246-255 |
| **Diagnostic probe** | Node.js CDP client that verifies renderer responsiveness | [`scripts/openwork-debug.sh`](https://github.com/different-ai/openwork/blob/main/scripts/openwork-debug.sh) lines 84-119 |
| **Main helper script** | Orchestrates start, stop, snapshot, and hang diagnosis | [`scripts/openwork-debug.sh`](https://github.com/different-ai/openwork/blob/main/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:

```bash

# 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`](https://github.com/different-ai/openwork/blob/main/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`:

```bash
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

```bash

# 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:

```json
[
  {
    "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`](https://github.com/different-ai/openwork/blob/main/scripts/openwork-debug.sh), a comprehensive helper that automates common CDP debugging workflows.

### Run Full Health Check

```bash
./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

```bash
./scripts/openwork-debug.sh snapshot

```

Outputs process tree, active CDP endpoints, recent logs, and environment state — useful for bug reports.

### Reset Development Stack

```bash
./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

```javascript
// 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:

```bash
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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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`](https://github.com/different-ai/openwork/blob/main/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.