From 972bf5b920cb1a01aff2165c20698b90324e484f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AE=80=E5=A4=95live?= Date: Fri, 16 Oct 2026 15:40:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20/v1/checkin-status?= =?UTF-8?q?=20=E4=B8=8E=20/v1/checkin=EF=BC=88=E6=AF=8F=E6=97=A5=E7=AD=BE?= =?UTF-8?q?=E5=88=B0=E9=80=8F=E4=BC=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 透传官方 billing 端点,便于客户端(如 dsh-llm-workbuddy 状态胶囊) 读取签到状态并领取每日签到: - GET /v1/checkin-status → /v2/billing/meter/checkin-status - POST /v1/checkin → /v2/billing/meter/daily-checkin 两者都用已登录会话调用,并在 401 时先刷新 token 再重试一次; 上游返回的 {code, msg, data} 原样透传,不做二次解释。 客户端此前只能探测到 404,因此会隐藏签到入口并停止每日自动签到; 补齐这两个端点后无需改客户端即可自动启用。 --- src/codebuddy_proxy/__main__.py | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/codebuddy_proxy/__main__.py b/src/codebuddy_proxy/__main__.py index 897356b..ee742b5 100755 --- a/src/codebuddy_proxy/__main__.py +++ b/src/codebuddy_proxy/__main__.py @@ -815,6 +815,61 @@ async def health(): } +# ============================================================================ +# 端点:/v1/checkin-status 与 /v1/checkin(每日签到透传) +# ============================================================================ + +async def _billing_post(path: str) -> dict[str, Any]: + """用本地已登录会话调用官方 billing 端点并返回原始 JSON。 + + 自动处理 401:先走 ensure_authenticated 刷新 token 再重试一次。 + """ + state = get_state() + url = state.client.endpoint.rstrip("/") + path + async with httpx.AsyncClient(timeout=httpx.Timeout(10.0)) as client: + resp = await client.post(url, json={}, headers=state.client.auth_headers()) + if resp.status_code == 401: + # token 过期:触发一次刷新后重试 + try: + await asyncio.to_thread(state.client.ensure_authenticated, False) + except Exception: + pass + resp = await client.post(url, json={}, headers=state.client.auth_headers()) + try: + return resp.json() + except Exception: + return {"code": resp.status_code, "msg": f"non-JSON response (HTTP {resp.status_code})"} + + +@app.get("/v1/checkin-status") +async def checkin_status(): + """查询每日签到活动状态(透传官方 /v2/billing/meter/checkin-status)。""" + state = get_state() + try: + state.ensure_auth() + except Exception as e: + raise HTTPException(status_code=503, detail={"error": {"message": f"not authenticated: {e}", "type": "auth_error"}}) + result = await _billing_post("/v2/billing/meter/checkin-status") + return {"status": "ok", "upstream": result} + + +@app.post("/v1/checkin") +async def checkin(): + """执行每日签到(透传官方 /v2/billing/meter/daily-checkin)。 + + 业务结果原样返回:code=0 且 data 非空为领取成功;已签到/活动结束等 + 业务拒绝通过上游 data.status / msg 区分,这里不做二次解释。 + """ + state = get_state() + try: + state.ensure_auth() + except Exception as e: + raise HTTPException(status_code=503, detail={"error": {"message": f"not authenticated: {e}", "type": "auth_error"}}) + result = await _billing_post("/v2/billing/meter/daily-checkin") + diagnostic("checkin_claim_result", code=result.get("code"), msg=result.get("msg")) + return {"status": "ok", "upstream": result} + + # ============================================================================ # 端点:/v1/models # ============================================================================