Programmatic APIs
There are two ways to use agent-lynx in automation scripts:
- CLI subprocesses: invoke commands from Shell, Python, Node.js, or another language, then parse JSON output to filter data, make decisions, or save results.
- Connector API: connect through
agent-lynx/connector in JavaScript or TypeScript to send CDP and App requests or subscribe to events.
For the examples below, connect an app using Getting Started, then obtain the target page IDs with list-clients and list-sessions. Set them as environment variables:
export CLIENT_ID='<client-id>'
export SESSION_ID='<session-id>'
Call the CLI as a Subprocess
Install the CLI and make sure your script can find agent-lynx on PATH:
npm install -g agent-lynx
Put --json before the command to request machine-readable output. These three examples perform the same task: read the page's element tree, evaluate an expression in the background VM to get the global property keys, and save both results to page-report.json.
Use Bash and jq:
set -euo pipefail
target=(--client "$CLIENT_ID" --session "$SESSION_ID")
document=$(agent-lynx --json cdp --method DOM.getDocument \
'{"depth":-1}' "${target[@]}")
props=$(agent-lynx --json evaluate \
'Object.keys(lynx.__globalProps || {})' "${target[@]}")
jq -n --argjson document "$document" --argjson props "$props" '
if $props.exceptionDetails then error($props.exceptionDetails | tostring)
else {document: $document, globalPropKeys: $props.result.value}
end
' > page-report.json
Save as inspect_page.py and run python3 inspect_page.py:
import json
import os
import subprocess
from pathlib import Path
def run(*args):
result = subprocess.run(
["agent-lynx", "--json", *args,
"--client", os.environ["CLIENT_ID"],
"--session", os.environ["SESSION_ID"]],
check=True, stdout=subprocess.PIPE, text=True,
)
return json.loads(result.stdout)
document = run("cdp", "--method", "DOM.getDocument", '{"depth":-1}')
props = run("evaluate", "Object.keys(lynx.__globalProps || {})")
if props.get("exceptionDetails"):
raise RuntimeError(props["exceptionDetails"])
report = {"document": document, "globalPropKeys": props["result"]["value"]}
Path("page-report.json").write_text(json.dumps(report, indent=2), encoding="utf-8")
Save as inspect-page.mjs and run node inspect-page.mjs:
import { execFileSync } from 'node:child_process';
import { writeFileSync } from 'node:fs';
function run(...args) {
return JSON.parse(execFileSync('agent-lynx', [
'--json', ...args,
'--client', process.env.CLIENT_ID,
'--session', process.env.SESSION_ID,
], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit'], maxBuffer: 16 * 1024 * 1024 }));
}
const document = run('cdp', '--method', 'DOM.getDocument', '{"depth":-1}');
const props = run('evaluate', 'Object.keys(lynx.__globalProps || {})');
if (props.exceptionDetails) throw new Error(JSON.stringify(props.exceptionDetails));
writeFileSync('page-report.json', JSON.stringify({
document, globalPropKeys: props.result.value,
}, null, 2));
The scripts check the process exit status and parse only stdout, leaving stderr for diagnostics. Python and Node.js pass argument arrays, so expressions and JSON parameters need no additional shell escaping.
JSON shapes depend on the command: cdp and evaluate already output JSON; evaluate returns its value in result.value and VM exceptions in exceptionDetails. Commands such as snapshot --json return an envelope containing ok, data, or error. get-console --watch --json emits one JSON event per line and should be parsed line by line. See the CLI reference for more options.
Use the Connector API
Use the Connector API to reuse a connection and compose protocol requests in JavaScript or TypeScript scripts. First, install agent-lynx in your project:
This example performs the same page inspection as the CLI examples and saves the results to page-report.json. Using the CLIENT_ID and SESSION_ID set above, save the script as inspect-page.mjs and run node inspect-page.mjs:
import { writeFileSync } from 'node:fs';
import { createDefaultConnector, evaluateExpression } from 'agent-lynx/connector';
const connector = createDefaultConnector();
const clientId = process.env.CLIENT_ID;
const sessionId = Number(process.env.SESSION_ID);
if (!clientId || !Number.isInteger(sessionId)) {
throw new Error('Set CLIENT_ID and SESSION_ID to the target page IDs.');
}
const document = await connector.sendCDPMessage(
clientId, sessionId, 'DOM.getDocument', { depth: -1 },
);
const props = await evaluateExpression(
connector, clientId, sessionId, 'Object.keys(lynx.__globalProps || {})',
);
if (props.exceptionDetails) throw new Error(JSON.stringify(props.exceptionDetails));
writeFileSync('page-report.json', JSON.stringify({
document, globalPropKeys: props.result.value,
}, null, 2));
createDefaultConnector() shares the daemon-managed app connection with the CLI. sendCDPMessage sends a protocol request to the selected page; evaluateExpression runs in the background VM by default and prepares the page's lynx context. Results arrive as JavaScript objects, without parsing process output.
Beyond page inspection, use sendAppMessage for host app operations or sendCDPStream to compose input events and subscribe to runtime messages. For more workflows, see the programmatic usage introduction, detailed Connector examples, and click and double-click example in lynx-community/skills.