npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lsagent-sdk

v0.1.1

Published

浏览器 JS SDK——网页连接用户本机的 lsagent AI 运行时(ws://127.0.0.1:37700/ws):建任务、流式事件、文件上传、模型配置引导

Readme

lsagent-sdk — 让你的网页使用用户本机的 AI

浏览器 JS SDK。你的网页通过它连接跑在用户自己电脑上的 AI 运行时 lsagent:用户在页面上传提单 / 发票 / 客户消息, AI 在用户本机解析成结构化字段回填表单;也能让 AI 读用户的邮箱。 业务系统零后端改造——文件不用传到服务器,模型密钥不用你代管。

你的网页 ── lsagent-sdk(WebSocket,首次系统确认一次)──> 用户电脑上的 lsagent ──> 模型 API / 本机文件 / 邮箱
                                                        │
                                            只监听 ws://127.0.0.1:37700/ws

典型场景:补料 / 单证 AI 导入、客户消息抽取建单、发票识别、邮件内容提取等—— 一切"AI 处理用户自己电脑上的东西"的需求。

与 lsagent 配合使用

lsagent(npm 包)与 lsagent-sdk 是一对,各装一端:

| 端 | 安装 | 角色 | | --- | --- | --- | | 用户的电脑 | npm i -g lsagent,运行 lsagent | AI 运行时:连模型 API、读本机文件、收发邮件、执行任务;只监听 ws://127.0.0.1:37700/ws | | 你的网页 | npm i lsagent-sdk | 遥控器:WebSocket 连接 + 认证 + 任务/事件 API |

用户侧一次性准备(你的页面可以引导完成):

  1. 安装 Node.js ≥ 20 → npm i -g lsagent → 终端运行 lsagent(前台常驻;开机自启自行加系统启动项)。
  2. 无需配对码、无需配白名单:网页不带配对码连接时,用户电脑上会弹一次系统确认框, 点"是"后你的网站即持久授权、以后无感直连(用户可用 lsagent --disallow-origin 撤销)。 localhost 开发环境同样只弹一次。
  3. 模型配置(API key + provider):用户手改两个 JSON 文件(格式见 lsagent README), 或完全由你的网页引导完成(见下方「第 1 层」的 saveProvider(),推荐)。

连接握手时序(免码模式):

connect() ──> connecting ──> pending(用户电脑弹系统确认框,onState 可感知)
   ──用户点「是」──> authed(此后刷新页面 / 重启浏览器无感直连)

lsagent 没在运行的判别:waitForAuthed() 超时返回 false 且 getLastError() 含 code=1006(连不上)——此时页面应出安装引导(指到 lsagent README),用户装好后再 connect() 重试。

兼容性:免码确认制需 lsagent ≥ 0.1.2(更早版本会按配对码错误拒绝免码连接); 配对码模式任意版本可用。npm i -g lsagent 默认装最新版,一般无需关心。

授权管理命令(在用户电脑执行,可写进你的用户帮助文档):

lsagent --show-code                                 # 查看 6 位配对码(配对码模式用)
lsagent --list-origins                              # 查看白名单与确认授权
lsagent --allow-origin https://crm.example.com      # 手动放行你的网站(免弹窗)
lsagent --disallow-origin https://crm.example.com   # 撤销授权(下次连接重新弹窗确认)

安装

方式一:script 标签(零构建;从本包拷 dist/lsagent-sdk.js 到你的 public/,或用 CDN)

<script src="lsagent-sdk.js"></script>
<!-- CDN:<script src="https://unpkg.com/lsagent-sdk/dist/lsagent-sdk.js"></script> -->
<script>
  const client = new LsagentSDK.LsagentClient(); // 免码模式:首次弹系统确认,一次点击永久授权
</script>

方式二:npm / ESM

npm i lsagent-sdk
import { LsagentClient } from "lsagent-sdk";

快速上手

const client = new LsagentClient(); // 免码模式(推荐):首次连接弹系统确认框,用户点一次"是"即永久授权

client.onState((s) => {
  console.log("连接状态:", s);        // disconnected|connecting|connected|pending|authed
  if (s === "pending") showHint("请在弹出的系统确认框上点【是】");
});
client.connect();
await client.waitForAuthed(120_000);                          // 首次要留时间等用户点确认;false = lsagent 未运行

// 创建并执行一个任务(firstMessage 立即开始;模型建议显式指定,见「第 1 层」)
const task = await client.createTask({
  title: "整理今日邮件摘要",
  provider: "deepseek", modelId: "deepseek-chat",              // 用户已配置的模型
  firstMessage: "把今天收到的邮件整理成摘要表格发给我",
  systemPromptAppend: "输出使用简体中文。",                      // 可选:业务背景/输出约定
  mcpServers: ["crm-tools"],                                  // 可选:只接入业务系统自己的 MCP 工具
});

// 实时进度(事件全量广播,按 taskId 过滤)
client.onEvent((event, payload) => {
  if (payload.taskId !== task.id) return;
  if (event === "task.delta") appendStream(payload.text);     // 流式文本
  if (event === "task.status") onDone(payload.status);        // idle|running|error
});

// 追问 / 控制
await client.prompt(task.id, "再按发件人分组");
await client.steer(task.id, "改成按主题分组");                  // 执行中插入转向
await client.abort(task.id);                                  // 中止

// 文件:先落到用户本机,AI 才能读(≤20MB)
const buf = await file.arrayBuffer();
const { path } = await client.request("fs.saveUpload", {
  name: file.name,
  bytesBase64: arrayBufferToBase64(buf),                      // FileReader.readAsDataURL 后取逗号后一段也行
});

// 查询
const models = await client.listModels();
const messages = await client.getMessages(task.id);

完整可跑示例见 demo/(npm run demo 后开 http://127.0.0.1:4567)。

API

| 成员 | 说明 | | --- | --- | | new LsagentClient({ url?, pairingCode? }) | url 缺省 ws://127.0.0.1:37700/ws,远程模式填 ws://IP:37700/ws;不传 pairingCode = 免码模式(推荐,首次系统弹窗确认一次),传则走配对码模式(要求页面是 localhost 或在白名单) | | connect() / disconnect() | 连接(幂等,断线自动指数退避重连)/ 主动断开 | | getState() / onState(cb) | 状态机:disconnected → connecting → connected → (pending) → authed;pending = 等用户在本机点确认框 | | getLastError() | 最近一次失败原因(如"等待用户在本机确认接入…"、"接入被拒绝(denied)"),UI 提示用 | | waitForAuthed(timeoutMs?) | 等待配对完成;超时返回 false。免码模式首次等待用户点击,建议给足超时(如 120_000) | | onEvent(cb) | 订阅事件(见下表),返回取消函数 | | request(type, payload?, timeoutMs?) | 通用透传:lsagent WS 协议全开放(fs.*、email.* 等) | | createTask(opts?) | 建任务;firstMessage 非空则立即执行 | | prompt(taskId, text) | 追加消息(任务空闲时触发执行;执行中会被拒,改用 steer 或等待) | | steer(taskId, text) / abort(taskId) | 执行中转向 / 中止 | | listModels() / listTasks() / getMessages(taskId) | 查询模型 / 任务列表 / 任务消息 | | listProviders() / saveProvider(spec) | 探测本机模型服务配置状态(hasKey)/ 新增更新模型服务(见「第 1 层」) |

createTask 选项:title、provider+modelId(缺省用全局默认模型)、cwd(本机工作目录)、 firstMessage、systemPromptAppend、skills、mcpServers(详见下节)。

模型、提示词与能力配置(集成方必读)

配置分三层,全部经 SDK 同一条连接传输(createTask 参数 + request() 透传),无需额外通道。

第 1 层:模型——密钥在用户侧,选择在任务级

| 关注点 | 谁负责 | 怎么做 | | --- | --- | --- | | 模型服务与 API 密钥 | 用户侧 | 配置存用户本机 ~/.lsagent/pi/(models.json + auth.json);业务系统可探测、可代填,但不该后端囤 key | | 用哪个模型跑任务 | 业务系统(任务级指定) | createTask({ provider, modelId });建议总是显式指定(见下方「默认模型」注意) |

const models = await client.listModels();          // available=true 的才可指定(已配凭据)
const task = await client.createTask({
  provider: models[0].provider, modelId: models[0].id,
  firstMessage: "...",
});

指定了用户未配置的模型会报错「模型不存在或已删除」。

首次配置引导(用户刚装 lsagent、还没配 key 时,全部经 SDK 同一条连接完成):

// 1. 探测:没有任何已配 key 的服务 → 页面出引导表单
const providers = await client.listProviders();
const unconfigured = providers.length === 0 || providers.every((p) => !p.hasKey);

// 2. 用户在表单里选常见服务商预设或自填,提交时落到用户本机,保存即生效:
await client.saveProvider({
  name: "deepseek",                       // provider 标识(自填时自定义,如 "my-company")
  displayName: "DeepSeek",
  baseUrl: "https://api.deepseek.com/v1",
  apiType: "openai-completions",          // OpenAI 兼容端点用这个;Anthropic 兼容用 "anthropic-messages"
  apiKey: userEnteredKey,                 // 写入用户本机 auth.json;留空 = 保留原 key
  models: [
    { id: "deepseek-chat", name: "DeepSeek-V3", contextWindow: 131072, maxTokens: 8192 },
  ],
});

// 3. 复查可用性,然后显式指定模型建任务
const models = await client.listModels();

常见预设速查(baseUrl + apiType):DeepSeek https://api.deepseek.com/v1 / openai-completions; Kimi https://api.moonshot.cn/v1 / openai-completions;智谱 GLM https://open.bigmodel.cn/api/anthropic / anthropic-messages。模型 contextWindow/maxTokens 填服务商标称值即可,影响上下文管理。

企业统一密钥(可选):业务后端托管一份公司级 provider 配置,用户配对完成后页面从业务后端 拉取并 saveProvider 推到用户本机——用户零配置。key 会落到用户本机明文文件(与用户自己配等价), 是否采用由业务方自行评估。

「默认模型」注意:全局默认模型存在用户本机 settings.json,新装用户大概率没设—— 所以 SDK 建任务时总是显式传 provider + modelId,不要依赖缺省。

第 2 层:提示词——systemPromptAppend 是业务注入的唯一正道

createTask({ systemPromptAppend }) 把文本追加到系统提示之后(不替换内置工具指引),适合放: 业务背景("这是货代公司的订单审核系统")、角色约定、输出格式("结果以 JSON 返回,字段…")、 红线("不要执行删除类操作")。

const task = await client.createTask({
  systemPromptAppend: [
    "你是订单审核助手,操作对象是本公司的 ERP。",
    "输出固定 JSON:{approved: boolean, reason: string}。",
  ].join("\n"),
  firstMessage: orderText,
});

单次业务数据(订单内容等)放 firstMessage / prompt(),不要塞进系统提示。

第 3 层:能力——Skills 与 MCP 工具

任务通过参数引用能力,能力本体落在用户本机配置文件里(用户可见、可删,透明可控):

Skills(给 AI 的操作手册/站点策略)

// 探测用户已有的
const local = await client.request("skills.local.list");

// 推送业务系统自己的 skill(首次集成时一次即可;内容 = markdown 手册)
await client.request("skills.local.save", {
  name: "erp-order-guide",
  content: "# ERP 订单审核指南\n1. 先查 ...\n2. ...",
  origin: "业务系统名",          // 可选,标记来源方便用户辨认
});

// 任务引用(按名内联全文进系统提示)
const task = await client.createTask({ skills: ["erp-order-guide"], firstMessage: "..." });

MCP(业务系统提供给 AI 的工具,如查订单/改状态)

// 注册业务系统自己的 MCP Server(http 型最简单:一个 URL + 鉴权头)
await client.request("mcp.local.save", {
  server: {
    name: "crm-tools",
    type: "http",
    url: "https://crm.example.com/mcp",
    headers: { Authorization: "Bearer <用户级token>" },
    enabled: true,
  },
  origin: "业务系统名",
});

// 任务只接入指定 MCP(缺省接入用户已启用的全部)
const task = await client.createTask({ mcpServers: ["crm-tools"], firstMessage: "..." });

stdio 型(command/args/env)也支持,但会启动用户本机进程,优先用 http 型。

推荐的首次集成初始化流程

用户打开业务页面 → SDK 免码连接(onState 出现 pending 时提示用户点系统确认框,waitForAuthed 等待)
  → request("skills.local.list") / request("mcp.local.list") 探测本系统已注册的能力
  → 缺则推送(skills.local.save + mcp.local.save;失败不阻塞,降级为纯 systemPromptAppend)
  → 之后每次 createTask({ systemPromptAppend, skills, mcpServers, ... })

能力推送是写用户本机配置的操作,请在页面里明示,不要静默反复写入; 版本更新时先比对内容再决定是否覆盖。

事件(onEvent)

| 事件 | 载荷(均含 taskId) | | --- | --- | | task.status | status: idle\|running\|error,error? 错误描述 | | task.delta | text 流式增量文本 | | task.message | message 完整消息(role/text/usage…) | | task.tool | phase: start\|end、name 工具名 | | task.usage | input/output/cost 本轮用量 | | task.deleted | 任务被删除 | | mail.new | 新邮件(无 taskId) |

事件为全量广播(与本机其它已配对客户端一致),业务方按 taskId/事件名自行过滤。

安全模型

  • 免码模式 = 系统弹窗确认制:首次连接时用户电脑弹原生确认框,点一次"是"授权即持久 (存 ~/.lsagent/web-grants.json,用户可 lsagent --disallow-origin <url> 撤销)。 授权绑定来源 origin,浏览器无法伪造;点"否"只拒当次,同一网站连拒 3 次转静默拦截, 恶意网站无法靠反复请求轰炸用户(10 分钟内最多弹 5 次确认框)。
  • 配对码 = 完全授权:持有配对码的页面可读本机文件、发邮件、执行任务,且要求页面 来源是 localhost 或用户白名单(防钓鱼页骗码直连)。只在用户明确信任的系统中使用, 建议用户输入而非硬编码进代码。
  • lsagent 有防爆破:同 IP 连续错 5 次锁 10 分钟;配对码 6 位、服务只监听本机回环(远程模式除外)。

限制

  • 需用户电脑开机且 lsagent 在线(onState 可感知断线,SDK 自动重连)。
  • 来源白名单为精确匹配,暂不支持 *.example.com 通配。
  • 本机模式面向「用户在场」的场景;无人值守/服务端集中模式不在本 SDK 范围。

本地开发

npm install          # 在仓库根目录(workspace)
npm run build -w sdk     # 构建 dist(esbuild 产物 + tsc 类型声明)
npm run demo -w sdk      # 演示页 http://127.0.0.1:4567
npm run typecheck -w sdk

许可证

MIT