Teleop(超视距遥操)API · JS / 浏览器

Teleop(超视距遥操)API · JS / 浏览器

⚠️ 尚未上线:本能力为 v1.5.0-SNAPSHOT 开发中特性,接口与协议可能调整,正式发布前请勿用于生产环境。

v1.5.0。本页列出 JS SDK 的公开 TypeScript API。wire 协议、事件类型、错误码与 Java v1.5.0 · Teleop API 一致;差异集中在回调模型(无回调类,走 message 事件)与浏览器鉴权 / 心跳(服务端代理)。

与原生 SDK 的差异

差异点Java / Android / iOSJS(浏览器)
推音频appendAudioDelta(eventId, base64[, gain])sendAudio(base64, eventId?, gain?),eventId 缺省用 startAudio 返回值
结束音频doneAudio(eventId)finishAudio(eventId?)
探活keepalive(cb, timeout)keepAlive()(无回调、无 timeout 参数)
ACKTeleopEnterCallback / TeleopExitCallback 等回调类无;sdk.on('message') 里按 type === "*_ack" 判读
鉴权SDK 直接带 X-App-* 头authProvider 回调向同源服务端换签名代理 URL
心跳协议级 PingJSON {type:"ping"},服务端代理转协议级 Ping
ID 生成IdGenerator.generateTeleopId()构造 TeleopSession 时自动生成 teleop_<hex>;sdk.newEventId() 生成 eventId

导入

ts
import {
  createAgentSdk,
  AgentSdk,
  AgentConnection,
  KeepAliveController,
  TeleopSession,
  AgentEventType,
  AgentPolicy,
  systemInfoSnapshot,
  systemInfoDescription,
  sendSdkMetaReport,
} from 'linksoul-agentsdk';

import type {
  AgentSdkOptions,
  AuthProvider,
  ConnectionEvents,
  ConnectionOptions,
  KeepAliveOptions,
  SocketFactory,
  SocketLike,
  TeleopOptions,
  TeleopAck,
  SystemInfoSnapshot,
} from 'linksoul-agentsdk';

类图

text
createAgentSdk(appId, options) → AgentSdk

AgentSdk
  ├── static create(appId, options) → AgentSdk        // 按 appId 单例
  ├── static get(appId) → AgentSdk | undefined
  ├── static listAppIds() → string[]
  ├── on(event, listener) → () => void                // 返回取消订阅函数
  ├── connect() → Promise<void>
  ├── close() → void
  ├── release() → void                                // close + 移出单例表
  ├── createTeleopSession(options) → TeleopSession
  ├── send(message) → void
  └── newEventId() → string                           // event_<base36>_<seq>_<hex>

TeleopSession
  ├── constructor(sdk, { agentId, teleopId? })
  ├── enter() → string(eventId)
  ├── startAudio(param?) → string(eventId)
  ├── sendAudio(base64, eventId?, gain?) → void
  ├── finishAudio(eventId?) → void
  ├── keepAlive() → string(eventId)
  └── exit() → string(eventId)

createAgentSdk / AgentSdk

ts
export function createAgentSdk(appId: string, options: AgentSdkOptions): AgentSdk;

export type AgentSdkOptions = ConnectionOptions;

export class AgentSdk {
  readonly connection: AgentConnection;
  readonly appId: string;
  readonly options: AgentSdkOptions;

  static create(appId: string, options: AgentSdkOptions): AgentSdk; // 同 appId 复用实例
  static get(appId: string): AgentSdk | undefined;
  static listAppIds(): string[];

  on<K extends keyof ConnectionEvents>(event: K, listener: (value: ConnectionEvents[K]) => void): () => void;
  connect(): Promise<void>;
  close(): void;
  release(): void;
  createTeleopSession(options: TeleopOptions): TeleopSession;
  send(message: Record<string, unknown>): void;
  newEventId(): string;
}

ConnectionOptions / AuthProvider

ts
export type ConnectionOptions = {
  authProvider: AuthProvider;
  socketFactory?: SocketFactory;                       // 可注入自定义 WebSocket(如 Node 侧 ws,测试用)
  reconnect?: boolean | { delayMs?: number };          // 默认开启,固定 3000ms
  keepAlive?: KeepAliveOptions;
};

export type AuthProvider = () => Promise<{ url: string; protocols?: string | string[] }>;

authProvider 在每次 connect() 与每次自动重连时被调用,需返回一个已鉴权的(通常指向同源代理的)WebSocket URL。

TeleopSession

ts
export type TeleopOptions = { agentId: string; teleopId?: string };
export type TeleopAck = { teleopId: string; eventId: string; code: number; message?: string };

export class TeleopSession {
  readonly agentId: string;
  readonly teleopId: string; // 未传时自动生成 teleop_<hex>

  constructor(sdk: AgentSdk, options: TeleopOptions);

  enter(): string;                                 // 发 teleop.enter,返回 eventId
  startAudio(param?: Record<string, unknown>): string; // 发 teleop_audio.start,返回并记住 eventId
  sendAudio(audioBase64: string, eventId?: string, gain?: number): void; // 发 teleop_audio.delta(含 audio + audioLen + gain);未 startAudio 先调会抛错
  finishAudio(eventId?: string): void;             // 发 teleop_audio.done,清空内部 audioEventId;未 startAudio 先调会抛错
  keepAlive(): string;                             // 发 teleop.keepalive,返回 eventId
  exit(): string;                                  // 发 teleop.exit,返回 eventId
}
  • 所有方法同步执行(内部 sdk.send() 立即经开着的 WebSocket 发出),返回 string(eventId)或 void,不返回 Promise;
  • sendAudio 会就地解码 base64 计算 audioLen(解码后字节数)一并发出;可选 gain 为音频增益倍数,默认 1.0,有效范围 (0, 10](<=0 归一为 1.0,>10 截断为 10.0),随消息 gain 字段下发;
  • 无 per-session 事件回调;TeleopAck 类型仅用于描述下行 ACK 的字段形状(teleopId / eventId / code / message),实际从 sdk.on('message') 解析。

下行 ACK 判读

JS SDK 不提供回调类。在 sdk.on('message') 里按 type 判读:

ts
sdk.on('message', ({ data }) => {
  let msg: { type: string; code?: number; teleopId?: string; eventId?: string; msg?: string };
  try { msg = JSON.parse(typeof data === 'string' ? data : ''); } catch { return; }
  switch (msg.type) {
    case AgentEventType.TeleopEnterAck:     /* msg.code === 0 成功 */ break;
    case AgentEventType.TeleopKeepaliveAck: /* msg.code !== 0 视为离线 */ break;
    case AgentEventType.TeleopExitAck:      /* 清理 */ break;
  }
});

code 语义与原生一致:0 成功;-1 服务端拒绝 / 机器人离线。

ConnectionEvents

ts
export type ConnectionEvents = {
  connected: void;
  disconnected: { willReconnect: boolean };
  reconnecting: { attempt: number };
  message: { data: unknown };
  sent: { data: Record<string, unknown> };
  error: Error;
  keepalive: { state: 'idle' | 'probing' | 'healthy' | 'timeout'; probes: number };
};

KeepAliveOptions(连接层心跳,非遥操 keepAlive)

ts
export type KeepAliveOptions = {
  readIdleMs?: number;      // 默认 20000:无下行超过此时长进入探测
  probeIntervalMs?: number; // 默认 1000:探测 ping 间隔
  maxProbes?: number;       // 默认 3:连续探测次数
  createPing?: (sequence: number) => string; // 默认 JSON.stringify({type:'ping', sequence})
};

注意区分:这里的连接层 keep-alive 是保活 WebSocket;而 TeleopSession.keepAlive() 是探测机器人是否在线,两者不同。

事件类型与策略

ts
export const AgentEventType = {
  TeleopEnter:        'agentsdk.teleop.enter',
  TeleopEnterAck:     'agentsdk.teleop.enter_ack',
  TeleopAudioStart:   'agentsdk.teleop_audio.start',
  TeleopAudioDelta:   'agentsdk.teleop_audio.delta',
  TeleopAudioDone:    'agentsdk.teleop_audio.done',
  TeleopKeepalive:    'agentsdk.teleop.keepalive',
  TeleopKeepaliveAck: 'agentsdk.teleop.keepalive_ack',
  TeleopExit:         'agentsdk.teleop.exit',
  TeleopExitAck:      'agentsdk.teleop.exit_ack',
  SdkMetaReport:      'agentsdk.sdk_meta.report',
  Error:              'agentsdk.error',
} as const;

export const AgentPolicy = {
  Active:  'active',
  Passive: 'passive',
  Teleop:  'teleop',
} as const;

wire 结构

上行 enter / keepalive / exit:

json
{
  "type":      "agentsdk.teleop.enter",
  "agentId":   "<agentId>",
  "teleopId":  "teleop_xxxxxxxxxxxxxxxxxxxxx",
  "eventId":   "event_xxxxxxxxxxxxxxxxxxxxx",
  "agentMode": "teleop"
}

startAudio(param 扁平放入):

json
{
  "type":      "agentsdk.teleop_audio.start",
  "agentId":   "<agentId>",
  "teleopId":  "teleop_xxxxxxxxxxxxxxxxxxxxx",
  "eventId":   "event_xxxxxxxxxxxxxxxxxxxxx",
  "agentMode": "teleop",
  "param":     { "role": "male", "threshold": 0 }
}

sendAudio(delta,含 audioLen / gain):

json
{
  "type":      "agentsdk.teleop_audio.delta",
  "agentId":   "<agentId>",
  "teleopId":  "teleop_xxxxxxxxxxxxxxxxxxxxx",
  "eventId":   "event_xxxxxxxxxxxxxxxxxxxxx",
  "agentMode": "teleop",
  "audio":     "<base64 chunk>",
  "audioLen":  1920,
  "gain":      1.0
}

audioLen 是解码后的字节数,供服务端做完整性 / QoS 校验;业务侧无需理会。

SDK meta 上报

ts
export function sendSdkMetaReport(sdk: AgentSdk, appId: string): void;
export function systemInfoSnapshot(): SystemInfoSnapshot;
export function systemInfoDescription(): string;

export interface SystemInfoSnapshot {
  readonly os: string;
  readonly osVersion: string;
  readonly arch: string;       // 浏览器恒为 "browser"
  readonly macAddress: string; // 无真实 MAC,用 localStorage 持久化的 client_<hex>
  readonly username: string;   // 浏览器恒为空串
}

每次 connected(含重连)SDK 自动调用 sendSdkMetaReport,发送 agentsdk.sdk_meta.report(sdkLanguage: "js")。上报失败被静默吞掉,不影响主流程。