架构概述 · JS / 浏览器

架构概述 · JS / 浏览器

JS SDK(linksoul-agentsdk)是面向浏览器的 TypeScript SDK(ESM),与 Java v1.5.0 共享同一套 wire 协议。与原生 SDK 最大的结构性差异是:浏览器 WebSocket 无法设置自定义请求头,因此鉴权与心跳都前置到同源服务端代理完成。

系统架构

text
┌─────────────────────────────────────────────────────────────────────────────┐
│                              灵心平台 (LinkSoul)                              │
│  ┌───────────────────────┐          ┌───────────────────────┐               │
│  │  LinkskyGateway        │ ◄──────► │  交互网关 (Gateway)    │               │
│  │  (二开接入网关)         │  Bridge  │  连接机器人终端         │               │
│  └───────────▲───────────┘          └───────────▲───────────┘               │
└──────────────┼───────────────────────────────────┼───────────────────────────┘
               │ wss(带 HMAC 五头签名)             │
               │                                   │
        ┌──────┴──────┐                     ┌──────┴──────┐
        │ 你的服务端代理 │                     │  机器人终端  │
        │ (计算签名)   │                     │ 音频/视频/动作│
        └──────▲──────┘                     └──────────────┘
               │ 本地代理 URL(无签名要求)
               │
        ┌──────┴──────┐
        │  浏览器 (JS SDK)│   TeleopSession / ExtSkillSession
        └──────────────┘

模块结构

text
linksoul-agentsdk/src/
├── agent-sdk.ts               # AgentSdk(按 appId 单例)+ createAgentSdk 工厂
├── connection/
│   ├── agent-connection.ts    # 连接编排(connect/重连/消息分发)
│   ├── keep-alive.ts          # KeepAliveController(应用层保活)
│   └── types.ts               # ConnectionOptions / AuthProvider / SocketLike ...
├── teleop/teleop-session.ts   # TeleopSession(遥操六方法)
├── ext-skill/ext-skill-session.ts  # ExtSkillSession(query / invoke)
├── protocol/events.ts         # AgentEventType / AgentPolicy / ArbiterDecision 常量
├── sdk-meta-report.ts         # 连接后自动上报 sdk_meta.report
├── system-info.ts             # systemInfoSnapshot(无真实 MAC/用户名)
└── random-id.ts               # randomHex

连接与鉴权

浏览器 WebSocket 构造器不支持自定义请求头,无法在握手时带 X-App-Id / X-Signature。JS SDK 通过 authProvider 回调把鉴权外包给同源服务端:

ts
const sdk = createAgentSdk(appId, {
  authProvider: async () => {
    const res = await fetch('/api/ws-auth', { method: 'POST' });   // 服务端用 appSecret 签名
    const { url } = await res.json();   // 返回已鉴权的(本地代理)URL
    return { url };
  },
});
await sdk.connect();
  • authProvider 在每次 connect() 与每次自动重连时都会被调用;
  • 返回 { url, protocols? },url 通常指向你服务端的本地代理;
  • appSecret 绝不出现在前端代码里。

保活(keepalive)

浏览器无法发协议级 PingFrame,JS SDK 的 KeepAliveController 发的是 JSON 文本 {"type":"ping"},由服务端代理转换成协议级 PingFrame 发给网关,再把 Pong 合成 JSON 回浏览器——这条转换是接入契约必须由代理完成(否则连接握手成功但心跳永远超时)。

重连机制

  • 连接失败/断开后自动重连,默认固定 3s(reconnect.delayMs 可调);
  • reconnect?: boolean | { delayMs?: number },传 false 关闭;
  • 保活探测超时(空闲 20s → 探测 3 次无响应)会主动 close(4000) 触发重连。

事件模型

JS SDK 采用 sdk.on(event, listener) 订阅连接级事件,返回取消订阅函数:

事件负载说明
connectedvoid连接成功(自动上报 sdk_meta.report)
disconnected{ willReconnect }连接断开
reconnecting{ attempt }第 N 次重连
message{ data }所有下行消息(业务侧 JSON.parse 后按 type 分派)
sent{ data }上行消息
errorError连接错误
keepalive{ state, probes }保活状态机

v1.5.0 范围:JS SDK 只内置 Teleop + ExtSkill 两条交互路径。被动(8 种回调)、主动(任务流)尚无公开实现——agent-sdk.ts 不含任何被动回调类或 TaskFlowRequest。相关 wire 常量(FunctionCallExpose / FunctionCallArbiter / HistoriesExpose / ArbiterDecision)在 protocol/events.ts 中预留但未接入分发。详见 被动交互指南 与 主动交互指南。

线程 / 并发模型

浏览器单线程事件循环,无多线程概念:sdk.send() 逻辑同步执行(立即写 WebSocket),异步只体现在 authProvider 与网络 I/O 上。