Node.js Capabilities
Current client, session, context, extension, download, CDP, Browser Agent, error, and logging APIs.
Client and regions
import { Lexmount } from "lexmount";
const client = new Lexmount({
region: "<region-id>",
timeout: 60_000,
});
console.log(await client.regionInfo());
console.log(await client.catalogInfo());The client reads credentials from constructor options or environment variables. Region catalog discovery is best effort: an unavailable catalog or probe falls back to the configured base URL.
Sessions
Create options
const session = await client.sessions.create({
browserMode: "normal",
windowSize: "1920,1080",
downloads: { enabled: true },
recording: { persistent: true },
});| Option | Purpose |
|---|---|
browserMode | normal for Chromium or light for Moli |
context / contextDescription | Reuse or create persistent browser state |
extensionIds | Mount uploaded extensions |
proxy / officialProxy | Select a local, external, or official proxy |
downloads | Enable or disable stored session downloads |
recording | Enable or disable persistent replay; forced off for Moli |
customImageId | Start an approved custom browser image |
windowSize | Set ACE browser size as width,height |
enableLightmountLayout | Enable the LightMount layout integration; defaults to true |
enableLightmountResource | Override layout-resource integration for this session |
enableCaptcha | Request automatic CAPTCHA processing for this session; disabled by default |
asyncCreate | Use async create and wait for active; defaults to true |
pollIntervalMs / pollTimeoutMs | Control activation polling |
officialProxy: true and proxy are mutually exclusive. Plan capabilities determine whether contexts, extensions, proxies, and custom images are available.
Automatic CAPTCHA processing
Use SDK 0.6.1 or later and explicitly opt in when creating a session:
const session = await client.sessions.create({
browserMode: "normal",
enableCaptcha: true,
});
try {
console.log(session.id);
} finally {
await session.close();
}The default is false. This preference applies only to the new session; other sessions in the same project can remain disabled. The SDK sends the top-level enable_captcha boolean in both synchronous and asynchronous create requests.
The service automatically detects and processes CAPTCHAs for opted-in sessions with eligible project access. Availability and usage depend on the project's plan and remaining CAPTCHA allowance. Continue controlling the browser with the existing SDK or CDP connection.
See session lifecycle for the corresponding console option.
Read, list, targets, and close
const current = await client.sessions.get(session.id);
const sessions = await client.sessions.list({ status: "active" });
console.log(sessions.pagination.totalCount);
for (const item of sessions) {
console.log(item.id, item.status, item.regionId, item.browserType);
}
for (const target of await client.sessions.listTargets(session.id)) {
console.log(target.title, target.url, target.inspectUrl);
}
await session.close();
// Equivalent when only the ID is available:
await client.sessions.delete({ sessionId: session.id });Downloads
const result = await client.sessions.downloads.list(session.id);
console.log(result.summary.count, result.summary.totalSize);
const file = await client.sessions.downloads.get(session.id, "<download-id>");
const archive = await client.sessions.downloads.archive(session.id);
const deleted = await client.sessions.downloads.delete(session.id);
console.log(file.length, archive.length, deleted.deletedCount);Stored downloads are enabled by default. Configure the remote browser download behavior as shown in the quickstart repository so files reach the persistent download directory.
Local Proxy Tunnels
SDK 0.6.0 introduced client.tunnels.open(); use 0.6.1 or later. Once ready, create a normal session with proxy: { type: "local", tunnelId: tunnel.id } to use the SDK machine's network and DNS. This requires custom_proxy, the same project/Key/region, and excludes official or external proxy. Close the session before the tunnel. See the local proxy guide for complete examples and options.
Contexts
const context = await client.contexts.create({
description: "Authenticated storefront",
metadata: { owner: "agent-a" },
});
const session = await client.sessions.create({
context: { id: context.id, mode: "readWrite" },
});
const contexts = await client.contexts.list({ status: "available", limit: 20 });
const details = await client.contexts.get(context.id);
const forked = await client.contexts.fork(context.id);
await client.contexts.forceRelease(context.id);
await client.contexts.delete(forked.id);A context used by an active read-write session is locked. Current fork behavior copies an offline available context; hot fork, copy-on-write, and lineage tracking are not supported.
Extensions
const extension = await client.extensions.upload(
"/absolute/path/to/extension.zip",
{
name: "demo-extension",
},
);
const extensions = await client.extensions.list({ limit: 20, offset: 0 });
const details = await client.extensions.get(extension.id);
const session = await client.sessions.create({ extensionIds: [extension.id] });
await session.close();
await client.extensions.delete(extension.id);The service accepts .zip and .crx archives. An extension cannot be deleted while an active session uses it.
CDP helpers and integrated authentication
import { connectOverCDP, Lexmount } from "lexmount";
const client = new Lexmount();
const session = await client.sessions.create();
const cdp = await connectOverCDP(session);
console.log(await cdp.send("Browser.getVersion"));
await cdp.close();
await session.close();
client.close();The package also exports registerIntegratedAuthCallback and deregisterIntegratedAuthCallback for application-provided authentication handling.
Errors and logs
import {
APIError,
AuthenticationError,
ContextLockedError,
NetworkError,
TimeoutError,
setLogLevel,
} from "lexmount";
setLogLevel("INFO");
try {
await client.sessions.create();
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("Check API Key and Project ID");
} else if (error instanceof ContextLockedError) {
console.error(error.activeSessionId, error.retryAfter);
} else if (error instanceof NetworkError || error instanceof TimeoutError) {
console.error("Check whether a session was reserved before retrying");
} else if (error instanceof APIError) {
console.error(error.statusCode);
} else {
throw error;
}
}Logging helpers are setLogLevel, enableLogging, disableLogging, and getLogger.
Browser Agent
Browser Agent executes a browser task from a natural-language instruction. A Run is one execution; a saved Agent holds a reusable instruction and optional output schema. Use client.browserAgent with SDK 0.7.0.
Setup
npm install lexmount@0.7.0
export LEXMOUNT_API_KEY='<your-api-key>'
export LEXMOUNT_PROJECT_ID='<your-project-id>'
# Optional: SDK 0.7.0 defaults to this global COM origin.
export LEXMOUNT_BASE_URL=https://api.lexmount.comUse an API Key and Project ID from the same project, with Browser Agent enabled in the selected environment. Set the HTTPS origin, without /v1/browser-agent; the SDK adds that path. Set https://api.lexmount.cn explicitly for CN. Playwright is not required for these Agent API calls.
Run a task
Choose and persist a new RUN_IDEMPOTENCY_KEY for one logical task before running this example. Reuse it with the identical request when recovering that task; use a new key for an intentionally new task.
export RUN_IDEMPOTENCY_KEY='<persistent-unique-key-for-this-task>'Save as agent.mjs and run node agent.mjs:
import { APIError, Lexmount, RunWaitTimeout } from 'lexmount';
const key = process.env.RUN_IDEMPOTENCY_KEY;
if (!key) throw new Error('Set a persistent RUN_IDEMPOTENCY_KEY first');
const client = new Lexmount();
try {
const receipt = await client.browserAgent.runs.create(
{
query: 'Read the page title and summarize the page.',
start_url: 'https://example.com/',
},
{ idempotencyKey: key }
);
console.log('Accepted:', receipt.id); // Save this Run ID before waiting.
const run = await client.browserAgent.runs.wait(receipt.id, {
timeoutMs: 300_000,
});
console.log(run.id, run.status, run.output);
if (run.status !== 'succeeded') {
console.error(run.stop_reason, run.error);
process.exitCode = 1;
}
} catch (error) {
if (error instanceof RunWaitTimeout) {
console.error('Resume with runs.get/wait:', error.run_id);
} else if (error instanceof APIError) {
console.error(error.status_code, error.code, error.request_id);
} else {
throw error;
}
process.exitCode = 1;
} finally {
client.close();
}A create response is a queued receipt, not a completed result. Save receipt.id and use runs.get(id) or runs.wait(id) to recover after a process restart. Closing the SDK client does not cancel an active Run.
Reuse a saved Agent
Set two additional persistent keys, AGENT_IDEMPOTENCY_KEY for the Agent creation and SAVED_RUN_IDEMPOTENCY_KEY for this Run. Save the returned Agent ID and use it for later Runs; creating the Agent is a one-time setup step.
import { Lexmount } from 'lexmount';
const agentKey = process.env.AGENT_IDEMPOTENCY_KEY;
const runKey = process.env.SAVED_RUN_IDEMPOTENCY_KEY;
if (!agentKey || !runKey)
throw new Error('Set both persistent idempotency keys');
const client = new Lexmount();
try {
const agent = await client.browserAgent.agents.create(
{
name: 'Page summary',
instruction: 'Summarize the page in {{language}}.',
start_url: 'https://example.com/',
output_schema: {
type: 'object',
properties: { summary: { type: 'string' } },
required: ['summary'],
additionalProperties: false,
},
},
{ idempotencyKey: agentKey }
);
const receipt = await client.browserAgent.runs.create(
{
agent_id: agent.id,
variables: { language: 'English' },
metadata: { source: 'docs' },
},
{ idempotencyKey: runKey }
);
console.log('Agent:', agent.id, 'Run:', receipt.id);
// Recover this Run later with runs.get(receipt.id) or runs.wait(receipt.id).
} finally {
client.close();
}Supply exactly the instruction's template variables. The accepted Run freezes the Agent revision, expanded instruction and schema; later Agent edits do not change that Run. metadata is returned to your application and is not sent to the model. A successful schema-based output is an object; free-form output is text.
Manage resources
All methods below are on client.browserAgent. Await each call.
| Operation | Call |
|---|---|
| Read / list Agents | agents.get(id) / agents.list() |
| Edit an Agent | agents.update(id, { revision: current.revision, name: "Updated name" }) |
| Delete an Agent | agents.delete(id) |
| Read / list Runs | runs.get(id) / runs.list() |
| Request cancellation | runs.cancel(id) |
| Wait for terminal state | runs.wait(id) |
Read the current Agent first and pass its revision when updating. A revision conflict requires reading it again; updates are not automatically retried. Cancellation can race with completion: inspect the returned state and keep reading until terminal if it is still cancelling.
Recovery and errors
- The SDK generates an idempotency key if omitted. For recovery across process restarts, supply your own:
idempotencyKeyin the second options argument. Persist the key and request body before sending; use 1–128 visible ASCII characters without spaces. Same key/body replays the original receipt for at least seven days; a different body conflicts. Beyond that window, reconcile through history before creating anything again. succeeded,failed,timed_out, andcancelledare terminal Run results. HTTP success alone does not mean the task succeeded; inspectstatus,stop_reason,error, andoutput.RunWaitTimeoutonly stops local polling. Resume with the known Run ID, or explicitly request cancellation.timeoutMs,pollIntervalMs, andrequestTimeoutMsuse milliseconds.APIErrorincludesstatus_code,code, andrequest_id. Keep the request ID for support. Pass{ withResponse: true }to get success response metadata.- Keep the API Key used to create a Run valid until execution and cleanup finish. Revoking it can fail an active Run with
credential_unavailable; changing the client's key does not replace the original Run's execution credential. usage.creditscan benullwhile metering is pending; it does not mean zero cost. Task execution may have external side effects, so an unknown result must not be blindly resubmitted.
Runnable examples cover basic execution, saved Agents and recovery.
