dsh-llm-adapters 1.2.0 → 1.2.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.
- package/DEVELOPMENT.md +92 -24
- package/FAQ.md +47 -10
- package/README.md +39 -25
- package/client.js +1802 -1733
- package/lib/index.js +733 -302
- package/lib/types/account-store.d.ts +17 -8
- package/lib/types/adapter.d.ts +8 -2
- package/lib/types/auth.d.ts +24 -11
- package/lib/types/codearts-oauth.d.ts +19 -0
- package/lib/types/codearts.d.ts +1 -5
- package/lib/types/config.d.ts +0 -5
- package/lib/types/index.d.ts +5 -6
- package/lib/types/raccoon.d.ts +79 -6
- 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 +20 -8
- package/src/adapter.ts +31 -3
- package/src/api.ts +15 -3
- package/src/auth.ts +62 -17
- package/src/codearts-oauth.ts +36 -5
- package/src/codearts.ts +24 -29
- package/src/config.ts +258 -264
- package/src/index.ts +6 -29
- package/src/raccoon.ts +163 -50
- 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/types/codearts-window.d.ts +0 -65
- package/src/codearts-window.ts +0 -136
|
@@ -30,12 +30,12 @@ export interface RaccoonAccountRecord {
|
|
|
30
30
|
loginAt: number;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* One Trae
|
|
33
|
+
* One Trae login.
|
|
34
34
|
*
|
|
35
|
-
* `accessToken` is the `Cloud-IDE-JWT`
|
|
36
|
-
* `machineId` are
|
|
37
|
-
*
|
|
38
|
-
* check-in
|
|
35
|
+
* `accessToken` is the `Cloud-IDE-JWT` Trae's own clients use. `deviceId` and
|
|
36
|
+
* `machineId` are replayed on chat, quota and check-in requests. A web login
|
|
37
|
+
* captures them from its browser session; a `client` login copies the desktop
|
|
38
|
+
* client's own values, which is the only source the check-in route accepts.
|
|
39
39
|
*/
|
|
40
40
|
export interface TraeAccountRecord {
|
|
41
41
|
accessToken: string;
|
|
@@ -45,6 +45,8 @@ export interface TraeAccountRecord {
|
|
|
45
45
|
userId?: string;
|
|
46
46
|
name?: string;
|
|
47
47
|
loginAt: number;
|
|
48
|
+
/** Where the token came from; absent means the plugin's browser login. */
|
|
49
|
+
source?: 'web' | 'client';
|
|
48
50
|
/** The `Cloud-IDE-JWT` expiry (about 8 hours); absent when the JWT carries none. */
|
|
49
51
|
expiresAt?: number;
|
|
50
52
|
/**
|
|
@@ -81,12 +83,19 @@ export interface CodeArtsAccountRecord {
|
|
|
81
83
|
d: string;
|
|
82
84
|
};
|
|
83
85
|
userName?: string;
|
|
86
|
+
/**
|
|
87
|
+
* The `x-ot-session-id` this account chats under: one stable value instead of
|
|
88
|
+
* one per request, because the gateway counts concurrent sessions by it.
|
|
89
|
+
*
|
|
90
|
+
* A record written before the field existed derives the same value from the
|
|
91
|
+
* refresh token's profile, so an existing sign-in needs no new login.
|
|
92
|
+
*/
|
|
93
|
+
otSessionId?: string;
|
|
84
94
|
/**
|
|
85
95
|
* Models the account answered `InferHub.4004.200` for.
|
|
86
96
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* call cannot succeed.
|
|
97
|
+
* Worth persisting: the account has no allowance pack for them, so without the
|
|
98
|
+
* verdict every call would pay for a request that cannot succeed.
|
|
90
99
|
*/
|
|
91
100
|
noBenefit?: string[];
|
|
92
101
|
loginAt: number;
|
package/lib/types/adapter.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { LoomyProvider } from './loomy.ts';
|
|
|
16
16
|
import { RaccoonProvider } from './raccoon.ts';
|
|
17
17
|
import { TraeProvider } from './trae.ts';
|
|
18
18
|
import type { AuthStatus } from './auth.ts';
|
|
19
|
-
import type { CheckinStatus, ProviderStatus, QuotaSnapshot } from './types.ts';
|
|
19
|
+
import type { CheckinStatus, ClientLoginStatus, ProviderStatus, QuotaSnapshot } from './types.ts';
|
|
20
20
|
import type { ReadImage } from './wire.ts';
|
|
21
21
|
export interface AdapterOptions {
|
|
22
22
|
brand: BrandId;
|
|
@@ -33,7 +33,7 @@ export interface AdapterOptions {
|
|
|
33
33
|
*
|
|
34
34
|
* Declared as an interface rather than a union so a new provider only has to
|
|
35
35
|
* implement the behaviour, not edit the adapter's type list. The optional
|
|
36
|
-
* check-in methods exist only on Trae.
|
|
36
|
+
* client-login and check-in methods exist only on Trae.
|
|
37
37
|
*/
|
|
38
38
|
export interface ProviderClient {
|
|
39
39
|
status(): Promise<AuthStatus>;
|
|
@@ -51,6 +51,10 @@ export interface ProviderClient {
|
|
|
51
51
|
invalidateModels(): void;
|
|
52
52
|
invalidateQuota(): void;
|
|
53
53
|
close(): void;
|
|
54
|
+
/** Trae only: the local desktop client's current sign-in state. */
|
|
55
|
+
localClientLogin?(): Promise<ClientLoginStatus>;
|
|
56
|
+
/** Trae only: copy that state into the plugin's own account store. */
|
|
57
|
+
importClientLogin?(): Promise<void>;
|
|
54
58
|
checkinStatus?(): Promise<CheckinStatus>;
|
|
55
59
|
claimCheckin?(): Promise<CheckinStatus>;
|
|
56
60
|
}
|
|
@@ -85,6 +89,8 @@ export declare abstract class LocalAdapter extends LlmAdapter {
|
|
|
85
89
|
cancelBrowserLogin(): Promise<void>;
|
|
86
90
|
/** Claim today's reward; providers without one reject it. */
|
|
87
91
|
claimCheckin(): Promise<CheckinStatus>;
|
|
92
|
+
/** Import a local client login and return the refreshed account reading. */
|
|
93
|
+
importClientLogin(): Promise<ProviderStatus>;
|
|
88
94
|
invalidateCatalog(): void;
|
|
89
95
|
invalidateUsage(): void;
|
|
90
96
|
/** Login state plus quota, shaped for the settings page. */
|
package/lib/types/auth.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Credential state for every provider.
|
|
3
3
|
*
|
|
4
|
-
* Sign-in happens in the provider's own web page
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Sign-in happens in the provider's own web page. Trae additionally offers an
|
|
5
|
+
* explicit, read-only import of the local desktop client's login state; nothing
|
|
6
|
+
* is ever written back to that client. Either way credentials end up in exactly
|
|
7
|
+
* one place, the plugin's own account store, and no token leaves this module in
|
|
8
|
+
* errors or display fields.
|
|
8
9
|
*
|
|
9
10
|
* @module dsh-llm-adapters/auth
|
|
10
11
|
*/
|
|
@@ -26,6 +27,8 @@ export interface AuthStatus {
|
|
|
26
27
|
possiblyExpired?: boolean;
|
|
27
28
|
account?: LocalAccount;
|
|
28
29
|
error?: string;
|
|
30
|
+
/** Trae only: where the token came from. */
|
|
31
|
+
loginSource?: 'web' | 'client';
|
|
29
32
|
}
|
|
30
33
|
export declare class LoomyAuth {
|
|
31
34
|
private readonly store;
|
|
@@ -41,6 +44,13 @@ export interface JwtFacts {
|
|
|
41
44
|
phone?: string;
|
|
42
45
|
ownerType?: string;
|
|
43
46
|
sessionId?: string;
|
|
47
|
+
/**
|
|
48
|
+
* The `sub` claim, which is what an account is called on the Raccoon gateway.
|
|
49
|
+
*
|
|
50
|
+
* Unlike the token itself it survives a re-sign, so it is the seed a stable
|
|
51
|
+
* per-account session id is derived from.
|
|
52
|
+
*/
|
|
53
|
+
subject?: string;
|
|
44
54
|
}
|
|
45
55
|
/** Read non-secret JWT claims; malformed tokens simply yield no facts. */
|
|
46
56
|
export declare function decodeJwtFacts(token: string): JwtFacts;
|
|
@@ -58,12 +68,13 @@ export declare class RaccoonAuth {
|
|
|
58
68
|
logout(): Promise<void>;
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
61
|
-
* Trae's
|
|
71
|
+
* Trae's login, one per region.
|
|
62
72
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
73
|
+
* A web login stores the `Cloud-IDE-JWT` from the website plus the cookies
|
|
74
|
+
* behind it, so the short token can be renewed silently until the website
|
|
75
|
+
* session dies. A client login stores the token the desktop client already
|
|
76
|
+
* uses; that token has no website session, and the client itself refreshes it.
|
|
77
|
+
* Only the client source carries the device id the check-in route accepts.
|
|
67
78
|
*/
|
|
68
79
|
export declare class TraeAuth {
|
|
69
80
|
private readonly store;
|
|
@@ -77,8 +88,10 @@ export declare class TraeAuth {
|
|
|
77
88
|
/**
|
|
78
89
|
* The bearer token, or an actionable error when there is none.
|
|
79
90
|
*
|
|
80
|
-
* A token near its expiry is renewed first; `force` means the gateway
|
|
81
|
-
* rejected it, so the renewal is attempted even if the clock
|
|
91
|
+
* A web token near its expiry is renewed first; `force` means the gateway
|
|
92
|
+
* already rejected it, so the renewal is attempted even if the clock
|
|
93
|
+
* disagrees. A client token is returned until its own expiry and then asks
|
|
94
|
+
* for a fresh import, because only the client can refresh it.
|
|
82
95
|
*/
|
|
83
96
|
accessToken(force?: boolean): Promise<string>;
|
|
84
97
|
/** Renew the eight-hour JWT with the stored website cookies. */
|
|
@@ -28,6 +28,17 @@ export interface CodeArtsCredential {
|
|
|
28
28
|
dpopPrivateJwk?: DpopPrivateJwk;
|
|
29
29
|
/** Display only, from the refresh token's profile; never used for signing. */
|
|
30
30
|
userName?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The id the gateway counts concurrent sessions against (`x-ot-session-id`).
|
|
33
|
+
*
|
|
34
|
+
* Measured on a real account: the three-session cap is keyed by this header,
|
|
35
|
+
* not by `Chat-Id` / `Session-Id`. A per-request value makes every call a new
|
|
36
|
+
* session, so the account spends its own budget on its own history and the
|
|
37
|
+
* fourth call of the minute comes back `TM.00001041`. Derived from the account
|
|
38
|
+
* identity instead of stored at random: the same value in every process, and
|
|
39
|
+
* one that survives a token renewal.
|
|
40
|
+
*/
|
|
41
|
+
otSessionId?: string;
|
|
31
42
|
}
|
|
32
43
|
/** Who the credential belongs to, as the portal encoded it in the token. */
|
|
33
44
|
export interface CodeArtsUserProfile {
|
|
@@ -75,6 +86,14 @@ export declare function readJwtExpiry(token: string): number | undefined;
|
|
|
75
86
|
* request; without it the field stays empty and the page says so.
|
|
76
87
|
*/
|
|
77
88
|
export declare function readUserProfile(token: string | undefined): CodeArtsUserProfile | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* The session id this account should chat under, derived from who the account is.
|
|
91
|
+
*
|
|
92
|
+
* 32 lowercase hex, the shape the IDE's own kernel sends. Nothing but stability
|
|
93
|
+
* matters here — the gateway uses the value as a key, not as an identity — so it
|
|
94
|
+
* is hashed from the principal id rather than kept in a file.
|
|
95
|
+
*/
|
|
96
|
+
export declare function otSessionIdOf(profile: CodeArtsUserProfile | undefined): string | undefined;
|
|
78
97
|
/** The portal URL the browser opens; the callback is on loopback. */
|
|
79
98
|
export declare function buildAuthorizeUrl(port: number, pkce: PkcePair, ticketId: string): string;
|
|
80
99
|
/** Where the browser lands after a successful or failed exchange. */
|
package/lib/types/codearts.d.ts
CHANGED
|
@@ -19,7 +19,6 @@ import type { AccountStore } from './account-store.ts';
|
|
|
19
19
|
import { CodeArtsAuth } from './auth.ts';
|
|
20
20
|
import type { AuthStatus } from './auth.ts';
|
|
21
21
|
import type { ProviderModel } from './catalog.ts';
|
|
22
|
-
import { CodeArtsSessionWindow } from './codearts-window.ts';
|
|
23
22
|
import type { LoginStatus } from './login-page.ts';
|
|
24
23
|
import type { OpenRouterLookup } from './openrouter.ts';
|
|
25
24
|
import type { CheckinStatus, QuotaPack, QuotaSnapshot } from './types.ts';
|
|
@@ -72,8 +71,6 @@ export interface CodeArtsProviderOptions {
|
|
|
72
71
|
loginTimeoutMs?: number;
|
|
73
72
|
/** Injectable OpenRouter lookup for tests; defaults to the live catalog. */
|
|
74
73
|
openRouter?: OpenRouterLookup;
|
|
75
|
-
/** Chat budget; defaults to the process-wide window (survives hot reloads). */
|
|
76
|
-
window?: CodeArtsSessionWindow;
|
|
77
74
|
}
|
|
78
75
|
/** One roster entry before it becomes a model row. */
|
|
79
76
|
export interface CodeArtsModelRow {
|
|
@@ -110,7 +107,6 @@ export declare class CodeArtsProvider {
|
|
|
110
107
|
/** Models that answered `InferHub.4004.200`: no allowance pack to bill. */
|
|
111
108
|
private readonly noBenefit;
|
|
112
109
|
private noBenefitLoaded;
|
|
113
|
-
private readonly window;
|
|
114
110
|
private readonly openRouter;
|
|
115
111
|
private readonly sessionId;
|
|
116
112
|
/**
|
|
@@ -159,7 +155,7 @@ export declare class CodeArtsProvider {
|
|
|
159
155
|
* so a conversation pays for the description once.
|
|
160
156
|
*/
|
|
161
157
|
private relayImages;
|
|
162
|
-
/** Ask the vision model to describe one image
|
|
158
|
+
/** Ask the vision model to describe one image; another turn on the same session. */
|
|
163
159
|
private describeImage;
|
|
164
160
|
stream(options: GenerateOptions, model: string, readImage: ReadImage | undefined, onInvalidate: () => void): AsyncGenerator<StreamChunk>;
|
|
165
161
|
private send;
|
package/lib/types/config.d.ts
CHANGED
|
@@ -45,8 +45,6 @@ export interface Config {
|
|
|
45
45
|
catalogFetchTimeoutSeconds?: number;
|
|
46
46
|
/** Minutes between automatic OpenRouter syncs; 0 disables the schedule. */
|
|
47
47
|
openRouterSyncMinutes?: number;
|
|
48
|
-
/** Minutes between Raccoon daily-point checks; 0 disables the monitor. */
|
|
49
|
-
raccoonCheckinMinutes?: number;
|
|
50
48
|
}
|
|
51
49
|
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
52
50
|
loomyEnabled: z<boolean, boolean, "volatile-defined">;
|
|
@@ -82,7 +80,6 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
82
80
|
modelCacheTtlSeconds: z<number, number, "volatile-defined">;
|
|
83
81
|
catalogFetchTimeoutSeconds: z<number, number, "volatile-defined">;
|
|
84
82
|
openRouterSyncMinutes: z<number, number, "volatile-defined">;
|
|
85
|
-
raccoonCheckinMinutes: z<number, number, "volatile-defined">;
|
|
86
83
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
87
84
|
loomyEnabled: z<boolean, boolean, "volatile-defined">;
|
|
88
85
|
raccoonEnabled: z<boolean, boolean, "volatile-defined">;
|
|
@@ -117,7 +114,6 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
117
114
|
modelCacheTtlSeconds: z<number, number, "volatile-defined">;
|
|
118
115
|
catalogFetchTimeoutSeconds: z<number, number, "volatile-defined">;
|
|
119
116
|
openRouterSyncMinutes: z<number, number, "volatile-defined">;
|
|
120
|
-
raccoonCheckinMinutes: z<number, number, "volatile-defined">;
|
|
121
117
|
}>>, "plain">;
|
|
122
118
|
export interface ResolvedConfig {
|
|
123
119
|
loomyEnabled: boolean;
|
|
@@ -153,7 +149,6 @@ export interface ResolvedConfig {
|
|
|
153
149
|
modelCacheTtlMs: number;
|
|
154
150
|
catalogFetchTimeoutMs: number;
|
|
155
151
|
openRouterSyncMinutes: number;
|
|
156
|
-
raccoonCheckinMinutes: number;
|
|
157
152
|
}
|
|
158
153
|
export type ParsedConfig = {
|
|
159
154
|
readonly [K in keyof Config]?: unknown;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -59,27 +59,28 @@ export { OpenRouterCatalog, buildOpenRouterIndex, parseOpenRouterModels, readOpe
|
|
|
59
59
|
export type { OpenRouterIndex, OpenRouterLookup, OpenRouterModel, OpenRouterStatus, OpenRouterSyncResult, } from './openrouter.ts';
|
|
60
60
|
export { LoomyProvider, LOOMY_BASE, LOOMY_VERSION, loomyGatewaySessionValid } from './loomy.ts';
|
|
61
61
|
export { RaccoonProvider, RACCOON_BASE, RACCOON_CLIENT_VERSION, raccoonAccessTokenValid, raccoonRefreshCandidate, } from './raccoon.ts';
|
|
62
|
-
export { TraeProvider, TRAE_CHAT_FUNCTIONS, TRAE_DIRECTORY_FUNCTIONS, mergeTraeModels, normalizeEffort, openTraeStream, parseTraeGlobalUsage, parseTraeRemoteModels, parseTraeUsage, parseTraeWireModels, traeMessages, traeTools, } from './trae.ts';
|
|
62
|
+
export { TraeProvider, TRAE_CHAT_FUNCTIONS, TRAE_CHECKIN_REQ_SOURCE, TRAE_DIRECTORY_FUNCTIONS, mergeTraeModels, normalizeEffort, openTraeStream, parseTraeCheckin, parseTraeGlobalUsage, parseTraeRemoteModels, parseTraeUsage, parseTraeWireModels, traeMessages, traeTools, } from './trae.ts';
|
|
63
63
|
export type { TraeCatalog, TraeModelMeta, TraeProviderOptions, TraeRemoteModel, TraeWireModel, } from './trae.ts';
|
|
64
64
|
export { TRAE_APP_ID, TRAE_REGION_PROFILES, TRAE_SOLO_CHAT_PATH, TRAE_WEB_USER_AGENT } from './trae-endpoints.ts';
|
|
65
65
|
export type { TraeRegionProfile } from './trae-endpoints.ts';
|
|
66
|
+
export { parseTraeClientStorage, readTraeClientLogin, traeClientDataDirs } from './trae-client.ts';
|
|
67
|
+
export type { TraeClientDetection, TraeClientLoginState, TraeClientReadOptions } from './trae-client.ts';
|
|
66
68
|
export { TraeWebLogin, machineIdOf } from './trae-login.ts';
|
|
67
69
|
export type { TraeLoginAccount, TraeWebLoginOptions } from './trae-login.ts';
|
|
68
70
|
export { CodeArtsProvider, CODEARTS_DEFAULT_EFFORT, CODEARTS_EFFORTS, clampMaxTokens, codeartsEfforts, normalizeCodeArtsModelId, parseCodeArtsPackage, parseCodeArtsRoster, pickActiveCredit, priceFactorOfLabel, } from './codearts.ts';
|
|
69
71
|
export type { CodeArtsActivity, CodeArtsModelRow, CodeArtsPackageInfo, CodeArtsProviderOptions, } from './codearts.ts';
|
|
70
72
|
export { CODEARTS_BENEFIT_FALLBACK, CODEARTS_CHAT_URL, CODEARTS_CONTEXT_WINDOWS, CODEARTS_EFFORT_LEVELS, CODEARTS_FALLBACK_CONTEXT_WINDOW, CODEARTS_MAX_OUTPUT_TOKENS, CODEARTS_SNAP_BASE, CODEARTS_VISION_MODELS, codeartsContextWindow, } from './codearts-endpoints.ts';
|
|
71
73
|
export { headersForFetch, signRequestHuawei } from './codearts-sign.ts';
|
|
72
|
-
export { RefreshTokenExpiredError, buildAuthorizeUrl, exchangeAuthorizationCode, exchangeRefreshToken, generateDpopKeyPair, generatePkcePair, readJwtExpiry, signDpopJws, } from './codearts-oauth.ts';
|
|
74
|
+
export { RefreshTokenExpiredError, buildAuthorizeUrl, exchangeAuthorizationCode, exchangeRefreshToken, generateDpopKeyPair, generatePkcePair, otSessionIdOf, readJwtExpiry, signDpopJws, } from './codearts-oauth.ts';
|
|
73
75
|
export type { CodeArtsCredential, DpopKeyPair, DpopPrivateJwk, PkcePair } from './codearts-oauth.ts';
|
|
74
76
|
export { CodeArtsLogin } from './codearts-login.ts';
|
|
75
77
|
export type { CodeArtsLoginAccount, CodeArtsLoginOptions } from './codearts-login.ts';
|
|
76
|
-
export { CODEARTS_SESSION_LIMIT, CODEARTS_SESSION_WINDOW_MS, CodeArtsSessionWindow, sharedSessionWindow, } from './codearts-window.ts';
|
|
77
78
|
export { ProviderHttpError, buildMessages, buildTools, consumeOpenAiStream, openAiStream, sseEvents } from './wire.ts';
|
|
78
79
|
export type { WireMessage } from './wire.ts';
|
|
79
80
|
export { API_PREFIX, buildRows } from './api.ts';
|
|
80
81
|
export { liveConfig, resolveConfig } from './config.ts';
|
|
81
82
|
export type { Config as ConfigShape, LiveConfig, ParsedConfig, ResolvedConfig } from './config.ts';
|
|
82
|
-
export type { CheckinStatus, ProviderStatus, QuotaPack, QuotaSnapshot } from './types.ts';
|
|
83
|
+
export type { CheckinStatus, ClientLoginStatus, ProviderStatus, QuotaPack, QuotaSnapshot } from './types.ts';
|
|
83
84
|
export declare const name = "llm-adapters";
|
|
84
85
|
export declare const inject: string[];
|
|
85
86
|
export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
@@ -116,7 +117,6 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
116
117
|
modelCacheTtlSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
117
118
|
catalogFetchTimeoutSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
118
119
|
openRouterSyncMinutes: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
119
|
-
raccoonCheckinMinutes: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
120
120
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
121
121
|
loomyEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
122
122
|
raccoonEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
|
|
@@ -151,6 +151,5 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
|
|
|
151
151
|
modelCacheTtlSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
152
152
|
catalogFetchTimeoutSeconds: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
153
153
|
openRouterSyncMinutes: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
154
|
-
raccoonCheckinMinutes: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
155
154
|
}>>, "plain">;
|
|
156
155
|
export declare function apply(ctx: Context, config: ParsedConfig): void;
|
package/lib/types/raccoon.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { ModelCatalog } from './catalog.ts';
|
|
|
15
15
|
import type { ProviderModel } from './catalog.ts';
|
|
16
16
|
import type { OpenRouterLookup } from './openrouter.ts';
|
|
17
17
|
import type { CheckinStatus, QuotaSnapshot } from './types.ts';
|
|
18
|
+
import { ProviderHttpError } from './wire.ts';
|
|
18
19
|
import type { ReadImage } from './wire.ts';
|
|
19
20
|
export declare const RACCOON_BASE = "https://xiaohuanxiong.com/api/web";
|
|
20
21
|
export declare const RACCOON_CLIENT_VERSION = "v1.0.35";
|
|
@@ -30,7 +31,68 @@ export interface RaccoonProviderOptions {
|
|
|
30
31
|
fetchImpl?: typeof fetch;
|
|
31
32
|
/** Injectable OpenRouter lookup for tests; defaults to the live catalog. */
|
|
32
33
|
openRouter?: OpenRouterLookup;
|
|
34
|
+
/** Injectable session-id bookkeeping for tests; defaults to the real one. */
|
|
35
|
+
sessionIds?: RaccoonSessionIds;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The upstream code for the account's token-throughput wall.
|
|
39
|
+
*
|
|
40
|
+
* It reads like a request-rate complaint (`您的模型请求频率超限`) and is not
|
|
41
|
+
* one: measured on a live account, ten large requests in ten seconds hit it
|
|
42
|
+
* while fifty small ones did not, and the window refills by throughput rather
|
|
43
|
+
* than by request count. The gateway sends no `Retry-After` and no
|
|
44
|
+
* `x-ratelimit-*` header, so nothing here can compute a wait either.
|
|
45
|
+
*/
|
|
46
|
+
export declare const RACCOON_RATE_LIMIT_CODE = "user_model_rate_limited";
|
|
47
|
+
/**
|
|
48
|
+
* Whether a rejection is the token wall rather than a generic failure.
|
|
49
|
+
*
|
|
50
|
+
* Matched on the status, the code and the wording, because the same condition
|
|
51
|
+
* arrives as an HTTP 429 and as an error frame inside a 200 stream.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isRaccoonRateLimited(status: number | undefined, text: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The wall, as an error the caller can tell apart from a generic 429.
|
|
56
|
+
*
|
|
57
|
+
* A subclass rather than a flag: the in-band path and the HTTP path would
|
|
58
|
+
* otherwise both be caught by the same handler, and the second pass would wrap
|
|
59
|
+
* the message the first pass had already rewritten.
|
|
60
|
+
*/
|
|
61
|
+
export declare class RaccoonRateLimitedError extends ProviderHttpError {
|
|
62
|
+
constructor(message: string);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One conversation's session id, stable across the turns of that conversation.
|
|
66
|
+
*
|
|
67
|
+
* The official client never opens a session per request — measured on its own
|
|
68
|
+
* logs, 125 calls over 26 minutes all carried one `sess-0-53927d74` — while an
|
|
69
|
+
* implementation that mints a fresh id per request tells the gateway that one
|
|
70
|
+
* account opened hundreds of sessions within minutes.
|
|
71
|
+
*
|
|
72
|
+
* The id is a pure function of (account, model) rather than a random value, so a
|
|
73
|
+
* restarted or hot-reloaded process continues the same conversation instead of
|
|
74
|
+
* presenting a new session, and two models stay two conversations. The principal
|
|
75
|
+
* itself never leaves the process — only a truncated digest of it does.
|
|
76
|
+
*/
|
|
77
|
+
export declare class RaccoonSessionIds {
|
|
78
|
+
private readonly principal;
|
|
79
|
+
/**
|
|
80
|
+
* @param principal - a value that is stable for one account and identifies it
|
|
81
|
+
* without being the token itself (the JWT's `sub`).
|
|
82
|
+
*/
|
|
83
|
+
constructor(principal: () => Promise<string | undefined>);
|
|
84
|
+
for(model: string): Promise<string | undefined>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* `sess-<sequence>-<8 hex>`, the shape the official client sends.
|
|
88
|
+
*
|
|
89
|
+
* The sequence is the official client's process-local counter and is pinned to 0
|
|
90
|
+
* here on purpose: an incrementing value would make one conversation look like a
|
|
91
|
+
* different session after a restart, which is the one thing this id exists to
|
|
92
|
+
* prevent. Identity lives in the digest, which is truncated on purpose — the id
|
|
93
|
+
* must separate accounts without naming them on the wire.
|
|
94
|
+
*/
|
|
95
|
+
export declare function raccoonSessionId(principal: string, model: string): string;
|
|
34
96
|
/** Whether the gateway accepts a token harvested from the browser. */
|
|
35
97
|
export declare function raccoonAccessTokenValid(token: string, fetchImpl?: typeof fetch): Promise<boolean>;
|
|
36
98
|
/**
|
|
@@ -43,9 +105,9 @@ export declare class RaccoonProvider {
|
|
|
43
105
|
readonly catalog: ModelCatalog;
|
|
44
106
|
private quotaCache;
|
|
45
107
|
private quotaInflight;
|
|
46
|
-
private checkinInflight;
|
|
47
108
|
private loginFlow;
|
|
48
109
|
private readonly openRouter;
|
|
110
|
+
private readonly sessionIds;
|
|
49
111
|
constructor(options: RaccoonProviderOptions);
|
|
50
112
|
status(): Promise<import("./auth.ts").AuthStatus>;
|
|
51
113
|
signedIn(): Promise<boolean>;
|
|
@@ -58,6 +120,14 @@ export declare class RaccoonProvider {
|
|
|
58
120
|
private login;
|
|
59
121
|
logoutAccount(): Promise<void>;
|
|
60
122
|
private headers;
|
|
123
|
+
/**
|
|
124
|
+
* The chat headers, including the session id the gateway counts sessions by.
|
|
125
|
+
*
|
|
126
|
+
* Only chat carries it: the catalogue, quota and check-in routes are plain
|
|
127
|
+
* business calls the official client sends outside any session, so leaving
|
|
128
|
+
* them alone keeps this change to the one route it was measured on.
|
|
129
|
+
*/
|
|
130
|
+
private chatHeaders;
|
|
61
131
|
/** Run one JSON call; a rejected token means this login needs a new scan. */
|
|
62
132
|
private authorizedJson;
|
|
63
133
|
private fetchModels;
|
|
@@ -76,12 +146,15 @@ export declare class RaccoonProvider {
|
|
|
76
146
|
* `point_grant_popups` on `setting_info`; the bills list is the durable proof.
|
|
77
147
|
*/
|
|
78
148
|
private dailyGrant;
|
|
79
|
-
checkinStatus(): Promise<CheckinStatus>;
|
|
80
|
-
claimCheckin(): Promise<CheckinStatus>;
|
|
81
149
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
150
|
+
* Read today's grant. **Read-only on purpose.**
|
|
151
|
+
*
|
|
152
|
+
* The gateway has no claim endpoint: points are issued on the day's first
|
|
153
|
+
* website visit. Replaying that visit (`setting_info`) can trigger the grant
|
|
154
|
+
* from here, but doing so takes a state-changing action on the account without
|
|
155
|
+
* the user asking for it in that moment, so the plugin only reports. Claiming
|
|
156
|
+
* is the website's job, and the card links there.
|
|
84
157
|
*/
|
|
85
|
-
|
|
158
|
+
checkinStatus(): Promise<CheckinStatus>;
|
|
86
159
|
stream(options: GenerateOptions, model: string, readImage: ReadImage | undefined, onUnauthorized?: () => void): AsyncGenerator<StreamChunk>;
|
|
87
160
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The local Trae desktop client's sign-in state.
|
|
3
|
+
*
|
|
4
|
+
* Trae's check-in route insists on the device id its own client registered, so
|
|
5
|
+
* the website login cannot use it. When the client is installed and already
|
|
6
|
+
* signed in, the plugin can reuse that state instead:
|
|
7
|
+
*
|
|
8
|
+
* - the `Cloud-IDE-JWT` stored under `iCubeAuthInfo://icube.cloudide`;
|
|
9
|
+
* - the 16-digit device id encoded in the `iCubeAuthInfo://icube-dc:<id>` key;
|
|
10
|
+
* - `telemetry.machineId`, the value the client sends as `x-machine-id`.
|
|
11
|
+
*
|
|
12
|
+
* The stored value uses the client's own AES-CBC envelope: six magic bytes, a
|
|
13
|
+
* 32-byte key material, then the ciphertext. The AES key and IV are derived
|
|
14
|
+
* from that key material with SHA-512 and a fixed salt, and the plaintext is a
|
|
15
|
+
* SHA-512 digest followed by the JSON document. Nothing here writes to the
|
|
16
|
+
* client, and the token is never returned by the settings API.
|
|
17
|
+
*
|
|
18
|
+
* @module dsh-llm-adapters/trae-client
|
|
19
|
+
*/
|
|
20
|
+
import type { TraeRegion } from './brand.ts';
|
|
21
|
+
import type { ClientLoginStatus } from './types.ts';
|
|
22
|
+
/** What the provider stores after importing a client login. */
|
|
23
|
+
export interface TraeClientLoginState {
|
|
24
|
+
region: TraeRegion;
|
|
25
|
+
token: string;
|
|
26
|
+
deviceId: string;
|
|
27
|
+
machineId: string;
|
|
28
|
+
userId?: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
expiresAt?: number;
|
|
31
|
+
}
|
|
32
|
+
/** The settings-facing half (no token) plus the internal state when usable. */
|
|
33
|
+
export interface TraeClientDetection {
|
|
34
|
+
status: ClientLoginStatus;
|
|
35
|
+
state?: TraeClientLoginState;
|
|
36
|
+
}
|
|
37
|
+
export interface TraeClientReadOptions {
|
|
38
|
+
platform?: NodeJS.Platform;
|
|
39
|
+
env?: NodeJS.ProcessEnv;
|
|
40
|
+
home?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The client's data directory, most specific first.
|
|
44
|
+
*
|
|
45
|
+
* `DSH_TRAE_CLIENT_DIR` is a portable/test override; the region-specific
|
|
46
|
+
* variables let one machine keep separate CN and international installs.
|
|
47
|
+
*/
|
|
48
|
+
export declare function traeClientDataDirs(region: TraeRegion, options?: TraeClientReadOptions): string[];
|
|
49
|
+
/** Parse one `storage.json` document; exported for tests and diagnostics. */
|
|
50
|
+
export declare function parseTraeClientStorage(region: TraeRegion, storage: Record<string, unknown>): TraeClientDetection;
|
|
51
|
+
/** Read the currently selected region's client state from this machine. */
|
|
52
|
+
export declare function readTraeClientLogin(region: TraeRegion, options?: TraeClientReadOptions): TraeClientDetection;
|
|
@@ -33,6 +33,8 @@ export interface TraeRegionProfile {
|
|
|
33
33
|
* the dollar allowance instead.
|
|
34
34
|
*/
|
|
35
35
|
usageV2: boolean;
|
|
36
|
+
/** Daily check-in exists only on the mainland deployment. */
|
|
37
|
+
checkin: boolean;
|
|
36
38
|
}
|
|
37
39
|
export declare const TRAE_REGION_PROFILES: Record<TraeRegion, TraeRegionProfile>;
|
|
38
40
|
/** The app id Trae's own clients send; not account-specific. */
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trae sign-in through the provider's own website.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* This flow does not read a desktop install: it opens a dedicated browser
|
|
5
5
|
* profile, lets the user sign in on trae.cn / trae.ai, and then takes the
|
|
6
6
|
* session the website itself created. Two things come out of that session:
|
|
7
7
|
*
|
|
8
8
|
* - the `Cloud-IDE-JWT` token, fetched with the page's own cookies through
|
|
9
9
|
* `/cloudide/api/v3/common/GetUserToken` — the same call the site makes;
|
|
10
10
|
* - the device fingerprint (`__msuuid__` from the page, or one generated in
|
|
11
|
-
* the page),
|
|
12
|
-
*
|
|
11
|
+
* the page), stored with the web login.
|
|
12
|
+
*
|
|
13
|
+
* The check-in route does not accept this web fingerprint, so check-in is
|
|
14
|
+
* enabled only for the separate client-login import in `trae-client.ts`.
|
|
13
15
|
*
|
|
14
16
|
* Cookies are captured over the DevTools protocol and kept in the plugin's own
|
|
15
17
|
* account store so the short-lived JWT can be renewed later; the dedicated
|
package/lib/types/trae.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Trae provider: model directory, SOLO chat and
|
|
2
|
+
* Trae provider: model directory, SOLO chat, quota and client check-in.
|
|
3
3
|
*
|
|
4
4
|
* Trae's website login yields the same `Cloud-IDE-JWT` its own clients use, so
|
|
5
|
-
* the plugin talks to the same gateways they do.
|
|
5
|
+
* the plugin talks to the same gateways they do. A second sign-in method copies
|
|
6
|
+
* that token from the local desktop client; only that method carries the device
|
|
7
|
+
* id the daily check-in route accepts. Two catalog sources are merged:
|
|
6
8
|
*
|
|
7
9
|
* - the Remote directory (`/api/remote/v1/models`) is the model skeleton —
|
|
8
10
|
* display name, context window, multiplier, reasoning options, multimodal;
|
|
@@ -29,7 +31,8 @@ import { ModelCatalog } from './catalog.ts';
|
|
|
29
31
|
import type { ProviderModel } from './catalog.ts';
|
|
30
32
|
import type { LoginStatus } from './login-page.ts';
|
|
31
33
|
import type { OpenRouterIndex, OpenRouterLookup } from './openrouter.ts';
|
|
32
|
-
import type {
|
|
34
|
+
import type { TraeClientDetection } from './trae-client.ts';
|
|
35
|
+
import type { CheckinStatus, ClientLoginStatus, QuotaSnapshot } from './types.ts';
|
|
33
36
|
import type { ReadImage, WireMessage } from './wire.ts';
|
|
34
37
|
/** Chat functions, in the order Trae itself prefers them. */
|
|
35
38
|
export declare const TRAE_CHAT_FUNCTIONS: readonly string[];
|
|
@@ -103,6 +106,8 @@ export interface TraeProviderOptions {
|
|
|
103
106
|
logger?: (message: string) => void;
|
|
104
107
|
/** The shared OpenRouter snapshot; a private one is created when omitted. */
|
|
105
108
|
openRouter?: OpenRouterLookup;
|
|
109
|
+
/** Overridable in tests; defaults to reading this machine's Trae client. */
|
|
110
|
+
clientLogin?: () => TraeClientDetection;
|
|
106
111
|
}
|
|
107
112
|
/**
|
|
108
113
|
* The persistent browser profile used for one region.
|
|
@@ -154,6 +159,12 @@ export declare function parseTraeUsage(payload: unknown): QuotaSnapshot;
|
|
|
154
159
|
* dollars and must not be printed as credits.
|
|
155
160
|
*/
|
|
156
161
|
export declare function parseTraeGlobalUsage(payload: unknown): QuotaSnapshot;
|
|
162
|
+
/**
|
|
163
|
+
* The check-in body's `req_source`: 1 is the IDE, 2 is Trae Work. The desktop
|
|
164
|
+
* client sends 1; the claim route rejects the plugin's former empty body.
|
|
165
|
+
*/
|
|
166
|
+
export declare const TRAE_CHECKIN_REQ_SOURCE = 1;
|
|
167
|
+
export declare function parseTraeCheckin(payload: unknown): CheckinStatus;
|
|
157
168
|
export declare class TraeProvider {
|
|
158
169
|
private readonly options;
|
|
159
170
|
private catalogInstance;
|
|
@@ -185,6 +196,7 @@ export declare class TraeProvider {
|
|
|
185
196
|
possiblyExpired?: boolean;
|
|
186
197
|
account?: import("./auth.ts").LocalAccount;
|
|
187
198
|
error?: string;
|
|
199
|
+
loginSource?: "web" | "client";
|
|
188
200
|
}>;
|
|
189
201
|
signedIn(): Promise<boolean>;
|
|
190
202
|
models(fresh?: boolean): Promise<readonly ProviderModel[]>;
|
|
@@ -199,6 +211,20 @@ export declare class TraeProvider {
|
|
|
199
211
|
private fetchWire;
|
|
200
212
|
private fetchCatalog;
|
|
201
213
|
quota(fresh?: boolean): Promise<QuotaSnapshot>;
|
|
214
|
+
/**
|
|
215
|
+
* The second sign-in method: the desktop client's current state.
|
|
216
|
+
*
|
|
217
|
+
* Read-only; the settings page only receives the non-secret status, never
|
|
218
|
+
* the token.
|
|
219
|
+
*/
|
|
220
|
+
localClientLogin(): Promise<ClientLoginStatus>;
|
|
221
|
+
private readClientLogin;
|
|
222
|
+
/** Import the desktop client's token, device id and machine id. */
|
|
223
|
+
importClientLogin(): Promise<void>;
|
|
224
|
+
private checkinContext;
|
|
225
|
+
checkinStatus(): Promise<CheckinStatus>;
|
|
226
|
+
/** Claim today's reward. The only state-changing call in this provider. */
|
|
227
|
+
claimCheckin(): Promise<CheckinStatus>;
|
|
202
228
|
stream(options: GenerateOptions, model: string, readImage: ReadImage | undefined, onInvalidate: () => void): AsyncGenerator<StreamChunk>;
|
|
203
229
|
startBrowserLogin(region?: TraeRegion): Promise<LoginStatus>;
|
|
204
230
|
browserLoginStatus(): LoginStatus;
|
package/lib/types/types.d.ts
CHANGED
|
@@ -53,6 +53,22 @@ export interface CheckinStatus {
|
|
|
53
53
|
/** A page the user can open when the provider has no claimable API. */
|
|
54
54
|
url?: string;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The local Trae client's state, as shown on the settings page.
|
|
58
|
+
*
|
|
59
|
+
* Never carries the token: only whether the client was found, whether a usable
|
|
60
|
+
* login was read, and the non-secret account fields.
|
|
61
|
+
*/
|
|
62
|
+
export interface ClientLoginStatus {
|
|
63
|
+
/** A Trae client data directory exists on this machine. */
|
|
64
|
+
available: boolean;
|
|
65
|
+
/** The stored login is present, unexpired and usable. */
|
|
66
|
+
signedIn: boolean;
|
|
67
|
+
name?: string;
|
|
68
|
+
userId?: string;
|
|
69
|
+
expiresAt?: number;
|
|
70
|
+
error?: string;
|
|
71
|
+
}
|
|
56
72
|
export interface ProviderStatus extends AuthStatus {
|
|
57
73
|
brand: BrandId;
|
|
58
74
|
/** Trae only: the region this reading belongs to. */
|
|
@@ -60,7 +76,9 @@ export interface ProviderStatus extends AuthStatus {
|
|
|
60
76
|
/** Present only after a successful quota read. */
|
|
61
77
|
quota?: QuotaSnapshot;
|
|
62
78
|
quotaError?: string;
|
|
63
|
-
/** Trae
|
|
79
|
+
/** Trae only: the second sign-in method, when the desktop client exists. */
|
|
80
|
+
clientLogin?: ClientLoginStatus;
|
|
81
|
+
/** Trae CN only, and only for a login imported from the client. */
|
|
64
82
|
checkin?: CheckinStatus;
|
|
65
83
|
}
|
|
66
84
|
export type { LocalAccount };
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-llm-adapters",
|
|
3
3
|
"displayName": "Local LLM Adapters",
|
|
4
|
-
"description": "DeepSeek Harness adapters for Loomy, SenseTime Raccoon and Trae. Sign in through the provider’s own web page (WeChat QR for Loomy and Raccoon, a dedicated browser window for Trae), then use the account’s models with quota, thinking levels and per-model settings pages.",
|
|
5
|
-
"version": "1.2.
|
|
4
|
+
"description": "DeepSeek Harness adapters for Loomy, SenseTime Raccoon and Trae. Sign in through the provider’s own web page (WeChat QR for Loomy and Raccoon, a dedicated browser window for Trae), or reuse a signed-in local Trae client for the mainland check-in, then use the account’s models with quota, thinking levels and per-model settings pages.",
|
|
5
|
+
"version": "1.2.2",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"author": "k1-h2",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"qr-login",
|
|
20
20
|
"wechat-login",
|
|
21
21
|
"web-login",
|
|
22
|
+
"client-login",
|
|
22
23
|
"openai-compatible",
|
|
23
24
|
"model-provider",
|
|
24
25
|
"trae",
|