快速开始 · JS / 浏览器

快速开始 · JS / 浏览器

5 分钟在浏览器接入 LinkSoul JS SDK。核心与原生 SDK 一致,但因浏览器限制,鉴权必须经过一个同源服务端代理——这是本页与原生快速开始最大的不同。

环境要求

项要求
包linksoul-agentsdk(ESM)
运行时现代浏览器;安全上下文(https 或 localhost),否则拿不到麦克风、crypto.randomUUID 也不可用
打包器Vite / webpack / esbuild 等支持 ESM 的工具
服务端任意能算 HMAC-SHA256 并代理 WebSocket 的后端(示例用 Node server.mjs)

安装与导入

bash
npm install linksoul-agentsdk
ts
import {
  createAgentSdk,
  AgentEventType,
  type TeleopSession,
} from 'linksoul-agentsdk';

服务端代理:为什么必须

浏览器的 WebSocket 构造器只接受 url 和 protocols,无法设置 X-App-Id / X-Signature 等鉴权头,也发不了协议级 Ping 帧。因此 JS SDK 把这两件事交给一个同源服务端:

text
浏览器                         你的服务端                        灵心网关
  │  POST /api/ws-auth           │                                 │
  │ ───────────────────────────→ │  计算 HMAC-SHA256               │
  │                              │  用 X-App-* 头连上游 ───────────→ │
  │  { url: ws://.../ws?t=票据 } │ ←────────────── 握手成功 ─────── │
  │ ←─────────────────────────── │                                 │
  │  new WebSocket(本地代理URL)   │                                 │
  │ ───────────────────────────→ │  ←── 双向透传 ────────────────→ │
  • appSecret 只存在于服务端;浏览器只拿到一个一次性票据换来的本地代理 URL;
  • 服务端把 SDK 发出的 JSON {type:"ping"} 转成 WebSocket 协议级 Ping,把网关的协议级 Pong 合成 JSON {type:"pong"} 回传,从而让 SDK 的 keep-alive 在浏览器下也能工作。

服务端签名要点(Node 示例)

js
import { createHmac, randomUUID } from 'node:crypto';

const timestamp = String(Date.now());
const nonce = randomUUID().replace(/-/g, '');
const signature = createHmac('sha256', appSecret)
  .update(`GET\n${pathname}\n${timestamp}\n${nonce}`)
  .digest('hex');

// 用这些头连上游网关:
// X-App-Id, X-App-Key, X-Timestamp, X-Nonce, X-Signature, X-Callback-Types

签名串固定为 HMAC-SHA256(appSecret, "GET\n{pathname}\n{timestamp}\n{nonce}")(hex)。完整可运行代理见示例 agentsdk_for_js/example/teleop/server.mjs。

完整接入代码

ts
import { createAgentSdk, AgentEventType } from 'linksoul-agentsdk';

const appId = '<灵心开放平台应用 appId>';
const agentId = '<目标机器人 agentId>';

// 1. 创建 SDK:authProvider 每次连接 / 重连时被调用,返回已签名的代理 URL
const sdk = createAgentSdk(appId, {
  authProvider: async () => {
    const res = await fetch('/api/ws-auth', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        url: 'wss://open.agibot.com/api/V1/open-portal/app/wss/agent-sdk',
        appId,
        // appKey / appSecret 只在服务端读取,切勿写进前端
      }),
    });
    const { url } = await res.json();
    return { url }; // 指向本地代理,如 ws://localhost:8787/api/ws?t=票据
  },
  reconnect: true, // 断线自动重连(固定 3s 间隔)
});

// 2. 监听下行消息(所有 ACK 都在这里)
sdk.on('message', ({ data }) => {
  const msg = JSON.parse(typeof data === 'string' ? data : '');
  if (msg.type === AgentEventType.TeleopEnterAck) {
    // msg.code === 0 表示进入成功
  }
});
sdk.on('connected', () => console.log('connected'));
sdk.on('disconnected', ({ willReconnect }) => console.log('closed', willReconnect));

// 3. 建立连接(内部会调用 authProvider)
await sdk.connect();

// 4. 之后创建遥操会话,完整流程见「超视距遥操指南」
const session = sdk.createTeleopSession({ agentId });
session.enter();

单例注意事项

createAgentSdk(appId, ...) / AgentSdk.create(appId, ...) 按 appId 维护单例:同 appId 重复调用返回同一个实例。切换账号或需要全新连接时,先 sdk.release()(关闭并从单例表移除)再重新 createAgentSdk。

常见问题

  • getUserMedia 报错 / 拿不到麦克风:确认页面运行在 https 或 localhost 下(安全上下文)。
  • 连接立即失败:多半是 authProvider 返回的 URL 无效或服务端签名失败;先单独用 curl / Postman 打通 /api/ws-auth。
  • crypto.randomUUID is not a function:非安全上下文下不可用;SDK 内部已回退到 crypto.getRandomValues / Math.random,但仍建议部署到 https。
  • 重连后要重新 enter 吗:要。重连是连接层行为,遥操会话状态不会自动恢复;在 connected 事件里重新走 createTeleopSession → enter。