dsh-connect-trae 2.10.0 → 3.0.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/CHANGELOG.md +479 -0
- package/README.en.md +4 -1
- package/README.md +5 -1
- package/lib/client.js +802 -287
- package/lib/index.d.ts +120 -23
- package/lib/index.js +402 -168
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -452,6 +452,202 @@ function createTraeAdapter(options) {
|
|
|
452
452
|
};
|
|
453
453
|
}
|
|
454
454
|
//#endregion
|
|
455
|
+
//#region src/status-paths.ts
|
|
456
|
+
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
457
|
+
const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
458
|
+
/** Plugin-owned live model refresh endpoint. */
|
|
459
|
+
const TRAE_MODELS_REFRESH_PATH = "/plugins/dsh-connect-trae/models/refresh";
|
|
460
|
+
/**
|
|
461
|
+
* Plugin-owned availability probe: POST the model ids to test, get one verdict
|
|
462
|
+
* per model. Unlike every read-only sibling this route SPENDS quota — a real
|
|
463
|
+
* (minimal) chat call per model — so it is user-initiated only, and the card
|
|
464
|
+
* labels the button as consuming a small amount.
|
|
465
|
+
*/
|
|
466
|
+
const TRAE_MODELS_TEST_PATH = "/plugins/dsh-connect-trae/models/test";
|
|
467
|
+
/** Plugin-owned local account rescan endpoint. */
|
|
468
|
+
const TRAE_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-trae/accounts/refresh";
|
|
469
|
+
/**
|
|
470
|
+
* General balance of EVERY account in a region, for the composer panel's table.
|
|
471
|
+
*
|
|
472
|
+
* Separate from the usage route on purpose: the usage document backs the
|
|
473
|
+
* 5-minute readout, and multiplying its upstream reads by the account count
|
|
474
|
+
* would charge a healthy account for something it never looks at — the same
|
|
475
|
+
* principle that keeps `creditAlternatives` firing only when a balance hits
|
|
476
|
+
* zero. This one is paid when the panel is actually opened, which is the only
|
|
477
|
+
* place the table is shown.
|
|
478
|
+
*/
|
|
479
|
+
const TRAE_ACCOUNT_CREDITS_PATH = "/plugins/dsh-connect-trae/account-credits";
|
|
480
|
+
/**
|
|
481
|
+
* Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
|
|
482
|
+
* route in this plugin that changes upstream account state.
|
|
483
|
+
*/
|
|
484
|
+
const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
485
|
+
/** Query parameter naming the region a card request addresses. */
|
|
486
|
+
const TRAE_REGION_PARAM = "region";
|
|
487
|
+
/** Every region, in card tab order. */
|
|
488
|
+
const TRAE_REGIONS = ["cn", "ai"];
|
|
489
|
+
/**
|
|
490
|
+
* Address one region's status route. The two regions are separate provider
|
|
491
|
+
* stacks; every card request carries the region whose tab the user is on.
|
|
492
|
+
*/
|
|
493
|
+
function withTraeRegion(path, region) {
|
|
494
|
+
return `${path}?${TRAE_REGION_PARAM}=${region}`;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Read the region parameter off a status-route URL. Absent means the domestic
|
|
498
|
+
* tab (`cn`); a present-but-unknown value returns undefined so the route can
|
|
499
|
+
* answer 400 instead of guessing.
|
|
500
|
+
*/
|
|
501
|
+
function regionOfTraeStatusUrl(url) {
|
|
502
|
+
const at = url.indexOf("?");
|
|
503
|
+
const value = at === -1 ? null : new URLSearchParams(url.slice(at + 1)).get(TRAE_REGION_PARAM);
|
|
504
|
+
if (value === null || value === "") return "cn";
|
|
505
|
+
return TRAE_REGIONS.includes(value) ? value : void 0;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
509
|
+
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
510
|
+
*/
|
|
511
|
+
function unwrapVolatile(value) {
|
|
512
|
+
if (value !== null && typeof value === "object" && typeof value.get === "function") return value.get();
|
|
513
|
+
return value;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Deep copy of a value with every `{get(): T}` live reference replaced by the
|
|
517
|
+
* value it resolves to.
|
|
518
|
+
*
|
|
519
|
+
* DSH 0.1.7 hands volatile-marked fields back as live references, and a live
|
|
520
|
+
* reference is still `typeof === 'object'` — so it passes a naive object check
|
|
521
|
+
* and every lookup on it is `undefined`. Worse, spreading one (`{ ...ref }`)
|
|
522
|
+
* does NOT read the field: it produces `{ get: <function> }`. Any merge that
|
|
523
|
+
* spreads a resolved field to preserve its siblings — the card's "write one
|
|
524
|
+
* region, keep the other" write, or the account map's spread — would otherwise
|
|
525
|
+
* DROP every sibling and leak a function into the document.
|
|
526
|
+
*
|
|
527
|
+
* Both halves therefore unwrap before touching a resolved field. This lives in
|
|
528
|
+
* the node-free host↔client bridge because the browser half needs it too.
|
|
529
|
+
*
|
|
530
|
+
* Non-reference values are recursed into so a nested volatile field is caught
|
|
531
|
+
* as well; arrays and objects are rebuilt rather than mutated, so the caller's
|
|
532
|
+
* value is never touched.
|
|
533
|
+
*/
|
|
534
|
+
function unwrapVolatileDeep(value) {
|
|
535
|
+
if (value === null || typeof value !== "object") return value;
|
|
536
|
+
if (typeof value.get === "function") return unwrapVolatileDeep(value.get());
|
|
537
|
+
if (Array.isArray(value)) return value.map((entry) => unwrapVolatileDeep(entry));
|
|
538
|
+
const source = value;
|
|
539
|
+
const out = {};
|
|
540
|
+
for (const key of Object.keys(source)) out[key] = unwrapVolatileDeep(source[key]);
|
|
541
|
+
return out;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Read the per-region account selections out of a settings value.
|
|
545
|
+
*
|
|
546
|
+
* SHARED because `accounts` now has TWO writers: the card, and the composer
|
|
547
|
+
* panel's account table (2.14.0). Two writers of one field must use one merge
|
|
548
|
+
* rule, or they drift — and the rule is not obvious.
|
|
549
|
+
*
|
|
550
|
+
* Deep-unwraps first: on DSH 0.1.7 a volatile field arrives as a `{get(): T}`
|
|
551
|
+
* live reference, which passes the `typeof === 'object'` check and would be
|
|
552
|
+
* returned as if it were the map. Every lookup on it is then `undefined`, and
|
|
553
|
+
* the merge (`{ ...configured, [region]: id }`) would spread a reference into
|
|
554
|
+
* `{get: <function>}` — dropping the other region's selection and leaking a
|
|
555
|
+
* function into the settings document.
|
|
556
|
+
*/
|
|
557
|
+
function configuredAccountsOf(configured) {
|
|
558
|
+
const accounts = unwrapVolatileDeep(configured)?.accounts;
|
|
559
|
+
return typeof accounts === "object" && accounts !== null ? accounts : {};
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Whether the host-side "follow the Trae app's sign-in" switch is on (issue
|
|
563
|
+
* #29). Absent reads as OFF, so an existing configuration keeps the strict
|
|
564
|
+
* bind it shipped with and the switch is an opt-in — the same opt-out
|
|
565
|
+
* semantics as {@link regionEnabledOf}, for the same reason: a config written
|
|
566
|
+
* before this field existed cannot ask for behavior it never declared.
|
|
567
|
+
*
|
|
568
|
+
* Reads the whole settings section (or the bare field); deep-unwraps first
|
|
569
|
+
* because on DSH 0.1.7 a volatile field arrives as a `{get(): T}` live
|
|
570
|
+
* reference, whose `typeof === 'object'` would otherwise pass a naive check.
|
|
571
|
+
*/
|
|
572
|
+
function followAppLoginEnabled(configured) {
|
|
573
|
+
return unwrapVolatileDeep(configured)?.followAppLogin === true;
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Build the next `regions` settings value for the card's save. The write
|
|
577
|
+
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
578
|
+
* is carried over untouched, so switching accounts never clobbers the other
|
|
579
|
+
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
580
|
+
* from an empty document.
|
|
581
|
+
*/
|
|
582
|
+
function nextRegionSlots(regions, region, slot) {
|
|
583
|
+
const unwrapped = unwrapVolatileDeep(regions);
|
|
584
|
+
return {
|
|
585
|
+
...typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {},
|
|
586
|
+
[region]: slot
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Narrow a settings value to the `regions` map. Accepts EITHER the whole
|
|
591
|
+
* settings section (`{ regions: {...}, ... }`) or the `regions` map itself, and
|
|
592
|
+
* unwraps the former. This tolerance is deliberate: passing the whole section
|
|
593
|
+
* where the map was expected was a real shipped bug — the lookup then read
|
|
594
|
+
* `section['cn']` (absent), so the card's checkbox reported `true` forever and
|
|
595
|
+
* clicking it appeared to do nothing even though the write succeeded.
|
|
596
|
+
*
|
|
597
|
+
* Unwraps live references first: on DSH 0.1.7 the `regions` field arrives as a
|
|
598
|
+
* `{get(): T}` reference, which passes the `typeof === 'object'` check below
|
|
599
|
+
* and would otherwise be returned as if it were the map — making every region
|
|
600
|
+
* read back as absent (and the enabled flag read as "on" forever).
|
|
601
|
+
*/
|
|
602
|
+
function regionsMapOf(value) {
|
|
603
|
+
const unwrapped = unwrapVolatileDeep(value);
|
|
604
|
+
if (typeof unwrapped !== "object" || unwrapped === null || Array.isArray(unwrapped)) return {};
|
|
605
|
+
const record = unwrapped;
|
|
606
|
+
const nested = record["regions"];
|
|
607
|
+
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) return nested;
|
|
608
|
+
return record;
|
|
609
|
+
}
|
|
610
|
+
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
611
|
+
function regionSlotOf(value, region) {
|
|
612
|
+
const slot = unwrapVolatile(regionsMapOf(value)[region]);
|
|
613
|
+
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
617
|
+
* explicit `false` disables it, so a config written before this switch existed
|
|
618
|
+
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
619
|
+
* providers running exactly as before. The Host reads the same rule through
|
|
620
|
+
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
621
|
+
*
|
|
622
|
+
* `value` may be the whole settings section or the `regions` map (see
|
|
623
|
+
* {@link regionsMapOf}).
|
|
624
|
+
*/
|
|
625
|
+
function regionEnabledOf(value, region) {
|
|
626
|
+
return regionSlotOf(value, region)["enabled"] !== false;
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
630
|
+
* the target region's `enabled` flag changes: every other field of that slot
|
|
631
|
+
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
632
|
+
* region's slot are carried over verbatim, so switching a provider off never
|
|
633
|
+
* discards the user's model picks and switching it back on restores them.
|
|
634
|
+
*
|
|
635
|
+
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
636
|
+
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
637
|
+
* for a region that is signed OUT — which is precisely the region a user wants
|
|
638
|
+
* to switch off (no international install, no international account).
|
|
639
|
+
*
|
|
640
|
+
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
641
|
+
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
642
|
+
* 'regions', ...)` needs.
|
|
643
|
+
*/
|
|
644
|
+
function nextRegionEnabled(value, region, enabled) {
|
|
645
|
+
return nextRegionSlots(regionsMapOf(value), region, {
|
|
646
|
+
...regionSlotOf(value, region),
|
|
647
|
+
enabled
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
//#endregion
|
|
455
651
|
//#region src/decrypt.ts
|
|
456
652
|
const TRAE_AUTH_STORAGE_KEY = "iCubeAuthInfo://icube.cloudide";
|
|
457
653
|
const SALT_A = Uint8Array.from([
|
|
@@ -1171,13 +1367,52 @@ var TraeCredentialStore = class {
|
|
|
1171
1367
|
return this.edition === "auto" ? all.filter((candidate) => candidate.source === "desktop" || candidate.edition === cliEdition) : all.filter((candidate) => candidate.edition === this.edition && (candidate.source === "desktop" || candidate.edition === cliEdition));
|
|
1172
1368
|
}
|
|
1173
1369
|
/**
|
|
1174
|
-
* Deterministic default when no account is explicitly selected: the
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1370
|
+
* Deterministic default when no account is explicitly selected: the account
|
|
1371
|
+
* the Trae desktop app is signed in to RIGHT NOW, else the first discovered
|
|
1372
|
+
* one. This is NOT credit-seeking — it never reorders accounts to find one
|
|
1373
|
+
* with general credits. The plugin bills exactly the account the user
|
|
1374
|
+
* selected, the app's current sign-in when nothing has been selected, and
|
|
1375
|
+
* only then the first account.
|
|
1376
|
+
*
|
|
1377
|
+
* The app's live sign-in outranks the plugin's own refreshed copies (issue
|
|
1378
|
+
* #29): after the user switches accounts in Trae, the desktop storage file
|
|
1379
|
+
* holds the NEW login while the plugin's copy of the old one still resolves,
|
|
1380
|
+
* so a bare `credentials[0]` kept defaulting to a sign-in the app has
|
|
1381
|
+
* abandoned — and `dsh-connect-workbuddy`, whose `preferred()` ranks the live
|
|
1382
|
+
* file first, followed the app instead. Ordering only: the VALUE behind an
|
|
1383
|
+
* account id is still whichever copy expires later (`readAll`), so a refresh
|
|
1384
|
+
* the plugin performed is never discarded by this preference.
|
|
1178
1385
|
*/
|
|
1179
1386
|
preferred(credentials) {
|
|
1180
|
-
return credentials[0];
|
|
1387
|
+
return credentials.find((credential) => credential.source === "desktop") ?? credentials[0];
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* The account the Trae app is signed in to right now, when that differs from
|
|
1391
|
+
* the account this store is bound to (issue #29).
|
|
1392
|
+
*
|
|
1393
|
+
* This is a READ, not a rebind: `current()` keeps resolving the bound
|
|
1394
|
+
* account verbatim so billing never moves without a decision — the store
|
|
1395
|
+
* only reports the mismatch, and the card (hint row) or the host (follow
|
|
1396
|
+
* switch) turns it into a settings write the user can see and undo. Absent
|
|
1397
|
+
* when nothing is live, when the live account IS the bound one, or when the
|
|
1398
|
+
* binding itself already fell through to the live account.
|
|
1399
|
+
*/
|
|
1400
|
+
async liveLogin() {
|
|
1401
|
+
if (this.accountId === void 0 || this.accountId === "") return void 0;
|
|
1402
|
+
const credentials = await this.readAll();
|
|
1403
|
+
const live = this.preferred(credentials);
|
|
1404
|
+
if (live === void 0) return void 0;
|
|
1405
|
+
const liveId = traeAccountId(live);
|
|
1406
|
+
if (liveId === this.accountId) return void 0;
|
|
1407
|
+
return {
|
|
1408
|
+
id: liveId,
|
|
1409
|
+
accountName: live.accountName ?? (live.userId || `${live.edition} account`),
|
|
1410
|
+
edition: live.edition,
|
|
1411
|
+
region: regionOfCredential(live),
|
|
1412
|
+
source: live.source,
|
|
1413
|
+
tokenExpiresAtMs: live.expiresAtMs,
|
|
1414
|
+
selected: false
|
|
1415
|
+
};
|
|
1181
1416
|
}
|
|
1182
1417
|
async accounts() {
|
|
1183
1418
|
const credentials = await this.readAll();
|
|
@@ -1266,13 +1501,16 @@ var TraeCredentialStore = class {
|
|
|
1266
1501
|
*/
|
|
1267
1502
|
async readAll() {
|
|
1268
1503
|
const { credentials: desktop } = await this.readDesktopAll();
|
|
1269
|
-
const
|
|
1270
|
-
|
|
1271
|
-
if (!this.matchesRegion(
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1504
|
+
const byAccount = /* @__PURE__ */ new Map();
|
|
1505
|
+
const consider = (credential) => {
|
|
1506
|
+
if (!this.matchesRegion(credential)) return;
|
|
1507
|
+
const id = traeAccountId(credential);
|
|
1508
|
+
const incumbent = byAccount.get(id);
|
|
1509
|
+
if (incumbent === void 0 || credential.expiresAtMs > incumbent.expiresAtMs) byAccount.set(id, credential);
|
|
1510
|
+
};
|
|
1511
|
+
for (const credential of desktop) consider(credential);
|
|
1512
|
+
for (const own of await this.readOwns()) consider(own);
|
|
1513
|
+
return [...byAccount.values()];
|
|
1276
1514
|
}
|
|
1277
1515
|
/**
|
|
1278
1516
|
* Which paths were tried and why each one failed. Read-only and token-free:
|
|
@@ -3924,159 +4162,6 @@ var TraeUsageClient = class {
|
|
|
3924
4162
|
}
|
|
3925
4163
|
};
|
|
3926
4164
|
//#endregion
|
|
3927
|
-
//#region src/status-paths.ts
|
|
3928
|
-
/** Plugin-owned usage endpoint consumed by its browser half. */
|
|
3929
|
-
const TRAE_USAGE_PATH = "/plugins/dsh-connect-trae/usage";
|
|
3930
|
-
/** Plugin-owned live model refresh endpoint. */
|
|
3931
|
-
const TRAE_MODELS_REFRESH_PATH = "/plugins/dsh-connect-trae/models/refresh";
|
|
3932
|
-
/**
|
|
3933
|
-
* Plugin-owned availability probe: POST the model ids to test, get one verdict
|
|
3934
|
-
* per model. Unlike every read-only sibling this route SPENDS quota — a real
|
|
3935
|
-
* (minimal) chat call per model — so it is user-initiated only, and the card
|
|
3936
|
-
* labels the button as consuming a small amount.
|
|
3937
|
-
*/
|
|
3938
|
-
const TRAE_MODELS_TEST_PATH = "/plugins/dsh-connect-trae/models/test";
|
|
3939
|
-
/** Plugin-owned local account rescan endpoint. */
|
|
3940
|
-
const TRAE_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-trae/accounts/refresh";
|
|
3941
|
-
/**
|
|
3942
|
-
* Plugin-owned daily check-in claim endpoint. POST, loopback-only, and the only
|
|
3943
|
-
* route in this plugin that changes upstream account state.
|
|
3944
|
-
*/
|
|
3945
|
-
const TRAE_CHECKIN_PATH = "/plugins/dsh-connect-trae/checkin";
|
|
3946
|
-
/** Query parameter naming the region a card request addresses. */
|
|
3947
|
-
const TRAE_REGION_PARAM = "region";
|
|
3948
|
-
/** Every region, in card tab order. */
|
|
3949
|
-
const TRAE_REGIONS = ["cn", "ai"];
|
|
3950
|
-
/**
|
|
3951
|
-
* Address one region's status route. The two regions are separate provider
|
|
3952
|
-
* stacks; every card request carries the region whose tab the user is on.
|
|
3953
|
-
*/
|
|
3954
|
-
function withTraeRegion(path, region) {
|
|
3955
|
-
return `${path}?${TRAE_REGION_PARAM}=${region}`;
|
|
3956
|
-
}
|
|
3957
|
-
/**
|
|
3958
|
-
* Read the region parameter off a status-route URL. Absent means the domestic
|
|
3959
|
-
* tab (`cn`); a present-but-unknown value returns undefined so the route can
|
|
3960
|
-
* answer 400 instead of guessing.
|
|
3961
|
-
*/
|
|
3962
|
-
function regionOfTraeStatusUrl(url) {
|
|
3963
|
-
const at = url.indexOf("?");
|
|
3964
|
-
const value = at === -1 ? null : new URLSearchParams(url.slice(at + 1)).get(TRAE_REGION_PARAM);
|
|
3965
|
-
if (value === null || value === "") return "cn";
|
|
3966
|
-
return TRAE_REGIONS.includes(value) ? value : void 0;
|
|
3967
|
-
}
|
|
3968
|
-
/**
|
|
3969
|
-
* Peel ONE `{get(): T}` live reference, the shape DSH 0.1.7 delivers a
|
|
3970
|
-
* volatile-marked settings field as (see {@link unwrapVolatileDeep}).
|
|
3971
|
-
*/
|
|
3972
|
-
function unwrapVolatile(value) {
|
|
3973
|
-
if (value !== null && typeof value === "object" && typeof value.get === "function") return value.get();
|
|
3974
|
-
return value;
|
|
3975
|
-
}
|
|
3976
|
-
/**
|
|
3977
|
-
* Deep copy of a value with every `{get(): T}` live reference replaced by the
|
|
3978
|
-
* value it resolves to.
|
|
3979
|
-
*
|
|
3980
|
-
* DSH 0.1.7 hands volatile-marked fields back as live references, and a live
|
|
3981
|
-
* reference is still `typeof === 'object'` — so it passes a naive object check
|
|
3982
|
-
* and every lookup on it is `undefined`. Worse, spreading one (`{ ...ref }`)
|
|
3983
|
-
* does NOT read the field: it produces `{ get: <function> }`. Any merge that
|
|
3984
|
-
* spreads a resolved field to preserve its siblings — the card's "write one
|
|
3985
|
-
* region, keep the other" write, or the account map's spread — would otherwise
|
|
3986
|
-
* DROP every sibling and leak a function into the document.
|
|
3987
|
-
*
|
|
3988
|
-
* Both halves therefore unwrap before touching a resolved field. This lives in
|
|
3989
|
-
* the node-free host↔client bridge because the browser half needs it too.
|
|
3990
|
-
*
|
|
3991
|
-
* Non-reference values are recursed into so a nested volatile field is caught
|
|
3992
|
-
* as well; arrays and objects are rebuilt rather than mutated, so the caller's
|
|
3993
|
-
* value is never touched.
|
|
3994
|
-
*/
|
|
3995
|
-
function unwrapVolatileDeep(value) {
|
|
3996
|
-
if (value === null || typeof value !== "object") return value;
|
|
3997
|
-
if (typeof value.get === "function") return unwrapVolatileDeep(value.get());
|
|
3998
|
-
if (Array.isArray(value)) return value.map((entry) => unwrapVolatileDeep(entry));
|
|
3999
|
-
const source = value;
|
|
4000
|
-
const out = {};
|
|
4001
|
-
for (const key of Object.keys(source)) out[key] = unwrapVolatileDeep(source[key]);
|
|
4002
|
-
return out;
|
|
4003
|
-
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Build the next `regions` settings value for the card's save. The write
|
|
4006
|
-
* targets ONLY the signed-in account's region slot; every other region's slot
|
|
4007
|
-
* is carried over untouched, so switching accounts never clobbers the other
|
|
4008
|
-
* region's picks. Tolerates any stored shape (absent, non-object) by starting
|
|
4009
|
-
* from an empty document.
|
|
4010
|
-
*/
|
|
4011
|
-
function nextRegionSlots(regions, region, slot) {
|
|
4012
|
-
const unwrapped = unwrapVolatileDeep(regions);
|
|
4013
|
-
return {
|
|
4014
|
-
...typeof unwrapped === "object" && unwrapped !== null && !Array.isArray(unwrapped) ? unwrapped : {},
|
|
4015
|
-
[region]: slot
|
|
4016
|
-
};
|
|
4017
|
-
}
|
|
4018
|
-
/**
|
|
4019
|
-
* Narrow a settings value to the `regions` map. Accepts EITHER the whole
|
|
4020
|
-
* settings section (`{ regions: {...}, ... }`) or the `regions` map itself, and
|
|
4021
|
-
* unwraps the former. This tolerance is deliberate: passing the whole section
|
|
4022
|
-
* where the map was expected was a real shipped bug — the lookup then read
|
|
4023
|
-
* `section['cn']` (absent), so the card's checkbox reported `true` forever and
|
|
4024
|
-
* clicking it appeared to do nothing even though the write succeeded.
|
|
4025
|
-
*
|
|
4026
|
-
* Unwraps live references first: on DSH 0.1.7 the `regions` field arrives as a
|
|
4027
|
-
* `{get(): T}` reference, which passes the `typeof === 'object'` check below
|
|
4028
|
-
* and would otherwise be returned as if it were the map — making every region
|
|
4029
|
-
* read back as absent (and the enabled flag read as "on" forever).
|
|
4030
|
-
*/
|
|
4031
|
-
function regionsMapOf(value) {
|
|
4032
|
-
const unwrapped = unwrapVolatileDeep(value);
|
|
4033
|
-
if (typeof unwrapped !== "object" || unwrapped === null || Array.isArray(unwrapped)) return {};
|
|
4034
|
-
const record = unwrapped;
|
|
4035
|
-
const nested = record["regions"];
|
|
4036
|
-
if (typeof nested === "object" && nested !== null && !Array.isArray(nested)) return nested;
|
|
4037
|
-
return record;
|
|
4038
|
-
}
|
|
4039
|
-
/** One region's stored slot as a plain object; any other shape reads as empty. */
|
|
4040
|
-
function regionSlotOf(value, region) {
|
|
4041
|
-
const slot = unwrapVolatile(regionsMapOf(value)[region]);
|
|
4042
|
-
return typeof slot === "object" && slot !== null && !Array.isArray(slot) ? slot : {};
|
|
4043
|
-
}
|
|
4044
|
-
/**
|
|
4045
|
-
* Whether one region's provider is switched on. Opt-out semantics: only an
|
|
4046
|
-
* explicit `false` disables it, so a config written before this switch existed
|
|
4047
|
-
* (and the pre-region-split flat fields, which never carry `enabled`) keep both
|
|
4048
|
-
* providers running exactly as before. The Host reads the same rule through
|
|
4049
|
-
* `regionStateOf`, so card and Host can never disagree about a region's state.
|
|
4050
|
-
*
|
|
4051
|
-
* `value` may be the whole settings section or the `regions` map (see
|
|
4052
|
-
* {@link regionsMapOf}).
|
|
4053
|
-
*/
|
|
4054
|
-
function regionEnabledOf(value, region) {
|
|
4055
|
-
return regionSlotOf(value, region)["enabled"] !== false;
|
|
4056
|
-
}
|
|
4057
|
-
/**
|
|
4058
|
-
* Build the next `regions` settings value for a provider on/off toggle. ONLY
|
|
4059
|
-
* the target region's `enabled` flag changes: every other field of that slot
|
|
4060
|
-
* (its directory, selection, image opt-ins, context budgets) and every other
|
|
4061
|
-
* region's slot are carried over verbatim, so switching a provider off never
|
|
4062
|
-
* discards the user's model picks and switching it back on restores them.
|
|
4063
|
-
*
|
|
4064
|
-
* This is deliberately separate from {@link nextRegionSlots}: that helper
|
|
4065
|
-
* writes a whole slot from a signed-in tab's draft, while this one must work
|
|
4066
|
-
* for a region that is signed OUT — which is precisely the region a user wants
|
|
4067
|
-
* to switch off (no international install, no international account).
|
|
4068
|
-
*
|
|
4069
|
-
* `value` may be the whole settings section or the `regions` map; the RETURN
|
|
4070
|
-
* value is always the `regions` map, i.e. exactly what `settingsScope.set(
|
|
4071
|
-
* 'regions', ...)` needs.
|
|
4072
|
-
*/
|
|
4073
|
-
function nextRegionEnabled(value, region, enabled) {
|
|
4074
|
-
return nextRegionSlots(regionsMapOf(value), region, {
|
|
4075
|
-
...regionSlotOf(value, region),
|
|
4076
|
-
enabled
|
|
4077
|
-
});
|
|
4078
|
-
}
|
|
4079
|
-
//#endregion
|
|
4080
4165
|
//#region src/web-status.ts
|
|
4081
4166
|
/**
|
|
4082
4167
|
* The upstream's business code for "该设备今日已参与签到" — this DEVICE already
|
|
@@ -4219,6 +4304,10 @@ async function traeWebUsage(deps, region) {
|
|
|
4219
4304
|
message: safeMessage(error)
|
|
4220
4305
|
};
|
|
4221
4306
|
}
|
|
4307
|
+
let appLogin;
|
|
4308
|
+
try {
|
|
4309
|
+
appLogin = await deps.liveLogin?.(region);
|
|
4310
|
+
} catch {}
|
|
4222
4311
|
const account = {
|
|
4223
4312
|
accountId: accounts.find((item) => item.selected)?.id ?? "",
|
|
4224
4313
|
accountName: credential.accountName ?? credential.userId,
|
|
@@ -4226,6 +4315,7 @@ async function traeWebUsage(deps, region) {
|
|
|
4226
4315
|
region,
|
|
4227
4316
|
enabled,
|
|
4228
4317
|
accounts,
|
|
4318
|
+
...appLogin === void 0 ? {} : { appLogin },
|
|
4229
4319
|
models: deps.displayModels(region).map((model) => ({
|
|
4230
4320
|
...model,
|
|
4231
4321
|
...model.input === void 0 ? {} : { input: [...model.input] }
|
|
@@ -4297,6 +4387,37 @@ async function creditAlternatives(deps, region, accounts) {
|
|
|
4297
4387
|
* The region a request addresses, or a 400 answer. Absent parameter means the
|
|
4298
4388
|
* domestic tab; an unknown value is refused rather than guessed.
|
|
4299
4389
|
*/
|
|
4390
|
+
/**
|
|
4391
|
+
* The general balance of every account in one region.
|
|
4392
|
+
*
|
|
4393
|
+
* One upstream read per account — the same call `creditsOfAccount` already
|
|
4394
|
+
* makes for the exhausted-balance alternatives, widened to every account and to
|
|
4395
|
+
* zero balances. Costs nothing until the composer panel is opened, so the
|
|
4396
|
+
* 5-minute readout never pays for it (see {@link TRAE_ACCOUNT_CREDITS_PATH}).
|
|
4397
|
+
*
|
|
4398
|
+
* An account whose read fails or is unavailable still appears, without a
|
|
4399
|
+
* figure: the table lists how many accounts exist even when one cannot be
|
|
4400
|
+
* quoted, rather than dropping it and making the switch unavailable.
|
|
4401
|
+
*/
|
|
4402
|
+
async function traeAccountCredits(deps, region) {
|
|
4403
|
+
const accounts = (await deps.store(region).accounts()).filter((account) => account.region === region);
|
|
4404
|
+
if (accounts.length === 0) return [];
|
|
4405
|
+
return Promise.all(accounts.map(async (account) => {
|
|
4406
|
+
let generalAvailable;
|
|
4407
|
+
if (account.selected) try {
|
|
4408
|
+
generalAvailable = toCredits(await deps.client(region).snapshot()).generalAvailable;
|
|
4409
|
+
} catch {}
|
|
4410
|
+
else if (deps.creditsOfAccount !== void 0) try {
|
|
4411
|
+
generalAvailable = (await deps.creditsOfAccount(region, account.id))?.generalAvailable;
|
|
4412
|
+
} catch {}
|
|
4413
|
+
return {
|
|
4414
|
+
id: account.id,
|
|
4415
|
+
accountName: account.accountName,
|
|
4416
|
+
selected: account.selected,
|
|
4417
|
+
...generalAvailable === void 0 ? {} : { generalAvailable }
|
|
4418
|
+
};
|
|
4419
|
+
}));
|
|
4420
|
+
}
|
|
4300
4421
|
function requestRegion(req, res) {
|
|
4301
4422
|
const region = regionOfTraeStatusUrl(req.url ?? "/");
|
|
4302
4423
|
if (region === void 0) {
|
|
@@ -4329,6 +4450,27 @@ function registerTraeUsageRoute(ctx, deps) {
|
|
|
4329
4450
|
}
|
|
4330
4451
|
}
|
|
4331
4452
|
});
|
|
4453
|
+
/**
|
|
4454
|
+
* General balance of every account in the region — read only when the
|
|
4455
|
+
* composer panel is opened. One upstream read per account, paid once per
|
|
4456
|
+
* open rather than on the 5-minute cycle (see
|
|
4457
|
+
* {@link TRAE_ACCOUNT_CREDITS_PATH}).
|
|
4458
|
+
*/
|
|
4459
|
+
const disposeAccountCredits = ctx.webServer.register({
|
|
4460
|
+
kind: "exact",
|
|
4461
|
+
path: TRAE_ACCOUNT_CREDITS_PATH,
|
|
4462
|
+
handler: async (req, res) => {
|
|
4463
|
+
if (req.method !== "GET") return json(res, 405, { error: "method not allowed" });
|
|
4464
|
+
if (!loopbackOrigin(req)) return json(res, 403, { error: "origin-not-trusted" });
|
|
4465
|
+
const region = requestRegion(req, res);
|
|
4466
|
+
if (region === void 0) return;
|
|
4467
|
+
try {
|
|
4468
|
+
json(res, 200, { accounts: await traeAccountCredits(deps, region) });
|
|
4469
|
+
} catch (error) {
|
|
4470
|
+
json(res, 500, { error: safeMessage(error) });
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
});
|
|
4332
4474
|
const disposeAccounts = ctx.webServer.register({
|
|
4333
4475
|
kind: "exact",
|
|
4334
4476
|
path: TRAE_ACCOUNTS_REFRESH_PATH,
|
|
@@ -4462,6 +4604,7 @@ function registerTraeUsageRoute(ctx, deps) {
|
|
|
4462
4604
|
disposeRefresh();
|
|
4463
4605
|
disposeModelsTest();
|
|
4464
4606
|
disposeAccounts();
|
|
4607
|
+
disposeAccountCredits();
|
|
4465
4608
|
disposeUsage();
|
|
4466
4609
|
};
|
|
4467
4610
|
}, "dsh-connect-trae: Web usage route");
|
|
@@ -4769,7 +4912,8 @@ const Config = z.object({
|
|
|
4769
4912
|
"solo-sg"
|
|
4770
4913
|
]).default("auto").description("Trae edition hint")),
|
|
4771
4914
|
accountId: z.string().description("Deprecated: pre-split account selector, attributed to its own region"),
|
|
4772
|
-
showPointsInMainUi: asVolatile(z.boolean().default(
|
|
4915
|
+
showPointsInMainUi: asVolatile(z.boolean().default(false).description("在输入框工具栏显示积分读数「Trae CN · <数值>」(仅当选中的模型属于本插件时出现,可点击查看账号与积分明细)")),
|
|
4916
|
+
followAppLogin: asVolatile(z.boolean().default(false).description("跟随 Trae 桌面端当前登录账号换绑(打开后每次配置生效时重绑到 App 现在登录的账号)")),
|
|
4773
4917
|
accounts: asVolatile(accountSelectionConfig.description("Per-region account selections, keyed cn | ai")),
|
|
4774
4918
|
regions: asVolatile(z.dict(regionStateConfig).default({}).description("Per-region model directory and selection, keyed cn | ai")),
|
|
4775
4919
|
lastCatalog: z.array(modelConfig).description("Deprecated: pre-region-split CN model directory"),
|
|
@@ -5025,6 +5169,49 @@ function apply(ctx, config) {
|
|
|
5025
5169
|
rawDiagnostic: () => rawDiagnostic()
|
|
5026
5170
|
};
|
|
5027
5171
|
}
|
|
5172
|
+
/**
|
|
5173
|
+
* The app's current sign-in when it differs from the bound account (issue
|
|
5174
|
+
* #29). Pure read: the store resolves the bound account verbatim, so billing
|
|
5175
|
+
* never moves from here — the card's hint and the follow switch below are
|
|
5176
|
+
* the only writers.
|
|
5177
|
+
*/
|
|
5178
|
+
const liveLoginOf = async (region) => {
|
|
5179
|
+
try {
|
|
5180
|
+
const live = await stacks[region].store.liveLogin();
|
|
5181
|
+
if (live === void 0 || live.region !== region) return void 0;
|
|
5182
|
+
return {
|
|
5183
|
+
id: live.id,
|
|
5184
|
+
accountName: live.accountName,
|
|
5185
|
+
edition: live.edition,
|
|
5186
|
+
region: live.region
|
|
5187
|
+
};
|
|
5188
|
+
} catch {
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5191
|
+
};
|
|
5192
|
+
/**
|
|
5193
|
+
* Persist `accounts.<region>` = the app's live sign-in and rebind the store
|
|
5194
|
+
* (issue #29). The ONE Host-side path that moves the binding, driven by the
|
|
5195
|
+
* follow switch in {@link maybeFollowAppLogin}; the card's one-click hint
|
|
5196
|
+
* goes through its own settings write instead (one writer per path). Returns
|
|
5197
|
+
* the applied login so the caller can report what moved.
|
|
5198
|
+
*
|
|
5199
|
+
* The write goes through `settings.update` (the volatile write gate), not a
|
|
5200
|
+
* direct config mutation: `apply()` receives a snapshot, and the settings
|
|
5201
|
+
* event (`loader/volatile-update`) is what re-runs `applySelection` with the
|
|
5202
|
+
* updated document — the same channel every card write already uses.
|
|
5203
|
+
*/
|
|
5204
|
+
const followAppLogin = async (region) => {
|
|
5205
|
+
const live = await liveLoginOf(region);
|
|
5206
|
+
if (live === void 0 || writeSettings === void 0) return void 0;
|
|
5207
|
+
const configured = configuredAccountsOf(current());
|
|
5208
|
+
if (configured[region] === live.id) return live;
|
|
5209
|
+
await writeSettings({ accounts: {
|
|
5210
|
+
...configured,
|
|
5211
|
+
[region]: live.id
|
|
5212
|
+
} });
|
|
5213
|
+
return live;
|
|
5214
|
+
};
|
|
5028
5215
|
ctx.inject(["webServer"], (webCtx) => registerTraeUsageRoute(webCtx, {
|
|
5029
5216
|
store: (region) => stacks[region].store,
|
|
5030
5217
|
client: (region) => stacks[region].usageClient,
|
|
@@ -5034,7 +5221,8 @@ function apply(ctx, config) {
|
|
|
5034
5221
|
discoverModels: (region, signal) => stacks[region].discoverModels(signal),
|
|
5035
5222
|
probeModels: (region, ids) => stacks[region].probeModels(ids),
|
|
5036
5223
|
rawDiagnostic: (region) => stacks[region].rawDiagnostic(),
|
|
5037
|
-
creditsOfAccount: (region, accountId) => stacks[region].creditsOfAccount(accountId)
|
|
5224
|
+
creditsOfAccount: (region, accountId) => stacks[region].creditsOfAccount(accountId),
|
|
5225
|
+
liveLogin: (region) => liveLoginOf(region)
|
|
5038
5226
|
}));
|
|
5039
5227
|
/**
|
|
5040
5228
|
* Live registration handles per region, filled once the shim is listening.
|
|
@@ -5090,13 +5278,59 @@ function apply(ctx, config) {
|
|
|
5090
5278
|
}
|
|
5091
5279
|
} catch {}
|
|
5092
5280
|
})();
|
|
5281
|
+
let writeSettings;
|
|
5093
5282
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
5094
5283
|
const settings = settingsCtx.settings;
|
|
5095
5284
|
ctx.effect(() => settings.configure({ auto: true }, ctx.fiber));
|
|
5285
|
+
writeSettings = async (patch) => {
|
|
5286
|
+
const revision = () => settings.describe().find((entry) => entry.ns === settingsNs)?.revision;
|
|
5287
|
+
const conflict = (error) => typeof error === "object" && error !== null && error.code === "SETTINGS_CONFLICT";
|
|
5288
|
+
try {
|
|
5289
|
+
await settings.update(settingsNs, patch, revision());
|
|
5290
|
+
} catch (error) {
|
|
5291
|
+
if (!conflict(error)) throw error;
|
|
5292
|
+
await settings.update(settingsNs, patch, revision());
|
|
5293
|
+
}
|
|
5294
|
+
};
|
|
5295
|
+
ctx.effect(() => () => {
|
|
5296
|
+
writeSettings = void 0;
|
|
5297
|
+
});
|
|
5096
5298
|
});
|
|
5097
5299
|
ctx.on("loader/volatile-update", () => {
|
|
5098
5300
|
applySelection(current());
|
|
5301
|
+
maybeFollowAppLogin();
|
|
5099
5302
|
});
|
|
5303
|
+
/**
|
|
5304
|
+
* Follow the app's sign-in on every settings change when the switch is ON
|
|
5305
|
+
* (issue #29). Runs AFTER `applySelection` so this pass binds the previous
|
|
5306
|
+
* document, reads the app's live login against it, and — if they differ —
|
|
5307
|
+
* writes `accounts.<region>` once; that write emits `volatile-update` again,
|
|
5308
|
+
* which re-runs `applySelection` on the NEW document. The loop terminates on
|
|
5309
|
+
* its own the moment `liveLogin()` reports no mismatch (the second pass binds
|
|
5310
|
+
* the live account, so `preferred` and `current` agree). The guard below
|
|
5311
|
+
* additionally forbids two writes in flight, so a stuck mismatch can never
|
|
5312
|
+
* turn into a write storm: the next settings change retries from scratch.
|
|
5313
|
+
*
|
|
5314
|
+
* The switch reads FALSE for an existing config (see {@link Config}), so a
|
|
5315
|
+
* user who never opted in never gets a rebind — the card's one-click hint is
|
|
5316
|
+
* their path instead.
|
|
5317
|
+
*/
|
|
5318
|
+
let followInFlight = false;
|
|
5319
|
+
const maybeFollowAppLogin = async () => {
|
|
5320
|
+
if (followInFlight || !followAppLoginEnabled(current())) return;
|
|
5321
|
+
followInFlight = true;
|
|
5322
|
+
try {
|
|
5323
|
+
for (const region of REGION_KEYS) {
|
|
5324
|
+
if (!regionEnabled(current(), region)) continue;
|
|
5325
|
+
await followAppLogin(region);
|
|
5326
|
+
}
|
|
5327
|
+
} catch (error) {
|
|
5328
|
+
ctx.logger.warn("dsh-connect-trae: follow-app-login rebind skipped", error);
|
|
5329
|
+
} finally {
|
|
5330
|
+
followInFlight = false;
|
|
5331
|
+
}
|
|
5332
|
+
};
|
|
5333
|
+
maybeFollowAppLogin();
|
|
5100
5334
|
let stopped = false;
|
|
5101
5335
|
ctx.effect(() => () => {
|
|
5102
5336
|
stopped = true;
|
|
@@ -5178,4 +5412,4 @@ function apply(ctx, config) {
|
|
|
5178
5412
|
});
|
|
5179
5413
|
}
|
|
5180
5414
|
//#endregion
|
|
5181
|
-
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withTraeRegion };
|
|
5415
|
+
export { Config, FALLBACK_TRAE_MODELS, FALLBACK_TRAE_MODELS_AI, REGION_GATEWAYS, SseDecoder, TRAE_AI_PROVIDER, TRAE_CHECKIN_PATH, TRAE_CN_AGENT_TASK_PATH, TRAE_CN_TITLE_PATH, TRAE_MODELS_TEST_PATH, TRAE_MODEL_DETAIL_FUNCTIONS, TRAE_MODEL_DETAIL_PATH, TRAE_PAY_BASE, TRAE_PROBE_TIMEOUT_MS, TRAE_PROVIDER, TRAE_PROVIDERS, TRAE_PROVIDER_DISPLAY_NAMES, TRAE_RAW_CHAT_V1_PATH, TRAE_RAW_CHAT_V2_PATH, TRAE_REASONING_EFFORTS, TRAE_REGIONS, TRAE_REGION_PARAM, TRAE_SETTINGS_NS, TRAE_SOLO_CHAT_PATH, TRAE_SOLO_FUNCTION, TRAE_SOLO_MODELS_PATH, TRAE_SOLO_REMOTE_BASE, TRAE_STATE_DB_FILENAME, TRAE_STREAM_IDLE_TIMEOUT_MS, TRAE_USAGE_PATH, TRAE_VERSION_CODE_FALLBACK, TraeCatalog, TraeCredentialStore, TraeDelegatingUpstreamClient, TraeFallbackUpstreamClient, TraeGatedUpstreamClient, TraeRawCapabilityController, TraeRawCapabilityState, TraeRawChatUpstreamClient, TraeSoloBridge, TraeSoloRemoteCatalogClient, TraeSoloUpstreamClient, TraeUsageClient, UnconfiguredTraeUpstreamClient, apply, applyContextBudgets, applyImageSelection, applyReasoningEffort, asVolatile, bridgeTraeSoloStream, buildTraeAgentTaskBody, buildTraeCnHeaders, buildTraeFusionRawChatEnvelope, buildTraeModelDetailRequest, buildTraeRawChatDraft, buildTraeRawChatRuntimeConfig, classifyProbeError, classifyProbeResult, classifyTraeRawChatFailure, configuredAccountsOf, createTraeAdapter, createTraeRawGateway, createTraeShim, decodeRawChatChunk, decodeTraeEvent, decryptTraeStorageValue, deriveCatalog, describeIdShape, describeNameShape, discoveredCatalog, fallbackModelsFor, followAppLoginEnabled, hashTraeRawChatArg, identityHeaders, inject, legacyTraeOwnAuthPath, maskUserPath, mergeTraeModelSources, name, nextRegionEnabled, nextRegionSlots, normalizeTraeCredential, normalizeTraeVersionCode, parseObservedModelConfig, parseReasoningCapability, parseTraeAuthValue, parseTraeCachedModel, parseTraeModelExtraConfigLogLine, parseTraeRawChatBehaviorConfig, parseTraeRemoteModel, parseTraeStorageDocument, pickTraeStorageIdentity, prepareSoloBody, probeBody, probeModelsSequentially, probeOneModel, probeTraeRawChatCapability, rawCapabilityDiagnostic, rawCapabilityFingerprint, readTraeCachedModel, readTraeCliIdentity, readTraeIdentity, refreshTraeCredential, regionEnabled, regionEnabledOf, regionOfCredential, regionOfEdition, regionOfHost, regionOfTraeProvider, regionOfTraeStatusUrl, regionOfUserRegion, regionStateOf, registerTraeUsageRoute, resolveTraeIdentity, resolveTraeRawRuntime, sanitizeCatalog, settingsNamespaceOf, traeEndpoint, traeInputModalities, traeModelDisplayName, traeOwnAuthPath, traeRawChatExtraInfo, traeStateDatabaseCandidates, traeStorageCandidates, traeWebUsage, traeWindowsAppNames, unwrapVolatile, unwrapVolatileDeep, withTraeRegion };
|