feat: 新增Droid cli支持

This commit is contained in:
shaw
2025-10-09 23:05:09 +08:00
parent 4de2ea3d17
commit 2fc84a6aca
13 changed files with 2734 additions and 36 deletions

View File

@@ -17,8 +17,18 @@ function maskToken(token, visiblePercent = 70) {
const { length } = token
// 对于非常短的 token至少隐藏一部分
if (length <= 2) {
return '*'.repeat(length)
}
if (length <= 5) {
return token.slice(0, 1) + '*'.repeat(length - 1)
}
if (length <= 10) {
return token.slice(0, 5) + '*'.repeat(length - 5)
const visibleLength = Math.min(5, length - 2)
const front = token.slice(0, visibleLength)
return front + '*'.repeat(length - visibleLength)
}
// 计算可见字符数量

View File

@@ -0,0 +1,170 @@
const axios = require('axios')
const config = require('../../config/config')
const logger = require('./logger')
const ProxyHelper = require('./proxyHelper')
const WORKOS_CONFIG = config.droid || {}
const WORKOS_DEVICE_AUTHORIZE_URL =
WORKOS_CONFIG.deviceAuthorizeUrl || 'https://api.workos.com/user_management/authorize/device'
const WORKOS_TOKEN_URL =
WORKOS_CONFIG.tokenUrl || 'https://api.workos.com/user_management/authenticate'
const WORKOS_CLIENT_ID = WORKOS_CONFIG.clientId || 'client_01HNM792M5G5G1A2THWPXKFMXB'
const DEFAULT_POLL_INTERVAL = 5
class WorkOSDeviceAuthError extends Error {
constructor(message, code, options = {}) {
super(message)
this.name = 'WorkOSDeviceAuthError'
this.code = code || 'unknown_error'
this.retryAfter = options.retryAfter || null
}
}
/**
* 启动设备码授权流程
* @param {object|null} proxyConfig - 代理配置
* @returns {Promise<object>} WorkOS 返回的数据
*/
async function startDeviceAuthorization(proxyConfig = null) {
const form = new URLSearchParams({
client_id: WORKOS_CLIENT_ID
})
const agent = ProxyHelper.createProxyAgent(proxyConfig)
try {
logger.info('🔐 请求 WorkOS 设备码授权', {
url: WORKOS_DEVICE_AUTHORIZE_URL,
hasProxy: !!agent
})
const response = await axios.post(WORKOS_DEVICE_AUTHORIZE_URL, form.toString(), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
httpsAgent: agent,
timeout: 15000
})
const data = response.data || {}
if (!data.device_code || !data.verification_uri) {
throw new Error('WorkOS 返回数据缺少必要字段 (device_code / verification_uri)')
}
logger.success('✅ 成功获取 WorkOS 设备码授权信息', {
verificationUri: data.verification_uri,
userCode: data.user_code
})
return {
deviceCode: data.device_code,
userCode: data.user_code,
verificationUri: data.verification_uri,
verificationUriComplete: data.verification_uri_complete || data.verification_uri,
expiresIn: data.expires_in || 300,
interval: data.interval || DEFAULT_POLL_INTERVAL
}
} catch (error) {
if (error.response) {
logger.error('❌ WorkOS 设备码授权失败', {
status: error.response.status,
data: error.response.data
})
throw new WorkOSDeviceAuthError(
error.response.data?.error_description ||
error.response.data?.error ||
'WorkOS 设备码授权失败',
error.response.data?.error
)
}
logger.error('❌ 请求 WorkOS 设备码授权异常', {
message: error.message
})
throw new WorkOSDeviceAuthError(error.message)
}
}
/**
* 轮询授权结果
* @param {string} deviceCode - 设备码
* @param {object|null} proxyConfig - 代理配置
* @returns {Promise<object>} WorkOS 返回的 token 数据
*/
async function pollDeviceAuthorization(deviceCode, proxyConfig = null) {
if (!deviceCode) {
throw new WorkOSDeviceAuthError('缺少设备码,无法查询授权结果', 'missing_device_code')
}
const form = new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
device_code: deviceCode,
client_id: WORKOS_CLIENT_ID
})
const agent = ProxyHelper.createProxyAgent(proxyConfig)
try {
const response = await axios.post(WORKOS_TOKEN_URL, form.toString(), {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
httpsAgent: agent,
timeout: 15000
})
const data = response.data || {}
if (!data.access_token) {
throw new WorkOSDeviceAuthError('WorkOS 返回结果缺少 access_token', 'missing_access_token')
}
logger.success('🤖 Droid 授权完成,获取到访问令牌', {
hasRefreshToken: !!data.refresh_token
})
return data
} catch (error) {
if (error.response) {
const responseData = error.response.data || {}
const errorCode = responseData.error || `http_${error.response.status}`
const errorDescription =
responseData.error_description || responseData.error || 'WorkOS 授权失败'
if (errorCode === 'authorization_pending' || errorCode === 'slow_down') {
const retryAfter =
Number(responseData.interval) ||
Number(error.response.headers?.['retry-after']) ||
DEFAULT_POLL_INTERVAL
throw new WorkOSDeviceAuthError(errorDescription, errorCode, {
retryAfter
})
}
if (errorCode === 'expired_token') {
throw new WorkOSDeviceAuthError(errorDescription, errorCode)
}
logger.error('❌ WorkOS 设备授权轮询失败', {
status: error.response.status,
data: responseData
})
throw new WorkOSDeviceAuthError(errorDescription, errorCode)
}
logger.error('❌ WorkOS 设备授权轮询异常', {
message: error.message
})
throw new WorkOSDeviceAuthError(error.message)
}
}
module.exports = {
startDeviceAuthorization,
pollDeviceAuthorization,
WorkOSDeviceAuthError
}