dsh-llm-adapters 1.2.0 → 1.2.1
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/DEVELOPMENT.md +26 -9
- package/FAQ.md +8 -2
- package/README.md +30 -17
- package/client.js +103 -14
- package/lib/index.js +553 -98
- package/lib/types/account-store.d.ts +7 -5
- package/lib/types/adapter.d.ts +8 -2
- package/lib/types/auth.d.ts +17 -11
- package/lib/types/index.d.ts +4 -2
- package/lib/types/trae-client.d.ts +52 -0
- package/lib/types/trae-endpoints.d.ts +2 -0
- package/lib/types/trae-login.d.ts +5 -3
- package/lib/types/trae.d.ts +29 -3
- package/lib/types/types.d.ts +19 -1
- package/package.json +3 -2
- package/src/account-store.ts +9 -5
- package/src/adapter.ts +31 -3
- package/src/api.ts +15 -3
- package/src/auth.ts +47 -16
- package/src/index.ts +5 -3
- package/src/trae-client.ts +312 -0
- package/src/trae-endpoints.ts +4 -0
- package/src/trae-login.ts +5 -3
- package/src/trae.ts +191 -3
- package/src/types.ts +20 -1
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path, { dirname, join } from "node:path";
|
|
2
2
|
import { LlmAdapter, attributionHeaders, offloadedImageText, requestImageHandleText } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { existsSync, mkdirSync, mkdtempSync, promises, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { createHash, createHmac, createPrivateKey, generateKeyPairSync, randomBytes, randomUUID, sign } from "node:crypto";
|
|
4
|
+
import { createDecipheriv, createHash, createHmac, createPrivateKey, generateKeyPairSync, randomBytes, randomUUID, sign } from "node:crypto";
|
|
5
5
|
import { homedir, tmpdir } from "node:os";
|
|
6
6
|
import { isVolatile } from "@deepseek-ai/cosmokit";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
@@ -485,6 +485,14 @@ var LocalAdapter = class extends LlmAdapter {
|
|
|
485
485
|
this.invalidateUsage();
|
|
486
486
|
return result;
|
|
487
487
|
}
|
|
488
|
+
/** Import a local client login and return the refreshed account reading. */
|
|
489
|
+
async importClientLogin() {
|
|
490
|
+
if (this.client.importClientLogin === void 0) throw new Error("该供应商不支持复用本机客户端登录");
|
|
491
|
+
await this.client.importClientLogin();
|
|
492
|
+
this.invalidateCatalog();
|
|
493
|
+
this.invalidateUsage();
|
|
494
|
+
return this.usageInfo(true);
|
|
495
|
+
}
|
|
488
496
|
invalidateCatalog() {
|
|
489
497
|
this.client.invalidateModels();
|
|
490
498
|
}
|
|
@@ -494,9 +502,15 @@ var LocalAdapter = class extends LlmAdapter {
|
|
|
494
502
|
/** Login state plus quota, shaped for the settings page. */
|
|
495
503
|
async usageInfo(fresh = false) {
|
|
496
504
|
const auth = await this.client.status();
|
|
505
|
+
const clientLogin = this.client.localClientLogin === void 0 ? void 0 : await this.client.localClientLogin().catch((error) => ({
|
|
506
|
+
available: false,
|
|
507
|
+
signedIn: false,
|
|
508
|
+
error: error instanceof Error ? error.message : String(error)
|
|
509
|
+
}));
|
|
497
510
|
const base = {
|
|
498
511
|
brand: this.brand,
|
|
499
|
-
...auth
|
|
512
|
+
...auth,
|
|
513
|
+
...clientLogin === void 0 ? {} : { clientLogin }
|
|
500
514
|
};
|
|
501
515
|
const checkin = this.client.checkinStatus === void 0 ? void 0 : await this.client.checkinStatus().catch(() => void 0);
|
|
502
516
|
const withCheckin = checkin === void 0 ? base : {
|
|
@@ -720,45 +734,48 @@ function defaultRoot() {
|
|
|
720
734
|
const base = dshHome !== void 0 && dshHome !== "" ? dshHome : path.join(homedir(), ".dsh");
|
|
721
735
|
return path.join(base, "llm-adapters");
|
|
722
736
|
}
|
|
723
|
-
function record$
|
|
737
|
+
function record$5(value) {
|
|
724
738
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
725
739
|
}
|
|
726
|
-
function text$
|
|
740
|
+
function text$5(value) {
|
|
727
741
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
728
742
|
}
|
|
729
743
|
function number(value) {
|
|
730
744
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
731
745
|
}
|
|
732
746
|
function traeRecord(value, region) {
|
|
733
|
-
const raw = record$
|
|
734
|
-
const accessToken = text$
|
|
747
|
+
const raw = record$5(value);
|
|
748
|
+
const accessToken = text$5(raw?.accessToken);
|
|
735
749
|
if (raw === void 0 || accessToken === void 0) return void 0;
|
|
750
|
+
const deviceId = text$5(raw.deviceId);
|
|
751
|
+
const source = raw.source === "web" || raw.source === "client" ? raw.source : void 0;
|
|
736
752
|
return {
|
|
737
753
|
accessToken,
|
|
738
754
|
region,
|
|
739
|
-
|
|
740
|
-
|
|
755
|
+
...source === void 0 ? {} : { source },
|
|
756
|
+
deviceId: deviceId ?? `web-${randomBytes(8).toString("hex")}`,
|
|
757
|
+
machineId: text$5(raw.machineId) ?? `web-${randomBytes(16).toString("hex")}`,
|
|
741
758
|
loginAt: number(raw.loginAt) ?? Date.now(),
|
|
742
|
-
...text$
|
|
743
|
-
...text$
|
|
759
|
+
...text$5(raw.userId) === void 0 ? {} : { userId: text$5(raw.userId) },
|
|
760
|
+
...text$5(raw.name) === void 0 ? {} : { name: text$5(raw.name) },
|
|
744
761
|
...number(raw.expiresAt) === void 0 ? {} : { expiresAt: number(raw.expiresAt) },
|
|
745
|
-
...text$
|
|
762
|
+
...text$5(raw.cookies) === void 0 ? {} : { cookies: text$5(raw.cookies) },
|
|
746
763
|
...number(raw.sessionExpiresAt) === void 0 ? {} : { sessionExpiresAt: number(raw.sessionExpiresAt) }
|
|
747
764
|
};
|
|
748
765
|
}
|
|
749
766
|
function codeartsRecord(value) {
|
|
750
|
-
const raw = record$
|
|
751
|
-
const accessKeyId = text$
|
|
752
|
-
const secretAccessKey = text$
|
|
753
|
-
const securityToken = text$
|
|
767
|
+
const raw = record$5(value);
|
|
768
|
+
const accessKeyId = text$5(raw?.accessKeyId);
|
|
769
|
+
const secretAccessKey = text$5(raw?.secretAccessKey);
|
|
770
|
+
const securityToken = text$5(raw?.securityToken);
|
|
754
771
|
if (raw === void 0 || accessKeyId === void 0 || secretAccessKey === void 0 || securityToken === void 0) return;
|
|
755
|
-
const jwk = record$
|
|
756
|
-
const key = jwk !== void 0 && text$
|
|
772
|
+
const jwk = record$5(raw.dpopPrivateJwk);
|
|
773
|
+
const key = jwk !== void 0 && text$5(jwk.x) !== void 0 && text$5(jwk.y) !== void 0 && text$5(jwk.d) !== void 0 ? {
|
|
757
774
|
kty: "EC",
|
|
758
775
|
crv: "P-256",
|
|
759
|
-
x: text$
|
|
760
|
-
y: text$
|
|
761
|
-
d: text$
|
|
776
|
+
x: text$5(jwk.x),
|
|
777
|
+
y: text$5(jwk.y),
|
|
778
|
+
d: text$5(jwk.d)
|
|
762
779
|
} : void 0;
|
|
763
780
|
const noBenefit = Array.isArray(raw.noBenefit) ? raw.noBenefit.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim()) : [];
|
|
764
781
|
return {
|
|
@@ -767,10 +784,10 @@ function codeartsRecord(value) {
|
|
|
767
784
|
securityToken,
|
|
768
785
|
loginAt: number(raw.loginAt) ?? Date.now(),
|
|
769
786
|
...number(raw.expiresAt) === void 0 ? {} : { expiresAt: number(raw.expiresAt) },
|
|
770
|
-
...text$
|
|
771
|
-
...text$
|
|
787
|
+
...text$5(raw.refreshToken) === void 0 ? {} : { refreshToken: text$5(raw.refreshToken) },
|
|
788
|
+
...text$5(raw.codeVerifier) === void 0 ? {} : { codeVerifier: text$5(raw.codeVerifier) },
|
|
772
789
|
...key === void 0 ? {} : { dpopPrivateJwk: key },
|
|
773
|
-
...text$
|
|
790
|
+
...text$5(raw.userName) === void 0 ? {} : { userName: text$5(raw.userName) },
|
|
774
791
|
...noBenefit.length === 0 ? {} : { noBenefit }
|
|
775
792
|
};
|
|
776
793
|
}
|
|
@@ -794,30 +811,30 @@ var AccountStore = class {
|
|
|
794
811
|
} catch {
|
|
795
812
|
return {};
|
|
796
813
|
}
|
|
797
|
-
const raw = record$
|
|
814
|
+
const raw = record$5(parsed);
|
|
798
815
|
if (raw === void 0) return {};
|
|
799
816
|
const out = {};
|
|
800
|
-
const loomy = record$
|
|
801
|
-
const loomySession = text$
|
|
817
|
+
const loomy = record$5(raw.loomy);
|
|
818
|
+
const loomySession = text$5(loomy?.session);
|
|
802
819
|
if (loomySession !== void 0) out.loomy = {
|
|
803
820
|
session: loomySession,
|
|
804
|
-
deviceId: text$
|
|
821
|
+
deviceId: text$5(loomy?.deviceId) ?? `web-${randomBytes(8).toString("hex")}`,
|
|
805
822
|
loginAt: number(loomy?.loginAt) ?? Date.now(),
|
|
806
823
|
expiresAt: number(loomy?.expiresAt) ?? Date.now(),
|
|
807
|
-
...text$
|
|
808
|
-
...text$
|
|
809
|
-
...text$
|
|
824
|
+
...text$5(loomy?.userId) === void 0 ? {} : { userId: text$5(loomy?.userId) },
|
|
825
|
+
...text$5(loomy?.phone) === void 0 ? {} : { phone: text$5(loomy?.phone) },
|
|
826
|
+
...text$5(loomy?.name) === void 0 ? {} : { name: text$5(loomy?.name) }
|
|
810
827
|
};
|
|
811
|
-
const raccoon = record$
|
|
812
|
-
const access = text$
|
|
828
|
+
const raccoon = record$5(raw.raccoon);
|
|
829
|
+
const access = text$5(raccoon?.accessToken);
|
|
813
830
|
if (access !== void 0) out.raccoon = {
|
|
814
831
|
accessToken: access,
|
|
815
832
|
loginAt: number(raccoon?.loginAt) ?? Date.now(),
|
|
816
|
-
...text$
|
|
817
|
-
...text$
|
|
818
|
-
...text$
|
|
833
|
+
...text$5(raccoon?.phone) === void 0 ? {} : { phone: text$5(raccoon?.phone) },
|
|
834
|
+
...text$5(raccoon?.name) === void 0 ? {} : { name: text$5(raccoon?.name) },
|
|
835
|
+
...text$5(raccoon?.ownerType) === void 0 ? {} : { ownerType: text$5(raccoon?.ownerType) }
|
|
819
836
|
};
|
|
820
|
-
const trae = record$
|
|
837
|
+
const trae = record$5(raw.trae);
|
|
821
838
|
if (trae !== void 0) {
|
|
822
839
|
const cn = traeRecord(trae.cn, "cn");
|
|
823
840
|
const global = traeRecord(trae.global, "global");
|
|
@@ -1051,6 +1068,14 @@ async function handleAccount(req, res, adapters) {
|
|
|
1051
1068
|
});
|
|
1052
1069
|
return;
|
|
1053
1070
|
}
|
|
1071
|
+
if (action === "client-login") {
|
|
1072
|
+
try {
|
|
1073
|
+
send(res, 200, await adapter.importClientLogin());
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
send(res, 400, { error: error instanceof Error ? error.message : String(error) });
|
|
1076
|
+
}
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1054
1079
|
if (action === "checkin") {
|
|
1055
1080
|
try {
|
|
1056
1081
|
send(res, 200, {
|
|
@@ -1062,7 +1087,7 @@ async function handleAccount(req, res, adapters) {
|
|
|
1062
1087
|
}
|
|
1063
1088
|
return;
|
|
1064
1089
|
}
|
|
1065
|
-
send(res, 400, { error: "action 只接受 browser、browser-cancel、checkin 或 logout" });
|
|
1090
|
+
send(res, 400, { error: "action 只接受 browser、browser-cancel、client-login、checkin 或 logout" });
|
|
1066
1091
|
}
|
|
1067
1092
|
async function handleApi(req, res, adapters, openRouter) {
|
|
1068
1093
|
try {
|
|
@@ -2080,7 +2105,8 @@ const TRAE_REGION_PROFILES = {
|
|
|
2080
2105
|
payBase: "https://api.trae.cn",
|
|
2081
2106
|
timezone: "Asia/Shanghai",
|
|
2082
2107
|
language: "zh-cn",
|
|
2083
|
-
usageV2: true
|
|
2108
|
+
usageV2: true,
|
|
2109
|
+
checkin: true
|
|
2084
2110
|
},
|
|
2085
2111
|
global: {
|
|
2086
2112
|
id: "global",
|
|
@@ -2092,7 +2118,8 @@ const TRAE_REGION_PROFILES = {
|
|
|
2092
2118
|
payBase: "https://growsg-normal.trae.ai",
|
|
2093
2119
|
timezone: "Asia/Singapore",
|
|
2094
2120
|
language: "en",
|
|
2095
|
-
usageV2: false
|
|
2121
|
+
usageV2: false,
|
|
2122
|
+
checkin: false
|
|
2096
2123
|
}
|
|
2097
2124
|
};
|
|
2098
2125
|
/** The app id Trae's own clients send; not account-specific. */
|
|
@@ -2298,12 +2325,13 @@ function mergeSetCookies(current, headers) {
|
|
|
2298
2325
|
return [...jar.entries()].map(([name, value]) => `${name}=${value}`).join("; ");
|
|
2299
2326
|
}
|
|
2300
2327
|
/**
|
|
2301
|
-
* Trae's
|
|
2328
|
+
* Trae's login, one per region.
|
|
2302
2329
|
*
|
|
2303
|
-
*
|
|
2304
|
-
*
|
|
2305
|
-
*
|
|
2306
|
-
*
|
|
2330
|
+
* A web login stores the `Cloud-IDE-JWT` from the website plus the cookies
|
|
2331
|
+
* behind it, so the short token can be renewed silently until the website
|
|
2332
|
+
* session dies. A client login stores the token the desktop client already
|
|
2333
|
+
* uses; that token has no website session, and the client itself refreshes it.
|
|
2334
|
+
* Only the client source carries the device id the check-in route accepts.
|
|
2307
2335
|
*/
|
|
2308
2336
|
var TraeAuth = class {
|
|
2309
2337
|
store;
|
|
@@ -2324,18 +2352,32 @@ var TraeAuth = class {
|
|
|
2324
2352
|
const stored = await this.store.trae(region);
|
|
2325
2353
|
if (stored === void 0) return {
|
|
2326
2354
|
signedIn: false,
|
|
2327
|
-
error: `${traeLabel(region)}
|
|
2355
|
+
error: `${traeLabel(region)}尚未登录。可以点「打开浏览器登录」,或复用本机已登录的 Trae 客户端。`
|
|
2328
2356
|
};
|
|
2357
|
+
const source = stored.source ?? "web";
|
|
2329
2358
|
const now = Date.now();
|
|
2330
2359
|
const { token, session } = traeExpiries(stored);
|
|
2331
2360
|
const tokenExpired = token !== void 0 && token <= now + TRAE_EXPIRY_MARGIN_MS;
|
|
2361
|
+
const account = traeAccount(stored);
|
|
2362
|
+
if (source === "client") {
|
|
2363
|
+
const signedIn = !tokenExpired;
|
|
2364
|
+
return {
|
|
2365
|
+
signedIn,
|
|
2366
|
+
loginSource: "client",
|
|
2367
|
+
loginAt: stored.loginAt,
|
|
2368
|
+
...token === void 0 ? {} : { expiresAt: token },
|
|
2369
|
+
...!signedIn ? { possiblyExpired: true } : {},
|
|
2370
|
+
...Object.keys(account).length === 0 ? {} : { account },
|
|
2371
|
+
...!signedIn ? { error: `${traeLabel(region)}客户端登录令牌已过期,请先在 Trae 客户端重新登录,再点「复用客户端登录态」。` } : {}
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2332
2374
|
const sessionExpired = session !== void 0 && session <= now + TRAE_EXPIRY_MARGIN_MS;
|
|
2333
2375
|
const refreshable = stored.cookies !== void 0 && !sessionExpired;
|
|
2334
2376
|
const signedIn = !tokenExpired || refreshable;
|
|
2335
|
-
const account = traeAccount(stored);
|
|
2336
2377
|
const expiresAt = refreshable ? session ?? token : token;
|
|
2337
2378
|
return {
|
|
2338
2379
|
signedIn,
|
|
2380
|
+
loginSource: "web",
|
|
2339
2381
|
loginAt: stored.loginAt,
|
|
2340
2382
|
...expiresAt === void 0 ? {} : { expiresAt },
|
|
2341
2383
|
...!signedIn ? { possiblyExpired: true } : {},
|
|
@@ -2346,15 +2388,21 @@ var TraeAuth = class {
|
|
|
2346
2388
|
/**
|
|
2347
2389
|
* The bearer token, or an actionable error when there is none.
|
|
2348
2390
|
*
|
|
2349
|
-
* A token near its expiry is renewed first; `force` means the gateway
|
|
2350
|
-
* rejected it, so the renewal is attempted even if the clock
|
|
2391
|
+
* A web token near its expiry is renewed first; `force` means the gateway
|
|
2392
|
+
* already rejected it, so the renewal is attempted even if the clock
|
|
2393
|
+
* disagrees. A client token is returned until its own expiry and then asks
|
|
2394
|
+
* for a fresh import, because only the client can refresh it.
|
|
2351
2395
|
*/
|
|
2352
2396
|
async accessToken(force = false) {
|
|
2353
2397
|
const region = this.region();
|
|
2354
2398
|
const stored = await this.store.trae(region);
|
|
2355
|
-
if (stored === void 0) throw new Error(`${traeLabel(region)}
|
|
2399
|
+
if (stored === void 0) throw new Error(`${traeLabel(region)}尚未登录。可以点「打开浏览器登录」,或复用本机已登录的 Trae 客户端。`);
|
|
2356
2400
|
const { token, session } = traeExpiries(stored);
|
|
2357
2401
|
const expired = token !== void 0 && token <= Date.now() + TRAE_EXPIRY_MARGIN_MS;
|
|
2402
|
+
if (stored.source === "client") {
|
|
2403
|
+
if (!force && !expired) return stored.accessToken;
|
|
2404
|
+
throw new Error(`${traeLabel(region)}客户端登录令牌已${force ? "失效" : "过期"},请先在 Trae 客户端重新登录,再点「复用客户端登录态」。`);
|
|
2405
|
+
}
|
|
2358
2406
|
if (!force && !expired) return stored.accessToken;
|
|
2359
2407
|
if (stored.cookies === void 0) throw new Error(`${traeLabel(region)}登录已${force ? "失效" : "过期"},请在设置页重新登录。`);
|
|
2360
2408
|
if (session !== void 0 && session <= Date.now() + TRAE_EXPIRY_MARGIN_MS) throw new Error(`${traeLabel(region)}网页会话已过期,请重新登录。`);
|
|
@@ -2366,7 +2414,7 @@ var TraeAuth = class {
|
|
|
2366
2414
|
if (existing !== void 0) return existing;
|
|
2367
2415
|
const task = (async () => {
|
|
2368
2416
|
const stored = await this.store.trae(region);
|
|
2369
|
-
if (stored === void 0 || stored.cookies === void 0) throw new Error(`${traeLabel(region)}登录已失效,请在设置页重新登录。`);
|
|
2417
|
+
if (stored === void 0 || stored.source === "client" || stored.cookies === void 0) throw new Error(`${traeLabel(region)}登录已失效,请在设置页重新登录。`);
|
|
2370
2418
|
const profile = TRAE_REGION_PROFILES[region];
|
|
2371
2419
|
let response;
|
|
2372
2420
|
try {
|
|
@@ -2425,7 +2473,7 @@ var TraeAuth = class {
|
|
|
2425
2473
|
async identity() {
|
|
2426
2474
|
const region = this.region();
|
|
2427
2475
|
const stored = await this.store.trae(region);
|
|
2428
|
-
if (stored === void 0) throw new Error(`${traeLabel(region)}
|
|
2476
|
+
if (stored === void 0) throw new Error(`${traeLabel(region)}尚未登录。可以点「打开浏览器登录」,或复用本机已登录的 Trae 客户端。`);
|
|
2429
2477
|
return {
|
|
2430
2478
|
deviceId: stored.deviceId,
|
|
2431
2479
|
machineId: stored.machineId,
|
|
@@ -2668,11 +2716,11 @@ function resolveConfig(config) {
|
|
|
2668
2716
|
raccoonCheckinMinutes: live.raccoonCheckinMinutes()
|
|
2669
2717
|
};
|
|
2670
2718
|
}
|
|
2671
|
-
function text$
|
|
2719
|
+
function text$4(value) {
|
|
2672
2720
|
return typeof value === "string" ? value.trim() : "";
|
|
2673
2721
|
}
|
|
2674
2722
|
function nonEmpty(value, fallback) {
|
|
2675
|
-
const valueText = text$
|
|
2723
|
+
const valueText = text$4(value);
|
|
2676
2724
|
return valueText.length > 0 ? valueText : fallback;
|
|
2677
2725
|
}
|
|
2678
2726
|
function positive(value, fallback) {
|
|
@@ -3499,10 +3547,10 @@ function codeartsEfforts(live) {
|
|
|
3499
3547
|
}
|
|
3500
3548
|
/** Every level a CodeArts model can get: the fallback when the router is silent. */
|
|
3501
3549
|
const CODEARTS_EFFORTS = codeartsEfforts();
|
|
3502
|
-
function record$
|
|
3550
|
+
function record$4(value) {
|
|
3503
3551
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
3504
3552
|
}
|
|
3505
|
-
function text$
|
|
3553
|
+
function text$3(value) {
|
|
3506
3554
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
3507
3555
|
}
|
|
3508
3556
|
function num$1(value) {
|
|
@@ -3521,7 +3569,7 @@ function unwrapEnvelope(payload) {
|
|
|
3521
3569
|
const code = payload.code;
|
|
3522
3570
|
if (typeof code !== "number") return payload;
|
|
3523
3571
|
if (code !== 0) return void 0;
|
|
3524
|
-
return record$
|
|
3572
|
+
return record$4(payload.data);
|
|
3525
3573
|
}
|
|
3526
3574
|
/** `deepseek-v4-flash-0731` → `deepseek-v4-flash`; chat only knows the latter. */
|
|
3527
3575
|
function normalizeCodeArtsModelId(id) {
|
|
@@ -3537,21 +3585,21 @@ function normalizeCodeArtsModelId(id) {
|
|
|
3537
3585
|
* how the upstream behaves when a model has no slots at all.
|
|
3538
3586
|
*/
|
|
3539
3587
|
function pickActiveCredit(credit, slots, nowMs = Date.now()) {
|
|
3540
|
-
const entries = credit.map((entry) => record$
|
|
3588
|
+
const entries = credit.map((entry) => record$4(entry)).filter((entry) => entry !== void 0);
|
|
3541
3589
|
if (entries.length === 0) return void 0;
|
|
3542
|
-
const table = record$
|
|
3590
|
+
const table = record$4(slots);
|
|
3543
3591
|
if (table === void 0) return entries[0];
|
|
3544
3592
|
for (const entry of entries) {
|
|
3545
|
-
const slotId = text$
|
|
3593
|
+
const slotId = text$3(entry.time_slot_id);
|
|
3546
3594
|
if (slotId === void 0) continue;
|
|
3547
|
-
const slot = record$
|
|
3595
|
+
const slot = record$4(table[slotId]);
|
|
3548
3596
|
if (slot === void 0) continue;
|
|
3549
3597
|
if (slotActive(slot, nowMs)) return entry;
|
|
3550
3598
|
}
|
|
3551
3599
|
return entries[0];
|
|
3552
3600
|
}
|
|
3553
3601
|
function slotActive(slot, nowMs) {
|
|
3554
|
-
const schedule = text$
|
|
3602
|
+
const schedule = text$3(slot.schedule);
|
|
3555
3603
|
if (schedule === void 0) return false;
|
|
3556
3604
|
const parts = schedule.split(/\s+/);
|
|
3557
3605
|
if (parts.length < 3) return false;
|
|
@@ -3570,18 +3618,18 @@ function parseCodeArtsRoster(items, nowMs = Date.now()) {
|
|
|
3570
3618
|
const seen = /* @__PURE__ */ new Set();
|
|
3571
3619
|
const out = [];
|
|
3572
3620
|
for (const item of items) {
|
|
3573
|
-
const entry = record$
|
|
3621
|
+
const entry = record$4(item);
|
|
3574
3622
|
if (entry === void 0) continue;
|
|
3575
|
-
const rawId = text$
|
|
3623
|
+
const rawId = text$3(entry.model_id);
|
|
3576
3624
|
if (rawId === void 0) continue;
|
|
3577
3625
|
const id = normalizeCodeArtsModelId(rawId);
|
|
3578
3626
|
const key = id.toLowerCase();
|
|
3579
3627
|
if (seen.has(key)) continue;
|
|
3580
3628
|
seen.add(key);
|
|
3581
|
-
const rateLabel = text$
|
|
3629
|
+
const rateLabel = text$3(pickActiveCredit(Array.isArray(entry.credit) ? entry.credit : [], entry.time_slots, nowMs)?.ratio_display);
|
|
3582
3630
|
out.push({
|
|
3583
3631
|
id,
|
|
3584
|
-
name: text$
|
|
3632
|
+
name: text$3(entry.model_name) ?? id,
|
|
3585
3633
|
...rateLabel === void 0 ? {} : { rateLabel },
|
|
3586
3634
|
...entry.supports_images === true ? { images: true } : {}
|
|
3587
3635
|
});
|
|
@@ -3762,8 +3810,8 @@ var CodeArtsProvider = class {
|
|
|
3762
3810
|
const now = Date.now();
|
|
3763
3811
|
const gateway = await this.signedGet(credential, CODEARTS_GATEWAY_CONFIG_URL);
|
|
3764
3812
|
const builtin = await this.signedGet(credential, `${CODEARTS_SNAP_BASE}${CODEARTS_MODEL_BUILTIN_PATH}`);
|
|
3765
|
-
const gatewayRows = parseCodeArtsRoster(record$
|
|
3766
|
-
const builtinRows = parseCodeArtsRoster(record$
|
|
3813
|
+
const gatewayRows = parseCodeArtsRoster(record$4(record$4(gateway)?.result)?.models, now);
|
|
3814
|
+
const builtinRows = parseCodeArtsRoster(record$4(builtin)?.builtinModels, now);
|
|
3767
3815
|
if (gatewayRows.length === 0 && builtinRows.length === 0) throw new Error("CodeArts 没有返回任何模型(签名、凭据或网关异常)");
|
|
3768
3816
|
if (gatewayRows.length > 0) this.benefitModels = new Set(gatewayRows.map((row) => row.id));
|
|
3769
3817
|
const textOnly = this.options.textOnly();
|
|
@@ -3844,7 +3892,7 @@ var CodeArtsProvider = class {
|
|
|
3844
3892
|
}
|
|
3845
3893
|
async readQuota() {
|
|
3846
3894
|
const credential = await this.credential();
|
|
3847
|
-
const payload = record$
|
|
3895
|
+
const payload = record$4(await this.signedGet(credential, `${CODEARTS_SNAP_BASE}${CODEARTS_PACKAGE_INFO_PATH}`));
|
|
3848
3896
|
if (payload === void 0) throw new Error("CodeArts 没有返回账户信息");
|
|
3849
3897
|
const info = parseCodeArtsPackage(payload);
|
|
3850
3898
|
const total = info.credit.amount ?? info.credit.remaining;
|
|
@@ -3936,26 +3984,26 @@ var CodeArtsProvider = class {
|
|
|
3936
3984
|
signal: AbortSignal.timeout(this.options.timeoutMs())
|
|
3937
3985
|
});
|
|
3938
3986
|
if (!response.ok) return void 0;
|
|
3939
|
-
return unwrapEnvelope(record$
|
|
3987
|
+
return unwrapEnvelope(record$4(await response.json()));
|
|
3940
3988
|
} catch {
|
|
3941
3989
|
return;
|
|
3942
3990
|
}
|
|
3943
3991
|
}
|
|
3944
3992
|
async accountInfo(credential) {
|
|
3945
|
-
const payload = record$
|
|
3993
|
+
const payload = record$4(await this.signedGet(credential, `${CODEARTS_SNAP_BASE}${CODEARTS_PACKAGE_INFO_PATH}`));
|
|
3946
3994
|
return payload === void 0 ? void 0 : parseCodeArtsPackage(payload);
|
|
3947
3995
|
}
|
|
3948
3996
|
async activities(credential) {
|
|
3949
3997
|
const url = `${CODEARTS_SNAP_BASE}${CODEARTS_OPS_DELIVERY_PATH}?channel=IDE`;
|
|
3950
|
-
const items = unwrapEnvelope(record$
|
|
3998
|
+
const items = unwrapEnvelope(record$4(await this.signedGet(credential, url)))?.items;
|
|
3951
3999
|
if (!Array.isArray(items)) return void 0;
|
|
3952
4000
|
return items.map((item) => {
|
|
3953
|
-
const entry = record$
|
|
4001
|
+
const entry = record$4(item) ?? {};
|
|
3954
4002
|
return {
|
|
3955
4003
|
campaignId: String(entry.campaignId ?? ""),
|
|
3956
|
-
type: text$
|
|
4004
|
+
type: text$3(entry.type) ?? "",
|
|
3957
4005
|
claimable: entry.claimable === true || String(entry.claimable).toLowerCase() === "true",
|
|
3958
|
-
status: text$
|
|
4006
|
+
status: text$3(entry.status) ?? "",
|
|
3959
4007
|
amount: num$1(entry.benefitAmount) ?? num$1(entry.amount) ?? num$1(entry.creditAmount) ?? 0
|
|
3960
4008
|
};
|
|
3961
4009
|
});
|
|
@@ -3971,7 +4019,7 @@ var CodeArtsProvider = class {
|
|
|
3971
4019
|
const cache = /* @__PURE__ */ new Map();
|
|
3972
4020
|
const file = this.imageCacheFile();
|
|
3973
4021
|
if (file !== void 0) try {
|
|
3974
|
-
const entries = record$
|
|
4022
|
+
const entries = record$4(record$4(JSON.parse(await promises.readFile(file, "utf8")))?.entries);
|
|
3975
4023
|
for (const [key, value] of Object.entries(entries ?? {})) if (typeof value === "string" && value.length > 0) cache.set(key, value);
|
|
3976
4024
|
} catch {}
|
|
3977
4025
|
this.imageCache = cache;
|
|
@@ -4024,7 +4072,7 @@ var CodeArtsProvider = class {
|
|
|
4024
4072
|
for (let index = 0; index < parts.length; index++) {
|
|
4025
4073
|
const part = parts[index];
|
|
4026
4074
|
if (part.type !== "image_url") continue;
|
|
4027
|
-
const url = record$
|
|
4075
|
+
const url = record$4(part.image_url)?.url;
|
|
4028
4076
|
if (typeof url !== "string" || url.length === 0) continue;
|
|
4029
4077
|
const key = sha256Hex(url);
|
|
4030
4078
|
let transcript = cache.get(key);
|
|
@@ -4088,9 +4136,9 @@ var CodeArtsProvider = class {
|
|
|
4088
4136
|
for await (const chunk of consumeOpenAiStream(response, {
|
|
4089
4137
|
...signal === void 0 ? {} : { signal },
|
|
4090
4138
|
onFrame: (frame) => {
|
|
4091
|
-
const code = text$
|
|
4139
|
+
const code = text$3(frame.error_code);
|
|
4092
4140
|
if (code === void 0) return;
|
|
4093
|
-
const message = text$
|
|
4141
|
+
const message = text$3(frame.error_msg) ?? code;
|
|
4094
4142
|
if (QUEUE_MARKERS.some((marker) => code.includes(marker))) throw new CodeArtsRetry("queue", message);
|
|
4095
4143
|
throw new ProviderHttpError(message, 502);
|
|
4096
4144
|
}
|
|
@@ -4175,9 +4223,9 @@ var CodeArtsProvider = class {
|
|
|
4175
4223
|
for await (const chunk of consumeOpenAiStream(response, {
|
|
4176
4224
|
...options.signal === void 0 ? {} : { signal: options.signal },
|
|
4177
4225
|
onFrame: (frame) => {
|
|
4178
|
-
const code = text$
|
|
4226
|
+
const code = text$3(frame.error_code);
|
|
4179
4227
|
if (code === void 0) return;
|
|
4180
|
-
const message = text$
|
|
4228
|
+
const message = text$3(frame.error_msg) ?? code;
|
|
4181
4229
|
if (QUEUE_MARKERS.some((marker) => code.includes(marker))) throw new CodeArtsRetry("queue", message);
|
|
4182
4230
|
if (code.includes("InferHub.4004.200")) throw new CodeArtsRetry("benefit", message);
|
|
4183
4231
|
throw new ProviderHttpError(message, 502);
|
|
@@ -4275,8 +4323,8 @@ function clampMaxTokens(value) {
|
|
|
4275
4323
|
* for them would claim credits expire when they do not.
|
|
4276
4324
|
*/
|
|
4277
4325
|
function parseCodeArtsPackage(payload) {
|
|
4278
|
-
const pkg = record$
|
|
4279
|
-
const endDate = text$
|
|
4326
|
+
const pkg = record$4(payload.package) ?? {};
|
|
4327
|
+
const endDate = text$3(payload.end_date);
|
|
4280
4328
|
const endMs = endDate === void 0 ? void 0 : Date.parse(endDate);
|
|
4281
4329
|
const labels = {
|
|
4282
4330
|
usageBasicPackageCredit: "基础积分包",
|
|
@@ -4286,9 +4334,9 @@ function parseCodeArtsPackage(payload) {
|
|
|
4286
4334
|
const packs = [];
|
|
4287
4335
|
let summary;
|
|
4288
4336
|
for (const item of Array.isArray(payload.metrics) ? payload.metrics : []) {
|
|
4289
|
-
const metric = record$
|
|
4337
|
+
const metric = record$4(item);
|
|
4290
4338
|
if (metric === void 0) continue;
|
|
4291
|
-
const name = text$
|
|
4339
|
+
const name = text$3(metric.name) ?? "";
|
|
4292
4340
|
const amount = num$1(metric.package_credit_amount);
|
|
4293
4341
|
const used = num$1(metric.package_credit_used);
|
|
4294
4342
|
const remain = num$1(metric.package_credit_remain);
|
|
@@ -4321,8 +4369,8 @@ function parseCodeArtsPackage(payload) {
|
|
|
4321
4369
|
});
|
|
4322
4370
|
return {
|
|
4323
4371
|
isCreditPackage: pkg.is_credit_package === true || String(pkg.is_credit_package).toLowerCase() === "true",
|
|
4324
|
-
packageName: text$
|
|
4325
|
-
packageStatus: text$
|
|
4372
|
+
packageName: text$3(pkg.package_name_cn) ?? text$3(pkg.package_name_en) ?? "",
|
|
4373
|
+
packageStatus: text$3(pkg.status) ?? "",
|
|
4326
4374
|
credit: {
|
|
4327
4375
|
...credit,
|
|
4328
4376
|
packs
|
|
@@ -6278,7 +6326,7 @@ function numeric(value) {
|
|
|
6278
6326
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
6279
6327
|
if (typeof value === "string" && /^-?\d+(?:\.\d+)?$/.test(value.trim())) return Number(value);
|
|
6280
6328
|
}
|
|
6281
|
-
function record$
|
|
6329
|
+
function record$3(value) {
|
|
6282
6330
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
6283
6331
|
}
|
|
6284
6332
|
function messageOf(payload) {
|
|
@@ -6332,7 +6380,7 @@ async function raccoonAccessTokenValid(token, fetchImpl = fetch) {
|
|
|
6332
6380
|
return false;
|
|
6333
6381
|
}
|
|
6334
6382
|
}
|
|
6335
|
-
function jwtExpiry$
|
|
6383
|
+
function jwtExpiry$2(token) {
|
|
6336
6384
|
const parts = token.split(".");
|
|
6337
6385
|
if (parts.length < 2) return void 0;
|
|
6338
6386
|
try {
|
|
@@ -6354,7 +6402,7 @@ function raccoonRefreshCandidate(tokens, accessToken) {
|
|
|
6354
6402
|
let best;
|
|
6355
6403
|
for (const token of tokens) {
|
|
6356
6404
|
if (token === accessToken) continue;
|
|
6357
|
-
const expiry = jwtExpiry$
|
|
6405
|
+
const expiry = jwtExpiry$2(token);
|
|
6358
6406
|
if (expiry === void 0) continue;
|
|
6359
6407
|
if (best === void 0 || expiry > best.expiry) best = {
|
|
6360
6408
|
token,
|
|
@@ -6517,13 +6565,13 @@ var RaccoonProvider = class {
|
|
|
6517
6565
|
*/
|
|
6518
6566
|
async dailyGrant() {
|
|
6519
6567
|
const payload = await this.authorizedJson("/points/v2/bills?paging.limit=50&type=all");
|
|
6520
|
-
const root = record$
|
|
6568
|
+
const root = record$3(payload) ?? {};
|
|
6521
6569
|
if (typeof root.code === "number" && root.code !== 0) throw new Error(messageOf(payload) ?? `小浣熊积分流水返回 code=${root.code}`);
|
|
6522
|
-
const data = record$
|
|
6570
|
+
const data = record$3(root.data) ?? root;
|
|
6523
6571
|
const items = Array.isArray(data.items) ? data.items : [];
|
|
6524
6572
|
const today = raccoonDay(Date.now());
|
|
6525
6573
|
for (const raw of items) {
|
|
6526
|
-
const item = record$
|
|
6574
|
+
const item = record$3(raw);
|
|
6527
6575
|
if (item?.biz_type !== "daily_grant") continue;
|
|
6528
6576
|
const day = item.grant_at ?? item.created_at;
|
|
6529
6577
|
if (typeof day !== "string" && typeof day !== "number") continue;
|
|
@@ -6633,19 +6681,253 @@ var RaccoonProvider = class {
|
|
|
6633
6681
|
}
|
|
6634
6682
|
}
|
|
6635
6683
|
};
|
|
6684
|
+
// src/trae-client.ts
|
|
6685
|
+
/**
|
|
6686
|
+
* The local Trae desktop client's sign-in state.
|
|
6687
|
+
*
|
|
6688
|
+
* Trae's check-in route insists on the device id its own client registered, so
|
|
6689
|
+
* the website login cannot use it. When the client is installed and already
|
|
6690
|
+
* signed in, the plugin can reuse that state instead:
|
|
6691
|
+
*
|
|
6692
|
+
* - the `Cloud-IDE-JWT` stored under `iCubeAuthInfo://icube.cloudide`;
|
|
6693
|
+
* - the 16-digit device id encoded in the `iCubeAuthInfo://icube-dc:<id>` key;
|
|
6694
|
+
* - `telemetry.machineId`, the value the client sends as `x-machine-id`.
|
|
6695
|
+
*
|
|
6696
|
+
* The stored value uses the client's own AES-CBC envelope: six magic bytes, a
|
|
6697
|
+
* 32-byte key material, then the ciphertext. The AES key and IV are derived
|
|
6698
|
+
* from that key material with SHA-512 and a fixed salt, and the plaintext is a
|
|
6699
|
+
* SHA-512 digest followed by the JSON document. Nothing here writes to the
|
|
6700
|
+
* client, and the token is never returned by the settings API.
|
|
6701
|
+
*
|
|
6702
|
+
* @module dsh-llm-adapters/trae-client
|
|
6703
|
+
*/
|
|
6704
|
+
const AUTH_PROVIDER_KEY = "iCubeAuthInfo://icube.cloudide";
|
|
6705
|
+
const DEVICE_KEY_PREFIX = "iCubeAuthInfo://icube-dc:";
|
|
6706
|
+
const MACHINE_KEY = "telemetry.machineId";
|
|
6707
|
+
const SERVICE_MACHINE_KEY = "storage.serviceMachineId";
|
|
6708
|
+
const AES_MAGIC = Buffer.from([
|
|
6709
|
+
116,
|
|
6710
|
+
99,
|
|
6711
|
+
5,
|
|
6712
|
+
16,
|
|
6713
|
+
0,
|
|
6714
|
+
0
|
|
6715
|
+
]);
|
|
6716
|
+
const AES_PRIVATE_MAGIC = Buffer.from([
|
|
6717
|
+
18,
|
|
6718
|
+
57,
|
|
6719
|
+
32,
|
|
6720
|
+
32,
|
|
6721
|
+
2,
|
|
6722
|
+
3
|
|
6723
|
+
]);
|
|
6724
|
+
const KEY_OFFSET = 6;
|
|
6725
|
+
const HASH_LENGTH = 64;
|
|
6726
|
+
/** Aue ^ Due from the client's byteCrypto module. */
|
|
6727
|
+
const AES_SALT = Buffer.from("4dd4c2e6b83162090e52b3c7a6733ba41cb2462b829ab58a196b39db57177524f49baf7f08e8d68d26a72e37c1a95a2f1f05a51892aef2949732b62a38aadd58", "hex");
|
|
6728
|
+
/** Tue ^ Iue, the AES_PRIVATE variant of the same envelope. */
|
|
6729
|
+
const AES_PRIVATE_SALT = Buffer.from("490cc21292b05d0cc06ccbe91fb92e21b5a7c538811dc85e27a96e28104e1cf69c7166efab232807390da78a8b91e49c44b49a43feb941a1e46f2656e613e47b", "hex");
|
|
6730
|
+
const DEVICE_RE = /^\d{16}$/;
|
|
6731
|
+
function record$2(value) {
|
|
6732
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
6733
|
+
}
|
|
6734
|
+
function text$2(value) {
|
|
6735
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
6736
|
+
}
|
|
6737
|
+
/** Accept epoch seconds, epoch milliseconds, or an ISO date. */
|
|
6738
|
+
function epochMs$2(value) {
|
|
6739
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value < 0xe8d4a51000 ? Math.round(value * 1e3) : Math.round(value);
|
|
6740
|
+
if (typeof value === "string") {
|
|
6741
|
+
if (/^\d+$/.test(value.trim())) return epochMs$2(Number(value));
|
|
6742
|
+
const parsed = Date.parse(value);
|
|
6743
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
function jwtExpiry$1(token) {
|
|
6747
|
+
const parts = token.split(".");
|
|
6748
|
+
if (parts.length < 2) return void 0;
|
|
6749
|
+
try {
|
|
6750
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
6751
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
6752
|
+
return epochMs$2(record$2(JSON.parse(Buffer.from(padded, "base64").toString("utf8")))?.exp);
|
|
6753
|
+
} catch {
|
|
6754
|
+
return;
|
|
6755
|
+
}
|
|
6756
|
+
}
|
|
6757
|
+
/**
|
|
6758
|
+
* Open one `iCubeAuthInfo` value.
|
|
6759
|
+
*
|
|
6760
|
+
* The digest check is the client's own integrity check: the first 64 plaintext
|
|
6761
|
+
* bytes are SHA-512 of the document that follows. A malformed or changed
|
|
6762
|
+
* envelope is rejected instead of being parsed as garbage.
|
|
6763
|
+
*/
|
|
6764
|
+
function decryptAuthValue(value) {
|
|
6765
|
+
const bytes = Buffer.from(value, "base64");
|
|
6766
|
+
if (bytes.length < 102) throw new Error("数据长度不足");
|
|
6767
|
+
const magic = bytes.subarray(0, KEY_OFFSET);
|
|
6768
|
+
const salt = magic.equals(AES_MAGIC) ? AES_SALT : magic.equals(AES_PRIVATE_MAGIC) ? AES_PRIVATE_SALT : void 0;
|
|
6769
|
+
if (salt === void 0) throw new Error("未知的加密格式");
|
|
6770
|
+
const keyMaterial = bytes.subarray(KEY_OFFSET, 38);
|
|
6771
|
+
const mixed = createHash("sha512").update(keyMaterial).digest();
|
|
6772
|
+
const derived = createHash("sha512").update(Buffer.concat([mixed, salt])).digest();
|
|
6773
|
+
const decipher = createDecipheriv("aes-128-cbc", derived.subarray(0, 16), derived.subarray(16, 32));
|
|
6774
|
+
const plain = Buffer.concat([decipher.update(bytes.subarray(38)), decipher.final()]);
|
|
6775
|
+
if (plain.length < HASH_LENGTH) throw new Error("解密结果为空");
|
|
6776
|
+
const body = plain.subarray(HASH_LENGTH);
|
|
6777
|
+
if (!createHash("sha512").update(body).digest().equals(plain.subarray(0, HASH_LENGTH))) throw new Error("校验失败");
|
|
6778
|
+
return JSON.parse(body.toString("utf8"));
|
|
6779
|
+
}
|
|
6780
|
+
/**
|
|
6781
|
+
* The client's data directory, most specific first.
|
|
6782
|
+
*
|
|
6783
|
+
* `DSH_TRAE_CLIENT_DIR` is a portable/test override; the region-specific
|
|
6784
|
+
* variables let one machine keep separate CN and international installs.
|
|
6785
|
+
*/
|
|
6786
|
+
function traeClientDataDirs(region, options = {}) {
|
|
6787
|
+
const platform = options.platform ?? process.platform;
|
|
6788
|
+
const env = options.env ?? process.env;
|
|
6789
|
+
const home = options.home ?? homedir();
|
|
6790
|
+
const name = region === "cn" ? "Trae CN" : "Trae";
|
|
6791
|
+
const out = [];
|
|
6792
|
+
const add = (directory) => {
|
|
6793
|
+
if (directory !== void 0 && directory !== "") out.push(directory);
|
|
6794
|
+
};
|
|
6795
|
+
add(env[region === "cn" ? "DSH_TRAE_CN_CLIENT_DIR" : "DSH_TRAE_GLOBAL_CLIENT_DIR"]);
|
|
6796
|
+
add(env["DSH_TRAE_CLIENT_DIR"]);
|
|
6797
|
+
if (platform === "win32") {
|
|
6798
|
+
const appData = env["APPDATA"] ?? join(home, "AppData", "Roaming");
|
|
6799
|
+
add(join(appData, name));
|
|
6800
|
+
} else if (platform === "darwin") add(join(home, "Library", "Application Support", name));
|
|
6801
|
+
else {
|
|
6802
|
+
const config = env["XDG_CONFIG_HOME"] ?? join(home, ".config");
|
|
6803
|
+
add(join(config, name));
|
|
6804
|
+
}
|
|
6805
|
+
return [...new Set(out)];
|
|
6806
|
+
}
|
|
6807
|
+
function storageFile(userDataDir) {
|
|
6808
|
+
return join(userDataDir, "User", "globalStorage", "storage.json");
|
|
6809
|
+
}
|
|
6810
|
+
function authValueOf(storage) {
|
|
6811
|
+
const direct = text$2(storage[AUTH_PROVIDER_KEY]);
|
|
6812
|
+
if (direct !== void 0) return direct;
|
|
6813
|
+
for (const key of Object.keys(storage)) if (key.startsWith("iCubeAuthInfo://") && key.endsWith(".cloudide")) {
|
|
6814
|
+
const value = text$2(storage[key]);
|
|
6815
|
+
if (value !== void 0) return value;
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
/** The client has one device id per machine; the last valid key is current. */
|
|
6819
|
+
function deviceIdOf(storage) {
|
|
6820
|
+
const keys = Object.keys(storage).filter((key) => key.startsWith(DEVICE_KEY_PREFIX));
|
|
6821
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
6822
|
+
const value = text$2(keys[index].slice(25));
|
|
6823
|
+
if (value !== void 0 && DEVICE_RE.test(value)) return value;
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
function regionMismatch(region, userInfo) {
|
|
6827
|
+
const account = record$2(userInfo.account);
|
|
6828
|
+
const evidence = (text$2(record$2(userInfo.userRegion)?.region) ?? text$2(account?.storeRegion))?.toUpperCase();
|
|
6829
|
+
if (evidence === void 0) return false;
|
|
6830
|
+
return region === "cn" ? evidence !== "CN" : evidence === "CN";
|
|
6831
|
+
}
|
|
6832
|
+
/** Parse one `storage.json` document; exported for tests and diagnostics. */
|
|
6833
|
+
function parseTraeClientStorage(region, storage) {
|
|
6834
|
+
const encrypted = authValueOf(storage);
|
|
6835
|
+
if (encrypted === void 0) return { status: {
|
|
6836
|
+
available: true,
|
|
6837
|
+
signedIn: false,
|
|
6838
|
+
error: "本机 Trae 客户端尚未登录。请先在客户端完成登录。"
|
|
6839
|
+
} };
|
|
6840
|
+
let parsed;
|
|
6841
|
+
try {
|
|
6842
|
+
parsed = decryptAuthValue(encrypted);
|
|
6843
|
+
} catch (error) {
|
|
6844
|
+
return { status: {
|
|
6845
|
+
available: true,
|
|
6846
|
+
signedIn: false,
|
|
6847
|
+
error: `读取本机 Trae 客户端登录态失败:${error instanceof Error ? error.message : String(error)}`
|
|
6848
|
+
} };
|
|
6849
|
+
}
|
|
6850
|
+
const userInfo = record$2(parsed);
|
|
6851
|
+
const token = text$2(userInfo?.token);
|
|
6852
|
+
if (userInfo === void 0 || token === void 0) return { status: {
|
|
6853
|
+
available: true,
|
|
6854
|
+
signedIn: false,
|
|
6855
|
+
error: "本机 Trae 客户端登录态里没有令牌,请先在客户端重新登录。"
|
|
6856
|
+
} };
|
|
6857
|
+
const account = record$2(userInfo.account);
|
|
6858
|
+
const name = text$2(account?.username) ?? text$2(account?.name);
|
|
6859
|
+
const userId = text$2(userInfo.userId);
|
|
6860
|
+
const expiresAt = epochMs$2(userInfo.expiredAt) ?? jwtExpiry$1(token);
|
|
6861
|
+
const identity = {
|
|
6862
|
+
...name === void 0 ? {} : { name },
|
|
6863
|
+
...userId === void 0 ? {} : { userId },
|
|
6864
|
+
...expiresAt === void 0 ? {} : { expiresAt }
|
|
6865
|
+
};
|
|
6866
|
+
const unavailable = (error) => ({ status: {
|
|
6867
|
+
available: true,
|
|
6868
|
+
signedIn: false,
|
|
6869
|
+
...identity,
|
|
6870
|
+
error
|
|
6871
|
+
} });
|
|
6872
|
+
if (regionMismatch(region, userInfo)) return unavailable(region === "cn" ? "本机 Trae 客户端登录的是国际版账号,请切换到国际版后再复用。" : "本机 Trae 客户端登录的是国内版账号,请切换到国内版后再复用。");
|
|
6873
|
+
if (expiresAt !== void 0 && expiresAt <= Date.now()) return unavailable("本机 Trae 客户端登录已过期,请先在客户端重新登录。");
|
|
6874
|
+
const deviceId = deviceIdOf(storage);
|
|
6875
|
+
if (deviceId === void 0) return unavailable("本机 Trae 客户端登录缺少 16 位设备号,请先在客户端重新登录。");
|
|
6876
|
+
const machineId = text$2(storage[MACHINE_KEY]) ?? text$2(storage[SERVICE_MACHINE_KEY]);
|
|
6877
|
+
if (machineId === void 0) return unavailable("本机 Trae 客户端登录缺少机器码,请先在客户端重新登录。");
|
|
6878
|
+
return {
|
|
6879
|
+
status: {
|
|
6880
|
+
available: true,
|
|
6881
|
+
signedIn: true,
|
|
6882
|
+
...identity
|
|
6883
|
+
},
|
|
6884
|
+
state: {
|
|
6885
|
+
region,
|
|
6886
|
+
token,
|
|
6887
|
+
deviceId,
|
|
6888
|
+
machineId,
|
|
6889
|
+
...identity
|
|
6890
|
+
}
|
|
6891
|
+
};
|
|
6892
|
+
}
|
|
6893
|
+
/** Read the currently selected region's client state from this machine. */
|
|
6894
|
+
function readTraeClientLogin(region, options = {}) {
|
|
6895
|
+
let available = false;
|
|
6896
|
+
let error;
|
|
6897
|
+
for (const directory of traeClientDataDirs(region, options)) {
|
|
6898
|
+
const file = storageFile(directory);
|
|
6899
|
+
if (!existsSync(file)) continue;
|
|
6900
|
+
available = true;
|
|
6901
|
+
try {
|
|
6902
|
+
const raw = readFileSync(file, "utf8").replace(/^\uFEFF/, "");
|
|
6903
|
+
const storage = record$2(JSON.parse(raw));
|
|
6904
|
+
if (storage === void 0) throw new Error("storage.json 不是对象");
|
|
6905
|
+
return parseTraeClientStorage(region, storage);
|
|
6906
|
+
} catch (cause) {
|
|
6907
|
+
error = `读取本机 Trae 客户端登录态失败:${cause instanceof Error ? cause.message : String(cause)}`;
|
|
6908
|
+
}
|
|
6909
|
+
}
|
|
6910
|
+
return { status: {
|
|
6911
|
+
available,
|
|
6912
|
+
signedIn: false,
|
|
6913
|
+
...error === void 0 ? {} : { error }
|
|
6914
|
+
} };
|
|
6915
|
+
}
|
|
6636
6916
|
// src/trae-login.ts
|
|
6637
6917
|
/**
|
|
6638
6918
|
* Trae sign-in through the provider's own website.
|
|
6639
6919
|
*
|
|
6640
|
-
*
|
|
6920
|
+
* This flow does not read a desktop install: it opens a dedicated browser
|
|
6641
6921
|
* profile, lets the user sign in on trae.cn / trae.ai, and then takes the
|
|
6642
6922
|
* session the website itself created. Two things come out of that session:
|
|
6643
6923
|
*
|
|
6644
6924
|
* - the `Cloud-IDE-JWT` token, fetched with the page's own cookies through
|
|
6645
6925
|
* `/cloudide/api/v3/common/GetUserToken` — the same call the site makes;
|
|
6646
6926
|
* - the device fingerprint (`__msuuid__` from the page, or one generated in
|
|
6647
|
-
* the page),
|
|
6648
|
-
*
|
|
6927
|
+
* the page), stored with the web login.
|
|
6928
|
+
*
|
|
6929
|
+
* The check-in route does not accept this web fingerprint, so check-in is
|
|
6930
|
+
* enabled only for the separate client-login import in `trae-client.ts`.
|
|
6649
6931
|
*
|
|
6650
6932
|
* Cookies are captured over the DevTools protocol and kept in the plugin's own
|
|
6651
6933
|
* account store so the short-lived JWT can be renewed later; the dedicated
|
|
@@ -7257,10 +7539,12 @@ var TraeWebLogin = class {
|
|
|
7257
7539
|
};
|
|
7258
7540
|
// src/trae.ts
|
|
7259
7541
|
/**
|
|
7260
|
-
* Trae provider: model directory, SOLO chat and
|
|
7542
|
+
* Trae provider: model directory, SOLO chat, quota and client check-in.
|
|
7261
7543
|
*
|
|
7262
7544
|
* Trae's website login yields the same `Cloud-IDE-JWT` its own clients use, so
|
|
7263
|
-
* the plugin talks to the same gateways they do.
|
|
7545
|
+
* the plugin talks to the same gateways they do. A second sign-in method copies
|
|
7546
|
+
* that token from the local desktop client; only that method carries the device
|
|
7547
|
+
* id the daily check-in route accepts. Two catalog sources are merged:
|
|
7264
7548
|
*
|
|
7265
7549
|
* - the Remote directory (`/api/remote/v1/models`) is the model skeleton —
|
|
7266
7550
|
* display name, context window, multiplier, reasoning options, multimodal;
|
|
@@ -7962,6 +8246,22 @@ function parseTraeGlobalUsage(payload) {
|
|
|
7962
8246
|
at: Date.now()
|
|
7963
8247
|
};
|
|
7964
8248
|
}
|
|
8249
|
+
/**
|
|
8250
|
+
* The check-in body's `req_source`: 1 is the IDE, 2 is Trae Work. The desktop
|
|
8251
|
+
* client sends 1; the claim route rejects the plugin's former empty body.
|
|
8252
|
+
*/
|
|
8253
|
+
const TRAE_CHECKIN_REQ_SOURCE = 1;
|
|
8254
|
+
function parseTraeCheckin(payload) {
|
|
8255
|
+
const root = record(payload) ?? {};
|
|
8256
|
+
const reward = num(root.credits) ?? 0;
|
|
8257
|
+
return {
|
|
8258
|
+
available: root.enable !== false,
|
|
8259
|
+
claimed: root.checked_in === true,
|
|
8260
|
+
deviceClaimed: root.did_checked_in === true,
|
|
8261
|
+
...reward === 0 ? {} : { reward },
|
|
8262
|
+
...text(root.message) === void 0 ? {} : { message: text(root.message) }
|
|
8263
|
+
};
|
|
8264
|
+
}
|
|
7965
8265
|
var TraeProvider = class {
|
|
7966
8266
|
options;
|
|
7967
8267
|
catalogInstance;
|
|
@@ -8172,6 +8472,160 @@ var TraeProvider = class {
|
|
|
8172
8472
|
this.quotaInflightRegion = region;
|
|
8173
8473
|
return this.quotaInflight;
|
|
8174
8474
|
}
|
|
8475
|
+
/**
|
|
8476
|
+
* The second sign-in method: the desktop client's current state.
|
|
8477
|
+
*
|
|
8478
|
+
* Read-only; the settings page only receives the non-secret status, never
|
|
8479
|
+
* the token.
|
|
8480
|
+
*/
|
|
8481
|
+
async localClientLogin() {
|
|
8482
|
+
return this.readClientLogin().status;
|
|
8483
|
+
}
|
|
8484
|
+
readClientLogin() {
|
|
8485
|
+
return (this.options.clientLogin ?? (() => readTraeClientLogin(this.options.region())))();
|
|
8486
|
+
}
|
|
8487
|
+
/** Import the desktop client's token, device id and machine id. */
|
|
8488
|
+
async importClientLogin() {
|
|
8489
|
+
const region = this.ensureRegion();
|
|
8490
|
+
const detection = this.readClientLogin();
|
|
8491
|
+
if (!detection.status.available) throw new Error("本机没有检测到 Trae 客户端。请先安装并登录客户端,或改用网页登录。");
|
|
8492
|
+
const state = detection.state;
|
|
8493
|
+
if (!detection.status.signedIn || state === void 0) throw new Error(detection.status.error ?? "本机 Trae 客户端尚未登录。请先在客户端完成登录。");
|
|
8494
|
+
await this.loginFlow?.cancel();
|
|
8495
|
+
await this.options.store.saveTrae({
|
|
8496
|
+
accessToken: state.token,
|
|
8497
|
+
region,
|
|
8498
|
+
source: "client",
|
|
8499
|
+
deviceId: state.deviceId,
|
|
8500
|
+
machineId: state.machineId,
|
|
8501
|
+
loginAt: Date.now(),
|
|
8502
|
+
...state.userId === void 0 ? {} : { userId: state.userId },
|
|
8503
|
+
...state.name === void 0 ? {} : { name: state.name },
|
|
8504
|
+
...state.expiresAt === void 0 ? {} : { expiresAt: state.expiresAt }
|
|
8505
|
+
});
|
|
8506
|
+
this.invalidateModels();
|
|
8507
|
+
this.invalidateQuota();
|
|
8508
|
+
}
|
|
8509
|
+
async checkinContext() {
|
|
8510
|
+
this.ensureRegion();
|
|
8511
|
+
const profile = this.profile();
|
|
8512
|
+
if (!profile.checkin) return {
|
|
8513
|
+
ok: false,
|
|
8514
|
+
status: {
|
|
8515
|
+
available: false,
|
|
8516
|
+
claimed: false,
|
|
8517
|
+
deviceClaimed: false,
|
|
8518
|
+
message: "Trae 国际版没有签到活动。"
|
|
8519
|
+
}
|
|
8520
|
+
};
|
|
8521
|
+
const auth = await this.options.auth.status();
|
|
8522
|
+
if (!auth.signedIn) return {
|
|
8523
|
+
ok: false,
|
|
8524
|
+
status: {
|
|
8525
|
+
available: false,
|
|
8526
|
+
claimed: false,
|
|
8527
|
+
deviceClaimed: false,
|
|
8528
|
+
message: auth.error ?? "尚未登录,无法签到。"
|
|
8529
|
+
}
|
|
8530
|
+
};
|
|
8531
|
+
if (auth.loginSource !== "client") return {
|
|
8532
|
+
ok: false,
|
|
8533
|
+
status: {
|
|
8534
|
+
available: false,
|
|
8535
|
+
claimed: false,
|
|
8536
|
+
deviceClaimed: false,
|
|
8537
|
+
message: "签到仅在复用本机 Trae 客户端登录时可用;网页登录没有客户端设备号。"
|
|
8538
|
+
}
|
|
8539
|
+
};
|
|
8540
|
+
let token;
|
|
8541
|
+
let identity;
|
|
8542
|
+
try {
|
|
8543
|
+
token = await this.options.auth.accessToken();
|
|
8544
|
+
identity = await this.options.auth.identity();
|
|
8545
|
+
} catch (error) {
|
|
8546
|
+
return {
|
|
8547
|
+
ok: false,
|
|
8548
|
+
status: {
|
|
8549
|
+
available: false,
|
|
8550
|
+
claimed: false,
|
|
8551
|
+
deviceClaimed: false,
|
|
8552
|
+
message: error instanceof Error ? error.message : String(error)
|
|
8553
|
+
}
|
|
8554
|
+
};
|
|
8555
|
+
}
|
|
8556
|
+
if (!/^\d{16}$/.test(identity.deviceId)) return {
|
|
8557
|
+
ok: false,
|
|
8558
|
+
status: {
|
|
8559
|
+
available: false,
|
|
8560
|
+
claimed: false,
|
|
8561
|
+
deviceClaimed: false,
|
|
8562
|
+
message: "本机 Trae 客户端登录缺少 16 位设备号,请先在客户端重新登录后再复用。"
|
|
8563
|
+
}
|
|
8564
|
+
};
|
|
8565
|
+
return {
|
|
8566
|
+
ok: true,
|
|
8567
|
+
profile,
|
|
8568
|
+
token,
|
|
8569
|
+
identity: {
|
|
8570
|
+
deviceId: identity.deviceId,
|
|
8571
|
+
machineId: identity.machineId
|
|
8572
|
+
}
|
|
8573
|
+
};
|
|
8574
|
+
}
|
|
8575
|
+
async checkinStatus() {
|
|
8576
|
+
const context = await this.checkinContext();
|
|
8577
|
+
if (!context.ok) return context.status;
|
|
8578
|
+
const response = await this.fetchImpl(`${context.profile.apiBase}/trae/api/v2/ug/checkin_credits/status`, {
|
|
8579
|
+
method: "POST",
|
|
8580
|
+
headers: {
|
|
8581
|
+
...this.headers(context.token, context.profile),
|
|
8582
|
+
"x-user-region": "CN",
|
|
8583
|
+
"x-device-id": context.identity.deviceId,
|
|
8584
|
+
"x-machine-id": context.identity.machineId
|
|
8585
|
+
},
|
|
8586
|
+
body: JSON.stringify({ req_source: 1 }),
|
|
8587
|
+
signal: AbortSignal.timeout(this.options.timeoutMs())
|
|
8588
|
+
});
|
|
8589
|
+
if (!response.ok) throw new ProviderHttpError(await responseError(response), response.status);
|
|
8590
|
+
return parseTraeCheckin(await response.json());
|
|
8591
|
+
}
|
|
8592
|
+
/** Claim today's reward. The only state-changing call in this provider. */
|
|
8593
|
+
async claimCheckin() {
|
|
8594
|
+
const context = await this.checkinContext();
|
|
8595
|
+
if (!context.ok) throw new Error(context.status.message ?? "当前登录方式不能签到。");
|
|
8596
|
+
const response = await this.fetchImpl(`${context.profile.apiBase}/trae/api/v2/ug/checkin_credits/claim`, {
|
|
8597
|
+
method: "POST",
|
|
8598
|
+
headers: {
|
|
8599
|
+
...this.headers(context.token, context.profile),
|
|
8600
|
+
"x-user-region": "CN",
|
|
8601
|
+
"x-device-id": context.identity.deviceId,
|
|
8602
|
+
"x-machine-id": context.identity.machineId
|
|
8603
|
+
},
|
|
8604
|
+
body: JSON.stringify({ req_source: 1 }),
|
|
8605
|
+
signal: AbortSignal.timeout(this.options.timeoutMs())
|
|
8606
|
+
});
|
|
8607
|
+
if (!response.ok) throw new ProviderHttpError(await responseError(response), response.status);
|
|
8608
|
+
const payload = record(await response.json()) ?? {};
|
|
8609
|
+
const code = typeof payload.code === "number" ? payload.code : 0;
|
|
8610
|
+
if (code === 0) {
|
|
8611
|
+
this.invalidateQuota();
|
|
8612
|
+
const reward = num(payload.credits);
|
|
8613
|
+
return {
|
|
8614
|
+
available: true,
|
|
8615
|
+
claimed: true,
|
|
8616
|
+
deviceClaimed: false,
|
|
8617
|
+
...reward === void 0 || reward === 0 ? {} : { reward },
|
|
8618
|
+
...text(payload.message) === void 0 ? {} : { message: text(payload.message) }
|
|
8619
|
+
};
|
|
8620
|
+
}
|
|
8621
|
+
if (code === 9095) return {
|
|
8622
|
+
available: true,
|
|
8623
|
+
claimed: false,
|
|
8624
|
+
deviceClaimed: true,
|
|
8625
|
+
message: "本机今日已签到"
|
|
8626
|
+
};
|
|
8627
|
+
throw new Error(text(payload.message) ?? `签到失败(code ${code})`);
|
|
8628
|
+
}
|
|
8175
8629
|
async *stream(options, model, readImage, onInvalidate) {
|
|
8176
8630
|
this.ensureRegion();
|
|
8177
8631
|
const profile = this.profile();
|
|
@@ -8229,6 +8683,7 @@ var TraeProvider = class {
|
|
|
8229
8683
|
await this.options.store.saveTrae({
|
|
8230
8684
|
accessToken: account.accessToken,
|
|
8231
8685
|
region: target,
|
|
8686
|
+
source: "web",
|
|
8232
8687
|
deviceId: account.deviceId,
|
|
8233
8688
|
machineId: account.machineId,
|
|
8234
8689
|
loginAt: Date.now(),
|
|
@@ -8510,4 +8965,4 @@ function apply(ctx, config) {
|
|
|
8510
8965
|
ctx.logger?.info(`llm-adapters: 设置接口已挂载于 ${API_PREFIX}`);
|
|
8511
8966
|
});
|
|
8512
8967
|
}
|
|
8513
|
-
export { API_PREFIX, AccountStore, BRANDS, CODEARTS_BENEFIT_FALLBACK, CODEARTS_BRAND, CODEARTS_CHAT_URL, CODEARTS_CONTEXT_WINDOWS, CODEARTS_DEFAULT_EFFORT, CODEARTS_EFFORTS, CODEARTS_EFFORT_LEVELS, CODEARTS_FALLBACK_CONTEXT_WINDOW, CODEARTS_MAX_OUTPUT_TOKENS, CODEARTS_PROVIDER, CODEARTS_SESSION_LIMIT, CODEARTS_SESSION_WINDOW_MS, CODEARTS_SNAP_BASE, CODEARTS_VISION_MODELS, CodeArtsAdapter, CodeArtsAuth, CodeArtsLogin, CodeArtsProvider, CodeArtsSessionWindow, Config, EMPTY_OPENROUTER_INDEX, LOOMY_ACCOUNT_BASE, LOOMY_BASE, LOOMY_BRAND, LOOMY_PROVIDER, LOOMY_VERSION, LOOMY_WECHAT_APP_ID, LOOMY_WECHAT_REDIRECT_URI, LocalAdapter, LoomyAdapter, LoomyAuth, LoomyProvider, LoomyQrLogin, ModelCatalog, OpenRouterCatalog, ProviderHttpError, RACCOON_BASE, RACCOON_BRAND, RACCOON_CLIENT_VERSION, RACCOON_IAM_BASE, RACCOON_PROVIDER, RACCOON_QR_APP_NAME, RACCOON_QR_ORIGIN, RaccoonAdapter, RaccoonAuth, RaccoonIamError, RaccoonProvider, RaccoonQrLogin, RefreshTokenExpiredError, TRAE_APP_ID, TRAE_BRAND, TRAE_CHAT_FUNCTIONS, TRAE_DIRECTORY_FUNCTIONS, TRAE_PROVIDER, TRAE_REGION_PROFILES, TRAE_SOLO_CHAT_PATH, TRAE_WEB_USER_AGENT, TraeAdapter, TraeAuth, TraeProvider, TraeWebLogin, apply, buildAuthorizeUrl, buildLoomyWechatAuthUrl, buildMessages, buildOpenRouterIndex, buildQrMatrix, buildRows, buildTools, clampMaxTokens, codeartsContextWindow, codeartsEfforts, consumeOpenAiStream, createRaccoonQr, decodeJwtFacts, defaultEffortOf, effortLabel, exchangeAuthorizationCode, exchangeRefreshToken, extractWechatUuid, fetchLoomyWechatUuid, generateDpopKeyPair, generatePkcePair, headersForFetch, inject, isBrandId, liveConfig, loomyGatewaySessionValid, machineIdOf, mergeTraeModels, modelKey, name, normalizeCodeArtsModelId, normalizeEffort, normalizeTraeRegion, openAiStream, openTraeStream, parseCodeArtsPackage, parseCodeArtsRoster, parseLoomyCatalog, parseModelName, parseOpenRouterModels, parseRaccoonCatalog, parseTraeGlobalUsage, parseTraeRemoteModels, parseTraeUsage, parseTraeWireModels, pickActiveCredit, pollRaccoonQr, priceFactorOf, priceFactorOfLabel, raccoonAccessTokenValid, raccoonQrContent, raccoonRefreshCandidate, readJwtExpiry, readOpenRouter, renderQrSvg, resolveCapabilities, resolveConfig, resolveModelId, sharedSessionWindow, signDpopJws, signRequestHuawei, sseEvents, startLoginPage, traeMessages, traeTools };
|
|
8968
|
+
export { API_PREFIX, AccountStore, BRANDS, CODEARTS_BENEFIT_FALLBACK, CODEARTS_BRAND, CODEARTS_CHAT_URL, CODEARTS_CONTEXT_WINDOWS, CODEARTS_DEFAULT_EFFORT, CODEARTS_EFFORTS, CODEARTS_EFFORT_LEVELS, CODEARTS_FALLBACK_CONTEXT_WINDOW, CODEARTS_MAX_OUTPUT_TOKENS, CODEARTS_PROVIDER, CODEARTS_SESSION_LIMIT, CODEARTS_SESSION_WINDOW_MS, CODEARTS_SNAP_BASE, CODEARTS_VISION_MODELS, CodeArtsAdapter, CodeArtsAuth, CodeArtsLogin, CodeArtsProvider, CodeArtsSessionWindow, Config, EMPTY_OPENROUTER_INDEX, LOOMY_ACCOUNT_BASE, LOOMY_BASE, LOOMY_BRAND, LOOMY_PROVIDER, LOOMY_VERSION, LOOMY_WECHAT_APP_ID, LOOMY_WECHAT_REDIRECT_URI, LocalAdapter, LoomyAdapter, LoomyAuth, LoomyProvider, LoomyQrLogin, ModelCatalog, OpenRouterCatalog, ProviderHttpError, RACCOON_BASE, RACCOON_BRAND, RACCOON_CLIENT_VERSION, RACCOON_IAM_BASE, RACCOON_PROVIDER, RACCOON_QR_APP_NAME, RACCOON_QR_ORIGIN, RaccoonAdapter, RaccoonAuth, RaccoonIamError, RaccoonProvider, RaccoonQrLogin, RefreshTokenExpiredError, TRAE_APP_ID, TRAE_BRAND, TRAE_CHAT_FUNCTIONS, TRAE_CHECKIN_REQ_SOURCE, TRAE_DIRECTORY_FUNCTIONS, TRAE_PROVIDER, TRAE_REGION_PROFILES, TRAE_SOLO_CHAT_PATH, TRAE_WEB_USER_AGENT, TraeAdapter, TraeAuth, TraeProvider, TraeWebLogin, apply, buildAuthorizeUrl, buildLoomyWechatAuthUrl, buildMessages, buildOpenRouterIndex, buildQrMatrix, buildRows, buildTools, clampMaxTokens, codeartsContextWindow, codeartsEfforts, consumeOpenAiStream, createRaccoonQr, decodeJwtFacts, defaultEffortOf, effortLabel, exchangeAuthorizationCode, exchangeRefreshToken, extractWechatUuid, fetchLoomyWechatUuid, generateDpopKeyPair, generatePkcePair, headersForFetch, inject, isBrandId, liveConfig, loomyGatewaySessionValid, machineIdOf, mergeTraeModels, modelKey, name, normalizeCodeArtsModelId, normalizeEffort, normalizeTraeRegion, openAiStream, openTraeStream, parseCodeArtsPackage, parseCodeArtsRoster, parseLoomyCatalog, parseModelName, parseOpenRouterModels, parseRaccoonCatalog, parseTraeCheckin, parseTraeClientStorage, parseTraeGlobalUsage, parseTraeRemoteModels, parseTraeUsage, parseTraeWireModels, pickActiveCredit, pollRaccoonQr, priceFactorOf, priceFactorOfLabel, raccoonAccessTokenValid, raccoonQrContent, raccoonRefreshCandidate, readJwtExpiry, readOpenRouter, readTraeClientLogin, renderQrSvg, resolveCapabilities, resolveConfig, resolveModelId, sharedSessionWindow, signDpopJws, signRequestHuawei, sseEvents, startLoginPage, traeClientDataDirs, traeMessages, traeTools };
|