feat(qoder): 适配国际版每日签到 — UMID 设备风控身份 + 国际版活动 schema
核心发现(实测): - Qoder 国际版服务端只对带 Cosy-MachineToken/Code/Type 的请求下发签到活动, 否则 campaigns 返回空数组(国内版无此限制) - 国际版活动 actionType=VIEW_DETAILS(国内版 CLAIM_BENEFIT),claimStatus 一致 - 活动窗口 2026-09-01~10-31,每日 10:00 (UTC+8) 刷新,领取后 30 天有效 实现: - 新增 umid.py:从官方 npm 包 @qoder-ai/qodercli 下载并提取内嵌 UMID 二进制 (x86_64/arm64 ELF),试跑校验后缓存到 @appdata/AgentCheckin/umid/ - qoder.py:签到前自动确保风控身份,支持国际版/国内版双 schema 活动解析 - API:/api/umid/status、/api/umid/install、/api/accounts/refresh_risk - 前端:设置页风控身份面板(下载/状态)、账号列表「刷新风控」按钮 - 诊断:测连接返回 risk_identity / campaigns_debug(原始字段) 验证:NAS 实测 Qoder 国际版从「活动列表为空」变为「今日已领取(75 积分)」 测试:37 后端 + 25 E2E + 9 前端,全部通过
This commit is contained in:
+25
-3
@@ -216,7 +216,10 @@ def main():
|
||||
"print('exp:', qoder.jwt_exp(tok), 'uid:', qoder.jwt_uid(tok))\n"
|
||||
"# global detection\n"
|
||||
"print('global:', qoder.is_global({'domain': 'openapi.qoder.sh'}), qoder.is_global({'domain': 'openapi.qoder.com.cn'}))\n"
|
||||
"# campaign parsing: claimable -> claim flow mocked\n"
|
||||
"# 国际版 schema: actionType=VIEW_DETAILS + claimStatus\n"
|
||||
"intl = [{'actionType': 'VIEW_DETAILS', 'claimStatus': 'CLAIMABLE', 'campaignId': 'i1'}]\n"
|
||||
"print('intl_benefits:', len(qoder._benefits(intl)))\n"
|
||||
"# 国内版 schema\n"
|
||||
"campaigns = [{'actionType': 'CLAIM_BENEFIT', 'claimStatus': 'CLAIMABLE', 'campaignId': 'c1', 'benefit': {'amount': 100}}]\n"
|
||||
"b = qoder._benefits(campaigns)\n"
|
||||
"print('benefits:', len(b), 'claimable:', len([c for c in b if c.get('claimStatus') == 'CLAIMABLE']))\n"
|
||||
@@ -230,8 +233,10 @@ def main():
|
||||
check("qoder device headers", lines and lines[0] == "clientType=10 machineId=MID version=1.2.3 emptyToken=''", out + err[-200:])
|
||||
check("qoder jwt helpers", len(lines) > 1 and lines[1] == "exp: 9999999999 uid: u123", out)
|
||||
check("qoder global detection", len(lines) > 2 and lines[2] == "global: True False", out)
|
||||
check("qoder campaign parsing", len(lines) > 3 and lines[3] == "benefits: 1 claimable: 1", out)
|
||||
check("qoder already-claimed detection", len(lines) > 4 and lines[4] == "claimed_today: 1", out)
|
||||
check("qoder campaign parsing", "benefits: 1 claimable: 1" in out, out)
|
||||
intl_line = next((l for l in lines if l.startswith("intl_benefits:")), "")
|
||||
check("qoder intl schema (VIEW_DETAILS)", intl_line == "intl_benefits: 1", out)
|
||||
check("qoder already-claimed detection", "claimed_today: 1" in out, out)
|
||||
|
||||
print("== 13. engine: add qoder account + token masking ==")
|
||||
rc, out, err = run_py(
|
||||
@@ -245,6 +250,23 @@ def main():
|
||||
check("engine add qoder", rc == 0 and "qoder_" in out and "MID" in out, out + err[-200:])
|
||||
check("qoder token masked in dashboard", "masked: True" in out, out)
|
||||
|
||||
print("== 14. umid module (offline) ==")
|
||||
rc, out, err = run_py(
|
||||
"import umid\n"
|
||||
"st = umid.status()\n"
|
||||
"print('supported:', st['supported'], '| arch:', st['cpu_arch'])\n"
|
||||
"print('installed:', st['installed'])\n"
|
||||
"import io, tarfile\n"
|
||||
"buf = io.BytesIO()\n"
|
||||
"with tarfile.open(fileobj=buf, mode='w') as tf:\n"
|
||||
" info = tarfile.TarInfo('package/bundle/qodercli.js'); info.size = 5\n"
|
||||
" tf.addfile(info, io.BytesIO(b'hello'))\n"
|
||||
"print('tar_extract_ok:', umid._extract_tar_entry(buf.getvalue(), 'package/bundle/qodercli.js') == b'hello')\n"
|
||||
)
|
||||
lines = out.splitlines() if rc == 0 else []
|
||||
check("umid: arch supported", len(lines) > 0 and "supported: True" in lines[0], out + err[-200:])
|
||||
check("umid: tar extraction", len(lines) > 2 and "tar_extract_ok: True" in lines[2], out)
|
||||
|
||||
print()
|
||||
print("PASSED: %d FAILED: %d" % (len(PASS), len(FAIL)))
|
||||
if FAIL:
|
||||
|
||||
Reference in New Issue
Block a user