@magpie-community/opencode-trae-auth 0.2.24 → 0.2.26
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 +23 -1
- package/index.mjs +66 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,11 @@ agent's `POST https://trae-api-cn.mchost.guru/api/agent/v3/llm_utils_chat`:
|
|
|
113
113
|
What `token_usage` says the prompt cache read and wrote
|
|
114
114
|
(`cache_read_input_tokens`, `cache_creation_input_tokens`) goes on under
|
|
115
115
|
those names, and `reasoning_tokens` as
|
|
116
|
-
`completion_tokens_details.reasoning_tokens`.
|
|
116
|
+
`completion_tokens_details.reasoning_tokens`. DeepSeek's
|
|
117
|
+
`prompt_cache_hit_tokens` and `cached_tokens` are read as cache reads
|
|
118
|
+
too. A `token_usage` that names no cache count at all goes on with
|
|
119
|
+
`magpie_cache_unknown: true`, so magpie shows the cache as unknown,
|
|
120
|
+
not as a 0% miss (0.2.26).
|
|
117
121
|
`done`'s `finish_reason` `length`/`max_tokens` is OpenAI's `length`.
|
|
118
122
|
- Queue events are ignored.
|
|
119
123
|
- A `{"reasoning_content": …}` object written into the text is reasoning,
|
|
@@ -448,6 +452,24 @@ answered it 9074 even with the client's headers.
|
|
|
448
452
|
changed or rotated to get round a 9095 or a 9074, and no account is
|
|
449
453
|
given another account's device or its own client's device unless a
|
|
450
454
|
client is signed in as that account.
|
|
455
|
+
- **Which device, said (0.2.25):** a 9074 or 9095 message ends with
|
|
456
|
+
the device the claim went on: this machine's Trae CN client's, signed in
|
|
457
|
+
as the account, or magpie's own for it, with how many client profiles
|
|
458
|
+
were found and that none is signed in as the account. A client device
|
|
459
|
+
that another profile, signed in as another account, also has is said
|
|
460
|
+
too: both accounts check in on one device, which Trae takes once a day.
|
|
461
|
+
No uid or device id is named. On plugins#436 two accounts shared one
|
|
462
|
+
machine whose two profiles (`Trae CN`, `TRAE SOLO CN`) were both the
|
|
463
|
+
first account's; it checked in and the second got 9074, which took
|
|
464
|
+
running `clientDevice` by hand to explain. A profile started with its
|
|
465
|
+
own `--user-data-dir` is not read, and there is no reason to: on
|
|
466
|
+
2026-10-29 TRAE CN 3.4.4 (Windows 11) kept a second account's sign-in
|
|
467
|
+
apart in such a folder but gave it the same device id as the default
|
|
468
|
+
folder, and the client itself said 「本设备今日已签到」 for that
|
|
469
|
+
account. The check-in is counted per device, so on one machine only the
|
|
470
|
+
first account to check in gets the day's credits; the others' usage,
|
|
471
|
+
models and requests are unaffected (plugins#436, closed as not
|
|
472
|
+
planned).
|
|
451
473
|
- **A claim's code 0 is not the answer (0.2.22):** Trae answers a
|
|
452
474
|
claim code 0 for one that registered nothing, so the status is read back
|
|
453
475
|
after every claim and only a day that is in is reported as `claimed`.
|
package/index.mjs
CHANGED
|
@@ -871,6 +871,15 @@ function clientStoragePaths(platform, home, env) {
|
|
|
871
871
|
// seen (and one unreadable file hid every profile after it).
|
|
872
872
|
async function clientDevice(uid) {
|
|
873
873
|
if (!uid) return undefined
|
|
874
|
+
return (await clientProfiles()).find((p) => String(p.uid) === String(uid))?.device
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// clientProfiles are this machine's Trae CN client profiles that can be
|
|
878
|
+
// made out, in ownClient.paths' order: the account each is signed in as
|
|
879
|
+
// and its one device. A file that isn't there, can't be read or parsed,
|
|
880
|
+
// names no single device or has a sign-in that doesn't decode is left out.
|
|
881
|
+
async function clientProfiles() {
|
|
882
|
+
const out = []
|
|
874
883
|
for (const p of ownClient.paths()) {
|
|
875
884
|
let text
|
|
876
885
|
try {
|
|
@@ -888,12 +897,34 @@ async function clientDevice(uid) {
|
|
|
888
897
|
const ids = Object.keys(doc ?? {}).filter((k) => k.startsWith(DC_KEY)).map((k) => k.slice(DC_KEY.length))
|
|
889
898
|
if (ids.length !== 1 || !/^\d{8,20}$/.test(ids[0])) continue
|
|
890
899
|
const who = clientAuth(doc[AUTH_KEY])?.userId
|
|
891
|
-
if (who != null && String(who)
|
|
900
|
+
if (who != null && String(who) !== "") out.push({ uid: String(who), device: ids[0] })
|
|
892
901
|
} catch {
|
|
893
902
|
continue // a sign-in that doesn't decode: the next profile may be
|
|
894
903
|
}
|
|
895
904
|
}
|
|
896
|
-
return
|
|
905
|
+
return out
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// deviceNote says which device a Trae CN check-in went on, for the message
|
|
909
|
+
// of a claim Trae refused (9074) or found already in (9095): the device of
|
|
910
|
+
// a client on this machine signed in as uid, or magpie's own for the
|
|
911
|
+
// account and why — how many client profiles were found, and that none is
|
|
912
|
+
// signed in as it. It names no uid and no device id. A client's device
|
|
913
|
+
// that another profile, signed in as another account, also has is said:
|
|
914
|
+
// both accounts check in on one device, and Trae takes one a day
|
|
915
|
+
// (plugins#436). It only states what was read; it promises no fix.
|
|
916
|
+
async function deviceNote(uid) {
|
|
917
|
+
const all = await clientProfiles()
|
|
918
|
+
const mine = all.find((p) => String(p.uid) === String(uid ?? ""))
|
|
919
|
+
if (mine) {
|
|
920
|
+
const shared = all.some((p) => p.device === mine.device && String(p.uid) !== String(uid))
|
|
921
|
+
return shared
|
|
922
|
+
? "sent on this machine's Trae CN client's device, signed in as this account — another profile here, signed in as another account, has the same device"
|
|
923
|
+
: "sent on this machine's Trae CN client's device, signed in as this account"
|
|
924
|
+
}
|
|
925
|
+
if (!all.length) return "sent on magpie's own device for this account: no Trae CN client profile was found on this machine"
|
|
926
|
+
const n = all.length
|
|
927
|
+
return `sent on magpie's own device for this account: ${n} Trae CN client profile${n === 1 ? " is" : "s are"} on this machine, none signed in as this account`
|
|
897
928
|
}
|
|
898
929
|
|
|
899
930
|
// clientAuth is the client's sign-in envelope, as stored or decoded
|
|
@@ -1448,6 +1479,22 @@ const eventName = (s) =>
|
|
|
1448
1479
|
// Claude (whether prompt_tokens counts them too is judged there, by
|
|
1449
1480
|
// whether it is at least as big); without them every request read as no
|
|
1450
1481
|
// cache hit (01huadalang on magpie's Discord: K3, 53 calls, all 0)
|
|
1482
|
+
//
|
|
1483
|
+
// A count that names no cache at all — Trae's token_usage as
|
|
1484
|
+
// {"prompt_tokens":1717,"completion_tokens":2,"total_tokens":1719}, a shape
|
|
1485
|
+
// plugins#455 captured from Trae — says magpie_cache_unknown, as the Kiro
|
|
1486
|
+
// plugin does: Trae didn't say what its cache read, so magpie shows no hit
|
|
1487
|
+
// rate rather than a 0% Trae never gave. Before, it and a count of 0 both
|
|
1488
|
+
// went on with no cache field, which magpie can only read as a miss, so
|
|
1489
|
+
// since 10-28 every chat showed 0 (yetone/magpie#3979, #3991; plugins#434
|
|
1490
|
+
// says Trae stopped sending cache_read_input_tokens then). A count naming
|
|
1491
|
+
// the cache, a 0 included, is Trae's own word and goes as before.
|
|
1492
|
+
// DeepSeek's prompt_cache_hit_tokens, a bare cached_tokens and the
|
|
1493
|
+
// Responses-style input_tokens_details are read too, the largest of them,
|
|
1494
|
+
// should Trae pass a model's own usage on.
|
|
1495
|
+
const CACHE_NAMED = ["cache_read_input_tokens", "cacheReadInputTokens", "cache_creation_input_tokens", "cacheCreationInputTokens", "prompt_cache_hit_tokens", "cached_tokens"]
|
|
1496
|
+
const namesCache = (u) =>
|
|
1497
|
+
CACHE_NAMED.some((k) => u[k] != null) || u.prompt_tokens_details?.cached_tokens != null || u.input_tokens_details?.cached_tokens != null
|
|
1451
1498
|
function tokensOf(u) {
|
|
1452
1499
|
if (!u || typeof u !== "object") return null
|
|
1453
1500
|
const n = (...v) => Number(v.find((x) => x != null) ?? 0) || 0
|
|
@@ -1455,9 +1502,10 @@ function tokensOf(u) {
|
|
|
1455
1502
|
const c = n(u.completion_tokens, u.output_tokens, u.completionTokens, u.outputTokens)
|
|
1456
1503
|
if (!p && !c) return null
|
|
1457
1504
|
const out = { prompt_tokens: p, completion_tokens: c, total_tokens: n(u.total_tokens, u.totalTokens) || p + c }
|
|
1458
|
-
const read =
|
|
1505
|
+
const read = Math.max(...[u.prompt_tokens_details?.cached_tokens, u.input_tokens_details?.cached_tokens, u.prompt_cache_hit_tokens, u.cached_tokens].map((x) => Number(x) || 0))
|
|
1459
1506
|
const write = n(u.prompt_tokens_details?.cache_write_tokens)
|
|
1460
1507
|
if (read || write) out.prompt_tokens_details = { cached_tokens: read, ...(write ? { cache_write_tokens: write } : {}) }
|
|
1508
|
+
else if (!namesCache(u)) out.magpie_cache_unknown = true
|
|
1461
1509
|
else {
|
|
1462
1510
|
const r = n(u.cache_read_input_tokens, u.cacheReadInputTokens)
|
|
1463
1511
|
const w = n(u.cache_creation_input_tokens, u.cacheCreationInputTokens)
|
|
@@ -2740,9 +2788,12 @@ const MAX = "-max"
|
|
|
2740
2788
|
// same names, as reasoning_effort, so a level an agent picks is sent as Trae
|
|
2741
2789
|
// spells it. OpenCode knows the three as low/high/xhigh, so the map goes both
|
|
2742
2790
|
// ways. Trae's client names two more, medium and max (its bundle's
|
|
2743
|
-
// reasoning_effort.{low,medium,high,extra_high,max}), handed out per model
|
|
2744
|
-
//
|
|
2745
|
-
//
|
|
2791
|
+
// reasoning_effort.{low,medium,high,extra_high,max}), handed out per model,
|
|
2792
|
+
// so they are spelled here too, and offered only for a model whose entry
|
|
2793
|
+
// names them. No list seen yet names either: TRAE CN's app offers DeepSeek
|
|
2794
|
+
// V4.1 Flash 轻/高/极高 (light/high/extra_high) alone, and its "Max" is the
|
|
2795
|
+
// 更大上下文 (Max) 1M-window switch, which is the model id + MAX below, not a
|
|
2796
|
+
// level (#434's screenshot).
|
|
2746
2797
|
//
|
|
2747
2798
|
// An entry without the config takes no level: Trae serves it at its own
|
|
2748
2799
|
// default and nothing is sent. The plugin used to send none for every model,
|
|
@@ -3075,7 +3126,7 @@ const makePlugin = (site) => async ({ client }) => {
|
|
|
3075
3126
|
const ce = errorOf(answer)
|
|
3076
3127
|
if (claim.status === 401 || lapsedCode(ce.code, site)) return { outcome: "failed", message: ce.message || `${a0.name || a0.uid}: Trae's sign-in has expired` }
|
|
3077
3128
|
// 9095: this device is in for today, whoever took it — the day's answer
|
|
3078
|
-
if (String(ce.code) === "9095") return { outcome: "ineligible", message: ce.message || "this device has checked in today" }
|
|
3129
|
+
if (String(ce.code) === "9095") return { outcome: "ineligible", message: `${ce.message || "this device has checked in today"} (${await deviceNote(a0.uid)})` }
|
|
3079
3130
|
// 9074: Trae did not take this claim for this account's device. Its own
|
|
3080
3131
|
// words say the pool is busy, which measurements do not bear out: the
|
|
3081
3132
|
// same account, token and headers were taken on a 16-digit id where a
|
|
@@ -3089,8 +3140,13 @@ const makePlugin = (site) => async ({ client }) => {
|
|
|
3089
3140
|
// it in. Nothing is said of signing one in: it is the one thing seen
|
|
3090
3141
|
// to change a 9074, but it hasn't been tried on a real account, and
|
|
3091
3142
|
// on a claim that already rode a client's device it is what the user
|
|
3092
|
-
// did — the advice would only send them back to do it again.
|
|
3093
|
-
|
|
3143
|
+
// did — the advice would only send them back to do it again. What it
|
|
3144
|
+
// does say, after both 9074 and 9095, is which device the claim went
|
|
3145
|
+
// on and why (deviceNote): moxuanchen (plugins#436) had to run
|
|
3146
|
+
// clientDevice by hand to learn that the account Trae CN took (A, 100
|
|
3147
|
+
// credits) rode the client's device and the one it refused 9074 (B)
|
|
3148
|
+
// rode magpie's own, since both of that machine's profiles were A's.
|
|
3149
|
+
if (String(ce.code) === "9074") return { outcome: "ineligible", message: `${ce.message} (9074) — Trae CN did not take this check-in for this account's device (${await deviceNote(a0.uid)})` }
|
|
3094
3150
|
if (claim.status !== 200 || (ce.code && String(ce.code) !== "0")) return { outcome: "failed", message: ce.message || statusLine(claim.status) }
|
|
3095
3151
|
if (!answer || typeof answer !== "object" || Array.isArray(answer)) return { outcome: "failed", message: `check-in claim: unreadable reply ${claimText.trim().slice(0, 120) || "(empty)"}` }
|
|
3096
3152
|
// a claim's own code 0 is not proof the day is in: Trae answers it for
|
|
@@ -3542,4 +3598,4 @@ const makePlugin = (site) => async ({ client }) => {
|
|
|
3542
3598
|
export const TraeCNAuthPlugin = makePlugin(SITES["trae-cn"])
|
|
3543
3599
|
export const TraeGlobalAuthPlugin = makePlugin(SITES["trae-global"])
|
|
3544
3600
|
|
|
3545
|
-
export const _internal = { ownClient, clientStoragePaths, clientDevice, clientDecode, thisMachine, traeHost, accountOf, HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, nameCall, toolNamed, unwrapped, traeMessages, chatBody, nativeTools, moonshotSchema, credits, dollarUsageOf, whenOf, newDevice, ugDevice, proofOf, SITES, SESSION, conversationID, LEVELS, levelsOf, variantsOf, effortOf }
|
|
3601
|
+
export const _internal = { ownClient, clientStoragePaths, clientDevice, clientProfiles, deviceNote, clientDecode, thisMachine, traeHost, accountOf, HOSTS: SITES["trae-cn"].hosts, MODELS: SITES["trae-cn"].models, TextTools, NativeCalls, looseJSON, glmCall, nameCall, toolNamed, unwrapped, traeMessages, chatBody, nativeTools, moonshotSchema, credits, dollarUsageOf, whenOf, newDevice, ugDevice, proofOf, SITES, SESSION, conversationID, LEVELS, levelsOf, variantsOf, effortOf }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-trae-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.26",
|
|
4
4
|
"description": "OpenCode provider plugins for Trae: Trae CN (trae.cn) and Trae Global (trae.ai), free tier included, with Trae's own browser sign-in",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|