sdks/typescript
开发模版与 SDK

TypeScript / Node.js SDK

基于原生 fetch 的单文件 TypeScript 客户端,零外部依赖

无论是构建 Electron 桌面应用、Next.js 后端还是 Node.js 自动化服务,将以下单文件直接拷入项目中即可开箱即用。


客户端源码 (LanghuaClient.ts)

export interface DeviceInfo {
  deviceId: string;
  brand: string;
  model: string;
  status: "ONLINE" | "OFFLINE";
  batteryLevel?: number;
  currentApp?: string;
}

export interface ScreenshotResult {
  image_base64: string;
  width: number;
  height: number;
}

export interface SkillResponse<T = any> {
  success: boolean;
  message: string;
  data?: T;
  skillId?: string;
  deviceId?: string;
}

export class LanghuaClient {
  private baseUrl: string;
  private appKey: string;

  constructor(options: { baseUrl?: string; appKey: string }) {
    this.baseUrl = (options.baseUrl || "http://localhost:58166").replace(/\/+$/, "");
    this.appKey = options.appKey;
  }

  private async request<T = any>(path: string, options: RequestInit = {}): Promise<T> {
    const res = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: {
        "Content-Type": "application/json",
        "X-AppKey": this.appKey,
        ...(options.headers || {})
      }
    });
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${await res.text()}`);
    }
    return (await res.json()) as T;
  }

  /** 统一技能执行通道 */
  async executeSkill<T = any>(deviceId: string, skillId: string, params: Record<string, any> = {}): Promise<SkillResponse<T>> {
    return this.request<SkillResponse<T>>("/gateway/skill/execute", {
      method: "POST",
      body: JSON.stringify({
        device_id: deviceId,
        skill_id: skillId,
        params
      })
    });
  }

  // ── 1. 设备与生命周期 ──

  /** 获取名下已绑定的在线设备列表 */
  async listDevices(): Promise<DeviceInfo[]> {
    const res = await this.request<{ code: number; data: DeviceInfo[] }>("/gateway/device/list");
    return res.data || [];
  }

  /** 唤醒屏幕并解锁 */
  async unlock(deviceId: string, credential = ""): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "system.unlock", { credential });
  }

  // ── 2. 触控与输入 ──

  /** 点击屏幕物理坐标 (x, y) */
  async tap(deviceId: string, x: number, y: number, duration = 150): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "ui.tap", { x, y, duration });
  }

  /** 拟人平滑滑动 */
  async swipe(deviceId: string, x1: number, y1: number, x2: number, y2: number, duration = 300, steps = 10): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "ui.swipe", { x1, y1, x2, y2, duration, steps });
  }

  /** 整页翻页 */
  async scroll(deviceId: string, action: "PAGE_DOWN" | "PAGE_UP" | "LEFT" | "RIGHT" = "PAGE_DOWN"): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "ui.scroll", { action });
  }

  /** 拟真逐字输入 (可传 x, y 自动先点击获焦) */
  async inputText(deviceId: string, text: string, options?: { x?: number; y?: number; clear?: boolean }): Promise<SkillResponse> {
    const params: Record<string, any> = {
      text,
      clear: options?.clear ?? true,
      delay: 150
    };
    if (options?.x !== undefined && options?.y !== undefined) {
      params.x = options.x;
      params.y = options.y;
    }
    return this.executeSkill(deviceId, "ui.input", params);
  }

  /** 物理按键 (4=Back, 3=Home, 187=Recents, 66=Enter) */
  async pressKey(deviceId: string, keyCode = 4): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "control.key_event", { keyCode });
  }

  // ── 3. 应用管理 ──

  /** 启动应用 (息屏自动唤醒解锁) */
  async launchApp(deviceId: string, packageName: string): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "app.launch", { package_name: packageName });
  }

  /** 获取当前活跃前台包名与界面 */
  async getCurrentApp(deviceId: string): Promise<{ package_name: string; activity_name: string }> {
    const res = await this.executeSkill(deviceId, "app.get_current");
    return res.data;
  }

  /** 静默授权敏感权限 */
  async grantPermission(deviceId: string, packageName: string, permission: string): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "app.grant_permission", {
      package_name: packageName,
      permission
    });
  }

  // ── 4. 视觉感知与媒体 ──

  /** 高质量采集 (返回 Base64 与宽高,赋能 VLM) */
  async screenshot(deviceId: string, options?: { quality?: number; scale?: number }): Promise<ScreenshotResult> {
    const res = await this.executeSkill<ScreenshotResult>(deviceId, "control.screenshot_info", {
      quality: options?.quality ?? 50,
      scale: options?.scale ?? 0.5
    });
    return (res.data || res) as ScreenshotResult;
  }

  /** 批量向系统相册注入图片 */
  async downloadToGallery(deviceId: string, imageUrls: string[]): Promise<SkillResponse> {
    return this.executeSkill(deviceId, "media.download_image", { image_urls: imageUrls });
  }
}

30 秒上手示例

import { LanghuaClient } from "./LanghuaClient";

const client = new LanghuaClient({
  baseUrl: "http://localhost:58166",
  appKey: "sk-lh-xxxxxxxxxxxxxxxx"
});

async function run() {
  const devices = await client.listDevices();
  const deviceId = devices[0].deviceId;

  // 1. 唤醒并启动微信
  await client.launchApp(deviceId, "com.tencent.mm");

  // 2. 在指定坐标获焦并输入文本
  await client.inputText(deviceId, "自动化测试消息", { x: 540, y: 320 });

  // 3. 点击发送按钮
  await client.tap(deviceId, 980, 2300);
}

run().catch(console.error);