@@ -391,8 +391,12 @@ async def fetch_quota(self, credential: TraeCredential) -> Quota:
391391
392392 同时保留每个权益包的名称/额度/已用/到期,供管理台展示奖励积分明细
393393 (它们各自独立到期,汇总数字看不出是哪些包、什么时候过期)。
394- 注意只填充展示用的 packages,不填 expiry_ladder:后者是选号排序
395- 指标,TRAE 按设计无周期概念(保持 None 不变)。
394+
395+ `expiry_ladder` 也一并填充:实测 TRAE 的权益包**确实各自独立到期**
396+ (每月登录积分按月、签到奖励各有到期日),不是「无周期概念」。只填
397+ 展示用的 packages 会让「到期积分」行恒为空,快过期的积分拿不到优先
398+ 消耗——这正是调度该避免的白丢。口径与 CodeBuddy 一致:阶梯只收
399+ 「未过期 + 有余额」的包(已过期积分作废、已用完的包不携带积分)。
396400 """
397401 data = await self ._post_json (
398402 f"{ self .ug_host } /trae/api/v2/pay/ide_user_ent_usage" , {},
@@ -401,8 +405,10 @@ async def fetch_quota(self, credential: TraeCredential) -> Quota:
401405 packs = data .get ("user_entitlement_pack_list" )
402406 if not isinstance (packs , list ):
403407 raise UpstreamProtocolViolation ("quota response missing pack list" )
408+ now_epoch = time .time ()
404409 limit = 0.0
405410 used = 0.0
411+ ladder : list [tuple [int , float ]] = []
406412 packages : list [dict [str , Any ]] = []
407413 for pack in packs :
408414 if not isinstance (pack , dict ):
@@ -412,16 +418,25 @@ async def fetch_quota(self, credential: TraeCredential) -> Quota:
412418 if not isinstance (pack_limit , (int , float )) or pack_limit <= 0 :
413419 continue
414420 pack_used = (pack .get ("usage" ) or {}).get ("credits_amount" )
421+ used_value = float (pack_used ) if isinstance (pack_used , (int , float )) else 0.0
422+ balance = max (0.0 , float (pack_limit ) - used_value )
423+ end = _pack_end (pack )
415424 limit += float (pack_limit )
416- used += float ( pack_used ) if isinstance ( pack_used , ( int , float )) else 0.0
425+ used += used_value
417426 packages .append ({
418427 "name" : _pack_name (pack ),
419428 "total" : float (pack_limit ),
420- "used" : float ( pack_used ) if isinstance ( pack_used , ( int , float )) else 0.0 ,
421- "end" : _pack_end ( pack ) ,
429+ "used" : used_value ,
430+ "end" : end ,
422431 })
432+ # 到期阶梯(选号指标):上游会把已过期/已用完的包一起返回,
433+ # 不排除则「最早到期」永远指向过去、指标恒为 0。
434+ if end is None or end <= now_epoch or balance <= 0 :
435+ continue
436+ ladder .append ((end , balance ))
423437 return Quota (remaining = max (0.0 , limit - used ), total = limit ,
424- packages = packages or None , probed_at = int (time .time ()))
438+ expiry_ladder = ladder , packages = packages or None ,
439+ probed_at = int (now_epoch ))
425440
426441 async def fetch_checkin_status (self , credential : TraeCredential ,
427442 * , device_id : str = "" ) -> dict [str , Any ]:
0 commit comments