排查结论(本机对真实接口做了 30+ 组对照实验)
- GET /sash/api/v1/me/campaigns 只返回一条常驻 VIEW_DETAILS 广告
(act-20260901-493,BOGO 订阅推广,claimStatus=CLAIMED 与签到无关)
- 无论 Cosy-MachineToken/Code/Type 用 env 0/3/4/prod、裸调用/带 stdin、
是否附 Cosy-MachineId/OS/Hostname/Origin/Referer/X-*、是否带 clientType,
都拿不到 CLAIM_BEFIT 轮次;账号本人 balances/achievements 正常
- 三个社区实现(qoder2api-hub、dsh-connect-qoder、CreditDaddy)一致:
每日轮次按「登录过官方客户端的那台设备」下发,NAS 侧另造的设备看不到它
修复
- _benefits() 改为只认 actionType=CLAIM_BEFIT(不再把 VIEW_DETAILS 当签到),
窗口过期也排除;claimStatus 未知按可领处理(领取接口幂等)
- 两段式取活动:先裸取拿 uid → 按 uid 生成风控身份 → 再带设备身份重取
- 状态文案改为「未下发每日签到活动」而不是误导性的「今日已领取」
新功能
- Qoder 一键登录(OAuth 设备授权 / PKCE S256):应用内点按钮 → 浏览器确认一次,
Token 直接绑定本机设备;同 uid 已存在则更新凭证而不是新增重复账号
- 手工导入桌面端设备身份接口 /api/qoder/import_device
- 诊断接口:/api/qoder/{probe,experiment,growth,matrix,desktop}
测试:39 后端 + 25 E2E + 9 前端全绿
235 lines
13 KiB
HTML
235 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Agent 签到助手</title>
|
||
<link rel="stylesheet" href="assets/style.css">
|
||
</head>
|
||
<body>
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<img src="assets/logo.svg" alt="" class="brand-logo">
|
||
<div>
|
||
<h1>Agent 签到助手</h1>
|
||
<p class="sub">TRAE · WorkBuddy 每日积分自动签到</p>
|
||
</div>
|
||
</div>
|
||
<div class="topbar-right">
|
||
<span id="daemon-chip" class="chip chip-gray">守护进程 —</span>
|
||
<span id="nextrun-chip" class="chip chip-blue">下次签到 —</span>
|
||
<button id="btn-run-all" class="btn btn-primary">立即签到</button>
|
||
</div>
|
||
</header>
|
||
|
||
<nav class="tabs">
|
||
<button class="tab active" data-tab="dashboard">仪表盘</button>
|
||
<button class="tab" data-tab="accounts">账号管理</button>
|
||
<button class="tab" data-tab="logs">签到记录</button>
|
||
<button class="tab" data-tab="settings">设置</button>
|
||
</nav>
|
||
|
||
<main>
|
||
<!-- ======================= 仪表盘 ======================= -->
|
||
<section id="tab-dashboard" class="tabpage active">
|
||
<div id="dash-cards" class="card-grid"></div>
|
||
<div class="panel">
|
||
<h2>使用说明</h2>
|
||
<ul class="hint-list">
|
||
<li><b>TRAE</b>(字节跳动·国内版):每日自动领取 <b>200</b> Work 专属积分。</li>
|
||
<li><b>WorkBuddy / CodeBuddy</b>(腾讯):7 天循环签到,第 1-6 天每天 100 分,第 7 天 1000 分。</li>
|
||
<li><b>Qoder</b>(国内版):每日自动领取 <b>100</b> Credits(活动非零点准时下发,多时段自动补领)。</li>
|
||
<li>签到由 NAS 本地守护进程自动执行,失败会在下一个时段自动补签; Token 过期会标记需重新授权。</li>
|
||
<li>所有凭据仅保存在本机 <code>@appdata/AgentCheckin</code>,不会上传到任何第三方。</li>
|
||
</ul>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ======================= 账号管理 ======================= -->
|
||
<section id="tab-accounts" class="tabpage">
|
||
<div class="panel">
|
||
<div class="panel-head">
|
||
<h2>账号列表</h2>
|
||
<button class="btn btn-primary" id="btn-add-account">+ 添加账号</button>
|
||
</div>
|
||
<div id="account-table-wrap"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ======================= 签到记录 ======================= -->
|
||
<section id="tab-logs" class="tabpage">
|
||
<div class="panel">
|
||
<div class="panel-head">
|
||
<h2>运行日志</h2>
|
||
<div>
|
||
<label class="switch-label"><input type="checkbox" id="log-autorefresh" checked> 自动刷新</label>
|
||
<button class="btn" id="btn-refresh-logs">刷新</button>
|
||
</div>
|
||
</div>
|
||
<pre id="log-view" class="log-view">加载中…</pre>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ======================= 设置 ======================= -->
|
||
<section id="tab-settings" class="tabpage">
|
||
<div class="panel">
|
||
<h2>签到时间</h2>
|
||
<p class="muted">每天在这些时间点自动检查并签到(已签会自动跳过)。错过时段后在守护进程启动时会自动补跑。</p>
|
||
<div id="time-chips" class="chips"></div>
|
||
<div class="row">
|
||
<input type="time" id="new-time" class="input input-time">
|
||
<button class="btn" id="btn-add-time">添加时间点</button>
|
||
</div>
|
||
</div>
|
||
<div class="panel">
|
||
<h2>Qoder 风控身份(UMID)</h2>
|
||
<p class="muted">Qoder 国际版只对带设备风控身份的请求下发签到活动。首次使用需下载官方组件(约 30MB,仅一次)。</p>
|
||
<div id="umid-status" class="muted">检测中…</div>
|
||
<div class="row">
|
||
<button class="btn" id="btn-umid-install">下载 / 重新获取风控组件</button>
|
||
</div>
|
||
<div id="umid-msg" class="msg"></div>
|
||
</div>
|
||
<div class="panel">
|
||
<h2>消息通知(可选)</h2>
|
||
<p class="muted">签到后向 Webhook 发送 POST JSON(<code>{"title","content","text","msgtype"}</code>),兼容企业微信/钉钉/飞书/Bark 等机器人。留空则不通知。</p>
|
||
<input type="url" id="webhook-url" class="input input-wide" placeholder="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=...">
|
||
<label style="margin-top:12px">通知平台
|
||
<select id="webhook-type" class="input">
|
||
<option value="auto">自动识别(推荐)</option>
|
||
<option value="wecom">企业微信机器人</option>
|
||
<option value="dingtalk">钉钉机器人</option>
|
||
<option value="feishu">飞书机器人</option>
|
||
<option value="bark">Bark(iOS 推送)</option>
|
||
<option value="generic">通用 JSON(Server酱 / PushPlus 等)</option>
|
||
</select>
|
||
</label>
|
||
<div class="row">
|
||
<label class="switch-label"><input type="checkbox" id="notify-fail" checked> 失败时通知</label>
|
||
<label class="switch-label"><input type="checkbox" id="notify-success"> 成功时也通知</label>
|
||
</div>
|
||
<div class="row">
|
||
<button class="btn btn-primary" id="btn-save-settings">保存设置</button>
|
||
<button class="btn" id="btn-test-notify">发送测试通知</button>
|
||
</div>
|
||
<div id="settings-msg" class="msg"></div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<!-- ======================= 添加账号弹窗 ======================= -->
|
||
<div id="modal-add" class="modal-mask hidden">
|
||
<div class="modal">
|
||
<div class="modal-head">
|
||
<h3>添加账号</h3>
|
||
<button class="modal-close" data-close="modal-add">×</button>
|
||
</div>
|
||
<div class="modal-tabs">
|
||
<button class="mtab active" data-mtab="wb-oauth">WorkBuddy 网页授权</button>
|
||
<button class="mtab" data-mtab="wb-manual">WorkBuddy 手动</button>
|
||
<button class="mtab" data-mtab="trae">TRAE</button>
|
||
<button class="mtab" data-mtab="qoder">Qoder</button>
|
||
</div>
|
||
|
||
<div id="mtab-wb-oauth" class="mtabpage active">
|
||
<p>点击下方按钮生成授权链接,在浏览器中打开并登录腾讯 CodeBuddy / WorkBuddy 账号,本页面会自动完成绑定。</p>
|
||
<button class="btn btn-primary" id="btn-oauth-begin">生成授权链接</button>
|
||
<div id="oauth-box" class="hidden">
|
||
<p>请在浏览器中打开以下链接完成登录(10 分钟内有效):</p>
|
||
<div class="row">
|
||
<input id="oauth-url" class="input input-wide" readonly>
|
||
<button class="btn" id="btn-oauth-copy">复制</button>
|
||
</div>
|
||
<p id="oauth-status" class="muted">等待登录…</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="mtab-wb-manual" class="mtabpage">
|
||
<label>名称(可选)<input id="wb-name" class="input input-wide" placeholder="我的 CodeBuddy"></label>
|
||
<label>Access Token <span class="req">*</span><input id="wb-token" class="input input-wide" placeholder="accessToken"></label>
|
||
<label>Refresh Token(可选,用于自动续期)<input id="wb-refresh" class="input input-wide" placeholder="refreshToken"></label>
|
||
<label>区域
|
||
<select id="wb-domain" class="input">
|
||
<option value="codebuddy.cn">国内版 codebuddy.cn(支持签到)</option>
|
||
<option value="workbuddy.ai">国际版 workbuddy.ai(无签到)</option>
|
||
</select>
|
||
</label>
|
||
<button class="btn btn-primary" id="btn-wb-manual-add">添加</button>
|
||
</div>
|
||
|
||
<div id="mtab-trae" class="mtabpage">
|
||
<label>名称(可选)<input id="trae-name" class="input input-wide" placeholder="TRAE 主账号"></label>
|
||
<label>Access Token <span class="req">*</span><input id="trae-token" class="input input-wide" placeholder="eyJ 开头的 JWT"></label>
|
||
<label>Refresh Token(可选)<input id="trae-refresh" class="input input-wide" placeholder="可选"></label>
|
||
<details class="help">
|
||
<summary>如何获取 TRAE Token?</summary>
|
||
<div class="help-body">
|
||
<p>在登录了 TRAE 国内版客户端的电脑上,凭据保存在:</p>
|
||
<p><code>Windows: %APPDATA%\Trae CN\User\globalStorage\storage.json</code></p>
|
||
<p>其中键 <code>iCubeAuthInfo://icube.cloudide</code> 的值为加密串,解密后可得 <code>token</code>(即 Access Token,JWT 格式)。</p>
|
||
<p>也可使用社区工具 <code>trae_get_token.py</code>(Trae-AutoCheckin 项目)一键提取。</p>
|
||
<p class="muted">Token 约 60 天有效;过期后在本页重新粘贴即可。</p>
|
||
</div>
|
||
</details>
|
||
<button class="btn btn-primary" id="btn-trae-add">添加</button>
|
||
</div>
|
||
|
||
<div id="mtab-qoder" class="mtabpage">
|
||
<div class="panel qoder-oauth">
|
||
<div class="row">
|
||
<button class="btn" id="btn-qoder-login">一键登录(设备授权,推荐)</button>
|
||
<span id="qoder-login-status" class="muted"></span>
|
||
</div>
|
||
<div id="qoder-login-link" class="msg"></div>
|
||
<p class="muted">点按钮 → 打开弹出的授权链接 → 在浏览器里确认一次即可自动加入账号。<b>不建议</b>再手工粘贴别处提取的 Token:国际版的「每天领 100 Credits」活动只下发给<b>这次登录的设备</b>,用别的机器提取的 Token 会看不到活动。</p>
|
||
</div>
|
||
<label>名称(可选)<input id="qoder-name" class="input input-wide" placeholder="Qoder 主账号"></label>
|
||
<label>Access Token <span class="req">*</span><input id="qoder-token" class="input input-wide" placeholder="dt- 开头的 JWT"></label>
|
||
<label>Refresh Token(可选,用于自动续期)<input id="qoder-refresh" class="input input-wide" placeholder="drt- 开头,可选"></label>
|
||
<label>站点区域(重要:两个站点 Token 不互通,选错会 401)
|
||
<select id="qoder-domain" class="input">
|
||
<option value="openapi.qoder.com.cn">国内版 openapi.qoder.com.cn(qoder.cn 客户端)</option>
|
||
<option value="openapi.qoder.sh">国际版 openapi.qoder.sh(qoder.com 客户端)</option>
|
||
</select>
|
||
</label>
|
||
<details class="help" open>
|
||
<summary>如何获取 Qoder Token?(三种方式)</summary>
|
||
<div class="help-body">
|
||
<p><b>方式 A · 一键登录(推荐,无需抓包)</b></p>
|
||
<p>直接点上方「一键登录(设备授权)」,在浏览器里确认一次即可。这种方式拿到的 Token 与<b>本机设备绑定</b>,国际版才能看到「每天领 100 Credits」活动。</p>
|
||
<p><b>方式 B · 浏览器抓包(最快)</b></p>
|
||
<p>浏览器登录 Qoder 网页版 → F12 开发者工具 → 网络 → 点页面里任意功能,任选一个请求 → 在请求头里找到 <code>authorization: Bearer dt-...</code></p>
|
||
<p><b>复制技巧</b>:右键该值 →「Copy value」复制完整内容,粘贴后删掉开头的 <code>Bearer </code>。</p>
|
||
<p><b>注意</b>:Qoder 的 <code>dt-</code> Token 原生就只有 <b>27/28 个字符</b>(不是截断,这是它的原生格式),<code>drt-</code> 刷新串同理。</p>
|
||
<p><b>关键</b>:国内版(qoder.cn)与国际版(qoder.com)的 Token <b>不互通</b>——用哪个客户端登录提的 Token,上方「站点区域」就要选对应的那个,选错会直接 401。拿不准可以先用「测连接」,它会自动探测 Token 属于哪个站点。</p>
|
||
<p><b>方式 C · 本机客户端提取</b></p>
|
||
<p>在装了并登录 Qoder 客户端的 Windows 上运行社区工具 <code>01_extract.py</code>(GitHub: wallechfox/qoder-checkin),自动解密 <code>auth.v1.dat</code> 取出 Token 和设备标识。</p>
|
||
<p class="muted">Token 有效期较短,过期后重新获取即可;填了 Refresh Token 的应用会自动续期。</p>
|
||
<p><b>关于风控身份(重要)</b></p>
|
||
<p>Qoder 服务端只对携带设备风控身份(<code>Cosy-MachineToken/Code/Type</code>)的请求下发「每天领 100 Credits」活动,否则活动列表为空。</p>
|
||
<p>本应用会自动从官方 npm 包 <code>@qoder-ai/qodercli</code> 提取 UMID 组件并生成身份(首次约需下载 30MB,之后缓存复用)。可在「设置 → Qoder 风控身份」查看/重新获取。</p>
|
||
</div>
|
||
</details>
|
||
<details class="help">
|
||
<summary>设备标识(可选,领取提示风控时填写)</summary>
|
||
<div class="help-body">
|
||
<p>Qoder 签到需要 Cosy-* 设备标识。一般情况留空即可;若提示风控/领取失败,用方式 C 的工具提取后填入:</p>
|
||
<div class="row"><input id="qoder-machine-id" class="input input-wide" placeholder="Cosy-MachineId"></div>
|
||
<div class="row"><input id="qoder-machine-token" class="input input-wide" placeholder="Cosy-MachineToken"></div>
|
||
<div class="row"><input id="qoder-machine-code" class="input input-wide" placeholder="Cosy-MachineCode"></div>
|
||
<div class="row"><input id="qoder-machine-type" class="input input-wide" placeholder="Cosy-MachineType"></div>
|
||
<div class="row"><input id="qoder-version" class="input input-wide" placeholder="Cosy-Version(客户端版本号)"></div>
|
||
</div>
|
||
</details>
|
||
<button class="btn btn-primary" id="btn-qoder-add">添加</button>
|
||
</div>
|
||
<div id="add-msg" class="msg"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="toast" class="toast hidden"></div>
|
||
|
||
<script src="assets/app.js"></script>
|
||
</body>
|
||
</html>
|