* 轮询获取访问令牌 * @param {string} device_code - 设备代码 * @param {string} code_verifier - 代码验证器 * @param {Object} [account] - Qwen 账户对象(用于解析账号级代理) * @returns {Promise } 访问令牌信息
(device_code, code_verifier, account)
| 179 | * @returns {Promise<Object>} 访问令牌信息 |
| 180 | */ |
| 181 | async pollForToken(device_code, code_verifier, account) { |
| 182 | let pollInterval = 5000 |
| 183 | const maxAttempts = 3 |
| 184 | const chatBaseUrl = getChatBaseUrl() |
| 185 | |
| 186 | for (let attempt = 0; attempt < maxAttempts; attempt++) { |
| 187 | const bodyData = new URLSearchParams({ |
| 188 | grant_type: "urn:ietf:params:oauth:grant-type:device_code", |
| 189 | client_id: "f0304373b74a44d2b584a3fb70ca9e56", |
| 190 | device_code: device_code, |
| 191 | code_verifier: code_verifier, |
| 192 | }) |
| 193 | |
| 194 | const fetchOptions = { |
| 195 | method: 'POST', |
| 196 | headers: { |
| 197 | 'Content-Type': 'application/x-www-form-urlencoded', |
| 198 | Accept: 'application/json', |
| 199 | }, |
| 200 | body: bodyData, |
| 201 | } |
| 202 | |
| 203 | applyProxyToFetchOptions(fetchOptions, account) |
| 204 | |
| 205 | try { |
| 206 | const response = await fetch(`${chatBaseUrl}/api/v1/oauth2/token`, fetchOptions) |
| 207 | |
| 208 | if (response.ok) { |
| 209 | const tokenData = await response.json() |
| 210 | |
| 211 | // 转换为凭据格式 |
| 212 | const credentials = { |
| 213 | access_token: tokenData.access_token, |
| 214 | refresh_token: tokenData.refresh_token || undefined, |
| 215 | expiry_date: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1000 : undefined, |
| 216 | } |
| 217 | |
| 218 | if (!credentials.access_token || !credentials.refresh_token || !credentials.expiry_date) { |
| 219 | logger.error('CLI轮询令牌成功但返回数据不完整', 'CLI', '', tokenData) |
| 220 | } |
| 221 | |
| 222 | return credentials |
| 223 | } |
| 224 | |
| 225 | const responseBody = await this.readResponseBody(response) |
| 226 | logger.warn(`CLI轮询令牌未完成 (${attempt + 1}/${maxAttempts})`, 'CLI', '', { |
| 227 | status: response.status, |
| 228 | statusText: response.statusText, |
| 229 | body: responseBody |
| 230 | }) |
| 231 | |
| 232 | // 等待5秒, 然后继续轮询 |
| 233 | await new Promise(resolve => setTimeout(resolve, pollInterval)) |
| 234 | } catch (error) { |
| 235 | // 等待5秒, 然后继续轮询 |
| 236 | await new Promise(resolve => setTimeout(resolve, pollInterval)) |
| 237 | logger.error(`CLI轮询令牌异常 (${attempt + 1}/${maxAttempts})`, 'CLI', '', { |
| 238 | url: `${chatBaseUrl}/api/v1/oauth2/token`, |
no test coverage detected