@broberg/lens-client

中文文档
0.2.0发布于 2026/10/31MITTypeScriptESM + CJS零依赖

Thin client for the HOSTED Lens (lens.cardmem.com) — createLensClient({baseUrl,token}).capture()/.runFlow() with cold-start retry + self-healing LocateSpec, and an optional mountable Hono proxy (createLensProxy) so a product frontend hits /api/lens/* same

原始使用文档
文档目录8 节
  1. The three-package split
  2. Usage (server-side)
  3. Self-healing locators
  4. Errors — a failed flow is data, not an exception
  5. Cold start
  6. Artifact token gotcha
  7. Browser proxy — @broberg/lens-client/hono
  8. API

@broberg/lens-client

A thin client for the hosted Lens (lens.cardmem.com). Screenshot pages and drive self-healing flows over HTTP — no Playwright, no browser on your side.

npm i @broberg/lens-client

The three-package split

You need to…Use
Call the hosted Lens over HTTP (no browser)@broberg/lens-client
Run a real browser yourself (capture + flow)@broberg/lens-engine (Playwright)
Mint / validate a Lens session (auth/compliance)@broberg/lens (dep-free)

Usage (server-side)

import { createLensClient } from "@broberg/lens-client";

const lens = createLensClient({
  baseUrl: process.env.LENS_CLOUD_URL,   // default https://lens.cardmem.com
  token: process.env.LENS_CLOUD_TOKEN,   // Bearer service token — server-side only
});

const shot = await lens.capture({ url: "https://example.com", mode: "fullPage" });
// → { run_id, screenshot_url, dom_hash, status: "ok", width, height, final_url, title }
const png = shot.screenshot_url ? await lens.fetchArtifact(shot.screenshot_url) : null;

const run = await lens.runFlow({
  base_url: "https://appstoreconnect.apple.com",
  steps: [
    { action: "goto", url: "/apps" },
    { action: "click", target: { role: "button", name: "New Version" } },  // self-healing LocateSpec
    { action: "fill",  target: { label: "Version Number" }, value: "1.2.0" },
  ],
});

baseUrl/token default to LENS_CLOUD_URL / LENS_CLOUD_TOKEN.

Self-healing locators

A step target is a plain string (CSS selector / bare data-testid) or a LocateSpec — { testid?, css?, role?, name?, label?, placeholder?, text?, exact?, nth?, vision? } — tried in fixed priority: testid → css → role → label → placeholder → text → vision.

Each step result carries resolved_via (which layer matched), so you can log a degraded-match alert when a flow drifted off testid onto a fuzzier layer.

Errors — a failed flow is data, not an exception

A failing step stops the flow and pins a screenshot; the flow comes back as a normal result with status: "failed" — read steps to see which step failed and why (never a thrown exception that loses context):

const run = await lens.runFlow(manuscript);
if (run.status === "failed") {
  const bad = run.steps.find((s) => s.status === "failed");
  // bad.index, bad.action, bad.error, bad.screenshot_url
}

Only transport/auth failures throw a LensClientError (.kind = auth on 401, unavailable on 503, network after retries, http otherwise).

Cold start

The hosted Lens auto-stops when idle. The client pre-warms with GET /health and retries a 502 / network error (1–2 tries, backoff). It never retries a 401 (bad token) or 503 (ship-dark) — those are terminal. Tune with { retries, retryBackoffMs, prewarm }.

Artifact token gotcha

screenshot_url points at /artifact?key=… and needs the same Bearer to fetch. fetchArtifact(url) attaches it only when the URL is same-origin as baseUrl — never leaking the token to a foreign host.

Browser proxy — @broberg/lens-client/hono

So a product's frontend can call Lens without ever seeing the token, mount the proxy on your own server (same-origin /api/lens/*, token stays server-side):

import { createLensProxy } from "@broberg/lens-client/hono";

app.route("/api/lens", createLensProxy());   // token from LENS_CLOUD_TOKEN

// browser:
const lens = createLensClient({ baseUrl: "/api/lens" });   // no token in the browser

hono is an optional peer — only needed for the proxy. The core client (.) has zero runtime dependencies.

API

function createLensClient(opts?: LensClientOptions): LensClient;
//   .capture(body) → CaptureResult   .runFlow(body) → FlowResult
//   .health() → boolean              .fetchArtifact(url) → Uint8Array
function createLensProxy(opts?: LensProxyOptions): Hono;   // "@broberg/lens-client/hono"
class LensClientError extends Error;   // .kind, .status

MIT · part of the @broberg/* shared-library family.

版本与发布记录

@broberg/lens-client 共发布 3 个版本,首次发布于 2026/07/04;过去 12 个月发布了 3 个版本。

版本发布日期距今
0.2.0latest2026/10/312 天前
0.1.22026/10/1222 天前
0.1.02026/07/044 个月前

关于 @broberg/lens-client 的常见问题

@broberg/lens-client 是什么?

瘦客户端为HOSTED Lens (lens.cardmem.com) - createLensClient({baseUrl,token}).capture()/.runFlow()与冷启动重试+自修复LocateSpec,和一个可选的挂载Hono代理(createLensProxy),所以一个产品前端点击/api/ Lens /* same

如何安装 @broberg/lens-client?

在项目目录运行 npm install @broberg/lens-client,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 lens-mcp,可以用 npx lens-mcp 直接运行。

@broberg/lens-client 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@broberg/lens-client 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@broberg/lens-client 可以商用吗?

@broberg/lens-client 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@broberg/lens-client 还在维护吗?

最新版本 0.2.0 发布于 2026/10/31(2 天前);至今共发布 3 个版本,其中过去 12 个月发布了 3 个;首次发布于 2026/07/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。