@magpie-community/opencode-trae-auth 0.2.15 → 0.2.16

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 +16 -5
  2. package/index.mjs +31 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -42,9 +42,11 @@ An account of one realm does not sign in to the other: trae.cn's accounts are
42
42
  not trae.ai's.
43
43
 
44
44
  At sign-in the plugin creates the account's device and names it to the
45
- authorization page: `device_id` (19 digits) and `machine_id` (32 hex). Every
45
+ authorization page: `device_id` and `machine_id` (32 hex). Every
46
46
  request then sends that same device. The relays saw requests dropped when the
47
- device was new each time.
47
+ device was new each time. A Trae CN `device_id` is 16 digits since 0.2.16, the
48
+ shape of the ids Trae's device service gives its clients (15 or 16 digits);
49
+ Trae Global's stays 19.
48
50
 
49
51
  ### Enterprise (SaaS) accounts
50
52
 
@@ -314,11 +316,20 @@ answered it 9074 even with the client's headers.
314
316
  macOS `~/Library/Application Support/Trae CN/…`, Windows
315
317
  `%APPDATA%\Trae CN\…` (or `trae-cn`), Linux `~/.config/trae-cn/…`
316
318
  (or `Trae CN`; not seen on a real Linux install).
319
+ - **Every other account's device (0.2.16):** its own device id, in the
320
+ client's shape. Trae CN answered a claim on the plugin's 19-digit id
321
+ 9074, while the same account and token on a 16-digit id were taken
322
+ (magpie-community/plugins#194). An account signed in since 0.2.16 has a
323
+ 16-digit id and checks in on it. One signed in before checks in on a
324
+ 16-digit id made from its own device id and uid: the same id every day,
325
+ never rotated, and no other account's. Its chat requests and renewals
326
+ keep the 19-digit device it signed in with.
317
327
  - **What comes back:** Trae's answer as it is. A 401 or code 1001 marks
318
328
  the account for a new sign-in. Code 9095 means this device has checked
319
- in today. No device id is ever made up, changed or rotated to get round
320
- a 9095 or a 9074, and no account is given a device that isn't its
321
- own client's.
329
+ in today, and that holds for the day. No device id is ever changed or
330
+ rotated to get round a 9095 or a 9074, and no account is given another
331
+ account's device or its own client's device unless the client is signed
332
+ in as that account.
322
333
 
323
334
  Only `api.trae.cn`'s `/trae/api/` pages are sent this way; any other URL
324
335
  that isn't a chat request is still refused (400). Needs 0.1.4 or later.
package/index.mjs CHANGED
@@ -296,8 +296,32 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
296
296
 
297
297
  // device is the machine an account signs in from, made once at sign-in and
298
298
  // named to trae.cn's authorization page, then sent with every request: a
299
- // fresh one each time is what the relays saw requests dropped for.
300
- const newDevice = () => ({ deviceId: digits(19), machineId: randomBytes(16).toString("hex"), ...newDeviceKey() })
299
+ // fresh one each time is what the relays saw requests dropped for. Trae
300
+ // CN's id has the shape its clients' have, 16 digits: the device service
301
+ // gives the IDE a 15- or 16-digit one (iCubeAuthInfo://icube-dc:<id>, see
302
+ // clientDevice), and Trae CN answers a check-in claim on a 19-digit id
303
+ // 9074 where the same account and token on a 16-digit one are taken
304
+ // (magpie-community/plugins#194). Trae's global site keeps the 19 digits
305
+ // it was signed in with: nothing says its ids are shaped otherwise.
306
+ const newDevice = (site) => ({ deviceId: digits(site?.id === "trae-cn" ? 16 : 19), machineId: randomBytes(16).toString("hex"), ...newDeviceKey() })
307
+
308
+ // ugDevice is the device id a Trae CN account's check-in goes on when this
309
+ // machine's client isn't signed in as it: its own when that already has a
310
+ // client's shape (15 or 16 digits), else, for an account signed in before
311
+ // 0.2.16 with 19 digits, a 16-digit id made from its own device id and
312
+ // uid. That one is the same on every check-in of the account, never
313
+ // rotated and never another account's: it is the account's own device in
314
+ // the shape Trae CN takes (#194), not a new device to get past a refusal,
315
+ // and a 9095 (this device is in for today) on it stays the day's answer.
316
+ // The chat's pages and the renewal keep the device the account signed in
317
+ // with.
318
+ function ugDevice(a) {
319
+ const id = String(a.deviceId ?? "")
320
+ if (/^\d{15,16}$/.test(id)) return id
321
+ if (!id && !a.uid) return id
322
+ const n = BigInt("0x" + createHash("sha256").update(`trae-cn-device\0${id}\0${a.uid ?? ""}`).digest("hex").slice(0, 30))
323
+ return String(1_000_000_000_000_000n + (n % 9_000_000_000_000_000n))
324
+ }
301
325
 
302
326
  // newDeviceKey is the device's key pair, an ECDSA P-256 one as the clients
303
327
  // keep (iCubeAuthInfo://icube-dc:<device>), whose public half goes with a
@@ -627,7 +651,7 @@ async function signedIn(q, device, site) {
627
651
  // browserSignIn is the IDE's sign-in: trae.cn's authorization page, back
628
652
  // to a callback on 127.0.0.1 (the page takes no other kind)
629
653
  async function browserSignIn(site) {
630
- const device = newDevice()
654
+ const device = newDevice(site)
631
655
  const trace = randomUUID()
632
656
  let over = false
633
657
  let settle
@@ -2491,6 +2515,9 @@ const makePlugin = (site) => async ({ client }) => {
2491
2515
  // other account keeps its own device; none is given the client's.
2492
2516
  const deviceId = ug && site.id === "trae-cn" ? await clientDevice(a.uid) : undefined
2493
2517
  const own = deviceId ? { deviceId, ...thisMachine() } : undefined
2518
+ // every other Trae CN account checks in on its own device, in the
2519
+ // client's shape (ugDevice, #194)
2520
+ if (ug && site.id === "trae-cn" && !own) a = { ...a, deviceId: ugDevice(a) }
2494
2521
  const res = await fetch(url, {
2495
2522
  method,
2496
2523
  headers: ug ? ugHeaders(a, site, { Accept: "application/json" }, own) : ideHeaders(a, site, { Accept: "application/json" }),
@@ -2861,4 +2888,4 @@ const makePlugin = (site) => async ({ client }) => {
2861
2888
  export const TraeCNAuthPlugin = makePlugin(SITES["trae-cn"])
2862
2889
  export const TraeGlobalAuthPlugin = makePlugin(SITES["trae-global"])
2863
2890
 
2864
- 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, credits, dollarUsageOf, whenOf, newDevice, proofOf, SITES, SESSION, conversationID }
2891
+ 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, credits, dollarUsageOf, whenOf, newDevice, ugDevice, proofOf, SITES, SESSION, conversationID }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-trae-auth",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
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",