dsh-connect-trae 4.1.0 → 4.3.0
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 +120 -0
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/lib/client.js +1047 -409
- package/lib/index.d.ts +76 -0
- package/lib/index.js +175 -12
- package/package.json +4 -2
package/lib/index.d.ts
CHANGED
|
@@ -3,6 +3,46 @@ import { Context, ProviderStreams } from "@earendil-works/pi-ai";
|
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
4
|
import { Context as Context$1 } from "@deepseek-ai/cordis";
|
|
5
5
|
import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
|
|
6
|
+
//#region src/promo.d.ts
|
|
7
|
+
/** One price pair, as upstream published it. Rates are credits per Mtoken. */
|
|
8
|
+
interface TraePromoPrice {
|
|
9
|
+
/** Undiscounted rate. */
|
|
10
|
+
beforeRate?: number;
|
|
11
|
+
/** Discounted rate, when upstream stated one for this branch. */
|
|
12
|
+
rate?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Pay ratio in percent, straight from `discount`: 100 = full price, 50 = 五折,
|
|
15
|
+
* 25 = 二五折. NOT a 0–1 multiplier — read literally, the way it is written.
|
|
16
|
+
*/
|
|
17
|
+
discountPercent?: number;
|
|
18
|
+
}
|
|
19
|
+
/** One `off_peak` window: ISO weekdays 1(Monday)–7(Sunday), minutes 0–1440. */
|
|
20
|
+
interface TraePromoWindow {
|
|
21
|
+
weekdays: number[];
|
|
22
|
+
/** Inclusive. */
|
|
23
|
+
startMinute: number;
|
|
24
|
+
/** Exclusive; 1440 is upstream's way of writing "end of day". */
|
|
25
|
+
endMinute: number;
|
|
26
|
+
}
|
|
27
|
+
/** The whole `activity_discount` document, kept unflattened on purpose. */
|
|
28
|
+
interface TraeActivityDiscount {
|
|
29
|
+
/** Upstream's machine name for the campaign, e.g. `subsidy_member_discount`. */
|
|
30
|
+
subKey?: string;
|
|
31
|
+
/** Upstream's own "right now" verdict. */
|
|
32
|
+
current?: TraePromoPrice & {
|
|
33
|
+
discountType?: string;
|
|
34
|
+
};
|
|
35
|
+
/** Subscription price — hover material only, see the module note. */
|
|
36
|
+
member?: TraePromoPrice;
|
|
37
|
+
/** Recurring cheap hours, judged locally against each model's own zone. */
|
|
38
|
+
offPeak?: TraePromoPrice & {
|
|
39
|
+
timeZone?: string;
|
|
40
|
+
windows: TraePromoWindow[];
|
|
41
|
+
};
|
|
42
|
+
/** Subsidy branch, when upstream names it separately from `current`. */
|
|
43
|
+
subsidy?: TraePromoPrice;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
6
46
|
//#region src/reasoning.d.ts
|
|
7
47
|
declare const TRAE_REASONING_EFFORTS: readonly ["minimal", "low", "medium", "high", "xhigh"];
|
|
8
48
|
type TraeReasoningEffort = typeof TRAE_REASONING_EFFORTS[number];
|
|
@@ -43,6 +83,11 @@ interface TraeDiscoveredModel {
|
|
|
43
83
|
* `config_info_list` for the chat functions).
|
|
44
84
|
*/
|
|
45
85
|
function?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Trae's discount rule for this row (`features.activity_discount`), kept whole
|
|
88
|
+
* so the card can judge it against the clock at paint time (src/promo.ts).
|
|
89
|
+
*/
|
|
90
|
+
activityDiscount?: TraeActivityDiscount;
|
|
46
91
|
}
|
|
47
92
|
/**
|
|
48
93
|
* Parse only capabilities explicitly advertised by Trae's remote model API.
|
|
@@ -181,6 +226,16 @@ interface TraeModelInfo {
|
|
|
181
226
|
* image to a model that cannot read one burns credits for a silent failure.
|
|
182
227
|
*/
|
|
183
228
|
multimodal?: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Trae's discount RULE for this model, exactly as published (see src/promo.ts).
|
|
231
|
+
*
|
|
232
|
+
* Kept unflattened and unjudged: whether a badge is painted depends on the
|
|
233
|
+
* clock, so the card judges it at paint time rather than trusting a verdict
|
|
234
|
+
* frozen into a saved catalog. Absent means the model carries no promotion
|
|
235
|
+
* document — including every `ai` (international) row, whose subscription
|
|
236
|
+
* models publish no `consumption_rate` at all.
|
|
237
|
+
*/
|
|
238
|
+
activityDiscount?: TraeActivityDiscount;
|
|
184
239
|
}
|
|
185
240
|
/**
|
|
186
241
|
* Bootstrap catalog: identity only where current Trae metadata has not been
|
|
@@ -267,6 +322,15 @@ interface TraeWireModel {
|
|
|
267
322
|
* therefore has to replay the directory's own function.
|
|
268
323
|
*/
|
|
269
324
|
function?: string;
|
|
325
|
+
/**
|
|
326
|
+
* The row's own `activity_discount`, read from `display_contact_config`.
|
|
327
|
+
*
|
|
328
|
+
* The wire roster is the richer of the two directories (8 rows carrying a
|
|
329
|
+
* discount vs the Remote directory's 4), and the same model can name a
|
|
330
|
+
* different campaign on each side, so this value wins over the Remote one
|
|
331
|
+
* during the merge — the precedence `creditMultiplier` already follows.
|
|
332
|
+
*/
|
|
333
|
+
activityDiscount?: TraeActivityDiscount;
|
|
270
334
|
}
|
|
271
335
|
/**
|
|
272
336
|
* Merge the two Trae model sources into one authoritative catalog.
|
|
@@ -748,6 +812,13 @@ interface TraeWebModel {
|
|
|
748
812
|
probe?: TraeModelProbeVerdict;
|
|
749
813
|
/** Upstream's own words for the verdict, when it gave any. */
|
|
750
814
|
probeDetail?: string;
|
|
815
|
+
/**
|
|
816
|
+
* Trae's discount rule for this row, straight from the directory that
|
|
817
|
+
* advertised it (see src/promo.ts). The CARD decides whether it is live right
|
|
818
|
+
* now: the rule carries weekday/time windows in an upstream-declared zone, so
|
|
819
|
+
* a verdict baked in here would be stale by the next morning.
|
|
820
|
+
*/
|
|
821
|
+
activityDiscount?: TraeActivityDiscount;
|
|
751
822
|
}
|
|
752
823
|
/**
|
|
753
824
|
* One model's availability verdict from the one-click test.
|
|
@@ -2717,6 +2788,11 @@ interface TraeSoloModel {
|
|
|
2717
2788
|
reasoning?: TraeReasoningCapability;
|
|
2718
2789
|
/** Effective (post-discount) credit multiplier, as the Trae IDE shows it. */
|
|
2719
2790
|
creditMultiplier?: number;
|
|
2791
|
+
/**
|
|
2792
|
+
* The row's `activity_discount` rule, read from `display_contact_config`.
|
|
2793
|
+
* Display data only: src/promo.ts decides at paint time whether a badge shows.
|
|
2794
|
+
*/
|
|
2795
|
+
activityDiscount?: TraeActivityDiscount;
|
|
2720
2796
|
/** The directory function that listed this config (replayed when calling it). */
|
|
2721
2797
|
function?: string;
|
|
2722
2798
|
}
|
package/lib/index.js
CHANGED
|
@@ -236,12 +236,14 @@ function mergeTraeModelSources(remote, wire) {
|
|
|
236
236
|
const wireModel = wireById.get(displayKey(model.id)) ?? wireByName.get(displayKey(model.name));
|
|
237
237
|
const creditMultiplier = wireModel?.creditMultiplier ?? model.creditMultiplier;
|
|
238
238
|
const wireFunction = wireModel?.function ?? model.function;
|
|
239
|
+
const activityDiscount = wireModel?.activityDiscount ?? model.activityDiscount;
|
|
239
240
|
result.push({
|
|
240
241
|
id: model.id,
|
|
241
242
|
name: model.name,
|
|
242
243
|
...model.contextWindow === void 0 ? {} : { contextWindow: model.contextWindow },
|
|
243
244
|
...model.maxContextWindow === void 0 ? {} : { maxContextWindow: model.maxContextWindow },
|
|
244
245
|
...creditMultiplier === void 0 ? {} : { creditMultiplier },
|
|
246
|
+
...activityDiscount === void 0 ? {} : { activityDiscount },
|
|
245
247
|
input: ["text"],
|
|
246
248
|
...wireModel?.maxTokens === void 0 ? {} : { maxTokens: wireModel.maxTokens },
|
|
247
249
|
...model.multimodal === void 0 ? {} : { multimodal: model.multimodal },
|
|
@@ -310,6 +312,7 @@ function discoveredCatalog(models) {
|
|
|
310
312
|
...model.maxContextWindow === void 0 ? {} : { maxContextWindow: model.maxContextWindow },
|
|
311
313
|
input: ["text"],
|
|
312
314
|
...model.creditMultiplier === void 0 ? {} : { creditMultiplier: model.creditMultiplier },
|
|
315
|
+
...model.activityDiscount === void 0 ? {} : { activityDiscount: model.activityDiscount },
|
|
313
316
|
...model.multimodal === void 0 ? {} : { multimodal: model.multimodal },
|
|
314
317
|
...model.function === void 0 ? {} : { wireFunction: model.function },
|
|
315
318
|
reasoningSupported: model.reasoningSupported,
|
|
@@ -3738,6 +3741,97 @@ function traeEndpoint(baseUrl, path) {
|
|
|
3738
3741
|
return `${baseUrl.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
3739
3742
|
}
|
|
3740
3743
|
//#endregion
|
|
3744
|
+
//#region src/promo.ts
|
|
3745
|
+
function record$1(value) {
|
|
3746
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
3747
|
+
}
|
|
3748
|
+
function finite(value) {
|
|
3749
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3750
|
+
}
|
|
3751
|
+
function percent(value) {
|
|
3752
|
+
const parsed = finite(value);
|
|
3753
|
+
return parsed !== void 0 && parsed > 0 && parsed < 100 ? parsed : void 0;
|
|
3754
|
+
}
|
|
3755
|
+
function ratePair(raw) {
|
|
3756
|
+
const entry = record$1(raw);
|
|
3757
|
+
if (entry === void 0) return {};
|
|
3758
|
+
const beforeRate = finite(entry["before_consumption_rate"]);
|
|
3759
|
+
const rate = finite(entry["consumption_rate"]) ?? finite(entry["after_consumption_rate"]);
|
|
3760
|
+
const discountPercent = percent(entry["discount"]);
|
|
3761
|
+
return {
|
|
3762
|
+
...beforeRate === void 0 || beforeRate <= 0 ? {} : { beforeRate },
|
|
3763
|
+
...rate === void 0 || rate <= 0 ? {} : { rate },
|
|
3764
|
+
...discountPercent === void 0 ? {} : { discountPercent }
|
|
3765
|
+
};
|
|
3766
|
+
}
|
|
3767
|
+
function windows(raw) {
|
|
3768
|
+
const list = Array.isArray(raw) ? raw : [];
|
|
3769
|
+
const parsed = [];
|
|
3770
|
+
for (const item of list) {
|
|
3771
|
+
const entry = record$1(item);
|
|
3772
|
+
if (entry === void 0) continue;
|
|
3773
|
+
const weekdays = Array.isArray(entry["weekdays"]) ? entry["weekdays"].filter((day) => Number.isInteger(day) && day >= 1 && day <= 7) : [];
|
|
3774
|
+
const startMinute = finite(entry["start_minute"]);
|
|
3775
|
+
const endMinute = finite(entry["end_minute"]);
|
|
3776
|
+
if (weekdays.length === 0 || startMinute === void 0 || endMinute === void 0) continue;
|
|
3777
|
+
if (startMinute < 0 || startMinute > 1439 || endMinute < 1 || endMinute > 1440) continue;
|
|
3778
|
+
if (startMinute >= endMinute) continue;
|
|
3779
|
+
parsed.push({
|
|
3780
|
+
weekdays,
|
|
3781
|
+
startMinute,
|
|
3782
|
+
endMinute
|
|
3783
|
+
});
|
|
3784
|
+
}
|
|
3785
|
+
return parsed;
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Parse one `activity_discount` document.
|
|
3789
|
+
*
|
|
3790
|
+
* `value` may be the already-parsed object (both call sites hand us the parent
|
|
3791
|
+
* object) or the raw JSON string; anything without `enable: true` reads as "no
|
|
3792
|
+
* promotion" rather than as a rule we might later misjudge.
|
|
3793
|
+
*/
|
|
3794
|
+
function parseTraeActivityDiscount(value) {
|
|
3795
|
+
let source = value;
|
|
3796
|
+
if (typeof source === "string") {
|
|
3797
|
+
if (source === "") return void 0;
|
|
3798
|
+
try {
|
|
3799
|
+
source = JSON.parse(source);
|
|
3800
|
+
} catch {
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
const raw = record$1(source);
|
|
3805
|
+
if (raw === void 0 || raw["enable"] !== true) return void 0;
|
|
3806
|
+
const data = record$1(raw["data"]);
|
|
3807
|
+
if (data === void 0) return void 0;
|
|
3808
|
+
const subKey = typeof raw["subKey"] === "string" && raw["subKey"] !== "" ? raw["subKey"] : void 0;
|
|
3809
|
+
const currentRaw = record$1(data["current"]);
|
|
3810
|
+
const currentType = typeof currentRaw?.["discount_type"] === "string" ? currentRaw["discount_type"] : void 0;
|
|
3811
|
+
const current = currentRaw === void 0 ? void 0 : {
|
|
3812
|
+
...ratePair(currentRaw),
|
|
3813
|
+
...currentType === void 0 ? {} : { discountType: currentType }
|
|
3814
|
+
};
|
|
3815
|
+
const memberRaw = data["member"];
|
|
3816
|
+
const member = record$1(memberRaw) === void 0 ? void 0 : ratePair(memberRaw);
|
|
3817
|
+
const subsidyRaw = data["subsidy"];
|
|
3818
|
+
const subsidy = record$1(subsidyRaw) === void 0 ? void 0 : ratePair(subsidyRaw);
|
|
3819
|
+
const offPeakRaw = record$1(data["off_peak"]);
|
|
3820
|
+
const offPeak = offPeakRaw === void 0 ? void 0 : {
|
|
3821
|
+
...ratePair(offPeakRaw),
|
|
3822
|
+
...typeof offPeakRaw["time_zone"] === "string" && offPeakRaw["time_zone"] !== "" ? { timeZone: offPeakRaw["time_zone"] } : {},
|
|
3823
|
+
windows: windows(offPeakRaw["time_windows"])
|
|
3824
|
+
};
|
|
3825
|
+
const parsed = {
|
|
3826
|
+
...subKey === void 0 ? {} : { subKey },
|
|
3827
|
+
...current === void 0 ? {} : { current },
|
|
3828
|
+
...member === void 0 ? {} : { member },
|
|
3829
|
+
...offPeak === void 0 ? {} : { offPeak },
|
|
3830
|
+
...subsidy === void 0 ? {} : { subsidy }
|
|
3831
|
+
};
|
|
3832
|
+
return parsed.current === void 0 && parsed.member === void 0 && parsed.offPeak === void 0 && parsed.subsidy === void 0 ? void 0 : parsed;
|
|
3833
|
+
}
|
|
3834
|
+
//#endregion
|
|
3741
3835
|
//#region src/reasoning.ts
|
|
3742
3836
|
const TRAE_REASONING_EFFORTS = [
|
|
3743
3837
|
"minimal",
|
|
@@ -3905,6 +3999,29 @@ function prepareSoloBody(source, defaultModel = "glm-5.2", functionName) {
|
|
|
3905
3999
|
return JSON.stringify(body);
|
|
3906
4000
|
}
|
|
3907
4001
|
/**
|
|
4002
|
+
* Parse this row's `display_contact_config` document.
|
|
4003
|
+
*
|
|
4004
|
+
* It is a *string* holding a second JSON document, and three different readers
|
|
4005
|
+
* (the credit multiplier, the promotion rule, and whatever upstream adds next)
|
|
4006
|
+
* need it — so it is decoded once here rather than three times.
|
|
4007
|
+
*/
|
|
4008
|
+
function wireContactConfig(config) {
|
|
4009
|
+
const raw = config["display_contact_config"];
|
|
4010
|
+
if (typeof raw !== "string" || raw === "") return void 0;
|
|
4011
|
+
let parsed;
|
|
4012
|
+
try {
|
|
4013
|
+
parsed = JSON.parse(raw);
|
|
4014
|
+
} catch {
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
4018
|
+
return parsed;
|
|
4019
|
+
}
|
|
4020
|
+
/** The promotion rule from `display_contact_config`, when the row carries one. */
|
|
4021
|
+
function wireActivityDiscount(config) {
|
|
4022
|
+
return parseTraeActivityDiscount(wireContactConfig(config)?.["activity_discount"]);
|
|
4023
|
+
}
|
|
4024
|
+
/**
|
|
3908
4025
|
* Read the effective credit multiplier from a `get_detail_param` row.
|
|
3909
4026
|
*
|
|
3910
4027
|
* The rate the Trae IDE renders lives in `display_contact_config` — a *string*
|
|
@@ -3920,15 +4037,8 @@ function prepareSoloBody(source, defaultModel = "glm-5.2", functionName) {
|
|
|
3920
4037
|
* field keep their bare name — parsed-but-absent, never fabricated.
|
|
3921
4038
|
*/
|
|
3922
4039
|
function wireCreditMultiplier(config) {
|
|
3923
|
-
const
|
|
3924
|
-
if (
|
|
3925
|
-
let parsed;
|
|
3926
|
-
try {
|
|
3927
|
-
parsed = JSON.parse(raw);
|
|
3928
|
-
} catch {
|
|
3929
|
-
return;
|
|
3930
|
-
}
|
|
3931
|
-
if (typeof parsed !== "object" || parsed === null) return void 0;
|
|
4040
|
+
const parsed = wireContactConfig(config);
|
|
4041
|
+
if (parsed === void 0) return void 0;
|
|
3932
4042
|
const consumption = parsed["consumption_rate"];
|
|
3933
4043
|
if (typeof consumption !== "object" || consumption === null) return void 0;
|
|
3934
4044
|
const entry = consumption;
|
|
@@ -4036,6 +4146,7 @@ var TraeSoloUpstreamClient = class {
|
|
|
4036
4146
|
...detail
|
|
4037
4147
|
});
|
|
4038
4148
|
const creditMultiplier = wireCreditMultiplier(config);
|
|
4149
|
+
const activityDiscount = wireActivityDiscount(config);
|
|
4039
4150
|
if (byId.has(id)) continue;
|
|
4040
4151
|
byId.set(id, {
|
|
4041
4152
|
id,
|
|
@@ -4044,6 +4155,7 @@ var TraeSoloUpstreamClient = class {
|
|
|
4044
4155
|
...maxTokens === void 0 ? {} : { maxTokens },
|
|
4045
4156
|
...reasoning === void 0 ? {} : { reasoning },
|
|
4046
4157
|
...creditMultiplier === void 0 ? {} : { creditMultiplier },
|
|
4158
|
+
...activityDiscount === void 0 ? {} : { activityDiscount },
|
|
4047
4159
|
function: directoryFunction
|
|
4048
4160
|
});
|
|
4049
4161
|
}
|
|
@@ -4858,6 +4970,7 @@ function parseTraeRemoteModel(value, groupFunction) {
|
|
|
4858
4970
|
const consumptionData = record(consumption?.["data"]);
|
|
4859
4971
|
const creditMultiplier = consumption?.["enable"] === true ? finitePositive(consumptionData?.["rate"]) : void 0;
|
|
4860
4972
|
const reasoningSupported = record(features?.["reasoning"])?.["enable"] === true;
|
|
4973
|
+
const activityDiscount = parseTraeActivityDiscount(features?.["activity_discount"]);
|
|
4861
4974
|
const reasoningConfig = record(raw.reasoning_effort_config);
|
|
4862
4975
|
const supported = (Array.isArray(reasoningConfig?.["options"]) ? reasoningConfig["options"] : []).flatMap((option) => {
|
|
4863
4976
|
if (typeof option !== "string") return [];
|
|
@@ -4874,6 +4987,7 @@ function parseTraeRemoteModel(value, groupFunction) {
|
|
|
4874
4987
|
...dev === void 0 ? {} : { contextWindow: dev },
|
|
4875
4988
|
...max === void 0 ? {} : { maxContextWindow: max },
|
|
4876
4989
|
...creditMultiplier === void 0 ? {} : { creditMultiplier },
|
|
4990
|
+
...activityDiscount === void 0 ? {} : { activityDiscount },
|
|
4877
4991
|
reasoningSupported,
|
|
4878
4992
|
...supported.length === 0 ? {} : { reasoning: {
|
|
4879
4993
|
supported,
|
|
@@ -4963,8 +5077,7 @@ function traeSoloRemoteFallbackBases(region, officialBase) {
|
|
|
4963
5077
|
return backupBase === officialBase ? [] : [backupBase];
|
|
4964
5078
|
}
|
|
4965
5079
|
/**
|
|
4966
|
-
* Of two rows for the SAME model id, the one
|
|
4967
|
-
* capacity (issue #23).
|
|
5080
|
+
* Of two rows for the SAME model id, the one the catalog should keep.
|
|
4968
5081
|
*
|
|
4969
5082
|
* The gateway groups its directory by function and reports a model's capacity
|
|
4970
5083
|
* per group, with the group order varying between calls. Keeping the strongest
|
|
@@ -4972,6 +5085,9 @@ function traeSoloRemoteFallbackBases(region, officialBase) {
|
|
|
4972
5085
|
* LEXICOGRAPHIC and must be applied all the way down, or ordering leaks back in
|
|
4973
5086
|
* at the next field:
|
|
4974
5087
|
*
|
|
5088
|
+
* 0. the kept row must also carry the promotional rule, which the groups
|
|
5089
|
+
* disagree about — the rule is copied onto the capacity winner, and of two
|
|
5090
|
+
* rules the one that can say more wins (see the comment in the body);
|
|
4975
5091
|
* 1. a row with a Max tier beats one without (`maxContextWindow` is only set
|
|
4976
5092
|
* when the group reported `max_mode: true` with a positive max);
|
|
4977
5093
|
* 2. two Max rows: the larger max wins;
|
|
@@ -4989,6 +5105,52 @@ function traeSoloRemoteFallbackBases(region, officialBase) {
|
|
|
4989
5105
|
*/
|
|
4990
5106
|
function preferStrongerRow(candidate, incumbent) {
|
|
4991
5107
|
if (incumbent === void 0) return candidate;
|
|
5108
|
+
const winner = strongerCapacityRow(candidate, incumbent);
|
|
5109
|
+
const rule = richerRule(candidate.activityDiscount, incumbent.activityDiscount);
|
|
5110
|
+
if (rule === void 0 || rule === winner.activityDiscount) return winner;
|
|
5111
|
+
return {
|
|
5112
|
+
...winner,
|
|
5113
|
+
activityDiscount: rule
|
|
5114
|
+
};
|
|
5115
|
+
}
|
|
5116
|
+
/**
|
|
5117
|
+
* Step 0's tie-break: of two rules for the same model id, the one that can say
|
|
5118
|
+
* more about this moment. Every parsed rule already passed `enable: true` (see
|
|
5119
|
+
* parseTraeActivityDiscount), so the ranking is: an upstream "right now"
|
|
5120
|
+
* discount first, then the rule covering more clock. Two rules that rank EQUAL
|
|
5121
|
+
* are compared by their canonical content, so the answer depends on the rule
|
|
5122
|
+
* documents and never on which group the gateway answered first (issue #23).
|
|
5123
|
+
*/
|
|
5124
|
+
function richerRule(candidate, incumbent) {
|
|
5125
|
+
if (candidate === void 0) return incumbent;
|
|
5126
|
+
if (incumbent === void 0) return candidate;
|
|
5127
|
+
const score = (rule) => {
|
|
5128
|
+
return (rule.current !== void 0 && (rule.current.discountType ?? "none") !== "none" ? 1 : 0) * 1e3 + (rule.offPeak?.windows.length ?? 0);
|
|
5129
|
+
};
|
|
5130
|
+
const candidateScore = score(candidate);
|
|
5131
|
+
const incumbentScore = score(incumbent);
|
|
5132
|
+
if (candidateScore !== incumbentScore) return candidateScore > incumbentScore ? candidate : incumbent;
|
|
5133
|
+
return canonicalRule(candidate) < canonicalRule(incumbent) ? candidate : incumbent;
|
|
5134
|
+
}
|
|
5135
|
+
/**
|
|
5136
|
+
* Content-only identity for two rules that scored the same.
|
|
5137
|
+
*
|
|
5138
|
+
* The score cannot distinguish, say, two `current: none` rules with two windows
|
|
5139
|
+
* each in different hours — and keeping "whichever arrived first" would let the
|
|
5140
|
+
* gateway's group order decide whether tonight's badge appears. Key order is
|
|
5141
|
+
* normalised (the objects are ours, but a caller may hand us a literal), so
|
|
5142
|
+
* equal documents compare equal and the incumbent is kept unchanged.
|
|
5143
|
+
*/
|
|
5144
|
+
function canonicalRule(rule) {
|
|
5145
|
+
const canonical = (value) => {
|
|
5146
|
+
if (Array.isArray(value)) return value.map((item) => canonical(item));
|
|
5147
|
+
if (typeof value !== "object" || value === null) return value;
|
|
5148
|
+
return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, item]) => [key, canonical(item)]));
|
|
5149
|
+
};
|
|
5150
|
+
return JSON.stringify(canonical(rule));
|
|
5151
|
+
}
|
|
5152
|
+
/** Steps 1–4 of the documented order: the row advertising the stronger capacity. */
|
|
5153
|
+
function strongerCapacityRow(candidate, incumbent) {
|
|
4992
5154
|
const candidateMax = candidate.maxContextWindow;
|
|
4993
5155
|
const incumbentMax = incumbent.maxContextWindow;
|
|
4994
5156
|
if (candidateMax !== void 0 && incumbentMax === void 0) return candidate;
|
|
@@ -8756,7 +8918,8 @@ const modelConfig = z.object({
|
|
|
8756
8918
|
creditMultiplier: z.number(),
|
|
8757
8919
|
wireFunction: z.string(),
|
|
8758
8920
|
wireConfigName: z.string(),
|
|
8759
|
-
multimodal: z.boolean()
|
|
8921
|
+
multimodal: z.boolean(),
|
|
8922
|
+
activityDiscount: z.any()
|
|
8760
8923
|
});
|
|
8761
8924
|
/**
|
|
8762
8925
|
* One panel's saved state. Panels are keyed `cn | enterprise | ai` — see
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "dsh-connect-trae",
|
|
3
3
|
"displayName": "DSH Connect Trae",
|
|
4
4
|
"description": "把本机登录的 Trae 模型接入 DeepSeek Harness:国内版与国际版双供应商并行,提供用量/积分概览与每日签到领取。",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.3.0",
|
|
6
6
|
"author": "dingminhua",
|
|
7
7
|
"copyright": "Copyright (c) 2026 LaoDing",
|
|
8
8
|
"license": "MIT",
|
|
@@ -50,7 +50,9 @@
|
|
|
50
50
|
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
|
|
51
51
|
"check": "pnpm run typecheck && pnpm run test && pnpm run build",
|
|
52
52
|
"verify:release": "node scripts/verify-release.mjs",
|
|
53
|
-
"card:shot": "vitest run --config vitest.card.config.ts"
|
|
53
|
+
"card:shot": "vitest run --config vitest.card.config.ts",
|
|
54
|
+
"promo:shot": "vitest run --config vitest.card.config.ts -t \"promo shot\"",
|
|
55
|
+
"panels:shot": "vitest run --config vitest.card.config.ts -t \"panels shot\""
|
|
54
56
|
},
|
|
55
57
|
"dsh": {
|
|
56
58
|
"bundle": {
|