@magpie-community/opencode-trae-auth 0.2.10 → 0.2.12
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 +4 -0
- package/index.mjs +82 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,10 @@ agent's `POST https://trae-api-cn.mchost.guru/api/agent/v3/llm_utils_chat`:
|
|
|
107
107
|
Text under `message`, `delta` or an event with no name is the answer's
|
|
108
108
|
too.
|
|
109
109
|
- `token_usage`, `done` and `error` mark usage, the end and failures.
|
|
110
|
+
What `token_usage` says the prompt cache read and wrote
|
|
111
|
+
(`cache_read_input_tokens`, `cache_creation_input_tokens`) goes on under
|
|
112
|
+
those names, and `reasoning_tokens` as
|
|
113
|
+
`completion_tokens_details.reasoning_tokens`.
|
|
110
114
|
`done`'s `finish_reason` `length`/`max_tokens` is OpenAI's `length`.
|
|
111
115
|
- Queue events are ignored.
|
|
112
116
|
- A `{"reasoning_content": …}` object written into the text is reasoning,
|
package/index.mjs
CHANGED
|
@@ -992,13 +992,35 @@ const eventName = (s) =>
|
|
|
992
992
|
.toLowerCase()
|
|
993
993
|
.replace(/^_|_$/g, "")
|
|
994
994
|
|
|
995
|
-
//
|
|
995
|
+
// tokensOf maps Trae's token counts to OpenAI's. Trae's token_usage event
|
|
996
|
+
// (TRAE SOLO CN's ai-agent, TokenUsageEvent: name, prompt_tokens,
|
|
997
|
+
// completion_tokens, total_tokens, reasoning_tokens,
|
|
998
|
+
// cache_creation_input_tokens, cache_read_input_tokens,
|
|
999
|
+
// prompt_tokens_total, completion_tokens_total) says what the prompt cache
|
|
1000
|
+
// read and wrote under Anthropic's names. They go on under those names,
|
|
1001
|
+
// which magpie reads beside prompt_tokens as it reads a relay's in front of
|
|
1002
|
+
// Claude (whether prompt_tokens counts them too is judged there, by
|
|
1003
|
+
// whether it is at least as big); without them every request read as no
|
|
1004
|
+
// cache hit (01huadalang on magpie's Discord: K3, 53 calls, all 0)
|
|
996
1005
|
function tokensOf(u) {
|
|
997
1006
|
if (!u || typeof u !== "object") return null
|
|
998
|
-
const
|
|
999
|
-
const
|
|
1007
|
+
const n = (...v) => Number(v.find((x) => x != null) ?? 0) || 0
|
|
1008
|
+
const p = n(u.prompt_tokens, u.input_tokens, u.promptTokens, u.inputTokens)
|
|
1009
|
+
const c = n(u.completion_tokens, u.output_tokens, u.completionTokens, u.outputTokens)
|
|
1000
1010
|
if (!p && !c) return null
|
|
1001
|
-
|
|
1011
|
+
const out = { prompt_tokens: p, completion_tokens: c, total_tokens: n(u.total_tokens, u.totalTokens) || p + c }
|
|
1012
|
+
const read = n(u.prompt_tokens_details?.cached_tokens)
|
|
1013
|
+
const write = n(u.prompt_tokens_details?.cache_write_tokens)
|
|
1014
|
+
if (read || write) out.prompt_tokens_details = { cached_tokens: read, ...(write ? { cache_write_tokens: write } : {}) }
|
|
1015
|
+
else {
|
|
1016
|
+
const r = n(u.cache_read_input_tokens, u.cacheReadInputTokens)
|
|
1017
|
+
const w = n(u.cache_creation_input_tokens, u.cacheCreationInputTokens)
|
|
1018
|
+
if (r) out.cache_read_input_tokens = r
|
|
1019
|
+
if (w) out.cache_creation_input_tokens = w
|
|
1020
|
+
}
|
|
1021
|
+
const thought = n(u.reasoning_tokens, u.completion_tokens_details?.reasoning_tokens)
|
|
1022
|
+
if (thought) out.completion_tokens_details = { reasoning_tokens: thought }
|
|
1023
|
+
return out
|
|
1002
1024
|
}
|
|
1003
1025
|
|
|
1004
1026
|
let callSeq = 0
|
|
@@ -1022,6 +1044,7 @@ class TextTools {
|
|
|
1022
1044
|
this.tools = tools
|
|
1023
1045
|
this.sent = "" // the text given out so far, to tell a block in code from a call
|
|
1024
1046
|
this.bad = new Set() // where the waiting block's JSON was tried and not read, not tried again
|
|
1047
|
+
this.mark = 0 // where in sent the text after the last call starts
|
|
1025
1048
|
}
|
|
1026
1049
|
push(s, end = false) {
|
|
1027
1050
|
this.buf += s
|
|
@@ -1044,6 +1067,27 @@ class TextTools {
|
|
|
1044
1067
|
const i = om ? om.index : -1
|
|
1045
1068
|
const d = this.buf.search(DSML_AT)
|
|
1046
1069
|
const r = this.buf.search(REASON_AT)
|
|
1070
|
+
const sm = this.buf.match(STRAY_AT)
|
|
1071
|
+
const s = sm ? sm.index : -1
|
|
1072
|
+
if (s >= 0 && [i, d, r].every((x) => x < 0 || s < x)) {
|
|
1073
|
+
// a close with no call open, with what GLM left before it
|
|
1074
|
+
// (}}</tool_call>, magpie#1945): not the answer, dropped; in code,
|
|
1075
|
+
// shown
|
|
1076
|
+
const head = this.buf.slice(0, s)
|
|
1077
|
+
// only a tail: a close with nothing before it since the last call
|
|
1078
|
+
// (or the start), or with GLM's braces, quotes or > right before
|
|
1079
|
+
// it; a close a sentence names in passing stays
|
|
1080
|
+
const tail = /^[}\]"'>]/.test(sm[0]) || !(this.sent + text + head).slice(this.mark).trim()
|
|
1081
|
+
if (!tail || quoted(this.sent + text + head)) {
|
|
1082
|
+
text += this.buf.slice(0, s + sm[0].length)
|
|
1083
|
+
this.buf = this.buf.slice(s + sm[0].length)
|
|
1084
|
+
continue
|
|
1085
|
+
}
|
|
1086
|
+
text += head
|
|
1087
|
+
this.buf = this.buf.slice(s + sm[0].length)
|
|
1088
|
+
this.after = true
|
|
1089
|
+
continue
|
|
1090
|
+
}
|
|
1047
1091
|
if (r >= 0 && (i < 0 || r < i) && (d < 0 || r < d)) {
|
|
1048
1092
|
const o = objectEnd(this.buf, r)
|
|
1049
1093
|
if (o === -1 && !end) {
|
|
@@ -1095,6 +1139,7 @@ class TextTools {
|
|
|
1095
1139
|
stop = body.length
|
|
1096
1140
|
}
|
|
1097
1141
|
calls.push({ name: inv[1], arguments: dsmlArgs(body.slice(0, stop)) })
|
|
1142
|
+
this.mark = this.sent.length + text.length
|
|
1098
1143
|
this.buf = body.slice(c && c.index === stop ? stop + c[0].length : stop)
|
|
1099
1144
|
this.after = true
|
|
1100
1145
|
continue
|
|
@@ -1136,6 +1181,7 @@ class TextTools {
|
|
|
1136
1181
|
return { text, calls, reasoning, error: b.error }
|
|
1137
1182
|
}
|
|
1138
1183
|
calls.push(b.call)
|
|
1184
|
+
this.mark = this.sent.length + text.length
|
|
1139
1185
|
this.buf = b.rest
|
|
1140
1186
|
this.after = true
|
|
1141
1187
|
}
|
|
@@ -1156,6 +1202,9 @@ class TextTools {
|
|
|
1156
1202
|
const at = [this.buf.search(OPEN_AT), this.buf.search(OPEN_HOLD), this.buf.search(DSML_AT), this.buf.search(REASON_AT)].filter((x) => x >= 0)
|
|
1157
1203
|
let keep = at.length ? this.buf.length - Math.min(...at) : 0
|
|
1158
1204
|
if (!keep) for (let k = Math.min(MARK_MAX - 1, this.buf.length); k > 0; k--) if (MARKS.some((m) => m.startsWith(this.buf.slice(-k)))) { keep = k; break }
|
|
1205
|
+
// and braces, brackets, quotes or a > a close may still come after
|
|
1206
|
+
const sh = this.buf.slice(0, this.buf.length - keep).match(STRAY_HOLD)
|
|
1207
|
+
if (sh) keep = this.buf.length - sh.index
|
|
1159
1208
|
text += this.buf.slice(0, this.buf.length - keep)
|
|
1160
1209
|
this.buf = this.buf.slice(this.buf.length - keep)
|
|
1161
1210
|
}
|
|
@@ -1205,8 +1254,27 @@ class TextTools {
|
|
|
1205
1254
|
const v = callJSON(body.slice(0, o))
|
|
1206
1255
|
// a name alone may have its parameters still to come
|
|
1207
1256
|
if (v && !end && Object.keys(v).length === 1 && "<parameter".startsWith(body.slice(o).replace(/^\s*>?\s*/, ""))) return { wait: true }
|
|
1208
|
-
if (v)
|
|
1209
|
-
|
|
1257
|
+
if (v) {
|
|
1258
|
+
// what follows the object up to the close: GLM leaves braces,
|
|
1259
|
+
// brackets, quotes or a > of its own there (}}</tool_call>,
|
|
1260
|
+
// "></tool_call>, magpie#1945), dropped with the close; held
|
|
1261
|
+
// while only those have come
|
|
1262
|
+
const tail = body.slice(o)
|
|
1263
|
+
const junk = tail.match(TAIL_JUNK)[0]
|
|
1264
|
+
const next = tail.slice(junk.length)
|
|
1265
|
+
const call = { name: String(v.name), arguments: callArgs(v) }
|
|
1266
|
+
if (!end && (!next || ["</tool_call>", "</toolcall>"].some((k) => k.startsWith(next)))) return { wait: true }
|
|
1267
|
+
const [c, n] = closeAt(next)
|
|
1268
|
+
if (c === 0) return { call, rest: next.slice(n) }
|
|
1269
|
+
// after blanks alone, or with no close to come: the call, and
|
|
1270
|
+
// what follows read on as before. Other text before the close
|
|
1271
|
+
// right after the object means the object ended early, at a
|
|
1272
|
+
// quote inside a string left unescaped: that cut is wrong (a
|
|
1273
|
+
// heredoc's command cut short, magpie#1945), so it is read up to
|
|
1274
|
+
// the close below, or not at all
|
|
1275
|
+
if (!/\S/.test(junk) && !/^[^\s<]/.test(next)) return { call, rest: tail }
|
|
1276
|
+
if (close < 0 && end) return { call, rest: tail }
|
|
1277
|
+
} else this.bad.add(":" + o)
|
|
1210
1278
|
}
|
|
1211
1279
|
// not whole, or not JSON as it stands: read up to a close (a file's
|
|
1212
1280
|
// content may hold one), or at the end all of it
|
|
@@ -1241,6 +1309,14 @@ const UNREADABLE = "the model's tool call, written as text, can't be read; it is
|
|
|
1241
1309
|
// also writes it without the underscore, <toolcall> (plugins#38)
|
|
1242
1310
|
const OPEN_AT = /<tool_?call(?:(\s[^<>{}]*)?>|(?=\s*\{))/
|
|
1243
1311
|
const OPEN_HOLD = /<tool_?call(?:\s[^<>{}\n]{0,200})?$/
|
|
1312
|
+
// what GLM leaves between a call's JSON and its close: blanks, closing
|
|
1313
|
+
// braces and brackets, quotes, a > (magpie#1945)
|
|
1314
|
+
const TAIL_JUNK = /^[\s}\]"'>]*/
|
|
1315
|
+
// a close with no call open, and that leftover before it: the tail of a
|
|
1316
|
+
// call Trae's service read itself, or of one already read (magpie#1945)
|
|
1317
|
+
const STRAY_AT = /[}\]"'>]*\s*<\/tool_?calls?>/
|
|
1318
|
+
// the end of text that may be such a tail still being written
|
|
1319
|
+
const STRAY_HOLD = /[}\]"'>][\s}\]"'>]*$/
|
|
1244
1320
|
// closeAt is where the next close is from i, </tool_call> or </toolcall>,
|
|
1245
1321
|
// and its length: [-1, 0] when there is none
|
|
1246
1322
|
function closeAt(s, i = 0) {
|
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.12",
|
|
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",
|