// 生成 Trae 设备身份派生的差分期望值(供 internal/core/channels_trae_derive_test.go 比对)。 // // 为什么要有这个文件:channels.lua 里的 traeDeriveStream 是**手工移植**的 // SHA-256 确定性伪随机流(Lua 5.1 没有 node:crypto)。移植错了不会报错 —— // 上游只会返回风控/失败,看起来像"运气不好"。所以拿参考实现(参考仓库 // vendor/channel-pack/src/trae.ts 的 seededStream/seededDigits)生成期望值, // 再让 Lua 侧算同一组输入做差分。 // // 用法(改了参考或改了移植后都要重跑): // node tools/trae_derive_expected.mjs > internal/core/trae_derive_expected.json // // 注意:这里刻意**逐行照抄**参考的 seededStream,而不是从参考仓库 import —— // 参考是 TS/ESM 且依赖其构建链,直接抄这 20 行更能保证"同一算法"。 import { createHash } from 'node:crypto' /** 照抄 trae.ts:seededStream —— SHA256(utf8(salt:seed) ++ counterBE32) 串联。 */ function seededStream(seed, salt, nbytes) { const prefix = `${salt}:${seed}` const result = [] let counter = 0 while (result.length < nbytes) { const counterBuf = new Uint8Array(4) counterBuf[0] = (counter >> 24) & 0xff counterBuf[1] = (counter >> 16) & 0xff counterBuf[2] = (counter >> 8) & 0xff counterBuf[3] = counter & 0xff const h = createHash('sha256') h.update(prefix, 'utf8') h.update(counterBuf) for (const b of h.digest()) { result.push(b) if (result.length >= nbytes) break } counter++ } return result.slice(0, nbytes) } /** 照抄 trae.ts:seededDigits —— 每字节 % 10,拼成 n 位数字串。 */ function seededDigits(n, seed, salt) { return seededStream(seed, salt, n).map((b) => b % 10).join('') } /** 照抄 trae.ts:deriveDeviceId15。 */ function deriveDeviceId15(userId) { return seededDigits(15, userId, 'devid') } /** 照抄 trae.ts:deriveMarketUserId —— UUID v4 形状。 */ function deriveMarketUserId(userId) { const bs = seededStream(userId, 'market', 16) bs[6] = (bs[6] & 0x0f) | 0x40 bs[8] = (bs[8] & 0x3f) | 0x80 const hex = bs.map((b) => b.toString(16).padStart(2, '0')).join('') return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}` } /** 照抄 trae.ts:deriveSessionId —— 64 位 hex。 */ function deriveSessionId(userId) { return seededStream(userId, 'sess', 32).map((b) => b.toString(16).padStart(2, '0')).join('') } // 覆盖边界:纯数字 / 带连字符 / 空串(空串也要能算,不能崩)/ 多字节 UTF-8 / // 超长(超过一个 SHA-256 块,逼出 counter 递增分支 —— 移植时最易错的地方)。 const ids = ['12345', 'UID-abc', '900000001', 'x', '', '用户1', '1234567890123456789012345'] const out = ids.map((u) => ({ u, devid: deriveDeviceId15(u), market: deriveMarketUserId(u), sess: deriveSessionId(u), })) process.stdout.write(`${JSON.stringify(out, null, 1)}\n`)