Node.js 核心能力
当前 client、session、context、extension、download、CDP、Browser Agent、错误和日志 API。
Client 与区域
import { Lexmount } from "lexmount";
const client = new Lexmount({
region: "<region-id>",
timeout: 60_000,
});
console.log(await client.regionInfo());
console.log(await client.catalogInfo());Client 从构造函数参数或环境变量读取凭据。区域目录发现是尽力而为:目录或探测不可用时,会回退到配置的 base URL。
Sessions
创建选项
const session = await client.sessions.create({
browserMode: "normal",
windowSize: "1920,1080",
downloads: { enabled: true },
recording: { persistent: true },
});| 选项 | 用途 |
|---|---|
browserMode | normal 表示 Chromium,light 表示 Moli |
context / contextDescription | 复用或创建持久化浏览器状态 |
extensionIds | 挂载已上传扩展 |
proxy / officialProxy | 选择本地代理、外部代理或官方代理池 |
downloads | 开启或关闭 session 存储下载 |
recording | 开启或关闭持久化回放;Moli 会强制关闭 |
customImageId | 使用获准的自定义浏览器镜像 |
windowSize | 以 width,height 设置 ACE 浏览器尺寸 |
enableLightmountLayout | 开启 LightMount layout 集成,默认 true |
enableLightmountResource | 覆盖当前 session 的 layout-resource 集成 |
enableCaptcha | 为当前会话开启自动验证码处理,默认关闭 |
asyncCreate | 异步创建并等待 active,默认 true |
pollIntervalMs / pollTimeoutMs | 控制激活状态轮询 |
officialProxy: true 与 proxy 不能同时使用。Context、扩展、代理和自定义镜像是否可用由套餐能力决定。
自动处理验证码
使用 SDK 0.6.1 或更新版本,在创建会话时显式传入 enableCaptcha:
const session = await client.sessions.create({
browserMode: "normal",
enableCaptcha: true,
});
try {
console.log(session.id);
} finally {
await session.close();
}默认值是 false。此设置仅作用于新建的当前会话,同一项目的其他会话可以保持关闭。SDK 在同步和异步创建请求中都会发送顶层 enable_captcha 布尔字段。
服务端会为已开启且符合项目权益的会话自动检测、处理验证码。可用性和使用量受项目套餐及剩余验证码额度约束;客户端继续使用原有 SDK 或 CDP 控制浏览器。
会话生命周期介绍控制台中的对应开关。
查询、列表、targets 与关闭
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();
// 只有 ID 时等价调用:
await client.sessions.delete({ sessionId: session.id });下载
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);存储下载默认开启。请参考 quickstart 仓库配置远端浏览器下载行为,使文件进入持久化下载目录。
本地代理隧道
SDK 0.6.0 起新增 client.tunnels.open();建议使用 0.6.1 或更高版本。隧道就绪后,以 proxy: { type: "local", tunnelId: tunnel.id } 创建 normal 会话,让云浏览器使用 SDK 电脑的网络与 DNS。需要 custom_proxy 权限,同项目/Key/区域,且与官方或外部代理互斥。先关会话,再关隧道;完整示例及选项见 本地代理指南。
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);Context 被活跃的读写 session 使用时会锁定。当前 fork 只会复制离线且状态为 available 的 context;不支持热 fork、copy-on-write 或父子谱系追踪。
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);服务接受 .zip 和 .crx。活跃 session 正在使用扩展时不能删除该扩展。
CDP 辅助方法与集成鉴权
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();包还导出 registerIntegratedAuthCallback 和 deregisterIntegratedAuthCallback,供应用处理集成鉴权。
错误与日志
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;
}
}日志辅助方法包括 setLogLevel、enableLogging、disableLogging 和 getLogger。
Browser Agent
Browser Agent 根据自然语言指令执行浏览器任务。Run 是一次执行;保存的 Agent 包含可复用指令及可选输出结构。SDK 0.7.0 通过 client.browserAgent 提供这些能力。
配置
npm install lexmount@0.7.0
export LEXMOUNT_API_KEY='<your-api-key>'
export LEXMOUNT_PROJECT_ID='<your-project-id>'
# 可选:SDK 0.7.0 默认使用全球 COM 地址。
export LEXMOUNT_BASE_URL=https://api.lexmount.comAPI Key 和 Project ID 必须属于同一项目,并在所选环境启用 Browser Agent。填写 HTTPS 源地址,不要附加 /v1/browser-agent,SDK 会添加路径。中国区请显式设置 https://api.lexmount.cn。调用这些 Agent API 无需安装 Playwright。
执行任务
运行示例前,为一次逻辑任务选择并持久保存 RUN_IDEMPOTENCY_KEY。恢复这次任务时复用同一个键和相同请求;只有明确创建新任务时才使用新键。
export RUN_IDEMPOTENCY_KEY='<persistent-unique-key-for-this-task>'保存为 agent.mjs,运行 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();
}创建响应只是已排队回执,不表示任务完成。保存 receipt.id,进程重启后通过 runs.get(id) 或 runs.wait(id) 恢复查询。关闭 SDK 客户端不会取消正在运行的任务。
复用已保存的 Agent
另外持久保存两个键:AGENT_IDEMPOTENCY_KEY 用于创建 Agent,SAVED_RUN_IDEMPOTENCY_KEY 用于本次 Run。保存返回的 Agent ID,后续 Run 直接使用它;创建 Agent 是一次性设置步骤。
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();
}变量必须与指令模板中的变量完全匹配。Run 被接受后会冻结 Agent 版本、展开的指令和输出结构,后续编辑 Agent 不影响已接受的 Run。metadata 返回给应用,不发送给模型。使用结构定义时,成功的输出是对象;自由输出是文本。
管理资源
以下方法均位于 client.browserAgent。每次调用都使用 await。
| 操作 | 调用 |
|---|---|
| 读取 / 列出 Agent | agents.get(id) / agents.list() |
| 编辑 Agent | agents.update(id, { revision: current.revision, name: "Updated name" }) |
| 删除 Agent | agents.delete(id) |
| 读取 / 列出 Run | runs.get(id) / runs.list() |
| 请求取消 | runs.cancel(id) |
| 等待终态 | runs.wait(id) |
编辑前先读取 Agent 并传入当前 revision;版本冲突时重新读取,更新操作不会自动重试。取消与完成可能同时发生,应检查返回状态;若仍为 cancelling,继续查询直到终态。
恢复与错误处理
- 未提供幂等键时 SDK 会自动生成。需要跨进程恢复时,请自行持久保存并传入:第二个选项参数中的
idempotencyKey。发送前持久保存键和请求体;键使用 1–128 个不含空格的可见 ASCII 字符。相同键和请求体至少七天内返回原回执,不同请求体会冲突。超过该窗口后,先核对历史记录再决定是否新建。 succeeded、failed、timed_out、cancelled都是 Run 终态。HTTP 成功不代表任务成功,应检查status、stop_reason、error和output。RunWaitTimeout仅停止本地轮询。用已知 Run ID 继续查询,或显式请求取消。timeoutMs、pollIntervalMs、requestTimeoutMs单位为毫秒。APIError包含status_code、code、request_id,排查时保留请求 ID。传入{ withResponse: true }可读取成功响应的元信息。- 创建 Run 时使用的 API Key 应保持有效,直到执行和清理完成。撤销它可能让活动 Run 以
credential_unavailable失败;更换客户端 Key 不会替换原 Run 的执行凭证。 - 计量尚未确认时,
usage.credits可能为null,不表示零费用。执行可能产生外部操作,结果未知时不要盲目重新提交。
可运行示例覆盖基础执行、已保存 Agent 和恢复流程。
