{"version":3,"file":"index.mjs","names":[],"sources":["../src/agp-client.ts","../src/index.ts"],"sourcesContent":["/**\n * AGP WebSocket Client\n *\n * Reverse-engineered from the wechat-access plugin's websocket-client.ts (739 lines).\n * Implements the full AGP (Agent Gateway Protocol) over WebSocket:\n *\n *   - Connection with token auth (?token= query param)\n *   - Auto-reconnect with exponential backoff (3s base, 1.5x, 25s cap)\n *   - Heartbeat via native ws ping/pong (20s default, 2x timeout detection)\n *   - System wakeup detection (timer drift > 15s triggers reconnect)\n *   - Message dedup (Set<msg_id>, cleaned every 5min, max 1000 entries)\n *   - Full send API: sendMessageChunk, sendToolCall, sendToolCallUpdate, sendPromptResponse\n *   - Event callbacks: onConnected, onDisconnected, onPrompt, onCancel, onError\n *\n * This is a server-push channel: the server sends session.prompt when a WeChat\n * user messages your agent, and you stream back responses via session.update +\n * session.promptResponse.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport WebSocket from \"ws\";\nimport type {\n  AGPEnvelope,\n  AGPMethod,\n  AGPClientConfig,\n  AGPClientCallbacks,\n  ConnectionState,\n  PromptMessage,\n  CancelMessage,\n  ContentBlock,\n  ToolCall,\n  UpdatePayload,\n  PromptResponsePayload,\n} from \"./agp-types.js\";\n\n// ============================================\n// Defaults\n// ============================================\n\nconst DEFAULT_RECONNECT_INTERVAL = 3000;\nconst DEFAULT_HEARTBEAT_INTERVAL = 20000;\nconst MAX_RECONNECT_DELAY = 25000;\nconst BACKOFF_MULTIPLIER = 1.5;\n\nconst WAKEUP_CHECK_INTERVAL = 5000;\nconst WAKEUP_THRESHOLD = 15000;\n\nconst MAX_MSG_ID_CACHE = 1000;\nconst MSG_ID_CLEANUP_INTERVAL = 5 * 60 * 1000;\n\n// ============================================\n// Client\n// ============================================\n\nexport class AGPClient {\n  // -- Config (resolved with defaults) --\n  private readonly url: string;\n  private token: string;\n  private readonly guid: string;\n  private readonly userId: string;\n  private readonly reconnectInterval: number;\n  private readonly maxReconnectAttempts: number;\n  private readonly heartbeatInterval: number;\n\n  // -- Callbacks --\n  private callbacks: AGPClientCallbacks;\n\n  // -- Connection state --\n  private ws: WebSocket | null = null;\n  private state: ConnectionState = \"disconnected\";\n\n  // -- Timers --\n  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n  private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n  private wakeupCheckTimer: ReturnType<typeof setInterval> | null = null;\n  private msgIdCleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n  // -- Reconnect tracking --\n  private reconnectAttempts = 0;\n\n  // -- Heartbeat tracking --\n  private lastPongTime = Date.now();\n\n  // -- Wakeup detection --\n  private lastTickTime = Date.now();\n\n  // -- Message dedup --\n  private processedMsgIds = new Set<string>();\n\n  constructor(config: AGPClientConfig, callbacks: AGPClientCallbacks = {}) {\n    this.url = config.url;\n    this.token = config.token;\n    this.guid = config.guid ?? \"\";\n    this.userId = config.userId ?? \"\";\n    this.reconnectInterval =\n      config.reconnectInterval ?? DEFAULT_RECONNECT_INTERVAL;\n    this.maxReconnectAttempts = config.maxReconnectAttempts ?? 0;\n    this.heartbeatInterval =\n      config.heartbeatInterval ?? DEFAULT_HEARTBEAT_INTERVAL;\n    this.callbacks = callbacks;\n  }\n\n  // -----------------------------------------------------------------------\n  // Public lifecycle\n  // -----------------------------------------------------------------------\n\n  /** Start the WebSocket connection.  No-op if already connected/connecting. */\n  start(): void {\n    if (this.state === \"connected\" || this.state === \"connecting\") {\n      return;\n    }\n    this.connect();\n    this.startMsgIdCleanup();\n  }\n\n  /**\n   * Gracefully stop.  Closes the socket, cancels all timers,\n   * and prevents automatic reconnection.\n   */\n  stop(): void {\n    this.state = \"disconnected\";\n    this.clearReconnectTimer();\n    this.clearHeartbeat();\n    this.clearWakeupDetection();\n    this.clearMsgIdCleanup();\n    this.processedMsgIds.clear();\n\n    if (this.ws) {\n      this.ws.close();\n      this.ws = null;\n    }\n  }\n\n  /** Current connection state. */\n  getState(): ConnectionState {\n    return this.state;\n  }\n\n  /** Replace the auth token (e.g. after a refresh).  Takes effect on next connect. */\n  setToken(token: string): void {\n    this.token = token;\n  }\n\n  /** Merge in new callbacks (existing ones are preserved if not overridden). */\n  setCallbacks(callbacks: Partial<AGPClientCallbacks>): void {\n    this.callbacks = { ...this.callbacks, ...callbacks };\n  }\n\n  // -----------------------------------------------------------------------\n  // Send methods (uplink: client -> server)\n  // -----------------------------------------------------------------------\n\n  /**\n   * Send a streaming text chunk (session.update, update_type=message_chunk).\n   *\n   * Call this repeatedly as your agent generates text.  Each call sends\n   * only the *new* incremental text, not the full accumulated response.\n   */\n  sendMessageChunk(\n    sessionId: string,\n    promptId: string,\n    text: string,\n    guid?: string,\n    userId?: string,\n  ): void {\n    const content: ContentBlock = { type: \"text\", text };\n    const payload: UpdatePayload = {\n      session_id: sessionId,\n      prompt_id: promptId,\n      update_type: \"message_chunk\",\n      content,\n    };\n    this.sendEnvelope(\"session.update\", payload, guid, userId);\n  }\n\n  /**\n   * Notify that a tool call has started (session.update, update_type=tool_call).\n   */\n  sendToolCall(\n    sessionId: string,\n    promptId: string,\n    toolCall: ToolCall,\n    guid?: string,\n    userId?: string,\n  ): void {\n    const payload: UpdatePayload = {\n      session_id: sessionId,\n      prompt_id: promptId,\n      update_type: \"tool_call\",\n      tool_call: toolCall,\n    };\n    this.sendEnvelope(\"session.update\", payload, guid, userId);\n  }\n\n  /**\n   * Update a tool call's status (session.update, update_type=tool_call_update).\n   */\n  sendToolCallUpdate(\n    sessionId: string,\n    promptId: string,\n    toolCall: ToolCall,\n    guid?: string,\n    userId?: string,\n  ): void {\n    const payload: UpdatePayload = {\n      session_id: sessionId,\n      prompt_id: promptId,\n      update_type: \"tool_call_update\",\n      tool_call: toolCall,\n    };\n    this.sendEnvelope(\"session.update\", payload, guid, userId);\n  }\n\n  /**\n   * Send the final response for a turn (session.promptResponse).\n   *\n   * This MUST be sent for every prompt -- even on cancellation or error.\n   * The server will not send a new prompt until it receives this.\n   */\n  sendPromptResponse(\n    payload: PromptResponsePayload,\n    guid?: string,\n    userId?: string,\n  ): void {\n    this.sendEnvelope(\"session.promptResponse\", payload, guid, userId);\n  }\n\n  /**\n   * Convenience: send a successful end-turn response with text content.\n   */\n  sendTextResponse(\n    sessionId: string,\n    promptId: string,\n    text: string,\n    guid?: string,\n    userId?: string,\n  ): void {\n    this.sendPromptResponse(\n      {\n        session_id: sessionId,\n        prompt_id: promptId,\n        stop_reason: \"end_turn\",\n        content: [{ type: \"text\", text }],\n      },\n      guid,\n      userId,\n    );\n  }\n\n  /**\n   * Convenience: send an error response for a turn.\n   */\n  sendErrorResponse(\n    sessionId: string,\n    promptId: string,\n    errorMessage: string,\n    guid?: string,\n    userId?: string,\n  ): void {\n    this.sendPromptResponse(\n      {\n        session_id: sessionId,\n        prompt_id: promptId,\n        stop_reason: \"error\",\n        error: errorMessage,\n      },\n      guid,\n      userId,\n    );\n  }\n\n  /**\n   * Convenience: send a cancellation acknowledgement for a turn.\n   */\n  sendCancelledResponse(\n    sessionId: string,\n    promptId: string,\n    guid?: string,\n    userId?: string,\n  ): void {\n    this.sendPromptResponse(\n      {\n        session_id: sessionId,\n        prompt_id: promptId,\n        stop_reason: \"cancelled\",\n      },\n      guid,\n      userId,\n    );\n  }\n\n  // -----------------------------------------------------------------------\n  // Connection management (private)\n  // -----------------------------------------------------------------------\n\n  private connect(): void {\n    if (!this.url) {\n      this.state = \"disconnected\";\n      this.callbacks.onError?.(new Error(\"AGPClient: url is empty\"));\n      return;\n    }\n    if (!this.token) {\n      this.state = \"disconnected\";\n      this.callbacks.onError?.(new Error(\"AGPClient: token is empty\"));\n      return;\n    }\n\n    this.state = \"connecting\";\n    const wsUrl = this.buildConnectionUrl();\n\n    try {\n      this.ws = new WebSocket(wsUrl);\n      this.setupEventHandlers();\n    } catch (error) {\n      this.handleConnectionError(\n        error instanceof Error ? error : new Error(String(error)),\n      );\n    }\n  }\n\n  private buildConnectionUrl(): string {\n    const url = new URL(this.url);\n    url.searchParams.set(\"token\", this.token);\n    return url.toString();\n  }\n\n  private setupEventHandlers(): void {\n    if (!this.ws) return;\n    this.ws.on(\"open\", this.handleOpen);\n    this.ws.on(\"message\", this.handleRawMessage);\n    this.ws.on(\"close\", this.handleClose);\n    this.ws.on(\"error\", this.handleError);\n    this.ws.on(\"pong\", this.handlePong);\n  }\n\n  // -----------------------------------------------------------------------\n  // Event handlers\n  // -----------------------------------------------------------------------\n\n  private handleOpen = (): void => {\n    this.state = \"connected\";\n    this.reconnectAttempts = 0;\n    this.lastPongTime = Date.now();\n    this.startHeartbeat();\n    this.startWakeupDetection();\n    this.callbacks.onConnected?.();\n  };\n\n  private handleRawMessage = (data: WebSocket.RawData): void => {\n    try {\n      const raw = typeof data === \"string\" ? data : data.toString();\n      const envelope = JSON.parse(raw) as AGPEnvelope;\n\n      // Dedup by msg_id\n      if (this.processedMsgIds.has(envelope.msg_id)) {\n        return;\n      }\n      this.processedMsgIds.add(envelope.msg_id);\n\n      // Dispatch by method\n      switch (envelope.method) {\n        case \"session.prompt\":\n          this.callbacks.onPrompt?.(envelope as PromptMessage);\n          break;\n        case \"session.cancel\":\n          this.callbacks.onCancel?.(envelope as CancelMessage);\n          break;\n        case \"ping\":\n          // Application-level ping -- no action needed, ws-level pong handles keepalive\n          break;\n        default:\n          // Unknown method -- silently ignore\n          break;\n      }\n    } catch (error) {\n      this.callbacks.onError?.(\n        error instanceof Error\n          ? error\n          : new Error(`Message parse failed: ${String(error)}`),\n      );\n    }\n  };\n\n  private handleClose = (code: number, reason: Buffer): void => {\n    const reasonStr = reason.toString() || `code=${code}`;\n    this.clearHeartbeat();\n    this.clearWakeupDetection();\n    this.ws = null;\n\n    if (this.state !== \"disconnected\") {\n      this.callbacks.onDisconnected?.(reasonStr);\n      this.scheduleReconnect();\n    }\n  };\n\n  private handlePong = (): void => {\n    this.lastPongTime = Date.now();\n  };\n\n  private handleError = (error: Error): void => {\n    this.callbacks.onError?.(error);\n  };\n\n  private handleConnectionError(error: Error): void {\n    this.callbacks.onError?.(error);\n    this.scheduleReconnect();\n  }\n\n  // -----------------------------------------------------------------------\n  // Reconnect (exponential backoff)\n  // -----------------------------------------------------------------------\n\n  private scheduleReconnect(): void {\n    if (\n      this.maxReconnectAttempts > 0 &&\n      this.reconnectAttempts >= this.maxReconnectAttempts\n    ) {\n      this.state = \"disconnected\";\n      this.callbacks.onDisconnected?.(\"max reconnect attempts reached\");\n      return;\n    }\n\n    this.state = \"reconnecting\";\n    this.reconnectAttempts++;\n\n    const delay = Math.min(\n      this.reconnectInterval *\n        Math.pow(BACKOFF_MULTIPLIER, this.reconnectAttempts - 1),\n      MAX_RECONNECT_DELAY,\n    );\n\n    this.reconnectTimer = setTimeout(() => {\n      this.reconnectTimer = null;\n      this.connect();\n    }, delay);\n  }\n\n  private clearReconnectTimer(): void {\n    if (this.reconnectTimer) {\n      clearTimeout(this.reconnectTimer);\n      this.reconnectTimer = null;\n    }\n  }\n\n  // -----------------------------------------------------------------------\n  // Heartbeat (ws native ping/pong)\n  // -----------------------------------------------------------------------\n\n  private startHeartbeat(): void {\n    this.clearHeartbeat();\n    this.heartbeatTimer = setInterval(() => {\n      if (this.ws && this.state === \"connected\") {\n        // Pong timeout: if no pong received within 2x heartbeat interval,\n        // the connection is considered dead (e.g. after system sleep).\n        const pongTimeout = this.heartbeatInterval * 2;\n        if (Date.now() - this.lastPongTime > pongTimeout) {\n          this.ws.terminate();\n          return;\n        }\n\n        try {\n          this.ws.ping();\n        } catch {\n          this.ws?.terminate();\n        }\n      }\n    }, this.heartbeatInterval);\n  }\n\n  private clearHeartbeat(): void {\n    if (this.heartbeatTimer) {\n      clearInterval(this.heartbeatTimer);\n      this.heartbeatTimer = null;\n    }\n  }\n\n  // -----------------------------------------------------------------------\n  // System wakeup detection\n  // -----------------------------------------------------------------------\n\n  private startWakeupDetection(): void {\n    this.clearWakeupDetection();\n    this.lastTickTime = Date.now();\n\n    this.wakeupCheckTimer = setInterval(() => {\n      const now = Date.now();\n      const elapsed = now - this.lastTickTime;\n      this.lastTickTime = now;\n\n      if (elapsed > WAKEUP_THRESHOLD) {\n        // Timer drift detected -- system likely slept.\n        // Reset reconnect counter and force a reconnect.\n        this.reconnectAttempts = 0;\n        if (this.ws && this.state === \"connected\") {\n          this.ws.terminate();\n        }\n      }\n    }, WAKEUP_CHECK_INTERVAL);\n  }\n\n  private clearWakeupDetection(): void {\n    if (this.wakeupCheckTimer) {\n      clearInterval(this.wakeupCheckTimer);\n      this.wakeupCheckTimer = null;\n    }\n  }\n\n  // -----------------------------------------------------------------------\n  // Message sending (internal)\n  // -----------------------------------------------------------------------\n\n  private sendEnvelope<T>(\n    method: AGPMethod,\n    payload: T,\n    guid?: string,\n    userId?: string,\n  ): void {\n    if (!this.ws || this.state !== \"connected\") {\n      this.callbacks.onError?.(\n        new Error(\n          `Cannot send message: not connected (state=${this.state})`,\n        ),\n      );\n      return;\n    }\n\n    const envelope: AGPEnvelope<T> = {\n      msg_id: randomUUID(),\n      guid: guid ?? this.guid,\n      user_id: userId ?? this.userId,\n      method,\n      payload,\n    };\n\n    try {\n      this.ws.send(JSON.stringify(envelope));\n    } catch (error) {\n      this.callbacks.onError?.(\n        error instanceof Error\n          ? error\n          : new Error(`Send failed: ${String(error)}`),\n      );\n    }\n  }\n\n  // -----------------------------------------------------------------------\n  // Message ID cache cleanup\n  // -----------------------------------------------------------------------\n\n  private startMsgIdCleanup(): void {\n    this.clearMsgIdCleanup();\n    this.msgIdCleanupTimer = setInterval(() => {\n      if (this.processedMsgIds.size > MAX_MSG_ID_CACHE) {\n        // Keep the newest half (Set iterates in insertion order)\n        const entries = [...this.processedMsgIds];\n        this.processedMsgIds.clear();\n        for (const id of entries.slice(-Math.floor(MAX_MSG_ID_CACHE / 2))) {\n          this.processedMsgIds.add(id);\n        }\n      }\n    }, MSG_ID_CLEANUP_INTERVAL);\n  }\n\n  private clearMsgIdCleanup(): void {\n    if (this.msgIdCleanupTimer) {\n      clearInterval(this.msgIdCleanupTimer);\n      this.msgIdCleanupTimer = null;\n    }\n  }\n}\n","/**\n * QClaw WeChat Access API Client\n *\n * Reverse-engineered from QClaw.app (Electron, asar unencrypted).\n * Implements the full jprx gateway protocol used by the renderer's\n * `openclawApiService` class (tS) found in platform-QEsQ5tXh.js.\n *\n * Usage:\n *   const client = new QClawClient({ env: \"production\" });\n *   const state  = await client.getWxLoginState({ guid: \"...\" });\n *   const login  = await client.wxLogin({ guid: \"...\", code: \"...\", state: \"...\" });\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type Environment = \"production\" | \"test\";\n\nexport interface EnvUrls {\n  jprxGateway: string;\n  wxLoginRedirectUri: string;\n  beaconUrl: string;\n  qclawBaseUrl: string;\n  wechatWsUrl: string;\n}\n\nexport interface WxLoginConfig {\n  appid: string;\n  redirect_uri: string;\n  wxLoginStyleBase64: string;\n}\n\nexport interface ClientOptions {\n  /** \"production\" (default) or \"test\" */\n  env?: Environment;\n  /** Persisted JWT from a previous session */\n  jwtToken?: string;\n  /** User info restored from a previous session */\n  userInfo?: UserInfo | null;\n  /** Override the web_version sent in every request body (default \"1.4.0\") */\n  webVersion?: string;\n}\n\nexport interface UserInfo {\n  nickname: string;\n  avatar: string;\n  guid: string;\n  userId: string;\n  loginKey?: string;\n  [key: string]: unknown;\n}\n\nexport interface ApiResponse<T = unknown> {\n  success: boolean;\n  code?: number;\n  message: string;\n  data: T | null;\n}\n\nexport interface WxLoginStateData {\n  state: string;\n  [key: string]: unknown;\n}\n\nexport interface WxLoginUserInfo {\n  nickname?: string;\n  avatar?: string;\n  avatar_url?: string;\n  user_id?: string;\n  [key: string]: unknown;\n}\n\nexport interface WxLoginData {\n  token: string;\n  openclaw_channel_token: string;\n  user_info?: WxLoginUserInfo;\n  /** @deprecated Flat fields may not exist; use user_info instead */\n  nickname?: string;\n  avatar?: string;\n  userId?: string;\n  guid?: string;\n  loginKey?: string;\n  [key: string]: unknown;\n}\n\nexport interface UserInfoData {\n  nickname: string;\n  avatar?: string;\n  head_img_url?: string;\n  head_img?: string;\n  nick_name?: string;\n  guid: string;\n  userId?: string;\n  user_id?: string;\n  [key: string]: unknown;\n}\n\nexport interface ApiKeyData {\n  key: string;\n  [key: string]: unknown;\n}\n\nexport interface InviteCodeStatus {\n  verified: boolean;\n  [key: string]: unknown;\n}\n\nexport interface ChannelTokenData {\n  openclaw_channel_token: string;\n  [key: string]: unknown;\n}\n\nexport interface UpdateInfo {\n  update_strategy: number;\n  download_url?: string;\n  version?: string;\n  release_notes?: string;\n  [key: string]: unknown;\n}\n\nexport interface DeviceInfo {\n  [key: string]: unknown;\n}\n\nexport interface ContactLinkData {\n  [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst ENV_URLS: Record<Environment, EnvUrls> = {\n  test: {\n    jprxGateway: \"https://jprx.sparta.html5.qq.com/\",\n    wxLoginRedirectUri: \"https://security-test.guanjia.qq.com/login\",\n    beaconUrl: \"https://pcmgrmonitor.3g.qq.com/test/datareport\",\n    qclawBaseUrl: \"https://jprx.sparta.html5.qq.com/aizone/v1\",\n    wechatWsUrl: \"wss://jprx.sparta.html5.qq.com/agentwss\",\n  },\n  production: {\n    jprxGateway: \"https://jprx.m.qq.com/\",\n    wxLoginRedirectUri: \"https://security.guanjia.qq.com/login\",\n    beaconUrl: \"https://pcmgrmonitor.3g.qq.com/datareport\",\n    qclawBaseUrl: \"https://mmgrcalltoken.3g.qq.com/aizone/v1\",\n    wechatWsUrl: \"wss://mmgrcalltoken.3g.qq.com/agentwss\",\n  },\n};\n\nconst WX_LOGIN_CONFIG: Record<Environment, WxLoginConfig> = {\n  production: {\n    appid: \"wx9d11056dd75b7240\",\n    redirect_uri: \"https://security.guanjia.qq.com/login\",\n    wxLoginStyleBase64: \"\", // base64 CSS, omitted for brevity\n  },\n  test: {\n    appid: \"wx3dd49afb7e2cf957\",\n    redirect_uri: \"https://security-test.guanjia.qq.com/login\",\n    wxLoginStyleBase64: \"\",\n  },\n};\n\n/** Fallback X-Token when no user is logged in */\nconst DEFAULT_LOGIN_KEY = \"m83qdao0AmE5\";\n\nconst WEB_VERSION = \"1.4.0\";\nconst WEB_ENV = \"release\";\n\n/**\n * API endpoint mapping.\n * Every call is a POST to `${jprxGateway}data/<id>/forward`.\n */\nconst Endpoint = {\n  GENERATE_CONTACT_LINK: \"data/4018/forward\",\n  QUERY_DEVICE_BY_GUID: \"data/4019/forward\",\n  DISCONNECT_DEVICE: \"data/4020/forward\",\n  WX_LOGIN: \"data/4026/forward\",\n  GET_USER_INFO: \"data/4027/forward\",\n  WX_LOGOUT: \"data/4028/forward\",\n  GET_WX_LOGIN_STATE: \"data/4050/forward\",\n  CREATE_API_KEY: \"data/4055/forward\",\n  CHECK_INVITE_CODE: \"data/4056/forward\",\n  SUBMIT_INVITE_CODE: \"data/4057/forward\",\n  REFRESH_CHANNEL_TOKEN: \"data/4058/forward\",\n  CHECK_UPDATE: \"data/4066/forward\",\n} as const;\n\n// ---------------------------------------------------------------------------\n// Client\n// ---------------------------------------------------------------------------\n\nexport class QClawClient {\n  private env: Environment;\n  private urls: EnvUrls;\n  private jwtToken: string | null;\n  private userInfo: UserInfo | null;\n  private webVersion: string;\n\n  constructor(opts: ClientOptions = {}) {\n    this.env = opts.env ?? \"production\";\n    this.urls = ENV_URLS[this.env];\n    this.jwtToken = opts.jwtToken ?? null;\n    this.userInfo = opts.userInfo ?? null;\n    this.webVersion = opts.webVersion ?? WEB_VERSION;\n  }\n\n  // -----------------------------------------------------------------------\n  // Accessors\n  // -----------------------------------------------------------------------\n\n  get envUrls(): Readonly<EnvUrls> {\n    return this.urls;\n  }\n\n  get wxLoginConfig(): Readonly<WxLoginConfig> {\n    return WX_LOGIN_CONFIG[this.env];\n  }\n\n  get currentUser(): Readonly<UserInfo> | null {\n    return this.userInfo;\n  }\n\n  get token(): string | null {\n    return this.jwtToken;\n  }\n\n  /** Returns the login key (X-Token header), falls back to the hardcoded default. */\n  private get loginKey(): string {\n    return this.userInfo?.loginKey ?? DEFAULT_LOGIN_KEY;\n  }\n\n  // -----------------------------------------------------------------------\n  // Low-level transport\n  // -----------------------------------------------------------------------\n\n  /**\n   * Build the full URL for a gateway endpoint.\n   */\n  private buildUrl(endpoint: string): string {\n    return `${this.urls.jprxGateway}${endpoint}`;\n  }\n\n  /**\n   * Build the standard request headers expected by the jprx gateway.\n   */\n  private buildHeaders(): Record<string, string> {\n    const headers: Record<string, string> = {\n      \"Content-Type\": \"application/json\",\n      \"X-Version\": \"1\",\n      \"X-Token\": this.loginKey,\n      \"X-Guid\": this.userInfo?.guid ?? \"1\",\n      \"X-Account\": this.userInfo?.userId ?? \"1\",\n      \"X-Session\": \"\",\n    };\n    if (this.jwtToken) {\n      headers[\"X-OpenClaw-Token\"] = this.jwtToken;\n    }\n    return headers;\n  }\n\n  /**\n   * Core request method.  Every API call is a POST with JSON body that\n   * includes `web_version` and `web_env` alongside caller-supplied params.\n   *\n   * Handles:\n   *  - Automatic JWT renewal via `X-New-Token` response header\n   *  - Session expiration (code 21004) → clears local auth state\n   *  - Nested Tencent response envelope unwrapping\n   */\n  private async request<T = unknown>(\n    endpoint: string,\n    body: Record<string, unknown> = {},\n  ): Promise<ApiResponse<T>> {\n    const url = this.buildUrl(endpoint);\n    const headers = this.buildHeaders();\n    const payload = {\n      ...body,\n      web_version: this.webVersion,\n      web_env: WEB_ENV,\n    };\n\n    let res: Response;\n    try {\n      res = await fetch(url, {\n        method: \"POST\",\n        headers,\n        body: JSON.stringify(payload),\n        redirect: \"follow\",\n      });\n    } catch (err) {\n      return {\n        success: false,\n        code: undefined,\n        message: `Network request failed: ${String(err)}`,\n        data: null,\n      };\n    }\n\n    // ---------- token auto-renewal ----------\n    const newToken = res.headers.get(\"X-New-Token\");\n    if (newToken) {\n      this.jwtToken = newToken;\n    }\n\n    // ---------- parse response body ----------\n    let parsed: any = null;\n    try {\n      const text = await res.text();\n      parsed = text ? JSON.parse(text) : null;\n    } catch {\n      // non-JSON response\n    }\n\n    // ---------- session expired (code 21004) ----------\n    const commonCode =\n      parsed?.data?.resp?.common?.code ??\n      parsed?.data?.common?.code ??\n      parsed?.resp?.common?.code ??\n      parsed?.common?.code;\n\n    if (commonCode === 21004) {\n      this.jwtToken = null;\n      this.userInfo = null;\n      return {\n        success: false,\n        code: 21004,\n        message: \"Session expired, please re-login\",\n        data: null,\n      };\n    }\n\n    // ---------- HTTP-level error ----------\n    if (!res.ok) {\n      return {\n        success: false,\n        code: res.status,\n        message: parsed?.message ?? res.statusText ?? `HTTP ${res.status}`,\n        data: parsed as T,\n      };\n    }\n\n    // ---------- business-level success check ----------\n    const ret = parsed?.ret;\n    const bizCode =\n      parsed?.data?.common?.code ??\n      parsed?.data?.resp?.common?.code ??\n      parsed?.resp?.common?.code ??\n      parsed?.common?.code;\n\n    if (ret !== undefined && ret !== 0) {\n      return {\n        success: false,\n        code: ret,\n        message: parsed?.message ?? \"Business request failed\",\n        data: (parsed?.data?.resp ?? parsed?.data ?? parsed) as T,\n      };\n    }\n\n    if (bizCode !== undefined && bizCode !== 0) {\n      return {\n        success: false,\n        code: bizCode,\n        message: parsed?.message ?? \"Business request failed\",\n        data: (parsed?.data?.resp ?? parsed?.data ?? parsed) as T,\n      };\n    }\n\n    return {\n      success: true,\n      code: 0,\n      message: \"ok\",\n      data: parsed as T,\n    };\n  }\n\n  /**\n   * Unwrap the deeply-nested Tencent response envelope.\n   *\n   * Real responses have varying nesting depths, observed patterns:\n   *   { ret, data: { resp: { common, data: PAYLOAD } } }     ← getWxLoginState\n   *   { ret, resp: { common, data: PAYLOAD } }                ← some endpoints\n   *   { data: PAYLOAD }                                       ← simple responses\n   *\n   * This method walks the known wrapper keys until it finds the innermost\n   * `data` that doesn't itself contain another `resp` or `data` wrapper.\n   */\n  private static unwrapData<T>(apiRes: ApiResponse<any>): T | null {\n    if (!apiRes.success || !apiRes.data) return null;\n    let d = apiRes.data as any;\n\n    // Walk through up to 4 levels of { data } / { resp } nesting\n    for (let i = 0; i < 4; i++) {\n      if (d?.resp?.data !== undefined) {\n        d = d.resp.data;\n      } else if (d?.data !== undefined && typeof d.data === \"object\") {\n        d = d.data;\n      } else {\n        break;\n      }\n    }\n    return d as T;\n  }\n\n  // -----------------------------------------------------------------------\n  // WeChat OAuth helpers\n  // -----------------------------------------------------------------------\n\n  /**\n   * Get the WeChat OAuth QR-code login URL (for embedding in a webview / iframe).\n   * The official SDK at https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js\n   * normally renders this, but you can also construct it manually.\n   */\n  buildWxLoginUrl(state: string): string {\n    const cfg = this.wxLoginConfig;\n    const params = new URLSearchParams({\n      appid: cfg.appid,\n      redirect_uri: cfg.redirect_uri,\n      response_type: \"code\",\n      scope: \"snsapi_login\",\n      state,\n    });\n    return `https://open.weixin.qq.com/connect/qrconnect?${params.toString()}#wechat_redirect`;\n  }\n\n  // -----------------------------------------------------------------------\n  // Public API methods\n  // -----------------------------------------------------------------------\n\n  /**\n   * Step 1 of login: obtain a CSRF `state` parameter for the QR login flow.\n   * Endpoint: data/4050/forward\n   */\n  async getWxLoginState(params: {\n    guid: string;\n  }): Promise<ApiResponse<WxLoginStateData>> {\n    return this.request<WxLoginStateData>(\n      Endpoint.GET_WX_LOGIN_STATE,\n      params,\n    );\n  }\n\n  /**\n   * Step 2 of login: exchange the WeChat authorization `code` for a session.\n   * Returns a JWT (`token`) and an `openclaw_channel_token`.\n   * Endpoint: data/4026/forward\n   */\n  async wxLogin(params: {\n    guid: string;\n    code: string;\n    state: string;\n  }): Promise<ApiResponse<WxLoginData>> {\n    const res = await this.request<WxLoginData>(Endpoint.WX_LOGIN, params);\n    if (res.success) {\n      const d = QClawClient.unwrapData<WxLoginData>(res);\n      if (d?.token) this.jwtToken = d.token;\n      if (d) {\n        // user_info is nested: { user_info: { nickname, avatar_url, user_id } }\n        // Mirrors the QClaw app's extraction at WXLoginView-Dzks_Y2M.js\n        const ui = d.user_info;\n        this.userInfo = {\n          nickname: ui?.nickname ?? d.nickname ?? \"\",\n          avatar: ui?.avatar_url ?? ui?.avatar ?? d.avatar ?? \"\",\n          guid: d.guid ?? params.guid,\n          userId: ui?.user_id ?? d.userId ?? \"\",\n          loginKey: d.loginKey,\n        };\n      }\n    }\n    return res;\n  }\n\n  /**\n   * Fetch the currently logged-in user's profile.\n   * Endpoint: data/4027/forward\n   */\n  async getUserInfo(params: {\n    guid: string;\n  }): Promise<ApiResponse<UserInfoData>> {\n    return this.request<UserInfoData>(Endpoint.GET_USER_INFO, params);\n  }\n\n  /**\n   * Log out and invalidate the current session.\n   * Endpoint: data/4028/forward\n   */\n  async wxLogout(params: { guid: string }): Promise<ApiResponse> {\n    const res = await this.request(Endpoint.WX_LOGOUT, params);\n    // Clear local state regardless of server response\n    this.jwtToken = null;\n    this.userInfo = null;\n    return res;\n  }\n\n  /**\n   * Create an API key for the qclaw model provider.\n   * Endpoint: data/4055/forward\n   */\n  async createApiKey(): Promise<ApiResponse<ApiKeyData>> {\n    return this.request<ApiKeyData>(Endpoint.CREATE_API_KEY, {});\n  }\n\n  /**\n   * Check whether the current user has verified an invite code.\n   * Endpoint: data/4056/forward\n   */\n  async checkInviteCode(params: {\n    guid: string;\n  }): Promise<ApiResponse<InviteCodeStatus>> {\n    return this.request<InviteCodeStatus>(Endpoint.CHECK_INVITE_CODE, params);\n  }\n\n  /**\n   * Submit an invite code for verification.\n   * Endpoint: data/4057/forward\n   */\n  async submitInviteCode(params: {\n    guid: string;\n    invite_code: string;\n  }): Promise<ApiResponse> {\n    return this.request(Endpoint.SUBMIT_INVITE_CODE, params);\n  }\n\n  /**\n   * Refresh the `openclaw_channel_token` used by the wechat-access channel.\n   * Endpoint: data/4058/forward\n   */\n  async refreshChannelToken(): Promise<string | null> {\n    const res = await this.request<ChannelTokenData>(\n      Endpoint.REFRESH_CHANNEL_TOKEN,\n      {},\n    );\n    if (!res.success) return null;\n    const d = QClawClient.unwrapData<ChannelTokenData>(res);\n    return d?.openclaw_channel_token ?? null;\n  }\n\n  /**\n   * Check for app updates.\n   * Endpoint: data/4066/forward\n   */\n  async checkUpdate(\n    currentVersion = \"\",\n    systemType = \"mac\",\n  ): Promise<ApiResponse<UpdateInfo>> {\n    return this.request<UpdateInfo>(Endpoint.CHECK_UPDATE, {\n      last_update_time: 0,\n      current_version: currentVersion,\n      system_type: systemType,\n    });\n  }\n\n  /**\n   * Generate a contact link (专属链接).\n   * Endpoint: data/4018/forward\n   */\n  async generateContactLink(\n    params: Record<string, unknown>,\n  ): Promise<ApiResponse<ContactLinkData>> {\n    return this.request<ContactLinkData>(\n      Endpoint.GENERATE_CONTACT_LINK,\n      params,\n    );\n  }\n\n  /**\n   * Query device status by GUID.\n   * Endpoint: data/4019/forward\n   */\n  async queryDeviceByGuid(\n    params: Record<string, unknown>,\n  ): Promise<ApiResponse<DeviceInfo>> {\n    return this.request<DeviceInfo>(Endpoint.QUERY_DEVICE_BY_GUID, params);\n  }\n\n  /**\n   * Disconnect a device.\n   * Endpoint: data/4020/forward\n   */\n  async disconnectDevice(\n    params: Record<string, unknown>,\n  ): Promise<ApiResponse> {\n    return this.request(Endpoint.DISCONNECT_DEVICE, params);\n  }\n\n  // -----------------------------------------------------------------------\n  // OpenClaw config helpers\n  // -----------------------------------------------------------------------\n\n  /**\n   * Build the config patch object that the Electron app writes via IPC\n   * after a successful login.  This is what goes into the OpenClaw\n   * gateway's YAML/JSON config file.\n   */\n  buildConfigPatch(\n    channelToken: string | null,\n    apiKey: string | null,\n  ): Record<string, unknown> {\n    const patch: Record<string, unknown> = {};\n    if (channelToken) {\n      patch.channels = { \"wechat-access\": { token: channelToken } };\n    }\n    if (apiKey) {\n      patch.models = { providers: { qclaw: { apiKey } } };\n    }\n    return patch;\n  }\n\n  /**\n   * Convenience: run the full post-login config update sequence\n   * (create API key + build config patch).\n   */\n  async buildPostLoginConfig(\n    channelToken: string,\n  ): Promise<Record<string, unknown>> {\n    let apiKey: string | null = null;\n    try {\n      const res = await this.createApiKey();\n      const d = QClawClient.unwrapData<ApiKeyData>(res);\n      apiKey = d?.key ?? null;\n    } catch {\n      // non-fatal\n    }\n    return this.buildConfigPatch(channelToken, apiKey);\n  }\n\n  // -----------------------------------------------------------------------\n  // Static helpers\n  // -----------------------------------------------------------------------\n\n  /** Get environment URLs without instantiating a client. */\n  static getEnvUrls(env: Environment): Readonly<EnvUrls> {\n    return ENV_URLS[env];\n  }\n\n  /** Get WeChat login config without instantiating a client. */\n  static getWxLoginConfig(env: Environment): Readonly<WxLoginConfig> {\n    return WX_LOGIN_CONFIG[env];\n  }\n\n  /** All known endpoint paths. */\n  static readonly Endpoints = Endpoint;\n\n  /** Unwrap a Tencent-style nested response. */\n  static unwrap<T>(res: ApiResponse<any>): T | null {\n    return QClawClient.unwrapData<T>(res);\n  }\n}\n\n// ---------------------------------------------------------------------------\n// AGP WebSocket re-exports\n// ---------------------------------------------------------------------------\n\nexport { AGPClient } from \"./agp-client.js\";\nexport type {\n  AGPEnvelope,\n  AGPMethod,\n  ContentBlock,\n  ToolCallStatus,\n  ToolCallKind,\n  ToolLocation,\n  ToolCall,\n  PromptPayload,\n  CancelPayload,\n  UpdateType,\n  UpdatePayload,\n  StopReason,\n  PromptResponsePayload,\n  PromptMessage,\n  CancelMessage,\n  UpdateMessage,\n  PromptResponseMessage,\n  ConnectionState,\n  AGPClientConfig,\n  AGPClientCallbacks,\n} from \"./agp-types.js\";\n\n// ---------------------------------------------------------------------------\n// Default export\n// ---------------------------------------------------------------------------\n\nexport default QClawClient;\n"],"mappings":"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"}