@harnessgg/electron

Published v0.1.0

CLI harness for AI agents to interact with Electron apps via Chrome DevTools Protocol. Connect to any Electron app via CDP. Query elements, click, type, assert, screenshot, and evaluate JavaScript. Session is implicit — one JSON object per command.

npm install -D @harnessgg/electron

Agents can file bugs or feature requests with curl -X POST https://harness.gg/api/submit using "package":"electron" in the JSON payload.

Prerequisite

Launch your Electron app with the CDP debugging port exposed:

terminal
$ electron . --remote-debugging-port=9222

Node.js ≥ 20 required.

Quick flow

full agent session
# 0. Start app with CDP enabled
$ electron . --remote-debugging-port=9222


# 1. Discover contract
$ harness-electron schema


# 2. Connect
$ harness-electron connect --port 9222
{"ok":true,"session":"default","data":{"target":"My App"}}


# 3. Inspect DOM
$ harness-electron dom --format summary
{"ok":true,"session":"default","data":{"interactive":[{"role":"button","name":"Sign in"}]}}


# 4. Query a stable target
$ harness-electron query --role button --name "Sign in"
{"ok":true,"session":"default","data":{"elements":[{"elementId":"e1","role":"button","name":"Sign in"}]}}


# 5. Interact
$ harness-electron type --css "input[type=email]" --value "user@example.com"
$ harness-electron click --element-id e1


# 6. Verify
$ harness-electron assert --kind url --expected "/dashboard"
{"ok":true,"session":"default","data":{"actual":"/dashboard"}}


# 7. Screenshot
$ harness-electron screenshot --path ./artifacts/dashboard.png


# 8. Cleanup
$ harness-electron disconnect

Schema examples

Each command returns one JSON object. Parse ok, then use data or error.

success
{"ok":true,"protocolVersion":"1.0","command":"click","session":"default","data":{"clicked":true,"target":{"elementId":"e1"}}}
error
{"ok":false,"protocolVersion":"1.0","command":"click","session":"default","error":{"code":"TARGET_NOT_FOUND","message":"No element matched selector","retryable":true,"suggestedNext":["dom --format summary"]}}

Command reference

connect
harness-electron connect --port <n> [--host <h>] [--window-title <t>] [--url-contains <s>]

Connect to an Electron app via CDP. Session is implicit — do not pass --session unless running multi-session flows.

FlagRequiredDescription
--port yes CDP port (e.g. 9222)
--host no Host (default: localhost)
--window-title no Filter targets by window title
--url-contains no Filter targets by URL substring
dom
harness-electron dom [--format summary|tree|html] [--max-nodes <n>]

Inspect the DOM. Start with summary to discover interactive elements before targeting.

FlagRequiredDescription
--format no summary | tree | html (default: summary)
--max-nodes no Cap node count in output
query
harness-electron query <selector> [--limit <n>] [--visible-only]

Resolve elements and return persistent elementIds for reuse in later commands. Prefer over inline selectors for stability.

FlagRequiredDescription
--css / --xpath / --text / --role / --testid no Selector strategy (one required)
--name no Accessible name (use with --role)
--limit no Max results
--visible-only no Exclude hidden elements
click
harness-electron click <target> [--timeout <ms>]

Click an element. Use --element-id from query for stable targeting, or a selector flag.

FlagRequiredDescription
--element-id no ID returned by query (preferred)
--css / --xpath / --text / --role / --testid no Inline selector (one required if no --element-id)
--name no Accessible name (use with --role)
--index no Pick nth match (0-based, default 0)
--timeout no Max wait ms
type
harness-electron type <target> --value <text> [--clear] [--timeout <ms>]

Type text into an input field.

FlagRequiredDescription
--value yes Text to type
--element-id no ID returned by query
--css / --xpath / --role / --testid no Inline selector
--clear no Clear existing content first
--timeout no Max wait ms
wait
harness-electron wait --for visible|hidden|url|text [<target>] [--value <v>] [--timeout <ms>]

Wait for a condition. Rules: url requires --value and no target; text requires target + --value; visible/hidden require target.

FlagRequiredDescription
--for yes visible | hidden | url | text
--value no Expected value (required for url and text)
--timeout no Max wait ms
screenshot
harness-electron screenshot --path <file> [--full-page] [<target>] [--timeout <ms>]

Capture a screenshot. No target = viewport. With target = element. --full-page cannot be combined with a target.

FlagRequiredDescription
--path yes Output .png path
--full-page no Full-page screenshot (no target allowed)
--element-id / --css no Element screenshot
--timeout no Max wait ms
assert
harness-electron assert --kind exists|visible|text|url [<target>] [--expected <v>] [--timeout <ms>]

Assert a condition. Returns ok:false with ASSERT_FAIL on failure — treat as test failure, not infrastructure failure.

FlagRequiredDescription
--kind yes exists | visible | text | url
--expected no Expected value (required for text and url)
--element-id / --css / --xpath no Target (required for exists, visible, text)
--timeout no Max wait ms
evaluate
harness-electron evaluate --script <js>

Execute a JavaScript expression in the page context and return the result as JSON.

FlagRequiredDescription
--script yes JavaScript expression
disconnect
harness-electron disconnect

Close the default CDP session.

sessions
harness-electron sessions list | prune

List persisted sessions or prune stale ones.

schema
harness-electron schema

Emit machine-readable command and flag schema. Run this first for dynamic discovery.

capabilities
harness-electron capabilities

Show a human-readable summary of what this harness can do.

Response shape

Every command writes a single JSON object to stdout. Check ok first.

Success

{
  "ok": true,
  "protocolVersion": "1.0",
  "command": "click",
  "session": "ses_a1b2c3",
  "data": { "selector": "[role=button]" }
}

Error

{
  "ok": false,
  "protocolVersion": "1.0",
  "command": "click",
  "session": "ses_a1b2c3",
  "error": {
    "code": "TARGET_NOT_FOUND",
    "message": "No element matched",
    "retryable": true,
    "suggestedNext": ["dom --format summary"]
  }
}
FieldTypeDescription
okbooleantrue on success, false on error
protocolVersionstringAlways "1.0"
commandstringThe command that was run
sessionstringSession ID (ses_xxx)
dataobjectCommand-specific payload (success only)
errorobjectcode, message, retryable, suggestedNext (error only)

Error codes

CodeExitRetryableAction
INVALID_INPUT 10 no Bad or missing arguments. Fix the command before retrying.
CONNECT_FAILED 20 yes Could not reach CDP port. Verify the app is running with --remote-debugging-port.
TARGET_NOT_FOUND 20 yes No element matched the selector. Run dom --format summary and retry with a different target.
INVALID_SELECTOR 30 no Malformed selector. Run dom --format tree to rediscover structure.
ACTION_FAILED 30 yes Action executed but failed (e.g. element not interactable). Retry once.
TIMEOUT 40 yes Condition did not occur in time. Retry with a larger --timeout.
ASSERT_FAIL 50 no Assertion did not pass. Treat as test failure — inspect actual value in error.message.
INTERNAL_ERROR 70 yes Unexpected error. Retry once; if it persists, file a bug.

Submit feedback

Agents can file bug reports and feature requests via POST /api/submit. Issues land on harnessgg/Harness-electron.

FieldRequiredValues
typeyes"bug" | "feature" | "question"
packageyes"electron"
titleyesShort summary
bodyyesFull description
contextnoSession ID, OS, version…
file a bug report
$ curl -X POST https://harness.gg/api/submit \
  -H "Content-Type: application/json" \
  -d '{"type":"bug","package":"electron","title":"connect fails","body":"Steps..."}'