需求
希望在 GET /api/accounts 返回的 quota 中,透出每个积分包的到期时间(到期面),让控制台和外部集成能回答「哪些积分将在什么时候过期、最近一笔先到期多少」。
目前只能看到总量:used / total / remaining / percentage / unit,看不到时间维度。
现状(v0.6.2 实测)
quota 的键集合恒为:
used, total, remaining, percentage, unit, exceeded,
has_add_on, add_on_{used,total,remaining,unit,available},
has_resource_package, resource_package_{used,total,remaining,unit,available},
fetched_at
无任何 end/expire 字段。另外穷举过下列接口,同样都没有到期信息:
/api/accounts/{id}、POST /api/accounts/{id}/refresh?quota=1、/api/accounts/{id}/checkins
/api/overview、/api/overview/summary、/api/providers、/api/models
/api/logs/stats、/api/keys、/api/system/settings
GET /api/accounts/{id}/export(只有登录 token 的 expires_at,不是积分到期)
/api/accounts/{id}/{quota,worker/quota,admin/quota} 均为 404;worker admin 是白名单(internal/providers/qoder/worker.go 的 AdminAction 只含 login/device、login/status、login/pat、rewarm),无法借控制台读取 worker 原始 quota
容器内直连 qoder worker GET /admin/quota 的原始返回也只有 total/used/remaining/percentage/unit(addOn 多一个 detailUrl)。
根因(源码位置)
WorkBuddy —— 上游有到期时间,但求和时被丢弃:
internal/providers/workbuddy/client.go:899-940(UserResource)按 PackageEndTimeRangeBegin/End 过滤套餐,说明上游是按结束时间筛包的;
- 解析用的结构体
internal/providers/workbuddy/client.go:942-949 没有时间字段:
type resourcePackage struct {
CapacityRemain int64 `json:"CapacityRemain"`
CapacityUsed int64 `json:"CapacityUsed"`
CapacitySize int64 `json:"CapacitySize"`
CycleCapacityRemain int64 `json:"CycleCapacityRemain"`
CycleCapacityUsed int64 `json:"CycleCapacityUsed"`
CycleCapacitySize int64 `json:"CycleCapacitySize"`
}
aggregateUserResource(同文件 :991)只累加 remain/used/size。
⚠️ 字段名提醒:上游真实下发的是 CycleEndTime("2006-01-02 15:04:05",UTC+8 墙钟),不是 PackageEndTime。CN / global 两域字段全集里都没有 PackageEndTime,用它做判据会恒 miss —— 这一点在另一个同上游的实现里有实测记录(workbuddy2api/internal/upstream/client.go:1451、internal/upstream/expiring_test.go:95-119)。所以新增解析请用 CycleEndTime。
Trae —— internal/providers/trae/client.go:963 的 parseEntitlementUsage 只取 entitlement_base_info.quota.credits_limit 与 usage.credits_amount;额度包自身的有效期字段没有被读取(上游是否下发需确认)。
Qoder —— worker 的 normalizeQuotaBlock(worker/src/daemon.mjs:555)只保留 total/used/remaining/percentage/unit(外加 detailUrl/available),原始 getQuotaUsage() 响应即便带时间信息也会被丢弃。
建议改动
internal/accounts/quota.go:给 QuotaSnapshot 增加可选到期面,例如
// ExpiresAt: 最近一次到期的 Unix 秒;0 = 无到期/未提供
ExpiresAt int64 `json:"expires_at,omitempty"`
// ExpiringRemain: 在 ExpiresAt 之前将要过期的剩余量
ExpiringRemain float64 `json:"expiring_remain,omitempty"`
// Packages: 逐包到期明细,便于展示「N 个套餐」
Packages []QuotaPackage `json:"packages,omitempty"`
QuotaPackage 建议为 {remain, used, size, unit, ends_at, end_time}。
-
internal/providers/workbuddy/client.go:resourcePackage 增加 CycleEndTime string \json:"CycleEndTime"`,在 aggregateUserResource里按 UTC+8 解析为 Unix 秒,取最小非零值作为ExpiresAt,并按包填充 Packages`。
-
internal/providers/interfaces.go(QuotaInfo,约 :133)增加同组可选字段,并在 internal/runtime/manager_quota.go:28 的 QuotaSnapshot{...} 构造处补齐透传。
-
Trae / Qoder:先核实上游是否真下发有效期;没有就留空,不要伪造。
兼容性
- 全部字段
omitempty,老客户端不受影响;语义与 fetched_at 一致(快照时刻的上游读数)。
- 不参与路由决策,仅用于展示,
Exceeded 判定逻辑不变。
参考实现(同机、同上游,已上线可对照)
workbuddy2api 已在 GET /credit 透出 packages_detail: [{end_time, ends_at, remain, used, size}] 与 expiring 分桶,逻辑见其 internal/server/handler.go:212-282、internal/upstream/client.go:1433-1580,字段命名与解析方式可直接复用。
感谢维护,这个网关很好用。
需求
希望在
GET /api/accounts返回的quota中,透出每个积分包的到期时间(到期面),让控制台和外部集成能回答「哪些积分将在什么时候过期、最近一笔先到期多少」。目前只能看到总量:
used / total / remaining / percentage / unit,看不到时间维度。现状(v0.6.2 实测)
quota的键集合恒为:无任何 end/expire 字段。另外穷举过下列接口,同样都没有到期信息:
/api/accounts/{id}、POST /api/accounts/{id}/refresh?quota=1、/api/accounts/{id}/checkins/api/overview、/api/overview/summary、/api/providers、/api/models/api/logs/stats、/api/keys、/api/system/settingsGET /api/accounts/{id}/export(只有登录 token 的expires_at,不是积分到期)/api/accounts/{id}/{quota,worker/quota,admin/quota}均为 404;worker admin 是白名单(internal/providers/qoder/worker.go的AdminAction只含login/device、login/status、login/pat、rewarm),无法借控制台读取 worker 原始 quota容器内直连 qoder worker
GET /admin/quota的原始返回也只有total/used/remaining/percentage/unit(addOn 多一个detailUrl)。根因(源码位置)
WorkBuddy —— 上游有到期时间,但求和时被丢弃:
internal/providers/workbuddy/client.go:899-940(UserResource)按PackageEndTimeRangeBegin/End过滤套餐,说明上游是按结束时间筛包的;internal/providers/workbuddy/client.go:942-949没有时间字段:aggregateUserResource(同文件:991)只累加 remain/used/size。CycleEndTime("2006-01-02 15:04:05",UTC+8 墙钟),不是PackageEndTime。CN / global 两域字段全集里都没有PackageEndTime,用它做判据会恒 miss —— 这一点在另一个同上游的实现里有实测记录(workbuddy2api/internal/upstream/client.go:1451、internal/upstream/expiring_test.go:95-119)。所以新增解析请用CycleEndTime。Trae ——
internal/providers/trae/client.go:963的parseEntitlementUsage只取entitlement_base_info.quota.credits_limit与usage.credits_amount;额度包自身的有效期字段没有被读取(上游是否下发需确认)。Qoder —— worker 的
normalizeQuotaBlock(worker/src/daemon.mjs:555)只保留total/used/remaining/percentage/unit(外加detailUrl/available),原始getQuotaUsage()响应即便带时间信息也会被丢弃。建议改动
internal/accounts/quota.go:给QuotaSnapshot增加可选到期面,例如QuotaPackage建议为{remain, used, size, unit, ends_at, end_time}。internal/providers/workbuddy/client.go:resourcePackage增加CycleEndTime string \json:"CycleEndTime"`,在aggregateUserResource里按 UTC+8 解析为 Unix 秒,取最小非零值作为ExpiresAt,并按包填充Packages`。internal/providers/interfaces.go(QuotaInfo,约:133)增加同组可选字段,并在internal/runtime/manager_quota.go:28的QuotaSnapshot{...}构造处补齐透传。Trae / Qoder:先核实上游是否真下发有效期;没有就留空,不要伪造。
兼容性
omitempty,老客户端不受影响;语义与fetched_at一致(快照时刻的上游读数)。Exceeded判定逻辑不变。参考实现(同机、同上游,已上线可对照)
workbuddy2api已在GET /credit透出packages_detail: [{end_time, ends_at, remain, used, size}]与expiring分桶,逻辑见其internal/server/handler.go:212-282、internal/upstream/client.go:1433-1580,字段命名与解析方式可直接复用。感谢维护,这个网关很好用。