@magpie-community/opencode-workbuddy-auth 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/index.mjs +84 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,15 @@ is used.
|
|
|
119
119
|
|
|
120
120
|
A model's reasoning levels are its variants (`reasoningEffort`).
|
|
121
121
|
|
|
122
|
+
## Credits in magpie
|
|
123
|
+
|
|
124
|
+
magpie's usage card shows the account's credits from the resource summary.
|
|
125
|
+
It also says when the soonest credits left run out (`runsOutAt`, and how
|
|
126
|
+
many: `runsOut`), from the paid and free package lists WorkBuddy's own
|
|
127
|
+
client reads. The daily Free Plan slice isn't counted: it ends at the same
|
|
128
|
+
midnight on every account. magpie's smart routing spends the account whose
|
|
129
|
+
credits run out first.
|
|
130
|
+
|
|
122
131
|
## Not here
|
|
123
132
|
|
|
124
133
|
magpie's built-in WorkBuddy does more than this package:
|
package/index.mjs
CHANGED
|
@@ -917,13 +917,13 @@ const statusText = (n) => STATUS_TEXT[n] || `HTTP ${n}`
|
|
|
917
917
|
|
|
918
918
|
// meter asks the billing API as magpie's built-in did, its errors said
|
|
919
919
|
// the same: WorkBuddy's message, else its code, else the HTTP status.
|
|
920
|
-
async function meter(site, a, path) {
|
|
920
|
+
async function meter(site, a, path, body = {}) {
|
|
921
921
|
const res = await fetch(site.endpoint + path, {
|
|
922
922
|
method: "POST",
|
|
923
923
|
headers: { "Content-Type": "application/json", Accept: "application/json", "User-Agent": "WorkBuddy/" + UA_VERSION,
|
|
924
924
|
Authorization: "Bearer " + a.access, "X-User-Id": a.uid ?? "", "X-Domain": domainOf(site, a), "X-Product": "SaaS",
|
|
925
925
|
"X-IDE-Type": "WorkBuddy", ...enterpriseHeaders(a) },
|
|
926
|
-
body:
|
|
926
|
+
body: JSON.stringify(body),
|
|
927
927
|
signal: AbortSignal.timeout(20000),
|
|
928
928
|
})
|
|
929
929
|
let env = {}
|
|
@@ -969,6 +969,76 @@ function usageOf(sum, plan) {
|
|
|
969
969
|
return out
|
|
970
970
|
}
|
|
971
971
|
|
|
972
|
+
// The packages' codes, as WorkBuddy's own client lists them when it asks
|
|
973
|
+
// for each package's credits (its COMMODITY_CODES): the API refuses a
|
|
974
|
+
// detail list without them (10001 "PackageCodes required").
|
|
975
|
+
const CODE = (n, s) => `TCACA_code_${n}_${s}`
|
|
976
|
+
const PAID_CODES = [CODE("002", "AkiJS3ZHF5"), CODE("005", "maRGyrHhw1"), CODE("003", "FAnt7lcmRT"), CODE("023", "4xbGhMrE6q"),
|
|
977
|
+
CODE("026", "BaESVICNoi"), CODE("027", "0FCGVA6vSa"), CODE("009", "0XmEQc2xOf"), CODE("038", "OhvqZtiPKr"), CODE("036", "lupO5WgNdG")]
|
|
978
|
+
const DAILY_CODE = CODE("001", "PqouKr6QWV")
|
|
979
|
+
const FREE_CODES = [DAILY_CODE, CODE("008", "cfWoLwvjU4"), CODE("035", "ArVxJcGDsm"), CODE("006", "DbXS0lrypC"), CODE("039", "KRcQj7wUat"),
|
|
980
|
+
CODE("040", "mi9rCYg46x"), CODE("007", "nzdH5h4Nl0"), CODE("028", "NtpWi0jzXs"), CODE("037", "WxOD3MpI2o"), CODE("029", "6wCGEWquYy"),
|
|
981
|
+
CODE("030", "BjSt89qTvr")]
|
|
982
|
+
// a slice package (CapacityType 4) is refreshed each day: its credits are
|
|
983
|
+
// lost at the same midnight on every account, so they tell none apart
|
|
984
|
+
const SLICE = "4"
|
|
985
|
+
|
|
986
|
+
// beijing is WorkBuddy's own time ("2026-10-13 00:39:27" is UTC+8, as its
|
|
987
|
+
// DeductionEndTime for the same package says).
|
|
988
|
+
const beijing = (ms) => new Date(ms + 8 * 3600_000).toISOString().slice(0, 19).replace("T", " ")
|
|
989
|
+
|
|
990
|
+
// packagesOf is every package of the account with credits, paid and free,
|
|
991
|
+
// as WorkBuddy's client asks for them: active and used-up (Status 0, 3),
|
|
992
|
+
// up to 10 pages of 200.
|
|
993
|
+
async function packagesOf(site, a, now = Date.now()) {
|
|
994
|
+
const day = beijing(now).slice(0, 10)
|
|
995
|
+
const lists = [
|
|
996
|
+
["/billing/meter/get-user-resource-paid-packages", { PackageCodes: PAID_CODES, NeedRenewInfo: true }],
|
|
997
|
+
["/billing/meter/get-user-resource-free-packages", { PackageCodes: FREE_CODES, SlicePeriodStartTime: day + " 00:00:00", SlicePeriodEndTime: day + " 23:59:59" }],
|
|
998
|
+
]
|
|
999
|
+
const out = []
|
|
1000
|
+
for (const [path, extra] of lists) {
|
|
1001
|
+
for (let page = 1; page <= 10; page++) {
|
|
1002
|
+
const d = await meter(site, a, path, { PageNumber: page, PageSize: 200, Status: [0, 3], ...extra })
|
|
1003
|
+
const got = d?.Accounts ?? []
|
|
1004
|
+
out.push(...got)
|
|
1005
|
+
if (got.length < 200 || out.length >= (d?.TotalCount ?? 0)) break
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return out
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// endOf is when a package's credits are lost: its DeductionEndTime, else
|
|
1012
|
+
// ExpiredTime, else the cycle's end (UTC+8), as WorkBuddy's client reads it.
|
|
1013
|
+
function endOf(p) {
|
|
1014
|
+
const ms = Number(p?.DeductionEndTime)
|
|
1015
|
+
if (ms > 0) return ms
|
|
1016
|
+
for (const v of [p?.ExpiredTime, p?.CycleEndTime]) {
|
|
1017
|
+
if (typeof v !== "string" || !v.trim()) continue
|
|
1018
|
+
const t = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(v.trim()) ? Date.parse(v.trim().replace(" ", "T") + "+08:00") : Date.parse(v)
|
|
1019
|
+
if (t > 0) return t
|
|
1020
|
+
}
|
|
1021
|
+
return 0
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// runsOutOf is the soonest moment credits left in one of the account's
|
|
1025
|
+
// packages are lost, and how many (cdredfox on X): smart routing spends
|
|
1026
|
+
// the account whose credits expire first. Only packages the summary
|
|
1027
|
+
// counts, with credits left, and not the daily slice; null when none.
|
|
1028
|
+
function runsOutOf(sum, packages, now = Date.now()) {
|
|
1029
|
+
const counted = new Set((sum?.Packages ?? []).map((p) => p?.PackageCode))
|
|
1030
|
+
let at = 0, amount = 0
|
|
1031
|
+
for (const p of packages ?? []) {
|
|
1032
|
+
if (!counted.has(p?.PackageCode) || p?.PackageCode === DAILY_CODE || String(p?.CapacityType) === SLICE) continue
|
|
1033
|
+
const left = capacity(p?.CycleCapacityRemainPrecise ?? p?.CycleCapacityRemain)
|
|
1034
|
+
const end = endOf(p)
|
|
1035
|
+
if (!(left > 0) || end <= now) continue
|
|
1036
|
+
if (!at || end < at) at = end, amount = left
|
|
1037
|
+
else if (end === at) amount += left
|
|
1038
|
+
}
|
|
1039
|
+
return at ? { runsOutAt: new Date(at).toISOString(), runsOut: amount } : null
|
|
1040
|
+
}
|
|
1041
|
+
|
|
972
1042
|
// ---- models -----------------------------------------------------------------
|
|
973
1043
|
|
|
974
1044
|
// variants are the reasoning levels as OpenCode gives them to the AI SDK.
|
|
@@ -1166,7 +1236,17 @@ function makePlugin(site) {
|
|
|
1166
1236
|
try {
|
|
1167
1237
|
const a = await current(site, client, auth)
|
|
1168
1238
|
// the meter has no /v2 prefix, on either site
|
|
1169
|
-
|
|
1239
|
+
const sum = await meter(site, a, "/billing/meter/get-user-resource-summary")
|
|
1240
|
+
const out = usageOf(sum, auth.plan)
|
|
1241
|
+
// when the credits left run out, for routing to spend them first;
|
|
1242
|
+
// a list that can't be read leaves it unknown, the card stands
|
|
1243
|
+
const credits = out.windows.find((w) => w.name === "Credits")
|
|
1244
|
+
if (credits) {
|
|
1245
|
+
try {
|
|
1246
|
+
Object.assign(credits, runsOutOf(sum, await packagesOf(site, a)) ?? {})
|
|
1247
|
+
} catch {}
|
|
1248
|
+
}
|
|
1249
|
+
return { ...out, signIn: "kept" }
|
|
1170
1250
|
} catch (e) {
|
|
1171
1251
|
return { error: e?.message ?? String(e), signIn: "kept" }
|
|
1172
1252
|
}
|
|
@@ -1258,4 +1338,4 @@ export const WorkBuddyAuthPlugin = makePlugin(SITES.workbuddy)
|
|
|
1258
1338
|
export const WorkBuddyAIAuthPlugin = makePlugin(SITES["workbuddy-ai"])
|
|
1259
1339
|
|
|
1260
1340
|
// for tests
|
|
1261
|
-
export const _internal = { enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
|
|
1341
|
+
export const _internal = { runsOutOf, packagesOf, endOf, enterpriseHeaders, enterpriseOf, liveModels, browserSignIn, withSystem, fitTools, fitted, renamed, unflagged, usageOf, desktopHeld, explained, REFUSED_HINT, EXHAUSTED_HINT, freeCredits, creditsOf, fresh, SITES, renewing, renewed, SESSION, attend, turnKey, conversationKey, signatureOf }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-workbuddy-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "OpenCode provider plugins for WorkBuddy's plans: WorkBuddy (CodeBuddy, copilot.tencent.com) and WorkBuddy AI (www.workbuddy.ai)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|