@magpie-community/opencode-trae-auth 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +8 -5
  2. package/index.mjs +35 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -32,9 +32,10 @@ agent's `POST https://trae-api-cn.mchost.guru/api/agent/v3/llm_utils_chat`:
32
32
  - **Request:**
33
33
  - The messages go as `{role, content: [{type: "text", text}]}`.
34
34
  - The model goes as `config_name` and `model`.
35
- - The function is `chat_v3`, the classic IDE's. If Trae answers 4001, 4023
36
- or 1005, the plugin tries `solo_work_lite` (SOLO's) once and remembers
37
- which one worked.
35
+ - The function is the one whose model list has the model (`chat_v3`, the
36
+ classic IDE's, first; else `solo_work_lite`, SOLO's Work mode; else
37
+ `solo_agent`, the TRAE agent's). If Trae answers 4001, 4023 or 1005,
38
+ the plugin tries the others once and remembers which one worked.
38
39
  - **Headers:** the IDE's: `Authorization: Cloud-IDE-JWT …`,
39
40
  `X-Cloudide-Token`, `x-app-id`, `x-ide-version`, the device headers and
40
41
  `x-uid`.
@@ -54,8 +55,10 @@ agent's `POST https://trae-api-cn.mchost.guru/api/agent/v3/llm_utils_chat`:
54
55
  - Earlier calls and their results go back in as text.
55
56
  - Images aren't sent.
56
57
 
57
- The model list is the account's own, from `/api/ide/v1/get_detail_param`.
58
- When that can't be read, the plugin uses the models Trae CN's `chat_v3` is
58
+ The model list is the account's own, from `/api/ide/v1/get_detail_param`,
59
+ asked for each of those functions and put together: SOLO and the TRAE
60
+ agent list models `chat_v3` doesn't (deepseek-v4.1-flash is the TRAE
61
+ agent's, `solo_agent`). When neither can be read, the plugin uses the models Trae CN's `chat_v3` is
59
62
  known to serve: GLM-5.2, GLM-5, Kimi K2.6, Qwen 3.7 Plus, DeepSeek V4 Pro
60
63
  and DeepSeek V4 Flash.
61
64
 
package/index.mjs CHANGED
@@ -26,8 +26,10 @@ const SIGN_IN_TIMEOUT = 10 * 60 * 1000
26
26
  const EARLY_MS = 2 * 60 * 1000 // a token this close to its end is renewed before a request
27
27
  const LEAD_MS = 10 * 60 * 1000 // and this close, magpie renews it ahead of time (auth.refresh)
28
28
  const DAY = 24 * 3600 * 1000
29
- // the IDE's chat functions: the classic IDE's agent, then SOLO's
30
- const FUNCTIONS = ["chat_v3", "solo_work_lite"]
29
+ // the IDE's chat functions: the classic IDE's agent, then SOLO's Work
30
+ // mode, then the TRAE agent (solo_agent): Trae CN 3.3.104's agent-type map
31
+ // files its models under "trae", and only that one has deepseek-v4.1-flash
32
+ const FUNCTIONS = ["chat_v3", "solo_work_lite", "solo_agent"]
31
33
 
32
34
  const MODEL = { attachment: false, tool_call: true, reasoning: true, temperature: true, limit: { context: 128_000, output: 32_000 }, modalities: { input: ["text"], output: ["text"] } }
33
35
  // what Trae CN's chat_v3 is known to serve; the live list replaces it
@@ -689,7 +691,7 @@ async function openai(req, it) {
689
691
  return new Response(stream, { status: 200, headers: { "content-type": "text/event-stream", "cache-control": "no-cache" } })
690
692
  }
691
693
 
692
- // a model or function this account's chat_v3 doesn't take: SOLO's may
694
+ // a model or function this account's chat_v3 doesn't take: another may
693
695
  const wrongFunction = (code) => ["4001", "4023", "1005"].includes(String(code))
694
696
 
695
697
  // ---- usage ------------------------------------------------------------------------
@@ -727,6 +729,8 @@ export const TraeCNAuthPlugin = async ({ client }) => {
727
729
  const renewed = new Map()
728
730
  // the chat function that served each account last
729
731
  const fnOf = new Map()
732
+ // the first chat function whose model list has each model
733
+ const listedBy = new Map()
730
734
 
731
735
  const save = async (auth) => {
732
736
  try {
@@ -814,10 +818,10 @@ export const TraeCNAuthPlugin = async ({ client }) => {
814
818
  return out
815
819
  }
816
820
 
817
- // liveModels is the account's model list, as the IDE asks for it
818
- const liveModels = async (a) => {
821
+ // listOf is one function's model list, as the IDE asks for it
822
+ const listOf = async (a, fn) => {
819
823
  const r = await post(a, apiOf(a) + "/api/ide/v1/get_detail_param", {
820
- function: FUNCTIONS[0], config_names: null, need_prompt: false, current_config_info: null, poly_prompt: true, mode_type: null, agent_type: null,
824
+ function: fn, config_names: null, need_prompt: false, current_config_info: null, poly_prompt: true, mode_type: null, agent_type: null,
821
825
  })
822
826
  if (r.status === 401 || lapsedCode(errorOf(r.v).code)) throw new Expired("Trae CN's sign-in has expired; sign in again")
823
827
  if (r.status !== 200) throw new Error(`Trae CN models: ${statusLine(r.status)}`)
@@ -825,6 +829,28 @@ export const TraeCNAuthPlugin = async ({ client }) => {
825
829
  return list.filter((m) => m?.config_name)
826
830
  }
827
831
 
832
+ // liveModels is the account's model list: every chat function's, since
833
+ // SOLO lists models the classic IDE's chat_v3 doesn't (deepseek-v4.1-flash
834
+ // came to the TRAE agent's first, yetone/magpie#681); a model listed by one function
835
+ // only is asked through that one. One function's list failing leaves the
836
+ // others'; all failing is the error.
837
+ const liveModels = async (a) => {
838
+ const got = await Promise.allSettled(FUNCTIONS.map((fn) => listOf(a, fn)))
839
+ const expired = got.find((g) => g.status === "rejected" && g.reason instanceof Expired)
840
+ if (expired) throw expired.reason
841
+ if (got.every((g) => g.status === "rejected")) throw got[0].reason
842
+ const out = new Map()
843
+ got.forEach((g, i) => {
844
+ if (g.status !== "fulfilled") return
845
+ for (const m of g.value) {
846
+ const id = String(m.config_name)
847
+ if (!out.has(id)) out.set(id, { m, fn: FUNCTIONS[i] })
848
+ }
849
+ })
850
+ for (const [id, { fn }] of out) listedBy.set(id, fn)
851
+ return [...out.values()].map((x) => x.m)
852
+ }
853
+
828
854
  const modelOf = (provider, m) => {
829
855
  const id = String(m.config_name)
830
856
  const was = provider.models?.[id] ?? {}
@@ -889,7 +915,9 @@ export const TraeCNAuthPlugin = async ({ client }) => {
889
915
  }
890
916
  const signIn = a.renewed ? "renewed" : "kept"
891
917
  const who = a.uid || a.name
892
- const fns = fnOf.has(who) ? [fnOf.get(who), ...FUNCTIONS.filter((f) => f !== fnOf.get(who))] : FUNCTIONS
918
+ // the function that lists the model first, then the one that
919
+ // served this account last, then the rest
920
+ const fns = [...new Set([listedBy.get(String(req.model)), fnOf.get(who), ...FUNCTIONS].filter(Boolean))]
893
921
  let last
894
922
  for (const fn of fns) {
895
923
  const res = await fetch(apiOf(a) + "/api/agent/v3/llm_utils_chat", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-trae-auth",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "OpenCode provider plugin for Trae CN (trae.cn): your Trae CN account's models, free tier included, with Trae's own browser sign-in",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",